Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: cdc-acm: export parsed capabilities through sysfs

this patch exports the attributes cdc-acm knows about a device through sysfs.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
c4cabd28 bb74782e

+79 -5
+76 -5
drivers/usb/class/cdc-acm.c
··· 212 212 } 213 213 return rc; 214 214 } 215 + /* 216 + * attributes exported through sysfs 217 + */ 218 + static ssize_t show_caps 219 + (struct device *dev, struct device_attribute *attr, char *buf) 220 + { 221 + struct usb_interface *intf = to_usb_interface(dev); 222 + struct acm *acm = usb_get_intfdata(intf); 215 223 224 + return sprintf(buf, "%d", acm->ctrl_caps); 225 + } 226 + static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); 227 + 228 + static ssize_t show_country_codes 229 + (struct device *dev, struct device_attribute *attr, char *buf) 230 + { 231 + struct usb_interface *intf = to_usb_interface(dev); 232 + struct acm *acm = usb_get_intfdata(intf); 233 + 234 + memcpy(buf, acm->country_codes, acm->country_code_size); 235 + return acm->country_code_size; 236 + } 237 + 238 + static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); 239 + 240 + static ssize_t show_country_rel_date 241 + (struct device *dev, struct device_attribute *attr, char *buf) 242 + { 243 + struct usb_interface *intf = to_usb_interface(dev); 244 + struct acm *acm = usb_get_intfdata(intf); 245 + 246 + return sprintf(buf, "%d", acm->country_rel_date); 247 + } 248 + 249 + static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); 216 250 /* 217 251 * Interrupt handlers for various ACM device responses 218 252 */ ··· 548 514 usb_free_urb(acm->writeurb); 549 515 for (i = 0; i < nr; i++) 550 516 usb_free_urb(acm->ru[i].urb); 517 + kfree(acm->country_codes); 551 518 kfree(acm); 552 519 } 553 520 ··· 796 761 const struct usb_device_id *id) 797 762 { 798 763 struct usb_cdc_union_desc *union_header = NULL; 764 + struct usb_cdc_country_functional_desc *cfd = NULL; 799 765 char *buffer = intf->altsetting->extra; 800 766 int buflen = intf->altsetting->extralen; 801 767 struct usb_interface *control_interface; ··· 860 824 union_header = (struct usb_cdc_union_desc *) 861 825 buffer; 862 826 break; 863 - case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ 864 - break; /* for now we ignore it */ 827 + case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ 828 + cfd = (struct usb_cdc_country_functional_desc *)buffer; 829 + break; 865 830 case USB_CDC_HEADER_TYPE: /* maybe check version */ 866 831 break; /* for now we ignore it */ 867 832 case USB_CDC_ACM_TYPE: ··· 1020 983 goto alloc_fail7; 1021 984 } 1022 985 986 + usb_set_intfdata (intf, acm); 987 + 988 + i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); 989 + if (i < 0) 990 + goto alloc_fail8; 991 + 992 + if (cfd) { /* export the country data */ 993 + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); 994 + if (!acm->country_codes) 995 + goto skip_countries; 996 + acm->country_code_size = cfd->bLength - 4; 997 + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); 998 + acm->country_rel_date = cfd->iCountryCodeRelDate; 999 + 1000 + i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); 1001 + if (i < 0) { 1002 + kfree(acm->country_codes); 1003 + goto skip_countries; 1004 + } 1005 + 1006 + i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); 1007 + if (i < 0) { 1008 + kfree(acm->country_codes); 1009 + goto skip_countries; 1010 + } 1011 + } 1012 + 1013 + skip_countries: 1023 1014 usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), 1024 1015 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); 1025 1016 acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ··· 1071 1006 tty_register_device(acm_tty_driver, minor, &control_interface->dev); 1072 1007 1073 1008 acm_table[minor] = acm; 1074 - usb_set_intfdata (intf, acm); 1075 - return 0; 1076 1009 1010 + return 0; 1011 + alloc_fail8: 1012 + usb_free_urb(acm->writeurb); 1077 1013 alloc_fail7: 1078 1014 for (i = 0; i < num_rx_buf; i++) 1079 1015 usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); ··· 1093 1027 1094 1028 static void acm_disconnect(struct usb_interface *intf) 1095 1029 { 1096 - struct acm *acm = usb_get_intfdata (intf); 1030 + struct acm *acm = usb_get_intfdata(intf); 1097 1031 struct usb_device *usb_dev = interface_to_usbdev(intf); 1098 1032 int i; 1099 1033 ··· 1107 1041 mutex_unlock(&open_mutex); 1108 1042 return; 1109 1043 } 1044 + if (acm->country_codes){ 1045 + device_remove_file(&intf->dev, &dev_attr_wCountryCodes); 1046 + device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate); 1047 + } 1048 + device_remove_file(&intf->dev, &dev_attr_bmCapabilities); 1110 1049 acm->dev = NULL; 1111 1050 usb_set_intfdata(acm->control, NULL); 1112 1051 usb_set_intfdata(acm->data, NULL);
+3
drivers/usb/class/cdc-acm.h
··· 91 91 struct urb *ctrlurb, *writeurb; /* urbs */ 92 92 u8 *ctrl_buffer; /* buffers of urbs */ 93 93 dma_addr_t ctrl_dma; /* dma handles of buffers */ 94 + u8 *country_codes; /* country codes from device */ 95 + unsigned int country_code_size; /* size of this buffer */ 96 + unsigned int country_rel_date; /* release date of version */ 94 97 struct acm_wb wb[ACM_NW]; 95 98 struct acm_ru ru[ACM_NR]; 96 99 struct acm_rb rb[ACM_NR];