/*
 * 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);
