diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 81 |
1 files changed, 76 insertions, 5 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 31ae661e586a..14de3b1b6a20 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -212,7 +212,41 @@ static int acm_write_start(struct acm *acm) | |||
212 | } | 212 | } |
213 | return rc; | 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); | ||
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 | } | ||
215 | 248 | ||
249 | static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); | ||
216 | /* | 250 | /* |
217 | * Interrupt handlers for various ACM device responses | 251 | * Interrupt handlers for various ACM device responses |
218 | */ | 252 | */ |
@@ -514,6 +548,7 @@ static void acm_tty_unregister(struct acm *acm) | |||
514 | usb_free_urb(acm->writeurb); | 548 | usb_free_urb(acm->writeurb); |
515 | for (i = 0; i < nr; i++) | 549 | for (i = 0; i < nr; i++) |
516 | usb_free_urb(acm->ru[i].urb); | 550 | usb_free_urb(acm->ru[i].urb); |
551 | kfree(acm->country_codes); | ||
517 | kfree(acm); | 552 | kfree(acm); |
518 | } | 553 | } |
519 | 554 | ||
@@ -761,6 +796,7 @@ static int acm_probe (struct usb_interface *intf, | |||
761 | const struct usb_device_id *id) | 796 | const struct usb_device_id *id) |
762 | { | 797 | { |
763 | struct usb_cdc_union_desc *union_header = NULL; | 798 | struct usb_cdc_union_desc *union_header = NULL; |
799 | struct usb_cdc_country_functional_desc *cfd = NULL; | ||
764 | char *buffer = intf->altsetting->extra; | 800 | char *buffer = intf->altsetting->extra; |
765 | int buflen = intf->altsetting->extralen; | 801 | int buflen = intf->altsetting->extralen; |
766 | struct usb_interface *control_interface; | 802 | struct usb_interface *control_interface; |
@@ -824,8 +860,9 @@ static int acm_probe (struct usb_interface *intf, | |||
824 | union_header = (struct usb_cdc_union_desc *) | 860 | union_header = (struct usb_cdc_union_desc *) |
825 | buffer; | 861 | buffer; |
826 | break; | 862 | break; |
827 | case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ | 863 | case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ |
828 | break; /* for now we ignore it */ | 864 | cfd = (struct usb_cdc_country_functional_desc *)buffer; |
865 | break; | ||
829 | case USB_CDC_HEADER_TYPE: /* maybe check version */ | 866 | case USB_CDC_HEADER_TYPE: /* maybe check version */ |
830 | break; /* for now we ignore it */ | 867 | break; /* for now we ignore it */ |
831 | case USB_CDC_ACM_TYPE: | 868 | case USB_CDC_ACM_TYPE: |
@@ -983,6 +1020,34 @@ skip_normal_probe: | |||
983 | goto alloc_fail7; | 1020 | goto alloc_fail7; |
984 | } | 1021 | } |
985 | 1022 | ||
1023 | usb_set_intfdata (intf, acm); | ||
1024 | |||
1025 | i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); | ||
1026 | if (i < 0) | ||
1027 | goto alloc_fail8; | ||
1028 | |||
1029 | if (cfd) { /* export the country data */ | ||
1030 | acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); | ||
1031 | if (!acm->country_codes) | ||
1032 | goto skip_countries; | ||
1033 | acm->country_code_size = cfd->bLength - 4; | ||
1034 | memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); | ||
1035 | acm->country_rel_date = cfd->iCountryCodeRelDate; | ||
1036 | |||
1037 | i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); | ||
1038 | if (i < 0) { | ||
1039 | kfree(acm->country_codes); | ||
1040 | goto skip_countries; | ||
1041 | } | ||
1042 | |||
1043 | i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); | ||
1044 | if (i < 0) { | ||
1045 | kfree(acm->country_codes); | ||
1046 | goto skip_countries; | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | skip_countries: | ||
986 | usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), | 1051 | usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), |
987 | acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); | 1052 | acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); |
988 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 1053 | acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
@@ -1006,9 +1071,10 @@ skip_normal_probe: | |||
1006 | tty_register_device(acm_tty_driver, minor, &control_interface->dev); | 1071 | tty_register_device(acm_tty_driver, minor, &control_interface->dev); |
1007 | 1072 | ||
1008 | acm_table[minor] = acm; | 1073 | acm_table[minor] = acm; |
1009 | usb_set_intfdata (intf, acm); | ||
1010 | return 0; | ||
1011 | 1074 | ||
1075 | return 0; | ||
1076 | alloc_fail8: | ||
1077 | usb_free_urb(acm->writeurb); | ||
1012 | alloc_fail7: | 1078 | alloc_fail7: |
1013 | for (i = 0; i < num_rx_buf; i++) | 1079 | for (i = 0; i < num_rx_buf; i++) |
1014 | usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); | 1080 | usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); |
@@ -1027,7 +1093,7 @@ alloc_fail: | |||
1027 | 1093 | ||
1028 | static void acm_disconnect(struct usb_interface *intf) | 1094 | static void acm_disconnect(struct usb_interface *intf) |
1029 | { | 1095 | { |
1030 | struct acm *acm = usb_get_intfdata (intf); | 1096 | struct acm *acm = usb_get_intfdata(intf); |
1031 | struct usb_device *usb_dev = interface_to_usbdev(intf); | 1097 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
1032 | int i; | 1098 | int i; |
1033 | 1099 | ||
@@ -1041,6 +1107,11 @@ static void acm_disconnect(struct usb_interface *intf) | |||
1041 | mutex_unlock(&open_mutex); | 1107 | mutex_unlock(&open_mutex); |
1042 | return; | 1108 | return; |
1043 | } | 1109 | } |
1110 | if (acm->country_codes){ | ||
1111 | device_remove_file(&intf->dev, &dev_attr_wCountryCodes); | ||
1112 | device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate); | ||
1113 | } | ||
1114 | device_remove_file(&intf->dev, &dev_attr_bmCapabilities); | ||
1044 | acm->dev = NULL; | 1115 | acm->dev = NULL; |
1045 | usb_set_intfdata(acm->control, NULL); | 1116 | usb_set_intfdata(acm->control, NULL); |
1046 | usb_set_intfdata(acm->data, NULL); | 1117 | usb_set_intfdata(acm->data, NULL); |