diff options
Diffstat (limited to 'drivers/usb/serial/option.c')
-rw-r--r-- | drivers/usb/serial/option.c | 34 |
1 files changed, 25 insertions, 9 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5d3999e3ff61..84c12b5f1271 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/tty.h> | 38 | #include <linux/tty.h> |
39 | #include <linux/tty_flip.h> | 39 | #include <linux/tty_flip.h> |
40 | #include <linux/module.h> | 40 | #include <linux/module.h> |
41 | #include <linux/bitops.h> | ||
41 | #include <linux/usb.h> | 42 | #include <linux/usb.h> |
42 | #include <linux/usb/serial.h> | 43 | #include <linux/usb/serial.h> |
43 | 44 | ||
@@ -240,6 +241,7 @@ struct option_port_private { | |||
240 | /* Output endpoints and buffer for this port */ | 241 | /* Output endpoints and buffer for this port */ |
241 | struct urb *out_urbs[N_OUT_URB]; | 242 | struct urb *out_urbs[N_OUT_URB]; |
242 | char out_buffer[N_OUT_URB][OUT_BUFLEN]; | 243 | char out_buffer[N_OUT_URB][OUT_BUFLEN]; |
244 | unsigned long out_busy; /* Bit vector of URBs in use */ | ||
243 | 245 | ||
244 | /* Settings for the port */ | 246 | /* Settings for the port */ |
245 | int rts_state; /* Handshaking pins (outputs) */ | 247 | int rts_state; /* Handshaking pins (outputs) */ |
@@ -370,7 +372,7 @@ static int option_write(struct usb_serial_port *port, | |||
370 | todo = OUT_BUFLEN; | 372 | todo = OUT_BUFLEN; |
371 | 373 | ||
372 | this_urb = portdata->out_urbs[i]; | 374 | this_urb = portdata->out_urbs[i]; |
373 | if (this_urb->status == -EINPROGRESS) { | 375 | if (test_and_set_bit(i, &portdata->out_busy)) { |
374 | if (time_before(jiffies, | 376 | if (time_before(jiffies, |
375 | portdata->tx_start_time[i] + 10 * HZ)) | 377 | portdata->tx_start_time[i] + 10 * HZ)) |
376 | continue; | 378 | continue; |
@@ -394,6 +396,7 @@ static int option_write(struct usb_serial_port *port, | |||
394 | dbg("usb_submit_urb %p (write bulk) failed " | 396 | dbg("usb_submit_urb %p (write bulk) failed " |
395 | "(%d, has %d)", this_urb, | 397 | "(%d, has %d)", this_urb, |
396 | err, this_urb->status); | 398 | err, this_urb->status); |
399 | clear_bit(i, &portdata->out_busy); | ||
397 | continue; | 400 | continue; |
398 | } | 401 | } |
399 | portdata->tx_start_time[i] = jiffies; | 402 | portdata->tx_start_time[i] = jiffies; |
@@ -413,15 +416,16 @@ static void option_indat_callback(struct urb *urb) | |||
413 | struct usb_serial_port *port; | 416 | struct usb_serial_port *port; |
414 | struct tty_struct *tty; | 417 | struct tty_struct *tty; |
415 | unsigned char *data = urb->transfer_buffer; | 418 | unsigned char *data = urb->transfer_buffer; |
419 | int status = urb->status; | ||
416 | 420 | ||
417 | dbg("%s: %p", __FUNCTION__, urb); | 421 | dbg("%s: %p", __FUNCTION__, urb); |
418 | 422 | ||
419 | endpoint = usb_pipeendpoint(urb->pipe); | 423 | endpoint = usb_pipeendpoint(urb->pipe); |
420 | port = (struct usb_serial_port *) urb->context; | 424 | port = (struct usb_serial_port *) urb->context; |
421 | 425 | ||
422 | if (urb->status) { | 426 | if (status) { |
423 | dbg("%s: nonzero status: %d on endpoint %02x.", | 427 | dbg("%s: nonzero status: %d on endpoint %02x.", |
424 | __FUNCTION__, urb->status, endpoint); | 428 | __FUNCTION__, status, endpoint); |
425 | } else { | 429 | } else { |
426 | tty = port->tty; | 430 | tty = port->tty; |
427 | if (urb->actual_length) { | 431 | if (urb->actual_length) { |
@@ -433,7 +437,7 @@ static void option_indat_callback(struct urb *urb) | |||
433 | } | 437 | } |
434 | 438 | ||
435 | /* Resubmit urb so we continue receiving */ | 439 | /* Resubmit urb so we continue receiving */ |
436 | if (port->open_count && urb->status != -ESHUTDOWN) { | 440 | if (port->open_count && status != -ESHUTDOWN) { |
437 | err = usb_submit_urb(urb, GFP_ATOMIC); | 441 | err = usb_submit_urb(urb, GFP_ATOMIC); |
438 | if (err) | 442 | if (err) |
439 | printk(KERN_ERR "%s: resubmit read urb failed. " | 443 | printk(KERN_ERR "%s: resubmit read urb failed. " |
@@ -446,17 +450,29 @@ static void option_indat_callback(struct urb *urb) | |||
446 | static void option_outdat_callback(struct urb *urb) | 450 | static void option_outdat_callback(struct urb *urb) |
447 | { | 451 | { |
448 | struct usb_serial_port *port; | 452 | struct usb_serial_port *port; |
453 | struct option_port_private *portdata; | ||
454 | int i; | ||
449 | 455 | ||
450 | dbg("%s", __FUNCTION__); | 456 | dbg("%s", __FUNCTION__); |
451 | 457 | ||
452 | port = (struct usb_serial_port *) urb->context; | 458 | port = (struct usb_serial_port *) urb->context; |
453 | 459 | ||
454 | usb_serial_port_softint(port); | 460 | usb_serial_port_softint(port); |
461 | |||
462 | portdata = usb_get_serial_port_data(port); | ||
463 | for (i = 0; i < N_OUT_URB; ++i) { | ||
464 | if (portdata->out_urbs[i] == urb) { | ||
465 | smp_mb__before_clear_bit(); | ||
466 | clear_bit(i, &portdata->out_busy); | ||
467 | break; | ||
468 | } | ||
469 | } | ||
455 | } | 470 | } |
456 | 471 | ||
457 | static void option_instat_callback(struct urb *urb) | 472 | static void option_instat_callback(struct urb *urb) |
458 | { | 473 | { |
459 | int err; | 474 | int err; |
475 | int status = urb->status; | ||
460 | struct usb_serial_port *port = (struct usb_serial_port *) urb->context; | 476 | struct usb_serial_port *port = (struct usb_serial_port *) urb->context; |
461 | struct option_port_private *portdata = usb_get_serial_port_data(port); | 477 | struct option_port_private *portdata = usb_get_serial_port_data(port); |
462 | struct usb_serial *serial = port->serial; | 478 | struct usb_serial *serial = port->serial; |
@@ -464,7 +480,7 @@ static void option_instat_callback(struct urb *urb) | |||
464 | dbg("%s", __FUNCTION__); | 480 | dbg("%s", __FUNCTION__); |
465 | dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); | 481 | dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); |
466 | 482 | ||
467 | if (urb->status == 0) { | 483 | if (status == 0) { |
468 | struct usb_ctrlrequest *req_pkt = | 484 | struct usb_ctrlrequest *req_pkt = |
469 | (struct usb_ctrlrequest *)urb->transfer_buffer; | 485 | (struct usb_ctrlrequest *)urb->transfer_buffer; |
470 | 486 | ||
@@ -495,10 +511,10 @@ static void option_instat_callback(struct urb *urb) | |||
495 | req_pkt->bRequestType,req_pkt->bRequest); | 511 | req_pkt->bRequestType,req_pkt->bRequest); |
496 | } | 512 | } |
497 | } else | 513 | } else |
498 | dbg("%s: error %d", __FUNCTION__, urb->status); | 514 | dbg("%s: error %d", __FUNCTION__, status); |
499 | 515 | ||
500 | /* Resubmit urb so we continue receiving IRQ data */ | 516 | /* Resubmit urb so we continue receiving IRQ data */ |
501 | if (urb->status != -ESHUTDOWN) { | 517 | if (status != -ESHUTDOWN) { |
502 | urb->dev = serial->dev; | 518 | urb->dev = serial->dev; |
503 | err = usb_submit_urb(urb, GFP_ATOMIC); | 519 | err = usb_submit_urb(urb, GFP_ATOMIC); |
504 | if (err) | 520 | if (err) |
@@ -518,7 +534,7 @@ static int option_write_room(struct usb_serial_port *port) | |||
518 | 534 | ||
519 | for (i=0; i < N_OUT_URB; i++) { | 535 | for (i=0; i < N_OUT_URB; i++) { |
520 | this_urb = portdata->out_urbs[i]; | 536 | this_urb = portdata->out_urbs[i]; |
521 | if (this_urb && this_urb->status != -EINPROGRESS) | 537 | if (this_urb && !test_bit(i, &portdata->out_busy)) |
522 | data_len += OUT_BUFLEN; | 538 | data_len += OUT_BUFLEN; |
523 | } | 539 | } |
524 | 540 | ||
@@ -537,7 +553,7 @@ static int option_chars_in_buffer(struct usb_serial_port *port) | |||
537 | 553 | ||
538 | for (i=0; i < N_OUT_URB; i++) { | 554 | for (i=0; i < N_OUT_URB; i++) { |
539 | this_urb = portdata->out_urbs[i]; | 555 | this_urb = portdata->out_urbs[i]; |
540 | if (this_urb && this_urb->status == -EINPROGRESS) | 556 | if (this_urb && test_bit(i, &portdata->out_busy)) |
541 | data_len += this_urb->transfer_buffer_length; | 557 | data_len += this_urb->transfer_buffer_length; |
542 | } | 558 | } |
543 | dbg("%s: %d", __FUNCTION__, data_len); | 559 | dbg("%s: %d", __FUNCTION__, data_len); |