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>
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 31ae661..14de3b1 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -212,7 +212,41 @@
}
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
*/
@@ -514,6 +548,7 @@
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 +796,7 @@
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 +860,9 @@
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 +1020,34 @@
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 +1071,10 @@
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 +1093,7 @@
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 +1107,11 @@
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);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 1bcaea3..09f7765 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -91,6 +91,9 @@
struct urb *ctrlurb, *writeurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
+ u8 *country_codes; /* country codes from device */
+ unsigned int country_code_size; /* size of this buffer */
+ unsigned int country_rel_date; /* release date of version */
struct acm_wb wb[ACM_NW];
struct acm_ru ru[ACM_NR];
struct acm_rb rb[ACM_NR];