📄 serial.c
字号:
/* * g_serial.c -- USB gadget serial driver * * 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. * */#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/proc_fs.h>#include <linux/device.h>#include <linux/tty.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>/* 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 "v0.1"#define GS_VERSION_NUM 0x0001#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 settings */#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 *//* USB Controllers *//* * NetChip 2280, PCI based. * * This has half a dozen configurable endpoints, four with dedicated * DMA channels to manage their FIFOs. It supports high speed. * Those endpoints can be arranged in any desired configuration. */#ifdef CONFIG_USB_GADGET_NET2280#define CHIP "net2280"#define EP0_MAXPACKET 64static const char EP_OUT_NAME[] = "ep-a";#define EP_OUT_NUM 2static const char EP_IN_NAME[] = "ep-b";#define EP_IN_NUM 2#define HIGHSPEED#define SELFPOWER USB_CONFIG_ATT_SELFPOWERextern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode);static inline void hw_optimize(struct usb_gadget *gadget){ /* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets * for highspeed bulk) because we're not using ep-c/ep-d. */ net2280_set_fifo_mode (gadget, 1);}#endif/* * Dummy_hcd, software-based loopback controller. * * This imitates the abilities of the NetChip 2280, so we will use * the same configuration. */#ifdef CONFIG_USB_GADGET_DUMMY_HCD#define CHIP "dummy"#define EP0_MAXPACKET 64static const char EP_OUT_NAME[] = "ep-a";#define EP_OUT_NUM 2static const char EP_IN_NAME[] = "ep-b";#define EP_IN_NUM 2#define HIGHSPEED#define SELFPOWER USB_CONFIG_ATT_SELFPOWER/* no hw optimizations to apply */#define hw_optimize(g) do {} while (0)#endif/* * PXA-2xx UDC: widely used in second gen Linux-capable PDAs. * * This has fifteen fixed-function full speed endpoints, and it * can support all USB transfer types. * * These supports three or four configurations, with fixed numbers. * The hardware interprets SET_INTERFACE, net effect is that you * can't use altsettings or reset the interfaces independently. * So stick to a single interface. */#ifdef CONFIG_USB_GADGET_PXA2XX#define CHIP "pxa2xx"#define EP0_MAXPACKET 16static const char EP_OUT_NAME[] = "ep2out-bulk";#define EP_OUT_NUM 2static const char EP_IN_NAME[] = "ep1in-bulk";#define EP_IN_NUM 1#define SELFPOWER USB_CONFIG_ATT_SELFPOWER/* no hw optimizations to apply */#define hw_optimize(g) do {} while (0)#endif/* * SA-1100 UDC: widely used in first gen Linux-capable PDAs. * * This has only two fixed function endpoints, which can only * be used for bulk (or interrupt) transfers. (Plus control.) * * Since it can't flush its TX fifos without disabling the UDC, * the current configuration or altsettings can't change except * in special situations. So this is a case of "choose it right * during enumeration" ... */#ifdef CONFIG_USB_GADGET_SA1100#define CHIP "sa1100"#define EP0_MAXPACKET 8static const char EP_OUT_NAME[] = "ep1out-bulk";#define EP_OUT_NUM 1static const char EP_IN_NAME [] = "ep2in-bulk";#define EP_IN_NUM 2#define SELFPOWER USB_CONFIG_ATT_SELFPOWER/* no hw optimizations to apply */#define hw_optimize(g) do {} while (0)#endif/* * Toshiba TC86C001 ("Goku-S") UDC * * This has three semi-configurable full speed bulk/interrupt endpoints. */#ifdef CONFIG_USB_GADGET_GOKU#define CHIP "goku"#define DRIVER_VERSION_NUM 0x0116#define EP0_MAXPACKET 8static const char EP_OUT_NAME [] = "ep1-bulk";#define EP_OUT_NUM 1static const char EP_IN_NAME [] = "ep2-bulk";#define EP_IN_NUM 2#define SELFPOWER USB_CONFIG_ATT_SELFPOWER/* no hw optimizations to apply */#define hw_optimize(g) do {} while (0)#endif/* * USB Controller Defaults */#ifndef EP0_MAXPACKET#error Configure some USB peripheral controller for g_serial!#endif#ifndef SELFPOWER/* default: say we rely on bus power */#define SELFPOWER 0/* else value must be USB_CONFIG_ATT_SELFPOWER */#endif#ifndef MAX_USB_POWER/* any hub supports this steady state bus power consumption */#define MAX_USB_POWER 100 /* mA */#endif#ifndef WAKEUP/* default: this driver won't do remote wakeup */#define WAKEUP 0/* else value must be USB_CONFIG_ATT_WAKEUP */#endif/* 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_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);/* Globals */static struct gs_dev *gs_device;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 driver struct */static struct tty_operations gs_tty_ops = { .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,};static struct tty_driver *gs_tty_driver;/* gadget driver struct */static struct usb_gadget_driver gs_gadget_driver = {#ifdef HIGHSPEED .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 struct usb_string gs_strings[] = { { GS_MANUFACTURER_STR_ID, UTS_SYSNAME " " UTS_RELEASE " with " CHIP }, { 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 const 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, .bMaxPacketSize0 = EP0_MAXPACKET, .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), .bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM), .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 | SELFPOWER | WAKEUP, .bMaxPower = (MAX_USB_POWER + 1) / 2,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -