bas-gigaset.c

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

C
2,371
字号
/* * USB driver for Gigaset 307x base via direct USB connection. * * Copyright (c) 2001 by Hansjoerg Lipp <hjlipp@web.de>, *                       Tilman Schmidt <tilman@imap.cc>, *                       Stefan Eilers. * * ===================================================================== *	This program is free software; you can redistribute it and/or *	modify it under the terms of the GNU General Public License as *	published by the Free Software Foundation; either version 2 of *	the License, or (at your option) any later version. * ===================================================================== */#include "gigaset.h"#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/timer.h>#include <linux/usb.h>#include <linux/module.h>#include <linux/moduleparam.h>/* Version Information */#define DRIVER_AUTHOR "Tilman Schmidt <tilman@imap.cc>, Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"#define DRIVER_DESC "USB Driver for Gigaset 307x"/* Module parameters */static int startmode = SM_ISDN;static int cidmode = 1;module_param(startmode, int, S_IRUGO);module_param(cidmode, int, S_IRUGO);MODULE_PARM_DESC(startmode, "start in isdn4linux mode");MODULE_PARM_DESC(cidmode, "Call-ID mode");#define GIGASET_MINORS     1#define GIGASET_MINOR      16#define GIGASET_MODULENAME "bas_gigaset"#define GIGASET_DEVFSNAME  "gig/bas/"#define GIGASET_DEVNAME    "ttyGB"/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */#define IF_WRITEBUF 264/* Values for the Gigaset 307x */#define USB_GIGA_VENDOR_ID      0x0681#define USB_3070_PRODUCT_ID     0x0001#define USB_3075_PRODUCT_ID     0x0002#define USB_SX303_PRODUCT_ID    0x0021#define USB_SX353_PRODUCT_ID    0x0022/* table of devices that work with this driver */static struct usb_device_id gigaset_table [] = {	{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },	{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },	{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },	{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) },	{ } /* Terminating entry */};MODULE_DEVICE_TABLE(usb, gigaset_table);/*======================= local function prototypes =============================*//* This function is called if a new device is connected to the USB port. It * checks whether this new device belongs to this driver. */static int gigaset_probe(struct usb_interface *interface,			 const struct usb_device_id *id);/* Function will be called if the device is unplugged */static void gigaset_disconnect(struct usb_interface *interface);static void read_ctrl_callback(struct urb *, struct pt_regs *);static void stopurbs(struct bas_bc_state *);static int atwrite_submit(struct cardstate *, unsigned char *, int);static int start_cbsend(struct cardstate *);/*==============================================================================*/struct bas_cardstate {	struct usb_device	*udev;		/* USB device pointer */	struct usb_interface	*interface;	/* interface for this device */	unsigned char		minor;		/* starting minor number */	struct urb		*urb_ctrl;	/* control pipe default URB */	struct usb_ctrlrequest	dr_ctrl;	struct timer_list	timer_ctrl;	/* control request timeout */	struct timer_list	timer_atrdy;	/* AT command ready timeout */	struct urb		*urb_cmd_out;	/* for sending AT commands */	struct usb_ctrlrequest	dr_cmd_out;	int			retry_cmd_out;	struct urb		*urb_cmd_in;	/* for receiving AT replies */	struct usb_ctrlrequest	dr_cmd_in;	struct timer_list	timer_cmd_in;	/* receive request timeout */	unsigned char		*rcvbuf;	/* AT reply receive buffer */	struct urb		*urb_int_in;	/* URB for interrupt pipe */	unsigned char		int_in_buf[3];	spinlock_t		lock;		/* locks all following */	atomic_t		basstate;	/* bitmap (BS_*) */	int			pending;	/* uncompleted base request */	int			rcvbuf_size;	/* size of AT receive buffer */						/* 0: no receive in progress */	int			retry_cmd_in;	/* receive req retry count */};/* status of direct USB connection to 307x base (bits in basstate) */#define BS_ATOPEN	0x001	/* AT channel open */#define BS_B1OPEN	0x002	/* B channel 1 open */#define BS_B2OPEN	0x004	/* B channel 2 open */#define BS_ATREADY	0x008	/* base ready for AT command */#define BS_INIT		0x010	/* base has signalled INIT_OK */#define BS_ATTIMER	0x020	/* waiting for HD_READY_SEND_ATDATA */#define BS_ATRDPEND	0x040	/* urb_cmd_in in use */#define BS_ATWRPEND	0x080	/* urb_cmd_out in use */static struct gigaset_driver *driver = NULL;static struct cardstate *cardstate = NULL;/* usb specific object needed to register this driver with the usb subsystem */static struct usb_driver gigaset_usb_driver = {	.name =         GIGASET_MODULENAME,	.probe =        gigaset_probe,	.disconnect =   gigaset_disconnect,	.id_table =     gigaset_table,};/* get message text for usb_submit_urb return code */static char *get_usb_rcmsg(int rc){	static char unkmsg[28];	switch (rc) {	case 0:		return "success";	case -ENOMEM:		return "out of memory";	case -ENODEV:		return "device not present";	case -ENOENT:		return "endpoint not present";	case -ENXIO:		return "URB type not supported";	case -EINVAL:		return "invalid argument";	case -EAGAIN:		return "start frame too early or too much scheduled";	case -EFBIG:		return "too many isochronous frames requested";	case -EPIPE:		return "endpoint stalled";	case -EMSGSIZE:		return "invalid packet size";	case -ENOSPC:		return "would overcommit USB bandwidth";	case -ESHUTDOWN:		return "device shut down";	case -EPERM:		return "reject flag set";	case -EHOSTUNREACH:		return "device suspended";	default:		snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc);		return unkmsg;	}}/* get message text for USB status code */static char *get_usb_statmsg(int status){	static char unkmsg[28];	switch (status) {	case 0:		return "success";	case -ENOENT:		return "unlinked (sync)";	case -EINPROGRESS:		return "pending";	case -EPROTO:		return "bit stuffing error, timeout, or unknown USB error";	case -EILSEQ:		return "CRC mismatch, timeout, or unknown USB error";	case -ETIMEDOUT:		return "timed out";	case -EPIPE:		return "endpoint stalled";	case -ECOMM:		return "IN buffer overrun";	case -ENOSR:		return "OUT buffer underrun";	case -EOVERFLOW:		return "too much data";	case -EREMOTEIO:		return "short packet detected";	case -ENODEV:		return "device removed";	case -EXDEV:		return "partial isochronous transfer";	case -EINVAL:		return "invalid argument";	case -ECONNRESET:		return "unlinked (async)";	case -ESHUTDOWN:		return "device shut down";	default:		snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status);		return unkmsg;	}}/* usb_pipetype_str * retrieve string representation of USB pipe type */static inline char *usb_pipetype_str(int pipe){	if (usb_pipeisoc(pipe))		return "Isoc";	if (usb_pipeint(pipe))		return "Int";	if (usb_pipecontrol(pipe))		return "Ctrl";	if (usb_pipebulk(pipe))		return "Bulk";	return "?";}/* dump_urb * write content of URB to syslog for debugging */static inline void dump_urb(enum debuglevel level, const char *tag,			    struct urb *urb){#ifdef CONFIG_GIGASET_DEBUG	int i;	gig_dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb);	if (urb) {		gig_dbg(level,			"  dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, "			"status=%d, hcpriv=0x%08lx, transfer_flags=0x%x,",			(unsigned long) urb->dev,			usb_pipetype_str(urb->pipe),			usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe),			usb_pipein(urb->pipe) ? "in" : "out",			urb->status, (unsigned long) urb->hcpriv,			urb->transfer_flags);		gig_dbg(level,			"  transfer_buffer=0x%08lx[%d], actual_length=%d, "			"bandwidth=%d, setup_packet=0x%08lx,",			(unsigned long) urb->transfer_buffer,			urb->transfer_buffer_length, urb->actual_length,			urb->bandwidth, (unsigned long) urb->setup_packet);		gig_dbg(level,			"  start_frame=%d, number_of_packets=%d, interval=%d, "			"error_count=%d,",			urb->start_frame, urb->number_of_packets, urb->interval,			urb->error_count);		gig_dbg(level,			"  context=0x%08lx, complete=0x%08lx, "			"iso_frame_desc[]={",			(unsigned long) urb->context,			(unsigned long) urb->complete);		for (i = 0; i < urb->number_of_packets; i++) {			struct usb_iso_packet_descriptor *pifd				= &urb->iso_frame_desc[i];			gig_dbg(level,				"    {offset=%u, length=%u, actual_length=%u, "				"status=%u}",				pifd->offset, pifd->length, pifd->actual_length,				pifd->status);		}	}	gig_dbg(level, "}}");#endif}/* read/set modem control bits etc. (m10x only) */static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,				  unsigned new_state){	return -EINVAL;}static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag){	return -EINVAL;}static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag){	return -EINVAL;}/* error_hangup * hang up any existing connection because of an unrecoverable error * This function may be called from any context and takes care of scheduling * the necessary actions for execution outside of interrupt context. * argument: *	B channel control structure */static inline void error_hangup(struct bc_state *bcs){	struct cardstate *cs = bcs->cs;	gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d",		__func__, bcs->channel);	if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL))		dev_err(cs->dev, "event queue full\n");	gigaset_schedule_event(cs);}/* error_reset * reset Gigaset device because of an unrecoverable error * This function may be called from any context, and should take care of * scheduling the necessary actions for execution outside of interrupt context. * Right now, it just generates a kernel message calling for help. * argument: *	controller state structure */static inline void error_reset(struct cardstate *cs){	//FIXME try to recover without bothering the user	dev_err(cs->dev,	    "unrecoverable error - please disconnect Gigaset base to reset\n");}/* check_pending * check for completion of pending control request * parameter: *	ucs	hardware specific controller state structure */static void check_pending(struct bas_cardstate *ucs){	unsigned long flags;	spin_lock_irqsave(&ucs->lock, flags);	switch (ucs->pending) {	case 0:		break;	case HD_OPEN_ATCHANNEL:		if (atomic_read(&ucs->basstate) & BS_ATOPEN)			ucs->pending = 0;		break;	case HD_OPEN_B1CHANNEL:		if (atomic_read(&ucs->basstate) & BS_B1OPEN)			ucs->pending = 0;		break;	case HD_OPEN_B2CHANNEL:		if (atomic_read(&ucs->basstate) & BS_B2OPEN)			ucs->pending = 0;		break;	case HD_CLOSE_ATCHANNEL:		if (!(atomic_read(&ucs->basstate) & BS_ATOPEN))			ucs->pending = 0;		break;	case HD_CLOSE_B1CHANNEL:		if (!(atomic_read(&ucs->basstate) & BS_B1OPEN))			ucs->pending = 0;		break;	case HD_CLOSE_B2CHANNEL:		if (!(atomic_read(&ucs->basstate) & BS_B2OPEN))			ucs->pending = 0;		break;	case HD_DEVICE_INIT_ACK:		/* no reply expected */		ucs->pending = 0;		break;	/* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE	 * are handled separately and should never end up here	 */	default:		dev_warn(&ucs->interface->dev,			 "unknown pending request 0x%02x cleared\n",			 ucs->pending);		ucs->pending = 0;	}	if (!ucs->pending)		del_timer(&ucs->timer_ctrl);	spin_unlock_irqrestore(&ucs->lock, flags);}/* cmd_in_timeout * timeout routine for command input request * argument: *	controller state structure */static void cmd_in_timeout(unsigned long data){	struct cardstate *cs = (struct cardstate *) data;	struct bas_cardstate *ucs = cs->hw.bas;	if (!ucs->rcvbuf_size) {		gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);		return;	}	dev_err(cs->dev, "timeout reading AT response\n");	error_reset(cs);	//FIXME retry?}/* set/clear bits in base connection state, return previous state */inline static int update_basstate(struct bas_cardstate *ucs,				  int set, int clear){	unsigned long flags;	int state;	spin_lock_irqsave(&ucs->lock, flags);	state = atomic_read(&ucs->basstate);	atomic_set(&ucs->basstate, (state & ~clear) | set);	spin_unlock_irqrestore(&ucs->lock, flags);	return state;}/* atread_submit * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout * parameters: *	cs	controller state structure *	timeout	timeout in 1/10 sec., 0: none * return value: *	0 on success *	-EBUSY if another request is pending *	any URB submission error code */static int atread_submit(struct cardstate *cs, int timeout){	struct bas_cardstate *ucs = cs->hw.bas;	int ret;	gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",		ucs->rcvbuf_size);	if (update_basstate(ucs, BS_ATRDPEND, 0) & BS_ATRDPEND) {		dev_err(cs->dev,			"could not submit HD_READ_ATMESSAGE: URB busy\n");		return -EBUSY;	}	ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ;	ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE;	ucs->dr_cmd_in.wValue = 0;	ucs->dr_cmd_in.wIndex = 0;	ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size);	usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev,			     usb_rcvctrlpipe(ucs->udev, 0),			     (unsigned char*) & ucs->dr_cmd_in,			     ucs->rcvbuf, ucs->rcvbuf_size,			     read_ctrl_callback, cs->inbuf);	if ((ret = usb_submit_urb(ucs->urb_cmd_in, SLAB_ATOMIC)) != 0) {		update_basstate(ucs, 0, BS_ATRDPEND);		dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",			get_usb_statmsg(ret));		return ret;	}	if (timeout > 0) {		gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);		ucs->timer_cmd_in.expires = jiffies + timeout * HZ / 10;

⌨️ 快捷键说明

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