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