diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 58 |
1 files changed, 29 insertions, 29 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5c8f58114677..b30e7423549b 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/usb/cdc.h> | 46 | #include <linux/usb/cdc.h> |
47 | #include <asm/byteorder.h> | 47 | #include <asm/byteorder.h> |
48 | #include <asm/unaligned.h> | 48 | #include <asm/unaligned.h> |
49 | #include <linux/idr.h> | ||
49 | #include <linux/list.h> | 50 | #include <linux/list.h> |
50 | 51 | ||
51 | #include "cdc-acm.h" | 52 | #include "cdc-acm.h" |
@@ -56,27 +57,27 @@ | |||
56 | 57 | ||
57 | static struct usb_driver acm_driver; | 58 | static struct usb_driver acm_driver; |
58 | static struct tty_driver *acm_tty_driver; | 59 | static struct tty_driver *acm_tty_driver; |
59 | static struct acm *acm_table[ACM_TTY_MINORS]; | ||
60 | 60 | ||
61 | static DEFINE_MUTEX(acm_table_lock); | 61 | static DEFINE_IDR(acm_minors); |
62 | static DEFINE_MUTEX(acm_minors_lock); | ||
62 | 63 | ||
63 | static void acm_tty_set_termios(struct tty_struct *tty, | 64 | static void acm_tty_set_termios(struct tty_struct *tty, |
64 | struct ktermios *termios_old); | 65 | struct ktermios *termios_old); |
65 | 66 | ||
66 | /* | 67 | /* |
67 | * acm_table accessors | 68 | * acm_minors accessors |
68 | */ | 69 | */ |
69 | 70 | ||
70 | /* | 71 | /* |
71 | * Look up an ACM structure by index. If found and not disconnected, increment | 72 | * Look up an ACM structure by minor. If found and not disconnected, increment |
72 | * its refcount and return it with its mutex held. | 73 | * its refcount and return it with its mutex held. |
73 | */ | 74 | */ |
74 | static struct acm *acm_get_by_index(unsigned index) | 75 | static struct acm *acm_get_by_minor(unsigned int minor) |
75 | { | 76 | { |
76 | struct acm *acm; | 77 | struct acm *acm; |
77 | 78 | ||
78 | mutex_lock(&acm_table_lock); | 79 | mutex_lock(&acm_minors_lock); |
79 | acm = acm_table[index]; | 80 | acm = idr_find(&acm_minors, minor); |
80 | if (acm) { | 81 | if (acm) { |
81 | mutex_lock(&acm->mutex); | 82 | mutex_lock(&acm->mutex); |
82 | if (acm->disconnected) { | 83 | if (acm->disconnected) { |
@@ -87,7 +88,7 @@ static struct acm *acm_get_by_index(unsigned index) | |||
87 | mutex_unlock(&acm->mutex); | 88 | mutex_unlock(&acm->mutex); |
88 | } | 89 | } |
89 | } | 90 | } |
90 | mutex_unlock(&acm_table_lock); | 91 | mutex_unlock(&acm_minors_lock); |
91 | return acm; | 92 | return acm; |
92 | } | 93 | } |
93 | 94 | ||
@@ -98,14 +99,9 @@ static int acm_alloc_minor(struct acm *acm) | |||
98 | { | 99 | { |
99 | int minor; | 100 | int minor; |
100 | 101 | ||
101 | mutex_lock(&acm_table_lock); | 102 | mutex_lock(&acm_minors_lock); |
102 | for (minor = 0; minor < ACM_TTY_MINORS; minor++) { | 103 | minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); |
103 | if (!acm_table[minor]) { | 104 | mutex_unlock(&acm_minors_lock); |
104 | acm_table[minor] = acm; | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | mutex_unlock(&acm_table_lock); | ||
109 | 105 | ||
110 | return minor; | 106 | return minor; |
111 | } | 107 | } |
@@ -113,9 +109,9 @@ static int acm_alloc_minor(struct acm *acm) | |||
113 | /* Release the minor number associated with 'acm'. */ | 109 | /* Release the minor number associated with 'acm'. */ |
114 | static void acm_release_minor(struct acm *acm) | 110 | static void acm_release_minor(struct acm *acm) |
115 | { | 111 | { |
116 | mutex_lock(&acm_table_lock); | 112 | mutex_lock(&acm_minors_lock); |
117 | acm_table[acm->minor] = NULL; | 113 | idr_remove(&acm_minors, acm->minor); |
118 | mutex_unlock(&acm_table_lock); | 114 | mutex_unlock(&acm_minors_lock); |
119 | } | 115 | } |
120 | 116 | ||
121 | /* | 117 | /* |
@@ -497,7 +493,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) | |||
497 | 493 | ||
498 | dev_dbg(tty->dev, "%s\n", __func__); | 494 | dev_dbg(tty->dev, "%s\n", __func__); |
499 | 495 | ||
500 | acm = acm_get_by_index(tty->index); | 496 | acm = acm_get_by_minor(tty->index); |
501 | if (!acm) | 497 | if (!acm) |
502 | return -ENODEV; | 498 | return -ENODEV; |
503 | 499 | ||
@@ -1267,12 +1263,9 @@ skip_normal_probe: | |||
1267 | != CDC_DATA_INTERFACE_TYPE) { | 1263 | != CDC_DATA_INTERFACE_TYPE) { |
1268 | if (control_interface->cur_altsetting->desc.bInterfaceClass | 1264 | if (control_interface->cur_altsetting->desc.bInterfaceClass |
1269 | == CDC_DATA_INTERFACE_TYPE) { | 1265 | == CDC_DATA_INTERFACE_TYPE) { |
1270 | struct usb_interface *t; | ||
1271 | dev_dbg(&intf->dev, | 1266 | dev_dbg(&intf->dev, |
1272 | "Your device has switched interfaces.\n"); | 1267 | "Your device has switched interfaces.\n"); |
1273 | t = control_interface; | 1268 | swap(control_interface, data_interface); |
1274 | control_interface = data_interface; | ||
1275 | data_interface = t; | ||
1276 | } else { | 1269 | } else { |
1277 | return -EINVAL; | 1270 | return -EINVAL; |
1278 | } | 1271 | } |
@@ -1301,12 +1294,9 @@ skip_normal_probe: | |||
1301 | /* workaround for switched endpoints */ | 1294 | /* workaround for switched endpoints */ |
1302 | if (!usb_endpoint_dir_in(epread)) { | 1295 | if (!usb_endpoint_dir_in(epread)) { |
1303 | /* descriptors are swapped */ | 1296 | /* descriptors are swapped */ |
1304 | struct usb_endpoint_descriptor *t; | ||
1305 | dev_dbg(&intf->dev, | 1297 | dev_dbg(&intf->dev, |
1306 | "The data interface has switched endpoints\n"); | 1298 | "The data interface has switched endpoints\n"); |
1307 | t = epread; | 1299 | swap(epread, epwrite); |
1308 | epread = epwrite; | ||
1309 | epwrite = t; | ||
1310 | } | 1300 | } |
1311 | made_compressed_probe: | 1301 | made_compressed_probe: |
1312 | dev_dbg(&intf->dev, "interfaces are valid\n"); | 1302 | dev_dbg(&intf->dev, "interfaces are valid\n"); |
@@ -1316,7 +1306,7 @@ made_compressed_probe: | |||
1316 | goto alloc_fail; | 1306 | goto alloc_fail; |
1317 | 1307 | ||
1318 | minor = acm_alloc_minor(acm); | 1308 | minor = acm_alloc_minor(acm); |
1319 | if (minor == ACM_TTY_MINORS) { | 1309 | if (minor < 0) { |
1320 | dev_err(&intf->dev, "no more free acm devices\n"); | 1310 | dev_err(&intf->dev, "no more free acm devices\n"); |
1321 | kfree(acm); | 1311 | kfree(acm); |
1322 | return -ENODEV; | 1312 | return -ENODEV; |
@@ -1477,6 +1467,11 @@ skip_countries: | |||
1477 | goto alloc_fail8; | 1467 | goto alloc_fail8; |
1478 | } | 1468 | } |
1479 | 1469 | ||
1470 | if (quirks & CLEAR_HALT_CONDITIONS) { | ||
1471 | usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); | ||
1472 | usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); | ||
1473 | } | ||
1474 | |||
1480 | return 0; | 1475 | return 0; |
1481 | alloc_fail8: | 1476 | alloc_fail8: |
1482 | if (acm->country_codes) { | 1477 | if (acm->country_codes) { |
@@ -1756,6 +1751,10 @@ static const struct usb_device_id acm_ids[] = { | |||
1756 | .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ | 1751 | .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ |
1757 | }, | 1752 | }, |
1758 | 1753 | ||
1754 | { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ | ||
1755 | .driver_info = CLEAR_HALT_CONDITIONS, | ||
1756 | }, | ||
1757 | |||
1759 | /* Nokia S60 phones expose two ACM channels. The first is | 1758 | /* Nokia S60 phones expose two ACM channels. The first is |
1760 | * a modem and is picked up by the standard AT-command | 1759 | * a modem and is picked up by the standard AT-command |
1761 | * information below. The second is 'vendor-specific' but | 1760 | * information below. The second is 'vendor-specific' but |
@@ -1945,6 +1944,7 @@ static void __exit acm_exit(void) | |||
1945 | usb_deregister(&acm_driver); | 1944 | usb_deregister(&acm_driver); |
1946 | tty_unregister_driver(acm_tty_driver); | 1945 | tty_unregister_driver(acm_tty_driver); |
1947 | put_tty_driver(acm_tty_driver); | 1946 | put_tty_driver(acm_tty_driver); |
1947 | idr_destroy(&acm_minors); | ||
1948 | } | 1948 | } |
1949 | 1949 | ||
1950 | module_init(acm_init); | 1950 | module_init(acm_init); |