diff options
Diffstat (limited to 'drivers/isdn/gigaset/bas-gigaset.c')
-rw-r--r-- | drivers/isdn/gigaset/bas-gigaset.c | 400 |
1 files changed, 248 insertions, 152 deletions
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 707d9c94cf9..178942a2ee6 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -109,6 +109,9 @@ struct bas_cardstate { struct urb *urb_int_in; /* URB for interrupt pipe */ unsigned char *int_in_buf; + struct work_struct int_in_wq; /* for usb_clear_halt() */ + struct timer_list timer_int_in; /* int read retry delay */ + int retry_int_in; spinlock_t lock; /* locks all following */ int basstate; /* bitmap (BS_*) */ @@ -169,7 +172,7 @@ static char *get_usb_rcmsg(int rc) case -EAGAIN: return "start frame too early or too much scheduled"; case -EFBIG: - return "too many isochronous frames requested"; + return "too many isoc frames requested"; case -EPIPE: return "endpoint stalled"; case -EMSGSIZE: @@ -200,13 +203,13 @@ static char *get_usb_statmsg(int status) case -ENOENT: return "unlinked (sync)"; case -EINPROGRESS: - return "pending"; + return "URB still pending"; case -EPROTO: - return "bit stuffing error, timeout, or unknown USB error"; + return "bitstuff error, timeout, or unknown USB error"; case -EILSEQ: return "CRC mismatch, timeout, or unknown USB error"; case -ETIME: - return "timed out"; + return "USB response timeout"; case -EPIPE: return "endpoint stalled"; case -ECOMM: @@ -214,15 +217,15 @@ static char *get_usb_statmsg(int status) case -ENOSR: return "OUT buffer underrun"; case -EOVERFLOW: - return "too much data"; + return "endpoint babble"; case -EREMOTEIO: - return "short packet detected"; + return "short packet"; case -ENODEV: return "device removed"; case -EXDEV: - return "partial isochronous transfer"; + return "partial isoc transfer"; case -EINVAL: - return "invalid argument"; + return "ISO madness"; case -ECONNRESET: return "unlinked (async)"; case -ESHUTDOWN: @@ -350,7 +353,7 @@ static inline void error_hangup(struct bc_state *bcs) * reset Gigaset device because of an unrecoverable error * This function may be called from any context, and takes care of * scheduling the necessary actions for execution outside of interrupt context. - * cs->lock must not be held. + * cs->hw.bas->lock must not be held. * argument: * controller state structure */ @@ -358,7 +361,9 @@ static inline void error_reset(struct cardstate *cs) { /* reset interrupt pipe to recover (ignore errors) */ update_basstate(cs->hw.bas, BS_RESETTING, 0); - req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT); + if (req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT)) + /* submission failed, escalate to USB port reset */ + usb_queue_reset_device(cs->hw.bas->interface); } /* check_pending @@ -438,23 +443,27 @@ static void cmd_in_timeout(unsigned long data) return; } - 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 { + if (ucs->retry_cmd_in++ >= BAS_RETRY) { 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 @@ -470,18 +479,11 @@ static void read_ctrl_callback(struct urb *urb) 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) { @@ -495,19 +497,10 @@ static void read_ctrl_callback(struct urb *urb) numbytes = ucs->rcvbuf_size; } - /* 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); + /* 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); } break; @@ -516,37 +509,32 @@ static void read_ctrl_callback(struct urb *urb) case -EINPROGRESS: /* pending */ case -ENODEV: /* device removed */ case -ESHUTDOWN: /* device shut down */ - /* no action necessary */ + /* no further action necessary */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(status)); break; - default: /* severe trouble */ - dev_warn(cs->dev, "control read: %s\n", - get_usb_statmsg(status)); + default: /* other errors: retry */ if (ucs->retry_cmd_in++ < BAS_RETRY) { - dev_notice(cs->dev, "control read: retry %d\n", - ucs->retry_cmd_in); + gig_dbg(DEBUG_USBREQ, "%s: %s, retry %d", __func__, + get_usb_statmsg(status), ucs->retry_cmd_in); rc = atread_submit(cs, BAS_TIMEOUT); - if (rc >= 0 || rc == -ENODEV) - /* resubmitted or disconnected */ - /* - bypass regular exit block */ + if (rc >= 0) + /* successfully resubmitted, skip freeing */ return; - } else { - dev_err(cs->dev, - "control read: giving up after %d tries\n", - ucs->retry_cmd_in); + if (rc == -ENODEV) + /* disconnect, no further action necessary */ + break; } + 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 @@ -605,14 +593,67 @@ static int atread_submit(struct cardstate *cs, int timeout) if (timeout > 0) { gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); - ucs->timer_cmd_in.expires = jiffies + timeout * HZ / 10; - ucs->timer_cmd_in.data = (unsigned long) cs; - ucs->timer_cmd_in.function = cmd_in_timeout; - add_timer(&ucs->timer_cmd_in); + mod_timer(&ucs->timer_cmd_in, jiffies + timeout * HZ / 10); } return 0; } +/* int_in_work + * workqueue routine to clear halt on interrupt in endpoint + */ + +static void int_in_work(struct work_struct *work) +{ + struct bas_cardstate *ucs = + container_of(work, struct bas_cardstate, int_in_wq); + struct urb *urb = ucs->urb_int_in; + struct cardstate *cs = urb->context; + int rc; + + /* clear halt condition */ + rc = usb_clear_halt(ucs->udev, urb->pipe); + gig_dbg(DEBUG_USBREQ, "clear_halt: %s", get_usb_rcmsg(rc)); + if (rc == 0) + /* success, resubmit interrupt read URB */ + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc != 0 && rc != -ENODEV) { + dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc)); + rc = usb_lock_device_for_reset(ucs->udev, ucs->interface); + if (rc == 0) { + rc = usb_reset_device(ucs->udev); + usb_unlock_device(ucs->udev); + } + } + ucs->retry_int_in = 0; +} + +/* int_in_resubmit + * timer routine for interrupt read delayed resubmit + * argument: + * controller state structure + */ +static void int_in_resubmit(unsigned long data) +{ + struct cardstate *cs = (struct cardstate *) data; + struct bas_cardstate *ucs = cs->hw.bas; + int rc; + + if (ucs->retry_int_in++ >= BAS_RETRY) { + dev_err(cs->dev, "interrupt read: giving up after %d tries\n", + ucs->retry_int_in); + usb_queue_reset_device(ucs->interface); + return; + } + + gig_dbg(DEBUG_USBREQ, "%s: retry %d", __func__, ucs->retry_int_in); + rc = usb_submit_urb(ucs->urb_int_in, GFP_ATOMIC); + if (rc != 0 && rc != -ENODEV) { + dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", + get_usb_rcmsg(rc)); + usb_queue_reset_device(ucs->interface); + } +} + /* read_int_callback * USB completion handler for interrupt pipe input * called by the USB subsystem in interrupt context @@ -633,19 +674,29 @@ static void read_int_callback(struct urb *urb) switch (status) { case 0: /* success */ + ucs->retry_int_in = 0; break; + case -EPIPE: /* endpoint stalled */ + schedule_work(&ucs->int_in_wq); + /* fall through */ case -ENOENT: /* cancelled */ case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ - /* ignore silently */ + case -ENODEV: /* device removed */ + case -ESHUTDOWN: /* device shut down */ + /* no further action necessary */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(status)); return; - case -ENODEV: /* device removed */ - case -ESHUTDOWN: /* device shut down */ - gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__); + case -EPROTO: /* protocol error or unplug */ + case -EILSEQ: + case -ETIME: + /* resubmit after delay */ + gig_dbg(DEBUG_USBREQ, "%s: %s", + __func__, get_usb_statmsg(status)); + mod_timer(&ucs->timer_int_in, jiffies + HZ / 10); return; - default: /* severe trouble */ + default: /* other errors: just resubmit */ dev_warn(cs->dev, "interrupt read: %s\n", get_usb_statmsg(status)); goto resubmit; @@ -723,6 +774,13 @@ static void read_int_callback(struct urb *urb) break; } spin_lock_irqsave(&cs->lock, flags); + if (ucs->basstate & BS_ATRDPEND) { + spin_unlock_irqrestore(&cs->lock, flags); + dev_warn(cs->dev, + "HD_RECEIVEATDATA_ACK(%d) during HD_READ_ATMESSAGE(%d) ignored\n", + l, ucs->rcvbuf_size); + break; + } if (ucs->rcvbuf_size) { /* throw away previous buffer - we have no queue */ dev_err(cs->dev, @@ -735,7 +793,6 @@ static void read_int_callback(struct urb *urb) if (ucs->rcvbuf == NULL) { spin_unlock_irqrestore(&cs->lock, flags); dev_err(cs->dev, "out of memory receiving AT data\n"); - error_reset(cs); break; } ucs->rcvbuf_size = l; @@ -745,13 +802,10 @@ static void read_int_callback(struct urb *urb) kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; - if (rc != -ENODEV) { - spin_unlock_irqrestore(&cs->lock, flags); - error_reset(cs); - break; - } } spin_unlock_irqrestore(&cs->lock, flags); + if (rc < 0 && rc != -ENODEV) + error_reset(cs); break; case HD_RESET_INTERRUPT_PIPE_ACK: @@ -818,6 +872,7 @@ static void read_iso_callback(struct urb *urb) tasklet_hi_schedule(&ubc->rcvd_tasklet); } else { /* tasklet still busy, drop data and resubmit URB */ + gig_dbg(DEBUG_ISO, "%s: overrun", __func__); ubc->loststatus = status; for (i = 0; i < BAS_NUMFRAMES; i++) { ubc->isoinlost += urb->iso_frame_desc[i].actual_length; @@ -833,13 +888,11 @@ static void read_iso_callback(struct urb *urb) urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = BAS_NUMFRAMES; - gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit", - __func__); rc = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(bcs->cs->dev, - "could not resubmit isochronous read " - "URB: %s\n", get_usb_rcmsg(rc)); + "could not resubmit isoc read URB: %s\n", + get_usb_rcmsg(rc)); dump_urb(DEBUG_ISO, "isoc read", urb); error_hangup(bcs); } @@ -1081,7 +1134,7 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) gig_dbg(DEBUG_ISO, "%s: disconnected", __func__); else dev_err(ucx->bcs->cs->dev, - "could not submit isochronous write URB: %s\n", + "could not submit isoc write URB: %s\n", get_usb_rcmsg(rc)); return rc; } @@ -1126,7 +1179,7 @@ static void write_iso_tasklet(unsigned long data) ubc->isooutovfl = NULL; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (ovfl) { - dev_err(cs->dev, "isochronous write buffer underrun\n"); + dev_err(cs->dev, "isoc write underrun\n"); error_hangup(bcs); break; } @@ -1151,7 +1204,7 @@ static void write_iso_tasklet(unsigned long data) if (next) { /* couldn't put it back */ dev_err(cs->dev, - "losing isochronous write URB\n"); + "losing isoc write URB\n"); error_hangup(bcs); } } @@ -1178,10 +1231,10 @@ static void write_iso_tasklet(unsigned long data) if (ifd->status || ifd->actual_length != ifd->length) { dev_warn(cs->dev, - "isochronous write: frame %d: %s, " - "only %d of %d bytes sent\n", - i, get_usb_statmsg(ifd->status), - ifd->actual_length, ifd->length); + "isoc write: frame %d[%d/%d]: %s\n", + i, ifd->actual_length, + ifd->length, + get_usb_statmsg(ifd->status)); offset = (ifd->offset + ifd->actual_length) % BAS_OUTBUFSIZE; @@ -1190,11 +1243,11 @@ static void write_iso_tasklet(unsigned long data) } break; case -EPIPE: /* stall - probably underrun */ - dev_err(cs->dev, "isochronous write stalled\n"); + dev_err(cs->dev, "isoc write: stalled\n"); error_hangup(bcs); break; - default: /* severe trouble */ - dev_warn(cs->dev, "isochronous write: %s\n", + default: /* other errors */ + dev_warn(cs->dev, "isoc write: %s\n", get_usb_statmsg(status)); } @@ -1250,6 +1303,7 @@ static void read_iso_tasklet(unsigned long data) struct cardstate *cs = bcs->cs; struct urb *urb; int status; + struct usb_iso_packet_descriptor *ifd; char *rcvbuf; unsigned long flags; int totleft, numbytes, offset, frame, rc; @@ -1267,8 +1321,7 @@ static void read_iso_tasklet(unsigned long data) ubc->isoindone = NULL; if (unlikely(ubc->loststatus != -EINPROGRESS)) { dev_warn(cs->dev, - "isochronous read overrun, " - "dropped URB with status: %s, %d bytes lost\n", + "isoc read overrun, URB dropped (status: %s, %d bytes)\n", get_usb_statmsg(ubc->loststatus), ubc->isoinlost); ubc->loststatus = -EINPROGRESS; @@ -1298,11 +1351,11 @@ static void read_iso_tasklet(unsigned long data) __func__, get_usb_statmsg(status)); continue; /* -> skip */ case -EPIPE: - dev_err(cs->dev, "isochronous read stalled\n"); + dev_err(cs->dev, "isoc read: stalled\n"); error_hangup(bcs); continue; /* -> skip */ - default: /* severe trouble */ - dev_warn(cs->dev, "isochronous read: %s\n", + default: /* other error */ + dev_warn(cs->dev, "isoc read: %s\n", get_usb_statmsg(status)); goto error; } @@ -1310,40 +1363,52 @@ static void read_iso_tasklet(unsigned long data) rcvbuf = urb->transfer_buffer; totleft = urb->actual_length; for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) { - numbytes = urb->iso_frame_desc[frame].actual_length; - if (unlikely(urb->iso_frame_desc[frame].status)) + ifd = &urb->iso_frame_desc[frame]; + numbytes = ifd->actual_length; + switch (ifd->status) { + case 0: /* success */ + break; + case -EPROTO: /* protocol error or unplug */ + case -EILSEQ: + case -ETIME: + /* probably just disconnected, ignore */ + gig_dbg(DEBUG_ISO, + "isoc read: frame %d[%d]: %s\n", + frame, numbytes, + get_usb_statmsg(ifd->status)); + break; + default: /* other error */ + /* report, assume transferred bytes are ok */ dev_warn(cs->dev, - "isochronous read: frame %d[%d]: %s\n", + "isoc read: frame %d[%d]: %s\n", frame, numbytes, - get_usb_statmsg( - urb->iso_frame_desc[frame].status)); + get_usb_statmsg(ifd->status)); + } if (unlikely(numbytes > BAS_MAXFRAME)) dev_warn(cs->dev, - "isochronous read: frame %d: " - "numbytes (%d) > BAS_MAXFRAME\n", - frame, numbytes); + "isoc read: frame %d[%d]: %s\n", + frame, numbytes, + "exceeds max frame size"); if (unlikely(numbytes > totleft)) { dev_warn(cs->dev, - "isochronous read: frame %d: " - "numbytes (%d) > totleft (%d)\n", - frame, numbytes, totleft); + "isoc read: frame %d[%d]: %s\n", + frame, numbytes, + "exceeds total transfer length"); numbytes = totleft; } - offset = urb->iso_frame_desc[frame].offset; + offset = ifd->offset; if (unlikely(offset + numbytes > BAS_INBUFSIZE)) { dev_warn(cs->dev, - "isochronous read: frame %d: " - "offset (%d) + numbytes (%d) " - "> BAS_INBUFSIZE\n", - frame, offset, numbytes); + "isoc read: frame %d[%d]: %s\n", + frame, numbytes, + "exceeds end of buffer"); numbytes = BAS_INBUFSIZE - offset; } gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs); totleft -= numbytes; } if (unlikely(totleft > 0)) - dev_warn(cs->dev, - "isochronous read: %d data bytes missing\n", + dev_warn(cs->dev, "isoc read: %d data bytes missing\n", totleft); error: @@ -1359,9 +1424,9 @@ error: rc = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(cs->dev, - "could not resubmit isochronous read URB: %s\n", + "could not resubmit isoc read URB: %s\n", get_usb_rcmsg(rc)); - dump_urb(DEBUG_ISO, "resubmit iso read", urb); + dump_urb(DEBUG_ISO, "resubmit isoc read", urb); error_hangup(bcs); } } @@ -1373,12 +1438,12 @@ error: /* req_timeout * timeout routine for control output request * argument: - * B channel control structure + * controller state structure */ static void req_timeout(unsigned long data) { - struct bc_state *bcs = (struct bc_state *) data; - struct bas_cardstate *ucs = bcs->cs->hw.bas; + struct cardstate *cs = (struct cardstate *) data; + struct bas_cardstate *ucs = cs->hw.bas; int pending; unsigned long flags; @@ -1395,38 +1460,44 @@ static void req_timeout(unsigned long data) break; case HD_OPEN_ATCHANNEL: - dev_err(bcs->cs->dev, "timeout opening AT channel\n"); - error_reset(bcs->cs); + dev_err(cs->dev, "timeout opening AT channel\n"); + error_reset(cs); break; - case HD_OPEN_B2CHANNEL: case HD_OPEN_B1CHANNEL: - dev_err(bcs->cs->dev, "timeout opening channel %d\n", - bcs->channel + 1); - error_hangup(bcs); + dev_err(cs->dev, "timeout opening channel 1\n"); + error_hangup(&cs->bcs[0]); + break; + + case HD_OPEN_B2CHANNEL: + dev_err(cs->dev, "timeout opening channel 2\n"); + error_hangup(&cs->bcs[1]); break; case HD_CLOSE_ATCHANNEL: - dev_err(bcs->cs->dev, "timeout closing AT channel\n"); - error_reset(bcs->cs); + dev_err(cs->dev, "timeout closing AT channel\n"); + error_reset(cs); break; - case HD_CLOSE_B2CHANNEL: case HD_CLOSE_B1CHANNEL: - dev_err(bcs->cs->dev, "timeout closing channel %d\n", - bcs->channel + 1); - error_reset(bcs->cs); + dev_err(cs->dev, "timeout closing channel 1\n"); + error_reset(cs); + break; + + case HD_CLOSE_B2CHANNEL: + dev_err(cs->dev, "timeout closing channel 2\n"); + error_reset(cs); break; case HD_RESET_INTERRUPT_PIPE: /* error recovery escalation */ - dev_err(bcs->cs->dev, + dev_err(cs->dev, "reset interrupt pipe timeout, attempting USB reset\n"); - usb_queue_reset_device(bcs->cs->hw.bas->interface); + usb_queue_reset_device(ucs->interface); break; default: - dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", + dev_warn(cs->dev, "request 0x%02x timed out, clearing\n", pending); } @@ -1557,10 +1628,7 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) if (timeout > 0) { gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); - ucs->timer_ctrl.expires = jiffies + timeout * HZ / 10; - ucs->timer_ctrl.data = (unsigned long) bcs; - ucs->timer_ctrl.function = req_timeout; - add_timer(&ucs->timer_ctrl); + mod_timer(&ucs->timer_ctrl, jiffies + timeout * HZ / 10); } spin_unlock_irqrestore(&ucs->lock, flags); @@ -1590,21 +1658,20 @@ static int gigaset_init_bchannel(struct bc_state *bcs) if (cs->hw.bas->basstate & BS_SUSPEND) { dev_notice(cs->dev, - "not starting isochronous I/O, " - "suspend in progress\n"); + "not starting isoc I/O, suspend in progress\n"); spin_unlock_irqrestore(&cs->lock, flags); return -EHOSTUNREACH; } 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", + "could not start isoc 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; } @@ -1614,11 +1681,11 @@ static int gigaset_init_bchannel(struct bc_state *bcs) 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; } @@ -1826,10 +1893,7 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) { gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs", ATRDY_TIMEOUT); - ucs->timer_atrdy.expires = jiffies + ATRDY_TIMEOUT * HZ / 10; - ucs->timer_atrdy.data = (unsigned long) cs; - ucs->timer_atrdy.function = atrdy_timeout; - add_timer(&ucs->timer_atrdy); + mod_timer(&ucs->timer_atrdy, jiffies + ATRDY_TIMEOUT * HZ / 10); } return 0; } @@ -1914,6 +1978,28 @@ static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb) * The next command will reopen the AT channel automatically. */ if (cb->len == 3 && !memcmp(cb->buf, "+++", 3)) { + /* If an HD_RECEIVEATDATA_ACK message remains unhandled + * because of an error, the base never sends another one. + * The response channel is thus effectively blocked. + * Closing and reopening the AT channel does *not* clear + * this condition. + * As a stopgap measure, submit a zero-length AT read + * before closing the AT channel. This has the undocumented + * effect of triggering a new HD_RECEIVEATDATA_ACK message + * from the base if necessary. + * The subsequent AT channel close then discards any pending + * messages. + */ + spin_lock_irqsave(&cs->lock, flags); + if (!(cs->hw.bas->basstate & BS_ATRDPEND)) { + kfree(cs->hw.bas->rcvbuf); + cs->hw.bas->rcvbuf = NULL; + cs->hw.bas->rcvbuf_size = 0; + cs->hw.bas->retry_cmd_in = 0; + atread_submit(cs, 0); + } + spin_unlock_irqrestore(&cs->lock, flags); + rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); if (cb->wake_tasklet) tasklet_schedule(cb->wake_tasklet); @@ -2010,7 +2096,7 @@ static int gigaset_freebcshw(struct bc_state *bcs) /* kill URBs and tasklets before freeing - better safe than sorry */ ubc->running = 0; - gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__); + gig_dbg(DEBUG_INIT, "%s: killing isoc URBs", __func__); for (i = 0; i < BAS_OUTURBS; ++i) { usb_kill_urb(ubc->isoouturbs[i].urb); usb_free_urb(ubc->isoouturbs[i].urb); @@ -2131,10 +2217,12 @@ static int gigaset_initcshw(struct cardstate *cs) ucs->pending = 0; ucs->basstate = 0; - init_timer(&ucs->timer_ctrl); - init_timer(&ucs->timer_atrdy); - init_timer(&ucs->timer_cmd_in); + setup_timer(&ucs->timer_ctrl, req_timeout, (unsigned long) cs); + setup_timer(&ucs->timer_atrdy, atrdy_timeout, (unsigned long) cs); + setup_timer(&ucs->timer_cmd_in, cmd_in_timeout, (unsigned long) cs); + setup_timer(&ucs->timer_int_in, int_in_resubmit, (unsigned long) cs); init_waitqueue_head(&ucs->waitqueue); + INIT_WORK(&ucs->int_in_wq, int_in_work); return 1; } @@ -2282,6 +2370,7 @@ static int gigaset_probe(struct usb_interface *interface, get_usb_rcmsg(rc)); goto error; } + ucs->retry_int_in = 0; /* tell the device that the driver is ready */ rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0); @@ -2334,10 +2423,12 @@ static void gigaset_disconnect(struct usb_interface *interface) /* stop driver (common part) */ gigaset_stop(cs); - /* stop timers and URBs, free ressources */ + /* stop delayed work and URBs, free ressources */ del_timer_sync(&ucs->timer_ctrl); del_timer_sync(&ucs->timer_atrdy); del_timer_sync(&ucs->timer_cmd_in); + del_timer_sync(&ucs->timer_int_in); + cancel_work_sync(&ucs->int_in_wq); freeurbs(cs); usb_set_intfdata(interface, NULL); kfree(ucs->rcvbuf); @@ -2400,10 +2491,14 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message) /* in case of timeout, proceed anyway */ } - /* kill all URBs and timers that might still be pending */ + /* kill all URBs and delayed work that might still be pending */ usb_kill_urb(ucs->urb_ctrl); usb_kill_urb(ucs->urb_int_in); del_timer_sync(&ucs->timer_ctrl); + del_timer_sync(&ucs->timer_atrdy); + del_timer_sync(&ucs->timer_cmd_in); + del_timer_sync(&ucs->timer_int_in); + cancel_work_sync(&ucs->int_in_wq); gig_dbg(DEBUG_SUSPEND, "suspend complete"); return 0; @@ -2425,6 +2520,7 @@ static int gigaset_resume(struct usb_interface *intf) get_usb_rcmsg(rc)); return rc; } + ucs->retry_int_in = 0; /* clear suspend flag to reallow activity */ update_basstate(ucs, 0, BS_SUSPEND); |