📄 option.c
字号:
/* Option Card (PCMCIA to) USB to Serial Driver 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: 2005-05-19 v0.1 Initial version, based on incomplete docs and analysis of misbehavior with the standard driver 2005-05-20 v0.2 Extended the input buffer to avoid losing random 64-byte chunks of data 2005-05-21 v0.3 implemented chars_in_buffer() turned on low_latency simplified the code somewhat 2005-05-24 v0.4 option_write() sometimes deadlocked under heavy load removed some dead code added sponsor notice coding style clean-up 2005-06-20 v0.4.1 add missing braces :-/ killed end-of-line whitespace 2005-07-15 v0.4.2 rename WLAN product to FUSION, add FUSION2 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes wants to send >2000 bytes. Work sponsored by: Sigos GmbH, Germany <info@sigos.de>*/#define DRIVER_VERSION "v0.4"#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver"#include <linux/config.h>#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/usb.h>#include "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, struct pt_regs *regs);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 termios *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 HUAWEI_VENDOR_ID 0x12D1#define AUDIOVOX_VENDOR_ID 0x0F3D#define OPTION_PRODUCT_OLD 0x5000#define OPTION_PRODUCT_FUSION 0x6000#define OPTION_PRODUCT_FUSION2 0x6300#define HUAWEI_PRODUCT_E600 0x1001#define AUDIOVOX_PRODUCT_AIRCARD 0x0112static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(usb, option_ids);static struct usb_driver option_driver = { .owner = THIS_MODULE, .name = "option", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = option_ids,};/* The card has three separate interfaces, wich the serial driver * recognizes separately, thus num_port=1. */static struct usb_serial_driver option_3port_device = { .driver = { .owner = THIS_MODULE, .name = "option", }, .description = "Option 3G data card", .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, /* 3, but the card reports its ports separately */ .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]; /* 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_3port_device); if (retval) goto failed_3port_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_3port_device);failed_3port_device_register: return retval;}static void __exit option_exit(void){ usb_deregister (&option_driver); usb_serial_deregister (&option_3port_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 termios *old_termios){ dbg("%s", __FUNCTION__); 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 (this_urb->status == -EINPROGRESS) { 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); continue; } portdata->tx_start_time[i] = jiffies; buf += todo; left -= todo; } count -= left; dbg("%s: wrote (did %d)", __FUNCTION__, count); return count;}static void option_indat_callback(struct urb *urb, struct pt_regs *regs){ int i, err; int endpoint; struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; dbg("%s: %p", __FUNCTION__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = (struct usb_serial_port *) urb->context; if (urb->status) { dbg("%s: nonzero status: %d on endpoint %02x.", __FUNCTION__, urb->status, endpoint); } else { tty = port->tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) tty_flip_buffer_push(tty); tty_insert_flip_char(tty, data[i], 0); } tty_flip_buffer_push(tty); } else { dbg("%s: empty read urb received", __FUNCTION__); } /* Resubmit urb so we continue receiving */ if (port->open_count && urb->status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) printk(KERN_ERR "%s: resubmit read urb failed. " "(%d)", __FUNCTION__, err); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -