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