diff options
author | Tilman Schmidt <tilman@imap.cc> | 2010-09-30 09:34:40 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-10-01 03:33:33 -0400 |
commit | c8701a08d6a4efeae45d84d0aa87172f23b14e3c (patch) | |
tree | 4f51fcc9ac6d091036b2d9bd24521d7ee4553ff0 | |
parent | b33ffa5cbf52ee751bb8068218ebb3c742c5a515 (diff) |
isdn/gigaset: fix bas_gigaset AT read error handling
Rework the handling of USB errors in AT response reads
to fix a possible infinite retry loop and a memory leak,
and silence a few overly verbose kernel messages.
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
CC: stable <stable@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/isdn/gigaset/bas-gigaset.c | 83 |
1 files changed, 33 insertions, 50 deletions
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index e143050e1b5c..131976d880d0 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c | |||
@@ -438,23 +438,27 @@ static void cmd_in_timeout(unsigned long data) | |||
438 | return; | 438 | return; |
439 | } | 439 | } |
440 | 440 | ||
441 | if (ucs->retry_cmd_in++ < BAS_RETRY) { | 441 | if (ucs->retry_cmd_in++ >= BAS_RETRY) { |
442 | dev_notice(cs->dev, "control read: timeout, retry %d\n", | ||
443 | ucs->retry_cmd_in); | ||
444 | rc = atread_submit(cs, BAS_TIMEOUT); | ||
445 | if (rc >= 0 || rc == -ENODEV) | ||
446 | /* resubmitted or disconnected */ | ||
447 | /* - bypass regular exit block */ | ||
448 | return; | ||
449 | } else { | ||
450 | dev_err(cs->dev, | 442 | dev_err(cs->dev, |
451 | "control read: timeout, giving up after %d tries\n", | 443 | "control read: timeout, giving up after %d tries\n", |
452 | ucs->retry_cmd_in); | 444 | ucs->retry_cmd_in); |
445 | kfree(ucs->rcvbuf); | ||
446 | ucs->rcvbuf = NULL; | ||
447 | ucs->rcvbuf_size = 0; | ||
448 | error_reset(cs); | ||
449 | return; | ||
450 | } | ||
451 | |||
452 | gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d", | ||
453 | __func__, ucs->retry_cmd_in); | ||
454 | rc = atread_submit(cs, BAS_TIMEOUT); | ||
455 | if (rc < 0) { | ||
456 | kfree(ucs->rcvbuf); | ||
457 | ucs->rcvbuf = NULL; | ||
458 | ucs->rcvbuf_size = 0; | ||
459 | if (rc != -ENODEV) | ||
460 | error_reset(cs); | ||
453 | } | 461 | } |
454 | kfree(ucs->rcvbuf); | ||
455 | ucs->rcvbuf = NULL; | ||
456 | ucs->rcvbuf_size = 0; | ||
457 | error_reset(cs); | ||
458 | } | 462 | } |
459 | 463 | ||
460 | /* read_ctrl_callback | 464 | /* read_ctrl_callback |
@@ -470,18 +474,11 @@ static void read_ctrl_callback(struct urb *urb) | |||
470 | struct cardstate *cs = inbuf->cs; | 474 | struct cardstate *cs = inbuf->cs; |
471 | struct bas_cardstate *ucs = cs->hw.bas; | 475 | struct bas_cardstate *ucs = cs->hw.bas; |
472 | int status = urb->status; | 476 | int status = urb->status; |
473 | int have_data = 0; | ||
474 | unsigned numbytes; | 477 | unsigned numbytes; |
475 | int rc; | 478 | int rc; |
476 | 479 | ||
477 | update_basstate(ucs, 0, BS_ATRDPEND); | 480 | update_basstate(ucs, 0, BS_ATRDPEND); |
478 | wake_up(&ucs->waitqueue); | 481 | wake_up(&ucs->waitqueue); |
479 | |||
480 | if (!ucs->rcvbuf_size) { | ||
481 | dev_warn(cs->dev, "%s: no receive in progress\n", __func__); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | del_timer(&ucs->timer_cmd_in); | 482 | del_timer(&ucs->timer_cmd_in); |
486 | 483 | ||
487 | switch (status) { | 484 | switch (status) { |
@@ -495,19 +492,10 @@ static void read_ctrl_callback(struct urb *urb) | |||
495 | numbytes = ucs->rcvbuf_size; | 492 | numbytes = ucs->rcvbuf_size; |
496 | } | 493 | } |
497 | 494 | ||
498 | /* copy received bytes to inbuf */ | 495 | /* copy received bytes to inbuf, notify event layer */ |
499 | have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes); | 496 | if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) { |
500 | 497 | gig_dbg(DEBUG_INTR, "%s-->BH", __func__); | |
501 | if (unlikely(numbytes < ucs->rcvbuf_size)) { | 498 | gigaset_schedule_event(cs); |
502 | /* incomplete - resubmit for remaining bytes */ | ||
503 | ucs->rcvbuf_size -= numbytes; | ||
504 | ucs->retry_cmd_in = 0; | ||
505 | rc = atread_submit(cs, BAS_TIMEOUT); | ||
506 | if (rc >= 0 || rc == -ENODEV) | ||
507 | /* resubmitted or disconnected */ | ||
508 | /* - bypass regular exit block */ | ||
509 | return; | ||
510 | error_reset(cs); | ||
511 | } | 499 | } |
512 | break; | 500 | break; |
513 | 501 | ||
@@ -516,37 +504,32 @@ static void read_ctrl_callback(struct urb *urb) | |||
516 | case -EINPROGRESS: /* pending */ | 504 | case -EINPROGRESS: /* pending */ |
517 | case -ENODEV: /* device removed */ | 505 | case -ENODEV: /* device removed */ |
518 | case -ESHUTDOWN: /* device shut down */ | 506 | case -ESHUTDOWN: /* device shut down */ |
519 | /* no action necessary */ | 507 | /* no further action necessary */ |
520 | gig_dbg(DEBUG_USBREQ, "%s: %s", | 508 | gig_dbg(DEBUG_USBREQ, "%s: %s", |
521 | __func__, get_usb_statmsg(status)); | 509 | __func__, get_usb_statmsg(status)); |
522 | break; | 510 | break; |
523 | 511 | ||
524 | default: /* severe trouble */ | 512 | default: /* other errors: retry */ |
525 | dev_warn(cs->dev, "control read: %s\n", | ||
526 | get_usb_statmsg(status)); | ||
527 | if (ucs->retry_cmd_in++ < BAS_RETRY) { | 513 | if (ucs->retry_cmd_in++ < BAS_RETRY) { |
528 | dev_notice(cs->dev, "control read: retry %d\n", | 514 | gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__, |
529 | ucs->retry_cmd_in); | 515 | get_usb_statmsg(status), ucs->retry_cmd_in); |
530 | rc = atread_submit(cs, BAS_TIMEOUT); | 516 | rc = atread_submit(cs, BAS_TIMEOUT); |
531 | if (rc >= 0 || rc == -ENODEV) | 517 | if (rc >= 0) |
532 | /* resubmitted or disconnected */ | 518 | /* successfully resubmitted, skip freeing */ |
533 | /* - bypass regular exit block */ | ||
534 | return; | 519 | return; |
535 | } else { | 520 | if (rc == -ENODEV) |
536 | dev_err(cs->dev, | 521 | /* disconnect, no further action necessary */ |
537 | "control read: giving up after %d tries\n", | 522 | break; |
538 | ucs->retry_cmd_in); | ||
539 | } | 523 | } |
524 | dev_err(cs->dev, "control read: %s, giving up after %d tries\n", | ||
525 | get_usb_statmsg(status), ucs->retry_cmd_in); | ||
540 | error_reset(cs); | 526 | error_reset(cs); |
541 | } | 527 | } |
542 | 528 | ||
529 | /* read finished, free buffer */ | ||
543 | kfree(ucs->rcvbuf); | 530 | kfree(ucs->rcvbuf); |
544 | ucs->rcvbuf = NULL; | 531 | ucs->rcvbuf = NULL; |
545 | ucs->rcvbuf_size = 0; | 532 | ucs->rcvbuf_size = 0; |
546 | if (have_data) { | ||
547 | gig_dbg(DEBUG_INTR, "%s-->BH", __func__); | ||
548 | gigaset_schedule_event(cs); | ||
549 | } | ||
550 | } | 533 | } |
551 | 534 | ||
552 | /* atread_submit | 535 | /* atread_submit |