bas-gigaset.c
来自「linux 内核源代码」· C语言 代码 · 共 2,396 行 · 第 1/5 页
C
2,396 行
/* * 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_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 const 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 ==========================*//* function called if a new device belonging to this driver is connected */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 int atread_submit(struct cardstate *, int);static void stopurbs(struct bas_bc_state *);static int req_submit(struct bc_state *, int, int, int);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 */ int retry_ctrl; 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 -ETIME: 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, " "setup_packet=0x%08lx,", (unsigned long) urb->transfer_buffer, urb->transfer_buffer_length, urb->actual_length, (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. * cs->lock must not be held. * 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 takes care of * scheduling the necessary actions for execution outside of interrupt context. * cs->lock must not be held. * argument: * controller state structure */static inline void error_reset(struct cardstate *cs){ /* close AT command channel to recover (ignore errors) */ req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); //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; int rc; if (!ucs->rcvbuf_size) { gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__); return; } if (ucs->retry_cmd_in++ < BAS_RETRY) { dev_notice(cs->dev, "control read: timeout, 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: timeout, giving up after %d tries\n", ucs->retry_cmd_in); } kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; error_reset(cs);}/* 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;}/* 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 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 != ucs->rcvbuf_size)) { dev_warn(cs->dev, "control read: received %d chars, expected %d\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?