aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial/cp210x.c
diff options
context:
space:
mode:
authorPreston Fick <preston.fick@silabs.com>2012-02-24 14:42:39 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-02 19:19:16 -0500
commita5360a53a7ccad5ed9ccef210b94fef13c6e5529 (patch)
treecc6980e42afb598aef2f8782832c6ac10832a973 /drivers/usb/serial/cp210x.c
parent3d71769014c55e05b2342b6d9c1464f7fb383322 (diff)
usb: cp210x: Update to support CP2105 and multiple interface devices
This patch updates the cp210x driver to support CP210x multiple interface devices devices from Silicon Labs. The existing driver always sends control requests to interface 0, which is hardcoded in the usb_control_msg function calls. This only allows for single interface devices to be used, and causes a bug when using ports on an interface other than 0 in the multiple interface devices. Here are the changes included in this patch: - Updated the device list to contain the Silicon Labs factory default VID/PID for multiple interface CP210x devices - Created a cp210x_port_private struct created for each port on startup, this struct holds the interface number - Added a cp210x_release function to clean up the cp210x_port_private memory created on startup - Modified usb_get_config and usb_set_config to get a pointer to the cp210x_port_private struct, and use the interface number there in the usb_control_message wIndex param Signed-off-by: Preston Fick <preston.fick@silabs.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial/cp210x.c')
-rw-r--r--drivers/usb/serial/cp210x.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 651b2aa13486..0310e2df59f5 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -49,6 +49,7 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port,
49 unsigned int, unsigned int); 49 unsigned int, unsigned int);
50static void cp210x_break_ctl(struct tty_struct *, int); 50static void cp210x_break_ctl(struct tty_struct *, int);
51static int cp210x_startup(struct usb_serial *); 51static int cp210x_startup(struct usb_serial *);
52static void cp210x_release(struct usb_serial *);
52static void cp210x_dtr_rts(struct usb_serial_port *p, int on); 53static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
53 54
54static bool debug; 55static bool debug;
@@ -121,6 +122,8 @@ static const struct usb_device_id id_table[] = {
121 { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ 122 { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
122 { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ 123 { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
123 { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ 124 { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
125 { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
126 { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */
124 { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ 127 { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
125 { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ 128 { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
126 { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ 129 { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
@@ -149,6 +152,10 @@ static const struct usb_device_id id_table[] = {
149 152
150MODULE_DEVICE_TABLE(usb, id_table); 153MODULE_DEVICE_TABLE(usb, id_table);
151 154
155struct cp210x_port_private {
156 __u8 bInterfaceNumber;
157};
158
152static struct usb_driver cp210x_driver = { 159static struct usb_driver cp210x_driver = {
153 .name = "cp210x", 160 .name = "cp210x",
154 .probe = usb_serial_probe, 161 .probe = usb_serial_probe,
@@ -172,6 +179,7 @@ static struct usb_serial_driver cp210x_device = {
172 .tiocmget = cp210x_tiocmget, 179 .tiocmget = cp210x_tiocmget,
173 .tiocmset = cp210x_tiocmset, 180 .tiocmset = cp210x_tiocmset,
174 .attach = cp210x_startup, 181 .attach = cp210x_startup,
182 .release = cp210x_release,
175 .dtr_rts = cp210x_dtr_rts 183 .dtr_rts = cp210x_dtr_rts
176}; 184};
177 185
@@ -263,6 +271,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
263 unsigned int *data, int size) 271 unsigned int *data, int size)
264{ 272{
265 struct usb_serial *serial = port->serial; 273 struct usb_serial *serial = port->serial;
274 struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
266 __le32 *buf; 275 __le32 *buf;
267 int result, i, length; 276 int result, i, length;
268 277
@@ -278,7 +287,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
278 /* Issue the request, attempting to read 'size' bytes */ 287 /* Issue the request, attempting to read 'size' bytes */
279 result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 288 result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
280 request, REQTYPE_DEVICE_TO_HOST, 0x0000, 289 request, REQTYPE_DEVICE_TO_HOST, 0x0000,
281 0, buf, size, 300); 290 port_priv->bInterfaceNumber, buf, size, 300);
282 291
283 /* Convert data into an array of integers */ 292 /* Convert data into an array of integers */
284 for (i = 0; i < length; i++) 293 for (i = 0; i < length; i++)
@@ -309,6 +318,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
309 unsigned int *data, int size) 318 unsigned int *data, int size)
310{ 319{
311 struct usb_serial *serial = port->serial; 320 struct usb_serial *serial = port->serial;
321 struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
312 __le32 *buf; 322 __le32 *buf;
313 int result, i, length; 323 int result, i, length;
314 324
@@ -330,12 +340,12 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
330 result = usb_control_msg(serial->dev, 340 result = usb_control_msg(serial->dev,
331 usb_sndctrlpipe(serial->dev, 0), 341 usb_sndctrlpipe(serial->dev, 0),
332 request, REQTYPE_HOST_TO_DEVICE, 0x0000, 342 request, REQTYPE_HOST_TO_DEVICE, 0x0000,
333 0, buf, size, 300); 343 port_priv->bInterfaceNumber, buf, size, 300);
334 } else { 344 } else {
335 result = usb_control_msg(serial->dev, 345 result = usb_control_msg(serial->dev,
336 usb_sndctrlpipe(serial->dev, 0), 346 usb_sndctrlpipe(serial->dev, 0),
337 request, REQTYPE_HOST_TO_DEVICE, data[0], 347 request, REQTYPE_HOST_TO_DEVICE, data[0],
338 0, NULL, 0, 300); 348 port_priv->bInterfaceNumber, NULL, 0, 300);
339 } 349 }
340 350
341 kfree(buf); 351 kfree(buf);
@@ -845,11 +855,39 @@ static void cp210x_break_ctl (struct tty_struct *tty, int break_state)
845 855
846static int cp210x_startup(struct usb_serial *serial) 856static int cp210x_startup(struct usb_serial *serial)
847{ 857{
858 struct cp210x_port_private *port_priv;
859 int i;
860
848 /* cp210x buffers behave strangely unless device is reset */ 861 /* cp210x buffers behave strangely unless device is reset */
849 usb_reset_device(serial->dev); 862 usb_reset_device(serial->dev);
863
864 for (i = 0; i < serial->num_ports; i++) {
865 port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
866 if (!port_priv)
867 return -ENOMEM;
868
869 memset(port_priv, 0x00, sizeof(*port_priv));
870 port_priv->bInterfaceNumber =
871 serial->interface->cur_altsetting->desc.bInterfaceNumber;
872
873 usb_set_serial_port_data(serial->port[i], port_priv);
874 }
875
850 return 0; 876 return 0;
851} 877}
852 878
879static void cp210x_release(struct usb_serial *serial)
880{
881 struct cp210x_port_private *port_priv;
882 int i;
883
884 for (i = 0; i < serial->num_ports; i++) {
885 port_priv = usb_get_serial_port_data(serial->port[i]);
886 kfree(port_priv);
887 usb_set_serial_port_data(serial->port[i], NULL);
888 }
889}
890
853module_usb_serial_driver(cp210x_driver, serial_drivers); 891module_usb_serial_driver(cp210x_driver, serial_drivers);
854 892
855MODULE_DESCRIPTION(DRIVER_DESC); 893MODULE_DESCRIPTION(DRIVER_DESC);