!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/arch/cris/drivers/   drwxr-xr-x
Free 318.32 GB of 458.09 GB (69.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     usb-host.c (66.15 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
 *
 * Copyright (c) 2001 Axis Communications AB.
 *
 * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj 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/dma.h>
#include <asm/system.h>
#include <asm/svinto.h>

#include <linux/usb.h>
#include "usb-host.h"

#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR

static const char *usb_hcd_version = "$Revision: 1.13 $";

#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 do {} while (0)
#define DBFEXIT  do {} while (0)
#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 unsigned long 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 __u32 ep_out_traffic;

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;

#ifdef USB_DEBUG_DESC
static void dump_urb (purb_t purb);
#endif
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(int epid, char devnum, char endpoint,
                 char packsize, char slow, char out_traffic);
static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint,
                 char slow, int maxp, char out_traffic);
static int etrax_usb_allocate_epid(void);
static void etrax_usb_free_epid(int epid);
static void cleanup_sb(USB_SB_Desc_t *sb);

static void etrax_usb_do_ctrl_hw_add(urb_t *urb, int epid, char maxlen);
static void etrax_usb_do_bulk_hw_add(urb_t *urb, int 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(...)     do {} while (0)
#define dump_in_desc(...) do {} while (0)
#define dump_sb_desc(...) do {} while (0)
#define dump_ep_desc(...) do {} while (0)
#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)
{
    USB_EP_Desc_t *tmp_ep;
    USB_EP_Desc_t *first_ep;
    
    USB_EP_Desc_t *ep_desc;
    
    int epid;
    char devnum;
    char endpoint;
    char slow;
    int maxlen;
    char out_traffic;
    int i;
    
    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));
    out_traffic = usb_pipeout(urb->pipe);

    epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
    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;
    
    int epid;
    char devnum;
    char endpoint;
    char maxlen;
    char out_traffic;
    char slow;
    int interval;
    int i;
    
    etrax_urb_priv_t *urb_priv;
    
    DBFENTER;

    devnum = usb_pipedevice(urb->pipe);
    endpoint = usb_pipeendpoint(urb->pipe);
    maxlen = usb_maxpacket(urb->dev, urb->pipe,
                   usb_pipeout(urb->pipe));
    out_traffic = 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, out_traffic);
    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, out_traffic);
    }
    /* 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 sucessfully 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 void handle_intr_transfer_attn(int 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(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic)
{
    unsigned long flags;
    
    DBFENTER;

    save_flags(flags);
    cli();
    
    if (test_bit(epid, (void *)&ep_usage_bitmask)) {
        restore_flags(flags);

        warn("Trying to setup used epid %d", epid);
        DBFEXIT;
        return;
    }
    
    set_bit(epid, (void *)&ep_usage_bitmask);
    
    *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);

    if (out_traffic)
        set_bit(epid, (void *)&ep_out_traffic);
    else
        clear_bit(epid, (void *)&ep_out_traffic);

    restore_flags(flags);

    dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)",
           epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN");

    DBFEXIT;
}

static void etrax_usb_free_epid(int 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, char out_traffic)
{
    int i;
    unsigned long flags;
    __u32 data;
    
    DBFENTER;

    save_flags(flags);
    cli();

    /* 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) &&
            test_bit(i, (void *)&ep_out_traffic) == out_traffic) {
            *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)) {
                restore_flags(flags);
    
                dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)",
                       i, devnum, endpoint, out_traffic ? "OUT" : "IN");
                DBFEXIT;
                return i;
            }
        }
    }

    restore_flags(flags);
    
    dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)",
           devnum, endpoint, out_traffic ? "OUT" : "IN");
    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)
{
    int epid;
    char devnum;
    char endpoint;
    char maxlen;
    char out_traffic;
    char slow;

    urb_t *tmp_urb;
    
    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));
    out_traffic = usb_pipeout(urb->pipe);
    slow = usb_pipeslow(urb->pipe);
    
    epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
    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, out_traffic);
    }
    /* 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 void etrax_usb_do_bulk_hw_add(urb_t *urb, int epid, char maxlen)
{
    USB_SB_Desc_t *sb_desc_1;

    etrax_urb_priv_t *urb_priv;

    unsigned long flags;

    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 void handle_bulk_transfer_attn(int 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)
{
    int epid;
    char devnum;
    char endpoint;
    char maxlen;
    char out_traffic;
    char slow;

    urb_t *tmp_urb;
    
    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));
    out_traffic = usb_pipeout(urb->pipe);
    slow = usb_pipeslow(urb->pipe);
    
    epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
    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, out_traffic);
    }
    /* 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 void etrax_usb_do_ctrl_hw_add(urb_t *urb, int 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;

    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;

    urb->next = NULL;

    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 devnum, endpoint, slow, maxlen, out_traffic;
    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));
    out_traffic = usb_pipeout(urb->pipe);

    epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);

    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];
        urb_t *prev = NULL;
        int pos = 0;

        for (; u; u = u->next) {
            pos++;
            if (u == urb) {
                if (!prev) {
                    URB_List[epid] = u->next;
                } else {
                    prev->next = u->next;
                }

                restore_flags(flags);

                if (!prev) {
                    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]));
                        }
                    }
                }

                info("Found urb at epid %d, pos %d", epid, pos);

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

                DBFEXIT;
                return 0;
            }
            prev = u;
        }
    }

    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)
{
    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 (%d) was bigger "
                        "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length);
                    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%08lX", epid, (unsigned long)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 void handle_control_transfer_attn(int 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;
    
    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 %lu 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 sucessfully 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 epid %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 epid %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 epid %d\n", epid);
                
            } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
                warn("Buffer error for epid %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 sucessfully completed
                        */
                        dbg_ctrl("Last SB for CTRL %d sent sucessfully", 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 sucessfully completed
                        */
                        dbg_bulk("Last SB for BULK %d sent sucessfully", 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 stat = 0;

    __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_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
            memcpy (data, root_hub_dev_des, len);
            OK (len);
        case (0x02):    /* configuration descriptor */
            len = min_t(unsigned int, leni, min_t(unsigned int, 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_t(unsigned int, leni, min_t(unsigned int, 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;
    ep_out_traffic = 0;

    memset(URB_List, 0, sizeof(URB_List));

    /* This code should really be moved */

    if (request_dma(USB_TX_DMA_NBR, "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(USB_RX_DMA_NBR, "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(USB_TX_DMA_NBR);
    free_dma(USB_RX_DMA_NBR);
    usb_deregister_bus(etrax_usb_bus);

    DBFEXIT;
}

module_init(etrax_usb_hc_init);
module_exit(etrax_usb_hc_cleanup);

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0087 ]--