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 + -
显示快捷键?