Commit c8701a08 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by David S. Miller

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: default avatarTilman Schmidt <tilman@imap.cc>
CC: stable <stable@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b33ffa5c
...@@ -438,23 +438,27 @@ static void cmd_in_timeout(unsigned long data) ...@@ -438,23 +438,27 @@ static void cmd_in_timeout(unsigned long data)
return; return;
} }
if (ucs->retry_cmd_in++ < BAS_RETRY) { if (ucs->retry_cmd_in++ >= BAS_RETRY) {
dev_notice(cs->dev, "control read: timeout, retry %d\n",
ucs->retry_cmd_in);
rc = atread_submit(cs, BAS_TIMEOUT);
if (rc >= 0 || rc == -ENODEV)
/* resubmitted or disconnected */
/* - bypass regular exit block */
return;
} else {
dev_err(cs->dev, dev_err(cs->dev,
"control read: timeout, giving up after %d tries\n", "control read: timeout, giving up after %d tries\n",
ucs->retry_cmd_in); ucs->retry_cmd_in);
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
error_reset(cs);
return;
}
gig_dbg(DEBUG_USBREQ, "%s: timeout, retry %d",
__func__, ucs->retry_cmd_in);
rc = atread_submit(cs, BAS_TIMEOUT);
if (rc < 0) {
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
if (rc != -ENODEV)
error_reset(cs);
} }
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
error_reset(cs);
} }
/* read_ctrl_callback /* read_ctrl_callback
...@@ -470,18 +474,11 @@ static void read_ctrl_callback(struct urb *urb) ...@@ -470,18 +474,11 @@ static void read_ctrl_callback(struct urb *urb)
struct cardstate *cs = inbuf->cs; struct cardstate *cs = inbuf->cs;
struct bas_cardstate *ucs = cs->hw.bas; struct bas_cardstate *ucs = cs->hw.bas;
int status = urb->status; int status = urb->status;
int have_data = 0;
unsigned numbytes; unsigned numbytes;
int rc; int rc;
update_basstate(ucs, 0, BS_ATRDPEND); update_basstate(ucs, 0, BS_ATRDPEND);
wake_up(&ucs->waitqueue); wake_up(&ucs->waitqueue);
if (!ucs->rcvbuf_size) {
dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
return;
}
del_timer(&ucs->timer_cmd_in); del_timer(&ucs->timer_cmd_in);
switch (status) { switch (status) {
...@@ -495,19 +492,10 @@ static void read_ctrl_callback(struct urb *urb) ...@@ -495,19 +492,10 @@ static void read_ctrl_callback(struct urb *urb)
numbytes = ucs->rcvbuf_size; numbytes = ucs->rcvbuf_size;
} }
/* copy received bytes to inbuf */ /* copy received bytes to inbuf, notify event layer */
have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes); if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
if (unlikely(numbytes < ucs->rcvbuf_size)) { gigaset_schedule_event(cs);
/* incomplete - resubmit for remaining bytes */
ucs->rcvbuf_size -= numbytes;
ucs->retry_cmd_in = 0;
rc = atread_submit(cs, BAS_TIMEOUT);
if (rc >= 0 || rc == -ENODEV)
/* resubmitted or disconnected */
/* - bypass regular exit block */
return;
error_reset(cs);
} }
break; break;
...@@ -516,37 +504,32 @@ static void read_ctrl_callback(struct urb *urb) ...@@ -516,37 +504,32 @@ static void read_ctrl_callback(struct urb *urb)
case -EINPROGRESS: /* pending */ case -EINPROGRESS: /* pending */
case -ENODEV: /* device removed */ case -ENODEV: /* device removed */
case -ESHUTDOWN: /* device shut down */ case -ESHUTDOWN: /* device shut down */
/* no action necessary */ /* no further action necessary */
gig_dbg(DEBUG_USBREQ, "%s: %s", gig_dbg(DEBUG_USBREQ, "%s: %s",
__func__, get_usb_statmsg(status)); __func__, get_usb_statmsg(status));
break; break;
default: /* severe trouble */ default: /* other errors: retry */
dev_warn(cs->dev, "control read: %s\n",
get_usb_statmsg(status));
if (ucs->retry_cmd_in++ < BAS_RETRY) { if (ucs->retry_cmd_in++ < BAS_RETRY) {
dev_notice(cs->dev, "control read: retry %d\n", gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
ucs->retry_cmd_in); get_usb_statmsg(status), ucs->retry_cmd_in);
rc = atread_submit(cs, BAS_TIMEOUT); rc = atread_submit(cs, BAS_TIMEOUT);
if (rc >= 0 || rc == -ENODEV) if (rc >= 0)
/* resubmitted or disconnected */ /* successfully resubmitted, skip freeing */
/* - bypass regular exit block */
return; return;
} else { if (rc == -ENODEV)
dev_err(cs->dev, /* disconnect, no further action necessary */
"control read: giving up after %d tries\n", break;
ucs->retry_cmd_in);
} }
dev_err(cs->dev, "control read: %s, giving up after %d tries\n",
get_usb_statmsg(status), ucs->retry_cmd_in);
error_reset(cs); error_reset(cs);
} }
/* read finished, free buffer */
kfree(ucs->rcvbuf); kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL; ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0; ucs->rcvbuf_size = 0;
if (have_data) {
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
gigaset_schedule_event(cs);
}
} }
/* atread_submit /* atread_submit
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment