diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 90 |
1 files changed, 80 insertions, 10 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d38a25f36ea..0081c1d1268 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -59,7 +59,6 @@ #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/mutex.h> #include <asm/uaccess.h> #include <linux/usb.h> @@ -212,7 +211,41 @@ static int acm_write_start(struct acm *acm) } return rc; } +/* + * attributes exported through sysfs + */ +static ssize_t show_caps +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->ctrl_caps); +} +static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); + +static ssize_t show_country_codes +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + memcpy(buf, acm->country_codes, acm->country_code_size); + return acm->country_code_size; +} + +static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); + +static ssize_t show_country_rel_date +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->country_rel_date); +} +static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); /* * Interrupt handlers for various ACM device responses */ @@ -332,9 +365,9 @@ static void acm_rx_tasklet(unsigned long _acm) if (!ACM_READY(acm)) return; - spin_lock(&acm->throttle_lock); + spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; - spin_unlock(&acm->throttle_lock); + spin_unlock_irqrestore(&acm->throttle_lock, flags); if (throttled) return; @@ -352,9 +385,9 @@ next_buffer: dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); tty_buffer_request_room(tty, buf->size); - spin_lock(&acm->throttle_lock); + spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; - spin_unlock(&acm->throttle_lock); + spin_unlock_irqrestore(&acm->throttle_lock, flags); if (!throttled) tty_insert_flip_string(tty, buf->base, buf->size); tty_flip_buffer_push(tty); @@ -514,6 +547,7 @@ static void acm_tty_unregister(struct acm *acm) usb_free_urb(acm->writeurb); for (i = 0; i < nr; i++) usb_free_urb(acm->ru[i].urb); + kfree(acm->country_codes); kfree(acm); } @@ -761,6 +795,7 @@ static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; + struct usb_cdc_country_functional_desc *cfd = NULL; char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -824,8 +859,9 @@ static int acm_probe (struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *) buffer; break; - case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ - break; /* for now we ignore it */ + case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + cfd = (struct usb_cdc_country_functional_desc *)buffer; + break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: @@ -983,6 +1019,34 @@ skip_normal_probe: goto alloc_fail7; } + usb_set_intfdata (intf, acm); + + i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); + if (i < 0) + goto alloc_fail8; + + if (cfd) { /* export the country data */ + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); + if (!acm->country_codes) + goto skip_countries; + acm->country_code_size = cfd->bLength - 4; + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); + acm->country_rel_date = cfd->iCountryCodeRelDate; + + i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + + i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + } + +skip_countries: usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -1006,9 +1070,10 @@ skip_normal_probe: tty_register_device(acm_tty_driver, minor, &control_interface->dev); acm_table[minor] = acm; - usb_set_intfdata (intf, acm); - return 0; + return 0; +alloc_fail8: + usb_free_urb(acm->writeurb); alloc_fail7: for (i = 0; i < num_rx_buf; i++) usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); @@ -1027,7 +1092,7 @@ alloc_fail: static void acm_disconnect(struct usb_interface *intf) { - struct acm *acm = usb_get_intfdata (intf); + struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); int i; @@ -1041,6 +1106,11 @@ static void acm_disconnect(struct usb_interface *intf) mutex_unlock(&open_mutex); return; } + if (acm->country_codes){ + device_remove_file(&intf->dev, &dev_attr_wCountryCodes); + device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + } + device_remove_file(&intf->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); |