diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 57 |
1 files changed, 28 insertions, 29 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5c8f58114677..519a77ba214c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -46,6 +46,7 @@ #include <linux/usb/cdc.h> #include <asm/byteorder.h> #include <asm/unaligned.h> +#include <linux/idr.h> #include <linux/list.h> #include "cdc-acm.h" @@ -56,27 +57,27 @@ static struct usb_driver acm_driver; static struct tty_driver *acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS]; -static DEFINE_MUTEX(acm_table_lock); +static DEFINE_IDR(acm_minors); +static DEFINE_MUTEX(acm_minors_lock); static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old); /* - * acm_table accessors + * acm_minors accessors */ /* - * Look up an ACM structure by index. If found and not disconnected, increment + * Look up an ACM structure by minor. If found and not disconnected, increment * its refcount and return it with its mutex held. */ -static struct acm *acm_get_by_index(unsigned index) +static struct acm *acm_get_by_minor(unsigned int minor) { struct acm *acm; - mutex_lock(&acm_table_lock); - acm = acm_table[index]; + mutex_lock(&acm_minors_lock); + acm = idr_find(&acm_minors, minor); if (acm) { mutex_lock(&acm->mutex); if (acm->disconnected) { @@ -87,7 +88,7 @@ static struct acm *acm_get_by_index(unsigned index) mutex_unlock(&acm->mutex); } } - mutex_unlock(&acm_table_lock); + mutex_unlock(&acm_minors_lock); return acm; } @@ -98,14 +99,9 @@ static int acm_alloc_minor(struct acm *acm) { int minor; - mutex_lock(&acm_table_lock); - for (minor = 0; minor < ACM_TTY_MINORS; minor++) { - if (!acm_table[minor]) { - acm_table[minor] = acm; - break; - } - } - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&acm_minors_lock); return minor; } @@ -113,9 +109,9 @@ static int acm_alloc_minor(struct acm *acm) /* Release the minor number associated with 'acm'. */ static void acm_release_minor(struct acm *acm) { - mutex_lock(&acm_table_lock); - acm_table[acm->minor] = NULL; - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + idr_remove(&acm_minors, acm->minor); + mutex_unlock(&acm_minors_lock); } /* @@ -497,7 +493,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) dev_dbg(tty->dev, "%s\n", __func__); - acm = acm_get_by_index(tty->index); + acm = acm_get_by_minor(tty->index); if (!acm) return -ENODEV; @@ -1267,12 +1263,9 @@ skip_normal_probe: != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { - struct usb_interface *t; dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; + swap(control_interface, data_interface); } else { return -EINVAL; } @@ -1301,12 +1294,9 @@ skip_normal_probe: /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ - struct usb_endpoint_descriptor *t; dev_dbg(&intf->dev, "The data interface has switched endpoints\n"); - t = epread; - epread = epwrite; - epwrite = t; + swap(epread, epwrite); } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); @@ -1316,7 +1306,7 @@ made_compressed_probe: goto alloc_fail; minor = acm_alloc_minor(acm); - if (minor == ACM_TTY_MINORS) { + if (minor < 0) { dev_err(&intf->dev, "no more free acm devices\n"); kfree(acm); return -ENODEV; @@ -1477,6 +1467,11 @@ skip_countries: goto alloc_fail8; } + if (quirks & CLEAR_HALT_CONDITIONS) { + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); + } + return 0; alloc_fail8: if (acm->country_codes) { @@ -1756,6 +1751,10 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ + .driver_info = CLEAR_HALT_CONDITIONS, + }, + /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command * information below. The second is 'vendor-specific' but |