📄 usbdev.c
字号:
/* * BRIEF MODULE DESCRIPTION * Au1000 USB Device-Side Serial TTY Driver * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * stevel@mvista.com or source@mvista.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/list.h>#include <linux/smp_lock.h>#define DEBUG#include <linux/usb.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/mipsregs.h>#include <asm/au1000.h>#include <asm/au1000_dma.h>/* Module information */MODULE_AUTHOR("Steve Longerbeam, stevel@mvista.com, www.mvista.com");MODULE_DESCRIPTION("Au1000 USB Device-Side Serial TTY Driver");#undef USBDEV_PIO#define SERIAL_TTY_MAJOR 189#define MAX(a,b) (((a)>(b))?(a):(b))#define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL)#define MAX_NUM_PORTS 2#define NUM_PORTS 1#define NUM_EP 2*NUM_PORTS#define EP0_MAX_PACKET_SIZE 64#define EP2_MAX_PACKET_SIZE 64#define EP3_MAX_PACKET_SIZE 64#define EP4_MAX_PACKET_SIZE 64#define EP5_MAX_PACKET_SIZE 64#ifdef USBDEV_PIO#define EP_FIFO_DEPTH 8#endiftypedef enum { ATTACHED = 0, POWERED, DEFAULT, ADDRESS, CONFIGURED} dev_state_t;/* 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 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);static int serial_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);static void serial_set_termios (struct tty_struct *tty, struct termios * old);typedef struct { int read_fifo; int write_fifo; int ctrl_stat; int read_fifo_status; int write_fifo_status;} endpoint_reg_t;typedef struct pkt { int size; u8 *bufptr; struct pkt *next; u8 buf[0];} pkt_t;typedef struct { pkt_t *head; pkt_t *tail; int count;} pkt_list_t;typedef struct { struct usb_endpoint_descriptor *desc; endpoint_reg_t *reg; // Only one of these are used, unless this is a control ep pkt_list_t inlist; pkt_list_t outlist; unsigned int indma, outdma; // DMA channel numbers for IN, OUT int inirq, outirq; // DMA buffer done irq numbers int max_pkt_size; spinlock_t lock;} endpoint_t;struct usb_serial_port { struct usb_serial *serial; /* ptr back to the owner of this port */ struct tty_struct *tty; /* the coresponding tty for this port */ unsigned char number; char active; /* someone has this device open */ spinlock_t port_lock; endpoint_t ep_bulkin; endpoint_t ep_bulkout; wait_queue_head_t write_wait; /* task queue for line discipline waking up on send packet complete */ struct tq_struct send_complete_tq; /* task queue for line discipline wakeup on receive packet complete */ struct tq_struct receive_complete_tq; int open_count; /* number of times this port has been opened */};struct usb_serial { struct tty_driver *tty_driver; /* the tty_driver for this device */ unsigned char minor; /* the minor number for this device */ endpoint_t ep_ctrl; struct usb_device_descriptor *dev_desc; struct usb_interface_descriptor *if_desc; struct usb_config_descriptor *conf_desc; struct usb_string_descriptor *str_desc[6]; struct usb_serial_port port[NUM_PORTS]; dev_state_t state; // device state int suspended; // suspended flag int address; // device address int interface; u8 alternate_setting; u8 configuration; // configuration value int remote_wakeup_en;};static struct usb_device_descriptor dev_desc = { bLength:USB_DT_DEVICE_SIZE, bDescriptorType:USB_DT_DEVICE, bcdUSB:0x0110, //usb rev 1.0 bDeviceClass:USB_CLASS_PER_INTERFACE, //class (none) bDeviceSubClass:0x00, //subclass (none) bDeviceProtocol:0x00, //protocol (none) bMaxPacketSize0:EP0_MAX_PACKET_SIZE, //max packet size for ep0 idVendor:0x6d04, //vendor id idProduct:0x0bc0, //product id bcdDevice:0x0001, //BCD rev 0.1 iManufacturer:0x01, //manufactuer string index iProduct:0x02, //product string index iSerialNumber:0x03, //serial# string index bNumConfigurations:0x01 //num configurations};static struct usb_endpoint_descriptor ep_desc[] = { { // EP2, Bulk IN for Port 0 bLength:USB_DT_ENDPOINT_SIZE, bDescriptorType:USB_DT_ENDPOINT, bEndpointAddress:USB_DIR_IN | 0x02, bmAttributes:USB_ENDPOINT_XFER_BULK, wMaxPacketSize:EP2_MAX_PACKET_SIZE, bInterval:0x00 // ignored for bulk }, { // EP4, Bulk OUT for Port 0 bLength:USB_DT_ENDPOINT_SIZE, bDescriptorType:USB_DT_ENDPOINT, bEndpointAddress:USB_DIR_OUT | 0x04, bmAttributes:USB_ENDPOINT_XFER_BULK, wMaxPacketSize:EP4_MAX_PACKET_SIZE, bInterval:0x00 // ignored for bulk }, { // EP3, Bulk IN for Port 1 bLength:USB_DT_ENDPOINT_SIZE, bDescriptorType:USB_DT_ENDPOINT, bEndpointAddress:USB_DIR_IN | 0x03, bmAttributes:USB_ENDPOINT_XFER_BULK, wMaxPacketSize:EP3_MAX_PACKET_SIZE, bInterval:0x00 // ignored for bulk }, { // EP5, Bulk OUT for Port 1 bLength:USB_DT_ENDPOINT_SIZE, bDescriptorType:USB_DT_ENDPOINT, bEndpointAddress:USB_DIR_OUT | 0x05, bmAttributes:USB_ENDPOINT_XFER_BULK, wMaxPacketSize:EP5_MAX_PACKET_SIZE, bInterval:0x00 // ignored for bulk },};static struct usb_interface_descriptor if_desc = { bLength:USB_DT_INTERFACE_SIZE, bDescriptorType:USB_DT_INTERFACE, bInterfaceNumber:0x00, bAlternateSetting:0x00, bNumEndpoints:NUM_EP, bInterfaceClass:0xff, bInterfaceSubClass:0xab, bInterfaceProtocol:0x00, iInterface:0x05};#define CONFIG_DESC_LEN \ USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + NUM_EP*USB_DT_ENDPOINT_SIZEstatic struct usb_config_descriptor config_desc = { bLength:USB_DT_CONFIG_SIZE, bDescriptorType:USB_DT_CONFIG, wTotalLength:CONFIG_DESC_LEN, bNumInterfaces:0x01, bConfigurationValue:0x01, iConfiguration:0x04, // configuration string bmAttributes:0xc0, // self-powered MaxPower:20 // 40 mA};// These strings will be converted to Unicode before sendingstatic char *strings[5] = { "Alchemy Semiconductor", "Alchemy Au1000", "1.0", "Au1000 UART Config", "Au1000 UART Interface"};// String[0] is a list of Language IDs supported by this devicestatic struct usb_string_descriptor string_desc0 = { bLength:4, bDescriptorType:USB_DT_STRING, wData:{0x0409} // English, US};static endpoint_reg_t ep_reg[] = { // FIFO's 0 and 1 are EP0 default control {USBD_EP0RD, USBD_EP0WR, USBD_EP0CS, USBD_EP0RDSTAT, USBD_EP0WRSTAT}, // FIFO 2 is EP2, Port 0, bulk IN { -1, USBD_EP2WR, USBD_EP2CS, -1, USBD_EP2WRSTAT }, // FIFO 4 is EP4, Port 0, bulk OUT {USBD_EP4RD, -1, USBD_EP4CS, USBD_EP3WR, -1}, // FIFO 3 is EP3, Port 1, bulk IN { -1, USBD_EP3WRSTAT, USBD_EP3CS, -1, USBD_EP3WRSTAT }, // FIFO 5 is EP5, Port 1, bulk OUT {USBD_EP5RD, -1, USBD_EP5CS, USBD_EP5RDSTAT, -1}};static struct { unsigned int id; const char *str;} ep_dma_id[] = { { DMA_ID_USBDEV_EP0_TX, "USBDev EP0 IN" }, { DMA_ID_USBDEV_EP0_RX, "USBDev EP0 OUT" }, { DMA_ID_USBDEV_EP2_TX, "USBDev EP2 IN" }, { DMA_ID_USBDEV_EP4_RX, "USBDev EP4 OUT" }, { DMA_ID_USBDEV_EP3_TX, "USBDev EP3 IN" }, { DMA_ID_USBDEV_EP5_RX, "USBDev EP5 OUT" }};static int serial_refcount;static struct tty_driver serial_tty_driver;static struct tty_struct *serial_tty[1];static struct termios *serial_termios[1];static struct termios *serial_termios_locked[1];static struct usb_serial usbserial;#define DIR_OUT 0#define DIR_IN (1<<3)static const u32 au1000_config_table[25] __devinitdata = { 0x00, ((EP0_MAX_PACKET_SIZE & 0x380) >> 7) | (USB_ENDPOINT_XFER_CONTROL << 4), (EP0_MAX_PACKET_SIZE & 0x7f) << 1, 0x00, 0x01, 0x10, ((EP2_MAX_PACKET_SIZE & 0x380) >> 7) | DIR_IN | (USB_ENDPOINT_XFER_BULK << 4), (EP2_MAX_PACKET_SIZE & 0x7f) << 1, 0x00, 0x02, 0x20, ((EP3_MAX_PACKET_SIZE & 0x380) >> 7) | DIR_IN | (USB_ENDPOINT_XFER_BULK << 4), (EP3_MAX_PACKET_SIZE & 0x7f) << 1, 0x00, 0x03, 0x30, ((EP4_MAX_PACKET_SIZE & 0x380) >> 7) | DIR_OUT | (USB_ENDPOINT_XFER_BULK << 4), (EP4_MAX_PACKET_SIZE & 0x7f) << 1, 0x00, 0x04, 0x40, ((EP5_MAX_PACKET_SIZE & 0x380) >> 7) | DIR_OUT | (USB_ENDPOINT_XFER_BULK << 4), (EP5_MAX_PACKET_SIZE & 0x7f) << 1, 0x00, 0x05};static inline endpoint_t *fifonum_to_ep(struct usb_serial* serial, int fifo_num){ switch (fifo_num) { case 0: case 1: return &serial->ep_ctrl; case 2: return &serial->port[0].ep_bulkin; case 3: return &serial->port[1].ep_bulkin; case 4: return &serial->port[0].ep_bulkout; case 5: return &serial->port[1].ep_bulkout; } return NULL;}static inline struct usb_serial_port *fifonum_to_port(struct usb_serial* serial, int fifo_num){ switch (fifo_num) { case 2: case 4: return &serial->port[0]; case 3: case 5: return &serial->port[1]; } return NULL;}static inline endpoint_t *epnum_to_ep(struct usb_serial* serial, int ep_num){ switch (ep_num) { case 0: return &serial->ep_ctrl; case 2: return &serial->port[0].ep_bulkin; case 3: return &serial->port[1].ep_bulkin; case 4: return &serial->port[0].ep_bulkout; case 5: return &serial->port[1].ep_bulkout; } return NULL;}static inline intport_paranoia_check(struct usb_serial_port *port, const char *function){ if (!port) { dbg("%s - port == NULL", function); return -1; } if (!port->serial) { dbg("%s - port->serial == NULL", function); return -1; } if (!port->tty) { dbg("%s - port->tty == NULL", function); return -1; } return 0;}static inline struct usb_serial*get_usb_serial (struct usb_serial_port *port, const char *function){ /* if no port was specified, or it fails a paranoia check */ if (!port || port_paranoia_check(port, function)) { /* then say that we dont have a valid usb_serial thing, * which will end up genrating -ENODEV return values */ return NULL; } return port->serial;}static inline pkt_t *alloc_packet(int data_size){ pkt_t* pkt = (pkt_t *)kmalloc(sizeof(pkt_t) + data_size, ALLOC_FLAGS); if (!pkt) return NULL; pkt->size = data_size; pkt->bufptr = pkt->buf;#ifndef USBDEV_PIO pkt->bufptr = KSEG1ADDR(pkt->bufptr);#endif pkt->next = NULL; return pkt;}/* * Link a packet to the tail of the enpoint's packet list. */static voidlink_packet(endpoint_t * ep, pkt_list_t * list, pkt_t * pkt){ unsigned long flags; spin_lock_irqsave(&ep->lock, flags); if (!list->tail) { list->head = list->tail = pkt; list->count = 1; } else { list->tail->next = pkt; list->tail = pkt; list->count++; } spin_unlock_irqrestore(&ep->lock, flags);}/* * Unlink and return a packet from the head of the enpoint's packet list. */static pkt_t *unlink_packet(endpoint_t * ep, pkt_list_t * list){ unsigned long flags; pkt_t *pkt; spin_lock_irqsave(&ep->lock, flags); pkt = list->head; if (!pkt || !list->count) { spin_unlock_irqrestore(&ep->lock, flags); return NULL; } list->head = pkt->next; if (!list->head) { list->head = list->tail = NULL; list->count = 0; } else list->count--; spin_unlock_irqrestore(&ep->lock, flags); return pkt;}/* * Create and attach a new packet to the tail of the enpoint's * packet list. */static pkt_t *add_packet(endpoint_t * ep, pkt_list_t * list, int size){ pkt_t *pkt = alloc_packet(size); if (!pkt) return NULL; link_packet(ep, list, pkt); return pkt;}/* * Unlink and free a packet from the head of the enpoint's * packet list. */static inline voidfree_packet(endpoint_t * ep, pkt_list_t * list){ kfree(unlink_packet(ep, list));}static inline voidflush_pkt_list(endpoint_t * ep, pkt_list_t * list){ while (list->count) free_packet(ep, list);}static inline voidflush_write_fifo(endpoint_t * ep){ if (ep->reg->write_fifo_status >= 0) { outl_sync(USBDEV_FSTAT_FLUSH, ep->reg->write_fifo_status); udelay(100); outl_sync(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->reg->write_fifo_status); }}static inline voidflush_read_fifo(endpoint_t * ep){ if (ep->reg->read_fifo_status >= 0) { outl_sync(USBDEV_FSTAT_FLUSH, ep->reg->read_fifo_status); udelay(100); outl_sync(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->reg->read_fifo_status); }}static voidendpoint_flush(endpoint_t * ep){ unsigned long flags; spin_lock_irqsave(&ep->lock, flags); // First, flush all packets flush_pkt_list(ep, &ep->inlist); flush_pkt_list(ep, &ep->outlist); // Now flush the endpoint's h/w FIFO(s) flush_write_fifo(ep); flush_read_fifo(ep); spin_unlock_irqrestore(&ep->lock, flags);}static voidendpoint_stall(endpoint_t * ep){ unsigned long flags; u32 cs; dbg(__FUNCTION__); spin_lock_irqsave(&ep->lock, flags); cs = inl(ep->reg->ctrl_stat) | USBDEV_CS_STALL; outl_sync(cs, ep->reg->ctrl_stat); spin_unlock_irqrestore(&ep->lock, flags);}static voidendpoint_unstall(endpoint_t * ep){ unsigned long flags; u32 cs; dbg(__FUNCTION__); spin_lock_irqsave(&ep->lock, flags); cs = inl(ep->reg->ctrl_stat) & ~USBDEV_CS_STALL; outl_sync(cs, ep->reg->ctrl_stat); spin_unlock_irqrestore(&ep->lock, flags);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -