/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: khci.c$
* $Version : 3.5.54.0$
* $Date    : Dec-9-2009$
*
* Comments:
*
*   This file contains KHCI-specific implementations of USB interfaces
*
*END************************************************************************/

#include "khci.h"

#include "mqx_host.h"

#include <lwevent.h>
#include <lwmsgq.h>

// KHCI task parameters
#define USB_KHCI_TASK_NUM_MESSAGES         16
#define USB_KHCI_TASK_TEMPLATE_INDEX       0
#define USB_KHCI_TASK_ADDRESS              _usb_khci_task
#define USB_KHCI_TASK_STACKSIZE            1600
//#define USB_KHCI_TASK_PRIORITY             8
#define USB_KHCI_TASK_NAME                 "KHCI Task"
#define USB_KHCI_TASK_ATTRIBUTES           0
#define USB_KHCI_TASK_CREATION_PARAMETER   0
#define USB_KHCI_TASK_DEFAULT_TIME_SLICE   0

// atom transaction error results
#define KHCI_ATOM_TR_PID_ERROR  (-1)
#define KHCI_ATOM_TR_EOF_ERROR  (-2)
#define KHCI_ATOM_TR_CRC_ERROR  (-4)
#define KHCI_ATOM_TR_TO         (-16)
#define KHCI_ATOM_TR_DMA_ERROR  (-32)
#define KHCI_ATOM_TR_BTS_ERROR  (-128)
#define KHCI_ATOM_TR_NAK        (-256)
#define KHCI_ATOM_TR_DATA_ERROR (-512)
#define KHCI_ATOM_TR_STALL      (-1024)
#define KHCI_ATOM_TR_RESET      (-2048)

#ifdef __ICCCF__
    #pragma segment="USB_BDT_Z"
//    #pragma location="USB_BDT_Z"
    __no_init static uint_8 bdt[512] @ "USB_BDT_Z";
#else
  #pragma define_section usb_bdt ".usb_bdt" RW
  __declspec(usb_bdt) static uint_8 bdt[512] = { 1 };    // DO NOT REMOVE INITIALIZATION !!! bug in CW (cw 7.1) - generate wrong binary code, data
#endif  

#define BDT_BASE              ((uint_32*)(bdt))
#define BD_PTR(ep, rxtx, odd)      (((uint_32)BDT_BASE) & 0xfffffe00 | ((ep & 0x0f) << 5) | ((rxtx & 1) << 4) | ((odd & 1) << 3))

// endpoint data structure
typedef struct {
    uint_8 rx_bd;
    uint_8 tx_bd;
} EP_STRUCT, _PTR_ EP_STRUCT_PTR;

/* FIXME: actually, only ep[0] is used in atom transaction */
static EP_STRUCT ep[16];

// prototypes
static void _usb_khci_process_tr_complete(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr, uint_32 remain, int_32 err);

USB_STATUS _usb_khci_init(_usb_host_handle handle);
USB_STATUS _usb_khci_shutdown(_usb_host_handle handle);
USB_STATUS _usb_khci_send(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr);
USB_STATUS _usb_khci_send_setup(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr);
USB_STATUS _usb_khci_recv(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr);
USB_STATUS _usb_khci_cancel(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr);
USB_STATUS _usb_khci_bus_control(_usb_host_handle handle, uint_8 bus_control);
uint_32 _usb_khci_get_frame_number(_usb_host_handle handle);
uint_32 _usb_khci_get_micro_frame_number(_usb_host_handle handle);

USB_STATUS _usb_khci_host_open_pipe(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr);
USB_STATUS _usb_khci_host_close_pipe(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr);

static int_32 _usb_khci_process_tr(uint_32 type, _usb_host_handle handle);
static int_32 _usb_khci_atom_tr(uint_32 type, uint_32 host_speed, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, uint_8 *buf, uint_16 len);


const USB_HOST_CALLBACK_FUNCTIONS_STRUCT _bsp_usb_host_callback_table = {
   /* The Host/Device init function */
   _usb_khci_init,

   /* The function to shutdown the host/device */
   _usb_khci_shutdown,

   /* The function to send data */
   _usb_khci_send,

   /* The function to send setup data */
   _usb_khci_send_setup,

   /* The function to receive data */
   _usb_khci_recv,
   
   /* The function to cancel the transfer */
   _usb_khci_cancel,
   
   /* The function for USB bus control */
   _usb_khci_bus_control,

   NULL,

   /* The function to close pipe */
   _usb_khci_host_close_pipe,
    
   /* Get frame number function */

   _usb_khci_get_frame_number,
   /* Get microframe number function - not applicable on FS */
   NULL,
   
   /* Open pipe function */
   _usb_khci_host_open_pipe,
   
   NULL,
   
   NULL
};

// event
static LWEVENT_STRUCT khci_event;

// KHCI event bits
#define KHCI_EVENT_ATTACH       1
#define KHCI_EVENT_RESET        2
#define KHCI_EVENT_TOK_DONE     4
#define KHCI_EVENT_SOF_TOK      8
#define KHCI_EVENT_MASK         0x0f

// max msg count for KHCI
//#define TR_QUE_MSG_CNT          10

// atom transaction type
typedef enum {
    TR_MSG_UNKNOWN,     // unknow - not used
    TR_MSG_SETUP,       // setup transaction
    TR_MSG_SEND,        // send trnasaction
    TR_MSG_RECV         // receive transaction
} TR_MSG_TYPE;

// atom transaction message
typedef struct {
    TR_MSG_TYPE type;                       // transaction type
    PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc;   // pointer to pipe descriptor
    PIPE_TR_STRUCT_PTR pipe_tr;             // pointer to transaction
} TR_MSG_STRUCT;

static PIPE_DESCRIPTOR_STRUCT_PTR last_to_pipe = NULL;

// transaction que
static _mqx_max_type tr_que[(sizeof(LWMSGQ_STRUCT) + USBCFG_KHCI_TR_QUE_MSG_CNT * sizeof(TR_MSG_STRUCT)) / sizeof(_mqx_max_type) + 1];


// max interrupt transaction count
//#define KHCI_MAX_INT_TR     10

// interrupt transaction
typedef struct tr_int_que_itm_struct {
    TIME_STRUCT   time;         /* next time to fire interrupt */
    _mqx_uint     period;       /* period in [ms] for interrupt endpoint */
    TR_MSG_STRUCT msg;          /* atom transaction message */
} TR_INT_QUE_ITM_STRUCT, _PTR_ TR_INT_QUE_ITM_STRUCT_PTR;

static TR_INT_QUE_ITM_STRUCT TR_INT_QUE[USBCFG_KHCI_MAX_INT_TR];

#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING)
static uint_32 HWTICKS_CONST_FS_ADD;
static uint_32 HWTICKS_CONST_FS_MUL;
static uint_32 HWTICKS_CONST_LS_ADD;
static uint_32 HWTICKS_CONST_LS_MUL;
static uint_32 HWTICKS_CONST_THLD;
static uint_32 HWTICKS_PER_TICK;
static uint_32 HWTICKS_PER_MSEC;
#endif

extern uint_32 _mcf5225_get_hwticks(_mqx_uint timer);

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_isr
*  Returned Value : None
*  Comments       :
*        Service all the interrupts in the kirin usb hardware
*END*-----------------------------------------------------------------*/
static void _usb_khci_isr(_usb_host_handle handle) {
    USB_HOST_STATE_STRUCT_PTR usb_host_ptr = (USB_HOST_STATE_STRUCT_PTR)handle;
    uchar status;
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
    
    while (1)
    {
        status = (uint_8)(usb_ptr->INT_STAT & usb_ptr->INT_ENB);
        
        if (!status)
            break;
        
        usb_ptr->INT_STAT = status;
    
        if (status & MCF522X_USBOTG_INT_STAT_ATTACH)
        {
            // usb device attached
            usb_ptr->INT_ENB &= ~MCF522X_USBOTG_INT_ENB_ATTACH_EN;
            _lwevent_set(&khci_event, KHCI_EVENT_ATTACH);

        }
        
        if (status & MCF522X_USBOTG_INT_STAT_TOK_DNE)
        {
            // atom transaction done - token done
            _lwevent_set(&khci_event, KHCI_EVENT_TOK_DONE);
        }
        
        if (status & MCF522X_USBOTG_INT_STAT_USB_RST)
        {
            // usb reset
            _lwevent_set(&khci_event, KHCI_EVENT_RESET);
        }
        
    };
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_init_int_tr
*  Returned Value : None
*  Comments       :
*        Initialize interrupt transaction que
*END*-----------------------------------------------------------------*/
static void _usb_khci_init_int_tr(void) {
    _mqx_int i;
    TR_INT_QUE_ITM_STRUCT_PTR tr = TR_INT_QUE;
    
    for (i = 0; i < USBCFG_KHCI_MAX_INT_TR; i++, tr++) {
        tr->msg.type = TR_MSG_UNKNOWN;
    }
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_add_int_tr
*  Returned Value : -1 que is full
*  Comments       :
*        Add new interrupt transaction to que
*END*-----------------------------------------------------------------*/
static int _usb_khci_add_int_tr(TR_MSG_STRUCT *msg, _mqx_uint period) {
    _mqx_int i = 0;
    TR_INT_QUE_ITM_STRUCT_PTR tr = TR_INT_QUE;
    TIME_STRUCT tm;
    
    // find free position
    for (i = 0; i < USBCFG_KHCI_MAX_INT_TR; i++, tr++)
    {
        if (tr->msg.type == TR_MSG_UNKNOWN)
        {
            tr->period = period;

            _time_get(&tm);
            tr->time.SECONDS = tm.SECONDS;
            tr->time.MILLISECONDS = tm.MILLISECONDS + period;

            tr->time.SECONDS += tr->time.MILLISECONDS / 1000;
            tr->time.MILLISECONDS %= 1000;

            USB_mem_copy(msg, &tr->msg, sizeof(TR_MSG_STRUCT));
            
            break;
        }
    }
    
    return (i < USBCFG_KHCI_MAX_INT_TR) ? i : -1;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_rm_int_tr
*  Returned Value : 0 successful
*  Comments       :
*        Remove interrupt transaction from que
*END*-----------------------------------------------------------------*/
static int _usb_khci_rm_int_tr(TR_MSG_STRUCT *msg) {
    _mqx_int i = 0;
    TR_INT_QUE_ITM_STRUCT_PTR tr = TR_INT_QUE;
    
    // find record
    for (i = 0; i < USBCFG_KHCI_MAX_INT_TR; i++, tr++)
    {
        if (tr->msg.pipe_desc == msg->pipe_desc && tr->msg.pipe_tr == msg->pipe_tr)
        {
            //USB_mem_zero(tr, sizeof(TR_INT_QUE_ITM_STRUCT));
            tr->msg.type = TR_MSG_UNKNOWN;    
            return 0;
        }
    }
    
    return -1;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_get_hot_int_tr
*  Returned Value : 0 successful
*  Comments       :
*        Make a message copy for transaction which need evaluation
*END*-----------------------------------------------------------------*/
static int _usb_khci_get_hot_int_tr(TR_MSG_STRUCT *msg) {
    _mqx_int i, res = -1;
    register TIME_STRUCT hot_time;
    TR_INT_QUE_ITM_STRUCT_PTR tr = TR_INT_QUE;
    TR_INT_QUE_ITM_STRUCT_PTR hot_tr = NULL;
    TIME_STRUCT tm;
    
    hot_time.SECONDS = hot_time.MILLISECONDS = 0xFFFFFFFF;

    /* find hottest transaction */
    for (i = 0; i < USBCFG_KHCI_MAX_INT_TR; i++, tr++) {
        if (tr->msg.type != TR_MSG_UNKNOWN) {
            if (hot_time.SECONDS > tr->time.SECONDS) {
                hot_time.SECONDS = tr->time.SECONDS;
                hot_time.MILLISECONDS = tr->time.MILLISECONDS;
                hot_tr = tr;
            }
            else if ((hot_time.SECONDS == tr->time.SECONDS) && (hot_time.MILLISECONDS > tr->time.MILLISECONDS)) {
                hot_time.MILLISECONDS = tr->time.MILLISECONDS;
                hot_tr = tr;
            }
        }
    }
    
    if (hot_tr) {
        /* test if hottest transaction was not the last one with timeout - if yes, don't allow to block USB transfers with timeouts */
        if (last_to_pipe == hot_tr->msg.pipe_desc) {
            last_to_pipe = NULL; //it is allowed to perform this interupt next time
            return res;
        }
        
        /* test if hottest transaction is enough hot to fire */
        _time_get(&tm);
            
        if ((hot_time.SECONDS < tm.SECONDS) || ((hot_time.SECONDS == tm.SECONDS) && (hot_time.MILLISECONDS < tm.MILLISECONDS))) {
            /* update time to next fire = now + period */
            hot_tr->time.SECONDS = tm.SECONDS;
            hot_tr->time.MILLISECONDS = tm.MILLISECONDS + hot_tr->period;

            hot_tr->time.SECONDS += hot_tr->time.MILLISECONDS / 1000;
            hot_tr->time.MILLISECONDS %= 1000;

            USB_mem_copy(&hot_tr->msg, msg, sizeof(TR_MSG_STRUCT));
            res = 0;
        }
    }
    
    return res;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_task
*  Returned Value : none
*  Comments       :
*        KHCI task
*END*-----------------------------------------------------------------*/
static void _usb_khci_task(uint_32 dev_inst_ptr) {
    USB_HOST_STATE_STRUCT_PTR usb_host_ptr = (USB_HOST_STATE_STRUCT_PTR)dev_inst_ptr;
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
    static const _mqx_uint  msg_size_in_max_type = 1 + (sizeof(TR_MSG_STRUCT) - 1) / sizeof(_mqx_max_type);
    static _mqx_uint seq_ints = 10;    
    TR_MSG_STRUCT msg;
    _mqx_uint remain = 0;
    _mqx_int res;
    uint_8 *buf;
    
    if (_lwmsgq_init((pointer)tr_que, USBCFG_KHCI_TR_QUE_MSG_CNT, msg_size_in_max_type) != MQX_OK) {
        // lwmsgq_init failed
        _mqx_exit(0);
    }
    
    _usb_khci_init_int_tr();
    
    while (1) {
        if (usb_host_ptr->DEVICE_LIST_PTR) {

            msg.type = TR_MSG_UNKNOWN;
        
            // look for interrupt transaction
            if ((_usb_khci_get_hot_int_tr(&msg) != 0) || (!seq_ints--)) {
                seq_ints = 10;
                if (_lwmsgq_receive((pointer)tr_que, (uint_32 *) &msg, LWMSGQ_RECEIVE_BLOCK_ON_EMPTY, USBCFG_KHCI_WAIT_TICK, 0) == MQX_OK)
                    if (msg.pipe_desc->PIPETYPE == USB_INTERRUPT_PIPE) 
                        _usb_khci_add_int_tr(&msg, msg.pipe_desc->INTERVAL);
            }
            
            if (msg.type != TR_MSG_UNKNOWN) {
                // evaluate message
                
                switch (msg.type) {
                    case TR_MSG_SETUP:
//                        msg.pipe_desc->NEXTDATA01 = 0; done in upper layer
                        _usb_khci_atom_tr(TR_CTRL, usb_host_ptr->speed, msg.pipe_desc, (uint_8 *) &msg.pipe_tr->setup_packet, 8);
                    
                        // send next token
                        if (msg.pipe_tr->RX_LENGTH) {
                            buf = msg.pipe_tr->RX_BUFFER;
                            remain = msg.pipe_tr->RX_LENGTH;
                        
                            while (remain > 0) {
                                res = _usb_khci_atom_tr(TR_IN, usb_host_ptr->speed, msg.pipe_desc, buf, remain);
                                
                                if (res < 0)
                                    break;
                            
                                buf += res;
                                remain -=res; 
                            }
                        
                            msg.pipe_desc->NEXTDATA01 = 1;
                            res = _usb_khci_atom_tr(TR_OUT, usb_host_ptr->speed, msg.pipe_desc, 0, 0);
                        }
                        else if (msg.pipe_tr->TX_LENGTH) {
                            buf = msg.pipe_tr->TX_BUFFER;
                            remain = msg.pipe_tr->TX_LENGTH;
                        
                            while (remain > 0) {
                                res = _usb_khci_atom_tr(TR_OUT, usb_host_ptr->speed, msg.pipe_desc, buf, remain);
                        
                                if (res < 0)
                                    break;
                            
                                buf += res;
                                remain -=res;
                            }

                            msg.pipe_desc->NEXTDATA01 = 1;
                            res = _usb_khci_atom_tr(TR_IN, usb_host_ptr->speed, msg.pipe_desc, 0, 0);
                        }
                        else {
                            msg.pipe_desc->NEXTDATA01 = 1;
                            res = _usb_khci_atom_tr(TR_IN, usb_host_ptr->speed, msg.pipe_desc, 0, 0);
                            remain = 0;
                        }
                        break;

                    case TR_MSG_RECV:
                        buf = msg.pipe_tr->RX_BUFFER;
                        remain = msg.pipe_tr->RX_LENGTH;

                        do {
                            res = _usb_khci_atom_tr(TR_IN, usb_host_ptr->speed, msg.pipe_desc, buf, remain);
                                
                            if (res < 0) 
                                break;

                            buf += res;
                            remain -=res;
                            
                            if ((remain == 0) || (res < msg.pipe_desc->MAX_PACKET_SIZE)) 
                            {
                                res = remain;
                                break;
                            }
                        
                        } while (1);
                        break;

                    case TR_MSG_SEND:
                        buf = msg.pipe_tr->TX_BUFFER;
                        remain = msg.pipe_tr->TX_LENGTH;
                    
                        do {
                            res = _usb_khci_atom_tr(TR_OUT, usb_host_ptr->speed, msg.pipe_desc, buf, remain);
                        
                            if (res < 0)
                                break;
                        
                            buf += res;
                            remain -=res;
                            
                            if ((remain == 0) || (res < msg.pipe_desc->MAX_PACKET_SIZE)) 
                            {
                                res = remain;
                                break;
                            }
                            
                        } while (1);
                        break;

                    default:
                       break;
                }
        
                if (res < 0) {
                    if (msg.pipe_desc->PIPETYPE != USB_INTERRUPT_PIPE) {    // TODO enhance condition for error (not NAK) in interrupt transaction
                        _usb_khci_process_tr_complete((pointer)usb_host_ptr, msg.pipe_desc, msg.pipe_tr, remain, res);
                    }
                }
                else {
                    _usb_khci_process_tr_complete((pointer)usb_host_ptr, msg.pipe_desc, msg.pipe_tr, remain, res);
        
                    if (msg.pipe_desc->PIPETYPE == USB_INTERRUPT_PIPE) {
                        _usb_khci_rm_int_tr(&msg);
                    }
                }
            }
        }
        else {
            // wait for event if device is not attached
            _lwevent_wait_ticks(&khci_event, KHCI_EVENT_MASK, FALSE, 0);
        }
        
        if (khci_event.VALUE & KHCI_EVENT_MASK) {
            // some event occurred
            
            if (khci_event.VALUE & KHCI_EVENT_ATTACH) {     // device attached
                usb_ptr->ADDR = 0;

                _time_delay(100);

                // speed check, set
                usb_host_ptr->speed = (uint_8)((usb_ptr->CTL & MCF522X_USBOTG_CTL_JSTATE) ? USB_SPEED_FULL : USB_SPEED_LOW);

                if (usb_host_ptr->speed == USB_SPEED_FULL) {
                    usb_ptr->ADDR |= MCF522X_USBOTG_ADDR_LS_EN;   
                }

                usb_ptr->INT_STAT = 0xff;   // clean each int flags
                usb_ptr->INT_ENB = \
                MCF522X_USBOTG_INT_ENB_TOK_DNE_EN |
//              MCF522X_USBOTG_INT_ENB_SOF_TOK_EN |
                MCF522X_USBOTG_INT_ENB_USB_RST_EN;
                usb_ptr->ENDPT0 |= MCF522X_USBOTG_ENDPT_HOST_WO_HUB;

                // bus reset
                usb_ptr->CTL |= MCF522X_USBOTG_CTL_RESET;
                _time_delay(10);
                usb_ptr->CTL &= ~MCF522X_USBOTG_CTL_RESET;

                // enable SOF sending
                usb_ptr->CTL |= MCF522X_USBOTG_CTL_USB_EN_SOF_EN;

                _time_delay(100);

                usb_dev_list_attach_device((pointer)usb_host_ptr, (uint_8)(usb_host_ptr->speed), 0, 0);
                
                _lwevent_clear(&khci_event, KHCI_EVENT_ATTACH);
            }

            if (khci_event.VALUE & KHCI_EVENT_RESET) {          // usb reset
                // test for device attach flag
                usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_ATTACH;

                if (usb_ptr->INT_STAT & MCF522X_USBOTG_INT_STAT_ATTACH) {
                    // device attached
                }
                else {
                    // device detached

                    usb_dev_list_detach_device((pointer)usb_host_ptr, 0, 0);

                    // TODO: some deinitialization and clearing in KHCI

                    // cleaning interrupt transaction queue from device is done
                    // by calling _usb_khci_host_close_pipe from upper layer
                    
                    // enable interrupt for usb attach
                    usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_ATTACH;
                    usb_ptr->INT_ENB = MCF522X_USBOTG_INT_ENB_ATTACH_EN;
                }
                
                _lwevent_clear(&khci_event, KHCI_EVENT_RESET | KHCI_EVENT_TOK_DONE);
            }
        }
    }
    
    return;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_task_create
*  Returned Value : error or USB_OK
*  Comments       :
*        Create KHCI task
*END*-----------------------------------------------------------------*/
static USB_STATUS _usb_khci_task_create(_usb_host_handle handle) {
    //USB_STATUS status;
    _task_id task_id;
    TASK_TEMPLATE_STRUCT task_template;
    
    // check wait time for events - must be > 0
    if (USBCFG_KHCI_WAIT_TICK < 1)
        return USBERR_UNKNOWN_ERROR;

    /* create task for processing interrupt deferred work */
    task_template.TASK_TEMPLATE_INDEX = USB_KHCI_TASK_TEMPLATE_INDEX;
    task_template.TASK_ADDRESS = USB_KHCI_TASK_ADDRESS;
    task_template.TASK_STACKSIZE = USB_KHCI_TASK_STACKSIZE;
    task_template.TASK_PRIORITY = USBCFG_KHCI_TASK_PRIORITY;
    task_template.TASK_NAME = USB_KHCI_TASK_NAME;
    task_template.TASK_ATTRIBUTES = USB_KHCI_TASK_ATTRIBUTES;
    task_template.CREATION_PARAMETER = (uint_32)handle;   //USB_KHCI_TASK_CREATION_PARAMETER;
    task_template.DEFAULT_TIME_SLICE = USB_KHCI_TASK_DEFAULT_TIME_SLICE;

    //status = _usb_rtos_task_create(0, 0, (uint_32)&task_template, (USB_RTOS_TASK_STRUCT_PTR)&usb_rtos_host_state.io_task_state.task);
    task_id = _task_create_blocked(0, 0, (uint_32)&task_template);
    
    if (task_id == 0) {
        return USBERR_UNKNOWN_ERROR;
    }
    
    _task_ready(_task_get_td(task_id));

    return USB_OK;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_init
*  Returned Value : error or USB_OK
*  Comments       :
*        Initialize the kirin HCI controller
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_init(_usb_host_handle handle) {
    
    USB_STATUS                 status = USB_OK;
    USB_HOST_STATE_STRUCT_PTR  usb_host_ptr = (USB_HOST_STATE_STRUCT_PTR)handle;
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING)
    PSP_TICK_STRUCT            tick_struct;
#endif
    
    /* create lwevent group */
    if (_lwevent_create(&khci_event, 0) != MQX_OK) {
        _mqx_exit(0);
    }
    
#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING)
    _psp_nsecs_to_ticks(1000000, &tick_struct); //1msec to tick struct
    HWTICKS_PER_TICK = _time_get_hwticks_per_tick();
    HWTICKS_PER_MSEC = tick_struct.TICKS[0] * HWTICKS_PER_TICK + tick_struct.HW_TICKS[0];
    HWTICKS_CONST_FS_ADD = (59684 + 1734) * HWTICKS_PER_MSEC / 100;
    HWTICKS_CONST_FS_MUL = 51099 * HWTICKS_PER_MSEC / 1000;
    HWTICKS_CONST_LS_ADD = (41943 + 1404 + 2 * 6000) * HWTICKS_PER_MSEC / 10; //3000 is the hub delay const
    HWTICKS_CONST_LS_MUL = 41390 * HWTICKS_PER_MSEC / 100;
    #ifdef KHCICFG_TIMEBASED_SCHEDULING
    HWTICKS_CONST_THLD = KHCICFG_THSLD * HWTICKS_PER_MSEC / 1000;
    #endif
    #ifdef KHCICFG_TIMEBASED_SCHEDULING_FAST
    //the difference from TIMEBASED_SCHEDULING is due to faster response of algorithm
    //to the question whether to wait for next SOF or not
    //compared to standard scheduling
    HWTICKS_CONST_THLD = KHCICFG_THSLD * HWTICKS_PER_MSEC / 1000;
    #endif
#endif  
    _usb_khci_task_create(usb_host_ptr);
    
    // install isr
    if (!(USB_install_isr(_bsp_get_usb_vector(0), _usb_khci_isr, (pointer)usb_host_ptr)))
        return USBERR_INSTALL_ISR;
    
    usb_ptr->INT_STAT = 0xff;
    usb_ptr->USB_CTRL = /*MCF522X_USBOTG_USB_CTRL_PDE |*/ MCF522X_USBOTG_USB_CTRL_CLK_SRC_OSCCLK;

    usb_ptr->CTL |= MCF522X_USBOTG_CTL_ODD_RST;
    
    USB_mem_zero(ep, sizeof(ep));
    
    usb_ptr->BDT_PAGE_01 = (uint_8)((uint_32)BDT_BASE >> 8);
    usb_ptr->BDT_PAGE_02 = (uint_8)((uint_32)BDT_BASE >> 16);
    usb_ptr->BDT_PAGE_03 = (uint_8)((uint_32)BDT_BASE >> 24);
    
    // Set SOF threshold
    usb_ptr->SOF_THLD = 1; //18; //1; //74;
    
    //usb_ptr->INT_STAT = 0xff;
    usb_ptr->INT_ENB = MCF522X_USBOTG_INT_ENB_ATTACH_EN;    // | MCF522X_USBOTG_INT_ENB_USB_RST_EN;

    usb_ptr->OTG_CTRL = MCF522X_USBOTG_OTG_CTRL_DM_LOW | MCF522X_USBOTG_OTG_CTRL_DP_LOW | MCF522X_USBOTG_OTG_CTRL_OTG_EN;
        

    usb_ptr->CTL = MCF522X_USBOTG_CTL_HOST_MODE_EN;
        
    return status;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_shutdown - NOT IMPLEMENT YET
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to shutdown the host
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_shutdown(_usb_host_handle handle) {
    return USB_OK;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_send
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to send data
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_send(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr) {
    USB_STATUS status = USB_OK;
    TR_MSG_STRUCT msg;

    msg.type = TR_MSG_SEND;
    msg.pipe_desc = pipe_desc_ptr;
    msg.pipe_tr = pipe_tr_ptr;
    
    _lwmsgq_send((pointer)tr_que, (uint_32 *) &msg, LWMSGQ_SEND_BLOCK_ON_FULL);    
    
    return status;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_send_setup
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to send setup data
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_send_setup(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr) {
    USB_STATUS status = USB_OK;
    //int_32 len;
    TR_MSG_STRUCT msg;
    
    msg.type = TR_MSG_SETUP;
    msg.pipe_desc = pipe_desc_ptr;
    msg.pipe_tr = pipe_tr_ptr;
    
    _lwmsgq_send((pointer)tr_que, (uint_32 *) &msg, LWMSGQ_SEND_BLOCK_ON_FULL);

    return status;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_recv
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to receive data
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_recv(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr) {
    USB_STATUS status = USB_OK;
    TR_MSG_STRUCT msg;

    msg.type = TR_MSG_RECV;
    msg.pipe_desc = pipe_desc_ptr;
    msg.pipe_tr = pipe_tr_ptr;
    
    _lwmsgq_send((pointer)tr_que, (uint_32 *) &msg, LWMSGQ_SEND_BLOCK_ON_FULL);    
    
    return status;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_cancel
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to cancel the transfer
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_cancel(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr) {
    TR_MSG_STRUCT msg;
    
    // remove messages from KHCI msg que
    while (_lwmsgq_receive((pointer)tr_que, (uint_32 *) &msg, 0, 0, 0) == MQX_OK)
        /* void */;
    
    return USB_OK;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_bus_control
*  Returned Value : error or USB_OK
*  Comments       :
*        The function for USB bus control
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_bus_control(_usb_host_handle handle, uint_8 bus_control) {
    return USB_OK;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_get_frame_number
*  Returned Value : frame number
*  Comments       :
*        The function to get frame number
*END*-----------------------------------------------------------------*/
uint_32 _usb_khci_get_frame_number(_usb_host_handle handle) {
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
    return (usb_ptr->FRM_NUMH << 8 | usb_ptr->FRM_NUML);
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_get_micro_frame_number
*  Returned Value : micro frame number
*  Comments       :
*        The function to get micro frame number
*END*-----------------------------------------------------------------*/
uint_32 _usb_khci_get_micro_frame_number(_usb_host_handle handle) {
    return 0;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_process_tr_complete
*  Returned Value : none
*  Comments       :
*        Transaction complete
*END*-----------------------------------------------------------------*/
static void _usb_khci_process_tr_complete(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, PIPE_TR_STRUCT_PTR pipe_tr_ptr, uint_32 remaining_bytes, int_32 err) {
    USB_HOST_STATE_STRUCT_PTR usb_host_ptr = (USB_HOST_STATE_STRUCT_PTR)handle;
    
    uchar_ptr buffer_ptr = NULL;
    uint_32 total_req_bytes = 0;
    uint_32 status = 0;
    
    if (err == KHCI_ATOM_TR_STALL)
        status = USBERR_ENDPOINT_STALLED;
    else if ((err == KHCI_ATOM_TR_NAK) || (err >= 0)) {
        status = USB_OK;
        
        if ((remaining_bytes > 0) || (err == KHCI_ATOM_TR_NAK)) //remaining bytes
            status = USBERR_TR_FAILED;

        if (pipe_desc_ptr->PIPETYPE == USB_CONTROL_PIPE) {
            if (pipe_tr_ptr->SEND_PHASE) {
                total_req_bytes = pipe_tr_ptr->TX_LENGTH;
                buffer_ptr = pipe_tr_ptr->TX_BUFFER;
                pipe_tr_ptr->SEND_PHASE = FALSE;
            }
            else {
                total_req_bytes = pipe_tr_ptr->RX_LENGTH;
                buffer_ptr = pipe_tr_ptr->RX_BUFFER;
            }
        }
        else {
            if (pipe_desc_ptr->DIRECTION) {
                total_req_bytes = pipe_tr_ptr->TX_LENGTH;
                buffer_ptr = pipe_tr_ptr->TX_BUFFER;
            }
            else {
                total_req_bytes = pipe_tr_ptr->RX_LENGTH;
                buffer_ptr = pipe_tr_ptr->RX_BUFFER;
            }
        }
    }
    else if (err < 0)
        status = USBERR_TR_FAILED;
    
    pipe_tr_ptr->status = USB_STATUS_IDLE;
    pipe_tr_ptr->TR_INDEX = 0;
    
    if (pipe_tr_ptr->status == USB_STATUS_IDLE) {
        /* Transfer done. Call the callback function for this 
        ** transaction if there is one (usually true). 
        */
        if (pipe_tr_ptr->CALLBACK != NULL) {
            pipe_tr_ptr->CALLBACK((pointer)pipe_desc_ptr, pipe_tr_ptr->CALLBACK_PARAM,
                buffer_ptr, (total_req_bytes - remaining_bytes), status);
                
            /* If the application enqueued another request on this pipe 
            ** in this callback context then it will be at the end of the list
            */
        }
    }
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_atom_tr
*  Returned Value : 
*  Comments       :
*        Atomic transaction
*END*-----------------------------------------------------------------*/
static int_32 _usb_khci_atom_tr(uint_32 type, uint_32 host_speed, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr, uint_8 *buf, uint_16 len) {
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
    EP_STRUCT_PTR ep_ptr;
    uint_32 bd;
    int_32 res;
    _mqx_int retry;
    _mqx_int delay_const = 10;
#ifdef KHCICFG_TIMEBASED_SCHEDULING
    static MQX_TICK_STRUCT SOF_time;
    MQX_TICK_STRUCT get_time, elapsed_hwticks;
    uint_32 tr_hwticks, elapsed_time;
#endif
#ifdef KHCICFG_TIMEBASED_SCHEDULING_FAST
    uint_32 tr_hwticks;
    static int_32  sof_hwticks, elapsed_hwticks;
#endif
    
    last_to_pipe = NULL; // at the beginning, consider that there was not timeout
    
    len = (len > pipe_desc_ptr->MAX_PACKET_SIZE) ? pipe_desc_ptr->MAX_PACKET_SIZE : len;
    
    ep_ptr = &ep[0];
    
    /* ADDR must be written before ENDPT0 (undocumented behavior) for to generate PRE packet */
    usb_ptr->ADDR = (uint_8)((pipe_desc_ptr->SPEED == USB_SPEED_FULL) ? MCF522X_USBOTG_ADDR_ADDR(pipe_desc_ptr->DEVICE_ADDRESS) : MCF522X_USBOTG_ADDR_LS_EN | MCF522X_USBOTG_ADDR_ADDR(pipe_desc_ptr->DEVICE_ADDRESS));
    
    usb_ptr->ENDPT0 =
        (host_speed == USB_SPEED_LOW ? MCF522X_USBOTG_ENDPT_HOST_WO_HUB : 0) | MCF522X_USBOTG_ENDPT_RETRY_DIS |
        MCF522X_USBOTG_ENDPT_EP_TX_EN | MCF522X_USBOTG_ENDPT_EP_RX_EN | MCF522X_USBOTG_ENDPT_EP_HSHK;

    retry = (pipe_desc_ptr->PIPETYPE == USB_INTERRUPT_PIPE) ? 0 : pipe_desc_ptr->NAK_COUNT; // set retry count - do not retry interrupt transaction
    
    do {
        res = 0;
        
        // wait for USB ready, but with timeout
        while (usb_ptr->CTL & MCF522X_USBOTG_CTL_TXSUSPEND_TOKENBUSY) {
            if (_lwevent_wait_ticks(&khci_event, KHCI_EVENT_MASK, FALSE, 1) == MQX_OK) {
                res = KHCI_ATOM_TR_RESET;
                
                break;
            }
        }
        
        if (!res) {
          // all is ok, do transaction

#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST)
          // compute the length of packet in hardware ticks
          if (pipe_desc_ptr->SPEED == USB_SPEED_FULL)
              tr_hwticks = (HWTICKS_CONST_FS_ADD + HWTICKS_CONST_FS_MUL * len) >> 16;
          else
              tr_hwticks = (HWTICKS_CONST_LS_ADD + HWTICKS_CONST_LS_MUL * len) >> 16;
          _int_disable();
          elapsed_hwticks = _mcf5225_get_hwticks(BSP_TIMER) - sof_hwticks;
          if (elapsed_hwticks < 0 )
              elapsed_hwticks = HWTICKS_PER_MSEC + elapsed_hwticks % HWTICKS_PER_MSEC;
          else
              elapsed_hwticks = elapsed_hwticks % HWTICKS_PER_MSEC;
          if (elapsed_hwticks + tr_hwticks > HWTICKS_CONST_THLD) 
          {
              usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_SOF_TOK; //clear SOF
              while (!(usb_ptr->INT_STAT & MCF522X_USBOTG_INT_STAT_SOF_TOK))
                 /* wait for next SOF */; 
              sof_hwticks = _mcf5225_get_hwticks(BSP_TIMER);
          }

          usb_ptr->SOF_THLD = 0;

#elif defined(KHCICFG_TIMEBASED_SCHEDULING)
          // compute the length of packet in hardware ticks
          if (pipe_desc_ptr->SPEED == USB_SPEED_FULL)
              tr_hwticks = (HWTICKS_CONST_FS_ADD + HWTICKS_CONST_FS_MUL * len) >> 16;
          else
              tr_hwticks = (HWTICKS_CONST_LS_ADD + HWTICKS_CONST_LS_MUL * len) >> 16;
          // we are going to compute time if the packet can be placed to bus
          // if we calculate that yes, then since here to the token must be performed as soon as possible
          // therefore we disabled interrupts
          _int_disable(); //it should be before time_get_elapsed_ticks, but that function enables interrupts
          _time_get_elapsed_ticks_fast(&get_time);
          _time_diff_ticks(&get_time, &SOF_time, &elapsed_hwticks);
          
          elapsed_time = (elapsed_hwticks.TICKS[1] * HWTICKS_PER_TICK + elapsed_hwticks.HW_TICKS) % HWTICKS_PER_MSEC;
          if ((elapsed_hwticks.TICKS[1] > 50) || ((elapsed_time + tr_hwticks) > HWTICKS_CONST_THLD))
          {
              usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_SOF_TOK; //clear SOF
              while (!(usb_ptr->INT_STAT & MCF522X_USBOTG_INT_STAT_SOF_TOK))
                 /* wait for next SOF */; 
                 
              _time_get_elapsed_ticks_fast(&SOF_time); //synchronize time & enable interrupts
          }

          usb_ptr->SOF_THLD = 0;

#elif defined(KHCICFG_BASIC_SCHEDULING)
          usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_SOF_TOK; //clear SOF
          while (!(usb_ptr->INT_STAT & MCF522X_USBOTG_INT_STAT_SOF_TOK))
                 /* wait for next SOF */;

          usb_ptr->SOF_THLD = 0;

#else
          if (len > (127 - KHCICFG_THSLD_DELAY))
             usb_ptr->SOF_THLD = 127; // USB SOF_THLD is limited to 127
          else
             usb_ptr->SOF_THLD = len + KHCICFG_THSLD_DELAY; // normally, len + 10 should be applied, but + 50 is here for hub delays
#endif          
          usb_ptr->INT_STAT |= MCF522X_USBOTG_INT_STAT_SOF_TOK; //clear SOF
          usb_ptr->ERR_STAT = 0xff; //clear error status

          switch (type) {
            case TR_CTRL:    
                pipe_desc_ptr->bd_ptr = (uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd);
                *((uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd) + 1) = HOST_TO_LE_LONG((uint_32)buf);
                *((uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd)) = HOST_TO_LE_LONG(MCF522X_USBOTG_BD_BC(len) |
                                                                            MCF522X_USBOTG_BD_OWN);
                usb_ptr->TOKEN = (uint_8)(MCF522X_USBOTG_TOKEN_TOKEN_ENDPT((uint_8)pipe_desc_ptr->ENDPOINT_NUMBER) | MCF522X_USBOTG_TOKEN_TOKEN_PID_SETUP);
                ep_ptr->tx_bd ^= 1;
                break;
            case TR_IN:
                pipe_desc_ptr->bd_ptr = (uint_32*)BD_PTR(0, 0, ep_ptr->rx_bd);
                *((uint_32*)BD_PTR(0, 0, ep_ptr->rx_bd) + 1) = HOST_TO_LE_LONG((uint_32)buf);
                *((uint_32*)BD_PTR(0, 0, ep_ptr->rx_bd)) = HOST_TO_LE_LONG( MCF522X_USBOTG_BD_BC(len) | 
                                                                            MCF522X_USBOTG_BD_OWN |
                                                                            MCF522X_USBOTG_BD_DATA01(pipe_desc_ptr->NEXTDATA01));
                usb_ptr->TOKEN = (uint_8)(MCF522X_USBOTG_TOKEN_TOKEN_ENDPT(pipe_desc_ptr->ENDPOINT_NUMBER) | MCF522X_USBOTG_TOKEN_TOKEN_PID_IN);
                ep_ptr->rx_bd ^= 1;         
                break;
            case TR_OUT:
                pipe_desc_ptr->bd_ptr = (uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd);
                *((uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd) + 1) = HOST_TO_LE_LONG((uint_32)buf);
                *((uint_32*)BD_PTR(0, 1, ep_ptr->tx_bd)) = HOST_TO_LE_LONG( MCF522X_USBOTG_BD_BC(len) |
                                                                            MCF522X_USBOTG_BD_OWN |
                                                                            MCF522X_USBOTG_BD_DATA01(pipe_desc_ptr->NEXTDATA01));
                usb_ptr->TOKEN = (uint_8)(MCF522X_USBOTG_TOKEN_TOKEN_ENDPT(pipe_desc_ptr->ENDPOINT_NUMBER) | MCF522X_USBOTG_TOKEN_TOKEN_PID_OUT);
                ep_ptr->tx_bd ^= 1;
                break;
          }
          
#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING)
          _int_enable(); //don't forget to reenable interrupts
#endif

          // wait for end of transaction or other event, max. 100 msec
          if (LWEVENT_WAIT_TIMEOUT == _lwevent_wait_ticks(&khci_event, KHCI_EVENT_MASK, FALSE, USBCFG_KHCI_WAIT_TICK))
          {
                res = KHCI_ATOM_TR_TO;
                last_to_pipe = pipe_desc_ptr; // remember this pipe as it had timeout last time
                continue;
          }
        }
        
try_again:
        // check for reset event (detach)
        if (khci_event.VALUE & KHCI_EVENT_RESET) {
            res = KHCI_ATOM_TR_RESET;
            break;
        }
    
        if (khci_event.VALUE & KHCI_EVENT_TOK_DONE) {
            // transaction finished
            _lwevent_clear(&khci_event, KHCI_EVENT_TOK_DONE);

            // check result
            bd = LONG_LE_TO_HOST(*pipe_desc_ptr->bd_ptr);
            
            if (usb_ptr->ERR_STAT & (
                    MCF522X_USBOTG_ERR_ENB_PID_ERR_EN |
#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING) || defined(KHCICFG_BASIC_SCHEDULING)
                    MCF522X_USBOTG_ERR_ENB_CRC5_EOF_EN |
#endif                   
                    MCF522X_USBOTG_ERR_ENB_CRC16_EN |
                    MCF522X_USBOTG_ERR_ENB_DFN8_EN |
//                    MCF522X_USBOTG_ERR_ENB_BTO_ERR_EN | //timeout tested elsewhere
                    MCF522X_USBOTG_ERR_ENB_DMA_ERR_EN |
                    MCF522X_USBOTG_ERR_ENB_BTS_ERR_EN))
            {
#if defined(KHCICFG_TIMEBASED_SCHEDULING_FAST) || defined(KHCICFG_TIMEBASED_SCHEDULING) || defined(KHCICFG_BASIC_SCHEDULING)
                if (usb_ptr->ERR_STAT & MCF522X_USBOTG_ERR_ENB_CRC5_EOF_EN)
                    retry = 0;
#endif                   
                res = -usb_ptr->ERR_STAT;
                break;
            }
            else 
            {
                switch (bd >> 2 & 0xf) {
                    case 0x03:  // DATA0
                    case 0x0b:  // DATA1
                    case 0x02:  // ACK
                        retry = 0;
                        res = (bd >> 16) & 0x3ff;
                        pipe_desc_ptr->NEXTDATA01 ^= 1;     // switch data toggle
                        break;

                    case 0x0e:  // STALL
                        res = KHCI_ATOM_TR_STALL;
                        retry = 0;
                        break;

                    case 0x0a:  // NAK
                        res = KHCI_ATOM_TR_NAK;
                        if (retry)
                            _time_delay(delay_const * (pipe_desc_ptr->NAK_COUNT - retry));
                        break;

                    case 0x00:  // bus timeout
                        //wait a bit, but not too much, perhaps some error occurs on the bus
                        if (LWEVENT_WAIT_TIMEOUT == _lwevent_wait_ticks(&khci_event, KHCI_EVENT_MASK, FALSE, delay_const)) 
                        {
                            res = KHCI_ATOM_TR_TO;
                            last_to_pipe = pipe_desc_ptr; // remember this pipe as it had timeout last time
                            if (retry)
                                _time_delay(delay_const * (pipe_desc_ptr->NAK_COUNT - retry));
                        }
                        else
                            goto try_again;
                        break;

                    case 0x0f:  // data error
                        //if this event happens during enumeration, then return means not finished enumeration
                        res = KHCI_ATOM_TR_DATA_ERROR;
                        _time_delay(delay_const * (pipe_desc_ptr->NAK_COUNT - retry));
                        break;
                        
                    default:
                        break;
                }
            }
        }
    } while (retry--);
    
    return res;
}

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_host_open_pipe
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to open pipe
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_host_open_pipe(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr) {
    VMCF522X_USBOTG_STRUCT_PTR usb_ptr = _bsp_get_usb_base(0);
    uint_32 *ep = ((uint_32 *)&usb_ptr->ENDPT0) + pipe_desc_ptr->ENDPOINT_NUMBER;

    switch (pipe_desc_ptr->PIPETYPE) {
    case USB_CONTROL_PIPE:
        *(uint_8*)ep |= MCF522X_USBOTG_ENDPT_EP_TX_EN | MCF522X_USBOTG_ENDPT_EP_RX_EN;
        pipe_desc_ptr->ep_ptr = ep;
        break;
    case USB_BULK_PIPE:
        break;
    case USB_INTERRUPT_PIPE:
        *(uint_8*)ep = MCF522X_USBOTG_ENDPT_EP_RX_EN;
        pipe_desc_ptr->ep_ptr = ep;
        break;
    case USB_ISOCHRONOUS_PIPE:
        break;
    default:
        return USBERR_OPEN_PIPE_FAILED;
    }
    
    return USB_OK;
}


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_khci_host_close_pipe
*  Returned Value : error or USB_OK
*  Comments       :
*        The function to close pipe
*END*-----------------------------------------------------------------*/
USB_STATUS _usb_khci_host_close_pipe(_usb_host_handle handle, PIPE_DESCRIPTOR_STRUCT_PTR pipe_desc_ptr) {

    if (pipe_desc_ptr->PIPETYPE == USB_INTERRUPT_PIPE) {
        _mqx_int i;
        TR_INT_QUE_ITM_STRUCT_PTR tr = TR_INT_QUE;

        // find record
        for (i = 0; i < USBCFG_KHCI_MAX_INT_TR; i++, tr++) {
            if (tr->msg.pipe_desc == pipe_desc_ptr)
                tr->msg.type = TR_MSG_UNKNOWN;    
        }
    }
    return USB_OK;
}
