diff options
Diffstat (limited to 'drivers/usb/serial/cypress_m8.c')
-rw-r--r-- | drivers/usb/serial/cypress_m8.c | 132 |
1 files changed, 101 insertions, 31 deletions
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 5de76efe1b37..e1173c1aee37 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c | |||
@@ -48,7 +48,6 @@ | |||
48 | /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */ | 48 | /* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */ |
49 | 49 | ||
50 | 50 | ||
51 | #include <linux/config.h> | ||
52 | #include <linux/kernel.h> | 51 | #include <linux/kernel.h> |
53 | #include <linux/errno.h> | 52 | #include <linux/errno.h> |
54 | #include <linux/init.h> | 53 | #include <linux/init.h> |
@@ -60,11 +59,11 @@ | |||
60 | #include <linux/moduleparam.h> | 59 | #include <linux/moduleparam.h> |
61 | #include <linux/spinlock.h> | 60 | #include <linux/spinlock.h> |
62 | #include <linux/usb.h> | 61 | #include <linux/usb.h> |
62 | #include <linux/usb/serial.h> | ||
63 | #include <linux/serial.h> | 63 | #include <linux/serial.h> |
64 | #include <linux/delay.h> | 64 | #include <linux/delay.h> |
65 | #include <asm/uaccess.h> | 65 | #include <asm/uaccess.h> |
66 | 66 | ||
67 | #include "usb-serial.h" | ||
68 | #include "cypress_m8.h" | 67 | #include "cypress_m8.h" |
69 | 68 | ||
70 | 69 | ||
@@ -130,6 +129,9 @@ struct cypress_private { | |||
130 | int cmd_ctrl; /* always set this to 1 before issuing a command */ | 129 | int cmd_ctrl; /* always set this to 1 before issuing a command */ |
131 | struct cypress_buf *buf; /* write buffer */ | 130 | struct cypress_buf *buf; /* write buffer */ |
132 | int write_urb_in_use; /* write urb in use indicator */ | 131 | int write_urb_in_use; /* write urb in use indicator */ |
132 | int write_urb_interval; /* interval to use for write urb */ | ||
133 | int read_urb_interval; /* interval to use for read urb */ | ||
134 | int comm_is_ok; /* true if communication is (still) ok */ | ||
133 | int termios_initialized; | 135 | int termios_initialized; |
134 | __u8 line_control; /* holds dtr / rts value */ | 136 | __u8 line_control; /* holds dtr / rts value */ |
135 | __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ | 137 | __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ |
@@ -169,6 +171,7 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, | |||
169 | static int cypress_chars_in_buffer (struct usb_serial_port *port); | 171 | static int cypress_chars_in_buffer (struct usb_serial_port *port); |
170 | static void cypress_throttle (struct usb_serial_port *port); | 172 | static void cypress_throttle (struct usb_serial_port *port); |
171 | static void cypress_unthrottle (struct usb_serial_port *port); | 173 | static void cypress_unthrottle (struct usb_serial_port *port); |
174 | static void cypress_set_dead (struct usb_serial_port *port); | ||
172 | static void cypress_read_int_callback (struct urb *urb, struct pt_regs *regs); | 175 | static void cypress_read_int_callback (struct urb *urb, struct pt_regs *regs); |
173 | static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs); | 176 | static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs); |
174 | /* baud helper functions */ | 177 | /* baud helper functions */ |
@@ -289,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
289 | 292 | ||
290 | priv = usb_get_serial_port_data(port); | 293 | priv = usb_get_serial_port_data(port); |
291 | 294 | ||
295 | if (!priv->comm_is_ok) | ||
296 | return -ENODEV; | ||
297 | |||
292 | switch(cypress_request_type) { | 298 | switch(cypress_request_type) { |
293 | case CYPRESS_SET_CONFIG: | 299 | case CYPRESS_SET_CONFIG: |
294 | 300 | ||
@@ -366,13 +372,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
366 | if (tries++ >= 3) | 372 | if (tries++ >= 3) |
367 | break; | 373 | break; |
368 | 374 | ||
369 | if (retval == EPIPE) | 375 | } while (retval != 8 && retval != -ENODEV); |
370 | usb_clear_halt(port->serial->dev, 0x00); | ||
371 | } while (retval != 8 && retval != ENODEV); | ||
372 | 376 | ||
373 | if (retval != 8) | 377 | if (retval != 8) { |
374 | err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); | 378 | err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); |
375 | else { | 379 | cypress_set_dead(port); |
380 | } else { | ||
376 | spin_lock_irqsave(&priv->lock, flags); | 381 | spin_lock_irqsave(&priv->lock, flags); |
377 | priv->baud_rate = new_baudrate; | 382 | priv->baud_rate = new_baudrate; |
378 | priv->cbr_mask = baud_mask; | 383 | priv->cbr_mask = baud_mask; |
@@ -393,12 +398,11 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
393 | if (tries++ >= 3) | 398 | if (tries++ >= 3) |
394 | break; | 399 | break; |
395 | 400 | ||
396 | if (retval == EPIPE) | 401 | } while (retval != 5 && retval != -ENODEV); |
397 | usb_clear_halt(port->serial->dev, 0x00); | ||
398 | } while (retval != 5 && retval != ENODEV); | ||
399 | 402 | ||
400 | if (retval != 5) { | 403 | if (retval != 5) { |
401 | err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval); | 404 | err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval); |
405 | cypress_set_dead(port); | ||
402 | return retval; | 406 | return retval; |
403 | } else { | 407 | } else { |
404 | spin_lock_irqsave(&priv->lock, flags); | 408 | spin_lock_irqsave(&priv->lock, flags); |
@@ -420,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
420 | } /* cypress_serial_control */ | 424 | } /* cypress_serial_control */ |
421 | 425 | ||
422 | 426 | ||
427 | static void cypress_set_dead(struct usb_serial_port *port) | ||
428 | { | ||
429 | struct cypress_private *priv = usb_get_serial_port_data(port); | ||
430 | unsigned long flags; | ||
431 | |||
432 | spin_lock_irqsave(&priv->lock, flags); | ||
433 | if (!priv->comm_is_ok) { | ||
434 | spin_unlock_irqrestore(&priv->lock, flags); | ||
435 | return; | ||
436 | } | ||
437 | priv->comm_is_ok = 0; | ||
438 | spin_unlock_irqrestore(&priv->lock, flags); | ||
439 | |||
440 | err("cypress_m8 suspending failing port %d - interval might be too short", | ||
441 | port->number); | ||
442 | } | ||
443 | |||
444 | |||
423 | /* given a baud mask, it will return integer baud on success */ | 445 | /* given a baud mask, it will return integer baud on success */ |
424 | static int mask_to_rate (unsigned mask) | 446 | static int mask_to_rate (unsigned mask) |
425 | { | 447 | { |
@@ -473,13 +495,15 @@ static unsigned rate_to_mask (int rate) | |||
473 | static int generic_startup (struct usb_serial *serial) | 495 | static int generic_startup (struct usb_serial *serial) |
474 | { | 496 | { |
475 | struct cypress_private *priv; | 497 | struct cypress_private *priv; |
498 | struct usb_serial_port *port = serial->port[0]; | ||
476 | 499 | ||
477 | dbg("%s - port %d", __FUNCTION__, serial->port[0]->number); | 500 | dbg("%s - port %d", __FUNCTION__, port->number); |
478 | 501 | ||
479 | priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); | 502 | priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL); |
480 | if (!priv) | 503 | if (!priv) |
481 | return -ENOMEM; | 504 | return -ENOMEM; |
482 | 505 | ||
506 | priv->comm_is_ok = !0; | ||
483 | spin_lock_init(&priv->lock); | 507 | spin_lock_init(&priv->lock); |
484 | priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); | 508 | priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); |
485 | if (priv->buf == NULL) { | 509 | if (priv->buf == NULL) { |
@@ -490,13 +514,24 @@ static int generic_startup (struct usb_serial *serial) | |||
490 | 514 | ||
491 | usb_reset_configuration (serial->dev); | 515 | usb_reset_configuration (serial->dev); |
492 | 516 | ||
493 | interval = 1; | ||
494 | priv->cmd_ctrl = 0; | 517 | priv->cmd_ctrl = 0; |
495 | priv->line_control = 0; | 518 | priv->line_control = 0; |
496 | priv->termios_initialized = 0; | 519 | priv->termios_initialized = 0; |
497 | priv->rx_flags = 0; | 520 | priv->rx_flags = 0; |
498 | priv->cbr_mask = B300; | 521 | priv->cbr_mask = B300; |
499 | usb_set_serial_port_data(serial->port[0], priv); | 522 | if (interval > 0) { |
523 | priv->write_urb_interval = interval; | ||
524 | priv->read_urb_interval = interval; | ||
525 | dbg("%s - port %d read & write intervals forced to %d", | ||
526 | __FUNCTION__,port->number,interval); | ||
527 | } else { | ||
528 | priv->write_urb_interval = port->interrupt_out_urb->interval; | ||
529 | priv->read_urb_interval = port->interrupt_in_urb->interval; | ||
530 | dbg("%s - port %d intervals: read=%d write=%d", | ||
531 | __FUNCTION__,port->number, | ||
532 | priv->read_urb_interval,priv->write_urb_interval); | ||
533 | } | ||
534 | usb_set_serial_port_data(port, priv); | ||
500 | 535 | ||
501 | return 0; | 536 | return 0; |
502 | } | 537 | } |
@@ -586,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) | |||
586 | 621 | ||
587 | dbg("%s - port %d", __FUNCTION__, port->number); | 622 | dbg("%s - port %d", __FUNCTION__, port->number); |
588 | 623 | ||
624 | if (!priv->comm_is_ok) | ||
625 | return -EIO; | ||
626 | |||
589 | /* clear halts before open */ | 627 | /* clear halts before open */ |
590 | usb_clear_halt(serial->dev, 0x81); | 628 | usb_clear_halt(serial->dev, 0x81); |
591 | usb_clear_halt(serial->dev, 0x02); | 629 | usb_clear_halt(serial->dev, 0x02); |
@@ -625,11 +663,12 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) | |||
625 | usb_fill_int_urb(port->interrupt_in_urb, serial->dev, | 663 | usb_fill_int_urb(port->interrupt_in_urb, serial->dev, |
626 | usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), | 664 | usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), |
627 | port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, | 665 | port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length, |
628 | cypress_read_int_callback, port, interval); | 666 | cypress_read_int_callback, port, priv->read_urb_interval); |
629 | result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); | 667 | result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); |
630 | 668 | ||
631 | if (result){ | 669 | if (result){ |
632 | dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); | 670 | dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result); |
671 | cypress_set_dead(port); | ||
633 | } | 672 | } |
634 | 673 | ||
635 | return result; | 674 | return result; |
@@ -734,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port) | |||
734 | struct cypress_private *priv = usb_get_serial_port_data(port); | 773 | struct cypress_private *priv = usb_get_serial_port_data(port); |
735 | unsigned long flags; | 774 | unsigned long flags; |
736 | 775 | ||
776 | if (!priv->comm_is_ok) | ||
777 | return; | ||
778 | |||
737 | dbg("%s - port %d", __FUNCTION__, port->number); | 779 | dbg("%s - port %d", __FUNCTION__, port->number); |
738 | dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size); | 780 | dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size); |
739 | 781 | ||
@@ -807,14 +849,16 @@ send: | |||
807 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size, | 849 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size, |
808 | port->interrupt_out_urb->transfer_buffer); | 850 | port->interrupt_out_urb->transfer_buffer); |
809 | 851 | ||
810 | port->interrupt_out_urb->transfer_buffer_length = actual_size; | 852 | usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev, |
811 | port->interrupt_out_urb->dev = port->serial->dev; | 853 | usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress), |
812 | port->interrupt_out_urb->interval = interval; | 854 | port->interrupt_out_buffer, port->interrupt_out_size, |
855 | cypress_write_int_callback, port, priv->write_urb_interval); | ||
813 | result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC); | 856 | result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC); |
814 | if (result) { | 857 | if (result) { |
815 | dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, | 858 | dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, |
816 | result); | 859 | result); |
817 | priv->write_urb_in_use = 0; | 860 | priv->write_urb_in_use = 0; |
861 | cypress_set_dead(port); | ||
818 | } | 862 | } |
819 | 863 | ||
820 | spin_lock_irqsave(&priv->lock, flags); | 864 | spin_lock_irqsave(&priv->lock, flags); |
@@ -1215,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port) | |||
1215 | priv->rx_flags = 0; | 1259 | priv->rx_flags = 0; |
1216 | spin_unlock_irqrestore(&priv->lock, flags); | 1260 | spin_unlock_irqrestore(&priv->lock, flags); |
1217 | 1261 | ||
1262 | if (!priv->comm_is_ok) | ||
1263 | return; | ||
1264 | |||
1218 | if (actually_throttled) { | 1265 | if (actually_throttled) { |
1219 | port->interrupt_in_urb->dev = port->serial->dev; | 1266 | port->interrupt_in_urb->dev = port->serial->dev; |
1220 | 1267 | ||
1221 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); | 1268 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); |
1222 | if (result) | 1269 | if (result) { |
1223 | dev_err(&port->dev, "%s - failed submitting read urb, " | 1270 | dev_err(&port->dev, "%s - failed submitting read urb, " |
1224 | "error %d\n", __FUNCTION__, result); | 1271 | "error %d\n", __FUNCTION__, result); |
1272 | cypress_set_dead(port); | ||
1273 | } | ||
1225 | } | 1274 | } |
1226 | } | 1275 | } |
1227 | 1276 | ||
@@ -1241,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs) | |||
1241 | 1290 | ||
1242 | dbg("%s - port %d", __FUNCTION__, port->number); | 1291 | dbg("%s - port %d", __FUNCTION__, port->number); |
1243 | 1292 | ||
1244 | if (urb->status) { | 1293 | switch (urb->status) { |
1245 | dbg("%s - nonzero read status received: %d", __FUNCTION__, | 1294 | case 0: /* success */ |
1246 | urb->status); | 1295 | break; |
1296 | case -ECONNRESET: | ||
1297 | case -ENOENT: | ||
1298 | case -ESHUTDOWN: | ||
1299 | /* precursor to disconnect so just go away */ | ||
1300 | return; | ||
1301 | case -EPIPE: | ||
1302 | usb_clear_halt(port->serial->dev,0x81); | ||
1303 | break; | ||
1304 | default: | ||
1305 | /* something ugly is going on... */ | ||
1306 | dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n", | ||
1307 | __FUNCTION__,urb->status); | ||
1308 | cypress_set_dead(port); | ||
1247 | return; | 1309 | return; |
1248 | } | 1310 | } |
1249 | 1311 | ||
@@ -1344,18 +1406,20 @@ continue_read: | |||
1344 | 1406 | ||
1345 | /* Continue trying to always read... unless the port has closed. */ | 1407 | /* Continue trying to always read... unless the port has closed. */ |
1346 | 1408 | ||
1347 | if (port->open_count > 0) { | 1409 | if (port->open_count > 0 && priv->comm_is_ok) { |
1348 | usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, | 1410 | usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, |
1349 | usb_rcvintpipe(port->serial->dev, | 1411 | usb_rcvintpipe(port->serial->dev, |
1350 | port->interrupt_in_endpointAddress), | 1412 | port->interrupt_in_endpointAddress), |
1351 | port->interrupt_in_urb->transfer_buffer, | 1413 | port->interrupt_in_urb->transfer_buffer, |
1352 | port->interrupt_in_urb->transfer_buffer_length, | 1414 | port->interrupt_in_urb->transfer_buffer_length, |
1353 | cypress_read_int_callback, port, interval); | 1415 | cypress_read_int_callback, port, priv->read_urb_interval); |
1354 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); | 1416 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); |
1355 | if (result) | 1417 | if (result) { |
1356 | dev_err(&urb->dev->dev, "%s - failed resubmitting " | 1418 | dev_err(&urb->dev->dev, "%s - failed resubmitting " |
1357 | "read urb, error %d\n", __FUNCTION__, | 1419 | "read urb, error %d\n", __FUNCTION__, |
1358 | result); | 1420 | result); |
1421 | cypress_set_dead(port); | ||
1422 | } | ||
1359 | } | 1423 | } |
1360 | 1424 | ||
1361 | return; | 1425 | return; |
@@ -1381,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs) | |||
1381 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | 1445 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); |
1382 | priv->write_urb_in_use = 0; | 1446 | priv->write_urb_in_use = 0; |
1383 | return; | 1447 | return; |
1384 | case -EPIPE: /* no break needed */ | 1448 | case -EPIPE: /* no break needed; clear halt and resubmit */ |
1449 | if (!priv->comm_is_ok) | ||
1450 | break; | ||
1385 | usb_clear_halt(port->serial->dev, 0x02); | 1451 | usb_clear_halt(port->serial->dev, 0x02); |
1386 | default: | ||
1387 | /* error in the urb, so we have to resubmit it */ | 1452 | /* error in the urb, so we have to resubmit it */ |
1388 | dbg("%s - Overflow in write", __FUNCTION__); | ||
1389 | dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); | 1453 | dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); |
1390 | port->interrupt_out_urb->transfer_buffer_length = 1; | 1454 | port->interrupt_out_urb->transfer_buffer_length = 1; |
1391 | port->interrupt_out_urb->dev = port->serial->dev; | 1455 | port->interrupt_out_urb->dev = port->serial->dev; |
1392 | result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); | 1456 | result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); |
1393 | if (result) | 1457 | if (!result) |
1394 | dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", | ||
1395 | __FUNCTION__, result); | ||
1396 | else | ||
1397 | return; | 1458 | return; |
1459 | dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", | ||
1460 | __FUNCTION__, result); | ||
1461 | cypress_set_dead(port); | ||
1462 | break; | ||
1463 | default: | ||
1464 | dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n", | ||
1465 | __FUNCTION__,urb->status); | ||
1466 | cypress_set_dead(port); | ||
1467 | break; | ||
1398 | } | 1468 | } |
1399 | 1469 | ||
1400 | priv->write_urb_in_use = 0; | 1470 | priv->write_urb_in_use = 0; |