diff options
Diffstat (limited to 'drivers/usb/serial/cypress_m8.c')
-rw-r--r-- | drivers/usb/serial/cypress_m8.c | 90 |
1 files changed, 75 insertions, 15 deletions
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 12a265c4a13b..e1173c1aee37 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c | |||
@@ -131,6 +131,7 @@ struct cypress_private { | |||
131 | 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 */ | 132 | int write_urb_interval; /* interval to use for write urb */ |
133 | int read_urb_interval; /* interval to use for read urb */ | 133 | int read_urb_interval; /* interval to use for read urb */ |
134 | int comm_is_ok; /* true if communication is (still) ok */ | ||
134 | int termios_initialized; | 135 | int termios_initialized; |
135 | __u8 line_control; /* holds dtr / rts value */ | 136 | __u8 line_control; /* holds dtr / rts value */ |
136 | __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 */ |
@@ -170,6 +171,7 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, | |||
170 | static int cypress_chars_in_buffer (struct usb_serial_port *port); | 171 | static int cypress_chars_in_buffer (struct usb_serial_port *port); |
171 | static void cypress_throttle (struct usb_serial_port *port); | 172 | static void cypress_throttle (struct usb_serial_port *port); |
172 | 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); | ||
173 | 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); |
174 | 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); |
175 | /* baud helper functions */ | 177 | /* baud helper functions */ |
@@ -290,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
290 | 292 | ||
291 | priv = usb_get_serial_port_data(port); | 293 | priv = usb_get_serial_port_data(port); |
292 | 294 | ||
295 | if (!priv->comm_is_ok) | ||
296 | return -ENODEV; | ||
297 | |||
293 | switch(cypress_request_type) { | 298 | switch(cypress_request_type) { |
294 | case CYPRESS_SET_CONFIG: | 299 | case CYPRESS_SET_CONFIG: |
295 | 300 | ||
@@ -369,9 +374,10 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
369 | 374 | ||
370 | } while (retval != 8 && retval != -ENODEV); | 375 | } while (retval != 8 && retval != -ENODEV); |
371 | 376 | ||
372 | if (retval != 8) | 377 | if (retval != 8) { |
373 | err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); | 378 | err("%s - failed sending serial line settings - %d", __FUNCTION__, retval); |
374 | else { | 379 | cypress_set_dead(port); |
380 | } else { | ||
375 | spin_lock_irqsave(&priv->lock, flags); | 381 | spin_lock_irqsave(&priv->lock, flags); |
376 | priv->baud_rate = new_baudrate; | 382 | priv->baud_rate = new_baudrate; |
377 | priv->cbr_mask = baud_mask; | 383 | priv->cbr_mask = baud_mask; |
@@ -396,6 +402,7 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
396 | 402 | ||
397 | if (retval != 5) { | 403 | if (retval != 5) { |
398 | 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); | ||
399 | return retval; | 406 | return retval; |
400 | } else { | 407 | } else { |
401 | spin_lock_irqsave(&priv->lock, flags); | 408 | spin_lock_irqsave(&priv->lock, flags); |
@@ -417,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m | |||
417 | } /* cypress_serial_control */ | 424 | } /* cypress_serial_control */ |
418 | 425 | ||
419 | 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 | |||
420 | /* given a baud mask, it will return integer baud on success */ | 445 | /* given a baud mask, it will return integer baud on success */ |
421 | static int mask_to_rate (unsigned mask) | 446 | static int mask_to_rate (unsigned mask) |
422 | { | 447 | { |
@@ -478,6 +503,7 @@ static int generic_startup (struct usb_serial *serial) | |||
478 | if (!priv) | 503 | if (!priv) |
479 | return -ENOMEM; | 504 | return -ENOMEM; |
480 | 505 | ||
506 | priv->comm_is_ok = !0; | ||
481 | spin_lock_init(&priv->lock); | 507 | spin_lock_init(&priv->lock); |
482 | priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); | 508 | priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE); |
483 | if (priv->buf == NULL) { | 509 | if (priv->buf == NULL) { |
@@ -595,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) | |||
595 | 621 | ||
596 | dbg("%s - port %d", __FUNCTION__, port->number); | 622 | dbg("%s - port %d", __FUNCTION__, port->number); |
597 | 623 | ||
624 | if (!priv->comm_is_ok) | ||
625 | return -EIO; | ||
626 | |||
598 | /* clear halts before open */ | 627 | /* clear halts before open */ |
599 | usb_clear_halt(serial->dev, 0x81); | 628 | usb_clear_halt(serial->dev, 0x81); |
600 | usb_clear_halt(serial->dev, 0x02); | 629 | usb_clear_halt(serial->dev, 0x02); |
@@ -639,6 +668,7 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) | |||
639 | 668 | ||
640 | if (result){ | 669 | if (result){ |
641 | 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); | ||
642 | } | 672 | } |
643 | 673 | ||
644 | return result; | 674 | return result; |
@@ -743,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port) | |||
743 | struct cypress_private *priv = usb_get_serial_port_data(port); | 773 | struct cypress_private *priv = usb_get_serial_port_data(port); |
744 | unsigned long flags; | 774 | unsigned long flags; |
745 | 775 | ||
776 | if (!priv->comm_is_ok) | ||
777 | return; | ||
778 | |||
746 | dbg("%s - port %d", __FUNCTION__, port->number); | 779 | dbg("%s - port %d", __FUNCTION__, port->number); |
747 | 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); |
748 | 781 | ||
@@ -825,6 +858,7 @@ send: | |||
825 | 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__, |
826 | result); | 859 | result); |
827 | priv->write_urb_in_use = 0; | 860 | priv->write_urb_in_use = 0; |
861 | cypress_set_dead(port); | ||
828 | } | 862 | } |
829 | 863 | ||
830 | spin_lock_irqsave(&priv->lock, flags); | 864 | spin_lock_irqsave(&priv->lock, flags); |
@@ -1225,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port) | |||
1225 | priv->rx_flags = 0; | 1259 | priv->rx_flags = 0; |
1226 | spin_unlock_irqrestore(&priv->lock, flags); | 1260 | spin_unlock_irqrestore(&priv->lock, flags); |
1227 | 1261 | ||
1262 | if (!priv->comm_is_ok) | ||
1263 | return; | ||
1264 | |||
1228 | if (actually_throttled) { | 1265 | if (actually_throttled) { |
1229 | port->interrupt_in_urb->dev = port->serial->dev; | 1266 | port->interrupt_in_urb->dev = port->serial->dev; |
1230 | 1267 | ||
1231 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); | 1268 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); |
1232 | if (result) | 1269 | if (result) { |
1233 | dev_err(&port->dev, "%s - failed submitting read urb, " | 1270 | dev_err(&port->dev, "%s - failed submitting read urb, " |
1234 | "error %d\n", __FUNCTION__, result); | 1271 | "error %d\n", __FUNCTION__, result); |
1272 | cypress_set_dead(port); | ||
1273 | } | ||
1235 | } | 1274 | } |
1236 | } | 1275 | } |
1237 | 1276 | ||
@@ -1251,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs) | |||
1251 | 1290 | ||
1252 | dbg("%s - port %d", __FUNCTION__, port->number); | 1291 | dbg("%s - port %d", __FUNCTION__, port->number); |
1253 | 1292 | ||
1254 | if (urb->status) { | 1293 | switch (urb->status) { |
1255 | dbg("%s - nonzero read status received: %d", __FUNCTION__, | 1294 | case 0: /* success */ |
1256 | 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); | ||
1257 | return; | 1309 | return; |
1258 | } | 1310 | } |
1259 | 1311 | ||
@@ -1354,7 +1406,7 @@ continue_read: | |||
1354 | 1406 | ||
1355 | /* Continue trying to always read... unless the port has closed. */ | 1407 | /* Continue trying to always read... unless the port has closed. */ |
1356 | 1408 | ||
1357 | if (port->open_count > 0) { | 1409 | if (port->open_count > 0 && priv->comm_is_ok) { |
1358 | usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, | 1410 | usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, |
1359 | usb_rcvintpipe(port->serial->dev, | 1411 | usb_rcvintpipe(port->serial->dev, |
1360 | port->interrupt_in_endpointAddress), | 1412 | port->interrupt_in_endpointAddress), |
@@ -1362,10 +1414,12 @@ continue_read: | |||
1362 | port->interrupt_in_urb->transfer_buffer_length, | 1414 | port->interrupt_in_urb->transfer_buffer_length, |
1363 | cypress_read_int_callback, port, priv->read_urb_interval); | 1415 | cypress_read_int_callback, port, priv->read_urb_interval); |
1364 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); | 1416 | result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); |
1365 | if (result) | 1417 | if (result) { |
1366 | dev_err(&urb->dev->dev, "%s - failed resubmitting " | 1418 | dev_err(&urb->dev->dev, "%s - failed resubmitting " |
1367 | "read urb, error %d\n", __FUNCTION__, | 1419 | "read urb, error %d\n", __FUNCTION__, |
1368 | result); | 1420 | result); |
1421 | cypress_set_dead(port); | ||
1422 | } | ||
1369 | } | 1423 | } |
1370 | 1424 | ||
1371 | return; | 1425 | return; |
@@ -1391,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs) | |||
1391 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | 1445 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); |
1392 | priv->write_urb_in_use = 0; | 1446 | priv->write_urb_in_use = 0; |
1393 | return; | 1447 | return; |
1394 | case -EPIPE: /* no break needed */ | 1448 | case -EPIPE: /* no break needed; clear halt and resubmit */ |
1449 | if (!priv->comm_is_ok) | ||
1450 | break; | ||
1395 | usb_clear_halt(port->serial->dev, 0x02); | 1451 | usb_clear_halt(port->serial->dev, 0x02); |
1396 | default: | ||
1397 | /* error in the urb, so we have to resubmit it */ | 1452 | /* error in the urb, so we have to resubmit it */ |
1398 | dbg("%s - Overflow in write", __FUNCTION__); | ||
1399 | dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); | 1453 | dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); |
1400 | port->interrupt_out_urb->transfer_buffer_length = 1; | 1454 | port->interrupt_out_urb->transfer_buffer_length = 1; |
1401 | port->interrupt_out_urb->dev = port->serial->dev; | 1455 | port->interrupt_out_urb->dev = port->serial->dev; |
1402 | result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); | 1456 | result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); |
1403 | if (result) | 1457 | if (!result) |
1404 | dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", | ||
1405 | __FUNCTION__, result); | ||
1406 | else | ||
1407 | 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; | ||
1408 | } | 1468 | } |
1409 | 1469 | ||
1410 | priv->write_urb_in_use = 0; | 1470 | priv->write_urb_in_use = 0; |