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