Fwd: [Linux-usb-users] device descriptor read/64, error -110

"Midhun Agnihotram" <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Hi All,

    We are have a development kit on which we are trying to run USB
host port. The drivers are from the vendor. The kernel recognizes the
USB host controller. But as soon as we insert a USB flash drive, the
kernel hangs with the following error:

usb 1-2: device descriptor read/64, error -110

  I do not use the EHCI or UHCI driver. It is a custom driver given to
us by the vendor. I could not find any solution to the above error on
the net.

   I am attaching the source code that I got from the vendor.

I would appreciate any help.

Thanks,
Midhun.

P.S.: I am not on the mailing list. Please cc my ID when replying. Thanks again.


Forwarded Conversation
Subject: device descriptor read/64, error -110
------------------------
 From: Alan Stern <[email protected]>
To: Midhun Agnihotram <[email protected]>
Cc: [email protected]
Date: Mon, Aug 13, 2007 at 7:38 PM

On Mon, 13 Aug 2007, Midhun Agnihotram wrote:

> Hi All,
>
>     We are have a development kit on which we are trying to run USB
> host port. The drivers are from the vendor. The kernel recognizes the
> USB host controller. But as soon as we insert a USB flash drive, the
> kernel hangs with the following error:
>
> usb 1-2: device descriptor read/64, error -110
>
>   I do not use the EHCI or UHCI driver. It is a custom driver given to
> us by the vendor. I could not find any solution to the above error on
> the net.
>
>   What does the error signify? Why does the kernel hang?

The error means the computer is unable to communicate with the flash
drive.  There was no response to a Get-Device-Descriptor request within
the timeout limit.

The kernel hangs because your vendor's driver is buggy.

> Any help would be appreciated.

It's hard to provide help without access to the source code.

Alan Stern


--------
 From: Midhun Agnihotram <[email protected]>
To: Alan Stern <[email protected]>
Cc: [email protected]
Date: Mon, Aug 13, 2007 at 9:25 PM


> It's hard to provide help without access to the source code.
>
> Alan Stern


Is it fine if I mail  the source code as an attachment?
Thanks,
Midhun.
--------
 From: Midhun Agnihotram <[email protected]>
To: Alan Stern <[email protected]>,
[email protected]
Date: Mon, Aug 13, 2007 at 9:30 PM

Hi,

> It's hard to provide help without access to the source code.
>

Is it fine if I send across the source code as an attachment?


--
Thanks,
Midhun.

--------
 From: Alan Stern <[email protected]>
To: Midhun Agnihotram <[email protected]>
Cc: [email protected]
Date: Mon, Aug 13, 2007 at 9:42 PM

On Mon, 13 Aug 2007, Midhun Agnihotram wrote:

> Hi,
>
> > It's hard to provide help without access to the source code.
> >
>
> Is it fine if I send across the source code as an attachment?

You can put the source code in the body of an email message or as an
attachment, or you can put the source code on a Web server and send the
URL in an email message.

However I suggest you do this on the linux-usb-devel list, since the
type of problem you're struggling with is more suited to developers
than to users.

Alan Stern

--------

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
imx21-hcd.c (text/plain, 50.7 KB)
#define INLINE
#if 0

//    /* Set transmit enable for port 2 - USBH1 */
//    USBCTRL |= USBCTRL_HOST1_TXEN_OE;

    switch(usb_pipetype(urb->pipe)) {
    case PIPE_CONTROL:
        if (urb->transfer_buffer_length > 4096) {
            printk("%s:%d Error XXXXXXXXXXXXXXXXXXXXXXXX\n", 
                   __FUNCTION__, __LINE__);
            return -EMSGSIZE;
        }
        num_tds = 3;  /* SETUP, data, ACK */
        break;
    case PIPE_ISOCHRONOUS:
//        num_tds = urb->number_of_packets;
        printk("Can't handle ISOCHRONOUS yet\n");
        return -ENODEV;
        break;
    case PIPE_BULK:
        printk("Can't handle BULK yet\n");
        return -ENODEV;
        break;
    case PIPE_INTERRUPT:
        printk("Can't handle INTERRUPT yet\n");
        return -ENODEV;
        break;
    }

    ped = kmalloc(sizeof(ped_t) + (num_tds * sizeof(ptd_t)), mem_flags);
    if (ped == NULL) {
        return -ENOMEM;
    }

    memset(ped, 0, sizeof(ped_t) + (num_tds * sizeof(ptd_t)));
    
    ped->urb = urb;
    ped->num_ptds = num_tds;

    urb->hcpriv = ped;

    dir = usb_pipeout(urb->pipe) ? IMX_USB_TD_DIR_OUT : IMX_USB_TD_DIR_IN;
    /* Fill in the TDs */
    switch(usb_pipetype(urb->pipe)) {
    case PIPE_CONTROL:
        if (urb->dev->speed == USB_SPEED_HIGH) {
            ped->speed = 0;
        } else {
            ped->speed = 1;
        }
        ped->format = IMX_USB_TD_FORMAT_CONTROL;
        ped->endpoint = usb_pipeendpoint(urb->pipe);
        ped->address = usb_pipedevice(urb->pipe);
        ped->interval = 0;
        ped->relpolpos = 0;
        ped->cur_ptd = 0;

        /* SETUP */
        ped->ptds[0].dir = IMX_USB_TD_DIR_SETUP;
        ped->ptds[0].buf = urb->setup_packet;
        ped->ptds[0].size = 8; /* Standard for control transfers */
        ped->ptds[0].toggle = IMX_USB_TD_TOGGLE_DATA0;
        ped->ptds[0].maxpacket = 8;

        dmem_offset = imx21_alloc_dmem(imx21, ped->ptds[0].size);
        if (dmem_offset < 0) {
            printk("Error allocating Data memory\n");
// Do error handling
            return -ENOMEM;
        }
        ped->ptds[0].dmem_offset = dmem_offset;
        ped->ptds[0].round = 0;

        /* Data */
        ped->ptds[1].dir = dir;
        ped->ptds[1].buf = urb->transfer_buffer;
        ped->ptds[1].size = urb->transfer_buffer_length;
        ped->ptds[1].toggle = IMX_USB_TD_TOGGLE_DATA1;
        dmem_offset = imx21_alloc_dmem(imx21, ped->ptds[1].size);
        if (dmem_offset < 0) {
            printk("Error allocating Data memory\n");
// Do error handling
            return -ENOMEM;
        }
        ped->ptds[1].dmem_offset = dmem_offset;
        if (dir == IMX_USB_TD_DIR_IN) {
            ped->ptds[1].round = 1;
        } else {
            ped->ptds[1].round = 0;
        }
        ped->ptds[1].maxpacket = usb_maxpacket(urb->dev, urb->pipe, 
                                               usb_pipeout(urb->pipe));

        /* ACK */
        if (dir == IMX_USB_TD_DIR_IN) {
            ped->ptds[2].dir = IMX_USB_TD_DIR_OUT;
        } else {
            ped->ptds[2].dir = IMX_USB_TD_DIR_IN;
        }
        ped->ptds[2].buf = urb->transfer_buffer; /* Irrelevant? */
        ped->ptds[2].size = 1;
        ped->ptds[2].toggle = IMX_USB_TD_TOGGLE_DATA1;
        dmem_offset = imx21_alloc_dmem(imx21, ped->ptds[2].size);
        if (dmem_offset < 0) {
            printk("Error allocating Data memory\n");
// Do error handling
            return -ENOMEM;
        }
        ped->ptds[2].dmem_offset = dmem_offset;
        ped->ptds[2].round = 0;
        ped->ptds[2].maxpacket = usb_maxpacket(urb->dev, urb->pipe, 
                                               usb_pipeout(urb->pipe));

        break;

    case PIPE_ISOCHRONOUS:
        printk("Can't handle ISOCHRONOUS yet\n");
        return -ENODEV;
        break;
    case PIPE_BULK:
        printk("Can't handle BULK yet\n");
        return -ENODEV;
        break;
    case PIPE_INTERRUPT:
        printk("Can't handle INTERRUPT yet\n");
        return -ENODEV;
        break;
    }


    spin_lock(imx21->lock);
        
    etdnum = imx21_alloc_etd(imx21);
    if (etdnum < 0) {
        spin_unlock(imx21->lock);
        return etdnum;
    }

    ped->etd = etdnum;
    imx21->ped[etdnum] = ped;

    start_xfer(imx21, ped);

    spin_unlock(imx21->lock);
    return -ENODEV;
#endif

#define IMX_USB_TD_DIR_SETUP        0
#define IMX_USB_TD_DIR_OUT          1
#define IMX_USB_TD_DIR_IN           2
#define IMX_USB_TD_FORMAT_CONTROL   0
#define IMX_USB_TD_FORMAT_ISO       1
#define IMX_USB_TD_FORMAT_BULK      2
#define IMX_USB_TD_FORMAT_INT       3
#define IMX_USB_TD_TOGGLE_CARRY     0
#define IMX_USB_TD_TOGGLE_DATA0     2
#define IMX_USB_TD_TOGGLE_DATA1     3
#define USB_DMEM(x)                 __REG(IMX_USBOTG_BASE + 0x1000 + (x))

/* control transfer states */
#define US_CTRL_SETUP   2
#define US_CTRL_DATA    1
#define US_CTRL_ACK     0

/* bulk transfer main state and 0-length packet */
#define US_BULK         1
#define US_BULK0        0


/*ETD format description*/
#define IMX_FMT_CTRL   0x0
#define IMX_FMT_ISO    0x1
#define IMX_FMT_BULK   0x2
#define IMX_FMT_INT    0x3

static char fmt_urb_to_etd[4] = {
/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO,
/*PIPE_INTERRUPT*/ IMX_FMT_INT,
/*PIPE_CONTROL*/ IMX_FMT_CTRL,
/*PIPE_BULK*/ IMX_FMT_BULK
};

/* condition (error) CC codes and mapping (OHCI like) */

#define TD_CC_NOERROR		0x00
#define TD_CC_CRC		0x01
#define TD_CC_BITSTUFFING	0x02
#define TD_CC_DATATOGGLEM	0x03
#define TD_CC_STALL		0x04
#define TD_DEVNOTRESP		0x05
#define TD_PIDCHECKFAIL		0x06
/*#define TD_UNEXPECTEDPID	0x07 - reserved, not active on MX2*/
#define TD_DATAOVERRUN		0x08
#define TD_DATAUNDERRUN		0x09
#define TD_BUFFEROVERRUN	0x0C
#define TD_BUFFERUNDERRUN	0x0D
#define TD_NOTACCESSED		0x0F

#define mx2otg_clear_toggling_bit(reg,mask) do { \
if (reg & mask) reg = mask;\
} while (0)

#define mx2otg_set_toggling_bit(reg,mask) do { \
if (!(reg & mask)) reg = mask;\
} while (0)

/*
 * USB Host Controller Driver for IMX21
 *
 *
 * Copyright (C) 2006 Loping Dog Embedded Systems
 * Written by Jay Monkman <[email protected]>
 *
 * This driver is licensed under the GPLv2.
 */
/*#include <linux/config.h>*/

#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/dma-mapping.h>
#include <asm/arch/imx21-hcd.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/byteorder.h>

#include "../core/hcd.h"

#define NUM_ETD 32
#define DMEM_SIZE 4096

#ifdef DEBUG
#  define _cp printk("%s:%d\n", __FUNCTION__, __LINE__)
#  define DBG(stuff...) printk(stuff)
#else
#  define _cp
#  define DBG(stuff...) do {} while(0)
#endif

#ifdef CONFIG_PM
#warning "IMX21 USB HCD driver doesn't support PM"
#endif

typedef struct urb_priv_s {
    int etd_num;     /* ETD number for URB (iso: even # packets) */
    int etd_num1;    /* ISO only: ETD number for odd # packets */
    int iso_index;
    int iso_index1;
    struct completion done;
    int unlinked;
    struct timer_list timeout;
} urb_priv_t;

static const int cc_to_error [16] = {
        /* No  Error  */               0,
        /* CRC Error  */               -EILSEQ,
        /* Bit Stuff  */               -EPROTO,
        /* Data Togg  */               -EILSEQ,
        /* Stall      */               -EPIPE,
        /* DevNotResp */               -ETIMEDOUT,
        /* PIDCheck   */               -EPROTO,
        /* UnExpPID   */               -EPROTO,
        /* DataOver   */               -EOVERFLOW,
        /* DataUnder  */               -EREMOTEIO,
        /* (for hw)   */               -EIO,
        /* (for hw)   */               -EIO,
        /* BufferOver */               -ECOMM,
        /* BuffUnder  */               -ENOSR,
        /* (for HCD)  */               -EALREADY,
        /* (for HCD)  */               -EALREADY
};


void dump_regs(void)
{
    printk("\n");
    printk("hwmode     = 0x%08x  cint_stat  = 0x%08x  cint_sten = 0x%08x\n",
           USBOTG_HWMODE, USBOTG_CINT_STAT, USBOTG_CINT_STEN);
    printk("clk_ctrl   = 0x%08x  rst_ctrl   = 0x%08x  frm_intvl = 0x%08x\n",
           USBOTG_CLK_CTRL, USBOTG_RST_CTRL, USBOTG_FRM_INTVL);
    printk("frm_remain = 0x%08x  hnp_csr   = 0x%08x  hint_isr = 0x%08x\n",
           USBOTG_FRM_REMAIN, USBOTG_HNP_CSR, USBOTG_HNP_ISR);
    printk("hnp_ien    = 0x%08x  usbctrl    = 0x%08x\n",
           USBOTG_HNP_IEN, USBCTRL);

    printk("\n");
    printk("host_ctrl  = 0x%08x  sysisr     = 0x%08x  sysien    = 0x%08x\n",
           USBH_HOST_CTRL, USBH_SYSISR, USBH_SYSIEN);
    printk("xbufstat   = 0x%08x  ybufstat   = 0x%08x  xyinten   = 0x%08x\n",
           USBH_XBUFSTAT, USBH_YBUFSTAT, USBH_XYINTEN);
    printk("xfillstat  = 0x%08x  yfillstat  = 0x%08x  etdenset  = 0x%08x\n",
           USBH_XFILLSTAT, USBH_YFILLSTAT, USBH_ETDENSET);
    printk("etdenclr   = 0x%08x  immedint   = 0x%08x  etddonest = 0x%08x\n",
           USBH_ETDENCLR, USBH_IMMEDINT, USBH_ETDDONESTAT);
    printk("etddoneen  = 0x%08x  frmnub     = 0x%08x  lsthresh  = 0x%08x\n",
           USBH_ETDDONEEN, USBH_FRMNUB, USBH_LSTHRESH);
    printk("roothuba   = 0x%08x  roothubb   = 0x%08x  rootstat  = 0x%08x\n",
           USBH_ROOTHUBA, USBH_ROOTHUBB, USBH_ROOTSTAT);
    printk("portst1    = 0x%08x  portst2    = 0x%08x  portst3   = 0x%08x\n",
           USBH_PORTSTAT(0), USBH_PORTSTAT(1), USBH_PORTSTAT(2));

}

static const char hcd_name[] = "imx21-hc";

/*
 * Each URB can cause multiple transfers over the USB. 
 * These data structures are used to describe each transfer
 * for an URB.
 */

/* One of these for each transfer, multiple per URB */
typedef struct {
    int dir;
    void *buf;
    int size;    int toggle;
    int round;
    int dmem_offset;
    int maxpacket;
} ptd_t;

/* One for each URB */
typedef struct {
    struct urb *urb;
    int etd;
    int num_ptds;
    int cur_ptd;
    int speed;
    int format;
    int endpoint;
    int address;
    int interval;
    int relpolpos;
    ptd_t ptds[0];
} ped_t;

typedef struct imx21_dmem_area_s{
    int offset;
    int size;
    struct imx21_dmem_area_s *next;
} imx21_dmem_area_t;

typedef struct epd {
    struct urb *pipe_head;
    struct list_head urb_queue;
    struct timer_list timeout;
} epd_t;

struct imx21 {
    spinlock_t lock;
    struct imx_usb_hcd_info *info;

    struct list_head ctrl_list;
    struct list_head bulk_list;
    struct list_head iso_list;
    struct list_head intr_list;

    wait_queue_head_t waitq;	/* deletion of URBs needs a waitqueue */
    imx21_dmem_area_t *dmem_list;
#if 0
    char etd_alloc[NUM_ETD];
    ped_t *ped[NUM_ETD];
#endif

    struct urb *etd_urb[NUM_ETD];  /* points to the URB for this ETD */
    int etd_len[NUM_ETD];          /* non-iso - DMA buffer length */
    char *etd_buf[NUM_ETD];        /* non-iso - DMA buffer address */
    int active_urbs;

    epd_t ed[NUM_ETD];

    struct imx21_usb_platform_data *board;
};

static inline void dump_ep(struct usb_host_endpoint *ep)
{
    printk("ep: %p\n", ep);
    printk("ep->hcpriv: %p\n", ep->hcpriv);
    printk("ep->urb_list: %p\n", &ep->urb_list);
    printk("ep->descriptor:\n");
    printk("    bEndpointAddress: %d\n", ep->desc.bEndpointAddress);
    printk("    wMaxPacketSize: %d\n", ep->desc.wMaxPacketSize);
}

static INLINE void dump_etd(int etd_num)
{
    volatile u32 *etd_addr = &USB_ETD(etd_num);
    printk("etd(%d): 0x%08x 0x%08x 0x%08x 0x%08x\n", etd_num,
           etd_addr[0], etd_addr[1], etd_addr[2], etd_addr[3]);
}

static inline void dump_urb(struct urb *urb)
{
    printk("urb: %p\n", urb);
    printk("   ->dev->speed: %s\n", 
           urb->dev->speed == USB_SPEED_FULL ? "FULL" : "LOW");
    printk("   ->hcpriv: %p\n", urb->hcpriv);
    printk("   ->pipe: 0x%x  %s  %s\n", urb->pipe,
           usb_pipeout(urb->pipe) ? "OUT" : "IN",
           (usb_pipeisoc(urb->pipe) ? "ISO" :
            usb_pipeint(urb->pipe) ? "INT" :
            usb_pipecontrol(urb->pipe) ? "CTRL" :
            usb_pipebulk(urb->pipe) ? "BULK" : "UNK"));
    printk("   ->transfer_flags: 0x%x\n", urb->transfer_flags);
    printk("   ->transfer_buffer: %p\n", urb->transfer_buffer);
    printk("   ->transfer_dma: 0x%x\n", urb->transfer_dma);
    printk("   ->transfer_buffer_length: %d\n", urb->transfer_buffer_length);
    printk("   ->setup_packet: %p\n", urb->setup_packet);
    printk("   ->setup_dma: 0x%x\n", urb->setup_dma);
    if (usb_pipecontrol(urb->pipe)) {
        printk("       0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
               ((unsigned char*)urb->setup_packet)[0], 
               ((unsigned char*)urb->setup_packet)[1],
               ((unsigned char*)urb->setup_packet)[2],
               ((unsigned char*)urb->setup_packet)[3],
               ((unsigned char*)urb->setup_packet)[4],
               ((unsigned char*)urb->setup_packet)[5],
               ((unsigned char*)urb->setup_packet)[6],
               ((unsigned char*)urb->setup_packet)[7]);
    }
    printk("   maxpacket: %d\n", 
           usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)));
}


/*find an unused ETD*/
static int
imx21_get_free_etd_num(struct imx21 *imx21)
{
    int etd_num;
    u32 bit_mask;

    bit_mask = 1;
    for (etd_num = 0; etd_num < USB_NUM_ETD; etd_num++) {  /* check all ETDs */
        if (imx21->etd_urb[etd_num] == 0)	/*if etd is not active */
            break;
        bit_mask <<= 1;
    }
    
    if (etd_num == USB_NUM_ETD) {	/*no available ETD found */
        err("no available ETD");	/*debug */
        return -ENODEV;
    }
    return etd_num;
}


static inline int qu_pipeindex(__u32 pipe)
{
    return (usb_pipeendpoint(pipe) << 1) | (usb_pipecontrol(pipe) ? 0 :
                                            usb_pipeout(pipe));
}

/***************************************************************************
 * disable host controller interrupts
 **************************************************************************/
static INLINE void
hc_stop_int(void)
{
  USBH_SYSIEN = 0;
}

/***************************************************************************
 * Save local control or bulk URB state
 * in unused bits of the URB pipe descriptor
 **************************************************************************/
static inline void
qu_seturbstate(struct urb *urb, int state)
{
    urb->pipe &= ~0x1f;
    urb->pipe |= state & 0x1f;
}

/***************************************************************************
 * Get local control or bulk URB state
 **************************************************************************/
static inline int
qu_urbstate(struct urb *urb)
{
  return urb->pipe & 0x1f;
}

/***************************************************************************
 * This function adds the urb to the appropriate active urb list and set
 * the urb state.
 *
 * There are four active lists: isochoronous list, interrupt list,
 * control list, and bulk list.
 **************************************************************************/
static inline void
qu_queue_active_urb(struct imx21 *imx21, struct urb *urb, epd_t * ed)
{
    int urb_state = 0;

    if (++imx21->active_urbs == 1) {
        /* Enable ETD done interrupts */
        USBH_SYSIEN |= USBH_SYSIEN_DONEINT | USBH_SYSIEN_SOFINT;
    }

    switch (usb_pipetype(urb->pipe)) {
    case PIPE_CONTROL:
        list_add(&urb->urb_list, &imx21->ctrl_list);
        urb_state = US_CTRL_SETUP;
        break;
        
    case PIPE_BULK:
        list_add(&urb->urb_list, &imx21->bulk_list);
        urb_state = US_BULK;
        break;
        
    case PIPE_INTERRUPT:
        list_add(&urb->urb_list, &imx21->intr_list);
        break;
        
    case PIPE_ISOCHRONOUS:
        list_add(&urb->urb_list, &imx21->iso_list);
        break;
    }
    
    qu_seturbstate(urb, urb_state);
}

/***************************************************************************
 * If the urb was active, add the next urb waiting from the proper local ED
 *   list to the proper local active list and return 0
 * If the urb was not active, return -1
 **************************************************************************/
static int
qu_next_urb(struct imx21 *imx21, struct urb *urb)
{
  epd_t *ed = &imx21->ed[qu_pipeindex(urb->pipe)];

  if (ed->pipe_head == urb) {	/*the urb was active */

    if (!list_empty(&ed->urb_queue)) {
      urb = list_entry(ed->urb_queue.next, struct urb, urb_list);
      list_del(&urb->urb_list);
      INIT_LIST_HEAD(&urb->urb_list);
      ed->pipe_head = urb;
      qu_queue_active_urb(imx21, urb, ed);
    } else {
      ed->pipe_head = NULL;
    }
    return 0;
  }
  return -1;
}

#if 0
static inline int imx21_alloc_etd(struct imx21 *imx21)
{
    int i;
    for (i = 0; i < NUM_ETD; i++) {
        if (imx21->etd_alloc[i] == 0) {
            imx21->etd_alloc[i] = 1;
            return i;
        }
    }

    return -1;
}

static inline void imx21_free_etd(struct imx21 *imx21, int etd)
{
    imx21->etd_alloc[etd] = 0;
}
#endif

static int imx21_alloc_dmem(struct imx21 *imx21, int size)
{
    int offset = 0;
    imx21_dmem_area_t *area;
    imx21_dmem_area_t **p;
    imx21_dmem_area_t *tmp;

    spin_lock(imx21->lock);

    /* Round the size up */
    size += (~size + 1) & 0x3;

    if (size > DMEM_SIZE) {
        return -ENOMEM;
    }

    area = kmalloc(sizeof(imx21_dmem_area_t), GFP_ATOMIC);
    if ( area == NULL) {
        return -ENOMEM;
    }

    p = &imx21->dmem_list;
    tmp = *p;
    while (tmp != NULL) {
        if ((size + offset) < offset) {
            goto fail;
        }
        if ((size + offset) <= tmp->offset) {
            break;
        }
        offset = tmp->size + tmp->offset;
        if ((offset + size) > DMEM_SIZE) {
            goto fail;
        }
        p = &tmp->next;
        tmp = *p;
    } 

    area->offset = offset;
    area->size = size;
    area->next = *p;
    *p = area;

    spin_unlock(imx21->lock);
    return offset;

 fail:
    spin_unlock(imx21->lock);
    kfree(area);
    return -ENOMEM;
}

static void imx21_free_dmem(struct imx21 *imx21, int offset)
{
    imx21_dmem_area_t **p;
    imx21_dmem_area_t *tmp;
    
    spin_lock(imx21->lock);
    p = &imx21->dmem_list;
    tmp = *p;
    while (tmp != NULL) {
        if (tmp->offset == offset) {
            *p = tmp->next;
            kfree(tmp);
            spin_unlock(imx21->lock);
            return;
        }
        p = &tmp->next;
        tmp = *p;
    } 
    spin_unlock(imx21->lock);
    printk("Trying to free data memory that wasn't allocated. Offset: %d\n",
           offset);
}    

    
static inline struct imx21 *hcd_to_imx21(struct usb_hcd *hcd)
{
    return (struct imx21 *) hcd->hcd_priv;
}


static int imx21_hc_hub_descriptor(struct usb_hcd *hcd,
                                   struct usb_hub_descriptor *desc)
{

    desc->bDescriptorType = 0x29;  /* HUB descriptor */
    desc->bHubContrCurrent = 0;

    desc->bNbrPorts = USBH_ROOTHUBA & USBH_ROOTHUBA_NDNSTMPRT_MASK;
    desc->bDescLength = 9;

    desc->bPwrOn2PwrGood = 0;

    desc->wHubCharacteristics = (__force __u16)cpu_to_le16(
        0x0002 | /* No power switching */
        0x0010 | /* No over current protection */
        0);

    desc->bitmap[0] = 1 << 1;
    desc->bitmap[1] = ~0;

    return 0;
}

#if 0
static void start_xfer(struct imx21 *imx21, ped_t *ped)
{
    volatile u32 *etd_word;
    ptd_t *ptd;

    ptd = &ped->ptds[ped->cur_ptd];

    etd_word = &USB_ETD(ped->etd);

    etd_word[0] = (
        ((ped->address & 0x3f) << 0) |
        ((ped->endpoint & 0xf) << 7) | 
        ((ptd->dir & 3) << 11) |
        ((ped->speed & 1) << 13) |
        ((ped->format & 0x3) << 14) |
        ((ptd->maxpacket & 0x3ff) << 16));

    etd_word[1] = (
        (((ptd->dmem_offset + ptd->maxpacket) & 0xffff) << 16) |
        (ptd->dmem_offset & 0xffff));

    etd_word[2] = (
        ((ped->interval & 0xff) << 0) | 
        ((ped->relpolpos & 0xff) << 8) | 
        ((ptd->dir & 3) << 16) |
        ((ptd->round & 1) << 18) |
        ((ptd->toggle & 3) << 22) |
        (0xf << 28));   /* Reset completion code */

    etd_word[3] = (
        (((ptd->maxpacket - 1) & 0x7ff) << 21) | 
        ((ptd->size & 0x1fffff) << 0));


    /* Clear the done status bit */
    USBH_ETDDONESTAT = (1 << ped->etd);

    /* Enable interrupt for this ETD */
    USBH_ETDDONEEN |= (1 << ped->etd);

    if ((ptd->dir == IMX_USB_TD_DIR_SETUP)||(ptd->dir == IMX_USB_TD_DIR_OUT)){
        memcpy(&USB_DMEM(ptd->dmem_offset), ptd->buf, ptd->size);

        /* Set X/Y filled status bits */
        USBH_XFILLSTAT |= (1 << ped->etd);
        USBH_YFILLSTAT |= (1 << ped->etd);
    } else {
        /* Clear X/Y filled status bits */
        USBH_XFILLSTAT &= ~(1 << ped->etd);
        USBH_YFILLSTAT &= ~(1 << ped->etd);
    }

    /* Clear the X/Y interrupt bits */
    USBH_XBUFSTAT = (1 << ped->etd);
    USBH_YBUFSTAT = (1 << ped->etd);

    /* Enable the ETD */
    USBH_ETDENSET = (1 << ped->etd);
    
}
#endif


static inline int
hc_add_iso_trans(struct imx21 *imx21, struct urb *urb)
{
    printk("%s NOT IMPLEMENTED YET\n", __FUNCTION__);
    return -ENODEV;
}



/***************************************************************************
 * Setup a bulk, control or interrupt ETD. Configure DMA if necessary.
 * Trigger the processing of the ETD by the host controller. Return error
 * and not do anything in case there are not enough resources.
 **************************************************************************/
static int
hc_add_noniso_trans(struct imx21 *imx21, struct urb *urb, int state)
{
    unsigned int pipe = urb->pipe;
    urb_priv_t *urb_priv = urb->hcpriv;
    int etd_num;
    int buf_addr;
    volatile u32 *etd_addr;
    u16 buf_size;
    u32 etd_mask;
    u8 *src_addr;
    u32 count;
    u8 dir;
    u8 bufround;
    u8 datatoggle;
    
    u16 maxpacket;
    
    u8 interval = 0;
    u8 relpolpos = 0;
    
    if (!(maxpacket = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe))))
        maxpacket = 8;
    
    if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) {
        if (state == US_CTRL_SETUP) {
            dir = IMX_USB_TD_DIR_SETUP;
            src_addr = (u8 *) urb->setup_packet;
            bufround = 0;
            count = 8;
            datatoggle = IMX_USB_TD_TOGGLE_DATA0;
        } else {
            dir = usb_pipeout(pipe) ? IMX_USB_TD_DIR_IN : IMX_USB_TD_DIR_OUT;
            src_addr = (u8 *) urb->transfer_buffer;
            bufround = 0;
            count = 0;
            datatoggle = IMX_USB_TD_TOGGLE_DATA1;
        }
        
    } else {
        dir = usb_pipeout(pipe) ? IMX_USB_TD_DIR_OUT : IMX_USB_TD_DIR_IN;
        bufround = (dir == IMX_USB_TD_DIR_IN) ? 1 : 0;
        src_addr = (u8 *) urb->transfer_buffer;
        if (usb_pipebulk(pipe) && (state == US_BULK0))
            count = 0;
        else
            count = urb->transfer_buffer_length;
        if (usb_pipecontrol(pipe)) {
            datatoggle = IMX_USB_TD_TOGGLE_DATA1;
        } else {
            datatoggle = (usb_gettoggle(urb->dev,
                                        usb_pipeendpoint(urb->
                                                         pipe),
                                        usb_pipeout(urb->
                                                    pipe))) ?
                IMX_USB_TD_TOGGLE_DATA1 : IMX_USB_TD_TOGGLE_DATA0;
            
        }
    }
    
    if (count > maxpacket)
        buf_size = maxpacket * 2;
    else
    buf_size = maxpacket;
    
    etd_num = imx21_get_free_etd_num(imx21);
    if (etd_num < 0) {
        dbg("cannot get a free ETD for non-iso");
        return etd_num;
    }
    
/* allocate x and y buffer space at once*/
    buf_addr = imx21_alloc_dmem(imx21, buf_size);
    if (buf_addr < 0) {
        dbg("not enough DATA memory for non-iso");
    return buf_addr;
    }
    
    etd_mask = 1 << etd_num;
    
    imx21->etd_buf[etd_num] = 0;
    
#ifdef MX2HCD_USE_DIRECT_DMA
    if ((u32) src_addr & 0x3) {
#endif
        imx21->etd_buf[etd_num] = kmalloc(count, GFP_ATOMIC | GFP_DMA);
        if (!imx21->etd_buf[etd_num]) {
            dbg("not enough memory for non-iso DMA buffer, %d bytes needed", count);
            imx21_free_dmem(imx21, buf_addr);
            return -ENOMEM;
        }
        if (dir != IMX_USB_TD_DIR_IN) {
            memcpy(imx21->etd_buf[etd_num], src_addr, count);
            consistent_sync(imx21->etd_buf[etd_num], count, DMA_TO_DEVICE);
        } else {
            consistent_sync(imx21->etd_buf[etd_num], count, DMA_FROM_DEVICE);
        }
        
        src_addr = imx21->etd_buf[etd_num];
#ifdef MX2HCD_USE_DIRECT_DMA
    } else {
        if (dir != IMX_USB_TD_DIR_IN) {
            consistent_sync(src_addr, count, DMA_TO_DEVICE);
        } else {
            consistent_sync(src_addr, count, DMA_FROM_DEVICE);
        }
    }
#endif
    
    if (usb_pipeint(pipe)) {
        interval = urb->interval;
        relpolpos = (USBH_FRMNUB + 1) & 0xff;
    }
    
    imx21->etd_urb[etd_num] = urb;
    urb_priv->etd_num = etd_num;
    

    
    etd_addr = &USB_ETD(etd_num);   /*set up word 0*/
    
    /*Normal USB mode is "do not stop on NAK" (bit 30 ==0)*/
   etd_addr[0] = (u32) usb_pipedevice(pipe) |
       ((u32) usb_pipeendpoint(pipe) << 7) |
       ((u32) dir << 11) |
       ((u32)((urb->dev->speed == USB_SPEED_LOW) ? 1  : 0) << 13) |
//        ((u32) usb_pipeslow(pipe) << 13) |
       ((u32) fmt_urb_to_etd[usb_pipetype(pipe)] << 14) |
       ((u32) maxpacket << 16);
    
   etd_addr[1] = 
        (((u32) buf_addr + (u32) maxpacket) << 16) | (u32) buf_addr;
    
    /*delay interrupt option is disabled (bit 19-21 == 0)*/
    etd_addr[2] = (u32) interval |
        ((u32) relpolpos << 8) |
        ((u32) dir << 16) |
        ((u32) bufround << 18) |
        ((u32) datatoggle << 22) |
        ((u32) 0xf << 28);      /*reset completion code */
    
    /*X or Y buffer size is always == 1 maxpacket */
    etd_addr[3] = ((u32) (maxpacket - 1) << 21) | (u32) count;
    
    /*clear etd done status */
    mx2otg_clear_toggling_bit(USBH_ETDDONESTAT, etd_mask);
    
    /*enable etd done interrupt */
    USBH_ETDDONEEN |= etd_mask;
    
    mx2otg_clear_toggling_bit(USBH_XFILLSTAT, etd_mask);
    mx2otg_clear_toggling_bit(USBH_YFILLSTAT, etd_mask);
    
    if (count == 0) {
        
        if (dir != IMX_USB_TD_DIR_IN) {
            /*need to set it even if count == 0 */
            mx2otg_set_toggling_bit(USBH_XFILLSTAT, etd_mask);
            mx2otg_set_toggling_bit(USBH_YFILLSTAT, etd_mask);
        }
        
    } else {
        
        mx2otg_set_toggling_bit(USB_ETDDMACHANLCLR, etd_mask);
        
        mx2otg_clear_toggling_bit(USBH_XBUFSTAT, etd_mask);
        mx2otg_clear_toggling_bit(USBH_YBUFSTAT, etd_mask);
        
        USB_ETDSMSA(etd_num) = virt_to_phys(src_addr);
        mx2otg_set_toggling_bit(USB_ETDDMAEN, etd_mask);
        
    }
    
    imx21->etd_len[etd_num] = count;
    
#ifdef DEBUG
    dump_etd(etd_num);
#endif
    
#ifdef CONFIG_MX2TO1
    mx2otg_set_toggling_bit(OTG_HOST_ETD_EN, etd_mask);
#else
    USBH_ETDENSET = etd_mask;
#endif
    return 0;
}

/***************************************************************************
 * Try to start an URB stransaction
 **************************************************************************/
static int
sh_add_packet(struct imx21 *imx21, struct urb *urb)
{
    if (usb_pipeisoc(urb->pipe)) {
        return hc_add_iso_trans(imx21, urb);
    } else {
        return hc_add_noniso_trans(imx21, urb, qu_urbstate(urb));
    }
}

/***************************************************************************
 * Scan through the active URB list and try to start transactions until the
 * first failure. Remove all succesfully started URBs from the list.
 **************************************************************************/
static int
sh_scan_urb_list(struct imx21 *imx21, struct list_head *list_lh)
{
    struct urb *urb;

    while (!list_empty(list_lh)) {
        urb = list_entry(list_lh->next, struct urb, urb_list);
        if (urb == NULL)
            return 0;
        
        if (sh_add_packet(imx21, urb) < 0) {
            return -1;
        } else {
            list_del(&urb->urb_list);
            INIT_LIST_HEAD(&urb->urb_list);
        }
        
    }
    return 0;
}
/***************************************************************************
 * Scan through all of the the active URB lists and try to start
 * transactions until the first failure.
 * Remove all succesfully started URBs from the list.
 **************************************************************************/
static void
sh_schedule_trans(struct imx21 *imx21)
{
    int res = 0;

    if (!list_empty(&imx21->intr_list)) {
        res = sh_scan_urb_list(imx21, &imx21->intr_list);
    }

    if (!list_empty(&imx21->iso_list) && res >= 0) {
      res = sh_scan_urb_list(imx21, &imx21->iso_list);
    }

    if (!list_empty(&imx21->ctrl_list) && res >= 0) {
        res = sh_scan_urb_list(imx21, &imx21->ctrl_list);
    }

    if (!list_empty(&imx21->bulk_list) && res >= 0) {
        sh_scan_urb_list(imx21, &imx21->bulk_list);
    }
}

/***************************************************************************
 * If the ETD is done, parse all status data. Copy received data with
 * memcpy if necessary. Report number of bytes actually transferred and
 * completion code
 **************************************************************************/
static INLINE struct urb *
hc_parse_trans(struct imx21 *imx21, int etd_num, int *actbytes, int *cc)
{
  u32 etd_mask = 1 << etd_num;
  urb_priv_t *urb_priv;
  unsigned int pipe;
  struct urb *urb;
  volatile u32 *etd_addr;
  u16 xbufaddr;

  int compl_code;
  u32 bytes_xfrd;
  int dir;

  _cp;
  if (!(USBH_ETDDONESTAT & etd_mask))
    return 0;
  _cp;
  urb = imx21->etd_urb[etd_num];
  if (!urb)
    return 0;
  _cp;

  urb_priv = urb->hcpriv;
  pipe = urb->pipe;

  etd_addr = &USB_ETD(etd_num);
  dir = (etd_addr[0] >> 11) & 0x3;

#ifdef DEBUG
  dump_etd(etd_num);
#endif

#ifdef CONFIG_MX2TO1
  mx2otg_clear_toggling_bit(OTG_HOST_ETD_EN, etd_mask);
#else
  USBH_ETDENCLR = etd_mask;
#endif

  mx2otg_clear_toggling_bit(USBH_ETDDONESTAT, etd_mask);
  mx2otg_clear_toggling_bit(USB_ETDDMAEN, etd_mask);

  if (usb_pipeisoc(pipe)) {
  _cp;
#if 0
    int iso_index;
    u8 *src_addr;

    if (etd_num == urb_priv->etd_num)
      iso_index = urb_priv->iso_index;
    else
      iso_index = urb_priv->iso_index1;

    xbufaddr = etd_addr[1] & 0xffff;

    /*etd completion code */
    /*compl_code = (etd_addr[2] >> 28) & 0xf; */

    /*packet completion code */
    compl_code = (etd_addr[3] >> 12) & 0xf;

    /*indicates the number of bytes not transferred */
    bytes_xfrd = etd_addr[3] & 0x3ff;

    /*or may be not, if receiving data. May be it is just not being updated.
    Convert to the number of bytes transferred */
    if (usb_pipeout(pipe))
      bytes_xfrd = urb->iso_frame_desc[iso_index].length - bytes_xfrd;

    if (usb_pipein(pipe)) {
      src_addr = (u8 *) urb->transfer_buffer +
	  urb->iso_frame_desc[iso_index].offset;

#ifdef MX2HCD_USE_DIRECT_ISO_DMA
      if ((u32) src_addr & 0x3) {
#endif
	memcpy(src_addr,
	       (u8 *) phys_to_virt(OTG_DATA_BASE_VIR_ADDR + xbufaddr), bytes_xfrd);
#ifdef MX2HCD_USE_DIRECT_ISO_DMA
      }
#endif
      mx2otg_clear_toggling_bit(USBH_XFILLSTAT, etd_mask);
    }

    urb->iso_frame_desc[iso_index].actual_length = bytes_xfrd;
    urb->iso_frame_desc[iso_index].status = cc_to_error[compl_code];

    /* freeing of ETD resources is done outside this function when all packets are processed */
#endif
  } else {			/* BULK, INT, CONTROL DATA */

  _cp;
    /*save toggle carry */
    usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
      usb_pipeout(urb->pipe), (etd_addr[0] >> 28) & 0x1);
    xbufaddr = etd_addr[1] & 0xffff;

    /*etd completion code */
    compl_code = (etd_addr[2] >> 28) & 0xf;

    /*indicates the number of bytes left to be transferred */
    bytes_xfrd = etd_addr[3] & 0x1fffff;

    /*convert to the number of bytes transfered */
    bytes_xfrd = imx21->etd_len[etd_num] - bytes_xfrd;

    if (dir == IMX_USB_TD_DIR_IN) {
  _cp;
      /*if data input */

      mx2otg_clear_toggling_bit(USBH_XFILLSTAT, etd_mask);
      mx2otg_clear_toggling_bit(USBH_YFILLSTAT, etd_mask);

      if (imx21->etd_buf[etd_num]) {
  _cp;
	memcpy((char *) urb->transfer_buffer,
	       imx21->etd_buf[etd_num], bytes_xfrd);
      }

    }

    if (imx21->etd_buf[etd_num]) {
  _cp;
      kfree(imx21->etd_buf[etd_num]);
    }

    imx21_free_dmem(imx21, xbufaddr);
    imx21->etd_urb[etd_num] = 0;
    urb_priv->etd_num = -1;
  }
  _cp;
  *cc = compl_code;
  *actbytes = bytes_xfrd;

  return urb;
}

/***************************************************************************
 * Part of the Host Controller interrupt handler.
 * Scan through all ETD done states. Submit another URB transaction if
 * necessary, or report URB completion.
 **************************************************************************/
static void
sh_done_list(struct imx21 *imx21)
{
  int actbytes = 0;
  int etd_done = 1;
  int cc;
  struct urb *urb;
  int urb_state = 0;
  int trans;
  u32 iso_index = 0;
  urb_priv_t *urb_priv;

  _cp;

  for (trans = 0; trans < USB_NUM_ETD; trans++) {

    urb = hc_parse_trans(imx21, trans, &actbytes, &cc);
    if (!urb)
      continue;			/*either not ready or not initiated */

    urb->error_count = 0;
    urb_priv = urb->hcpriv;

    if (!(urb->transfer_flags & URB_SHORT_NOT_OK)
	&& (cc == TD_DATAUNDERRUN))
      cc = TD_CC_NOERROR;

//    if (cc == TD_CC_STALL) {
//      usb_endpoint_halt(urb->dev,
//			usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
//    }

    etd_done = (cc_to_error[cc] != 0);	/*stop if error */
    switch (usb_pipetype(urb->pipe)) {

#if 0
    case PIPE_ISOCHRONOUS:
      {
	struct urb *urbt;
	int i;
	u32 framenum;
	u32 iso_index1;

	urb->actual_length += actbytes;

	if (urb_priv->unlinked)
	  etd_done = 1;

	if (!etd_done) {

	  if (trans == urb_priv->etd_num)
	    iso_index = urb_priv->iso_index;
	  else
	    iso_index = urb_priv->iso_index1;

	  framenum = USBH_FRMNUB & 0xffff;
	  iso_index1 = urb->start_frame + iso_index;
	  if ((((iso_index1 + 1) & 0xffff) != framenum)
	      && ((iso_index1 & 0xffff) != framenum)) {
	    /*something delayed us, restore the pace.
             Sometimes we may be called here on the same frame */

	    if (framenum < urb->start_frame)
	      framenum += 0x10000;
	    iso_index1 = framenum - 1 - urb->start_frame;
	    dbg("iso delayed %d frames", iso_index1 - iso_index);
	    iso_index = iso_index1;

	  }

	  if ((iso_index + 1) >= urb->number_of_packets) {
	    /*processing the last packet */
	    for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next) ;
	    if (urbt) {
	      /*need to requeue the ISO urb. Sending the second packet */

	      urb->status = cc_to_error[cc];
	      if (urb->complete) {
		urb->complete(urb);
	      }
#ifdef DEBUG
	      dump_urb(urb);
#endif

	      urb->actual_length = 0;
	      urb->status = USB_ST_URB_PENDING;
	      urb->start_frame = (urb->start_frame + urb->number_of_packets)
		  & 0xffff;

	      for (i = 0; i < urb->number_of_packets; i++) {
		urb->iso_frame_desc[i].actual_length = 0;
		urb->iso_frame_desc[i].status = -EXDEV;
	      }

	      if ((urb->number_of_packets > 1)
		  && ((urb_priv->iso_index == 0)
		      || (urb_priv->iso_index1 == 0)))
		hc_next_iso_trans(imx21, urb, 1, trans);
	      else
		hc_next_iso_trans(imx21, urb, 0, trans);

	    } else {
	      etd_done = 1;
	    }
	  } else if ((iso_index + 2) >= urb->number_of_packets) {
	    /*processing the next to last packet */
	    for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next) ;
	    if (urbt) {
	      /*Need to requeue ISO. Send the first packet */
	      /*iso_index will be converted to 0 inside */
	      hc_next_iso_trans(imx21, urb, iso_index + 2, trans);
	    } else {
	      /*clear up unused resources */
	      hc_abort_transfer(imx21, trans);
	      /*let the next urb waiting start being processed ahead of time */
	      qu_next_urb(imx21, urb);
	    }
	  } else {
	    /*processing a regular packet */
	    hc_next_iso_trans(imx21, urb, iso_index + 2, trans);
	  }
	}
	if (etd_done) {
	  hc_abort_transfer(imx21, urb_priv->etd_num);
	  hc_abort_transfer(imx21, urb_priv->etd_num1);
	}

	break;
      }
#endif
    case PIPE_CONTROL:
      urb_state = qu_urbstate(urb);

      if (urb_state == US_CTRL_SETUP) {
	if (urb->transfer_buffer_length > 0)
	  urb_state = US_CTRL_DATA;
	else
	  urb_state = US_CTRL_ACK;
      } else if (urb_state == US_CTRL_DATA) {
	urb->actual_length += actbytes;
	urb_state = US_CTRL_ACK;

      } else
	etd_done = 1;		/*US_CTRL_ACK */

      if (!etd_done) {
	qu_seturbstate(urb, urb_state);
	if (hc_add_noniso_trans(imx21, urb, urb_state) < 0)
	  /*requeue in case of res. shortage */
	  list_add(&urb->urb_list, &imx21->ctrl_list);
      }

      break;
    case PIPE_BULK:
      urb->actual_length += actbytes;

      if ((qu_urbstate(urb) == US_BULK)
	  && (urb->transfer_flags & URB_ZERO_PACKET)
	  && urb->transfer_buffer_length > 0
	  && ((urb->transfer_buffer_length %
	    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) == 0)) {
	dbg("processing bulk 0-packet");
	/*need a 0-packet */
	qu_seturbstate(urb, US_BULK0);

	/*requeue in case of res. shortage */
	if (hc_add_noniso_trans(imx21, urb, US_BULK0) < 0)
	  list_add(&urb->urb_list, &imx21->bulk_list);

      } else {
	etd_done = 1;
      }
      break;
    case PIPE_INTERRUPT:
      urb->actual_length += actbytes;
      if (!etd_done) {
	if (urb_priv->unlinked) {
	  complete(&urb_priv->done);
	} else {
	  urb->status = cc_to_error[cc];
#ifdef DEBUG
	  dump_urb(urb);
#endif
	  if (urb->complete) {
	    urb->complete(urb);	/* call complete */
	  }

	  urb->actual_length = 0;
	  urb->status = -EINPROGRESS;

	  /*requeue in case of res. shortage */
	  if (hc_add_noniso_trans(imx21, urb, 0) < 0)
	    list_add(&urb->urb_list, &imx21->intr_list);
	}
      }
      break;
    }

    if (etd_done) {

      if (urb_priv->unlinked) {
	complete(&urb_priv->done);
      } else {

#ifdef HC_URB_TIMEOUT
	if (urb->timeout)
	  del_timer(&ed->timeout);
#endif

	if (!--imx21->active_urbs)
	  hc_stop_int();
	qu_next_urb(imx21, urb);

	urb->status = cc_to_error[cc];
#ifdef DEBUG
	dump_urb(urb);
#endif
	kfree(urb_priv);
	urb->hcpriv = NULL;

	urb->dev = NULL;

	if (urb->complete) {
	  urb->complete(urb);	/* call complete */
	}

      }

    }

  }

}

static irqreturn_t imx21_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{
    u32 ints = USBH_SYSISR;
    struct imx21 *imx21 = hcd_to_imx21(hcd);

    _cp;

    /* Finished transfer */
    if (ints & USBH_SYSISR_DONEINT) {
        _cp;
        sh_done_list(imx21);
    }

    /* Start of frame */
    if (ints & USBH_SYSISR_SOFINT) {
        _cp;
        sh_schedule_trans(imx21);
    }

    USBH_SYSISR = ints;

    return IRQ_HANDLED;
}


static void imx21_hc_stop(struct usb_hcd *hcd)
{
    _cp;
}

static int imx21_hc_reset(struct usb_hcd *hcd)
{
    _cp;
    return 0;
}

static int __devinit imx21_hc_start(struct usb_hcd *hcd)
{
    struct usb_device *udev;

    _cp;

    udev = usb_alloc_dev(NULL, &hcd->self, 0);
    if (udev == NULL) {
        return -ENOMEM;
    }

    udev->speed = USB_SPEED_FULL;
    hcd->state = HC_STATE_RUNNING;

    /* Enable host controller interrupts */
    USBOTG_CINT_STEN |= USBOTG_HCINT;

    _cp;
    return 0;
}


static int imx21_hc_urb_enqueue(struct usb_hcd *hcd,
                                struct usb_host_endpoint *ep,
                                struct urb *urb,
                                gfp_t mem_flags)
{
    struct imx21 *imx21 = hcd_to_imx21(hcd);
    urb_priv_t *urb_priv;
    unsigned long flags;
    epd_t *ed = &imx21->ed[qu_pipeindex(urb->pipe)];

    _cp;
    dump_ep(ep);
    dump_urb(urb);

    urb_priv = kmalloc(sizeof(urb_priv_t), 
                       in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
    if (urb_priv == NULL) {
        return -ENOMEM;
    }

    memset(urb_priv, 0, sizeof(urb_priv_t));
    urb_priv->etd_num  = -1;
    urb_priv->etd_num1 = -1;
    init_completion(&urb_priv->done);

    urb->hcpriv = urb_priv;

    /* Add the URB to the correct ED */
    spin_lock_irqsave(&imx21->lock, flags);
    if (usb_pipeisoc(urb->pipe)) {
        int i;
        for (i = 0; i < urb->number_of_packets; i++) {
            urb->iso_frame_desc[i].actual_length = 0;
            urb->iso_frame_desc[i].status = -EXDEV;
        }
    }

    urb->status = -EINPROGRESS;
    urb->actual_length = 0;
    urb->error_count = 0;
    if (ed->pipe_head) {
        __list_add(&urb->urb_list, ed->urb_queue.prev, &(ed->urb_queue)); 
    } else {
        ed->pipe_head = urb;
        qu_queue_active_urb(imx21, urb, ed);
    }

    spin_unlock_irqrestore(&imx21->lock, flags);

    return 0;
}

static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
{
    _cp;
    return -ENODEV;
}

static void imx21_hc_endpoint_disable(struct usb_hcd *hcd, 
                                      struct usb_host_endpoint *ep)
{
    _cp;
}

static int imx21_hc_get_frame(struct usb_hcd *hcd)
{
    _cp;
    return 0;
}

/*
 * Checks the root hub's status for changes.
 *
 * If a port has changed, then the appropriate bit in buf is
 * set.
 *
 * The function returns the number of ports that have changed.
 *
 * For the IMX21, there is only one port. 
 */
static int imx21_hc_hub_status_data(struct usb_hcd *hcd, char *buf)
{
    struct imx21 *imx21 = hcd_to_imx21(hcd);
    int ports;
    int changed = 0;
    int i;
    unsigned long flags;

//    _cp;

    spin_lock_irqsave(&imx21->lock, flags);
    ports = (USBH_ROOTHUBA & USBH_ROOTHUBA_NDNSTMPRT_MASK);
// Ensure ports < 7
    for (i = 0; i < ports; i++) {
        if (USBH_PORTSTAT(i) & (USBH_PORTSTAT_CONNECTSC |
                                USBH_PORTSTAT_PRTENBLSC |
                                USBH_PORTSTAT_PRTSTATSC |
                                USBH_PORTSTAT_OVRCURIC |
                                USBH_PORTSTAT_PRTRSTSC)) {
            _cp;
            printk("    port %d (of %d): 0x%x\n", i, ports, USBH_PORTSTAT(i));
            changed = 1;
            buf[0] |= 1 << (i + 1);
        }
    }

    spin_unlock_irqrestore(&imx21->lock, flags);

    if (changed != 0) {
        return 1;
    } else {
        return 0;
    }
}

static int imx21_hc_hub_control(struct usb_hcd *hcd,
                                u16             typeReq,
                                u16             wValue,
                                u16             wIndex,
                                char            *buf,
                                u16             wLength)
{
    int rc = -ENODEV;
    _cp;

    switch(typeReq) {
    case ClearHubFeature:
        DBG("  ClearHubFeature\n");
        switch(wValue) {
        case C_HUB_OVER_CURRENT:
            DBG("    C_HUB_OVER_CURRENT\n");
            rc = 0;
            break;
        case C_HUB_LOCAL_POWER:
            DBG("    C_HUB_LOCAL_POWER\n");
            rc = 0;
            break;
        default:
            DBG("    default\n");
            break;
        }
        break;

    case ClearPortFeature:
        DBG("  ClearPortFeature\n");
// Need to check wIndex boundary
        switch (wValue) {
        case USB_PORT_FEAT_ENABLE:
            DBG("    USB_PORT_FEAT_ENABLE\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_CURCONST;
            rc = 0;
            break;
        case USB_PORT_FEAT_SUSPEND:
            DBG("    USB_PORT_FEAT_SUSPEND\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTOVRCURI;
            rc = 0;
            break;
        case USB_PORT_FEAT_POWER:
            DBG("    USB_PORT_FEAT_POWER\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_LSDEVCON;
            rc = 0;
            break;
        case USB_PORT_FEAT_C_ENABLE:
            DBG("    USB_PORT_FEAT_C_ENABLE\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTENBLSC;
            rc = 0;
            break;
        case USB_PORT_FEAT_C_SUSPEND:
            DBG("    USB_PORT_FEAT_C_SUSPEND\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTSTATSC;
            rc = 0;
            break;
        case USB_PORT_FEAT_C_CONNECTION:
            DBG("    USB_PORT_FEAT_C_CONNECTION\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_CONNECTSC;
            rc = 0;
            break;
        case USB_PORT_FEAT_C_OVER_CURRENT:
            DBG("    USB_PORT_FEAT_C_OVER_CURRENT\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_OVRCURIC;
            rc = 0;
            break;
        case USB_PORT_FEAT_C_RESET:
            DBG("    USB_PORT_FEAT_C_RESET\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTRSTSC;
            rc = 0;
            break;
        default:
            DBG("    default\n");
            break;
        }

        break;

    case GetHubDescriptor:
        DBG("  GetHubDescriptor\n");
        rc = imx21_hc_hub_descriptor(hcd, (void *)buf);
        break;

    case GetHubStatus:
        DBG("  GetHubStatus\n");
        *(__le32*)buf = 0;
        rc = 0;
        break;

    case GetPortStatus:
        DBG("  GetPortStatus: port: %d, 0x%x\n", 
               wIndex, USBH_PORTSTAT(wIndex - 1));
// Need to check wIndex boundary
        *(__le32*)buf = USBH_PORTSTAT(wIndex - 1);
        rc = 0;
        break;

    case SetHubFeature:
        DBG("  SetHubFeature\n");
        switch (wValue) {
        case C_HUB_OVER_CURRENT:
            DBG("    C_HUB_OVER_CURRENT\n");
            break;

        case C_HUB_LOCAL_POWER:
            DBG("    C_HUB_LOCAL_POWER\n");
            break;
        default:
            DBG("    default\n");
            break;
        }

        break;

    case SetPortFeature:
        DBG("  SetPortFeature\n");
        switch (wValue) {
// Need to check wIndex boundary
        case USB_PORT_FEAT_SUSPEND:
            DBG("    USB_PORT_FEAT_SUSPEND\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTSUSPST;
            rc = 0;
            break;
        case USB_PORT_FEAT_POWER:
            DBG("    USB_PORT_FEAT_POWER\n");
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTPWRST;
            rc = 0;
            break;
        case USB_PORT_FEAT_RESET:
            DBG("    USB_PORT_FEAT_RESET\n");
            dump_regs();
            USBH_PORTSTAT(wIndex - 1) = USBH_PORTSTAT_PRTRSTST;
            rc = 0;
            break;
        default:
            DBG("    default\n");
            break;
        }
        break;
        
    default:
        DBG("  default\n");
        break;
    }
    
    return -rc;
}


static struct hc_driver imx21_hc_driver = {
    .description = hcd_name,
    .product_desc = "IMX21 USB Host Controller",
    .hcd_priv_size = sizeof(struct imx21),

    .flags = HCD_USB11,   /* USB 1.1 */
    .irq = imx21_irq,

    .reset = imx21_hc_reset,
    .start = imx21_hc_start,
    .stop  = imx21_hc_stop,

    /* I/O requests */
    .urb_enqueue = imx21_hc_urb_enqueue,
    .urb_dequeue = imx21_hc_urb_dequeue,
    .endpoint_disable = imx21_hc_endpoint_disable,

    /* scheduling support */
    .get_frame_number = imx21_hc_get_frame,

    /* Root hub support */
    .hub_status_data = imx21_hc_hub_status_data,
    .hub_control = imx21_hc_hub_control,
/* For PM
    .hub_suspend = ;
    .hub_resume = ;
*/
};
    

static int imx21_remove(struct platform_device *pdev)
{
    _cp;
    return 0;
}


static int imx21_probe(struct platform_device *pdev)
{
    struct usb_hcd *hcd;
    struct imx21 *imx21;
    int rc;
    int i;

    _cp;

    /* Clear the ETDs */
    for (i = 0; i < USB_NUM_ETD; i++) {
        volatile u32 *etd;
        etd = &USB_ETD(i);
        etd[0] = 0;
        etd[1] = 0;
        etd[2] = 0;
        etd[3] = 0;
    }

    /* Take the HC out of reset */
    USBH_HOST_CTRL = (USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL | 
                      USBH_HOST_CTRL_CTLBLKSR_1);

    USBH_PORTSTAT(0) = USBH_PORTSTAT_PRTPWRST;
    USBH_PORTSTAT(1) = USBH_PORTSTAT_PRTPWRST;
    USBH_PORTSTAT(2) = USBH_PORTSTAT_PRTPWRST;

    USBH_PORTSTAT(0) = USBH_PORTSTAT_PRTENABST;
    USBH_PORTSTAT(1) = USBH_PORTSTAT_PRTENABST;
    USBH_PORTSTAT(2) = USBH_PORTSTAT_PRTENABST;

    hcd = usb_create_hcd(&imx21_hc_driver, &pdev->dev, pdev->dev.bus_id);
    if (hcd == NULL) {
        printk("%s:%d No platform_data available\n", __FUNCTION__, __LINE__);
        return -ENOMEM;
    }

    imx21 = hcd_to_imx21(hcd);
    memset(imx21, 0, sizeof(struct imx21));
    imx21->board = pdev->dev.platform_data;
    spin_lock_init(&imx21->lock);
    INIT_LIST_HEAD(&imx21->ctrl_list);
    INIT_LIST_HEAD(&imx21->bulk_list);
    INIT_LIST_HEAD(&imx21->iso_list);
    INIT_LIST_HEAD(&imx21->intr_list);
    for (i = 0; i < NUM_ETD; i++) {
        INIT_LIST_HEAD(&(imx21->ed[i].urb_queue));
        imx21->ed[i].pipe_head = NULL;
    }
    init_waitqueue_head(&imx21->waitq);

    rc = usb_add_hcd(hcd, INT_USBHOST, SA_INTERRUPT);
    if (rc != 0) {
        printk("Error adding HCD.\n");
        usb_remove_hcd(hcd);
        return rc;
    }

#if 0
    rc = imx21_hc_start(hcd);
    if (rc < 0) {
        return -ENODEV;
    }
#endif    
    _cp;
    return 0;
}

static struct platform_driver imx21_hcd_driver = {
    .driver = {
        .name = (char *)hcd_name,
    },
    .probe  = imx21_probe,
    .remove = imx21_remove,
    .suspend = NULL,
    .resume = NULL,
};

static int __init imx21_hcd_init(void)
{
    _cp;
    return platform_driver_register(&imx21_hcd_driver);
}

static void __exit imx21_hcd_cleanup(void)
{
    _cp;
    platform_driver_unregister(&imx21_hcd_driver);
}

module_init(imx21_hcd_init);
module_exit(imx21_hcd_cleanup);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.