bas-gigaset.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,371 行 · 第 1/5 页
C
2,371 行
ucs->timer_cmd_in.data = (unsigned long) cs; ucs->timer_cmd_in.function = cmd_in_timeout; add_timer(&ucs->timer_cmd_in); } return 0;}/* read_int_callback * USB completion handler for interrupt pipe input * called by the USB subsystem in interrupt context * parameter: * urb USB request block * urb->context = controller state structure */static void read_int_callback(struct urb *urb, struct pt_regs *regs){ struct cardstate *cs = urb->context; struct bas_cardstate *ucs = cs->hw.bas; struct bc_state *bcs; unsigned long flags; int rc; unsigned l; int channel; switch (urb->status) { case 0: /* success */ break; case -ENOENT: /* cancelled */ case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; case -ENODEV: /* device removed */ case -ESHUTDOWN: /* device shut down */ //FIXME use this as disconnect indicator? gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__); return; default: /* severe trouble */ dev_warn(cs->dev, "interrupt read: %s\n", get_usb_statmsg(urb->status)); //FIXME corrective action? resubmission always ok? goto resubmit; } /* drop incomplete packets even if the missing bytes wouldn't matter */ if (unlikely(urb->actual_length < 3)) { dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n", urb->actual_length); goto resubmit; } l = (unsigned) ucs->int_in_buf[1] + (((unsigned) ucs->int_in_buf[2]) << 8); gig_dbg(DEBUG_USBREQ, "<-------%d: 0x%02x (%u [0x%02x 0x%02x])", urb->actual_length, (int)ucs->int_in_buf[0], l, (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]); channel = 0; switch (ucs->int_in_buf[0]) { case HD_DEVICE_INIT_OK: update_basstate(ucs, BS_INIT, 0); break; case HD_READY_SEND_ATDATA: del_timer(&ucs->timer_atrdy); update_basstate(ucs, BS_ATREADY, BS_ATTIMER); start_cbsend(cs); break; case HD_OPEN_B2CHANNEL_ACK: ++channel; case HD_OPEN_B1CHANNEL_ACK: bcs = cs->bcs + channel; update_basstate(ucs, BS_B1OPEN << channel, 0); gigaset_bchannel_up(bcs); break; case HD_OPEN_ATCHANNEL_ACK: update_basstate(ucs, BS_ATOPEN, 0); start_cbsend(cs); break; case HD_CLOSE_B2CHANNEL_ACK: ++channel; case HD_CLOSE_B1CHANNEL_ACK: bcs = cs->bcs + channel; update_basstate(ucs, 0, BS_B1OPEN << channel); stopurbs(bcs->hw.bas); gigaset_bchannel_down(bcs); break; case HD_CLOSE_ATCHANNEL_ACK: update_basstate(ucs, 0, BS_ATOPEN); break; case HD_B2_FLOW_CONTROL: ++channel; case HD_B1_FLOW_CONTROL: bcs = cs->bcs + channel; atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES, &bcs->hw.bas->corrbytes); gig_dbg(DEBUG_ISO, "Flow control (channel %d, sub %d): 0x%02x => %d", channel, bcs->hw.bas->numsub, l, atomic_read(&bcs->hw.bas->corrbytes)); break; case HD_RECEIVEATDATA_ACK: /* AT response ready to be received */ if (!l) { dev_warn(cs->dev, "HD_RECEIVEATDATA_ACK with length 0 ignored\n"); break; } spin_lock_irqsave(&cs->lock, flags); if (ucs->rcvbuf_size) { /* throw away previous buffer - we have no queue */ dev_err(cs->dev, "receive AT data overrun, %d bytes lost\n", ucs->rcvbuf_size); kfree(ucs->rcvbuf); ucs->rcvbuf_size = 0; } if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == 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; ucs->retry_cmd_in = 0; if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) { kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; if (rc != -ENODEV) //FIXME corrective action? error_reset(cs); } spin_unlock_irqrestore(&cs->lock, flags); break; case HD_RESET_INTERRUPT_PIPE_ACK: gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK"); break; case HD_SUSPEND_END: gig_dbg(DEBUG_USBREQ, "HD_SUSPEND_END"); break; default: dev_warn(cs->dev, "unknown Gigaset signal 0x%02x (%u) ignored\n", (int) ucs->int_in_buf[0], l); } check_pending(ucs);resubmit: rc = usb_submit_urb(urb, SLAB_ATOMIC); if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", get_usb_rcmsg(rc)); error_reset(cs); }}/* read_ctrl_callback * USB completion handler for control pipe input * called by the USB subsystem in interrupt context * parameter: * urb USB request block * urb->context = inbuf structure for controller state */static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs){ struct inbuf_t *inbuf = urb->context; struct cardstate *cs = inbuf->cs; struct bas_cardstate *ucs = cs->hw.bas; int have_data = 0; unsigned numbytes; int rc; update_basstate(ucs, 0, BS_ATRDPEND); if (!ucs->rcvbuf_size) { dev_warn(cs->dev, "%s: no receive in progress\n", __func__); return; } del_timer(&ucs->timer_cmd_in); switch (urb->status) { case 0: /* normal completion */ numbytes = urb->actual_length; if (unlikely(numbytes == 0)) { dev_warn(cs->dev, "control read: empty block received\n"); goto retry; } if (unlikely(numbytes != ucs->rcvbuf_size)) { dev_warn(cs->dev, "control read: received %d chars, expected %d\n", numbytes, ucs->rcvbuf_size); if (numbytes > ucs->rcvbuf_size) 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; goto retry; } break; case -ENOENT: /* cancelled */ case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ case -ENODEV: /* device removed */ case -ESHUTDOWN: /* device shut down */ /* no action necessary */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(urb->status)); break; default: /* severe trouble */ dev_warn(cs->dev, "control read: %s\n", get_usb_statmsg(urb->status)); retry: if (ucs->retry_cmd_in++ < BAS_RETRY) { dev_notice(cs->dev, "control read: 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: giving up after %d tries\n", ucs->retry_cmd_in); } error_reset(cs); } kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; if (have_data) { gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(cs); }}/* read_iso_callback * USB completion handler for B channel isochronous input * called by the USB subsystem in interrupt context * parameter: * urb USB request block of completed request * urb->context = bc_state structure */static void read_iso_callback(struct urb *urb, struct pt_regs *regs){ struct bc_state *bcs; struct bas_bc_state *ubc; unsigned long flags; int i, rc; /* status codes not worth bothering the tasklet with */ if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -EINPROGRESS || urb->status == -ENODEV || urb->status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; } bcs = urb->context; ubc = bcs->hw.bas; spin_lock_irqsave(&ubc->isoinlock, flags); if (likely(ubc->isoindone == NULL)) { /* pass URB to tasklet */ ubc->isoindone = urb; tasklet_schedule(&ubc->rcvd_tasklet); } else { /* tasklet still busy, drop data and resubmit URB */ ubc->loststatus = urb->status; for (i = 0; i < BAS_NUMFRAMES; i++) { ubc->isoinlost += urb->iso_frame_desc[i].actual_length; if (unlikely(urb->iso_frame_desc[i].status != 0 && urb->iso_frame_desc[i].status != -EINPROGRESS)) ubc->loststatus = urb->iso_frame_desc[i].status; urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } if (likely(atomic_read(&ubc->running))) { /* 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; gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit", __func__); rc = usb_submit_urb(urb, SLAB_ATOMIC); if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(bcs->cs->dev, "could not resubmit isochronous read " "URB: %s\n", get_usb_rcmsg(rc)); dump_urb(DEBUG_ISO, "isoc read", urb); error_hangup(bcs); } } } spin_unlock_irqrestore(&ubc->isoinlock, flags);}/* write_iso_callback * USB completion handler for B channel isochronous output * called by the USB subsystem in interrupt context * parameter: * urb USB request block of completed request * urb->context = isow_urbctx_t structure */static void write_iso_callback(struct urb *urb, struct pt_regs *regs){ struct isow_urbctx_t *ucx; struct bas_bc_state *ubc; unsigned long flags; /* status codes not worth bothering the tasklet with */ if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -EINPROGRESS || urb->status == -ENODEV || urb->status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; } /* pass URB context to tasklet */ ucx = urb->context; ubc = ucx->bcs->hw.bas; spin_lock_irqsave(&ubc->isooutlock, flags); ubc->isooutovfl = ubc->isooutdone; ubc->isooutdone = ucx; spin_unlock_irqrestore(&ubc->isooutlock, flags); tasklet_schedule(&ubc->sent_tasklet);}/* starturbs * prepare and submit USB request blocks for isochronous input and output * argument: * B channel control structure * return value: * 0 on success * < 0 on error (no URBs submitted) */static int starturbs(struct bc_state *bcs){ struct bas_bc_state *ubc = bcs->hw.bas; struct urb *urb; int j, k; int rc; /* initialize L2 reception */ if (bcs->proto2 == ISDN_PROTO_L2_HDLC) bcs->inputstate |= INS_flag_hunt; /* submit all isochronous input URBs */ atomic_set(&ubc->running, 1); for (k = 0; k < BAS_INURBS; k++) { urb = ubc->isoinurbs[k]; if (!urb) { rc = -EFAULT; goto error; } urb->dev = bcs->cs->hw.bas->udev; urb->pipe = usb_rcvisocpipe(urb->dev, 3 + 2 * bcs->channel); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ubc->isoinbuf + k * BAS_INBUFSIZE; urb->transfer_buffer_length = BAS_INBUFSIZE; urb->number_of_packets = BAS_NUMFRAMES; urb->interval = BAS_FRAMETIME; urb->complete = read_iso_callback; urb->context = bcs; for (j = 0; j < BAS_NUMFRAMES; j++) { urb->iso_frame_desc[j].offset = j * BAS_MAXFRAME; urb->iso_frame_desc[j].length = BAS_MAXFRAME; urb->iso_frame_desc[j].status = 0; urb->iso_frame_desc[j].actual_length = 0; } dump_urb(DEBUG_ISO, "Initial isoc read", urb); if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) goto error; } /* initialize L2 transmission */ gigaset_isowbuf_init(ubc->isooutbuf, PPP_FLAG); /* set up isochronous output URBs for flag idling */ for (k = 0; k < BAS_OUTURBS; ++k) { urb = ubc->isoouturbs[k].urb; if (!urb) { rc = -EFAULT; goto error; } urb->dev = bcs->cs->hw.bas->udev; urb->pipe = usb_sndisocpipe(urb->dev, 4 + 2 * bcs->channel); urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ubc->isooutbuf->data; urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data); urb->number_of_packets = BAS_NUMFRAMES; urb->interval = BAS_FRAMETIME; urb->complete = write_iso_callback; urb->context = &ubc->isoouturbs[k]; for (j = 0; j < BAS_NUMFRAMES; ++j) { urb->iso_frame_desc[j].offset = BAS_OUTBUFSIZE; urb->iso_frame_desc[j].length = BAS_NORMFRAME; urb->iso_frame_desc[j].status = 0; urb->iso_frame_desc[j].actual_length = 0; } ubc->isoouturbs[k].limit = -1; } /* submit two URBs, keep third one */ for (k = 0; k < 2; ++k) { dump_urb(DEBUG_ISO, "Initial isoc write", urb); rc = usb_submit_urb(ubc->isoouturbs[k].urb, SLAB_ATOMIC); if (rc != 0) goto error; } dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb); ubc->isooutfree = &ubc->isoouturbs[2]; ubc->isooutdone = ubc->isooutovfl = NULL; return 0; error: stopurbs(ubc); return rc;}/* stopurbs * cancel the USB request blocks for isochronous input and output * errors are silently ignored * argument: * B channel control structure */static void stopurbs(struct bas_bc_state *ubc){ int k, rc; atomic_set(&ubc->running, 0); for (k = 0; k < BAS_INURBS; ++k) { rc = usb_unlink_urb(ubc->isoinurbs[k]); gig_dbg(DEBUG_ISO, "%s: isoc input URB %d unlinked, result = %s", __func__, k, get_usb_rcmsg(rc)); } for (k = 0; k < BAS_OUTURBS; ++k) { rc = usb_unlink_urb(ubc->isoouturbs[k].urb);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?