From ca79b7b4158cbf32625793a1fc1d59ac46d44197 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 12 Feb 2007 08:41:35 +0100 Subject: USB: cdc-acm: fix incorrect throtteling, make set_control optional this is Joris' fixes reshuffelled and features renamed as David requested. - acm_set_control is not mandatory, honour that - throtteling is reset upon open - throtteling is read consistently when processing input data Signed-off-by: Joris van Rantwijk Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 98199628e394..023cf5d45a9d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -326,10 +326,16 @@ static void acm_rx_tasklet(unsigned long _acm) struct tty_struct *tty = acm->tty; struct acm_ru *rcv; unsigned long flags; - int i = 0; + unsigned char throttled; dbg("Entering acm_rx_tasklet"); - if (!ACM_READY(acm) || acm->throttle) + if (!ACM_READY(acm)) + return; + + spin_lock(&acm->throttle_lock); + throttled = acm->throttle; + spin_unlock(&acm->throttle_lock); + if (throttled) return; next_buffer: @@ -346,22 +352,20 @@ next_buffer: dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); tty_buffer_request_room(tty, buf->size); - if (!acm->throttle) + spin_lock(&acm->throttle_lock); + throttled = acm->throttle; + spin_unlock(&acm->throttle_lock); + if (!throttled) tty_insert_flip_string(tty, buf->base, buf->size); tty_flip_buffer_push(tty); - spin_lock(&acm->throttle_lock); - if (acm->throttle) { - dbg("Throtteling noticed"); - memmove(buf->base, buf->base + i, buf->size - i); - buf->size -= i; - spin_unlock(&acm->throttle_lock); + if (throttled) { + dbg("Throttling noticed"); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->filled_read_bufs); spin_unlock_irqrestore(&acm->read_lock, flags); return; } - spin_unlock(&acm->throttle_lock); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->spare_read_bufs); @@ -467,7 +471,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) goto bail_out; } - if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) + if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && + (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; INIT_LIST_HEAD(&acm->spare_read_urbs); @@ -480,6 +485,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) list_add(&(acm->rb[i].list), &acm->spare_read_bufs); } + acm->throttle = 0; + tasklet_schedule(&acm->urb_task); done: -- cgit v1.2.1 From 9be8456c00c5bd603b933e6e9d82041e8b32c401 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 12 Feb 2007 08:50:03 +0100 Subject: USB: quirky device for cdc-acm here's a quirklist entry reported by Stephen Murphy. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 023cf5d45a9d..d38a25f36ea5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1099,6 +1099,10 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, + /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER) }, -- cgit v1.2.1 From 834dbca5b6b79ddb7cf56001ea7b6d4481fdf1e7 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 6 Mar 2007 10:47:04 +0100 Subject: USB: fix spinlock recursion in cdc-acm.c this fixes the spinlock recursion issue. The older fix was incomplete. Signed-off-by: Oliver Neukum Acked-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d38a25f36ea5..31ae661e586a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -332,9 +332,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 +352,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); -- cgit v1.2.1 From 5ec71db59ac07eeac89a54b35a90c181bbd3c445 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 16 Mar 2007 15:53:07 -0400 Subject: usblp: quirk flag and device entry for Seiko Epson M129C printer This patch (as872) adds a device table entry and a new quirk flag to the usblp driver for the Seiko Epson Receipt printer. This printer returns Vendor-Specific values for bInterfaceClass and bInterfaceSubClass, but the bInterfaceProtocol value is valid and it works with usblp. The new quirks flag tells the driver to ignore the Class and SubClass values in the interface descriptor. Signed-off-by: Alan Stern Cc: Vojtech Pavlik Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 63e50a1f1396..6584cf00f7f3 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -202,6 +202,7 @@ struct quirk_printer_struct { #define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */ #define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */ +#define USBLP_QUIRK_BAD_CLASS 0x4 /* descriptor uses vendor-specific Class or SubClass */ static const struct quirk_printer_struct quirk_printers[] = { { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */ @@ -218,6 +219,7 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut */ + { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */ { 0, 0 } }; @@ -1048,7 +1050,8 @@ static int usblp_select_alts(struct usblp *usblp) ifd = &if_alt->altsetting[i]; if (ifd->desc.bInterfaceClass != 7 || ifd->desc.bInterfaceSubClass != 1) - continue; + if (!(usblp->quirks & USBLP_QUIRK_BAD_CLASS)) + continue; if (ifd->desc.bInterfaceProtocol < USBLP_FIRST_PROTOCOL || ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL) @@ -1232,6 +1235,7 @@ static struct usb_device_id usblp_ids [] = { { USB_INTERFACE_INFO(7, 1, 1) }, { USB_INTERFACE_INFO(7, 1, 2) }, { USB_INTERFACE_INFO(7, 1, 3) }, + { USB_DEVICE(0x04b8, 0x0202) }, /* Seiko Epson Receipt Printer M129C */ { } /* Terminating entry */ }; -- cgit v1.2.1 From c4cabd28c73116716dcfd0d5f91414b48c0cf5ce Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 27 Feb 2007 15:28:55 +0100 Subject: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 81 ++++++++++++++++++++++++++++++++++++++++++--- drivers/usb/class/cdc-acm.h | 3 ++ 2 files changed, 79 insertions(+), 5 deletions(-) (limited to 'drivers/usb/class') 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) } 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 @@ 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 +796,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 +860,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 +1020,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 +1071,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 +1093,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 +1107,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); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 1bcaea32cfc1..09f7765dbf8d 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -91,6 +91,9 @@ struct acm { 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]; -- cgit v1.2.1