📄 gserial.c
字号:
/* * g_serial.c -- USB gadget serial driver * * $Id: gserial.c,v 1.17 2003/10/01 06:31:57 borchers Exp $ * * Copyright 2003 (c) Al Borchers (alborchers@steinerpoint.com) * * This code is based in part on the Gadget Zero driver, which * is Copyright (C) 2003 by David Brownell, all rights reserved. * * This code also borrows from usbserial.c, which is * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2000 Peter Berger (pberger@brimson.com) * Copyright (c) 2000 Al Borchers (alborchers@steinerpoint.com) * * This software is distributed under the terms of the GNU General * Public License ("GPL") as published by the Free Software Foundation, * either version 2 of that License or (at your option) any later version. * */#ifndef __KERNEL__#define __KERNEL__#endif#ifndef MODULE#define MODULE#endif/* Includes */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/uts.h>#include <linux/version.h>#include <linux/wait.h>#include <linux/list.h>#include <linux/proc_fs.h>#include <linux/tty_flip.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include <asm/uaccess.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>#include "gadget_chips.h"/* Wait Cond */#define __wait_cond_interruptible(wq, condition, lock, flags, ret) \do { \ wait_queue_t __wait; \ init_waitqueue_entry(&__wait, current); \ \ add_wait_queue(&wq, &__wait); \ for (;;) { \ set_current_state(TASK_INTERRUPTIBLE); \ if (condition) \ break; \ if (!signal_pending(current)) { \ spin_unlock_irqrestore(lock, flags); \ schedule(); \ spin_lock_irqsave(lock, flags); \ continue; \ } \ ret = -ERESTARTSYS; \ break; \ } \ current->state = TASK_RUNNING; \ remove_wait_queue(&wq, &__wait); \} while (0) #define wait_cond_interruptible(wq, condition, lock, flags) \({ \ int __ret = 0; \ if (!(condition)) \ __wait_cond_interruptible(wq, condition, lock, flags, \ __ret); \ __ret; \})#define __wait_cond_interruptible_timeout(wq, condition, lock, flags, \ timeout, ret) \do { \ signed long __timeout = timeout; \ wait_queue_t __wait; \ init_waitqueue_entry(&__wait, current); \ \ add_wait_queue(&wq, &__wait); \ for (;;) { \ set_current_state(TASK_INTERRUPTIBLE); \ if (__timeout == 0) \ break; \ if (condition) \ break; \ if (!signal_pending(current)) { \ spin_unlock_irqrestore(lock, flags); \ __timeout = schedule_timeout(__timeout); \ spin_lock_irqsave(lock, flags); \ continue; \ } \ ret = -ERESTARTSYS; \ break; \ } \ current->state = TASK_RUNNING; \ remove_wait_queue(&wq, &__wait); \} while (0) #define wait_cond_interruptible_timeout(wq, condition, lock, flags, \ timeout) \({ \ int __ret = 0; \ if (!(condition)) \ __wait_cond_interruptible_timeout(wq, condition, lock, \ flags, timeout, __ret); \ __ret; \})/* Defines */#define GS_VERSION_STR "v1.0"#define GS_VERSION_NUM 0x0100#define GS_LONG_NAME "Gadget Serial"#define GS_SHORT_NAME "g_serial"#define GS_MAJOR 127#define GS_MINOR_START 0#define GS_NUM_PORTS 16#define GS_NUM_CONFIGS 1#define GS_NO_CONFIG_ID 0#define GS_BULK_CONFIG_ID 2#define GS_NUM_INTERFACES 1#define GS_INTERFACE_ID 0#define GS_ALT_INTERFACE_ID 0#define GS_NUM_ENDPOINTS 2#define GS_MAX_DESC_LEN 256#define GS_DEFAULT_READ_Q_SIZE 32#define GS_DEFAULT_WRITE_Q_SIZE 32#define GS_DEFAULT_WRITE_BUF_SIZE 8192#define GS_TMP_BUF_SIZE 8192#define GS_CLOSE_TIMEOUT 15/* debug macro */#if G_SERIAL_DEBUGstatic int debug = G_SERIAL_DEBUG;#define gs_debug(format, arg...) \ do { if(debug) printk( KERN_DEBUG format, ## arg ); } while(0)#define gs_debug_level(level, format, arg...) \ do { if(debug>=level) printk( KERN_DEBUG format, ## arg ); } while(0)#else#define gs_debug(format, arg...) \ do { } while(0)#define gs_debug_level(level, format, arg...) \ do { } while(0)#endif /* G_SERIAL_DEBUG *//* Thanks to NetChip Technologies for donating this product ID. * * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */#define GS_VENDOR_ID 0x0525 /* NetChip */#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget *//* Structures */struct gs_dev;/* circular buffer */struct gs_buf { unsigned int buf_size; char *buf_buf; char *buf_get; char *buf_put;};/* list of requests */struct gs_req_entry { struct list_head re_entry; struct usb_request *re_req;};/* the port structure holds info for each port, one for each minor number */struct gs_port { struct gs_dev *port_dev; /* pointer to device struct */ struct tty_struct *port_tty; /* pointer to tty struct */ spinlock_t port_lock; int port_num; int port_open_count; int port_in_use; /* open/close in progress */ wait_queue_head_t port_write_wait;/* waiting to write */ struct gs_buf *port_write_buf;};/* the device structure holds info for the USB device */struct gs_dev { struct usb_gadget *dev_gadget; /* gadget device pointer */ spinlock_t dev_lock; /* lock for set/reset config */ int dev_config; /* configuration number */ struct usb_ep *dev_in_ep; /* address of in endpoint */ struct usb_ep *dev_out_ep; /* address of out endpoint */ struct usb_request *dev_ctrl_req; /* control request */ struct list_head dev_req_list; /* list of write requests */ int dev_sched_port; /* round robin port scheduled */ struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */};/* Functions *//* module */static int __init gs_module_init( void );static void __exit gs_module_exit( void );/* tty driver */static int gs_open( struct tty_struct *tty, struct file *file );static void gs_close( struct tty_struct *tty, struct file *file );static int gs_write( struct tty_struct *tty, int from_user, const unsigned char *buf, int count );static void gs_put_char( struct tty_struct *tty, unsigned char ch );static void gs_flush_chars( struct tty_struct *tty );static int gs_write_room( struct tty_struct *tty );static int gs_chars_in_buffer( struct tty_struct *tty );static void gs_throttle( struct tty_struct * tty );static void gs_unthrottle( struct tty_struct * tty );static void gs_break( struct tty_struct *tty, int break_state );static int gs_ioctl( struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg );static void gs_set_termios( struct tty_struct *tty, struct termios *old );static int gs_read_proc( char *page, char **start, off_t off, int count, int *eof, void *data );static int gs_send( struct gs_dev *dev );static int gs_send_packet( struct gs_dev *dev, char *packet, unsigned int size );static int gs_recv_packet( struct gs_dev *dev, char *packet, unsigned int size );static void gs_read_complete( struct usb_ep *ep, struct usb_request *req );static void gs_write_complete( struct usb_ep *ep, struct usb_request *req );/* gadget driver */static int gs_bind( struct usb_gadget *gadget );static void gs_unbind( struct usb_gadget *gadget );static int gs_setup( struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl );static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req );static void gs_disconnect( struct usb_gadget *gadget );static int gs_set_config( struct gs_dev *dev, unsigned config );static void gs_reset_config( struct gs_dev *dev );static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed, u8 type, unsigned int index );static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len, int kmalloc_flags );static void gs_free_req( struct usb_ep *ep, struct usb_request *req );static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep, unsigned len, int kmalloc_flags );static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req );static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags );static void gs_free_ports( struct gs_dev *dev );/* circular buffer */static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags );static void gs_buf_free( struct gs_buf *gb );static void gs_buf_clear( struct gs_buf *gb );static unsigned int gs_buf_data_avail( struct gs_buf *gb );static unsigned int gs_buf_space_avail( struct gs_buf *gb );static unsigned int gs_buf_put( struct gs_buf *gb, const char *buf, unsigned int count );static unsigned int gs_buf_get( struct gs_buf *gb, char *buf, unsigned int count );/* external functions */extern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode);/* Globals */static struct gs_dev *gs_device;static const char *EP_IN_NAME;static const char *EP_OUT_NAME;static struct semaphore gs_open_close_sem[GS_NUM_PORTS];static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE];static struct semaphore gs_tmp_buf_sem;/* tty variables */static int gs_refcount;static struct tty_struct *gs_tty[GS_NUM_PORTS];static struct termios *gs_termios[GS_NUM_PORTS];static struct termios *gs_termios_locked[GS_NUM_PORTS];/* tty driver struct */static struct tty_driver gs_tty_driver = { .magic = TTY_DRIVER_MAGIC, .driver_name = GS_SHORT_NAME, .name = "ttygs", .major = GS_MAJOR, .minor_start = GS_MINOR_START, .num = GS_NUM_PORTS, .type = TTY_DRIVER_TYPE_SERIAL, .subtype = SERIAL_TYPE_NORMAL, .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, .refcount = &gs_refcount, .table = gs_tty, .termios = gs_termios, .termios_locked = gs_termios_locked, .open = gs_open, .close = gs_close, .write = gs_write, .put_char = gs_put_char, .flush_chars = gs_flush_chars, .write_room = gs_write_room, .ioctl = gs_ioctl, .set_termios = gs_set_termios, .throttle = gs_throttle, .unthrottle = gs_unthrottle, .break_ctl = gs_break, .chars_in_buffer = gs_chars_in_buffer, .read_proc = gs_read_proc,};/* gadget driver struct */static struct usb_gadget_driver gs_gadget_driver = {#ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH,#else .speed = USB_SPEED_FULL,#endif .function = GS_LONG_NAME, .bind = gs_bind, .unbind = gs_unbind, .setup = gs_setup, .disconnect = gs_disconnect, .driver = { .name = GS_SHORT_NAME, /* .shutdown = ... */ /* .suspend = ... */ /* .resume = ... */ },};/* USB descriptors */#define GS_MANUFACTURER_STR_ID 1#define GS_PRODUCT_STR_ID 2#define GS_SERIAL_STR_ID 3#define GS_CONFIG_STR_ID 4/* static strings, in iso 8859/1 */static char manufacturer[40];static struct usb_string gs_strings[] = { { GS_MANUFACTURER_STR_ID, manufacturer }, { GS_PRODUCT_STR_ID, GS_LONG_NAME }, { GS_SERIAL_STR_ID, "0" }, { GS_CONFIG_STR_ID, "Bulk" }, { } /* end of list */};static struct usb_gadget_strings gs_string_table = { .language = 0x0409, /* en-us */ .strings = gs_strings,};static struct usb_device_descriptor gs_device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_VENDOR_SPEC, .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), .iManufacturer = GS_MANUFACTURER_STR_ID, .iProduct = GS_PRODUCT_STR_ID, .iSerialNumber = GS_SERIAL_STR_ID, .bNumConfigurations = GS_NUM_CONFIGS,};static const struct usb_config_descriptor gs_config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, /* .wTotalLength set by gs_build_config_desc */ .bNumInterfaces = GS_NUM_INTERFACES, .bConfigurationValue = GS_BULK_CONFIG_ID, .iConfiguration = GS_CONFIG_STR_ID, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 1,};static const struct usb_interface_descriptor gs_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = GS_NUM_ENDPOINTS, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .iInterface = GS_CONFIG_STR_ID,};static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK,};static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK,};static struct usb_endpoint_descriptor gs_highspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512),};static struct usb_endpoint_descriptor gs_highspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512),};#ifdef CONFIG_USB_GADGET_DUALSPEEDstatic struct usb_qualifier_descriptor gs_qualifier_desc = { .bLength = sizeof(struct usb_qualifier_descriptor), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = __constant_cpu_to_le16 (0x0200), .bDeviceClass = USB_CLASS_VENDOR_SPEC, /* assumes ep0 uses the same value for both speeds ... */ .bNumConfigurations = GS_NUM_CONFIGS,};#endif/* Module */MODULE_DESCRIPTION( GS_LONG_NAME );MODULE_AUTHOR( "Al Borchers" );MODULE_LICENSE( "GPL" );#if G_SERIAL_DEBUGMODULE_PARM( debug, "i" );MODULE_PARM_DESC( debug, "Enable debugging, 0=off, 1=on" );#endifMODULE_PARM( read_q_size, "i" );MODULE_PARM_DESC( read_q_size, "Read request queue size, default=32" );MODULE_PARM( write_q_size, "i" );MODULE_PARM_DESC( write_q_size, "Write request queue size, default=32" );MODULE_PARM( write_buf_size, "i" );MODULE_PARM_DESC( write_buf_size, "Write buffer size, default=8192" );module_init( gs_module_init );module_exit( gs_module_exit );/** gs_module_init** Register as a USB gadget driver and a tty driver.*/static int __init gs_module_init( void ){ int i,ret; if( (ret=usb_gadget_register_driver( &gs_gadget_driver )) ) { printk( KERN_ERR "gs_module_init: cannot register gadget driver, ret=%d\n", ret ); return( ret ); } /* initial stty settings */ gs_tty_driver.init_termios = tty_std_termios; gs_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; for( i=0; i<GS_NUM_PORTS; i++ ) sema_init( &gs_open_close_sem[i], 1 ); sema_init( &gs_tmp_buf_sem, 1 ); if( (ret=tty_register_driver( &gs_tty_driver )) ) { usb_gadget_unregister_driver( &gs_gadget_driver ); printk( KERN_ERR "gs_module_init: cannot register tty driver, ret=%d\n", ret ); return( ret ); } printk( KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, GS_VERSION_STR ); return( 0 ); }/** gs_module_exit** Unregister as a tty driver and a USB gadget driver.*/static void __exit gs_module_exit( void ){ tty_unregister_driver( &gs_tty_driver ); usb_gadget_unregister_driver( &gs_gadget_driver ); printk( KERN_INFO "gs_module_exit: %s %s unloaded\n", GS_LONG_NAME, GS_VERSION_STR );}/* TTY Driver *//* * gs_open */static int gs_open( struct tty_struct *tty, struct file *file ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -