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,
"control read: timeout, giving up after %d tries\n",
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
struct cardstate *cs = inbuf->cs;
struct bas_cardstate *ucs = cs->hw.bas;
int status = urb->status;
+ int have_data = 0;
unsigned numbytes;
int rc;
update_basstate(ucs, 0, BS_ATRDPEND);
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);
switch (status) {
numbytes = ucs->rcvbuf_size;
}
- /* copy received bytes to inbuf, notify event layer */
- if (gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes)) {
- gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
- gigaset_schedule_event(cs);
+ /* copy received bytes to inbuf */
+ have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);
+
+ if (unlikely(numbytes < ucs->rcvbuf_size)) {
+ /* 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;
case -EINPROGRESS: /* pending */
case -ENODEV: /* device removed */
case -ESHUTDOWN: /* device shut down */
- /* no further action necessary */
+ /* no action necessary */
gig_dbg(DEBUG_USBREQ, "%s: %s",
__func__, get_usb_statmsg(status));
break;
- default: /* other errors: retry */
+ default: /* severe trouble */
+ dev_warn(cs->dev, "control read: %s\n",
+ get_usb_statmsg(status));
if (ucs->retry_cmd_in++ < BAS_RETRY) {
- gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__,
- get_usb_statmsg(status), ucs->retry_cmd_in);
+ dev_notice(cs->dev, "control read: retry %d\n",
+ ucs->retry_cmd_in);
rc = atread_submit(cs, BAS_TIMEOUT);
- if (rc >= 0)
- /* successfully resubmitted, skip freeing */
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
return;
- if (rc == -ENODEV)
- /* disconnect, no further action necessary */
- break;
+ } else {
+ dev_err(cs->dev,
+ "control read: giving up after %d tries\n",
+ 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);
}
- /* read finished, free buffer */
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
+ if (have_data) {
+ gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+ gigaset_schedule_event(cs);
+ }
}
/* atread_submit
ret = starturbs(bcs);
if (ret < 0) {
- spin_unlock_irqrestore(&cs->lock, flags);
dev_err(cs->dev,
"could not start isochronous I/O for channel B%d: %s\n",
bcs->channel + 1,
ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
if (ret != -ENODEV)
error_hangup(bcs);
+ spin_unlock_irqrestore(&cs->lock, flags);
return ret;
}
dev_err(cs->dev, "could not open channel B%d\n",
bcs->channel + 1);
stopurbs(bcs->hw.bas);
+ if (ret != -ENODEV)
+ error_hangup(bcs);
}
spin_unlock_irqrestore(&cs->lock, flags);
- if (ret < 0 && ret != -ENODEV)
- error_hangup(bcs);
return ret;
}