bas-gigaset.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,371 行 · 第 1/5 页

C
2,371
字号
 * return value: *	0 on success *	-EBUSY if another request is pending *	any URB submission error code */static int req_submit(struct bc_state *bcs, int req, int val, int timeout){	struct bas_cardstate *ucs = bcs->cs->hw.bas;	int ret;	unsigned long flags;	gig_dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val);	spin_lock_irqsave(&ucs->lock, flags);	if (ucs->pending) {		spin_unlock_irqrestore(&ucs->lock, flags);		dev_err(bcs->cs->dev,			"submission of request 0x%02x failed: "			"request 0x%02x still pending\n",			req, ucs->pending);		return -EBUSY;	}	ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ;	ucs->dr_ctrl.bRequest = req;	ucs->dr_ctrl.wValue = cpu_to_le16(val);	ucs->dr_ctrl.wIndex = 0;	ucs->dr_ctrl.wLength = 0;	usb_fill_control_urb(ucs->urb_ctrl, ucs->udev,			     usb_sndctrlpipe(ucs->udev, 0),			     (unsigned char*) &ucs->dr_ctrl, NULL, 0,			     write_ctrl_callback, ucs);	if ((ret = usb_submit_urb(ucs->urb_ctrl, SLAB_ATOMIC)) != 0) {		dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n",			req, get_usb_statmsg(ret));		spin_unlock_irqrestore(&ucs->lock, flags);		return ret;	}	ucs->pending = req;	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);	}	spin_unlock_irqrestore(&ucs->lock, flags);	return 0;}/* gigaset_init_bchannel * called by common.c to connect a B channel * initialize isochronous I/O and tell the Gigaset base to open the channel * argument: *	B channel control structure * return value: *	0 on success, error code < 0 on error */static int gigaset_init_bchannel(struct bc_state *bcs){	int req, ret;	unsigned long flags;	spin_lock_irqsave(&bcs->cs->lock, flags);	if (unlikely(!bcs->cs->connected)) {		gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);		spin_unlock_irqrestore(&bcs->cs->lock, flags);		return -ENODEV;	}	if ((ret = starturbs(bcs)) < 0) {		dev_err(bcs->cs->dev,			"could not start isochronous 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(&bcs->cs->lock, flags);		return ret;	}	req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;	if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) {		dev_err(bcs->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(&bcs->cs->lock, flags);	return ret;}/* gigaset_close_bchannel * called by common.c to disconnect a B channel * tell the Gigaset base to close the channel * stopping isochronous I/O and LL notification will be done when the * acknowledgement for the close arrives * argument: *	B channel control structure * return value: *	0 on success, error code < 0 on error */static int gigaset_close_bchannel(struct bc_state *bcs){	int req, ret;	unsigned long flags;	spin_lock_irqsave(&bcs->cs->lock, flags);	if (unlikely(!bcs->cs->connected)) {		spin_unlock_irqrestore(&bcs->cs->lock, flags);		gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);		return -ENODEV;	}	if (!(atomic_read(&bcs->cs->hw.bas->basstate) &	      (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {		/* channel not running: just signal common.c */		spin_unlock_irqrestore(&bcs->cs->lock, flags);		gigaset_bchannel_down(bcs);		return 0;	}	/* channel running: tell device to close it */	req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;	if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0)		dev_err(bcs->cs->dev, "closing channel B%d failed\n",			bcs->channel + 1);	spin_unlock_irqrestore(&bcs->cs->lock, flags);	return ret;}/* Device Operations *//* ================= *//* complete_cb * unqueue first command buffer from queue, waking any sleepers * must be called with cs->cmdlock held * parameter: *	cs	controller state structure */static void complete_cb(struct cardstate *cs){	struct cmdbuf_t *cb = cs->cmdbuf;	/* unqueue completed buffer */	cs->cmdbytes -= cs->curlen;	gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD,		"write_command: sent %u bytes, %u left",		cs->curlen, cs->cmdbytes);	if ((cs->cmdbuf = cb->next) != NULL) {		cs->cmdbuf->prev = NULL;		cs->curlen = cs->cmdbuf->len;	} else {		cs->lastcmdbuf = NULL;		cs->curlen = 0;	}	if (cb->wake_tasklet)		tasklet_schedule(cb->wake_tasklet);	kfree(cb);}/* write_command_callback * USB completion handler for AT command transmission * called by the USB subsystem in interrupt context * parameter: *	urb	USB request block of completed request *		urb->context = controller state structure */static void write_command_callback(struct urb *urb, struct pt_regs *regs){	struct cardstate *cs = urb->context;	struct bas_cardstate *ucs = cs->hw.bas;	unsigned long flags;	update_basstate(ucs, 0, BS_ATWRPEND);	/* check status */	switch (urb->status) {	case 0:					/* normal completion */		break;	case -ENOENT:			/* cancelled */	case -ECONNRESET:		/* cancelled (async) */	case -EINPROGRESS:		/* pending */	case -ENODEV:			/* device removed */	case -ESHUTDOWN:		/* device shut down */		/* ignore silently */		gig_dbg(DEBUG_USBREQ, "%s: %s",			__func__, get_usb_statmsg(urb->status));		return;	default:				/* any failure */		if (++ucs->retry_cmd_out > BAS_RETRY) {			dev_warn(cs->dev,				 "command write: %s, "				 "giving up after %d retries\n",				 get_usb_statmsg(urb->status),				 ucs->retry_cmd_out);			break;		}		if (cs->cmdbuf == NULL) {			dev_warn(cs->dev,				 "command write: %s, "				 "cannot retry - cmdbuf gone\n",				 get_usb_statmsg(urb->status));			break;		}		dev_notice(cs->dev, "command write: %s, retry %d\n",			   get_usb_statmsg(urb->status), ucs->retry_cmd_out);		if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0)			/* resubmitted - bypass regular exit block */			return;		/* command send failed, assume base still waiting */		update_basstate(ucs, BS_ATREADY, 0);	}	spin_lock_irqsave(&cs->cmdlock, flags);	if (cs->cmdbuf != NULL)		complete_cb(cs);	spin_unlock_irqrestore(&cs->cmdlock, flags);}/* atrdy_timeout * timeout routine for AT command transmission * argument: *	controller state structure */static void atrdy_timeout(unsigned long data){	struct cardstate *cs = (struct cardstate *) data;	struct bas_cardstate *ucs = cs->hw.bas;	dev_warn(cs->dev, "timeout waiting for HD_READY_SEND_ATDATA\n");	/* fake the missing signal - what else can I do? */	update_basstate(ucs, BS_ATREADY, BS_ATTIMER);	start_cbsend(cs);}/* atwrite_submit * submit an HD_WRITE_ATMESSAGE command URB * parameters: *	cs	controller state structure *	buf	buffer containing command to send *	len	length of command to send * return value: *	0 on success *	-EBUSY if another request is pending *	any URB submission error code */static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len){	struct bas_cardstate *ucs = cs->hw.bas;	int rc;	gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len);	if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) {		dev_err(cs->dev,			"could not submit HD_WRITE_ATMESSAGE: URB busy\n");		return -EBUSY;	}	ucs->dr_cmd_out.bRequestType = OUT_VENDOR_REQ;	ucs->dr_cmd_out.bRequest = HD_WRITE_ATMESSAGE;	ucs->dr_cmd_out.wValue = 0;	ucs->dr_cmd_out.wIndex = 0;	ucs->dr_cmd_out.wLength = cpu_to_le16(len);	usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev,			     usb_sndctrlpipe(ucs->udev, 0),			     (unsigned char*) &ucs->dr_cmd_out, buf, len,			     write_command_callback, cs);	rc = usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC);	if (unlikely(rc)) {		update_basstate(ucs, 0, BS_ATWRPEND);		dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n",			get_usb_rcmsg(rc));		return rc;	}	/* submitted successfully, start timeout if necessary */	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);	}	return 0;}/* start_cbsend * start transmission of AT command queue if necessary * parameter: *	cs		controller state structure * return value: *	0 on success *	error code < 0 on error */static int start_cbsend(struct cardstate *cs){	struct cmdbuf_t *cb;	struct bas_cardstate *ucs = cs->hw.bas;	unsigned long flags;	int rc;	int retval = 0;	/* check if AT channel is open */	if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) {		gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open");		rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);		if (rc < 0) {			/* flush command queue */			spin_lock_irqsave(&cs->cmdlock, flags);			while (cs->cmdbuf != NULL)				complete_cb(cs);			spin_unlock_irqrestore(&cs->cmdlock, flags);		}		return rc;	}	/* try to send first command in queue */	spin_lock_irqsave(&cs->cmdlock, flags);	while ((cb = cs->cmdbuf) != NULL &&	       atomic_read(&ucs->basstate) & BS_ATREADY) {		ucs->retry_cmd_out = 0;		rc = atwrite_submit(cs, cb->buf, cb->len);		if (unlikely(rc)) {			retval = rc;			complete_cb(cs);		}	}	spin_unlock_irqrestore(&cs->cmdlock, flags);	return retval;}/* gigaset_write_cmd * This function is called by the device independent part of the driver * to transmit an AT command string to the Gigaset device. * It encapsulates the device specific method for transmission over the * direct USB connection to the base. * The command string is added to the queue of commands to send, and * USB transmission is started if necessary. * parameters: *	cs		controller state structure *	buf		command string to send *	len		number of bytes to send (max. IF_WRITEBUF) *	wake_tasklet	tasklet to run when transmission is completed *			(NULL if none) * return value: *	number of bytes queued on success *	error code < 0 on error */static int gigaset_write_cmd(struct cardstate *cs,			     const unsigned char *buf, int len,			     struct tasklet_struct *wake_tasklet){	struct cmdbuf_t *cb;	unsigned long flags;	int status;	gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ?			     DEBUG_TRANSCMD : DEBUG_LOCKCMD,			   "CMD Transmit", len, buf);	if (len <= 0)		return 0;			/* nothing to do */	if (len > IF_WRITEBUF)		len = IF_WRITEBUF;	if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {		dev_err(cs->dev, "%s: out of memory\n", __func__);		return -ENOMEM;	}	memcpy(cb->buf, buf, len);	cb->len = len;	cb->offset = 0;	cb->next = NULL;	cb->wake_tasklet = wake_tasklet;	spin_lock_irqsave(&cs->cmdlock, flags);	cb->prev = cs->lastcmdbuf;	if (cs->lastcmdbuf)		cs->lastcmdbuf->next = cb;	else {		cs->cmdbuf = cb;		cs->curlen = len;	}	cs->cmdbytes += len;	cs->lastcmdbuf = cb;	spin_unlock_irqrestore(&cs->cmdlock, flags);	spin_lock_irqsave(&cs->lock, flags);	if (unlikely(!cs->connected)) {		spin_unlock_irqrestore(&cs->lock, flags);		gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);		return -ENODEV;	}	status = start_cbsend(cs);	spin_unlock_irqrestore(&cs->lock, flags);	return status < 0 ? status : len;}/* gigaset_write_room * tty_driver.write_room interface routine * return number of characters the driver will accept to be written via * gigaset_write_cmd * parameter: *	controller state structure * return value: *	number of characters */static int gigaset_write_room(struct cardstate *cs){	return IF_WRITEBUF;}/* gigaset_chars_in_buffer * tty_driver.chars_in_buffer interface routine * return number of characters waiting to be sent * parameter: *	controller state structure * return value: *	number of characters */static int gigaset_chars_in_buffer(struct cardstate *cs){	unsigned long flags;	unsigned bytes;	spin_lock_irqsave(&cs->cmdlock, flags);	bytes = cs->cmdbytes;	spin_unlock_irqrestore(&cs->cmdlock, flags);	return bytes;}/* gigaset_brkchars * implementation of ioctl(GIGASET_BRKCHARS) * parameter: *	controller state structure * return value: *	-EINVAL (unimplemented function) */static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6]){	return -EINVAL;}/* Device Initialization/Shutdown *//* ============================== *//* Free hardware dependent part of the B channel structure * parameter: *	bcs	B channel structure * return value: *	!=0 on success */static int gigaset_freebcshw(struct bc_state *bcs){	struct bas_bc_state *ubc = bcs->hw.bas;	int i;	if (!ubc)		return 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?