/*
**************************************************************************
*                              NXP USB Host Stack
*
*              (c) Copyright 2008, NXP SemiConductors
*              (c) Copyright 2008, OnChip  Technologies LLC
*                          All Rights Reserved
*
*                             www.nxp.com
*                        www.onchiptech.com
*
* File           : usbhost_lpc2468.c
* Programmer(s)  : Ravikanth.P
* Version        :
*
*************************************************************************
*/
/*
*************************************************************************
*                   INCLUDE HEADER FILES
*************************************************************************
*/

#include  "usbhost_lpc2388.h"
#include  "../gpdma.h"

#define	SCHED_DELAY	8	/* Scheule ISO transfer 8ms later */
#define	TD_FRAMES	8	/* We have 8 frames per TD */

typedef struct {
    USB_INT08U td_UserBuffer[USB_DMA_BUFSIZE+CODEC_DMA_BUFSIZE];
    HCCA td_Hcca __attribute__((aligned(256)));
    HCTD td_TDHead;
    HCTD td_TDTail;
    HCED td_EDCtrl;
    HCED td_EDIsoOut;
    HCED td_EDBulkIn;
    HCED td_EDBulkOut;
    USB_INT08U td_ParamBuff[32];
    USB_INT08U td_TDBuffer[256];
    HCISOTD td_IsoTDs[NUM_TD];
} t_usb_buffer __attribute__((aligned(256)));

t_usb_buffer _d_UsbBuffer __attribute__((section(".usbram")));

/*
************************************************************************
*                    GLOBAL VARIABLES
************************************************************************
*/

volatile  USB_INT32U   HOST_RhscIntr = 0;   /* Root Hub Status Change interrupt                       */
volatile  HCED  *EDCtrl;     /* Control endpoint descriptor structure */
volatile  HCED  *EDBulkIn;   /* BulkIn endpoint descriptor  structure */
volatile  HCED  *EDBulkOut;  /* BulkOut endpoint descriptor structure */
volatile  HCED	*EDIsoOut;   /* IsoOut endpoint descriptor structure */
volatile  HCTD  *TDHead;     /* Head transfer descriptor structure */
volatile  HCTD  *TDTail;     /* Tail transfer descriptor structure */
volatile  HCCA  *Hcca;       /* Host Controller Communications Area structure */
volatile  HCISOTD *IsoTop;   /* Top of ISO Transfer descriptor */
volatile  USB_INT08U  *TDBuffer;   /* Current Buffer Pointer of transfer descriptor */
volatile  USB_INT08U  *ParamBuffer; /* Buffer used by application  */
volatile  USB_INT08U  *UserBuffer; /* Buffer used by application  */

static int next_fn;
static void pcmb_init();

/*
**************************************************************************
*                           DELAY IN MILLI SECONDS
*
* Description: This function provides a delay in milli seconds
*
* Arguments  : delay    The delay required
*
* Returns    : None
*
**************************************************************************
*/

void  Host_DelayMS (USB_INT32U  delay)
{
    dly_tsk(delay);
}

/*
**************************************************************************
*                        DELAY IN MICRO SECONDS
*
* Description: This function provides a delay in micro seconds
*
* Arguments  : delay    The delay required
*
* Returns    : None
*
**************************************************************************
*/

void  Host_DelayUS (USB_INT32U  delay)
{
    volatile  USB_INT32U  i;


    for (i = 0; i < (4 * delay); i++) {    /* This logic was tested. It gives app. 1 micro sec delay        */
        ;
    }
}

void Host_HCCASet(volatile HCCA *hcca, volatile HCED *ed);

/*
***************************************************************************
*                        INITIALIZE THE HOST CONTROLLER
*
* Description: This function initializes lpc2468 host controller
*
* Arguments  : None
*
* Returns    :
*
***************************************************************************
*/
void  Host_Init (void)
{
    UW fitbit;

    PRINT_Log("Initializing Host Stack\n\r");
    Hcca       = (volatile  HCCA       *)&_d_UsbBuffer.td_Hcca;
    TDHead     = (volatile  HCTD       *)&_d_UsbBuffer.td_TDHead;
    TDTail     = (volatile  HCTD       *)&_d_UsbBuffer.td_TDTail;
    EDCtrl     = (volatile  HCED       *)&_d_UsbBuffer.td_EDCtrl;
    EDBulkIn   = (volatile  HCED       *)&_d_UsbBuffer.td_EDBulkIn;
    EDBulkOut  = (volatile  HCED       *)&_d_UsbBuffer.td_EDBulkOut;
    EDIsoOut   = (volatile  HCED       *)&_d_UsbBuffer.td_EDIsoOut;
    ParamBuffer= (volatile  USB_INT08U *)&_d_UsbBuffer.td_ParamBuff[0];
    TDBuffer   = (volatile  USB_INT08U *)&_d_UsbBuffer.td_TDBuffer[0];
    IsoTop     = (volatile HCISOTD     *)&_d_UsbBuffer.td_IsoTDs[0];
    UserBuffer = (volatile  USB_INT08U *)&_d_UsbBuffer.td_UserBuffer[0];

    next_fn = -1;

    /* Initialize all the TDs, EDs and HCCA to 0  */
    Host_EDInit(EDCtrl);
    Host_EDInit(EDBulkIn);
    Host_EDInit(EDBulkOut);
    Host_TDInit(TDHead);
    Host_TDInit(TDTail);
    Host_IsoTDInit(IsoTop, 0);
    Host_IsoEDInit(EDIsoOut, IsoTop);
    Host_HCCAInit(Hcca);

    Host_DelayMS(50);           /* Wait 50 ms before apply reset */
    HcControl       = 0;        /* HARDWARE RESET   */
    HcControlHeadED = 0;        /* Initialize Control list head to Zero */
    HcBulkHeadED    = 0;        /* Initialize Bulk list head to Zero */

                                /* SOFTWARE RESET              */
    HcCommandStatus = OR_CMD_STATUS_HCR;
    /* Write Fm Interval and Largest Data Packet Counter */
    fitbit = (HcFmInterval ^ 0x80000000) & 0x80000000;
    HcFmInterval    = DEFAULT_FMINTERVAL | fitbit;

    /* Put HC in operational state */
    HcControl  = (HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
HC_RH_DESCB = (1 << 0x01);
    HcRhStatus = OR_RH_STATUS_LPSC;       /* Set Global Power */

    HcHCCA = (USB_INT32U)Hcca;
    HcInterruptStatus |= HcInterruptStatus;   /* Clear Interrrupt Status */
    /* Enable  interrupts  */
    HcInterruptEnable  = OR_INTR_ENABLE_MIE |
                         OR_INTR_ENABLE_WDH |
                         OR_INTR_ENABLE_RHSC;

    HcPeriodicStart = 0x2000;
    Host_HCCASet(Hcca, EDIsoOut);
    PRINT_Log("Host Initialized\n\r");
    ena_int(INHNO_USB);
}

/*
**************************************************************************
*                            INTERRUPT SERVICE ROUTINE
*
* Description: This function services the interrupt caused by host controller
*
* Arguments  : None
*
* Returns    : None
*
**************************************************************************
*/

void  Host_Isr (void)
{
    USB_INT32U   int_status;
    USB_INT32U   ie_status;
    volatile HCCA *hcca = Hcca;
    HCTD *td;

    if (USB_INT_STAT & 0x00000008) {	/* Interrupt from USB Host */
        int_status = HcInterruptStatus; /* Read Interrupt Status */
        ie_status = HcInterruptEnable;  /* Read Interrupt enable status	*/ 

        if (!(int_status & ie_status)) {
            return;
        } else {
            int_status = int_status & ie_status;

            if (int_status & OR_INTR_STATUS_RHSC) {
                 /* Root hub status change interrupt	 */
                if (HcRhPortStatus1 & OR_RH_PORT_CSC) { /* Port1 */
                    if (HcRhPortStatus1 & OR_RH_PORT_CCS) {
                        HOST_RhscIntr = 1;
                        iset_flg(FLG_USBHUB, PORT1_INS);
                    } else {
                        HOST_RhscIntr = 0;
#ifdef VDEBUG
                        PRINT_Log("Port1 Device Disconnected\n\r");
#endif
                        iset_flg(FLG_USBHUB, PORT1_REM);
                    }
                    HcRhPortStatus1 = OR_RH_PORT_CSC;
                }
                if (HcRhPortStatus1 & OR_RH_PORT_PRSC) {
                    HcRhPortStatus1 = OR_RH_PORT_PRSC;
                }

                if (HcRhPortStatus2 & OR_RH_PORT_CSC) { /* Port2 */
                    if (HcRhPortStatus2 & OR_RH_PORT_CCS) {
                        HOST_RhscIntr = 2;
                        iset_flg(FLG_USBHUB, PORT2_INS);
                    } else {
                        HOST_RhscIntr = 0;
#ifdef VDEBUG
                        PRINT_Log("Port2 Device Disconnected\n\r");
#endif
                        iset_flg(FLG_USBHUB, PORT2_REM);
                    }
                    HcRhPortStatus2 = OR_RH_PORT_CSC;
                }
                if (HcRhPortStatus2 & OR_RH_PORT_PRSC) {
                    HcRhPortStatus2 = OR_RH_PORT_PRSC;
                }
            }
            if (int_status & OR_INTR_STATUS_WDH) {
                  /* Writeback Done Head interrupt */
                td = (HCTD *) hcca->DoneHead;
                while (td) {
                    if (td >= (HCTD *)IsoTop) {
                        iso_loop++;
                        isig_sem(SEM_PCMISO);
                        if (--in_dma == 0) {
                            HcControl &= ~(OR_CONTROL_IE|OR_CONTROL_PLE);
                            in_halt++;
                        }
                    } else
                        isig_sem(SEM_USBHOST);
                    td = (HCTD *) td->Next;
                }
            }
            HcInterruptStatus = int_status;
            /* Clear interrupt status register	  */
        }
    }
}

/*
**************************************************************************************************************
*                                     PROCESS TRANSFER DESCRIPTOR
*
* Description: This function processes the transfer descriptor
*
* Arguments  : ed            Endpoint descriptor that contains this transfer descriptor
*              token         SETUP, IN, OUT
*              buffer        Current Buffer Pointer of the transfer descriptor
*              buffer_len    Length of the buffer
*
* Returns    : OK       if TD submission is successful
*              ERROR    if TD submission fails
*
**************************************************************************************************************
*/

USB_INT32S  Host_ProcessTD (volatile  HCED       *ed,
                            volatile  USB_INT32U  token,
                            volatile  USB_INT08U *buffer,
                                      USB_INT32U  buffer_len)
{
    volatile  USB_INT32U   td_toggle;


    if (ed == EDCtrl) {
        if (token == TD_SETUP) {
            td_toggle = TD_TOGGLE_0;
        } else {
            td_toggle = TD_TOGGLE_1;
        }
    } else {
        td_toggle = 0;
    }
    TDHead->Control = (TD_ROUNDING    |
                      token           |
                      TD_DELAY_INT(0) |
                      td_toggle       |
                      TD_CC);
    TDTail->Control = 0;
    TDHead->CurrBufPtr   = (USB_INT32U) buffer;
    TDTail->CurrBufPtr   = 0;
    TDHead->Next         = (USB_INT32U) TDTail;
    TDTail->Next         = 0;
    TDHead->BufEnd       = (USB_INT32U)(buffer + (buffer_len - 1));
    TDTail->BufEnd       = 0;

    ed->HeadTd  = (USB_INT32U)TDHead | ((ed->HeadTd) & 0x00000002);
    ed->TailTd  = (USB_INT32U)TDTail;
    ed->Next    = 0;

    if (ed == EDCtrl) {
        HcControlHeadED = (USB_INT32U)ed;
        HcCommandStatus = HcCommandStatus | OR_CMD_STATUS_CLF;
        HcControl       = HcControl       | OR_CONTROL_CLE;
    } else {
#ifdef USE_BULK
        HcBulkHeadED    = (USB_INT32U)ed;
        HcCommandStatus = HcCommandStatus | OR_CMD_STATUS_BLF;
        HcControl       = HcControl       | OR_CONTROL_BLE;
#endif
    }

    Host_WDHWait();

    if (!(TDHead->Control & 0xF0000000)) {
        return (OK);
    } else {
        return (ERR_TD_FAIL);
    }
}

/*
***************************************************************************
*                               ENUMERATE THE DEVICE
*
* Description: This function is used to enumerate the device connected
*
* Arguments  : None
*
* Returns    : None
*
***************************************************************************
*/

USB_INT32S  Host_EnumDev (int debug)
{
    USB_INT32S  rc;

    if (debug) PRINT_Log("Connect a Audio device\n\r");
    while (!HOST_RhscIntr);

    /* USB 2.0 spec says atleast 50ms delay beore port reset */
    Host_DelayMS(50);

    if(HOST_RhscIntr==1){
        HcRhPortStatus1 |= OR_RH_PORT_PRS; /* Apply port reset */
    }else if(HOST_RhscIntr==2){
        HcRhPortStatus2 |= OR_RH_PORT_PRS; /* Apply port reset */
    }
    Host_DelayMS(100);        /* Wait for 100 MS after port reset  */
if (debug) cons_printf("Port = %d\n", HOST_RhscIntr);

    EDCtrl->Control = 8 << 16; /* Put max pkt size = 8 */

    /* Read first 8 bytes of device desc */
    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 8);
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    EDCtrl->Control = TDBuffer[7] << 16;  /* Get max pkt size of endpoint 0*/
    rc = HOST_SET_ADDRESS(1);             /* Set the device address to 1 */
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    Host_DelayMS(2);
    EDCtrl->Control = (EDCtrl->Control) | 1;  /* Modify control pipe with address 1 */
    /* Get the configuration descriptor   */
    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, 9);
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    /* Get the first configuration data  */
    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, ReadLE16U(&TDBuffer[2]));
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    rc = Audio_ParseConfiguration();    /* Parse the configuration */
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    rc = USBH_SET_CONFIGURATION(1);  /* Select device configuration 1 */
    if (rc != OK) {
        PRINT_Err(rc);
    }
    Host_DelayMS(100);           /* Some devices may require this delay */
    return (rc);
}

/*
***************************************************************************
*                                        RECEIVE THE CONTROL INFORMATION
*
* Description: This function is used to receive the control information
*
* Arguments  : bm_request_type
*              b_request
*              w_value
*              w_index
*              w_length
*              buffer
*
* Returns    : OK       if Success
*              ERROR    if Failed
*
**************************************************************************
*/

USB_INT32S  Host_CtrlRecv (         USB_INT08U   bm_request_type,
                                    USB_INT08U   b_request,
                                    USB_INT16U   w_value,
                                    USB_INT16U   w_index,
                                    USB_INT16U   w_length,
                          volatile  USB_INT08U  *buffer)
{
    USB_INT32S  rc;


    Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length);
    rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8);
    if (rc == OK) {
        if (w_length) {
            rc = Host_ProcessTD(EDCtrl, TD_IN, buffer, w_length);
        }
        if (rc == OK) {
            rc = Host_ProcessTD(EDCtrl, TD_OUT, NULL, 0);
        }
    }
    return (rc);
}

/*
**************************************************************************************************************
*                                         SEND THE CONTROL INFORMATION
*
* Description: This function is used to send the control information
*
* Arguments  : None
*
* Returns    : OK		      if Success
*              ERR_INVALID_BOOTSIG    if Failed
*
**************************************************************************************************************
*/

USB_INT32S  Host_CtrlSend (          USB_INT08U   bm_request_type,
                                     USB_INT08U   b_request,
                                     USB_INT16U   w_value,
                                     USB_INT16U   w_index,
                                     USB_INT16U   w_length,
                           volatile  USB_INT08U  *buffer)
{
    USB_INT32S  rc;


    Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length);

    rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8);
    if (rc == OK) {
        if (w_length) {
            rc = Host_ProcessTD(EDCtrl, TD_OUT, buffer, w_length);
        }
        if (rc == OK) {
            rc = Host_ProcessTD(EDCtrl, TD_IN, NULL, 0);
        }
    }
    return (rc);
}

/*
**************************************************************************************************************
*                                          FILL SETUP PACKET
*
* Description: This function is used to fill the setup packet
*
* Arguments  : None
*
* Returns    : OK		      if Success
*              ERR_INVALID_BOOTSIG    if Failed
*
**************************************************************************************************************
*/

void  Host_FillSetup (USB_INT08U   bm_request_type,
                      USB_INT08U   b_request,
                      USB_INT16U   w_value,
                      USB_INT16U   w_index,
                      USB_INT16U   w_length)
{
    TDBuffer[0] = bm_request_type;
    TDBuffer[1] = b_request;
    WriteLE16U(&TDBuffer[2], w_value);
    WriteLE16U(&TDBuffer[4], w_index);
    WriteLE16U(&TDBuffer[6], w_length);
}

/*
**************************************************************************************************************
*                                         INITIALIZE THE TRANSFER DESCRIPTOR
*
* Description: This function initializes transfer descriptor
*
* Arguments  : Pointer to TD structure
*
* Returns    : None
*
**************************************************************************************************************
*/

void  Host_TDInit (volatile  HCTD *td)
{
    td->Control    = 0;
    td->CurrBufPtr = 0;
    td->Next       = 0;
    td->BufEnd     = 0;
}


void  Host_IsoTDInit(volatile HCISOTD *td, int frame_number)
{
    int i;

    for (i = 0; i < NUM_TD; i++) {
        td->Control = 0;
        td->Next = 0;
        td++;
    }
}

/*
**************************************************************************************************************
*                                         INITIALIZE THE ENDPOINT DESCRIPTOR
*
* Description: This function initializes endpoint descriptor
*
* Arguments  : Pointer to ED strcuture
*
* Returns    : None
*
**************************************************************************************************************
*/

void  Host_EDInit (volatile  HCED *ed)
{
    ed->Control = 0;
    ed->TailTd  = 0;
    ed->HeadTd  = 0;
    ed->Next    = 0;
}

#define	DIR_OUT	1

/*
 * Iso Endpoint Descriptor. Four TDs are linked to this ED.
 */
void Host_IsoEDInit (volatile HCED *ed, volatile HCISOTD *td)
{
    /* F=1, D=1, FA=1 */
    ed->Control = (192<<16) | (1<<15) | (DIR_OUT<<11)| (EP_ISO<<7) | 1;
    ed->HeadTd = ed->TailTd = (USB_INT32U) td;
    ed->Next    = 0;
#ifdef VDEBUG
    syslog(LOG_NOTICE, "IsoEDInit (@%x): %x, %x", ed, ed->Control, ed->HeadTd);
#endif
}

void ed_dump()
{
    volatile HCED *ed = EDIsoOut;

    cons_printf("ED @%08x:", ed);
    cons_printf("Control: %08x, ", ed->Control);
    cons_printf("HeadTd: %08x, ", ed->HeadTd);
    cons_printf("TailTd: %08x, ", ed->TailTd);
    cons_printf("Next: %08x\n", ed->Next);
}

void td_dump()
{
    volatile HCISOTD *td = IsoTop;
    UW *wp;
    int i, nt;

    wp = (UW *)IsoTop;
    for (nt = 0; nt < 4; nt++) {
        cons_printf("TD %d@%08x\n", nt, td);
        cons_printf("  %08x %08x %08x %08x\n", wp[0], wp[1], wp[2], wp[3]);
        cons_printf("  %08x %08x %08x %08x\n", wp[4], wp[5], wp[6], wp[7]);
        wp += 8;
        cons_printf("  Control:  %08x,  BP0: %08x\n", td->Control, td->BP0);
        cons_printf("  Next: %08x,  BufEnd: %08x\n", td->Next, td->BufEnd);
        cons_printf("  Offset\n");
        for (i = 0; i < 8; i++) {
            cons_printf("  %d: %04x", i, td->Offset[i]);
            if (i == 3) cons_printf("\n");
        }
        cons_printf("\n");
        td++;
    }
}

/*
**************************************************************************************************************
*                                 INITIALIZE HOST CONTROLLER COMMUNICATIONS AREA
*
* Description: This function initializes host controller communications area
*
* Arguments  : Pointer to HCCA
*
* Returns    :
*
**************************************************************************************************************
*/

void  Host_HCCAInit (volatile  HCCA  *hcca)
{
    USB_INT32U  i;


    for (i = 0; i < 32; i++) {
        hcca->IntTable[i] = 0;
        hcca->FrameNumber = 0;
        hcca->DoneHead    = 0;
    }
}

void Host_HCCASet(volatile HCCA *hcca, volatile HCED *ed)
{
    USB_INT32U  i, n;

    n = 0;
    Host_IsoTDInit(IsoTop, 0);
    for (i = 0; i < 32; i++) {
        hcca->IntTable[i] = (USB_INT32U) ed;
    }
}

#define	CC_MASK		(0x07 << 13)
#define	NOT_ACCESSED	(0x07 << 13)
#define	ISO_UNIT	192

static void usb_pcm_output(pcm_data *pcm)
{
    int room, len;
    int limit, idx;
    volatile HCISOTD *td;
    volatile HCISOTD *wtd;
    USB_INT32U offset;
    volatile HCED *ed = EDIsoOut;
    int err;
    UB *buffer = (UB *)pcm->raw_buffer;
    struct pcmb_desc *pcmb = &pcmbufstate;

#ifdef DEBUG
    pcmb->frames++;
    if (abort_play)
        return;
#endif
    len = pcm->samples << 1;
    while (len > 0) {
        td = IsoTop + (pcmb->index >> 3);
        idx = pcmb->index & 7;
        if (pcmb->putpos == 0) {
            if (idx == 0) {  /* Need to get TD and buffer */
#ifdef DEBUG
                err = twai_sem(SEM_PCMISO, 256);
                if (err != E_OK) {
                    cons_printf("fn = %x, %x\n", next_fn, Hcca->FrameNumber);
                    cons_printf("TMOUT (%d): SEM_PCMISO (%d, %d)\n", err, iso_loop, pcmb->copy_count);
                    cons_printf("index = %d, putpos = %d\n", pcmb->index, pcmb->putpos);
                    cons_printf("HcInterruptStatus: %x\n", HcInterruptStatus);
                    ed_dump();
                    td_dump();
                    abort_play = 1;
                    return;
                }
#else
                wai_sem(SEM_PCMISO);
#endif
                if (abort_play) return;
                td->BP0 = (USB_INT32U) UserBuffer;
            }
            offset = (USB_INT32U)(pcmb->putptr) - (USB_INT32U)UserBuffer;
            if (offset >= 4096)
                td->Offset[idx] = NOT_ACCESSED | (1<<12) | (offset - 4096);
            else
                td->Offset[idx] = NOT_ACCESSED | (0<<12) | (offset & 0xfff);
        }
        /* Determine how many bytes we need to fill in current buffer */
        if (pcm->rate == 44100) {
            limit = (pcmb->count_10ms == 8)? 180: 176;
        } else {
            limit = ISO_UNIT;
        }
        room = limit - pcmb->putpos;
        if (len < room) {	/* All bytes fits in current buffer */
            memcpy(pcmb->putptr, buffer, len);
            pcmb->putpos += len;
            pcmb->putptr += len;
            return;
        }
        /* Fill upto end of current buffer */
        memcpy(pcmb->putptr, buffer, room);
        pcmb->putptr += room;
        pcmb->putpos = 0;
        buffer += room;
        if (idx == 7) {	/* This is last buffer in the TD */
            td->BufEnd = (USB_INT32U) (pcmb->putptr - 1);
            if (next_fn < 0) {	/* Initialize next_fn if necessary */
                next_fn = (Hcca->FrameNumber + SCHED_DELAY) & 0xffff;
            }
            /* Find next TD */
            if (pcmb->index == 31) {
                wtd = IsoTop;
                pcmb->index = 0;
                pcmb->putptr = (UB *) UserBuffer;
            } else {
                wtd = td + 1;
                pcmb->index++;
            }
#ifdef XDEBUG
cons_printf("index: %d, putpos: %d, putptr: %x, End: %x\n", pcmb->index, pcmb->putpos, pcmb->putptr, td->BufEnd);
#endif
            wtd->Next = wtd->Control = 0;
            td->Next = (USB_INT32U) wtd;
            td->Control = (0x0f << 28) | (7 << 24) | next_fn;
            next_fn += TD_FRAMES;
            next_fn &= 0xffff;
            ed->TailTd = (USB_INT32U) wtd;

            if (!(HcControl & OR_CONTROL_IE))
                HcControl |= (OR_CONTROL_IE | OR_CONTROL_PLE);
            in_dma++;
        } else {
            pcmb->index++;
        }
        pcmb->count_10ms++;
        if (pcmb->count_10ms == 10) {
            pcmb->count_10ms = 0;
        }
        pcmb->copy_count++;
        len -= room;
    }
}

void usb_init_output()
{
    pcmb_init();
    next_fn = -1;
    Host_IsoTDInit(IsoTop, 0);
    Host_IsoEDInit(EDIsoOut, IsoTop);
}

static void usb_stop_output()
{
}

void usb_restart_output()
{
    HCISOTD *td;

    /* Be sure to stop Iso transfer */
    HcControl &= ~(OR_CONTROL_IE | OR_CONTROL_PLE);
    /* HC will stop its work within 2ms */
    dly_tsk(2);

    /* Reschedule frame numbers */
    next_fn = (Hcca->FrameNumber + SCHED_DELAY) & 0xffff;
    for (td = (HCISOTD *)EDIsoOut->HeadTd; td ; td = (HCISOTD *) td->Next) {
        td->Control = (td->Control & 0xffff0000) | next_fn;
        next_fn += TD_FRAMES;
        next_fn &= 0xffff;
    }
    HcControl |= (OR_CONTROL_IE | OR_CONTROL_PLE);
}

/*
**************************************************************************************************************
*                                         WAIT FOR WDH INTERRUPT
*
* Description: This function is infinite loop which breaks when ever a WDH interrupt rises
*
* Arguments  : None
*
* Returns    : None
*
**************************************************************************************************************
*/

void  Host_WDHWait (void)
{
  wai_sem(SEM_USBHOST);
}

/*
**************************************************************************************************************
*                                         READ LE 32U
*
* Description: This function is used to read an unsigned integer from a charecter buffer in the platform
*              containing little endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*
* Returns    : val     Unsigned integer
*
**************************************************************************************************************
*/

USB_INT32U  ReadLE32U (volatile  USB_INT08U  *pmem)
{
    USB_INT32U   val;

    ((USB_INT08U *)&val)[0] = pmem[0];
    ((USB_INT08U *)&val)[1] = pmem[1];
    ((USB_INT08U *)&val)[2] = pmem[2];
    ((USB_INT08U *)&val)[3] = pmem[3];

    return (val);
}

/*
**************************************************************************************************************
*                                        WRITE LE 32U
*
* Description: This function is used to write an unsigned integer into a charecter buffer in the platform
*              containing little endian processor.
*
* Arguments  : pmem    Pointer to the charecter buffer
*              val     Integer value to be placed in the charecter buffer
*
* Returns    : None
*
**************************************************************************************************************
*/

void  WriteLE32U (volatile  USB_INT08U  *pmem,
                            USB_INT32U   val)
{
    pmem[0] = ((USB_INT08U *)&val)[0];
    pmem[1] = ((USB_INT08U *)&val)[1];
    pmem[2] = ((USB_INT08U *)&val)[2];
    pmem[3] = ((USB_INT08U *)&val)[3];
}

/*
**************************************************************************************************************
*                                          READ LE 16U
*
* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform
*              containing little endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*
* Returns    : val     Unsigned short integer
*
**************************************************************************************************************
*/

USB_INT16U  ReadLE16U (volatile  USB_INT08U  *pmem)
{
    USB_INT16U   val;

    ((USB_INT08U *)&val)[0] = pmem[0];
    ((USB_INT08U *)&val)[1] = pmem[1];


    return (val);
}

/*
**************************************************************************************************************
*                                         WRITE LE 16U
*
* Description: This function is used to write an unsigned short integer into a charecter buffer in the
*              platform containing little endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*              val     Value to be placed in the charecter buffer
*
* Returns    : None
*
**************************************************************************************************************
*/

void  WriteLE16U (volatile  USB_INT08U  *pmem,
                            USB_INT16U   val)
{
    pmem[0] = ((USB_INT08U *)&val)[0];
    pmem[1] = ((USB_INT08U *)&val)[1];
}

/*
**************************************************************************************************************
*                                         READ BE 32U
*
* Description: This function is used to read an unsigned integer from a charecter buffer in the platform
*              containing big endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*
* Returns    : val     Unsigned integer
*
**************************************************************************************************************
*/

USB_INT32U  ReadBE32U (volatile  USB_INT08U  *pmem)
{
    USB_INT32U   val;

    ((USB_INT08U *)&val)[0] = pmem[3];
    ((USB_INT08U *)&val)[1] = pmem[2];
    ((USB_INT08U *)&val)[2] = pmem[1];
    ((USB_INT08U *)&val)[3] = pmem[0];

    return (val);
}

/*
**************************************************************************************************************
*                                         WRITE BE 32U
*
* Description: This function is used to write an unsigned integer into a charecter buffer in the platform
*              containing big endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*              val     Value to be placed in the charecter buffer
*
* Returns    : None
*
**************************************************************************************************************
*/

void  WriteBE32U (volatile  USB_INT08U  *pmem,
                            USB_INT32U   val)
{
    pmem[0] = ((USB_INT08U *)&val)[3];
    pmem[1] = ((USB_INT08U *)&val)[2];
    pmem[2] = ((USB_INT08U *)&val)[1];
    pmem[3] = ((USB_INT08U *)&val)[0];

}

/*
**************************************************************************************************************
*                                         READ BE 16U
*
* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform
*              containing big endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*
* Returns    : val     Unsigned short integer
*
**************************************************************************************************************
*/

USB_INT16U  ReadBE16U (volatile  USB_INT08U  *pmem)
{
    USB_INT16U  val;


    ((USB_INT08U *)&val)[0] = pmem[1];
    ((USB_INT08U *)&val)[1] = pmem[0];

    return (val);
}

/*
**************************************************************************************************************
*                                         WRITE BE 16U
*
* Description: This function is used to write an unsigned short integer into the charecter buffer in the
*              platform containing big endian processor
*
* Arguments  : pmem    Pointer to the charecter buffer
*              val     Value to be placed in the charecter buffer
*
* Returns    : None
*
**************************************************************************************************************
*/

void  WriteBE16U (volatile  USB_INT08U  *pmem,
                            USB_INT16U   val)
{
    pmem[0] = ((USB_INT08U *)&val)[1];
    pmem[1] = ((USB_INT08U *)&val)[0];
}


USB_INT08U
GetPlugStatus(void)
{
    return HOST_RhscIntr;
}

int ReadReportDescriptor()
{
    USB_INT32S  rc, nb;
    int len = 0x32;
    UB  *bp;

    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_REPORT, 0, ParamBuffer, len);
    if (rc != OK) {
        PRINT_Err(rc);
        return (rc);
    }
    bp = (UB *)ParamBuffer;
    for (nb = 0; nb < len; nb++) {
        cons_printf(" %02x", *bp++);
        if ((++nb % 16) == 0)
            cons_printf("\n");
    }
    return 0;
}
