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

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)