patch-2.4.4 linux/arch/cris/drivers/usb-host.c

Next file: linux/arch/cris/drivers/usb-host.h
Previous file: linux/arch/cris/drivers/sync_serial.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/arch/cris/drivers/usb-host.c linux/arch/cris/drivers/usb-host.c
@@ -0,0 +1,2501 @@
+/*
+ * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
+ *
+ * Copyright (c) 2001 Axis Communications AB.
+ *
+ * $Id: usb-host.c,v 1.8 2001/02/27 13:52:48 bjornw Exp $
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/list.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/svinto.h>
+
+#include <linux/usb.h>
+#include "usb-host.h"
+
+#define ETRAX_USB_HC_IRQ 31
+#define ETRAX_USB_RX_IRQ 25
+#define ETRAX_USB_TX_IRQ 24
+
+static const char *usb_hcd_version = "$Revision: 1.8 $";
+
+#undef KERN_DEBUG
+#define KERN_DEBUG ""
+
+#undef USB_DEBUG_RH
+#undef USB_DEBUG_EP
+#undef USB_DEBUG_DESC
+#undef USB_DEBUG_TRACE
+#undef USB_DEBUG_CTRL
+#undef USB_DEBUG_BULK
+#undef USB_DEBUG_INTR
+
+#ifdef USB_DEBUG_RH
+#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
+#else
+#define dbg_rh(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_EP
+#define dbg_ep(format, arg...) printk(KERN_DEBUG __FILE__ ": (EP) " format "\n" , ## arg)
+#else
+#define dbg_ep(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_CTRL
+#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
+#else
+#define dbg_ctrl(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_BULK
+#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
+#else
+#define dbg_bulk(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_INTR
+#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
+#else
+#define dbg_intr(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_TRACE
+#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering : " __FUNCTION__ "\n"))
+#define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting  : " __FUNCTION__ "\n"))
+#else
+#define DBFENTER (NULL)
+#define DBFEXIT  (NULL)
+#endif
+
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+static __u8 root_hub_dev_des[] =
+{
+	0x12,  /*  __u8  bLength; */
+	0x01,  /*  __u8  bDescriptorType; Device */
+	0x00,  /*  __u16 bcdUSB; v1.0 */
+	0x01,
+	0x09,  /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,  /*  __u8  bDeviceSubClass; */
+	0x00,  /*  __u8  bDeviceProtocol; */
+	0x08,  /*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,  /*  __u16 idVendor; */
+	0x00,
+	0x00,  /*  __u16 idProduct; */
+	0x00,
+	0x00,  /*  __u16 bcdDevice; */
+	0x00,
+	0x00,  /*  __u8  iManufacturer; */
+	0x02,  /*  __u8  iProduct; */
+	0x01,  /*  __u8  iSerialNumber; */
+	0x01   /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+	0x09,  /*  __u8  bLength; */
+	0x02,  /*  __u8  bDescriptorType; Configuration */
+	0x19,  /*  __u16 wTotalLength; */
+	0x00,
+	0x01,  /*  __u8  bNumInterfaces; */
+	0x01,  /*  __u8  bConfigurationValue; */
+	0x00,  /*  __u8  iConfiguration; */
+	0x40,  /*  __u8  bmAttributes; Bit 7: Bus-powered */
+	0x00,  /*  __u8  MaxPower; */
+
+     /* interface */
+	0x09,  /*  __u8  if_bLength; */
+	0x04,  /*  __u8  if_bDescriptorType; Interface */
+	0x00,  /*  __u8  if_bInterfaceNumber; */
+	0x00,  /*  __u8  if_bAlternateSetting; */
+	0x01,  /*  __u8  if_bNumEndpoints; */
+	0x09,  /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,  /*  __u8  if_bInterfaceSubClass; */
+	0x00,  /*  __u8  if_bInterfaceProtocol; */
+	0x00,  /*  __u8  if_iInterface; */
+
+     /* endpoint */
+	0x07,  /*  __u8  ep_bLength; */
+	0x05,  /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,  /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,  /*  __u8  ep_bmAttributes; Interrupt */
+	0x08,  /*  __u16 ep_wMaxPacketSize; 8 Bytes */
+	0x00,
+	0xff   /*  __u8  ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+	0x09,  /*  __u8  bLength; */
+	0x29,  /*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,  /*  __u8  bNbrPorts; */
+	0x00,  /* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,  /*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,  /*  __u8  bHubContrCurrent; 0 mA */
+	0x00,  /*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff   /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+
+#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
+#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
+{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
+
+static submit_urb_count = 0;
+
+//#define ETRAX_USB_INTR_IRQ
+//#define ETRAX_USB_INTR_ERROR_FATAL
+
+#define RX_BUF_SIZE        32768
+#define RX_DESC_BUF_SIZE   64
+#define NBR_OF_RX_DESC     (RX_BUF_SIZE / RX_DESC_BUF_SIZE)
+
+#define NBR_OF_EP_DESC     32
+
+#define MAX_INTR_INTERVAL 128
+
+static __u32 ep_usage_bitmask;
+static __u32 ep_really_active;
+
+static unsigned char RxBuf[RX_BUF_SIZE];
+static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+
+static volatile USB_IN_Desc_t *myNextRxDesc;
+static volatile USB_IN_Desc_t *myLastRxDesc;
+static volatile USB_IN_Desc_t *myPrevRxDesc;
+
+static USB_EP_Desc_t TxCtrlEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
+static USB_EP_Desc_t TxBulkEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
+
+static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+
+static urb_t *URB_List[NBR_OF_EP_DESC];
+static kmem_cache_t *usb_desc_cache;
+static struct usb_bus *etrax_usb_bus;
+
+static void dump_urb (purb_t purb);
+static void init_rx_buffers(void);
+static int etrax_rh_unlink_urb (urb_t *urb);
+static void etrax_rh_send_irq(urb_t *urb);
+static void etrax_rh_init_int_timer(urb_t *urb);
+static void etrax_rh_int_timer_do(unsigned long ptr);
+
+static void etrax_usb_setup_epid(char epid, char devnum, char endpoint,
+				 char packsize, char slow);
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp);
+static int etrax_usb_allocate_epid(void);
+static void etrax_usb_free_epid(char epid);
+static void cleanup_sb(USB_SB_Desc_t *sb);
+
+static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen);
+static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen);
+
+static int etrax_usb_submit_ctrl_urb(urb_t *urb);
+
+static int etrax_usb_submit_urb(urb_t *urb);
+static int etrax_usb_unlink_urb(urb_t *urb);
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
+
+static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs);
+
+static int etrax_rh_submit_urb (urb_t *urb);
+
+static int etrax_usb_hc_init(void);
+static void etrax_usb_hc_cleanup(void);
+
+static struct usb_operations etrax_usb_device_operations =
+{
+	etrax_usb_allocate_dev,
+	etrax_usb_deallocate_dev,
+	etrax_usb_get_frame_number,
+	etrax_usb_submit_urb,
+	etrax_usb_unlink_urb
+};
+
+#ifdef USB_DEBUG_DESC
+static void dump_urb(purb_t purb)
+{
+	printk("\nurb                   :0x%08X\n", purb);
+	printk("next                  :0x%08X\n", purb->next);
+	printk("dev                   :0x%08X\n", purb->dev);
+	printk("pipe                  :0x%08X\n", purb->pipe);
+	printk("status                :%d\n", purb->status);
+	printk("transfer_flags        :0x%08X\n", purb->transfer_flags);
+	printk("transfer_buffer       :0x%08X\n", purb->transfer_buffer);
+	printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+	printk("actual_length         :%d\n", purb->actual_length);
+	printk("setup_packet          :0x%08X\n", purb->setup_packet);
+	printk("start_frame           :%d\n", purb->start_frame);
+	printk("number_of_packets     :%d\n", purb->number_of_packets);
+	printk("interval              :%d\n", purb->interval);
+	printk("error_count           :%d\n", purb->error_count);
+	printk("context               :0x%08X\n", purb->context);
+	printk("complete              :0x%08X\n\n", purb->complete);
+}
+
+static void dump_in_desc(USB_IN_Desc_t *in)
+{
+	printk("\nUSB_IN_Desc at 0x%08X\n", in);
+	printk("  sw_len  : 0x%04X (%d)\n", in->sw_len, in->sw_len);
+	printk("  command : 0x%04X\n", in->command);
+	printk("  next    : 0x%08X\n", in->next);
+	printk("  buf     : 0x%08X\n", in->buf);
+	printk("  hw_len  : 0x%04X (%d)\n", in->hw_len, in->hw_len);
+	printk("  status  : 0x%04X\n\n", in->status);
+}
+
+static void dump_sb_desc(USB_SB_Desc_t *sb)
+{
+	printk("\nUSB_SB_Desc at 0x%08X\n", sb);
+	printk("  sw_len  : 0x%04X (%d)\n", sb->sw_len, sb->sw_len);
+	printk("  command : 0x%04X\n", sb->command);
+	printk("  next    : 0x%08X\n", sb->next);
+	printk("  buf     : 0x%08X\n\n", sb->buf);
+}
+
+
+static void dump_ep_desc(USB_EP_Desc_t *ep)
+{
+	printk("\nUSB_EP_Desc at 0x%08X\n", ep);
+	printk("  hw_len  : 0x%04X (%d)\n", ep->hw_len, ep->hw_len);
+	printk("  command : 0x%08X\n", ep->command);
+	printk("  sub     : 0x%08X\n", ep->sub);
+	printk("  nep     : 0x%08X\n\n", ep->nep);
+}
+
+
+#else
+#define dump_urb(...)     (NULL)
+#define dump_ep_desc(...) (NULL)
+#define dump_sb_desc(...) (NULL)
+#define dump_in_desc(...) (NULL)
+#endif
+
+static void init_rx_buffers(void)
+{
+	int i;
+	
+	DBFENTER;
+	
+	for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+		RxDescList[i].command = 0;
+		RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
+		RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+		RxDescList[i].hw_len = 0;
+		RxDescList[i].status = 0;
+	}
+	
+	RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+	RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
+	RxDescList[i].next = virt_to_phys(&RxDescList[0]);
+	RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+	RxDescList[i].hw_len = 0;
+	RxDescList[i].status = 0;
+
+	myNextRxDesc = &RxDescList[0];
+	myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+	myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+
+	*R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
+	*R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
+	
+	DBFEXIT;
+}
+
+static void init_tx_ctrl_ep(void)
+{
+	int i;
+	
+	DBFENTER;
+	
+	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
+		TxCtrlEPList[i].hw_len = 0;
+		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxCtrlEPList[i].sub = 0;
+		TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[i + 1]);
+	}
+	
+	TxCtrlEPList[i].hw_len = 0;
+	TxCtrlEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
+		IO_FIELD(USB_EP_command, epid, i);
+
+	TxCtrlEPList[i].sub = 0;
+	TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[0]);
+	
+	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+	
+	DBFEXIT;
+}
+
+static void init_tx_bulk_ep(void)
+{
+	int i;
+	
+	DBFENTER;
+	
+	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
+		TxBulkEPList[i].hw_len = 0;
+		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxBulkEPList[i].sub = 0;
+		TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[i + 1]);
+	}
+	
+	TxBulkEPList[i].hw_len = 0;
+	TxBulkEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
+		IO_FIELD(USB_EP_command, epid, i);
+
+	TxBulkEPList[i].sub = 0;
+	TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[0]);
+	
+	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[0]);
+	*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+	
+	DBFEXIT;
+}
+
+static void init_tx_intr_ep(void)
+{
+	int i;
+
+	DBFENTER;
+
+	TxIntrSB_zout.sw_len = 0;
+	TxIntrSB_zout.next = 0;
+	TxIntrSB_zout.buf = 0;
+	TxIntrSB_zout.command = IO_FIELD(USB_SB_command, rem, 0) |
+		IO_STATE(USB_SB_command, tt, zout) |
+		IO_STATE(USB_SB_command, full, yes) |
+		IO_STATE(USB_SB_command, eot, yes) |
+		IO_STATE(USB_SB_command, eol, yes);
+
+	for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+		TxIntrEPList[i].hw_len = 0;
+		TxIntrEPList[i].command = IO_STATE(USB_EP_command, eof, yes) |
+			IO_STATE(USB_EP_command, enable, yes) |
+			IO_FIELD(USB_EP_command, epid, 0);
+		TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+		TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[i + 1]);
+	}
+	
+	TxIntrEPList[i].hw_len = 0;
+	TxIntrEPList[i].command =
+		IO_STATE(USB_EP_command, eof, yes) |
+		IO_STATE(USB_EP_command, enable, yes) |
+		IO_FIELD(USB_EP_command, epid, 0);
+	TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+	TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[0]);
+
+	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
+	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+	
+	DBFEXIT;
+}
+
+
+static int etrax_usb_unlink_intr_urb(urb_t *urb)
+{
+	struct usb_device *usb_dev = urb->dev;
+	etrax_hc_t *hc = usb_dev->bus->hcpriv;
+
+	USB_EP_Desc_t *tmp_ep;
+	USB_EP_Desc_t *first_ep;
+	
+	USB_EP_Desc_t *ep_desc;
+	USB_SB_Desc_t *sb_desc;
+	
+	char epid;
+	char devnum;
+	char endpoint;
+	char slow;
+	int maxlen;
+	int i;
+	
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	
+	DBFENTER;
+
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe,
+			       usb_pipeout(urb->pipe));
+
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	if (epid == -1) {
+		err("Trying to unlink urb that is not in traffic queue!!");
+		return -1;  /* fix this */
+	}
+
+	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, stop);
+	/* Somehow wait for the DMA to finish current activities */
+	i = jiffies + 100;
+	while (jiffies < i);	
+	
+	first_ep = &TxIntrEPList[0];
+	tmp_ep = first_ep;
+	
+	do {
+		if (IO_EXTRACT(USB_EP_command, epid, ((USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep))->command)
+		    == epid) {
+			/* Unlink it !!! */
+			dbg_intr("Found urb to unlink for epid %d", epid);
+			
+			ep_desc = phys_to_virt(tmp_ep->nep);
+			tmp_ep->nep = ep_desc->nep;
+			kmem_cache_free(usb_desc_cache, phys_to_virt(ep_desc->sub));
+			kmem_cache_free(usb_desc_cache, ep_desc);
+		}
+
+		tmp_ep = phys_to_virt(tmp_ep->nep);
+		
+	} while (tmp_ep != first_ep);
+
+	/* We should really try to move the EP register to an EP that is not removed
+	   instead of restarting, but this will work too */
+	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
+	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+
+	clear_bit(epid, (void *)&ep_really_active);
+	URB_List[epid] = NULL;
+	etrax_usb_free_epid(epid);
+	
+	DBFEXIT;
+
+	return 0;
+}
+
+void etrax_usb_do_intr_recover(int epid)
+{
+	USB_EP_Desc_t *first_ep, *tmp_ep;
+	
+	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+	tmp_ep = first_ep;
+
+	do {
+		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+		}
+		
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
+		
+	} while (tmp_ep != first_ep);
+}
+
+static int etrax_usb_submit_intr_urb(urb_t *urb)
+{
+	USB_EP_Desc_t *tmp_ep;
+	USB_EP_Desc_t *first_ep;
+	
+	USB_SB_Desc_t *sb_desc;
+	
+	char epid;
+	char devnum;
+	char endpoint;
+	char maxlen;
+	char slow;
+	int interval;
+	int i;
+	
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	
+	DBFENTER;
+
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe,
+			       usb_pipeout(urb->pipe));
+
+	slow = usb_pipeslow(urb->pipe);
+	interval = urb->interval;
+
+	dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d",
+		 devnum, endpoint, maxlen, slow);
+	
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	if (epid == -1) {
+		epid = etrax_usb_allocate_epid();
+		if (epid == -1) {
+			/* We're out of endpoints, return some error */
+			err("We're out of endpoints");
+			return -ENOMEM;
+		}
+		/* Now we have to fill in this ep */
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+	}
+	/* Ok, now we got valid endpoint, lets insert some traffic */
+
+	urb_priv = (etrax_urb_priv_t *)kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
+	urb_priv->first_sb = 0;
+	urb_priv->rx_offset = 0;
+	urb_priv->eot = 0;
+	INIT_LIST_HEAD(&urb_priv->ep_in_list);
+	urb->hcpriv = urb_priv;
+
+	/* This is safe since there cannot be any other URB's for this epid */
+	URB_List[epid] = urb;
+#if 0
+	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+#else
+	first_ep = &TxIntrEPList[0];
+#endif
+
+	/* Round of the interval to 2^n, it is obvious that this code favours
+	   smaller numbers, but that is actually a good thing */
+	for (i = 0; interval; i++) {
+		interval = interval >> 1;
+	}
+
+	urb->interval = interval = 1 << (i - 1);
+
+	dbg_intr("Interval rounded to %d", interval);
+
+	tmp_ep = first_ep;
+	i = 0;
+	do {
+		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+			if ((i % interval) == 0) {
+				/* Insert the traffic ep after tmp_ep */
+				USB_EP_Desc_t *traffic_ep;
+				USB_SB_Desc_t *traffic_sb;
+
+				traffic_ep = (USB_EP_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+				traffic_sb = (USB_SB_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+
+				traffic_ep->hw_len = 0;
+				traffic_ep->command = IO_FIELD(USB_EP_command, epid, epid) |
+					IO_STATE(USB_EP_command, enable, yes);
+				traffic_ep->sub = virt_to_phys(traffic_sb);
+
+				if (usb_pipein(urb->pipe)) {
+					traffic_sb->sw_len = urb->transfer_buffer_length ?
+						(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+					traffic_sb->next = 0;
+					traffic_sb->buf = 0;
+					traffic_sb->command = IO_FIELD(USB_SB_command, rem,
+								       urb->transfer_buffer_length % maxlen) |
+						IO_STATE(USB_SB_command, tt, in) |
+						IO_STATE(USB_SB_command, eot, yes) |
+						IO_STATE(USB_SB_command, eol, yes);
+					
+				} else if (usb_pipeout(urb->pipe)) {
+					traffic_sb->sw_len = urb->transfer_buffer_length;
+					traffic_sb->next = 0;
+					traffic_sb->buf = virt_to_phys(urb->transfer_buffer);
+					traffic_sb->command = IO_FIELD(USB_SB_command, rem, 0) |
+						IO_STATE(USB_SB_command, tt, out) |
+						IO_STATE(USB_SB_command, eot, yes) |
+						IO_STATE(USB_SB_command, eol, yes) |
+						IO_STATE(USB_SB_command, full, yes);
+				}
+
+				traffic_ep->nep = tmp_ep->nep;
+				tmp_ep->nep = virt_to_phys(traffic_ep);
+				dbg_intr("One ep successfully inserted");
+			}
+			i++;
+		}
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
+	} while (tmp_ep != first_ep);
+
+	set_bit(epid, (void *)&ep_really_active);
+	
+	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+
+	DBFEXIT;
+	
+	return 0;
+}
+
+
+static int handle_intr_transfer_attn(char epid, int status)
+{
+	urb_t *old_urb;
+
+	DBFENTER;
+
+	old_urb = URB_List[epid];
+	
+	/* if (status == 0 && IN) find data and copy to urb */
+	if (status == 0 && usb_pipein(old_urb->pipe)) {
+		unsigned long flags;
+		etrax_urb_priv_t *urb_priv;
+		struct list_head *entry;
+		struct in_chunk *in;
+
+		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+		
+		save_flags(flags);
+		cli();
+
+		list_for_each(entry, &urb_priv->ep_in_list) {
+			in = list_entry(entry, struct in_chunk, list);
+			memcpy(old_urb->transfer_buffer, in->data, in->length);
+			old_urb->actual_length = in->length;
+			old_urb->status = status;
+			
+			if (old_urb->complete) {
+				old_urb->complete(old_urb);
+			}
+			
+			list_del(entry);
+			kfree(in->data);
+			kfree(in);
+		}		
+		
+		restore_flags(flags);
+
+	} else if (status != 0) {
+		warn("Some sort of error for INTR EP !!!!");
+#ifdef ETRAX_USB_INTR_ERROR_FATAL
+		/* This means that an INTR error is fatal for that endpoint */
+		etrax_usb_unlink_intr_urb(old_urb);
+		old_urb->status = status;
+		if (old_urb->complete) {
+			old_urb->complete(old_urb);
+		}
+#else
+		/* In this case we reenable the disabled endpoint(s) */
+		etrax_usb_do_intr_recover(epid);
+#endif	
+	}
+	
+	DBFEXIT;
+}
+
+static int etrax_rh_unlink_urb (urb_t *urb)
+{
+	etrax_hc_t *hc;
+	
+	DBFENTER;
+	
+	hc = urb->dev->bus->hcpriv;
+	
+	if (hc->rh.urb == urb) {
+		hc->rh.send = 0;
+		del_timer(&hc->rh.rh_int_timer);
+	}
+	
+	DBFEXIT;
+	return 0;
+}
+
+static void etrax_rh_send_irq(urb_t *urb)
+{
+	__u16 data = 0;
+	etrax_hc_t *hc = urb->dev->bus->hcpriv;
+//	static prev_wPortStatus_1 = 0;
+//	static prev_wPortStatus_2 = 0;
+	
+/*	DBFENTER; */
+	
+	
+/*
+  dbg_rh("R_USB_FM_NUMBER   : 0x%08X", *R_USB_FM_NUMBER);
+  dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
+*/
+	
+	data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
+	data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
+
+	*((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+	urb->actual_length = 1;
+	urb->status = 0;
+
+	
+	if (data && hc->rh.send && urb->complete) {
+		dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1); 
+		dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
+
+		urb->complete(urb);
+	}
+  
+/*	DBFEXIT; */
+}
+
+static void etrax_rh_init_int_timer(urb_t *urb)
+{
+	etrax_hc_t *hc;
+	
+/*	DBFENTER; */
+	
+	hc = urb->dev->bus->hcpriv;
+	hc->rh.interval = urb->interval;
+	init_timer(&hc->rh.rh_int_timer);
+	hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
+	hc->rh.rh_int_timer.data = (unsigned long)urb;
+	hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
+	add_timer(&hc->rh.rh_int_timer);
+	
+/*	DBFEXIT; */
+}
+
+static void etrax_rh_int_timer_do(unsigned long ptr)
+{
+	urb_t *urb;
+	etrax_hc_t *hc;
+	
+/*	DBFENTER; */
+	
+	urb = (urb_t*)ptr;
+	hc = urb->dev->bus->hcpriv;
+	
+	if (hc->rh.send) {
+		etrax_rh_send_irq(urb);
+	}
+	
+	etrax_rh_init_int_timer(urb);
+	
+/*	DBFEXIT; */
+}
+
+static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, char packsize, char slow)
+{
+	unsigned long flags;
+	
+	DBFENTER;
+
+	save_flags(flags);
+	cli();
+	
+	if (test_bit(epid, (void *)&ep_usage_bitmask)) {
+		warn("Trying to setup used epid %d", epid);
+		DBFEXIT;
+		return;
+	}
+	
+	set_bit(epid, (void *)&ep_usage_bitmask);
+	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d",
+	       epid, devnum, endpoint, packsize);
+	
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+	nop();
+	*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+		IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+		IO_FIELD(R_USB_EPT_DATA, dev, devnum) |
+		IO_FIELD(R_USB_EPT_DATA, max_len, packsize) |
+		IO_FIELD(R_USB_EPT_DATA, low_speed, slow);
+
+	restore_flags(flags);
+
+	DBFEXIT;
+}
+
+static void etrax_usb_free_epid(char epid)
+{
+	unsigned long flags;
+	
+	DBFENTER;
+
+	if (!test_bit(epid, (void *)&ep_usage_bitmask)) {
+		warn("Trying to free unused epid %d", epid);
+		DBFEXIT;
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+	nop();
+	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold))printk("+");
+	*R_USB_EPT_DATA = 0;
+	clear_bit(epid, (void *)&ep_usage_bitmask);
+	restore_flags(flags);
+	dbg_ep("epid: %d freed", epid);
+	
+	DBFEXIT;
+}
+
+
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp)
+{
+	int i;
+	unsigned long flags;
+	__u32 data;
+	
+	DBFENTER;
+
+	save_flags(flags);
+	
+	/* Skip first ep_id since it is reserved when intr. or iso traffic is used */
+	for (i = 0; i < NBR_OF_EP_DESC; i++) {
+		if (test_bit(i, (void *)&ep_usage_bitmask)) {
+			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
+			nop();
+			data = *R_USB_EPT_DATA;
+			if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+			    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+			    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+			    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+			    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) {
+				dbg_ep("Found ep_id %d for devnum %d, endpoint %d",
+				       i, devnum, endpoint);
+				DBFEXIT;
+				return i;
+			}
+		}
+	}
+
+	restore_flags(flags);
+	
+	dbg_ep("Found no ep_id for devnum %d, endpoint %d",
+	       devnum, endpoint);
+	DBFEXIT;
+	return -1;
+}
+
+static int etrax_usb_allocate_epid(void)
+{
+	int i;
+	
+	DBFENTER;
+
+	for (i = 0; i < NBR_OF_EP_DESC; i++) {
+		if (!test_bit(i, (void *)&ep_usage_bitmask)) {
+			dbg_ep("Found free ep_id at %d", i);
+			DBFEXIT;
+			return i;
+		}
+	}
+
+	dbg_ep("Found no free ep_id's");
+	DBFEXIT;
+	return -1;
+}
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb)
+{
+	char epid;
+	char devnum;
+	char endpoint;
+	char maxlen;
+	char slow;
+
+	urb_t *tmp_urb;
+	
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	
+	DBFENTER;
+
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe,
+			       usb_pipeout(urb->pipe));
+	slow = usb_pipeslow(urb->pipe);
+	
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	if (epid == -1) {
+		epid = etrax_usb_allocate_epid();
+		if (epid == -1) {
+			/* We're out of endpoints, return some error */
+			err("We're out of endpoints");
+			return -ENOMEM;
+		}
+		/* Now we have to fill in this ep */
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+	}
+	/* Ok, now we got valid endpoint, lets insert some traffic */
+
+	urb->status = -EINPROGRESS;
+
+	save_flags(flags);
+	cli();
+	
+	if (URB_List[epid]) {
+		/* Find end of list and add */
+		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
+			dump_urb(tmp_urb);
+
+		tmp_urb->next = urb;
+		restore_flags(flags);
+	} else {
+		/* If this is the first URB, add the URB and do HW add */
+		URB_List[epid] = urb;
+		restore_flags(flags);
+		etrax_usb_do_bulk_hw_add(urb, epid, maxlen);
+	}
+
+	DBFEXIT;
+
+	return 0;
+}
+
+static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
+{
+	USB_SB_Desc_t *sb_desc_1;
+
+	etrax_urb_priv_t *urb_priv;
+
+	unsigned long flags;
+	__u32 r_usb_ept_data;
+
+	DBFENTER;
+
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
+	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+
+	if (usb_pipeout(urb->pipe)) {
+
+		dbg_bulk("Bulk transfer for epid %d is OUT", epid);
+		dbg_bulk("transfer_buffer_length == %d", urb->transfer_buffer_length);
+		dbg_bulk("actual_length == %d", urb->actual_length);
+	
+		if (urb->transfer_buffer_length > 0xffff) {
+			panic(__FILE__ __FUNCTION__ ":urb->transfer_buffer_length > 0xffff\n");
+		}
+
+		sb_desc_1->sw_len = urb->transfer_buffer_length;  /* was actual_length */
+		sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
+			IO_STATE(USB_SB_command, tt, out) |
+
+#if 0
+			IO_STATE(USB_SB_command, full, no) |
+#else
+			IO_STATE(USB_SB_command, full, yes) |
+#endif
+
+			IO_STATE(USB_SB_command, eot, yes) |
+			IO_STATE(USB_SB_command, eol, yes);
+		
+		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
+		
+		sb_desc_1->buf = virt_to_phys(urb->transfer_buffer);
+		sb_desc_1->next = 0;
+		
+	} else if (usb_pipein(urb->pipe)) {
+
+		dbg_bulk("Transfer for epid %d is IN", epid);
+		dbg_bulk("transfer_buffer_length = %d", urb->transfer_buffer_length);
+		dbg_bulk("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
+		
+		sb_desc_1->sw_len = urb->transfer_buffer_length ?
+			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+		dbg_bulk("sw_len got %d", sb_desc_1->sw_len);
+		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
+		
+		sb_desc_1->command =
+			IO_FIELD(USB_SB_command, rem,
+				 urb->transfer_buffer_length % maxlen) |
+			IO_STATE(USB_SB_command, tt, in) |
+			IO_STATE(USB_SB_command, eot, yes) |
+			IO_STATE(USB_SB_command, eol, yes);
+		
+		sb_desc_1->buf = 0;
+		sb_desc_1->next = 0;
+
+		urb_priv->rx_offset = 0;
+		urb_priv->eot = 0;
+	}
+	
+	urb_priv->first_sb = sb_desc_1;
+	
+	urb->hcpriv = (void *)urb_priv;
+	
+	/* Reset toggle bits and reset error count, remeber to di and ei */
+	/* Warning: it is possible that this locking doesn't work with bottom-halves */
+
+	save_flags(flags);
+	cli();
+
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+		panic("Hold was set in %s\n", __FUNCTION__);
+	}
+
+	*R_USB_EPT_DATA &=
+		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+		  IO_MASK(R_USB_EPT_DATA, error_count_out));
+	
+	if (usb_pipeout(urb->pipe)) {
+		char toggle =
+		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
+		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
+	} else {
+		char toggle =
+		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
+		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
+	}
+		
+	/* Enable the EP descr. */
+
+	set_bit(epid, (void *)&ep_really_active);
+	
+	TxBulkEPList[epid].sub = virt_to_phys(sb_desc_1);
+	TxBulkEPList[epid].hw_len = 0;
+	TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+	restore_flags(flags);
+
+	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+		
+	}
+
+	DBFEXIT;
+}
+
+static int handle_bulk_transfer_attn(char epid, int status)
+{
+	urb_t *old_urb;
+	etrax_urb_priv_t *hc_priv;
+	unsigned long flags;
+
+	DBFENTER;
+
+	clear_bit(epid, (void *)&ep_really_active);
+	
+	old_urb = URB_List[epid];
+	URB_List[epid] = old_urb->next;
+
+	/* if (status == 0 && IN) find data and copy to urb */
+	if (status == 0 && usb_pipein(old_urb->pipe)) {
+		etrax_urb_priv_t *urb_priv;
+		
+		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+		save_flags(flags);
+		cli();
+		if (urb_priv->eot == 1) {
+			old_urb->actual_length = urb_priv->rx_offset;
+		} else {
+			if (urb_priv->rx_offset == 0) {
+				status = 0;
+			} else {
+				status = -EPROTO;
+			}
+			
+			old_urb->actual_length = 0;
+			err("(BULK) No eot set in IN data!!! rx_offset is: %d", urb_priv->rx_offset);
+		}
+		
+		restore_flags(flags);
+	}
+
+	save_flags(flags);
+	cli();
+		
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	if (usb_pipeout(old_urb->pipe)) {
+		char toggle =
+		IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
+			      usb_pipeout(old_urb->pipe), toggle);
+	} else {
+		char toggle =
+		IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
+			      usb_pipeout(old_urb->pipe), toggle);
+	}
+	restore_flags(flags);
+	
+	/* If there are any more URB's in the list we'd better start sending */
+	if (URB_List[epid]) {
+		etrax_usb_do_bulk_hw_add(URB_List[epid], epid,
+					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
+						       usb_pipeout(URB_List[epid]->pipe)));
+	}
+#if 1
+	else {
+		/* This means that this EP is now free, deconfigure it */
+		etrax_usb_free_epid(epid);
+	}
+#endif
+	
+	/* Remember to free the SB's */
+	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+	cleanup_sb(hc_priv->first_sb);
+	kfree(hc_priv);
+
+	old_urb->status = status;
+	if (old_urb->complete) {
+		old_urb->complete(old_urb);
+	}
+
+	DBFEXIT;
+}
+
+/* ---------------------------------------------------------------------------- */
+
+static int etrax_usb_submit_ctrl_urb(urb_t *urb)
+{
+	char epid;
+	char devnum;
+	char endpoint;
+	char maxlen;
+	char slow;
+
+	urb_t *tmp_urb;
+	
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	
+	DBFENTER;
+
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe,
+			       usb_pipeout(urb->pipe));
+	slow = usb_pipeslow(urb->pipe);
+	
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	if (epid == -1) {
+		epid = etrax_usb_allocate_epid();
+		if (epid == -1) {
+			/* We're out of endpoints, return some error */
+			err("We're out of endpoints");
+			return -ENOMEM;
+		}
+		/* Now we have to fill in this ep */
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+	}
+	/* Ok, now we got valid endpoint, lets insert some traffic */
+
+	urb->status = -EINPROGRESS;
+
+	save_flags(flags);
+	cli();
+	
+	if (URB_List[epid]) {
+		/* Find end of list and add */
+		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
+			dump_urb(tmp_urb);
+
+		tmp_urb->next = urb;
+		restore_flags(flags);
+	} else {
+		/* If this is the first URB, add the URB and do HW add */
+		URB_List[epid] = urb;
+		restore_flags(flags);
+		etrax_usb_do_ctrl_hw_add(urb, epid, maxlen);
+	}
+
+	DBFEXIT;
+
+	return 0;
+}
+
+static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
+{
+	USB_SB_Desc_t *sb_desc_1;
+	USB_SB_Desc_t *sb_desc_2;
+	USB_SB_Desc_t *sb_desc_3;
+
+	etrax_urb_priv_t *urb_priv;
+
+	unsigned long flags;
+	__u32 r_usb_ept_data;
+	
+
+	DBFENTER;
+
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
+	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+	sb_desc_2 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+
+	if (!(sb_desc_1 && sb_desc_2)) {
+		panic("kmem_cache_alloc in ctrl_hw_add gave NULL pointers !!!\n");
+	}
+	
+	sb_desc_1->sw_len = 8;
+	sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
+		IO_STATE(USB_SB_command, tt, setup) |
+		IO_STATE(USB_SB_command, full, yes) |
+		IO_STATE(USB_SB_command, eot, yes);
+	
+	sb_desc_1->buf = virt_to_phys(urb->setup_packet);
+	sb_desc_1->next = virt_to_phys(sb_desc_2);
+	dump_sb_desc(sb_desc_1);
+
+	if (usb_pipeout(urb->pipe)) {
+		dbg_ctrl("Transfer for epid %d is OUT", epid);
+
+		/* If this Control OUT transfer has an optional data stage we add an OUT token
+		   before the mandatory IN (status) token, hence the reordered SB list */
+		
+		if (urb->transfer_buffer) {
+			dbg_ctrl("This OUT transfer has an extra data stage");
+			sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+
+			sb_desc_1->next = virt_to_phys(sb_desc_3);
+			
+			sb_desc_3->sw_len = urb->transfer_buffer_length;
+			sb_desc_3->command = IO_STATE(USB_SB_command, tt, out) |
+				IO_STATE(USB_SB_command, full, yes) |
+				IO_STATE(USB_SB_command, eot, yes);
+			sb_desc_3->buf = virt_to_phys(urb->transfer_buffer);
+			sb_desc_3->next = virt_to_phys(sb_desc_2);
+		}
+		
+		sb_desc_2->sw_len = 1;
+		sb_desc_2->command = IO_FIELD(USB_SB_command, rem, 0) |
+			IO_STATE(USB_SB_command, tt, in) |
+			IO_STATE(USB_SB_command, eot, yes) |
+			IO_STATE(USB_SB_command, eol, yes);
+
+		sb_desc_2->buf = 0;
+		sb_desc_2->next = 0;
+		dump_sb_desc(sb_desc_2);
+		
+	} else if (usb_pipein(urb->pipe)) {
+
+		dbg_ctrl("Transfer for epid %d is IN", epid);
+		dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
+		dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
+
+		sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+		
+		sb_desc_2->sw_len = urb->transfer_buffer_length ?
+			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+		dbg_ctrl("sw_len got %d", sb_desc_2->sw_len);
+		
+		sb_desc_2->command =
+			IO_FIELD(USB_SB_command, rem,
+				 urb->transfer_buffer_length % maxlen) |
+			IO_STATE(USB_SB_command, tt, in) |
+			IO_STATE(USB_SB_command, eot, yes);
+		
+		sb_desc_2->buf = 0;
+		sb_desc_2->next = virt_to_phys(sb_desc_3);
+		dump_sb_desc(sb_desc_2);
+
+		sb_desc_3->sw_len = 1;
+		sb_desc_3->command = IO_FIELD(USB_SB_command, rem, 0) |
+			IO_STATE(USB_SB_command, tt, zout) |
+			IO_STATE(USB_SB_command, full, yes) |
+			IO_STATE(USB_SB_command, eot, yes) |
+			IO_STATE(USB_SB_command, eol, yes);
+				
+		sb_desc_3->buf = 0;
+		sb_desc_3->next = 0;
+		dump_sb_desc(sb_desc_3);
+
+		urb_priv->rx_offset = 0;
+		urb_priv->eot = 0;
+	}
+	
+	urb_priv->first_sb = sb_desc_1;
+	
+	urb->hcpriv = (void *)urb_priv;
+	
+	/* Reset toggle bits and reset error count, remeber to di and ei */
+	/* Warning: it is possible that this locking doesn't work with bottom-halves */
+
+	save_flags(flags);
+	cli();
+
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+		panic("Hold was set in %s\n", __FUNCTION__);
+	}
+	
+
+	*R_USB_EPT_DATA &=
+		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+		  IO_MASK(R_USB_EPT_DATA, error_count_out) |
+		  IO_MASK(R_USB_EPT_DATA, t_in) |
+		  IO_MASK(R_USB_EPT_DATA, t_out));
+
+	/* Enable the EP descr. */
+
+	set_bit(epid, (void *)&ep_really_active);
+	
+	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_1);
+	TxCtrlEPList[epid].hw_len = 0;
+	TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+	restore_flags(flags);
+
+	dump_ep_desc(&TxCtrlEPList[epid]);
+
+	if (!(*R_DMA_CH8_SUB1_CMD & IO_MASK(R_DMA_CH8_SUB1_CMD, cmd))) {
+		*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+		
+	}
+	
+	DBFEXIT;
+}
+
+static int etrax_usb_submit_urb(urb_t *urb)
+{
+	etrax_hc_t *hc;
+	int rval = -EINVAL;
+	
+	DBFENTER;
+
+	dump_urb(urb);
+	submit_urb_count++;
+	
+	hc = (etrax_hc_t*) urb->dev->bus->hcpriv;
+	
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		/* This request if for the Virtual Root Hub */
+		rval = etrax_rh_submit_urb(urb);
+		
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+		rval = etrax_usb_submit_ctrl_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+		rval = etrax_usb_submit_bulk_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+		int bustime;
+
+		if (urb->bandwidth == 0) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				rval = bustime;
+			} else {
+				usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+				rval = etrax_usb_submit_intr_urb(urb);
+			}
+			
+		}
+	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		warn("Isochronous traffic is not supported !!!");
+		rval = -EINVAL;
+	}
+
+	DBFEXIT;
+
+	return rval;
+}
+
+static int etrax_usb_unlink_urb(urb_t *urb)
+{
+	etrax_hc_t *hc = urb->dev->bus->hcpriv;
+	int epid;
+	int pos;
+	int devnum, endpoint, slow, maxlen;
+	etrax_urb_priv_t *hc_priv;
+	unsigned long flags;
+	
+	DBFENTER;
+	dump_urb(urb);
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe,
+			       usb_pipeout(urb->pipe));
+
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+
+	if (epid == -1)
+		return 0;
+	
+	
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		int ret;
+		ret =  etrax_rh_unlink_urb(urb);
+		DBFEXIT;
+		return ret;
+	} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+		int ret;
+		ret = etrax_usb_unlink_intr_urb(urb);
+		urb->status = -ENOENT;
+		if (urb->complete) {
+			urb->complete(urb);
+		}
+		DBFEXIT;
+		return ret;
+	}
+
+	info("Unlink of BULK or CTRL");
+
+	save_flags(flags);
+	cli();
+	
+	for (epid = 0; epid < 32; epid++) {
+		urb_t *u = URB_List[epid];
+		pos = 0;
+
+		for (; u; u = u->next) {
+			pos++;
+			if (u == urb) {
+				info("Found urb at epid %d, pos %d", epid, pos);
+
+				if (pos == 1) {
+					if (usb_pipetype(u->pipe) == PIPE_CONTROL) {
+						if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+							/* The EP was enabled, disable it and wait */
+							TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+							while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+						}
+						
+					} else if (usb_pipetype(u->pipe) == PIPE_BULK) {
+						if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+							TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+							while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+						}
+					}
+
+					URB_List[epid] = u->next;
+				
+				} else {
+					urb_t *up;
+					for (up = URB_List[epid]; up->next != u; up = up->next);
+					up->next = u->next;
+				}
+				u->status = -ENOENT;
+				if (u->complete) {
+					u->complete(u);
+				}
+				
+				hc_priv = (etrax_urb_priv_t *)u->hcpriv;
+				cleanup_sb(hc_priv->first_sb);
+				kfree(hc_priv);
+			}
+		}
+	}
+
+	restore_flags(flags);
+		
+	DBFEXIT;
+	return 0;
+}
+
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+{
+	DBFENTER;
+	DBFEXIT;
+	return (*R_USB_FM_NUMBER);
+}
+
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
+{  
+	DBFENTER;
+	DBFEXIT;
+	return 0;
+}
+
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
+{
+	DBFENTER;
+	DBFEXIT;
+	return 0;
+}
+
+static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	etrax_hc_t *hc = (etrax_hc_t *)vhc;
+	int epid;
+	char eol;
+	urb_t *urb;
+	USB_EP_Desc_t *tmp_ep;
+	USB_SB_Desc_t *tmp_sb;
+	
+	DBFENTER;
+
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+		info("dma8_sub0_descr (BULK) intr.");
+		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+		info("dma8_sub1_descr (CTRL) intr.");
+		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+		info("dma8_sub2_descr (INT) intr.");
+		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+		info("dma8_sub3_descr (ISO) intr.");
+		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+	}
+	
+	DBFEXIT;
+}
+
+static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	int epid = 0;
+	urb_t *urb;
+	etrax_urb_priv_t *urb_priv;
+		
+	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+		if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+
+			goto skip_out;
+		}
+
+		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+			
+			goto skip_out;
+		}
+		
+		epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+
+		urb = URB_List[epid];
+
+		if (urb && usb_pipein(urb->pipe)) {
+			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+
+			if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+				struct in_chunk *in;
+				dbg_intr("Packet for epid %d in rx buffers", epid);
+				in = kmalloc(sizeof(struct in_chunk), GFP_ATOMIC);
+				in->length = myNextRxDesc->hw_len;
+				in->data = kmalloc(in->length, GFP_ATOMIC);
+				memcpy(in->data, phys_to_virt(myNextRxDesc->buf), in->length);
+				list_add_tail(&in->list, &urb_priv->ep_in_list);
+#ifndef ETRAX_USB_INTR_IRQ
+				etrax_usb_hc_intr_top_half(irq, vhc, regs);
+#endif
+				
+			} else {
+				if ((urb_priv->rx_offset + myNextRxDesc->hw_len) >
+				    urb->transfer_buffer_length) {
+					err("Packet (epid: %d) in RX buffer was bigger "
+					    "than the URB has room for !!!", epid);
+					goto skip_out;
+				}
+				
+				memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+				       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+				
+				urb_priv->rx_offset += myNextRxDesc->hw_len;
+			}
+			
+			if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+				urb_priv->eot = 1;
+			}
+			
+		} else {
+			err("This is almost fatal, inpacket for epid %d which does not exist "
+			    " or is out !!!\nURB was at 0x%08X", epid, urb);
+			
+			goto skip_out;
+		}
+
+	skip_out:
+		myPrevRxDesc = myNextRxDesc;
+		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+		myLastRxDesc = myPrevRxDesc;
+
+		myNextRxDesc->status = 0;
+		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+	}
+}
+
+
+
+static void cleanup_sb(USB_SB_Desc_t *sb)
+{
+	USB_SB_Desc_t *next_sb;
+	
+	DBFENTER;
+
+	if (sb == NULL) {
+		err("cleanup_sb was given a NULL pointer");
+		return;
+	}
+
+	while (!(sb->command & IO_MASK(USB_SB_command, eol))) {
+		next_sb = (USB_SB_Desc_t *)phys_to_virt(sb->next);
+		kmem_cache_free(usb_desc_cache, sb);
+		sb = next_sb;
+	}
+
+	kmem_cache_free(usb_desc_cache, sb);
+
+	DBFEXIT;
+
+}
+
+static int handle_control_transfer_attn(char epid, int status)
+{
+	urb_t *old_urb;
+	etrax_urb_priv_t *hc_priv;	
+
+	DBFENTER;
+
+	clear_bit(epid, (void *)&ep_really_active);
+	
+	old_urb = URB_List[epid];
+	URB_List[epid] = old_urb->next;
+	
+	/* if (status == 0 && IN) find data and copy to urb */
+	if (status == 0 && usb_pipein(old_urb->pipe)) {
+		unsigned long flags;
+		etrax_urb_priv_t *urb_priv;
+
+		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+		save_flags(flags);
+		cli();
+		if (urb_priv->eot == 1) {
+			old_urb->actual_length = urb_priv->rx_offset;
+			dbg_ctrl("urb_priv->rx_offset: %d in handle_control_attn", urb_priv->rx_offset);
+		} else {
+			status = -EPROTO;
+			old_urb->actual_length = 0;
+			err("(CTRL) No eot set in IN data!!! rx_offset: %d", urb_priv->rx_offset);
+		}
+
+		restore_flags(flags);
+	}
+	
+	/* If there are any more URB's in the list we'd better start sending */
+	if (URB_List[epid]) {
+		etrax_usb_do_ctrl_hw_add(URB_List[epid], epid,
+					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
+						       usb_pipeout(URB_List[epid]->pipe)));
+	}
+#if 1
+	else {
+		/* This means that this EP is now free, deconfigure it */
+		etrax_usb_free_epid(epid);
+	}
+#endif
+	
+	/* Remember to free the SB's */
+	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+	cleanup_sb(hc_priv->first_sb);
+	kfree(hc_priv);
+
+	old_urb->status = status;
+	if (old_urb->complete) {
+		old_urb->complete(old_urb);
+	}
+
+	DBFEXIT;
+}
+
+
+
+static void etrax_usb_hc_intr_bottom_half(void *data)
+{
+	struct usb_reg_context *reg = (struct usb_reg_context *)data;
+	urb_t *old_urb;
+	
+	int error_code;
+	int epid;
+
+	__u32 r_usb_ept_data;
+
+	etrax_hc_t *hc = reg->hc; 
+	__u16 r_usb_rh_port_status_1;
+	__u16 r_usb_rh_port_status_2;
+	
+	DBFENTER;
+
+	if (reg->r_usb_irq_mask_read & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+
+		/*
+		  The Etrax RH does not include a wPortChange register, so this has
+		  to be handled in software. See section 11.16.2.6.2 in USB 1.1 spec
+		  for details.
+		*/
+		
+		r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+		r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+		
+		dbg_rh("port_status pending");
+		dbg_rh("r_usb_rh_port_status_1: 0x%04X", r_usb_rh_port_status_1);
+		dbg_rh("r_usb_rh_port_status_2: 0x%04X", r_usb_rh_port_status_2);
+
+		/* C_PORT_CONNECTION is set on any transition */
+		hc->rh.wPortChange_1 |=
+			((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+			 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+			(1 << RH_PORT_CONNECTION) : 0;
+		
+		hc->rh.wPortChange_2 |=
+			((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+			 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+			(1 << RH_PORT_CONNECTION) : 0;
+
+		/* C_PORT_ENABLE is _only_ set on a one to zero transition */
+		hc->rh.wPortChange_1 |=
+			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+			 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+			(1 << RH_PORT_ENABLE) : 0;
+		
+		hc->rh.wPortChange_2 |=
+			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+			 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+			(1 << RH_PORT_ENABLE) : 0;
+		
+		/* C_PORT_SUSPEND seems difficult, lets ignore it.. (for now) */
+		
+		/* C_PORT_RESET is _only_ set on a transition from the resetting state
+		   to the enabled state */
+		hc->rh.wPortChange_1 |=
+			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+			 && (r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+			(1 << RH_PORT_RESET) : 0;
+		
+		hc->rh.wPortChange_2 |=
+			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+			 && (r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+			(1 << RH_PORT_RESET) : 0;
+		
+		hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+		hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+	}
+
+	for (epid = 0; epid < 32; epid++) {
+
+		unsigned long flags;
+
+		save_flags(flags);
+		cli();
+		*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+		r_usb_ept_data = *R_USB_EPT_DATA;
+		restore_flags(flags);
+
+		if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+			warn("Was hold for epid %d", epid);
+			continue;
+		}
+
+		if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+			continue;
+		}
+		
+		
+		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+
+			if (URB_List[epid] == NULL) {
+				err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data);
+				err("submit urb has been called %d times..", submit_urb_count);
+				err("EPID_ATTN for epid %d, with NULL entry in list", epid);
+				return;
+			}
+			
+			dbg_ep("r_usb_ept_data [%d] == 0x%08X", epid,
+			       r_usb_ept_data);
+			
+			error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code,
+						r_usb_ept_data);
+			
+			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+				/* no_error means that this urb was successfully sent or that we have
+				   some undefinde error*/
+				
+				if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 ||
+				    IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) {
+				/* Actually there were transmission errors */
+					warn("Undefined error for endpoint %d", epid);
+					if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+						handle_control_transfer_attn(epid, -EPROTO);
+					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
+						handle_bulk_transfer_attn(epid, -EPROTO);
+					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+						handle_intr_transfer_attn(epid, -EPROTO);
+					}
+							   
+				} else {
+
+					if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+						if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+							etrax_usb_do_intr_recover(epid);
+						} else {
+							panic("Epid attention for epid %d (none INTR), with no errors and no "
+							      "exessive retry r_usb_status is 0x%02X\n",
+							      epid, reg->r_usb_status);
+						}
+						
+					} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+						panic("Epid attention for epid %d, with no errors and no "
+						      "exessive retry r_usb_status is 0x%02X\n",
+						      epid, reg->r_usb_status);
+						
+					}
+					
+					warn("Epid attention for epid %d, with no errors and no "
+					     "exessive retry r_usb_status is 0x%02X",
+					     epid, reg->r_usb_status);
+					warn("OUT error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_out,
+									       r_usb_ept_data));
+					warn("IN  error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_in,
+									       r_usb_ept_data));
+					
+
+				}
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+				warn("Stall for endpoint %d", epid);
+				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+					handle_control_transfer_attn(epid, -EPIPE);
+				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
+					handle_bulk_transfer_attn(epid, -EPIPE);
+				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+					handle_intr_transfer_attn(epid, -EPIPE);
+				}
+				
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+				panic("USB bus error for endpoint %d\n", epid);
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+				warn("Buffer error for endpoint %d", epid);
+
+				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+					handle_control_transfer_attn(epid, -EPROTO);
+				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
+					handle_bulk_transfer_attn(epid, -EPROTO);
+				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+					handle_intr_transfer_attn(epid, -EPROTO);
+				}
+
+			}
+		} else if (test_bit(epid, (void *)&ep_really_active)) {
+			/* Should really be else if (testbit(really active)) */
+
+			if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+
+				if (!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+					/* Now we have to verify that this CTRL endpoint got disabled
+					   cause it reached end of list with no error */
+					
+					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+						/*
+						  This means that the endpoint has no error, is disabled
+						  and had inserted traffic,
+						  i.e. transfer successfully completed
+						*/
+						dbg_ctrl("Last SB for CTRL %d sent successfully", epid);
+						handle_control_transfer_attn(epid, 0);
+					}
+				}
+				
+			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
+				if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+					/* Now we have to verify that this BULK endpoint go disabled
+					   cause it reached end of list with no error */
+
+					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+						/*
+						  This means that the endpoint has no error, is disabled
+						  and had inserted traffic,
+						  i.e. transfer successfully completed
+						*/
+						dbg_bulk("Last SB for BULK %d sent successfully", epid);
+						handle_bulk_transfer_attn(epid, 0);
+					}
+				}
+			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
+				handle_intr_transfer_attn(epid, 0);
+			}
+		}
+		
+	}	
+
+	kfree(reg);
+
+	DBFEXIT;
+}
+
+
+static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs)
+{
+	struct usb_reg_context *reg;
+
+	DBFENTER;
+
+	reg = (struct usb_reg_context *)kmalloc(sizeof(struct usb_reg_context), GFP_ATOMIC);
+
+	if (!(reg)) {
+		panic("kmalloc failed in top_half\n");
+	}
+
+	reg->hc = (etrax_hc_t *)vhc;
+	reg->r_usb_irq_mask_read    = *R_USB_IRQ_MASK_READ;
+	reg->r_usb_status           = *R_USB_STATUS;
+
+#if 0
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+		panic("r_usb_status said perror\n");
+	}
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+		panic("r_usb_status said ourun !!!\n");
+	}
+#endif
+	
+	reg->r_usb_epid_attn        = *R_USB_EPID_ATTN;
+
+	reg->r_usb_rh_port_status_1 = *R_USB_RH_PORT_STATUS_1;
+	reg->r_usb_rh_port_status_2 = *R_USB_RH_PORT_STATUS_2;
+
+	reg->usb_bh.sync = 0;
+	reg->usb_bh.routine = etrax_usb_hc_intr_bottom_half;
+	reg->usb_bh.data = reg;
+
+	queue_task(&reg->usb_bh, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	DBFEXIT;
+}
+
+static int etrax_rh_submit_urb(urb_t *urb)
+{
+	struct usb_device *usb_dev = urb->dev;
+	etrax_hc_t *hc = usb_dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	devrequest *cmd = (devrequest *) urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = 0;
+	int stat = 0;
+	int i;
+
+	__u16 cstatus;
+
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	DBFENTER;
+
+	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+		dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
+		hc->rh.urb = urb;
+		hc->rh.send = 1;
+		hc->rh.interval = urb->interval;
+		etrax_rh_init_int_timer(urb);
+		DBFEXIT;
+		
+		return 0;
+	}
+
+	bmRType_bReq = cmd->requesttype | cmd->request << 8;
+	wValue = le16_to_cpu(cmd->value);
+	wIndex = le16_to_cpu(cmd->index);
+	wLength = le16_to_cpu(cmd->length);
+
+	dbg_rh("bmRType_bReq : 0x%04X (%d)", bmRType_bReq, bmRType_bReq);
+	dbg_rh("wValue       : 0x%04X (%d)", wValue, wValue);
+	dbg_rh("wIndex       : 0x%04X (%d)", wIndex, wIndex);
+	dbg_rh("wLength      : 0x%04X (%d)", wLength, wLength);
+	
+	switch (bmRType_bReq) {
+		
+		/* Request Destination:
+		   without flags: Device, 
+		   RH_INTERFACE: interface, 
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here, 
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+		 */
+
+	case RH_GET_STATUS:
+		*(__u16 *) data = cpu_to_le16 (1);
+		OK (2);
+		
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+		
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+		
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *) data = cpu_to_le32 (0);
+		OK (4);		/* hub power ** */
+		
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+                if (wIndex == 1) {
+			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
+			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
+                }
+                else if (wIndex == 2) {
+			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
+			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
+                }
+                else {
+			dbg_rh("RH_GET_STATUS whith invalid wIndex !!");
+			OK(0);
+                }
+		
+                OK(4);
+                
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case (RH_ENDPOINT_STALL):
+			OK (0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case (RH_C_HUB_OVER_CURRENT):
+			OK (0);	/* hub power over current ** */
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_ENABLE):
+			if (wIndex == 1) {
+
+				dbg_rh("trying to do disable of port 1");
+
+				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+				while (hc->rh.prev_wPortStatus_1 &
+				       IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
+				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+				dbg_rh("Port 1 is disabled");
+
+			} else if (wIndex == 2) {
+
+				dbg_rh("trying to do disable of port 2");
+				
+				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+				while (hc->rh.prev_wPortStatus_2 &
+				       IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
+				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+				dbg_rh("Port 2 is disabled");
+
+			} else {
+				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
+				       "with invalid wIndex == %d!!", wIndex);
+			}			
+				
+			OK (0);
+		case (RH_PORT_SUSPEND):
+			/* Opposite to suspend should be resume, so well do a resume */
+			if (wIndex == 1) {
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port1) |
+					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+			} else if (wIndex == 2) {
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port2) |
+					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+			} else {
+				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
+				       "with invalid wIndex == %d!!", wIndex);
+			}
+			
+			OK (0);
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_C_PORT_CONNECTION):
+			
+                        if (wIndex == 1) {
+				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
+                        }
+                        else if (wIndex == 2) {
+				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
+                        }
+                        else {
+				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
+				       "with invalid wIndex == %d!!", wIndex);
+                        }
+
+			OK (0);
+		case (RH_C_PORT_ENABLE):
+			if (wIndex == 1) {
+				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
+			}
+			else if (wIndex == 2) {
+				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
+			}
+			else {
+				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
+				       "with invalid wIndex == %d!!", wIndex);
+			}
+			OK (0);
+		case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK (0);
+		case (RH_C_PORT_OVER_CURRENT):
+			OK (0);	/* port power over current ** */
+		case (RH_C_PORT_RESET):
+			if (wIndex == 1) {
+				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
+			}
+			else if (wIndex == 2) {
+				dbg_rh("This is wPortChange before clear: 0x%04X", hc->rh.wPortChange_2);
+				
+				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
+				dbg_rh("This is wPortChange after clear: 0x%04X", hc->rh.wPortChange_2);
+			} else {
+				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
+				       "with invalid index == %d!!", wIndex);
+			}
+
+			OK (0);
+		
+		}
+		break;
+		
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_SUSPEND):
+			if (wIndex == 1) {
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port1) |
+					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+			} else if (wIndex == 2) {
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port2) |
+					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+			} else {
+				dbg_rh("RH_SET_FEATURE->RH_C_PORT_SUSPEND "
+				       "with invalid wIndex == %d!!", wIndex);
+			}			
+
+			OK (0);
+		case (RH_PORT_RESET):
+                        if (wIndex == 1) {
+				int port1_retry;
+				
+			port1_redo:
+				dbg_rh("Doing reset of port 1");
+
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+					IO_STATE(R_USB_COMMAND, port_sel, port1);
+
+				/* We must once again wait at least 10ms for the device to recover */
+
+				port1_retry = 0;
+				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_1)) &
+					 IO_STATE(R_USB_RH_PORT_STATUS_1,
+						  enabled, yes))) {
+					printk(""); if (port1_retry++ >= 10000) {goto port1_redo;}
+				}
+				
+				/* This only seems to work if we use printk,
+				   not even schedule() works !!! WHY ?? */
+
+				udelay(15000);
+                        }
+                        else if (wIndex == 2) {
+				int port2_retry;
+				
+			port2_redo:
+				dbg_rh("Doing reset of port 2");
+				
+				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+					IO_STATE(R_USB_COMMAND, port_sel, port2);
+
+				/* We must once again wait at least 10ms for the device to recover */
+
+				port2_retry = 0;
+				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_2)) &
+					 IO_STATE(R_USB_RH_PORT_STATUS_2,
+						  enabled, yes))) {
+					printk(""); if (port2_retry++ >= 10000) {goto port2_redo;}
+				}
+				
+				/* This only seems to work if we use printk,
+				   not even schedule() works !!! WHY ?? */
+
+				udelay(15000);
+                        }
+
+			/* Try to bring the HC into running state */
+			*R_USB_COMMAND =
+				IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+			
+			nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+			
+                        dbg_rh("...Done");
+                        OK(0);
+                        
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_PORT_ENABLE):
+			/* There is no rh port enable command in the Etrax USB interface!!!! */
+			OK (0);
+                        
+		}
+		break;
+		
+	case RH_SET_ADDRESS:
+		hc->rh.devnum = wValue;
+                dbg_rh("RH address set to: %d", hc->rh.devnum);
+		OK (0);
+		
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case (0x01):	/* device descriptor */
+			len = min (leni, min (sizeof (root_hub_dev_des), wLength));
+			memcpy (data, root_hub_dev_des, len);
+			OK (len);
+		case (0x02):	/* configuration descriptor */
+			len = min (leni, min (sizeof (root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK (len);
+		case (0x03):	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+						   0xff, "ETRAX 100LX",
+						   data, wLength);
+			if (len > 0) {
+				OK (min (leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+		
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = hc->rh.numports;
+		len = min (leni, min (sizeof (root_hub_hub_des), wLength));
+		memcpy (data, root_hub_hub_des, len);
+		OK (len);
+		
+	case RH_GET_CONFIGURATION:
+		*(__u8 *) data = 0x01;
+		OK (1);
+		
+	case RH_SET_CONFIGURATION:
+		OK (0);
+		
+	default:
+		stat = -EPIPE;
+	}
+
+	urb->actual_length = len;
+	urb->status = stat;
+	urb->dev=NULL;
+	if (urb->complete) {
+		urb->complete (urb);
+	}
+	DBFEXIT;
+	
+	return 0;
+}
+
+static int __init etrax_usb_hc_init(void)
+{
+	static etrax_hc_t *hc;
+	struct usb_bus *bus;
+	struct usb_device *usb_rh;
+	
+	DBFENTER;
+
+	info("ETRAX 100LX USB-HCD %s (c) 2001 Axis Communications AB\n", usb_hcd_version);
+	
+	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+
+	/* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
+	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 0, 0, 0);
+	if (!usb_desc_cache) {
+		panic("USB Desc Cache allocation failed !!!\n");
+	}
+	
+	etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
+	hc->bus = bus;
+	bus->hcpriv = hc;
+        
+	/* Initalize RH to the default address.
+	   And make sure that we have no status change indication */
+	hc->rh.numports = 2;  /* The RH has two ports */
+	hc->rh.devnum = 0;
+	hc->rh.wPortChange_1 = 0;
+	hc->rh.wPortChange_2 = 0;
+
+	/* Also initate the previous values to zero */
+	hc->rh.prev_wPortStatus_1 = 0;
+	hc->rh.prev_wPortStatus_2 = 0;
+
+	/* Initialize the intr-traffic flags */
+	hc->intr.sleeping = 0;
+	hc->intr.wq = NULL;
+        
+	/* Initially all ep's are free except ep 0 */
+	ep_usage_bitmask = 0;
+	set_bit(0, (void *)&ep_usage_bitmask);
+	ep_really_active = 0;
+
+	memset(URB_List, 0, sizeof(URB_List));
+
+	/* This code should really be moved */
+
+        if (request_dma(8, "ETRAX 100LX built-in USB (Tx)")) {
+		err("Could not allocate DMA ch 8 for USB");
+		etrax_usb_hc_cleanup();
+		DBFEXIT;
+		return -1;
+	}
+	
+	if (request_dma(9, "ETRAX 100LX built-in USB (Rx)")) {
+		err("Could not allocate DMA ch 9 for USB");
+		etrax_usb_hc_cleanup();
+		DBFEXIT;
+		return -1;
+	}	
+#if 0  /* Moved to head.S */        
+	*R_GEN_CONFIG = genconfig_shadow =
+		(genconfig_shadow & ~(IO_MASK(R_GEN_CONFIG, usb1) |
+				      IO_MASK(R_GEN_CONFIG, usb2) |
+				      IO_MASK(R_GEN_CONFIG, dma8) |
+				      IO_MASK(R_GEN_CONFIG, dma9))) |
+		IO_STATE(R_GEN_CONFIG, dma8, usb) |
+		IO_STATE(R_GEN_CONFIG, dma9, usb)
+#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+		| IO_STATE(R_GEN_CONFIG, usb1, select)
+#endif
+#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+		| IO_STATE(R_GEN_CONFIG, usb2, select)
+#endif
+	;
+#endif
+	
+	usb_register_bus(hc->bus);
+
+	/* We may have to set more bits, but these are the obvious ones */
+	*R_IRQ_MASK2_SET =
+		IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
+
+	*R_IRQ_MASK2_SET =
+		IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
+
+	*R_USB_IRQ_MASK_SET = 
+		IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set) |
+		IO_STATE(R_USB_IRQ_MASK_SET, ctl_eot, set) |
+		IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
+#ifdef ETRAX_USB_INTR_IRQ
+		IO_STATE(R_USB_IRQ_MASK_SET, intr_eot, set) |
+#endif
+		IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
+		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set);
+	
+	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_intr_top_half, 0,
+			"ETRAX 100LX built-in USB (HC)", hc)) {
+		err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
+		etrax_usb_hc_cleanup();
+		DBFEXIT;
+		return -1;
+	}
+	
+	if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
+			"ETRAX 100LX built-in USB (Rx)", hc)) {
+		err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
+		etrax_usb_hc_cleanup();
+		DBFEXIT;
+		return -1;
+	}
+	
+	if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
+			"ETRAX 100LX built-in USB (Tx)", hc)) {
+		err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
+		etrax_usb_hc_cleanup();
+		DBFEXIT;
+		return -1;
+	}
+
+	/* Reset the USB interface (configures as HC) */
+	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset);
+	
+	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+#if 1
+	/* Initate PSTART to all unallocatable bit times */
+	*R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 10000);
+#endif
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+	*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+#endif
+	
+#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+	*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+#endif
+	
+	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset);
+	
+	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, port_sel, port1) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset);
+
+	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+	/* Here we must wait at least 10ms so the device has time to recover */
+	udelay(15000);
+
+	init_rx_buffers();
+	init_tx_bulk_ep();
+	init_tx_ctrl_ep();
+	init_tx_intr_ep();
+
+	/* This works. It seems like the host_run command only has effect when a device is connected,
+	 i.e. it has to be done when a interrup */
+	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+	
+	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+	usb_rh = usb_alloc_dev(NULL, hc->bus);
+	hc->bus->root_hub = usb_rh;
+	usb_connect(usb_rh);
+	usb_new_device(usb_rh);
+
+	DBFEXIT;
+	
+	return 0;
+}
+
+static void etrax_usb_hc_cleanup(void)
+{
+	DBFENTER;
+	
+	free_irq(ETRAX_USB_HC_IRQ, NULL);
+	free_irq(ETRAX_USB_RX_IRQ, NULL);
+	free_irq(ETRAX_USB_TX_IRQ, NULL);
+
+	free_dma(8);
+	free_dma(9);
+	usb_deregister_bus(etrax_usb_bus);
+
+	DBFEXIT;
+}
+
+module_init(etrax_usb_hc_init);
+module_exit(etrax_usb_hc_cleanup);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)