bas-gigaset.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,371 行 · 第 1/5 页
C
2,371 行
gig_dbg(DEBUG_ISO, "%s: isoc output URB %d unlinked, result = %s", __func__, k, get_usb_rcmsg(rc)); }}/* Isochronous Write - Bottom Half *//* =============================== *//* submit_iso_write_urb * fill and submit the next isochronous write URB * parameters: * ucx context structure containing URB * return value: * number of frames submitted in URB * 0 if URB not submitted because no data available (isooutbuf busy) * error code < 0 on error */static int submit_iso_write_urb(struct isow_urbctx_t *ucx){ struct urb *urb = ucx->urb; struct bas_bc_state *ubc = ucx->bcs->hw.bas; struct usb_iso_packet_descriptor *ifd; int corrbytes, nframe, rc; /* urb->dev is clobbered by USB subsystem */ urb->dev = ucx->bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ubc->isooutbuf->data; urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data); for (nframe = 0; nframe < BAS_NUMFRAMES; nframe++) { ifd = &urb->iso_frame_desc[nframe]; /* compute frame length according to flow control */ ifd->length = BAS_NORMFRAME; if ((corrbytes = atomic_read(&ubc->corrbytes)) != 0) { gig_dbg(DEBUG_ISO, "%s: corrbytes=%d", __func__, corrbytes); if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME) corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME; else if (corrbytes < BAS_LOWFRAME - BAS_NORMFRAME) corrbytes = BAS_LOWFRAME - BAS_NORMFRAME; ifd->length += corrbytes; atomic_add(-corrbytes, &ubc->corrbytes); } /* retrieve block of data to send */ ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length); if (ifd->offset < 0) { if (ifd->offset == -EBUSY) { gig_dbg(DEBUG_ISO, "%s: buffer busy at frame %d", __func__, nframe); /* tasklet will be restarted from gigaset_send_skb() */ } else { dev_err(ucx->bcs->cs->dev, "%s: buffer error %d at frame %d\n", __func__, ifd->offset, nframe); return ifd->offset; } break; } ucx->limit = atomic_read(&ubc->isooutbuf->nextread); ifd->status = 0; ifd->actual_length = 0; } if (unlikely(nframe == 0)) return 0; /* no data to send */ urb->number_of_packets = nframe; rc = usb_submit_urb(urb, SLAB_ATOMIC); if (unlikely(rc)) { if (rc == -ENODEV) /* device removed - give up silently */ gig_dbg(DEBUG_ISO, "%s: disconnected", __func__); else dev_err(ucx->bcs->cs->dev, "could not submit isochronous write URB: %s\n", get_usb_rcmsg(rc)); return rc; } ++ubc->numsub; return nframe;}/* write_iso_tasklet * tasklet scheduled when an isochronous output URB from the Gigaset device * has completed * parameter: * data B channel state structure */static void write_iso_tasklet(unsigned long data){ struct bc_state *bcs = (struct bc_state *) data; struct bas_bc_state *ubc = bcs->hw.bas; struct cardstate *cs = bcs->cs; struct isow_urbctx_t *done, *next, *ovfl; struct urb *urb; struct usb_iso_packet_descriptor *ifd; int offset; unsigned long flags; int i; struct sk_buff *skb; int len; int rc; /* loop while completed URBs arrive in time */ for (;;) { if (unlikely(!(atomic_read(&ubc->running)))) { gig_dbg(DEBUG_ISO, "%s: not running", __func__); return; } /* retrieve completed URBs */ spin_lock_irqsave(&ubc->isooutlock, flags); done = ubc->isooutdone; ubc->isooutdone = NULL; ovfl = ubc->isooutovfl; ubc->isooutovfl = NULL; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (ovfl) { dev_err(cs->dev, "isochronous write buffer underrun\n"); error_hangup(bcs); break; } if (!done) break; /* submit free URB if available */ spin_lock_irqsave(&ubc->isooutlock, flags); next = ubc->isooutfree; ubc->isooutfree = NULL; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { rc = submit_iso_write_urb(next); if (unlikely(rc <= 0 && rc != -ENODEV)) { /* could not submit URB, put it back */ spin_lock_irqsave(&ubc->isooutlock, flags); if (ubc->isooutfree == NULL) { ubc->isooutfree = next; next = NULL; } spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { /* couldn't put it back */ dev_err(cs->dev, "losing isochronous write URB\n"); error_hangup(bcs); } } } /* process completed URB */ urb = done->urb; switch (urb->status) { case -EXDEV: /* partial completion */ gig_dbg(DEBUG_ISO, "%s: URB partially completed", __func__); /* fall through - what's the difference anyway? */ case 0: /* normal completion */ /* inspect individual frames * assumptions (for lack of documentation): * - actual_length bytes of first frame in error are * successfully sent * - all following frames are not sent at all */ offset = done->limit; /* default (no error) */ for (i = 0; i < BAS_NUMFRAMES; i++) { ifd = &urb->iso_frame_desc[i]; 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); offset = (ifd->offset + ifd->actual_length) % BAS_OUTBUFSIZE; break; } }#ifdef CONFIG_GIGASET_DEBUG /* check assumption on remaining frames */ for (; i < BAS_NUMFRAMES; i++) { ifd = &urb->iso_frame_desc[i]; if (ifd->status != -EINPROGRESS || ifd->actual_length != 0) { dev_warn(cs->dev, "isochronous write: frame %d: %s, " "%d of %d bytes sent\n", i, get_usb_statmsg(ifd->status), ifd->actual_length, ifd->length); offset = (ifd->offset + ifd->actual_length) % BAS_OUTBUFSIZE; break; } }#endif break; case -EPIPE: /* stall - probably underrun */ dev_err(cs->dev, "isochronous write stalled\n"); error_hangup(bcs); break; default: /* severe trouble */ dev_warn(cs->dev, "isochronous write: %s\n", get_usb_statmsg(urb->status)); } /* mark the write buffer area covered by this URB as free */ if (done->limit >= 0) atomic_set(&ubc->isooutbuf->read, done->limit); /* mark URB as free */ spin_lock_irqsave(&ubc->isooutlock, flags); next = ubc->isooutfree; ubc->isooutfree = done; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { /* only one URB still active - resubmit one */ rc = submit_iso_write_urb(next); if (unlikely(rc <= 0 && rc != -ENODEV)) { /* couldn't submit */ error_hangup(bcs); } } } /* process queued SKBs */ while ((skb = skb_dequeue(&bcs->squeue))) { /* copy to output buffer, doing L2 encapsulation */ len = skb->len; if (gigaset_isoc_buildframe(bcs, skb->data, len) == -EAGAIN) { /* insufficient buffer space, push back onto queue */ skb_queue_head(&bcs->squeue, skb); gig_dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d", __func__, skb_queue_len(&bcs->squeue)); break; } skb_pull(skb, len); gigaset_skb_sent(bcs, skb); dev_kfree_skb_any(skb); }}/* Isochronous Read - Bottom Half *//* ============================== *//* read_iso_tasklet * tasklet scheduled when an isochronous input URB from the Gigaset device * has completed * parameter: * data B channel state structure */static void read_iso_tasklet(unsigned long data){ struct bc_state *bcs = (struct bc_state *) data; struct bas_bc_state *ubc = bcs->hw.bas; struct cardstate *cs = bcs->cs; struct urb *urb; char *rcvbuf; unsigned long flags; int totleft, numbytes, offset, frame, rc; /* loop while more completed URBs arrive in the meantime */ for (;;) { /* retrieve URB */ spin_lock_irqsave(&ubc->isoinlock, flags); if (!(urb = ubc->isoindone)) { spin_unlock_irqrestore(&ubc->isoinlock, flags); return; } ubc->isoindone = NULL; if (unlikely(ubc->loststatus != -EINPROGRESS)) { dev_warn(cs->dev, "isochronous read overrun, " "dropped URB with status: %s, %d bytes lost\n", get_usb_statmsg(ubc->loststatus), ubc->isoinlost); ubc->loststatus = -EINPROGRESS; } spin_unlock_irqrestore(&ubc->isoinlock, flags); if (unlikely(!(atomic_read(&ubc->running)))) { gig_dbg(DEBUG_ISO, "%s: channel not running, " "dropped URB with status: %s", __func__, get_usb_statmsg(urb->status)); return; } switch (urb->status) { case 0: /* normal completion */ break; case -EXDEV: /* inspect individual frames (we do that anyway) */ gig_dbg(DEBUG_ISO, "%s: URB partially completed", __func__); break; case -ENOENT: case -ECONNRESET: case -EINPROGRESS: gig_dbg(DEBUG_ISO, "%s: %s", __func__, get_usb_statmsg(urb->status)); continue; /* -> skip */ case -EPIPE: dev_err(cs->dev, "isochronous read stalled\n"); error_hangup(bcs); continue; /* -> skip */ default: /* severe trouble */ dev_warn(cs->dev, "isochronous read: %s\n", get_usb_statmsg(urb->status)); goto error; } rcvbuf = urb->transfer_buffer; totleft = urb->actual_length; for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) { if (unlikely(urb->iso_frame_desc[frame].status)) { dev_warn(cs->dev, "isochronous read: frame %d: %s\n", frame, get_usb_statmsg( urb->iso_frame_desc[frame].status)); break; } numbytes = urb->iso_frame_desc[frame].actual_length; if (unlikely(numbytes > BAS_MAXFRAME)) { dev_warn(cs->dev, "isochronous read: frame %d: " "numbytes (%d) > BAS_MAXFRAME\n", frame, numbytes); break; } if (unlikely(numbytes > totleft)) { dev_warn(cs->dev, "isochronous read: frame %d: " "numbytes (%d) > totleft (%d)\n", frame, numbytes, totleft); break; } offset = urb->iso_frame_desc[frame].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); break; } gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs); totleft -= numbytes; } if (unlikely(totleft > 0)) dev_warn(cs->dev, "isochronous read: %d data bytes missing\n", totleft); error: /* URB processed, resubmit */ for (frame = 0; frame < BAS_NUMFRAMES; frame++) { urb->iso_frame_desc[frame].status = 0; urb->iso_frame_desc[frame].actual_length = 0; } /* urb->dev is clobbered by USB subsystem */ urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = BAS_NUMFRAMES; rc = usb_submit_urb(urb, SLAB_ATOMIC); if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(cs->dev, "could not resubmit isochronous read URB: %s\n", get_usb_rcmsg(rc)); dump_urb(DEBUG_ISO, "resubmit iso read", urb); error_hangup(bcs); } }}/* Channel Operations *//* ================== *//* req_timeout * timeout routine for control output request * argument: * B channel control structure */static void req_timeout(unsigned long data){ struct bc_state *bcs = (struct bc_state *) data; struct bas_cardstate *ucs = bcs->cs->hw.bas; int pending; unsigned long flags; check_pending(ucs); spin_lock_irqsave(&ucs->lock, flags); pending = ucs->pending; ucs->pending = 0; spin_unlock_irqrestore(&ucs->lock, flags); switch (pending) { case 0: /* no pending request */ gig_dbg(DEBUG_USBREQ, "%s: no request pending", __func__); break; case HD_OPEN_ATCHANNEL: dev_err(bcs->cs->dev, "timeout opening AT channel\n"); error_reset(bcs->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); break; case HD_CLOSE_ATCHANNEL: dev_err(bcs->cs->dev, "timeout closing AT channel\n"); break; case HD_CLOSE_B2CHANNEL: case HD_CLOSE_B1CHANNEL: dev_err(bcs->cs->dev, "timeout closing channel %d\n", bcs->channel + 1); break; default: dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", pending); }}/* write_ctrl_callback * USB completion handler for control pipe output * called by the USB subsystem in interrupt context * parameter: * urb USB request block of completed request * urb->context = hardware specific controller state structure */static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs){ struct bas_cardstate *ucs = urb->context; unsigned long flags; spin_lock_irqsave(&ucs->lock, flags); if (urb->status && ucs->pending) { dev_err(&ucs->interface->dev, "control request 0x%02x failed: %s\n", ucs->pending, get_usb_statmsg(urb->status)); del_timer(&ucs->timer_ctrl); ucs->pending = 0; } /* individual handling of specific request types */ switch (ucs->pending) { case HD_DEVICE_INIT_ACK: /* no reply expected */ ucs->pending = 0; break; } spin_unlock_irqrestore(&ucs->lock, flags);}/* req_submit * submit a control output request without message buffer to the Gigaset base * and optionally start a timeout * parameters: * bcs B channel control structure * req control request code (HD_*) * val control request parameter value (set to 0 if unused) * timeout timeout in seconds (0: no timeout)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?