patch-2.4.20 linux-2.4.20/arch/mips/au1000/common/usbdev.c
Next file: linux-2.4.20/arch/mips/au1000/pb1000/Makefile
Previous file: linux-2.4.20/arch/mips/au1000/common/time.c
Back to the patch index
Back to the overall index
- Lines: 2791
- Date:
Thu Nov 28 15:53:09 2002
- Orig file:
linux-2.4.19/arch/mips/au1000/common/usbdev.c
- Orig date:
Fri Aug 2 17:39:43 2002
diff -urN linux-2.4.19/arch/mips/au1000/common/usbdev.c linux-2.4.20/arch/mips/au1000/common/usbdev.c
@@ -1,8 +1,8 @@
/*
* BRIEF MODULE DESCRIPTION
- * Au1000 USB Device-Side Serial TTY Driver
+ * Au1000 USB Device-Side (device layer)
*
- * Copyright 2001 MontaVista Software Inc.
+ * Copyright 2001-2002 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* stevel@mvista.com or source@mvista.com
*
@@ -37,9 +37,6 @@
#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>
@@ -53,54 +50,30 @@
#include <asm/mipsregs.h>
#include <asm/au1000.h>
#include <asm/au1000_dma.h>
+#include <asm/au1000_usbdev.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
+#ifdef DEBUG
+#undef VDEBUG
+#ifdef VDEBUG
+#define vdbg(fmt, arg...) printk(KERN_DEBUG __FILE__ ": " fmt "\n" , ## arg)
+#else
+#define vdbg(fmt, arg...) do {} while (0)
+#endif
+#else
+#define vdbg(fmt, arg...) do {} while (0)
+#endif
#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
-#endif
typedef 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);
+ SETUP_STAGE = 0,
+ DATA_STAGE,
+ STATUS_STAGE
+} ep0_stage_t;
typedef struct {
int read_fifo;
@@ -110,184 +83,68 @@
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;
+ usbdev_pkt_t *head;
+ usbdev_pkt_t *tail;
int count;
} pkt_list_t;
typedef struct {
+ int active;
struct usb_endpoint_descriptor *desc;
endpoint_reg_t *reg;
- // Only one of these are used, unless this is a control ep
+ /* Only one of these are used, unless this is the 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
+ unsigned int indma, outdma; /* DMA channel numbers for IN, OUT */
+ /* following are extracted from endpoint descriptor for easy access */
int max_pkt_size;
+ int type;
+ int direction;
+ /* WE assign endpoint addresses! */
+ int address;
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];
+static struct usb_dev {
+ endpoint_t ep[6];
+ ep0_stage_t ep0_stage;
+
+ struct usb_device_descriptor * dev_desc;
+ struct usb_interface_descriptor* if_desc;
+ struct usb_config_descriptor * conf_desc;
+ u8 * full_conf_desc;
+ struct usb_string_descriptor * str_desc[6];
+
+ /* callback to function layer */
+ void (*func_cb)(usbdev_cb_type_t type, unsigned long arg,
+ void *cb_data);
+ void* cb_data;
- struct usb_serial_port port[NUM_PORTS];
-
- dev_state_t state; // device state
+ usbdev_state_t state; // device state
int suspended; // suspended flag
int address; // device address
int interface;
+ int num_ep;
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_SIZE
-
-static 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 sending
-static 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 device
-static struct usb_string_descriptor string_desc0 = {
- bLength:4,
- bDescriptorType:USB_DT_STRING,
- wData:{0x0409} // English, US
-};
+} usbdev;
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
+ {USBD_EP0RD, USBD_EP0WR, USBD_EP0CS, USBD_EP0RDSTAT, USBD_EP0WRSTAT },
+ {0},
+ // FIFO 2 is EP2, 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}
+ // FIFO 3 is EP3, IN
+ { -1, USBD_EP3WR, USBD_EP3CS, -1, USBD_EP3WRSTAT },
+ // FIFO 4 is EP4, OUT
+ {USBD_EP4RD, -1, USBD_EP4CS, USBD_EP4RDSTAT, -1 },
+ // FIFO 5 is EP5, OUT
+ {USBD_EP5RD, -1, USBD_EP5CS, USBD_EP5RDSTAT, -1 }
};
static struct {
@@ -297,172 +154,88 @@
{ 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_EP4_RX, "USBDev EP4 OUT" },
{ 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;
-}
+#define CONTROL_EP USB_ENDPOINT_XFER_CONTROL
+#define BULK_EP USB_ENDPOINT_XFER_BULK
static inline endpoint_t *
-epnum_to_ep(struct usb_serial* serial, int ep_num)
+epaddr_to_ep(struct usb_dev* dev, int ep_addr)
{
- 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;
- }
-
+ if (ep_addr >= 0 && ep_addr < 2)
+ return &dev->ep[0];
+ if (ep_addr < 6)
+ return &dev->ep[ep_addr];
return NULL;
}
+static const char* std_req_name[] = {
+ "GET_STATUS",
+ "CLEAR_FEATURE",
+ "RESERVED",
+ "SET_FEATURE",
+ "RESERVED",
+ "SET_ADDRESS",
+ "GET_DESCRIPTOR",
+ "SET_DESCRIPTOR",
+ "GET_CONFIGURATION",
+ "SET_CONFIGURATION",
+ "GET_INTERFACE",
+ "SET_INTERFACE",
+ "SYNCH_FRAME"
+};
-static inline int
-port_paranoia_check(struct usb_serial_port *port, const char *function)
+static inline const char*
+get_std_req_name(int req)
{
- 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;
+ return (req >= 0 && req <= 12) ? std_req_name[req] : "UNKNOWN";
}
-static inline struct usb_serial*
-get_usb_serial (struct usb_serial_port *port, const char *function)
+#if 0
+static void
+dump_setup(devrequest* s)
{
- /* 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;
+ dbg(__FUNCTION__ ": requesttype=%d", s->requesttype);
+ dbg(__FUNCTION__ ": request=%d %s", s->request,
+ get_std_req_name(s->request));
+ dbg(__FUNCTION__ ": value=0x%04x", s->wValue);
+ dbg(__FUNCTION__ ": index=%d", s->index);
+ dbg(__FUNCTION__ ": length=%d", s->length);
}
+#endif
-
-static inline pkt_t *
-alloc_packet(int data_size)
+static inline usbdev_pkt_t *
+alloc_packet(endpoint_t * ep, int data_size, void* data)
{
- pkt_t* pkt = (pkt_t *)kmalloc(sizeof(pkt_t) + data_size, ALLOC_FLAGS);
+ usbdev_pkt_t* pkt =
+ (usbdev_pkt_t *)kmalloc(sizeof(usbdev_pkt_t) + data_size,
+ ALLOC_FLAGS);
if (!pkt)
return NULL;
+ pkt->ep_addr = ep->address;
pkt->size = data_size;
- pkt->bufptr = pkt->buf;
-#ifndef USBDEV_PIO
- pkt->bufptr = KSEG1ADDR(pkt->bufptr);
-#endif
+ pkt->status = 0;
pkt->next = NULL;
+ if (data)
+ memcpy(pkt->payload, data, data_size);
+
return pkt;
}
/*
* Link a packet to the tail of the enpoint's packet list.
+ * EP spinlock must be held when calling.
*/
static void
-link_packet(endpoint_t * ep, pkt_list_t * list, pkt_t * pkt)
+link_tail(endpoint_t * ep, pkt_list_t * list, usbdev_pkt_t * pkt)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ep->lock, flags);
-
if (!list->tail) {
list->head = list->tail = pkt;
list->count = 1;
@@ -471,24 +244,20 @@
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.
+ * Unlink and return a packet from the head of the given packet
+ * list. It is the responsibility of the caller to free the packet.
+ * EP spinlock must be held when calling.
*/
-static pkt_t *
-unlink_packet(endpoint_t * ep, pkt_list_t * list)
+static usbdev_pkt_t *
+unlink_head(pkt_list_t * list)
{
- unsigned long flags;
- pkt_t *pkt;
-
- spin_lock_irqsave(&ep->lock, flags);
+ usbdev_pkt_t *pkt;
pkt = list->head;
if (!pkt || !list->count) {
- spin_unlock_irqrestore(&ep->lock, flags);
return NULL;
}
@@ -499,118 +268,107 @@
} 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.
+ * packet list. EP spinlock must be held when calling.
*/
-static pkt_t *
+static usbdev_pkt_t *
add_packet(endpoint_t * ep, pkt_list_t * list, int size)
{
- pkt_t *pkt = alloc_packet(size);
+ usbdev_pkt_t *pkt = alloc_packet(ep, size, NULL);
if (!pkt)
return NULL;
- link_packet(ep, list, pkt);
+ link_tail(ep, list, pkt);
return pkt;
}
/*
* Unlink and free a packet from the head of the enpoint's
- * packet list.
+ * packet list. EP spinlock must be held when calling.
*/
static inline void
-free_packet(endpoint_t * ep, pkt_list_t * list)
+free_packet(pkt_list_t * list)
{
- kfree(unlink_packet(ep, list));
+ kfree(unlink_head(list));
}
+/* EP spinlock must be held when calling. */
static inline void
-flush_pkt_list(endpoint_t * ep, pkt_list_t * list)
+flush_pkt_list(pkt_list_t * list)
{
while (list->count)
- free_packet(ep, list);
+ free_packet(list);
}
-
+/* EP spinlock must be held when calling */
static inline void
flush_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,
+ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF |
+ USBDEV_FSTAT_OF,
ep->reg->write_fifo_status);
+ //udelay(100);
+ //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF,
+ // ep->reg->write_fifo_status);
}
}
-
+/* EP spinlock must be held when calling */
static inline void
flush_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,
+ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF |
+ USBDEV_FSTAT_OF,
ep->reg->read_fifo_status);
+ //udelay(100);
+ //au_writel(USBDEV_FSTAT_UF | USBDEV_FSTAT_OF,
+ // ep->reg->read_fifo_status);
}
}
+/* EP spinlock must be held when calling. */
static void
endpoint_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);
+ flush_pkt_list(&ep->inlist);
+ flush_pkt_list(&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);
}
-
+/* EP spinlock must be held when calling. */
static void
endpoint_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);
+ warn(__FUNCTION__);
- spin_unlock_irqrestore(&ep->lock, flags);
+ cs = au_readl(ep->reg->ctrl_stat) | USBDEV_CS_STALL;
+ au_writel(cs, ep->reg->ctrl_stat);
}
+/* EP spinlock must be held when calling. */
static void
endpoint_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);
+ warn(__FUNCTION__);
- spin_unlock_irqrestore(&ep->lock, flags);
+ cs = au_readl(ep->reg->ctrl_stat) & ~USBDEV_CS_STALL;
+ au_writel(cs, ep->reg->ctrl_stat);
}
static void
@@ -620,147 +378,182 @@
}
-#ifdef USBDEV_PIO
+/* EP spinlock must be held when calling. */
static int
endpoint_fifo_read(endpoint_t * ep)
{
- unsigned long flags;
int read_count = 0;
u8 *bufptr;
- pkt_t *pkt = ep->outlist.tail;
+ usbdev_pkt_t *pkt = ep->outlist.tail;
if (!pkt)
return -EINVAL;
- spin_lock_irqsave(&ep->lock, flags);
-
- bufptr = pkt->bufptr;
- while (inl(ep->reg->read_fifo_status) & USBDEV_FSTAT_FCNT_MASK) {
- *bufptr++ = inl(ep->reg->read_fifo) & 0xff;
+ bufptr = &pkt->payload[pkt->size];
+ while (au_readl(ep->reg->read_fifo_status) & USBDEV_FSTAT_FCNT_MASK) {
+ *bufptr++ = au_readl(ep->reg->read_fifo) & 0xff;
read_count++;
pkt->size++;
}
- pkt->bufptr = bufptr;
- spin_unlock_irqrestore(&ep->lock, flags);
return read_count;
}
-
+#if 0
+/* EP spinlock must be held when calling. */
static int
-endpoint_fifo_write(endpoint_t * ep)
+endpoint_fifo_write(endpoint_t * ep, int index)
{
- unsigned long flags;
int write_count = 0;
u8 *bufptr;
- pkt_t *pkt = ep->inlist.head;
+ usbdev_pkt_t *pkt = ep->inlist.head;
if (!pkt)
return -EINVAL;
- spin_lock_irqsave(&ep->lock, flags);
-
- bufptr = pkt->bufptr;
- while ((inl(ep->reg->write_fifo_status) & USBDEV_FSTAT_FCNT_MASK) <
- EP_FIFO_DEPTH) {
- if (bufptr < pkt->buf + pkt->size) {
- outl_sync(*bufptr++, ep->reg->write_fifo);
+ bufptr = &pkt->payload[index];
+ while ((au_readl(ep->reg->write_fifo_status) &
+ USBDEV_FSTAT_FCNT_MASK) < EP_FIFO_DEPTH) {
+ if (bufptr < pkt->payload + pkt->size) {
+ au_writel(*bufptr++, ep->reg->write_fifo);
write_count++;
} else {
break;
}
}
- pkt->bufptr = bufptr;
- spin_unlock_irqrestore(&ep->lock, flags);
return write_count;
}
-#endif // USBDEV_PIO
+#endif
/*
* This routine is called to restart transmission of a packet.
* The endpoint's TSIZE must be set to the new packet's size,
* and DMA to the write FIFO needs to be restarted.
+ * EP spinlock must be held when calling.
*/
static void
kickstart_send_packet(endpoint_t * ep)
{
u32 cs;
- pkt_t *pkt = ep->inlist.head;
+ usbdev_pkt_t *pkt = ep->inlist.head;
- dbg(__FUNCTION__ ": pkt=%p", pkt);
+ vdbg(__FUNCTION__ ": ep%d, pkt=%p", ep->address, pkt);
- if (!pkt)
+ if (!pkt) {
+ err(__FUNCTION__ ": head=NULL! list->count=%d",
+ ep->inlist.count);
return;
+ }
+
+ dma_cache_wback_inv((unsigned long)pkt->payload, pkt->size);
/*
- * The write fifo should already be drained if things are
- * working right, but flush it anyway just in case.
+ * make sure FIFO is empty
*/
flush_write_fifo(ep);
- cs = inl(ep->reg->ctrl_stat) & USBDEV_CS_STALL;
+
+ cs = au_readl(ep->reg->ctrl_stat) & USBDEV_CS_STALL;
cs |= (pkt->size << USBDEV_CS_TSIZE_BIT);
- outl_sync(cs, ep->reg->ctrl_stat);
-#ifdef USBDEV_PIO
- endpoint_fifo_write(ep);
-#else
- disable_dma(ep->indma);
- if (get_dma_active_buffer(ep->indma)) {
+ au_writel(cs, ep->reg->ctrl_stat);
+
+ if (get_dma_active_buffer(ep->indma) == 1) {
set_dma_count1(ep->indma, pkt->size);
- set_dma_addr1(ep->indma, virt_to_phys(pkt->bufptr));
+ set_dma_addr1(ep->indma, virt_to_phys(pkt->payload));
enable_dma_buffer1(ep->indma); // reenable
} else {
set_dma_count0(ep->indma, pkt->size);
- set_dma_addr0(ep->indma, virt_to_phys(pkt->bufptr));
+ set_dma_addr0(ep->indma, virt_to_phys(pkt->payload));
enable_dma_buffer0(ep->indma); // reenable
}
- enable_dma(ep->indma);
-#endif
+ if (dma_halted(ep->indma))
+ start_dma(ep->indma);
}
/*
* This routine is called when a packet in the inlist has been
* completed. Frees the completed packet and starts sending the
- * next.
+ * next. EP spinlock must be held when calling.
*/
-static void
+static usbdev_pkt_t *
send_packet_complete(endpoint_t * ep)
{
- if (ep->inlist.head)
- dbg(__FUNCTION__ ": pkt=%p, ab=%d",
- ep->inlist.head, get_dma_active_buffer(ep->indma));
-
- outl_sync(inl(ep->reg->ctrl_stat) & USBDEV_CS_STALL,
- ep->reg->ctrl_stat);
- //disable_dma(ep->indma);
- free_packet(ep, &ep->inlist);
+ usbdev_pkt_t *pkt = unlink_head(&ep->inlist);
+
+ if (pkt) {
+ pkt->status =
+ (au_readl(ep->reg->ctrl_stat) & USBDEV_CS_NAK) ?
+ PKT_STATUS_NAK : PKT_STATUS_ACK;
+
+ vdbg(__FUNCTION__ ": ep%d, %s pkt=%p, list count=%d",
+ ep->address, (pkt->status & PKT_STATUS_NAK) ?
+ "NAK" : "ACK", pkt, ep->inlist.count);
+ }
+
+ /*
+ * The write fifo should already be drained if things are
+ * working right, but flush it anyway just in case.
+ */
+ flush_write_fifo(ep);
+
// begin transmitting next packet in the inlist
- if (ep->inlist.count)
+ if (ep->inlist.count) {
kickstart_send_packet(ep);
-}
+ }
+ return pkt;
+}
/*
- * Unlink and return a packet from the head of the given ep's packet
- * outlist. It is the responsibility of the caller to free the packet.
- * The receive complete interrupt adds packets to the tail of this list.
+ * Add a new packet to the tail of the given ep's packet
+ * inlist. The transmit complete interrupt frees packets from
+ * the head of this list. EP spinlock must be held when calling.
*/
-static pkt_t *
-receive_packet(endpoint_t * ep)
+static int
+send_packet(struct usb_dev* dev, usbdev_pkt_t *pkt, int async)
{
- pkt_t *pkt = unlink_packet(ep, &ep->outlist);
- //dma_cache_inv((unsigned long)pkt->buf, pkt->size);
- return pkt;
+ pkt_list_t *list;
+ endpoint_t* ep;
+
+ if (!pkt || !(ep = epaddr_to_ep(dev, pkt->ep_addr)))
+ return -EINVAL;
+
+ if (!pkt->size)
+ return 0;
+
+ list = &ep->inlist;
+
+ if (!async && list->count) {
+ halt_dma(ep->indma);
+ flush_pkt_list(list);
+ }
+
+ link_tail(ep, list, pkt);
+
+ vdbg(__FUNCTION__ ": ep%d, pkt=%p, size=%d, list count=%d",
+ ep->address, pkt, pkt->size, list->count);
+
+ if (list->count == 1) {
+ /*
+ * if the packet count is one, it means the list was empty,
+ * and no more data will go out this ep until we kick-start
+ * it again.
+ */
+ kickstart_send_packet(ep);
+ }
+
+ return pkt->size;
}
/*
* This routine is called to restart reception of a packet.
+ * EP spinlock must be held when calling.
*/
static void
kickstart_receive_packet(endpoint_t * ep)
{
- pkt_t *pkt;
+ usbdev_pkt_t *pkt;
// get and link a new packet for next reception
if (!(pkt = add_packet(ep, &ep->outlist, ep->max_pkt_size))) {
@@ -768,23 +561,21 @@
return;
}
- /*
- * The read fifo should already be drained if things are
- * working right, but flush it anyway just in case.
- */
- flush_read_fifo(ep);
-#ifndef USBDEV_PIO
- if (get_dma_active_buffer(ep->outdma)) {
+ if (get_dma_active_buffer(ep->outdma) == 1) {
+ clear_dma_done1(ep->outdma);
set_dma_count1(ep->outdma, ep->max_pkt_size);
- set_dma_addr1(ep->outdma, virt_to_phys(pkt->bufptr));
+ set_dma_count0(ep->outdma, 0);
+ set_dma_addr1(ep->outdma, virt_to_phys(pkt->payload));
enable_dma_buffer1(ep->outdma); // reenable
} else {
+ clear_dma_done0(ep->outdma);
set_dma_count0(ep->outdma, ep->max_pkt_size);
- set_dma_addr0(ep->outdma, virt_to_phys(pkt->bufptr));
+ set_dma_count1(ep->outdma, 0);
+ set_dma_addr0(ep->outdma, virt_to_phys(pkt->payload));
enable_dma_buffer0(ep->outdma); // reenable
}
- enable_dma(ep->outdma);
-#endif
+ if (dma_halted(ep->outdma))
+ start_dma(ep->outdma);
}
@@ -795,404 +586,499 @@
* remaining DMA counter. Then prepares a new packet for reception
* and restarts DMA. FIXME: what if another packet comes in
* on top of the completed packet? Counter would be wrong.
+ * EP spinlock must be held when calling.
*/
-static void
+static usbdev_pkt_t *
receive_packet_complete(endpoint_t * ep)
{
- pkt_t *pkt = ep->outlist.tail;
+ usbdev_pkt_t *pkt = ep->outlist.tail;
+ u32 cs;
+
+ halt_dma(ep->outdma);
+
+ cs = au_readl(ep->reg->ctrl_stat);
if (!pkt)
- return;
+ return NULL;
- disable_dma(ep->outdma);
pkt->size = ep->max_pkt_size - get_dma_residue(ep->outdma);
-#ifdef USBDEV_PIO
- pkt->bufptr = pkt->buf; // reset bufptr
-#endif
- dbg(__FUNCTION__ ": size=%d", pkt->size);
+ if (pkt->size)
+ dma_cache_inv((unsigned long)pkt->payload, pkt->size);
+ /*
+ * need to pull out any remaining bytes in the FIFO.
+ */
+ endpoint_fifo_read(ep);
+ /*
+ * should be drained now, but flush anyway just in case.
+ */
+ flush_read_fifo(ep);
+
+ pkt->status = (cs & USBDEV_CS_NAK) ? PKT_STATUS_NAK : PKT_STATUS_ACK;
+ if (ep->address == 0 && (cs & USBDEV_CS_SU))
+ pkt->status |= PKT_STATUS_SU;
+
+ vdbg(__FUNCTION__ ": ep%d, %s pkt=%p, size=%d",
+ ep->address, (pkt->status & PKT_STATUS_NAK) ?
+ "NAK" : "ACK", pkt, pkt->size);
kickstart_receive_packet(ep);
-}
+ return pkt;
+}
/*
- * Add a new packet to the tail of the given ep's packet
- * inlist. The transmit complete interrupt frees packets from
- * the head of this list.
+ ****************************************************************************
+ * Here starts the standard device request handlers. They are
+ * all called by do_setup() via a table of function pointers.
+ ****************************************************************************
*/
-static int
-send_packet(endpoint_t * ep, u8 * data, int data_len, int from_user)
-{
- unsigned long flags;
- pkt_list_t *list = &ep->inlist;
- pkt_t *pkt;
-
- if (!data || !data_len)
- return 0;
- if (!(pkt = alloc_packet(data_len))) {
- err(__FUNCTION__ ": could not alloc new packet");
- return -ENOMEM;
+static ep0_stage_t
+do_get_status(struct usb_dev* dev, devrequest* setup)
+{
+ switch (setup->requesttype) {
+ case 0x80: // Device
+ // FIXME: send device status
+ break;
+ case 0x81: // Interface
+ // FIXME: send interface status
+ break;
+ case 0x82: // End Point
+ // FIXME: send endpoint status
+ break;
+ default:
+ // Invalid Command
+ endpoint_stall(&dev->ep[0]); // Stall End Point 0
+ break;
}
- if (from_user)
- copy_from_user(pkt->bufptr, data, data_len);
- else
- memcpy(pkt->bufptr, data, data_len);
- au_sync();
-
- //dma_cache_wback_inv((unsigned long)pkt->buf, data_len);
+ return STATUS_STAGE;
+}
- link_packet(ep, list, pkt);
+static ep0_stage_t
+do_clear_feature(struct usb_dev* dev, devrequest* setup)
+{
+ switch (setup->requesttype) {
+ case 0x00: // Device
+ if ((le16_to_cpu(setup->wValue) & 0xff) == 1)
+ dev->remote_wakeup_en = 0;
+ else
+ endpoint_stall(&dev->ep[0]);
+ break;
+ case 0x02: // End Point
+ if ((le16_to_cpu(setup->wValue) & 0xff) == 0) {
+ endpoint_t *ep =
+ epaddr_to_ep(dev,
+ le16_to_cpu(setup->index) & 0xff);
+
+ endpoint_unstall(ep);
+ endpoint_reset_datatoggle(ep);
+ } else
+ endpoint_stall(&dev->ep[0]);
+ break;
+ }
- spin_lock_irqsave(&ep->lock, flags);
+ return SETUP_STAGE;
+}
- dbg(__FUNCTION__ ": size=%d, list count=%d", pkt->size, list->count);
+static ep0_stage_t
+do_reserved(struct usb_dev* dev, devrequest* setup)
+{
+ // Invalid request, stall End Point 0
+ endpoint_stall(&dev->ep[0]);
+ return SETUP_STAGE;
+}
- if (list->count == 1) {
- /*
- * if the packet count is one, it means the list was empty,
- * and no more data will go out this ep until we kick-start
- * it again.
- */
- kickstart_send_packet(ep);
+static ep0_stage_t
+do_set_feature(struct usb_dev* dev, devrequest* setup)
+{
+ switch (setup->requesttype) {
+ case 0x00: // Device
+ if ((le16_to_cpu(setup->wValue) & 0xff) == 1)
+ dev->remote_wakeup_en = 1;
+ else
+ endpoint_stall(&dev->ep[0]);
+ break;
+ case 0x02: // End Point
+ if ((le16_to_cpu(setup->vwValue) & 0xff) == 0) {
+ endpoint_t *ep =
+ epaddr_to_ep(dev,
+ le16_to_cpu(setup->index) & 0xff);
+
+ endpoint_stall(ep);
+ } else
+ endpoint_stall(&dev->ep[0]);
+ break;
}
- spin_unlock_irqrestore(&ep->lock, flags);
- return data_len;
+ return SETUP_STAGE;
}
-
-// SETUP packet request parser
-static void
-process_setup (struct usb_serial* serial, devrequest* setup)
+static ep0_stage_t
+do_set_address(struct usb_dev* dev, devrequest* setup)
{
- int desc_len, strnum;
+ int new_state = dev->state;
+ int new_addr = le16_to_cpu(setup->wValue);
- dbg(__FUNCTION__ ": req %d", setup->request);
+ dbg(__FUNCTION__ ": our address=%d", new_addr);
- switch (setup->request) {
- case USB_REQ_SET_ADDRESS:
- serial->address = le16_to_cpu(setup->value);
- dbg(__FUNCTION__ ": our address=%d", serial->address);
- if (serial->address > 127 || serial->state == CONFIGURED) {
+ if (new_addr > 127) {
// usb spec doesn't tell us what to do, so just go to
// default state
- serial->state = DEFAULT;
- serial->address = 0;
- } else if (serial->address)
- serial->state = ADDRESS;
- else
- serial->state = DEFAULT;
- break;
- case USB_REQ_GET_DESCRIPTOR:
- desc_len = le16_to_cpu(setup->length);
- switch (le16_to_cpu(setup->value) >> 8) {
+ new_state = DEFAULT;
+ dev->address = 0;
+ } else if (dev->address != new_addr) {
+ dev->address = new_addr;
+ new_state = ADDRESS;
+ }
+
+ if (dev->state != new_state) {
+ dev->state = new_state;
+ /* inform function layer of usbdev state change */
+ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data);
+ }
+
+ return SETUP_STAGE;
+}
+
+static ep0_stage_t
+do_get_descriptor(struct usb_dev* dev, devrequest* setup)
+{
+ int strnum, desc_len = le16_to_cpu(setup->length);
+
+ switch (le16_to_cpu(setup->wValue) >> 8) {
case USB_DT_DEVICE:
// send device descriptor!
- desc_len = desc_len > serial->dev_desc->bLength ?
- serial->dev_desc->bLength : desc_len;
+ desc_len = desc_len > dev->dev_desc->bLength ?
+ dev->dev_desc->bLength : desc_len;
dbg("sending device desc, size=%d", desc_len);
- send_packet(&serial->ep_ctrl, (u8*)serial->dev_desc,
- desc_len, 0);
+ send_packet(dev, alloc_packet(&dev->ep[0], desc_len,
+ dev->dev_desc), 0);
break;
case USB_DT_CONFIG:
// If the config descr index in low-byte of
- // setup->value is valid, send config descr,
+ // setup->wValue is valid, send config descr,
// otherwise stall ep0.
- if ((le16_to_cpu(setup->value) & 0xff) == 0) {
+ if ((le16_to_cpu(setup->wValue) & 0xff) == 0) {
// send config descriptor!
if (desc_len <= USB_DT_CONFIG_SIZE) {
dbg("sending partial config desc, size=%d",
desc_len);
- send_packet(&serial->ep_ctrl,
- (u8*)serial->conf_desc,
- desc_len, 0);
+ send_packet(dev,
+ alloc_packet(&dev->ep[0],
+ desc_len,
+ dev->conf_desc),
+ 0);
} else {
- u8 full_conf_desc[CONFIG_DESC_LEN];
- int i, index = 0;
- memcpy(&full_conf_desc[index],
- serial->conf_desc,
- USB_DT_CONFIG_SIZE);
- index += USB_DT_CONFIG_SIZE;
- memcpy(&full_conf_desc[index],
- serial->if_desc,
- USB_DT_INTERFACE_SIZE);
- index += USB_DT_INTERFACE_SIZE;
- for (i = 0; i < NUM_PORTS; i++) {
- memcpy(&full_conf_desc[index],
- serial->port[i].ep_bulkin.desc,
- USB_DT_ENDPOINT_SIZE);
- index += USB_DT_ENDPOINT_SIZE;
- memcpy(&full_conf_desc[index],
- serial->port[i].ep_bulkout.desc,
- USB_DT_ENDPOINT_SIZE);
- index += USB_DT_ENDPOINT_SIZE;
- }
- dbg("sending whole config desc, size=%d, our size=%d",
- desc_len, CONFIG_DESC_LEN);
- desc_len = desc_len > CONFIG_DESC_LEN ?
- CONFIG_DESC_LEN : desc_len;
- send_packet(&serial->ep_ctrl,
- full_conf_desc, desc_len, 0);
+ int len = dev->conf_desc->wTotalLength;
+ dbg("sending whole config desc,"
+ " size=%d, our size=%d", desc_len, len);
+ desc_len = desc_len > len ? len : desc_len;
+ send_packet(dev,
+ alloc_packet(&dev->ep[0],
+ desc_len,
+ dev->full_conf_desc),
+ 0);
}
} else
- endpoint_stall(&serial->ep_ctrl);
+ endpoint_stall(&dev->ep[0]);
break;
case USB_DT_STRING:
- // If the string descr index in low-byte of setup->value
+ // If the string descr index in low-byte of setup->wValue
// is valid, send string descr, otherwise stall ep0.
- strnum = le16_to_cpu(setup->value) & 0xff;
+ strnum = le16_to_cpu(setup->wValue) & 0xff;
if (strnum >= 0 && strnum < 6) {
struct usb_string_descriptor *desc =
- serial->str_desc[strnum];
+ dev->str_desc[strnum];
desc_len = desc_len > desc->bLength ?
desc->bLength : desc_len;
dbg("sending string desc %d", strnum);
- send_packet(&serial->ep_ctrl, (u8 *) desc,
- desc_len, 0);
+ send_packet(dev,
+ alloc_packet(&dev->ep[0], desc_len,
+ desc), 0);
} else
- endpoint_stall(&serial->ep_ctrl);
+ endpoint_stall(&dev->ep[0]);
break;
- default: // Invalid request
- dbg("invalid get desc=%d, stalled",
- le16_to_cpu(setup->value) >> 8);
- endpoint_stall(&serial->ep_ctrl); // Stall endpoint 0
+ default:
+ // Invalid request
+ err("invalid get desc=%d, stalled",
+ le16_to_cpu(setup->wValue) >> 8);
+ endpoint_stall(&dev->ep[0]); // Stall endpoint 0
break;
}
- break;
- case USB_REQ_SET_DESCRIPTOR:
- // FIXME: anything to set here?
- break;
- case USB_REQ_GET_INTERFACE:
+
+ return STATUS_STAGE;
+}
+
+static ep0_stage_t
+do_set_descriptor(struct usb_dev* dev, devrequest* setup)
+{
+ // TODO: implement
+ // there will be an OUT data stage (the descriptor to set)
+ return DATA_STAGE;
+}
+
+static ep0_stage_t
+do_get_configuration(struct usb_dev* dev, devrequest* setup)
+{
+ // send dev->configuration
+ dbg("sending config");
+ send_packet(dev, alloc_packet(&dev->ep[0], 1, &dev->configuration),
+ 0);
+ return STATUS_STAGE;
+}
+
+static ep0_stage_t
+do_set_configuration(struct usb_dev* dev, devrequest* setup)
+{
+ // set active config to low-byte of setup->wValue
+ dev->configuration = le16_to_cpu(setup->wValue) & 0xff;
+ dbg("set config, config=%d", dev->configuration);
+ if (!dev->configuration && dev->state > DEFAULT) {
+ dev->state = ADDRESS;
+ /* inform function layer of usbdev state change */
+ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data);
+ } else if (dev->configuration == 1) {
+ dev->state = CONFIGURED;
+ /* inform function layer of usbdev state change */
+ dev->func_cb(CB_NEW_STATE, dev->state, dev->cb_data);
+ } else {
+ // FIXME: "respond with request error" - how?
+ }
+
+ return SETUP_STAGE;
+}
+
+static ep0_stage_t
+do_get_interface(struct usb_dev* dev, devrequest* setup)
+{
// interface must be zero.
- if ((le16_to_cpu(setup->index) & 0xff) ||
- serial->state == ADDRESS) {
+ if ((le16_to_cpu(setup->index) & 0xff) || dev->state == ADDRESS) {
// FIXME: respond with "request error". how?
- } else if (serial->state == CONFIGURED) {
- // send serial->alternate_setting
+ } else if (dev->state == CONFIGURED) {
+ // send dev->alternate_setting
dbg("sending alt setting");
- send_packet(&serial->ep_ctrl,
- &serial->alternate_setting, 1, 0);
+ send_packet(dev, alloc_packet(&dev->ep[0], 1,
+ &dev->alternate_setting), 0);
}
- break;
- case USB_REQ_SET_INTERFACE:
- if (serial->state == ADDRESS) {
+
+ return STATUS_STAGE;
+
+}
+
+static ep0_stage_t
+do_set_interface(struct usb_dev* dev, devrequest* setup)
+{
+ if (dev->state == ADDRESS) {
// FIXME: respond with "request error". how?
- } else if (serial->state == CONFIGURED) {
- serial->interface = le16_to_cpu(setup->index) & 0xff;
- serial->alternate_setting =
- le16_to_cpu(setup->value) & 0xff;
+ } else if (dev->state == CONFIGURED) {
+ dev->interface = le16_to_cpu(setup->index) & 0xff;
+ dev->alternate_setting =
+ le16_to_cpu(setup->wValue) & 0xff;
// interface and alternate_setting must be zero
- if (serial->interface || serial->alternate_setting) {
+ if (dev->interface || dev->alternate_setting) {
// FIXME: respond with "request error". how?
}
}
- break;
- case USB_REQ_SET_CONFIGURATION:
- // set active config to low-byte of serial.value
- serial->configuration = le16_to_cpu(setup->value) & 0xff;
- dbg("set config, config=%d", serial->configuration);
- if (!serial->configuration && serial->state > DEFAULT)
- serial->state = ADDRESS;
- else if (serial->configuration == 1)
- serial->state = CONFIGURED;
- else {
- // FIXME: "respond with request error" - how?
- }
- break;
- case USB_REQ_GET_CONFIGURATION:
- // send serial->configuration
- dbg("sending config");
- send_packet(&serial->ep_ctrl, &serial->configuration, 1, 0);
- break;
- case USB_REQ_GET_STATUS:
- // FIXME: looks like the h/w handles this one
- switch (setup->requesttype) {
- case 0x80: // Device
- // FIXME: send device status
- break;
- case 0x81: // Interface
- // FIXME: send interface status
- break;
- case 0x82: // End Point
- // FIXME: send endpoint status
- break;
- default: // Invalid Command
- endpoint_stall(&serial->ep_ctrl); // Stall End Point 0
- break;
- }
- break;
- case USB_REQ_CLEAR_FEATURE:
- switch (setup->requesttype) {
- case 0x00: // Device
- if ((le16_to_cpu(setup->value) & 0xff) == 1)
- serial->remote_wakeup_en = 0;
- else
- endpoint_stall(&serial->ep_ctrl);
- break;
- case 0x02: // End Point
- if ((le16_to_cpu(setup->value) & 0xff) == 0) {
- endpoint_t *ep =
- epnum_to_ep(serial,
- le16_to_cpu(setup->index) & 0xff);
- endpoint_unstall(ep);
- endpoint_reset_datatoggle(ep);
- } else
- endpoint_stall(&serial->ep_ctrl);
- break;
+ return SETUP_STAGE;
+}
+
+static ep0_stage_t
+do_synch_frame(struct usb_dev* dev, devrequest* setup)
+{
+ // TODO
+ return SETUP_STAGE;
+}
+
+typedef ep0_stage_t (*req_method_t)(struct usb_dev* dev,
+ devrequest* setup);
+
+
+/* Table of the standard device request handlers */
+static const req_method_t req_method[] = {
+ do_get_status,
+ do_clear_feature,
+ do_reserved,
+ do_set_feature,
+ do_reserved,
+ do_set_address,
+ do_get_descriptor,
+ do_set_descriptor,
+ do_get_configuration,
+ do_set_configuration,
+ do_get_interface,
+ do_set_interface,
+ do_synch_frame
+};
+
+
+// SETUP packet request dispatcher
+static void
+do_setup (struct usb_dev* dev, devrequest* setup)
+{
+ req_method_t m;
+
+ dbg(__FUNCTION__ ": req %d %s", setup->request,
+ get_std_req_name(setup->request));
+
+ if ((setup->requesttype & USB_TYPE_MASK) != USB_TYPE_STANDARD ||
+ (setup->requesttype & USB_RECIP_MASK) != USB_RECIP_DEVICE) {
+ err(__FUNCTION__ ": invalid requesttype 0x%02x",
+ setup->requesttype);
+ return;
}
- break;
- case USB_REQ_SET_FEATURE:
- switch (setup->requesttype) {
- case 0x00: // Device
- if ((le16_to_cpu(setup->value) & 0xff) == 1)
- serial->remote_wakeup_en = 1;
+
+ if ((setup->requesttype & 0x80) == USB_DIR_OUT && setup->length)
+ dbg(__FUNCTION__ ": OUT phase! length=%d", setup->length);
+
+ if (setup->request < sizeof(req_method)/sizeof(req_method_t))
+ m = req_method[setup->request];
else
- endpoint_stall(&serial->ep_ctrl);
- break;
- case 0x02: // End Point
- if ((le16_to_cpu(setup->value) & 0xff) == 0) {
- endpoint_t *ep =
- epnum_to_ep(serial,
- le16_to_cpu(setup->index) & 0xff);
+ m = do_reserved;
- endpoint_stall(ep);
- } else
- endpoint_stall(&serial->ep_ctrl);
- break;
- }
- break;
- default:
- endpoint_stall(&serial->ep_ctrl); // Stall End Point 0
- break;
- }
+ dev->ep0_stage = (*m)(dev, setup);
}
-
/*
- * A complete packet (SETUP, DATA0, or DATA1) has been received
- * on the given endpoint's fifo.
+ * A SETUP, DATA0, or DATA1 packet has been received
+ * on the default control endpoint's fifo.
*/
static void
-process_complete (struct usb_serial* serial, int fifo_num)
+process_ep0_receive (struct usb_dev* dev)
{
- endpoint_t *ep = fifonum_to_ep(serial, fifo_num);
- struct usb_serial_port *port = NULL;
- pkt_t *pkt = 0;
- u32 cs;
+ endpoint_t *ep0 = &dev->ep[0];
+ usbdev_pkt_t *pkt;
- cs = inl(ep->reg->ctrl_stat);
+ spin_lock(&ep0->lock);
- switch (fifo_num) {
- case 0:
- spin_lock(&ep->lock);
// complete packet and prepare a new packet
- receive_packet_complete(ep);
-
- // Get it immediately from endpoint.
- if (!(pkt = receive_packet(ep))) {
- spin_unlock(&ep->lock);
+ pkt = receive_packet_complete(ep0);
+ if (!pkt) {
+ // FIXME: should put a warn/err here.
+ spin_unlock(&ep0->lock);
return;
}
-
- // SETUP packet received ?
- //if (cs & USBDEV_CS_SU) { FIXME: uncomment!
+
+ // unlink immediately from endpoint.
+ unlink_head(&ep0->outlist);
+
+ // override current stage if h/w says it's a setup packet
+ if (pkt->status & PKT_STATUS_SU)
+ dev->ep0_stage = SETUP_STAGE;
+
+ switch (dev->ep0_stage) {
+ case SETUP_STAGE:
+ vdbg("SU bit is %s in setup stage",
+ (pkt->status & PKT_STATUS_SU) ? "set" : "not set");
+
if (pkt->size == sizeof(devrequest)) {
- devrequest setup;
- if ((cs & (USBDEV_CS_NAK | USBDEV_CS_ACK)) ==
- USBDEV_CS_ACK)
- dbg("got SETUP");
+#ifdef VDEBUG
+ if (pkt->status & PKT_STATUS_ACK)
+ vdbg("received SETUP");
else
- dbg("got NAK SETUP, cs=%08x", cs);
- memcpy(&setup, pkt->bufptr, sizeof(devrequest));
- process_setup(serial, &setup);
- //} else FIXME: uncomment!
- //dbg(__FUNCTION__ ": wrong size SETUP received");
- } else {
- // DATAx packet received on endpoint 0
- // FIXME: will need a state machine for control
- // OUT transactions
- dbg("got DATAx on EP0, size=%d, cs=%08x",
- pkt->size, cs);
+ vdbg("received NAK SETUP");
+#endif
+ do_setup(dev, (devrequest*)pkt->payload);
+ } else
+ err(__FUNCTION__ ": wrong size SETUP received");
+ break;
+ case DATA_STAGE:
+ /*
+ * this setup has an OUT data stage. Of the standard
+ * device requests, only set_descriptor has this stage,
+ * so this packet is that descriptor. TODO: drop it for
+ * now, set_descriptor not implemented.
+ *
+ * Need to place a byte in the write FIFO here, to prepare
+ * to send a zero-length DATA ack packet to the host in the
+ * STATUS stage.
+ */
+ au_writel(0, ep0->reg->write_fifo);
+ dbg("received OUT stage DATAx on EP0, size=%d", pkt->size);
+ dev->ep0_stage = SETUP_STAGE;
+ break;
+ case STATUS_STAGE:
+ // this setup had an IN data stage, and host is ACK'ing
+ // the packet we sent during that stage.
+ if (pkt->size != 0)
+ warn("received non-zero ACK on EP0??");
+#ifdef VDEBUG
+ else
+ vdbg("received ACK on EP0");
+#endif
+ dev->ep0_stage = SETUP_STAGE;
+ break;
}
- spin_unlock(&ep->lock);
+ spin_unlock(&ep0->lock);
// we're done processing the packet, free it
kfree(pkt);
- break;
+}
+
+
+/*
+ * A DATA0/1 packet has been received on one of the OUT endpoints (4 or 5)
+ */
+static void
+process_ep_receive (struct usb_dev* dev, endpoint_t *ep)
+{
+ usbdev_pkt_t *pkt;
- case 4:
- case 5:
- port = fifonum_to_port(serial, fifo_num);
- dbg("got DATAx on port %d, cs=%08x", port->number, cs);
spin_lock(&ep->lock);
- receive_packet_complete(ep);
+ pkt = receive_packet_complete(ep);
spin_unlock(&ep->lock);
- // mark a bh to push this data up to the tty
- queue_task(&port->receive_complete_tq, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- break;
- default:
- break;
- }
+ dev->func_cb(CB_PKT_COMPLETE, (unsigned long)pkt, dev->cb_data);
}
-// This ISR needs to ack both the complete and suspend events
+/* This ISR handles the receive complete and suspend events */
static void
req_sus_intr (int irq, void *dev_id, struct pt_regs *regs)
{
- struct usb_serial *serial = (struct usb_serial *) dev_id;
- int i;
+ struct usb_dev *dev = (struct usb_dev *) dev_id;
u32 status;
- status = inl(USB_DEV_INT_STATUS);
- outl_sync(status, USB_DEV_INT_STATUS); // ack'em
+ status = au_readl(USBD_INTSTAT);
+ au_writel(status, USBD_INTSTAT); // ack'em
-#ifdef USBDEV_PIO
- for (i = 0; i < 6; i++) {
- if (status & (1 << (USBDEV_INT_HF_BIT + i))) {
- endpoint_t *ep = fifonum_to_ep(serial, i);
-
- if (ep->desc->bEndpointAddress & USB_DIR_IN)
- endpoint_fifo_write(ep);
- else
- endpoint_fifo_read(ep);
- }
- }
-#endif
-
- for (i = 0; i < 6; i++) {
- if (status & (1 << i))
- process_complete(serial, i);
- }
+ if (status & (1<<0))
+ process_ep0_receive(dev);
+ if (status & (1<<4))
+ process_ep_receive(dev, &dev->ep[4]);
+ if (status & (1<<5))
+ process_ep_receive(dev, &dev->ep[5]);
}
+/* This ISR handles the DMA done events on EP0 */
static void
-dma_done_ctrl(struct usb_serial* serial)
+dma_done_ep0_intr(int irq, void *dev_id, struct pt_regs *regs)
{
- endpoint_t *ep = &serial->ep_ctrl;
+ struct usb_dev *dev = (struct usb_dev *) dev_id;
+ usbdev_pkt_t* pkt;
+ endpoint_t *ep0 = &dev->ep[0];
u32 cs0, buff_done;
- spin_lock(&ep->lock);
- cs0 = inl(ep->reg->ctrl_stat);
+ spin_lock(&ep0->lock);
+ cs0 = au_readl(ep0->reg->ctrl_stat);
// first check packet transmit done
- if ((buff_done = get_dma_buffer_done(ep->indma)) != 0) {
- // transmitted a DATAx packet on control endpoint 0
+ if ((buff_done = get_dma_buffer_done(ep0->indma)) != 0) {
+ // transmitted a DATAx packet during DATA stage
+ // on control endpoint 0
// clear DMA done bit
- if (buff_done == DMA_D0)
- clear_dma_done0(ep->indma);
- else
- clear_dma_done1(ep->indma);
-
- send_packet_complete(ep);
+ if (buff_done & DMA_D0)
+ clear_dma_done0(ep0->indma);
+ if (buff_done & DMA_D1)
+ clear_dma_done1(ep0->indma);
+
+ pkt = send_packet_complete(ep0);
+ if (pkt)
+ kfree(pkt);
}
/*
@@ -1200,645 +1086,477 @@
* the receive packet complete intr should happen
* before the DMA done intr occurs.
*/
- if ((buff_done = get_dma_buffer_done(ep->outdma)) != 0) {
+ if ((buff_done = get_dma_buffer_done(ep0->outdma)) != 0) {
// clear DMA done bit
- if (buff_done == DMA_D0)
- clear_dma_done0(ep->outdma);
- else
- clear_dma_done1(ep->outdma);
+ if (buff_done & DMA_D0)
+ clear_dma_done0(ep0->outdma);
+ if (buff_done & DMA_D1)
+ clear_dma_done1(ep0->outdma);
+
+ //process_ep0_receive(dev);
}
- spin_unlock(&ep->lock);
+ spin_unlock(&ep0->lock);
}
+/* This ISR handles the DMA done events on endpoints 2,3,4,5 */
static void
-dma_done_port(struct usb_serial_port * port)
+dma_done_ep_intr(int irq, void *dev_id, struct pt_regs *regs)
{
- endpoint_t *ep;
+ struct usb_dev *dev = (struct usb_dev *) dev_id;
+ int i;
+
+ for (i = 2; i < 6; i++) {
u32 buff_done;
+ usbdev_pkt_t* pkt;
+ endpoint_t *ep = &dev->ep[i];
+
+ if (!ep->active) continue;
- // first check packet transmit done (bulk IN ep)
- ep = &port->ep_bulkin;
spin_lock(&ep->lock);
- if ((buff_done = get_dma_buffer_done(ep->indma)) != 0) {
- // transmitted a DATAx packet on the port's bulk IN endpoint
+
+ if (ep->direction == USB_DIR_IN) {
+ buff_done = get_dma_buffer_done(ep->indma);
+ if (buff_done != 0) {
+ // transmitted a DATAx pkt on the IN ep
// clear DMA done bit
- if (buff_done == DMA_D0)
+ if (buff_done & DMA_D0)
clear_dma_done0(ep->indma);
- else
+ if (buff_done & DMA_D1)
clear_dma_done1(ep->indma);
- send_packet_complete(ep);
- // mark a bh to wakeup any tty write system call on the port.
- queue_task(&port->send_complete_tq, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- }
- spin_unlock(&ep->lock);
+ pkt = send_packet_complete(ep);
+ spin_unlock(&ep->lock);
+ dev->func_cb(CB_PKT_COMPLETE,
+ (unsigned long)pkt,
+ dev->cb_data);
+ spin_lock(&ep->lock);
+ }
+ } else {
/*
- * Now check packet receive done (bulk OUT ep). Shouldn't
- * get these, the receive packet complete intr should happen
+ * Check packet receive done (OUT ep). Shouldn't get
+ * these, the rx packet complete intr should happen
* before the DMA done intr occurs.
*/
- ep = &port->ep_bulkout;
- spin_lock(&ep->lock);
- if ((buff_done = get_dma_buffer_done(ep->outdma)) != 0) {
- // received a DATAx packet on the port's bulk OUT endpoint
+ buff_done = get_dma_buffer_done(ep->outdma);
+ if (buff_done != 0) {
+ // received a DATAx pkt on the OUT ep
// clear DMA done bit
- if (buff_done == DMA_D0)
+ if (buff_done & DMA_D0)
clear_dma_done0(ep->outdma);
- else
+ if (buff_done & DMA_D1)
clear_dma_done1(ep->outdma);
- }
- spin_unlock(&ep->lock);
-}
-
-
-// This ISR needs to handle dma done events for ALL endpoints!
-static void
-dma_done_intr (int irq, void *dev_id, struct pt_regs *regs)
-{
- struct usb_serial *serial = (struct usb_serial *) dev_id;
- int i;
-
- dma_done_ctrl(serial);
- for (i = 0; i < NUM_PORTS; i++)
- dma_done_port(&serial->port[i]);
-}
-
-
-
-/*****************************************************************************
- * Here begins the tty driver interface functions
- *****************************************************************************/
-
-static int serial_open(struct tty_struct *tty, struct file *filp)
-{
- int portNumber;
- struct usb_serial_port *port;
- struct usb_serial *serial = &usbserial;
- unsigned long flags;
-
- /* initialize the pointer incase something fails */
- tty->driver_data = NULL;
-
- MOD_INC_USE_COUNT;
-
- /* set up our port structure making the tty driver remember
- our port object, and us it */
- portNumber = MINOR(tty->device) - serial->minor;
- port = &serial->port[portNumber];
- tty->driver_data = port;
- port->tty = tty;
-
- if (port_paranoia_check(port, __FUNCTION__))
- return -ENODEV;
-
- dbg(__FUNCTION__ " - port %d", port->number);
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- ++port->open_count;
-
- if (!port->active) {
- port->active = 1;
-
- /*
- * force low_latency on so that our tty_push actually forces
- * the data through, otherwise it is scheduled, and with high
- * data rates (like with OHCI) data can get lost.
- */
- port->tty->low_latency = 1;
+ //process_ep_receive(dev, ep);
}
-
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- return 0;
-}
-
-
-static void serial_close(struct tty_struct *tty, struct file *filp)
-{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- unsigned long flags;
-
- if (!serial)
- return;
-
- dbg(__FUNCTION__ " - port %d", port->number);
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not opened");
- return;
}
- spin_lock_irqsave(&port->port_lock, flags);
-
- --port->open_count;
-
- if (port->open_count <= 0) {
- port->active = 0;
- port->open_count = 0;
+ spin_unlock(&ep->lock);
}
-
- spin_unlock_irqrestore(&port->port_lock, flags);
- MOD_DEC_USE_COUNT;
}
-static int serial_write(struct tty_struct *tty, int from_user,
- const unsigned char *buf, int count)
+/***************************************************************************
+ * Here begins the external interface functions
+ ***************************************************************************
+ */
+
+/*
+ * allocate a new packet
+ */
+int
+usbdev_alloc_packet(int ep_addr, int data_size, usbdev_pkt_t** pkt)
{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- endpoint_t *ep = &port->ep_bulkin;
+ endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr);
+ usbdev_pkt_t* lpkt = NULL;
- if (!serial)
+ if (!ep || !ep->active || ep->address < 2)
return -ENODEV;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not opened");
+ if (data_size > ep->max_pkt_size)
return -EINVAL;
- }
-
- if (count == 0) {
- dbg(__FUNCTION__ " - write request of 0 bytes");
- return (0);
- }
- count = (count > ep->max_pkt_size) ? ep->max_pkt_size : count;
- send_packet(ep, (u8 *) buf, count, from_user);
-
- return (count);
+ lpkt = *pkt = alloc_packet(ep, data_size, NULL);
+ if (!lpkt)
+ return -ENOMEM;
+ return 0;
}
-static int serial_write_room(struct tty_struct *tty)
+/*
+ * packet send
+ */
+int
+usbdev_send_packet(int ep_addr, usbdev_pkt_t * pkt)
{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- endpoint_t *ep = &port->ep_bulkin;
+ unsigned long flags;
+ int count;
+ endpoint_t * ep;
- if (!serial)
+ if (!pkt || !(ep = epaddr_to_ep(&usbdev, pkt->ep_addr)) ||
+ !ep->active || ep->address < 2)
return -ENODEV;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
+ if (ep->direction != USB_DIR_IN)
return -EINVAL;
- }
- return ep->max_pkt_size;
-}
+ spin_lock_irqsave(&ep->lock, flags);
+ count = send_packet(&usbdev, pkt, 1);
+ spin_unlock_irqrestore(&ep->lock, flags);
+ return count;
+}
-static int serial_chars_in_buffer(struct tty_struct *tty)
+/*
+ * packet receive
+ */
+int
+usbdev_receive_packet(int ep_addr, usbdev_pkt_t** pkt)
{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- endpoint_t *ep = &port->ep_bulkin;
- pkt_list_t *list = &ep->inlist;
- pkt_t *scan;
unsigned long flags;
- int chars = 0;
+ usbdev_pkt_t* lpkt = NULL;
+ endpoint_t *ep = epaddr_to_ep(&usbdev, ep_addr);
- if (!serial)
+ if (!ep || !ep->active || ep->address < 2)
return -ENODEV;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
+ if (ep->direction != USB_DIR_OUT)
return -EINVAL;
- }
spin_lock_irqsave(&ep->lock, flags);
- for (scan = list->head; scan; scan = scan->next)
- chars += scan->size;
+ if (ep->outlist.count > 1)
+ lpkt = unlink_head(&ep->outlist);
spin_unlock_irqrestore(&ep->lock, flags);
- return (chars);
-}
-
-
-static void serial_throttle(struct tty_struct *tty)
-{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
-
- if (!serial)
- return;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
- return;
+ if (!lpkt) {
+ /* no packet available */
+ *pkt = NULL;
+ return -ENODATA;
}
- // FIXME: anything to do?
-}
-
-
-static void serial_unthrottle(struct tty_struct *tty)
-{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- if (!serial)
- return;
+ *pkt = lpkt;
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
- return;
- }
- // FIXME: anything to do?
+ return lpkt->size;
}
-static int serial_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+/*
+ * return total queued byte count on the endpoint.
+ */
+int
+usbdev_get_byte_count(int ep_addr)
{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
+ unsigned long flags;
+ pkt_list_t *list;
+ usbdev_pkt_t *scan;
+ int count = 0;
+ endpoint_t * ep = epaddr_to_ep(&usbdev, ep_addr);
- if (!serial)
+ if (!ep || !ep->active || ep->address < 2)
return -ENODEV;
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
- return -ENODEV;
- }
- // FIXME: need any IOCTLs?
-
- return -ENOIOCTLCMD;
-}
-
-
-static void serial_set_termios(struct tty_struct *tty, struct termios *old)
-{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
-
- if (!serial)
- return;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
- return;
- }
- // FIXME: anything to do?
-}
-
+ if (ep->direction == USB_DIR_IN) {
+ list = &ep->inlist;
-static void serial_break(struct tty_struct *tty, int break_state)
-{
- struct usb_serial_port *port =
- (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
-
- if (!serial)
- return;
-
- if (!port->active) {
- dbg(__FUNCTION__ " - port not open");
- return;
- }
- // FIXME: anything to do?
-}
-
-
-static void port_send_complete(void *private)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) private;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- struct tty_struct *tty;
-
- dbg(__FUNCTION__ " - port %d", port->number);
-
- if (!serial) {
- return;
- }
+ spin_lock_irqsave(&ep->lock, flags);
+ for (scan = list->head; scan; scan = scan->next)
+ count += scan->size;
+ spin_unlock_irqrestore(&ep->lock, flags);
+ } else {
+ list = &ep->outlist;
- tty = port->tty;
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup) {
- dbg(__FUNCTION__ " - write wakeup call.");
- (tty->ldisc.write_wakeup) (tty);
+ spin_lock_irqsave(&ep->lock, flags);
+ if (list->count > 1) {
+ for (scan = list->head; scan != list->tail;
+ scan = scan->next)
+ count += scan->size;
}
-
- wake_up_interruptible(&tty->write_wait);
-}
-
-
-static void port_receive_complete(void *private)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) private;
- struct usb_serial *serial = get_usb_serial(port, __FUNCTION__);
- struct tty_struct *tty = port->tty;
- pkt_t *pkt;
- int i;
-
- dbg(__FUNCTION__ " - port %d", port->number);
-
- if (!serial) {
- return;
+ spin_unlock_irqrestore(&ep->lock, flags);
}
- if (!(pkt = receive_packet(&port->ep_bulkout)))
- return;
-
- for (i = 0; i < pkt->size; i++) {
- /* if we insert more than TTY_FLIPBUF_SIZE characters,
- we drop them. */
- if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
- tty_flip_buffer_push(tty);
- }
- /* this doesn't actually push the data through
- unless tty->low_latency is set */
- tty_insert_flip_char(tty, pkt->bufptr[i], 0);
- }
- tty_flip_buffer_push(tty);
-
- // we're done processing the packet, free it
- kfree(pkt);
+ return count;
}
-static struct tty_driver serial_tty_driver = {
- magic:TTY_DRIVER_MAGIC,
- driver_name:"usbdev-serial",
- name:"usb/ttsdev/%d",
- major:SERIAL_TTY_MAJOR,
- minor_start:0,
- num:1,
- type:TTY_DRIVER_TYPE_SERIAL,
- subtype:SERIAL_TYPE_NORMAL,
- flags:TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
- refcount:&serial_refcount,
- table:serial_tty,
- termios:serial_termios,
- termios_locked:serial_termios_locked,
-
- open:serial_open,
- close:serial_close,
- write:serial_write,
- write_room:serial_write_room,
- ioctl:serial_ioctl,
- set_termios:serial_set_termios,
- throttle:serial_throttle,
- unthrottle:serial_unthrottle,
- break_ctl:serial_break,
- chars_in_buffer:serial_chars_in_buffer,
-};
-
-
-void usbdev_serial_exit(void)
+void
+usbdev_exit(void)
{
endpoint_t *ep;
int i;
- outl_sync(0, USB_DEV_INT_ENABLE); // disable usb dev ints
- outl_sync(0, USB_DEV_ENABLE); // disable usb dev
+ au_writel(0, USBD_INTEN); // disable usb dev ints
+ au_writel(0, USBD_ENABLE); // disable usb dev
- // first free all control endpoint resources
- ep = &usbserial.ep_ctrl;
- free_irq(AU1000_USB_DEV_REQ_INT, &usbserial);
- free_irq(AU1000_USB_DEV_SUS_INT, &usbserial);
- free_irq(ep->inirq, &usbserial);
- //free_irq(ep->outirq, &usbserial);
+ free_irq(AU1000_USB_DEV_REQ_INT, &usbdev);
+ free_irq(AU1000_USB_DEV_SUS_INT, &usbdev);
+
+ // free all control endpoint resources
+ ep = &usbdev.ep[0];
free_au1000_dma(ep->indma);
free_au1000_dma(ep->outdma);
endpoint_flush(ep);
- // now free all port resources
- for (i = 0; i < NUM_PORTS; i++) {
- // free port's bulk IN endpoint resources
- ep = &usbserial.port[i].ep_bulkin;
- free_irq(ep->inirq, &usbserial);
- free_au1000_dma(ep->indma);
- endpoint_flush(ep);
+ // free ep resources
+ for (i = 2; i < 6; i++) {
+ ep = &usbdev.ep[i];
+ if (!ep->active) continue;
- // free port's bulk OUT endpoint resources
- ep = &usbserial.port[i].ep_bulkout;
- //free_irq(ep->outirq, &usbserial);
+ if (ep->direction == USB_DIR_IN) {
+ free_au1000_dma(ep->indma);
+ } else {
free_au1000_dma(ep->outdma);
+ }
endpoint_flush(ep);
-
- tty_unregister_devfs(&serial_tty_driver, i);
- info("usbdev serial converter now disconnected from ttyUSBdev%d",
- i);
}
- kfree(usbserial.str_desc[0]);
- tty_unregister_driver(&serial_tty_driver);
+ if (usbdev.full_conf_desc)
+ kfree(usbdev.full_conf_desc);
}
-int usbdev_serial_init(void)
-{
- struct usb_serial_port *port;
+int
+usbdev_init(struct usb_device_descriptor* dev_desc,
+ struct usb_config_descriptor* config_desc,
+ struct usb_interface_descriptor* if_desc,
+ struct usb_endpoint_descriptor* ep_desc,
+ struct usb_string_descriptor* str_desc[],
+ void (*cb)(usbdev_cb_type_t, unsigned long, void *),
+ void* cb_data)
+{
+ endpoint_t *ep0;
+ int i, ret=0;
+ u8* fcd;
+
+ if (dev_desc->bNumConfigurations > 1 ||
+ config_desc->bNumInterfaces > 1 ||
+ if_desc->bNumEndpoints > 4) {
+ err("Only one config, one i/f, and no more "
+ "than 4 ep's allowed");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!cb) {
+ err("Function-layer callback required");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (dev_desc->bMaxPacketSize0 != USBDEV_EP0_MAX_PACKET_SIZE) {
+ warn("EP0 Max Packet size must be %d",
+ USBDEV_EP0_MAX_PACKET_SIZE);
+ dev_desc->bMaxPacketSize0 = USBDEV_EP0_MAX_PACKET_SIZE;
+ }
+
+ memset(&usbdev, 0, sizeof(struct usb_dev));
+
+ usbdev.state = DEFAULT;
+ usbdev.dev_desc = dev_desc;
+ usbdev.if_desc = if_desc;
+ usbdev.conf_desc = config_desc;
+ for (i=0; i<6; i++)
+ usbdev.str_desc[i] = str_desc[i];
+ usbdev.func_cb = cb;
+ usbdev.cb_data = cb_data;
+
+ /* Initialize default control endpoint */
+ ep0 = &usbdev.ep[0];
+ ep0->active = 1;
+ ep0->type = CONTROL_EP;
+ ep0->max_pkt_size = USBDEV_EP0_MAX_PACKET_SIZE;
+ spin_lock_init(&ep0->lock);
+ ep0->desc = NULL; // ep0 has no descriptor
+ ep0->address = 0;
+ ep0->direction = 0;
+ ep0->reg = &ep_reg[0];
+
+ /* Initialize the other requested endpoints */
+ for (i = 0; i < if_desc->bNumEndpoints; i++) {
+ struct usb_endpoint_descriptor* epd = &ep_desc[i];
endpoint_t *ep;
- void *str_desc_buf;
- int str_desc_len;
- int i;
- /* register the tty driver */
- serial_tty_driver.init_termios = tty_std_termios;
- serial_tty_driver.init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-
- if (tty_register_driver(&serial_tty_driver)) {
- err(__FUNCTION__ ": failed to register tty driver");
- return -1;
+ if ((epd->bEndpointAddress & 0x80) == USB_DIR_IN) {
+ ep = &usbdev.ep[2];
+ ep->address = 2;
+ if (ep->active) {
+ ep = &usbdev.ep[3];
+ ep->address = 3;
+ if (ep->active) {
+ err("too many IN ep's requested");
+ ret = -ENODEV;
+ goto out;
}
-
- memset(&usbserial, 0, sizeof(struct usb_serial));
- usbserial.minor = 0;
-
- usbserial.state = DEFAULT;
-
- usbserial.dev_desc = &dev_desc;
- usbserial.if_desc = &if_desc;
- usbserial.conf_desc = &config_desc;
-
- /*
- * initialize the string descriptors
- */
-
- /* alloc buffer big enough for all string descriptors */
- str_desc_len = string_desc0.bLength;
- for (i = 0; i < 5; i++)
- str_desc_len += 2 + 2 * strlen(strings[i]);
- str_desc_buf = (void *) kmalloc(str_desc_len, ALLOC_FLAGS);
- if (!str_desc_buf) {
- err(__FUNCTION__ ": failed to alloc string descriptors");
- return -1;
- }
-
- usbserial.str_desc[0] = (struct usb_string_descriptor *)str_desc_buf;
- memcpy(usbserial.str_desc[0], &string_desc0, string_desc0.bLength);
- usbserial.str_desc[1] = (struct usb_string_descriptor *)
- (str_desc_buf + string_desc0.bLength);
- for (i = 1; i < 6; i++) {
- struct usb_string_descriptor *desc = usbserial.str_desc[i];
- char *str = strings[i - 1];
- int j, str_len = strlen(str);
-
- desc->bLength = 2 + 2 * str_len;
- desc->bDescriptorType = USB_DT_STRING;
- for (j = 0; j < str_len; j++) {
- desc->wData[j] = (u16) str[j];
- }
- if (i < 5)
- usbserial.str_desc[i + 1] =
- (struct usb_string_descriptor *)
- ((u8 *) desc + desc->bLength);
- }
-
- // request the USB device transfer complete interrupt
- if (request_irq(AU1000_USB_DEV_REQ_INT, req_sus_intr, SA_SHIRQ,
- "USBdev req", &usbserial)) {
- err("Can't get device request intr\n");
- goto err_out;
- }
- // request the USB device suspend interrupt
- if (request_irq(AU1000_USB_DEV_SUS_INT, req_sus_intr, SA_SHIRQ,
- "USBdev sus", &usbserial)) {
- err("Can't get device suspend intr\n");
- goto err_out;
- }
-
- // Initialize default control endpoint
- ep = &usbserial.ep_ctrl;
- spin_lock_init(&ep->lock);
- ep->desc = NULL; // ep0 has no ep descriptor
- ep->reg = &ep_reg[0];
- ep->max_pkt_size = usbserial.dev_desc->bMaxPacketSize0;
- ep->indma = ep->outdma = -1;
- if ((ep->indma = request_au1000_dma(ep_dma_id[0].id,
- ep_dma_id[0].str)) < 0) {
- err("Can't get %s DMA\n", ep_dma_id[0].str);
- goto err_out;
- }
- if ((ep->outdma = request_au1000_dma(ep_dma_id[1].id,
- ep_dma_id[1].str)) < 0) {
- err("Can't get %s DMA\n", ep_dma_id[1].str);
- goto err_out;
- }
- ep->inirq = get_dma_done_irq(ep->indma);
- ep->outirq = get_dma_done_irq(ep->outdma);
- // allocate EP0's DMA done interrupts.
- if (request_irq(ep->inirq, dma_done_intr, SA_INTERRUPT,
- "USBdev ep0 IN", &usbserial)) {
- err("Can't get ep0 IN dma done irq\n");
- goto err_out;
}
-#if 0
- if (request_irq(ep->outirq, dma_done_intr, SA_INTERRUPT,
- "USBdev ep0 OUT", &usbserial)) {
- err("Can't get ep0 OUT dma done irq\n");
- goto err_out;
+ } else {
+ ep = &usbdev.ep[4];
+ ep->address = 4;
+ if (ep->active) {
+ ep = &usbdev.ep[5];
+ ep->address = 5;
+ if (ep->active) {
+ err("too many OUT ep's requested");
+ ret = -ENODEV;
+ goto out;
+ }
}
-#endif
-
- /* initialize the devfs nodes for this device and let the user
- know what ports we are bound to */
- for (i = 0; i < NUM_PORTS; ++i) {
- tty_register_devfs(&serial_tty_driver, 0, i);
- info("usbdev serial attached to ttyUSBdev%d (or devfs usb/ttsdev/%d)",
- i, i);
- port = &usbserial.port[i];
- port->serial = &usbserial;
- port->number = i;
- port->send_complete_tq.routine = port_send_complete;
- port->send_complete_tq.data = port;
- port->receive_complete_tq.routine = port_receive_complete;
- port->receive_complete_tq.data = port;
- spin_lock_init(&port->port_lock);
-
- // Initialize the port's bulk IN endpoint
- ep = &port->ep_bulkin;
- spin_lock_init(&ep->lock);
- ep->desc = &ep_desc[NUM_PORTS * i];
- ep->reg = &ep_reg[1 + NUM_PORTS * i];
- ep->max_pkt_size = ep->desc->wMaxPacketSize;
- ep->indma = ep->outdma = -1;
- if ((ep->indma =
- request_au1000_dma(ep_dma_id[2+NUM_PORTS*i].id,
- ep_dma_id[2 + NUM_PORTS * i].str)) < 0) {
- err("Can't get %s DMA\n",
- ep_dma_id[2 + NUM_PORTS * i].str);
- goto err_out;
- }
- ep->inirq = get_dma_done_irq(ep->indma);
- if (request_irq(ep->inirq, dma_done_intr, SA_INTERRUPT,
- "USBdev bulk IN", &usbserial)) {
- err("Can't get port %d bulk IN dma done irq\n", i);
- goto err_out;
}
- // Initialize the port's bulk OUT endpoint
- ep = &port->ep_bulkout;
+
+ ep->active = 1;
+ epd->bEndpointAddress &= ~0x0f;
+ epd->bEndpointAddress |= (u8)ep->address;
+ ep->direction = epd->bEndpointAddress & 0x80;
+ ep->type = epd->bmAttributes & 0x03;
+ ep->max_pkt_size = epd->wMaxPacketSize;
spin_lock_init(&ep->lock);
- ep->desc = &ep_desc[NUM_PORTS * i + 1];
- ep->reg = &ep_reg[1 + NUM_PORTS * i + 1];
- ep->max_pkt_size = ep->desc->wMaxPacketSize;
- ep->indma = ep->outdma = -1;
- if ((ep->outdma =
- request_au1000_dma(ep_dma_id[2+NUM_PORTS*i + 1].id,
- ep_dma_id[2+NUM_PORTS*i + 1].str)) < 0) {
- err("Can't get %s DMA\n",
- ep_dma_id[2 + NUM_PORTS * i + 1].str);
- goto err_out;
+ ep->desc = epd;
+ ep->reg = &ep_reg[ep->address];
}
- ep->outirq = get_dma_done_irq(ep->outdma);
-#if 0
- if (request_irq(ep->outirq, dma_done_intr, SA_INTERRUPT,
- "USBdev bulk OUT", &usbserial)) {
- err("Can't get port %d bulk OUT dma done irq\n", i);
- goto err_out;
- }
-#endif
+
+ /*
+ * initialize the full config descriptor
+ */
+ usbdev.full_conf_desc = fcd = kmalloc(config_desc->wTotalLength,
+ ALLOC_FLAGS);
+ if (!fcd) {
+ err("failed to alloc full config descriptor");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(fcd, config_desc, USB_DT_CONFIG_SIZE);
+ fcd += USB_DT_CONFIG_SIZE;
+ memcpy(fcd, if_desc, USB_DT_INTERFACE_SIZE);
+ fcd += USB_DT_INTERFACE_SIZE;
+ for (i = 0; i < if_desc->bNumEndpoints; i++) {
+ memcpy(fcd, &ep_desc[i], USB_DT_ENDPOINT_SIZE);
+ fcd += USB_DT_ENDPOINT_SIZE;
}
- // enable device controller
- outl_sync(0x0002, USB_DEV_ENABLE);
+ /* Now we're ready to enable the controller */
+ au_writel(0x0002, USBD_ENABLE);
udelay(100);
- outl_sync(0x0003, USB_DEV_ENABLE);
+ au_writel(0x0003, USBD_ENABLE);
udelay(100);
- for (i = 0; i < sizeof(au1000_config_table) / sizeof(u32); ++i)
- outl_sync(au1000_config_table[i], USB_DEV_CONFIG);
-
- // Flush the endpoint buffers and FIFOs
- ep = &usbserial.ep_ctrl;
- endpoint_flush(ep);
- // start packet reception on control ep
- kickstart_receive_packet(ep);
- for (i = 0; i < NUM_PORTS; ++i) {
- struct usb_serial_port *port = &usbserial.port[i];
- endpoint_flush(&port->ep_bulkin);
- endpoint_flush(&port->ep_bulkout);
- // start packet reception on bulk OUT endpoint
- kickstart_receive_packet(&port->ep_bulkout);
+ /* build and send config table based on ep descriptors */
+ for (i = 0; i < 6; i++) {
+ endpoint_t *ep;
+ if (i == 1)
+ continue; // skip dummy ep
+ ep = &usbdev.ep[i];
+ if (ep->active) {
+ au_writel((ep->address << 4) | 0x04, USBD_CONFIG);
+ au_writel(((ep->max_pkt_size & 0x380) >> 7) |
+ (ep->direction >> 4) | (ep->type << 4),
+ USBD_CONFIG);
+ au_writel((ep->max_pkt_size & 0x7f) << 1, USBD_CONFIG);
+ au_writel(0x00, USBD_CONFIG);
+ au_writel(ep->address, USBD_CONFIG);
+ } else {
+ u8 dir = (i==2 || i==3) ? DIR_IN : DIR_OUT;
+ au_writel((i << 4) | 0x04, USBD_CONFIG);
+ au_writel(((16 & 0x380) >> 7) | dir |
+ (BULK_EP << 4), USBD_CONFIG);
+ au_writel((16 & 0x7f) << 1, USBD_CONFIG);
+ au_writel(0x00, USBD_CONFIG);
+ au_writel(i, USBD_CONFIG);
+ }
}
/*
* Enable Receive FIFO Complete interrupts only. Transmit
* complete is being handled by the DMA done interrupts.
*/
- outl_sync(0x31, USB_DEV_INT_ENABLE);
+ au_writel(0x31, USBD_INTEN);
- return 0;
+ /*
+ * Controller is now enabled, request DMA and IRQ
+ * resources.
+ */
- err_out:
- usbdev_serial_exit();
- return -1;
-}
+ /* request the USB device transfer complete interrupt */
+ if (request_irq(AU1000_USB_DEV_REQ_INT, req_sus_intr, SA_INTERRUPT,
+ "USBdev req", &usbdev)) {
+ err("Can't get device request intr");
+ ret = -ENXIO;
+ goto out;
+ }
+ /* request the USB device suspend interrupt */
+ if (request_irq(AU1000_USB_DEV_SUS_INT, req_sus_intr, SA_INTERRUPT,
+ "USBdev sus", &usbdev)) {
+ err("Can't get device suspend intr");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* Request EP0 DMA and IRQ */
+ if ((ep0->indma = request_au1000_dma(ep_dma_id[0].id,
+ ep_dma_id[0].str,
+ dma_done_ep0_intr,
+ SA_INTERRUPT,
+ &usbdev)) < 0) {
+ err("Can't get %s DMA", ep_dma_id[0].str);
+ ret = -ENXIO;
+ goto out;
+ }
+ if ((ep0->outdma = request_au1000_dma(ep_dma_id[1].id,
+ ep_dma_id[1].str,
+ NULL, 0, NULL)) < 0) {
+ err("Can't get %s DMA", ep_dma_id[1].str);
+ ret = -ENXIO;
+ goto out;
+ }
+
+ // Flush the ep0 buffers and FIFOs
+ endpoint_flush(ep0);
+ // start packet reception on ep0
+ kickstart_receive_packet(ep0);
+
+ /* Request DMA and IRQ for the other endpoints */
+ for (i = 2; i < 6; i++) {
+ endpoint_t *ep = &usbdev.ep[i];
+ if (!ep->active)
+ continue;
+
+ // Flush the endpoint buffers and FIFOs
+ endpoint_flush(ep);
+ if (ep->direction == USB_DIR_IN) {
+ ep->indma =
+ request_au1000_dma(ep_dma_id[ep->address].id,
+ ep_dma_id[ep->address].str,
+ dma_done_ep_intr,
+ SA_INTERRUPT,
+ &usbdev);
+ if (ep->indma < 0) {
+ err("Can't get %s DMA",
+ ep_dma_id[ep->address].str);
+ ret = -ENXIO;
+ goto out;
+ }
+ } else {
+ ep->outdma =
+ request_au1000_dma(ep_dma_id[ep->address].id,
+ ep_dma_id[ep->address].str,
+ NULL, 0, NULL);
+ if (ep->outdma < 0) {
+ err("Can't get %s DMA",
+ ep_dma_id[ep->address].str);
+ ret = -ENXIO;
+ goto out;
+ }
+
+ // start packet reception on OUT endpoint
+ kickstart_receive_packet(ep);
+ }
+ }
+
+ out:
+ if (ret)
+ usbdev_exit();
+ return ret;
+}
-module_init(usbdev_serial_init);
-module_exit(usbdev_serial_exit);
+EXPORT_SYMBOL(usbdev_init);
+EXPORT_SYMBOL(usbdev_exit);
+EXPORT_SYMBOL(usbdev_alloc_packet);
+EXPORT_SYMBOL(usbdev_receive_packet);
+EXPORT_SYMBOL(usbdev_send_packet);
+EXPORT_SYMBOL(usbdev_get_byte_count);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)