aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-acm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r--drivers/usb/class/cdc-acm.c58
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
57static struct usb_driver acm_driver; 58static struct usb_driver acm_driver;
58static struct tty_driver *acm_tty_driver; 59static struct tty_driver *acm_tty_driver;
59static struct acm *acm_table[ACM_TTY_MINORS];
60 60
61static DEFINE_MUTEX(acm_table_lock); 61static DEFINE_IDR(acm_minors);
62static DEFINE_MUTEX(acm_minors_lock);
62 63
63static void acm_tty_set_termios(struct tty_struct *tty, 64static 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 */
74static struct acm *acm_get_by_index(unsigned index) 75static 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'. */
114static void acm_release_minor(struct acm *acm) 110static 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 }
1311made_compressed_probe: 1301made_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;
1481alloc_fail8: 1476alloc_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
1950module_init(acm_init); 1950module_init(acm_init);