option.c
来自「linux 内核源代码」· C语言 代码 · 共 822 行 · 第 1/2 页
C
822 行
/* USB Driver for GSM modems Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> This driver is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> History: see the git log. Work sponsored by: Sigos GmbH, Germany <info@sigos.de> This driver exists because the "normal" serial driver doesn't work too well with GSM modems. Issues: - data loss -- one single Receive URB is not nearly enough - nonstandard flow (Option devices) control - controlling the baud rate doesn't make sense This driver is named "option" because the most common device it's used for is a PC-Card (with an internal OHCI-USB interface, behind which the GSM interface sits), made by Option Inc. Some of the "one port" devices actually exhibit multiple USB instances on the USB bus. This is not a bug, these ports are used for different device features.*/#define DRIVER_VERSION "v0.7.1"#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"#define DRIVER_DESC "USB Driver for GSM modems"#include <linux/kernel.h>#include <linux/jiffies.h>#include <linux/errno.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/module.h>#include <linux/bitops.h>#include <linux/usb.h>#include <linux/usb/serial.h>/* Function prototypes */static int option_open(struct usb_serial_port *port, struct file *filp);static void option_close(struct usb_serial_port *port, struct file *filp);static int option_startup(struct usb_serial *serial);static void option_shutdown(struct usb_serial *serial);static void option_rx_throttle(struct usb_serial_port *port);static void option_rx_unthrottle(struct usb_serial_port *port);static int option_write_room(struct usb_serial_port *port);static void option_instat_callback(struct urb *urb);static int option_write(struct usb_serial_port *port, const unsigned char *buf, int count);static int option_chars_in_buffer(struct usb_serial_port *port);static int option_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg);static void option_set_termios(struct usb_serial_port *port, struct ktermios *old);static void option_break_ctl(struct usb_serial_port *port, int break_state);static int option_tiocmget(struct usb_serial_port *port, struct file *file);static int option_tiocmset(struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);static int option_send_setup(struct usb_serial_port *port);/* Vendor and product IDs */#define OPTION_VENDOR_ID 0x0AF0#define OPTION_PRODUCT_COLT 0x5000#define OPTION_PRODUCT_RICOLA 0x6000#define OPTION_PRODUCT_RICOLA_LIGHT 0x6100#define OPTION_PRODUCT_RICOLA_QUAD 0x6200#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT 0x6300#define OPTION_PRODUCT_RICOLA_NDIS 0x6050#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT 0x6150#define OPTION_PRODUCT_RICOLA_NDIS_QUAD 0x6250#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT 0x6350#define OPTION_PRODUCT_COBRA 0x6500#define OPTION_PRODUCT_COBRA_BUS 0x6501#define OPTION_PRODUCT_VIPER 0x6600#define OPTION_PRODUCT_VIPER_BUS 0x6601#define OPTION_PRODUCT_GT_MAX_READY 0x6701#define OPTION_PRODUCT_GT_MAX 0x6711#define OPTION_PRODUCT_FUJI_MODEM_LIGHT 0x6721#define OPTION_PRODUCT_FUJI_MODEM_GT 0x6741#define OPTION_PRODUCT_FUJI_MODEM_EX 0x6761#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT 0x6731#define OPTION_PRODUCT_FUJI_NETWORK_GT 0x6751#define OPTION_PRODUCT_FUJI_NETWORK_EX 0x6771#define OPTION_PRODUCT_KOI_MODEM 0x6800#define OPTION_PRODUCT_KOI_NETWORK 0x6811#define OPTION_PRODUCT_SCORPION_MODEM 0x6901#define OPTION_PRODUCT_SCORPION_NETWORK 0x6911#define OPTION_PRODUCT_ETNA_MODEM 0x7001#define OPTION_PRODUCT_ETNA_NETWORK 0x7011#define OPTION_PRODUCT_ETNA_MODEM_LITE 0x7021#define OPTION_PRODUCT_ETNA_MODEM_GT 0x7041#define OPTION_PRODUCT_ETNA_MODEM_EX 0x7061#define OPTION_PRODUCT_ETNA_NETWORK_LITE 0x7031#define OPTION_PRODUCT_ETNA_NETWORK_GT 0x7051#define OPTION_PRODUCT_ETNA_NETWORK_EX 0x7071#define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100#define OPTION_PRODUCT_ETNA_KOI_NETWORK 0x7111#define HUAWEI_VENDOR_ID 0x12D1#define HUAWEI_PRODUCT_E600 0x1001#define HUAWEI_PRODUCT_E220 0x1003#define HUAWEI_PRODUCT_E220BIS 0x1004#define NOVATELWIRELESS_VENDOR_ID 0x1410#define DELL_VENDOR_ID 0x413C#define ANYDATA_VENDOR_ID 0x16d5#define ANYDATA_PRODUCT_ADU_E100A 0x6501#define ANYDATA_PRODUCT_ADU_500A 0x6502#define BANDRICH_VENDOR_ID 0x1A8D#define BANDRICH_PRODUCT_C100_1 0x1002#define BANDRICH_PRODUCT_C100_2 0x1003static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1100) }, /* Novatel Merlin XS620/S640 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1110) }, /* Novatel Merlin S620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1120) }, /* Novatel Merlin EX720 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1130) }, /* Novatel Merlin S720 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1400) }, /* Novatel U730 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1410) }, /* Novatel U740 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1420) }, /* Novatel EU870 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2100) }, /* Novatel EV620 CDMA/EV-DO */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2110) }, /* Novatel Merlin ES620 / Merlin ES720 / Ovation U720 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x4100) }, /* Novatel U727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x4400) }, /* Novatel MC950 */ { USB_DEVICE(DELL_VENDOR_ID, 0x8114) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ { USB_DEVICE(DELL_VENDOR_ID, 0x8115) }, /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ { USB_DEVICE(DELL_VENDOR_ID, 0x8116) }, /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ { USB_DEVICE(DELL_VENDOR_ID, 0x8117) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ { USB_DEVICE(DELL_VENDOR_ID, 0x8128) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ { USB_DEVICE(DELL_VENDOR_ID, 0x8136) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ { USB_DEVICE(DELL_VENDOR_ID, 0x8137) }, /* Dell Wireless HSDPA 5520 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(usb, option_ids);static struct usb_driver option_driver = { .name = "option", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = option_ids, .no_dynamic_id = 1,};/* The card has three separate interfaces, which the serial driver * recognizes separately, thus num_port=1. */static struct usb_serial_driver option_1port_device = { .driver = { .owner = THIS_MODULE, .name = "option1", }, .description = "GSM modem (1-port)", .usb_driver = &option_driver, .id_table = option_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, .open = option_open, .close = option_close, .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, .throttle = option_rx_throttle, .unthrottle = option_rx_unthrottle, .ioctl = option_ioctl, .set_termios = option_set_termios, .break_ctl = option_break_ctl, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, .shutdown = option_shutdown, .read_int_callback = option_instat_callback,};#ifdef CONFIG_USB_DEBUGstatic int debug;#else#define debug 0#endif/* per port private data */#define N_IN_URB 4#define N_OUT_URB 1#define IN_BUFLEN 4096#define OUT_BUFLEN 128struct option_port_private { /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; char in_buffer[N_IN_URB][IN_BUFLEN]; /* Output endpoints and buffer for this port */ struct urb *out_urbs[N_OUT_URB]; char out_buffer[N_OUT_URB][OUT_BUFLEN]; unsigned long out_busy; /* Bit vector of URBs in use */ /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ int dtr_state; int cts_state; /* Handshaking pins (inputs) */ int dsr_state; int dcd_state; int ri_state; unsigned long tx_start_time[N_OUT_URB];};/* Functions used by new usb-serial code. */static int __init option_init(void){ int retval; retval = usb_serial_register(&option_1port_device); if (retval) goto failed_1port_device_register; retval = usb_register(&option_driver); if (retval) goto failed_driver_register; info(DRIVER_DESC ": " DRIVER_VERSION); return 0;failed_driver_register: usb_serial_deregister (&option_1port_device);failed_1port_device_register: return retval;}static void __exit option_exit(void){ usb_deregister (&option_driver); usb_serial_deregister (&option_1port_device);}module_init(option_init);module_exit(option_exit);static void option_rx_throttle(struct usb_serial_port *port){ dbg("%s", __FUNCTION__);}static void option_rx_unthrottle(struct usb_serial_port *port){ dbg("%s", __FUNCTION__);}static void option_break_ctl(struct usb_serial_port *port, int break_state){ /* Unfortunately, I don't know how to send a break */ dbg("%s", __FUNCTION__);}static void option_set_termios(struct usb_serial_port *port, struct ktermios *old_termios){ dbg("%s", __FUNCTION__); /* Doesn't support option setting */ tty_termios_copy_hw(port->tty->termios, old_termios); option_send_setup(port);}static int option_tiocmget(struct usb_serial_port *port, struct file *file){ unsigned int value; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); value = ((portdata->rts_state) ? TIOCM_RTS : 0) | ((portdata->dtr_state) ? TIOCM_DTR : 0) | ((portdata->cts_state) ? TIOCM_CTS : 0) | ((portdata->dsr_state) ? TIOCM_DSR : 0) | ((portdata->dcd_state) ? TIOCM_CAR : 0) | ((portdata->ri_state) ? TIOCM_RNG : 0); return value;}static int option_tiocmset(struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear){ struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); if (set & TIOCM_RTS) portdata->rts_state = 1; if (set & TIOCM_DTR) portdata->dtr_state = 1; if (clear & TIOCM_RTS) portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; return option_send_setup(port);}static int option_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg){ return -ENOIOCTLCMD;}/* Write */static int option_write(struct usb_serial_port *port, const unsigned char *buf, int count){ struct option_port_private *portdata; int i; int left, todo; struct urb *this_urb = NULL; /* spurious */ int err; portdata = usb_get_serial_port_data(port); dbg("%s: write (%d chars)", __FUNCTION__, count); i = 0; left = count; for (i=0; left > 0 && i < N_OUT_URB; i++) { todo = left; if (todo > OUT_BUFLEN) todo = OUT_BUFLEN; this_urb = portdata->out_urbs[i]; if (test_and_set_bit(i, &portdata->out_busy)) { if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ)) continue; usb_unlink_urb(this_urb); continue; } if (this_urb->status != 0) dbg("usb_write %p failed (err=%d)", this_urb, this_urb->status); dbg("%s: endpoint %d buf %d", __FUNCTION__, usb_pipeendpoint(this_urb->pipe), i); /* send the data */ memcpy (this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; this_urb->dev = port->serial->dev; err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " "(%d, has %d)", this_urb, err, this_urb->status); clear_bit(i, &portdata->out_busy); continue;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?