📄 usb-serial.c
字号:
/* * USB Serial Converter driver * * (C) Copyright (C) 1999, 2000 * Greg Kroah-Hartman (greg@kroah.com) * * 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. * * This driver was originally based on the ACM driver by Armin Fuerst (which was * based on a driver by Brad Keryan) * * See Documentation/usb-serial.txt for more information on using this driver. * * (01/13/2000) gkh * Fixed the vendor id for the generic driver to the one I meant it to be. * * (01/12/2000) gkh * Forget the version numbering...that's pretty useless... * Made the driver able to be compiled so that the user can select which * converter they want to use. This allows people who only want the Visor * support to not pay the memory size price of the WhiteHEAT. * Fixed bug where the generic driver (idVendor=0000 and idProduct=0000) * grabbed the root hub. Not good. * * version 0.4.0 (01/10/2000) gkh * Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT * device. Added startup function to allow firmware to be downloaded to * a device if it needs to be. * Added firmware download logic to the WhiteHEAT device. * Started to add #defines to split up the different drivers for potential * configuration option. * * version 0.3.1 (12/30/99) gkh * Fixed problems with urb for bulk out. * Added initial support for multiple sets of endpoints. This enables * the Handspring Visor to be attached successfully. Only the first * bulk in / bulk out endpoint pair is being used right now. * * version 0.3.0 (12/27/99) gkh * Added initial support for the Handspring Visor based on a patch from * Miles Lott (milos@sneety.insync.net) * Cleaned up the code a bunch and converted over to using urbs only. * * version 0.2.3 (12/21/99) gkh * Added initial support for the Connect Tech WhiteHEAT converter. * Incremented the number of ports in expectation of getting the * WhiteHEAT to work properly (4 ports per connection). * Added notification on insertion and removal of what port the * device is/was connected to (and what kind of device it was). * * version 0.2.2 (12/16/99) gkh * Changed major number to the new allocated number. We're legal now! * * version 0.2.1 (12/14/99) gkh * Fixed bug that happens when device node is opened when there isn't a * device attached to it. Thanks to marek@webdesign.no for noticing this. * * version 0.2.0 (11/10/99) gkh * Split up internals to make it easier to add different types of serial * converters to the code. * Added a "generic" driver that gets it's vendor and product id * from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net) * for the idea and sample code (from the usb scanner driver.) * Cleared up any licensing questions by releasing it under the GNU GPL. * * version 0.1.2 (10/25/99) gkh * Fixed bug in detecting device. * * version 0.1.1 (10/05/99) gkh * Changed the major number to not conflict with anything else. * * version 0.1 (09/28/99) gkh * Can recognize the two different devices and start up a read from * device when asked to. Writes also work. No control signals yet, this * all is vendor specific data (i.e. no spec), also no control for * different baud rates or other bit settings. * Currently we are using the same devid as the acm driver. This needs * to change. * */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/malloc.h>#include <linux/fcntl.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/tty.h>#include <linux/module.h>#include <linux/spinlock.h>#ifdef CONFIG_USB_SERIAL_WHITEHEAT#include "whiteheat.h" /* firmware for the ConnectTech WhiteHEAT device */#endif#define DEBUG#include "usb.h"/* Module information */MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");MODULE_DESCRIPTION("USB Serial Driver");#ifdef CONFIG_USB_SERIAL_GENERICstatic __u16 vendor = 0x05f9;static __u16 product = 0xffff;MODULE_PARM(vendor, "i");MODULE_PARM_DESC(vendor, "User specified USB idVendor");MODULE_PARM(product, "i");MODULE_PARM_DESC(product, "User specified USB idProduct");#endif/* USB Serial devices vendor ids and device ids that this driver supports */#define BELKIN_VENDOR_ID 0x056c#define BELKIN_SERIAL_CONVERTER 0x8007#define PERACOM_VENDOR_ID 0x0565#define PERACOM_SERIAL_CONVERTER 0x0001#define CONNECT_TECH_VENDOR_ID 0x0710#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001#define CONNECT_TECH_WHITE_HEAT_ID 0x8001#define HANDSPRING_VENDOR_ID 0x082d#define HANDSPRING_VISOR_ID 0x0100#define SERIAL_MAJOR 188 /* Nice legal number now */#define NUM_PORTS 16 /* Actually we are allowed 255, but this is good for now */static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);static void usb_serial_disconnect(struct usb_device *dev, void *ptr);#define MAX_ENDPOINTS 8struct usb_serial_state { struct usb_device * dev; struct usb_serial_device_type * type; void * irq_handle; unsigned int irqpipe; struct tty_struct * tty; /* the coresponding tty for this device */ unsigned char number; char present; char active; char num_interrupt_in; /* number of interrupt in endpoints we have */ char interrupt_in_inuse; /* if the interrupt in endpoint is in use */ __u8 interrupt_in_endpoint[MAX_ENDPOINTS]; __u8 interrupt_in_interval[MAX_ENDPOINTS]; __u16 interrupt_in_size[MAX_ENDPOINTS]; /* the size of the interrupt in endpoint */ unsigned int interrupt_in_pipe[MAX_ENDPOINTS]; unsigned char * interrupt_in_buffer[MAX_ENDPOINTS]; void * interrupt_in_transfer[MAX_ENDPOINTS]; struct urb control_urb; char num_bulk_in; /* number of bulk in endpoints we have */ __u8 bulk_in_endpoint[MAX_ENDPOINTS]; __u8 bulk_in_interval[MAX_ENDPOINTS]; __u16 bulk_in_size[MAX_ENDPOINTS]; /* the size of the bulk in endpoint */ unsigned int bulk_in_pipe[MAX_ENDPOINTS]; unsigned char * bulk_in_buffer[MAX_ENDPOINTS]; void * bulk_in_transfer[MAX_ENDPOINTS]; struct urb read_urb; char num_bulk_out; /* number of bulk out endpoints we have */ __u8 bulk_out_endpoint[MAX_ENDPOINTS]; __u8 bulk_out_interval[MAX_ENDPOINTS]; __u16 bulk_out_size[MAX_ENDPOINTS]; /* the size of the bulk out endpoint */ unsigned int bulk_out_pipe[MAX_ENDPOINTS]; unsigned char * bulk_out_buffer[MAX_ENDPOINTS]; void * bulk_out_transfer[MAX_ENDPOINTS]; struct urb write_urb;};#define MUST_HAVE_NOT 0x01#define MUST_HAVE 0x02#define DONT_CARE 0x03#define HAS 0x02#define HAS_NOT 0x01#define NUM_DONT_CARE (-1)/* local function prototypes */static int serial_open (struct tty_struct *tty, struct file * filp);static void serial_close (struct tty_struct *tty, struct file * filp);static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);static void serial_put_char (struct tty_struct *tty, unsigned char ch);static int serial_write_room (struct tty_struct *tty);static int serial_chars_in_buffer (struct tty_struct *tty);static void serial_throttle (struct tty_struct * tty);static void serial_unthrottle (struct tty_struct * tty);/* This structure defines the individual serial converter. */struct usb_serial_device_type { char *name; __u16 *idVendor; __u16 *idProduct; char needs_interrupt_in; char needs_bulk_in; char needs_bulk_out; char num_interrupt_in; char num_bulk_in; char num_bulk_out; /* function call to make before accepting driver */ int (*startup) (struct usb_serial_state *serial); /* return 0 to continue initialization, anything else to abort */ /* serial function calls */ int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); int (*write)(struct tty_struct * tty, int from_user,const unsigned char *buf, int count); void (*put_char)(struct tty_struct *tty, unsigned char ch); int (*write_room)(struct tty_struct *tty); int (*chars_in_buffer)(struct tty_struct *tty); void (*throttle)(struct tty_struct * tty); void (*unthrottle)(struct tty_struct * tty);};/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) *//* need to always compile these in, as some of the other devices use these functions as their own. */static int generic_serial_open (struct tty_struct *tty, struct file *filp);static void generic_serial_close (struct tty_struct *tty, struct file *filp);static int generic_serial_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count);static void generic_serial_put_char (struct tty_struct *tty, unsigned char ch);static int generic_write_room (struct tty_struct *tty);static int generic_chars_in_buffer (struct tty_struct *tty);#ifdef CONFIG_USB_SERIAL_GENERIC/* All of the device info needed for the Generic Serial Converter */static struct usb_serial_device_type generic_device = { name: "Generic", idVendor: &vendor, /* use the user specified vendor id */ idProduct: &product, /* use the user specified product id */ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ num_interrupt_in: NUM_DONT_CARE, num_bulk_in: NUM_DONT_CARE, num_bulk_out: NUM_DONT_CARE, open: generic_serial_open, close: generic_serial_close, write: generic_serial_write, put_char: generic_serial_put_char, write_room: generic_write_room, chars_in_buffer: generic_chars_in_buffer,};#endif#if defined(CONFIG_USB_SERIAL_BELKIN) || defined(CONFIG_USB_SERIAL_PERACOM)/* function prototypes for the eTek type converters (this includes Belkin and Peracom) */static int etek_serial_open (struct tty_struct *tty, struct file *filp);static void etek_serial_close (struct tty_struct *tty, struct file *filp);#endif#ifdef CONFIG_USB_SERIAL_BELKIN/* All of the device info needed for the Belkin Serial Converter */static __u16 belkin_vendor_id = BELKIN_VENDOR_ID;static __u16 belkin_product_id = BELKIN_SERIAL_CONVERTER;static struct usb_serial_device_type belkin_device = { name: "Belkin", idVendor: &belkin_vendor_id, /* the Belkin vendor id */ idProduct: &belkin_product_id, /* the Belkin serial converter product id */ needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ num_interrupt_in: 1, num_bulk_in: 1, num_bulk_out: 1, open: etek_serial_open, close: etek_serial_close, write: generic_serial_write, put_char: generic_serial_put_char, write_room: generic_write_room, chars_in_buffer: generic_chars_in_buffer,};#endif#ifdef CONFIG_USB_SERIAL_PERACOM/* All of the device info needed for the Peracom Serial Converter */static __u16 peracom_vendor_id = PERACOM_VENDOR_ID;static __u16 peracom_product_id = PERACOM_SERIAL_CONVERTER;static struct usb_serial_device_type peracom_device = { name: "Peracom", idVendor: &peracom_vendor_id, /* the Peracom vendor id */ idProduct: &peracom_product_id, /* the Peracom serial converter product id */ needs_interrupt_in: MUST_HAVE, /* this device must have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ num_interrupt_in: 1, num_bulk_in: 1, num_bulk_out: 1, open: etek_serial_open, close: etek_serial_close, write: generic_serial_write, put_char: generic_serial_put_char, write_room: generic_write_room, chars_in_buffer: generic_chars_in_buffer,};#endif#ifdef CONFIG_USB_SERIAL_WHITEHEAT/* function prototypes for the Connect Tech WhiteHEAT serial converter */static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp);static void whiteheat_serial_close (struct tty_struct *tty, struct file *filp);static void whiteheat_throttle (struct tty_struct *tty);static void whiteheat_unthrottle (struct tty_struct *tty);static int whiteheat_startup (struct usb_serial_state *serial);/* All of the device info needed for the Connect Tech WhiteHEAT */static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID;static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID;static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID;static struct usb_serial_device_type whiteheat_fake_device = { name: "Connect Tech - WhiteHEAT - (prerenumeration)", idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ num_interrupt_in: NUM_DONT_CARE, num_bulk_in: NUM_DONT_CARE, num_bulk_out: NUM_DONT_CARE, startup: whiteheat_startup };static struct usb_serial_device_type whiteheat_device = { name: "Connect Tech - WhiteHEAT", idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ num_interrupt_in: NUM_DONT_CARE, num_bulk_in: NUM_DONT_CARE, num_bulk_out: NUM_DONT_CARE, open: whiteheat_serial_open, close: whiteheat_serial_close, write: generic_serial_write, put_char: generic_serial_put_char, write_room: generic_write_room, chars_in_buffer: generic_chars_in_buffer, throttle: whiteheat_throttle, unthrottle: whiteheat_unthrottle};#endif#ifdef CONFIG_USB_SERIAL_VISOR/* function prototypes for a handspring visor */static int visor_serial_open (struct tty_struct *tty, struct file *filp);static void visor_serial_close (struct tty_struct *tty, struct file *filp);static void visor_throttle (struct tty_struct *tty);static void visor_unthrottle (struct tty_struct *tty);/* All of the device info needed for the Handspring Visor */static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID;static __u16 handspring_product_id = HANDSPRING_VISOR_ID;static struct usb_serial_device_type handspring_device = { name: "Handspring Visor", idVendor: &handspring_vendor_id, /* the Handspring vendor ID */ idProduct: &handspring_product_id, /* the Handspring Visor product id */ needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ num_interrupt_in: 0, num_bulk_in: 2, num_bulk_out: 2, open: visor_serial_open, close: visor_serial_close, write: generic_serial_write, put_char: generic_serial_put_char, write_room: generic_write_room, chars_in_buffer: generic_chars_in_buffer, throttle: visor_throttle, unthrottle: visor_unthrottle};#endif/* To add support for another serial converter, create a usb_serial_device_type structure for that device, and add it to this list, making sure that the last entry is NULL. */static struct usb_serial_device_type *usb_serial_devices[] = {#ifdef CONFIG_USB_SERIAL_GENERIC &generic_device,#endif#ifdef CONFIG_USB_SERIAL_WHITEHEAT &whiteheat_fake_device, &whiteheat_device,#endif#ifdef CONFIG_USB_SERIAL_BELKIN &belkin_device,#endif#ifdef CONFIG_USB_SERIAL_PERACOM &peracom_device,#endif#ifdef CONFIG_USB_SERIAL_VISOR &handspring_device,#endif NULL};static struct usb_driver usb_serial_driver = { "serial", usb_serial_probe, usb_serial_disconnect, { NULL, NULL }};static int serial_refcount;static struct tty_struct * serial_tty[NUM_PORTS];static struct termios * serial_termios[NUM_PORTS];static struct termios * serial_termios_locked[NUM_PORTS];static struct usb_serial_state serial_state_table[NUM_PORTS];static void serial_read_bulk (struct urb *urb){ struct usb_serial_state *serial = (struct usb_serial_state *)urb->context; struct tty_struct *tty = serial->tty; unsigned char *data = urb->transfer_buffer; int i; dbg("serial_read_irq"); if (urb->status) { dbg("nonzero read bulk status received: %d", urb->status); return; } if (urb->actual_length) dbg("%d %s", urb->actual_length, data); if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); } tty_flip_buffer_push(tty); } /* Continue trying to always read */ if (usb_submit_urb(urb)) dbg("failed resubmitting read urb"); return;}static void serial_write_bulk (struct urb *urb){ struct usb_serial_state *serial = (struct usb_serial_state *) urb->context; struct tty_struct *tty = serial->tty; dbg("serial_write_irq"); if (urb->status) { dbg("nonzero write bulk status received: %d", urb->status); return; } if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); return;}/***************************************************************************** * Driver tty interface functions *****************************************************************************/static int serial_open (struct tty_struct *tty, struct file * filp){ struct usb_serial_state *serial; dbg("serial_open"); /* assign a serial object to the tty pointer */ serial = &serial_state_table [MINOR(tty->device)-tty->driver.minor_start]; /* do some sanity checking that we really have a device present */ if (!serial) { dbg("serial == NULL!"); return (-ENODEV);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -