Bläddra i källkod

add host msc for rt-thread

tfx2001 3 år sedan
förälder
incheckning
222bea2c60

+ 38 - 26
rt-thread/SConscript

@@ -2,15 +2,38 @@ import rtconfig
 from building import *
 
 cwd     = GetCurrentDir()
-src     = Split("""
-../src/tusb.c
-../src/common/tusb_fifo.c
-../src/device/usbd.c
-../src/device/usbd_control.c
-./tinyusb_port.c
-./usb_descriptor.c
-""")
+src     = ["../src/tusb.c",
+           "../src/common/tusb_fifo.c",
+           "./tinyusb_port.c"]
 path = [cwd, cwd + "/../src"]
+    
+# Device
+if GetDepend(["PKG_TINYUSB_DEVICE_ENABLE"]):
+    src += ["../src/device/usbd.c",
+            "../src/device/usbd_control.c",
+            "./usb_descriptor.c"]
+
+if GetDepend(["PKG_TINYUSB_DEVICE_CDC"]):
+    src += ["../src/class/cdc/cdc_device.c"]
+
+if GetDepend(["PKG_TINYUSB_DEVICE_MSC"]):
+    src += ["../src/class/msc/msc_device.c", "port/msc_device_port.c"]
+
+if GetDepend(["PKG_TINYUSB_DEVICE_HID"]):
+    src += ["../src/class/hid/hid_device.c", "port/hid_device_port.c"]
+
+if GetDepend(["PKG_TINYUSB_DEVICE_EXAMPLE_CDC"]):
+    src += ["example/cdc_example.c"]
+
+if GetDepend(["PKG_TINYUSB_DEVICE_EXAMPLE_HID"]):
+    src += ["example/hid_example.c"]
+
+# Host
+if GetDepend(["PKG_TINYUSB_HOST_ENABLE"]):
+    src += ["../src/host/usbh.c"]
+
+if GetDepend(["PKG_TINYUSB_HOST_MSC"]):
+    src += ["../src/class/msc/msc_host.c", "port/msc_host_app.c"]
 
 # BSP
 if GetDepend(["SOC_FAMILY_STM32"]):
@@ -23,29 +46,18 @@ if GetDepend(["SOC_NRF52840"]):
             "bsp/nrf5x/drv_tinyusb.c"]
 
 if GetDepend(["SOC_HPM6000"]):
-    src +=  ["bsp/hpmicro/drv_tinyusb.c",
-    		 "../src/portable/hpm/dcd_hpm.c"]            
+    src +=  ["bsp/hpmicro/drv_tinyusb.c"]
+    
+    if GetDepend(["PKG_TINYUSB_DEVICE_ENABLE"]):
+        src += ["../src/portable/hpm/dcd_hpm.c"]
+    
+    if GetDepend(["PKG_TINYUSB_HOST_ENABLE"]):
+        src += ["../src/portable/hpm/hcd_hpm.c"]
                 
 if GetDepend(["SOC_RP2040"]):
     src += ["bsp/rp2040/drv_tinyusb.c",
             "../src/portable/raspberrypi/rp2040/rp2040_usb.c",
             "../src/portable/raspberrypi/rp2040/dcd_rp2040.c"]
-
-# Device class
-if GetDepend(["PKG_TINYUSB_DEVICE_CDC"]):
-    src += ["../src/class/cdc/cdc_device.c"]
-
-if GetDepend(["PKG_TINYUSB_DEVICE_MSC"]):
-    src += ["../src/class/msc/msc_device.c", "port/msc_device_port.c"]
-
-if GetDepend(["PKG_TINYUSB_DEVICE_HID"]):
-    src += ["../src/class/hid/hid_device.c", "port/hid_device_port.c"]
-
-if GetDepend(["PKG_TINYUSB_DEVICE_EXAMPLE_CDC"]):
-    src += ["example/cdc_example.c"]
-
-if GetDepend(["PKG_TINYUSB_DEVICE_EXAMPLE_HID"]):
-    src += ["example/hid_example.c"]
   
 LOCAL_CFLAGS = ''
 

+ 14 - 1
rt-thread/bsp/hpmicro/drv_tinyusb.c

@@ -10,24 +10,32 @@
 
 extern void tud_descriptor_set_serial(char *serial_number, uint8_t length);
 
+#ifdef PKG_TINYUSB_DEVICE_ENABLE
 TU_ATTR_WEAK void generate_serial_number(void)
 {
     char serial_number[32] = {"00001"};
 
     tud_descriptor_set_serial(serial_number, sizeof(serial_number));
 }
+#endif
 
 TU_ATTR_WEAK int tusb_board_init(void)
 {
+#ifdef PKG_TINYUSB_DEVICE_ENABLE
     generate_serial_number();
-
+#endif
     return 0;
 }
 
 TU_ATTR_WEAK void isr_usb0(void)
 {
     rt_interrupt_enter();
+#if defined(PKG_TINYUSB_DEVICE_ENABLE) && (PKG_TINYUSB_DEVICE_RHPORT_NUM == 0)
     dcd_int_handler(0);
+#endif
+#if defined(PKG_TINYUSB_HOST_ENABLE) && (PKG_TINYUSB_HOST_RHPORT_NUM == 0)
+    hcd_int_handler(0);
+#endif
     rt_interrupt_leave();
 }
 SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usb0)
@@ -35,7 +43,12 @@ SDK_DECLARE_EXT_ISR_M(IRQn_USB0, isr_usb0)
 TU_ATTR_WEAK void isr_usb1(void)
 {
     rt_interrupt_enter();
+#if defined(PKG_TINYUSB_DEVICE_ENABLE) && (PKG_TINYUSB_DEVICE_RHPORT_NUM == 1)
     dcd_int_handler(1);
+#endif
+#if defined(PKG_TINYUSB_HOST_ENABLE) && (PKG_TINYUSB_HOST_RHPORT_NUM == 1)
+    hcd_int_handler(1);
+#endif
     rt_interrupt_leave();
 }
 SDK_DECLARE_EXT_ISR_M(IRQn_USB1, isr_usb1)

+ 328 - 0
rt-thread/port/msc_host_app.c

@@ -0,0 +1,328 @@
+#include <tusb.h>
+
+#include <string.h>
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rthw.h>
+#include <dfs_fs.h>
+
+#define DBG_TAG "udisk"
+#define DBG_LVL DBG_LOG
+#include <rtdbg.h>
+
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+
+#define MAX_PARTITION_COUNT      1
+#define SCSI_TIMEOUT             PKG_TINYUSB_HOST_MSC_SCSI_TIMEOUT
+#define UDISK_MOUNTPOINT         PKG_TINYUSB_HOST_MSC_MOUNT_POINT
+
+#define UDISK_EVENT_READ10_CPLT  0x01
+#define UDISK_EVENT_WRITE10_CPLT 0x02
+
+typedef struct upart
+{
+    uint32_t block_size;
+    struct dfs_partition dfs_part;
+    struct rt_device dev;
+} upart_t;
+static upart_t _upart[MAX_PARTITION_COUNT];
+static uint8_t _dev_count;
+static uint8_t _dev_addr;
+static rt_mutex_t _lock;
+static rt_event_t _udisk_event;
+
+static CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN scsi_inquiry_resp_t inquiry_resp;
+static CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t sector_buf[PKG_TINYUSB_HOST_MSC_BUFSIZE];
+static const rt_size_t MAX_PACKET_SIZE = sizeof(sector_buf) / SECTOR_SIZE;
+
+static bool read10_complete_cb(uint8_t dev_addr, msc_cbw_t const *cbw, msc_csw_t const *csw)
+{
+    (void)cbw;
+
+    if (csw->status != 0)
+    {
+        LOG_E("READ10 failed");
+        return false;
+    }
+
+    rt_event_send(_udisk_event, UDISK_EVENT_READ10_CPLT);
+    return true;
+}
+
+static bool write10_complete_cb(uint8_t dev_addr, msc_cbw_t const *cbw, msc_csw_t const *csw)
+{
+    (void)cbw;
+
+    if (csw->status != 0)
+    {
+        LOG_E("WRITE10 failed");
+        return false;
+    }
+
+    rt_event_send(_udisk_event, UDISK_EVENT_WRITE10_CPLT);
+    return true;
+}
+
+static rt_err_t udisk_init(rt_device_t dev)
+{
+    return RT_EOK;
+}
+
+static rt_size_t udisk_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+    rt_err_t ret;
+    upart_t *part;
+    rt_size_t read_size;
+
+    /* check parameter */
+    RT_ASSERT(dev != RT_NULL);
+    RT_ASSERT(buffer != RT_NULL);
+
+    rt_mutex_take(_lock, RT_WAITING_FOREVER);
+
+    part = (upart_t *)dev->user_data;
+
+    read_size = 0;
+    while (size)
+    {
+        rt_size_t packet_size = MIN(size, MAX_PACKET_SIZE);
+        tuh_msc_read10(_dev_addr, 0, sector_buf, pos + part->dfs_part.offset + read_size, packet_size, read10_complete_cb);
+        ret = rt_event_recv(_udisk_event, UDISK_EVENT_READ10_CPLT, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, SCSI_TIMEOUT, RT_NULL);
+
+        if (ret == RT_EOK)
+        {
+            rt_memcpy(buffer + read_size * SECTOR_SIZE, sector_buf, SECTOR_SIZE * packet_size);
+            read_size += packet_size;
+            size -= packet_size;
+        }
+        else
+        {
+            break;
+        }
+    }
+    rt_mutex_release(_lock);
+    return read_size;
+}
+
+static rt_size_t udisk_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+    rt_err_t ret;
+    upart_t *part;
+    rt_size_t sent_size;
+
+    /* check parameter */
+    RT_ASSERT(dev != RT_NULL);
+    RT_ASSERT(buffer != RT_NULL);
+
+    rt_mutex_take(_lock, RT_WAITING_FOREVER);
+
+    part = (upart_t *)dev->user_data;
+    sent_size = 0;
+    while (size)
+    {
+        rt_size_t packet_size = MIN(size, MAX_PACKET_SIZE);
+        rt_memcpy(sector_buf, buffer + sent_size * SECTOR_SIZE, packet_size * SECTOR_SIZE);
+        tuh_msc_write10(_dev_addr, 0, sector_buf, pos + part->dfs_part.offset + sent_size, packet_size, write10_complete_cb);
+        ret = rt_event_recv(_udisk_event, UDISK_EVENT_WRITE10_CPLT, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, SCSI_TIMEOUT, RT_NULL);
+
+        if (ret == RT_EOK)
+        {
+            sent_size += packet_size;
+            size -= packet_size;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    rt_mutex_release(_lock);
+
+    if (ret != RT_EOK)
+    {
+        return 0;
+    }
+
+    return sent_size;
+}
+
+static rt_err_t udisk_control(rt_device_t dev, int cmd, void *args)
+{
+    rt_err_t ret;
+    upart_t *part;
+
+    /* check parameter */
+    RT_ASSERT(dev != RT_NULL);
+
+    part = (upart_t *)dev->user_data;
+
+    if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
+    {
+        struct rt_device_blk_geometry *geometry;
+
+        geometry = (struct rt_device_blk_geometry *)args;
+        if (geometry == RT_NULL)
+            return -RT_ERROR;
+
+        geometry->bytes_per_sector = SECTOR_SIZE;
+        geometry->block_size = part->block_size;
+        geometry->sector_count = part->dfs_part.size;
+    }
+
+    return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops udisk_device_ops =
+    {
+        udisk_init,
+        RT_NULL,
+        RT_NULL,
+        udisk_read,
+        udisk_write,
+        udisk_control};
+#endif
+
+static rt_err_t register_device(upart_t *upart, const char *device_name)
+{
+    upart->block_size = sizeof(sector_buf);
+
+    /* register sd card device */
+    upart->dev.type = RT_Device_Class_Block;
+#ifdef RT_USING_DEVICE_OPS
+    stor->dev[i].ops = &udisk_device_ops;
+#else
+    upart->dev.init = udisk_init;
+    upart->dev.read = udisk_read;
+    upart->dev.write = udisk_write;
+    upart->dev.control = udisk_control;
+#endif
+    upart->dev.user_data = (void *)upart;
+
+    return rt_device_register(&upart->dev, device_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
+}
+
+/**
+ * This function will run udisk driver when usb disk is detected.
+ *
+ * @param intf the usb interface instance.
+ *
+ * @return the error code, RT_EOK on successfully.
+ */
+static void udisk_run(void *parameter)
+{
+    rt_err_t ret;
+    uint32_t block_count;
+    uint32_t block_size;
+    char dname[8];
+
+    block_count = tuh_msc_get_block_count(_dev_addr, 0);
+    block_size = tuh_msc_get_block_size(_dev_addr, 0);
+
+    LOG_I("capacity %lu MB, block size %d",
+          block_count / ((1024 * 1024) / block_size),
+          block_size);
+
+    LOG_D("read partition table");
+
+    for (int i = 0; i < MAX_PARTITION_COUNT; i++)
+    {
+        /* get the partition table */
+        tuh_msc_read10(_dev_addr, 0, sector_buf, 0, 1, read10_complete_cb);
+        ret = rt_event_recv(_udisk_event, UDISK_EVENT_READ10_CPLT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, SCSI_TIMEOUT, RT_NULL);
+        if (ret != RT_EOK)
+        {
+            LOG_E("read partition table error");
+            break;
+        }
+        /* get the first partition */
+        ret = dfs_filesystem_get_partition(&_upart[i].dfs_part, sector_buf, i);
+
+        if (ret == RT_EOK)
+        {
+            rt_snprintf(dname, 7, "ud%d%d", _dev_addr, i);
+            ret = register_device(_upart + i, dname);
+            if (ret == RT_EOK)
+            {
+                LOG_I("udisk part %d register successfully", i);
+            }
+            else
+            {
+                LOG_E("udisk part %d registerfailed: %d", i, ret);
+            }
+            _dev_count++;
+        }
+        else
+        {
+            /* there is no partition */
+            if (i == 0)
+            {
+                rt_snprintf(dname, 8, "ud%d", _dev_addr);
+                _upart[0].dfs_part.offset = 0;
+                _upart[0].dfs_part.size = 0;
+                ret = register_device(_upart, dname);
+                if (ret == RT_EOK)
+                {
+                    LOG_I("udisk register successfully", 0);
+                }
+                else
+                {
+                    LOG_E("udisk register failed: %d", 0, rt_get_errno());
+                }
+                _dev_count = 1;
+            }
+            break;
+        }
+    }
+
+    return;
+}
+
+void tuh_msc_mount_cb(uint8_t dev_addr)
+{
+    rt_thread_t udisk_thread;
+
+    LOG_I("A MassStorage device is mounted");
+
+    _udisk_event = rt_event_create("udisk", RT_IPC_FLAG_PRIO);
+    _lock = rt_mutex_create("udisk", RT_IPC_FLAG_PRIO);
+    if (!_udisk_event || !_lock)
+    {
+        LOG_E("init failed: cannot create mutex or event");
+        return;
+    }
+
+    _dev_addr = dev_addr;
+    udisk_thread = rt_thread_create("udisk", udisk_run, &_dev_addr, 4096, PKG_TINYUSB_THREAD_PRIORITY, 10);
+    rt_thread_startup(udisk_thread);
+}
+
+void tuh_msc_umount_cb(uint8_t dev_addr)
+{
+    (void)dev_addr;
+
+    rt_base_t level = rt_hw_interrupt_disable();
+
+    LOG_I("A MassStorage device is unmounted");
+
+    if (_udisk_event)
+    {
+        rt_event_delete(_udisk_event);
+        _udisk_event = RT_NULL;
+    }
+    if (_lock)
+    {
+        rt_mutex_delete(_lock);
+        _lock = RT_NULL;
+    }
+
+    for (int i = 0; i < _dev_count; i++)
+    {
+        rt_device_unregister(&_upart[i].dev);
+    }
+
+    _dev_count = 0;
+
+    rt_hw_interrupt_enable(level);
+}

+ 5 - 0
rt-thread/tinyusb_port.c

@@ -27,7 +27,12 @@ static void tusb_thread_entry(void *parameter)
     (void) parameter;
     while (1)
     {
+#ifdef PKG_TINYUSB_DEVICE_ENABLE
         tud_task();
+#endif
+#ifdef PKG_TINYUSB_HOST_ENABLE
+        tuh_task();
+#endif
     }
 }
 

+ 32 - 4
rt-thread/tusb_config.h

@@ -61,20 +61,34 @@ extern "C" {
 #endif /* CFG_TUSB_DEBUG */
 
 #ifndef BOARD_DEVICE_RHPORT_NUM
-#define BOARD_DEVICE_RHPORT_NUM     PKG_TINYUSB_RHPORT_NUM
+#define BOARD_DEVICE_RHPORT_NUM     PKG_TINYUSB_DEVICE_RHPORT_NUM
 #endif
-
 #ifndef BOARD_DEVICE_RHPORT_SPEED
 #define BOARD_DEVICE_RHPORT_SPEED   PKG_TINYUSB_DEVICE_PORT_SPEED
 #endif
 
+#ifndef BOARD_HOST_RHPORT_NUM
+#define BOARD_HOST_RHPORT_NUM     PKG_TINYUSB_HOST_RHPORT_NUM
+#endif
+#ifndef BOARD_HOST_RHPORT_SPEED
+#define BOARD_HOST_RHPORT_SPEED   PKG_TINYUSB_HOST_PORT_SPEED
+#endif
+
+#ifdef PKG_TINYUSB_DEVICE_ENABLE
 #if   BOARD_DEVICE_RHPORT_NUM == 0
 #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
 #elif BOARD_DEVICE_RHPORT_NUM == 1
 #define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
-#else
-  #error "Incorrect RHPort configuration"
 #endif
+#endif /* PKG_TINYUSB_ENABLE_DEVICE */
+
+#ifdef PKG_TINYUSB_HOST_ENABLE
+#if   BOARD_HOST_RHPORT_NUM == 0
+#define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_HOST | BOARD_HOST_RHPORT_SPEED)
+#elif BOARD_HOST_RHPORT_NUM == 1
+#define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_HOST | BOARD_HOST_RHPORT_SPEED)
+#endif
+#endif /* PKG_TINYUSB_ENABLE_HOST */
 
 /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
  * Tinyusb use follows macros to declare transferring memory so that they can be put
@@ -119,6 +133,20 @@ extern "C" {
 #define PKG_TINYUSB_DEVICE_HID_STRING ""
 #endif
 
+//--------------------------------------------------------------------
+// HOST CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE PKG_TINYUSB_HOST_ENUM_BUFSIZE
+
+#define CFG_TUH_HUB                 0
+#define CFG_TUH_CDC                 0
+#define CFG_TUH_HID                 0 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_VENDOR              0
+
+// max device support (excluding hub device)
+#define CFG_TUH_DEVICE_MAX          (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
 
 #ifdef __cplusplus
 }

+ 3 - 4
src/host/usbh.c

@@ -258,7 +258,7 @@ struct
   uint8_t daddr;
   volatile uint8_t stage;
   volatile uint16_t actual_len;
-}_ctrl_xfer;
+} _ctrl_xfer CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN;
 
 //------------- Helper Function -------------//
 
@@ -1381,9 +1381,8 @@ static bool enum_new_device(hcd_event_t* event)
     // connected/disconnected directly with roothub
     // wait until device is stable TODO non blocking
     hcd_port_reset(_dev0.rhport);
-    osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
-                                  // sof of controller may not running while reseting
-    hcd_port_reset_end( _dev0.rhport);
+    // osal_task_delay(RESET_DELAY);
+    // hcd_port_reset_end( _dev0.rhport);
 
     // device unplugged while delaying
     if ( !hcd_port_connect_status(_dev0.rhport) ) return true;

+ 414 - 0
src/portable/hpm/hcd_hpm.c

@@ -0,0 +1,414 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+/*
+ * Copyright (c) 2021 hpmicro
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*---------------------------------------------------------------------*
+ * Includes
+ *---------------------------------------------------------------------*/
+#include "tusb_option.h"
+
+#if TUSB_OPT_HOST_ENABLED && CFG_TUSB_MCU == OPT_MCU_HPM
+
+#include "board.h"
+#include "common/tusb_common.h"
+#include "hpm_usb_host.h"
+#include "host/hcd.h"
+// #include "ff.h"
+#include "hpm_interrupt.h"
+/*---------------------------------------------------------------------*
+ * Enum Declaration
+ *---------------------------------------------------------------------*/
+typedef enum {
+  hcd_int_mask_usb                   = HPM_BITSMASK(1, 0),
+  hcd_int_mask_error                 = HPM_BITSMASK(1, 1),
+  hcd_int_mask_port_change           = HPM_BITSMASK(1, 2),
+
+  hcd_int_mask_framelist_rollover    = HPM_BITSMASK(1, 3),
+  hcd_int_mask_pci_host_system_error = HPM_BITSMASK(1, 4),
+  hcd_int_mask_async_advance         = HPM_BITSMASK(1, 5),
+  hcd_int_mask_sof                   = HPM_BITSMASK(1, 7),
+
+  hcd_int_mask_async                 = HPM_BITSMASK(1, 18),
+  hcd_int_mask_periodic              = HPM_BITSMASK(1, 19),
+
+  hcd_int_mask_all                   = hcd_int_mask_usb | hcd_int_mask_error | hcd_int_mask_port_change |
+                                       hcd_int_mask_framelist_rollover | hcd_int_mask_pci_host_system_error |
+                                       hcd_int_mask_async_advance | hcd_int_mask_sof |
+                                       hcd_int_mask_async | hcd_int_mask_periodic
+} usb_interrupt_mask_t;
+typedef struct
+{
+    USB_Type *regs;            /* register base */
+    const uint32_t irqnum;     /* IRQ number */
+}hcd_controller_t;
+
+static const hcd_controller_t _hcd_controller[] =
+{
+    { .regs = (USB_Type *) HPM_USB0_BASE, .irqnum = IRQn_USB0 },
+    #ifdef HPM_USB1_BASE
+    { .regs = (USB_Type *) HPM_USB1_BASE, .irqnum = IRQn_USB1 }
+    #endif
+};
+
+/*---------------------------------------------------------------------*
+ * Variable Definitions
+ *---------------------------------------------------------------------*/
+ATTR_PLACE_AT_NONCACHEABLE static usb_host_handle_t usb_host_handle;
+ATTR_PLACE_AT_NONCACHEABLE static bool hcd_int_sta;
+ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT(USB_SOC_DCD_DATA_RAM_ADDRESS_ALIGNMENT) static hcd_data_t _hcd_data;
+
+bool hcd_init(uint8_t rhport)
+{
+    uint32_t int_mask;
+
+    if (rhport > USB_SOC_MAX_COUNT) {
+        return false;
+    }
+
+    usb_host_handle.rhport   = rhport;
+    usb_host_handle.regs     = _hcd_controller[rhport].regs;
+    usb_host_handle.hcd_data = &_hcd_data;
+    usb_host_handle.hcd_vbus_ctrl_cb = board_usb_vbus_ctrl;
+
+    int_mask = hcd_int_mask_error | hcd_int_mask_port_change | hcd_int_mask_async_advance |
+               hcd_int_mask_periodic | hcd_int_mask_async | hcd_int_mask_framelist_rollover;
+
+    usb_host_init(&usb_host_handle, int_mask, USB_HOST_FRAMELIST_SIZE);
+
+    return true;
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+    intc_m_enable_irq(_hcd_controller[rhport].irqnum);
+    hcd_int_sta  = true;
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+    intc_m_disable_irq(_hcd_controller[rhport].irqnum);
+    hcd_int_sta = false;
+}
+
+bool hcd_int_status(void)
+{
+    return hcd_int_sta;
+}
+
+uint32_t hcd_uframe_number(uint8_t rhport)
+{
+    return usb_host_uframe_number(&usb_host_handle);
+}
+
+void hcd_port_reset(uint8_t hostid)
+{
+    usb_host_port_reset(&usb_host_handle);
+}
+
+bool hcd_port_connect_status(uint8_t hostid)
+{
+    return usb_host_get_port_ccs(&usb_host_handle);
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t hostid)
+{
+    return usb_host_get_port_speed(&usb_host_handle);
+}
+
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+    usb_host_device_close(&usb_host_handle, dev_addr);
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+    return usb_host_edpt_xfer(&usb_host_handle, dev_addr, ep_addr, buffer, buflen);
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+    return usb_host_setup_send(&usb_host_handle, dev_addr, setup_packet);
+}
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+    usb_host_handle.ep_speed = usb_host_get_port_speed(&usb_host_handle);
+    usb_host_handle.hub_addr = 0;
+    usb_host_handle.hub_port = 0;
+
+    return usb_host_edpt_open(&usb_host_handle, dev_addr, (usb_desc_endpoint_t const *)ep_desc);
+}
+
+bool hcd_pipe_queue_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes)
+{
+    return usb_host_pipe_queue_xfer(&usb_host_handle, dev_addr, ep_addr, buffer, total_bytes);
+}
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
+{
+    return usb_host_edpt_busy(&usb_host_handle, dev_addr, ep_addr);
+}
+
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
+{
+    return usb_host_edpt_stalled(&usb_host_handle, dev_addr, ep_addr);
+}
+
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+    return usb_host_edpt_clear_stall(&usb_host_handle, dev_addr, ep_addr);
+}
+
+/*---------------------------------------------------------------------*
+ * HCD Interrupt Handler
+ *---------------------------------------------------------------------*/
+/* async_advance is handshake between usb stack & ehci controller.
+ * This isr mean it is safe to modify previously removed queue head from async list.
+ * In tinyusb, queue head is only removed when device is unplugged.
+ */
+static void async_advance_isr(usb_host_handle_t *handle)
+{
+    hcd_qhd_t* qhd_pool = handle->hcd_data->qhd_pool;
+
+    for(uint32_t i = 0; i < USB_SOC_HCD_MAX_ENDPOINT_COUNT; i++) {
+        if (qhd_pool[i].removing) {
+            qhd_pool[i].removing = 0;
+            qhd_pool[i].used     = 0;
+        }
+    }
+}
+
+static void port_connect_status_change_isr(usb_host_handle_t *handle)
+{
+    /* NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device */
+    if (usb_host_get_port_ccs(handle)) {
+        usb_host_port_reset(handle);
+        hcd_event_device_attach(handle->rhport, true);
+    } else { /* device unplugged */
+        hcd_event_device_remove(handle->rhport, true);
+    }
+}
+
+static void qhd_xfer_complete_isr(hcd_qhd_t * p_qhd)
+{
+    bool is_ioc;
+
+    while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active) {
+        /* TD need to be freed and removed from qhd, before invoking callback */
+        is_ioc = (p_qhd->p_qtd_list_head->int_on_complete != 0);
+        p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
+
+        p_qhd->p_qtd_list_head->used = 0; /* free QTD */
+        usb_host_qtd_remove_1st_from_qhd(p_qhd);
+
+        if (is_ioc) {
+            hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->qtd_overlay.pid == usb_pid_in ? 1 : 0), p_qhd->total_xferred_bytes, XFER_RESULT_SUCCESS, true);
+            p_qhd->total_xferred_bytes = 0;
+        }
+    }
+}
+
+static void async_list_xfer_complete_isr(hcd_qhd_t * const async_head)
+{
+    hcd_qhd_t *p_qhd = async_head;
+
+    do {
+        if ( !p_qhd->qtd_overlay.halted ) { /* halted or error is processed in error isr */
+            qhd_xfer_complete_isr(p_qhd);
+        }
+
+        p_qhd = usb_host_qhd_next(p_qhd);
+        p_qhd = (hcd_qhd_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p_qhd);
+
+    } while(p_qhd != async_head); /* async list traversal, stop if loop around */
+}
+
+static void period_list_xfer_complete_isr(usb_host_handle_t *handle, uint8_t interval_ms)
+{
+    uint16_t max_loop = 0;
+    uint32_t const period_1ms_addr = (uint32_t) usb_host_get_period_head(handle, 1);
+    hcd_link_t next_item = *usb_host_get_period_head(handle, interval_ms);
+    hcd_qhd_t *p_qhd_int;
+
+    /* TODO abstract max loop guard for period */
+    while(!next_item.terminate &&
+          !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) &&
+          max_loop < (USB_SOC_HCD_MAX_ENDPOINT_COUNT + usb_max_itd + usb_max_sitd) * CFG_TUH_DEVICE_MAX) {
+
+        switch ( next_item.type ) {
+            case usb_qtype_qhd:
+                p_qhd_int = (hcd_qhd_t *)tu_align32(next_item.address);
+                if (!p_qhd_int->qtd_overlay.halted) {
+                    qhd_xfer_complete_isr(p_qhd_int);
+                }
+
+                break;
+
+            case usb_qtype_itd:
+            case usb_qtype_sitd:
+            case usb_qtype_fstn:
+
+            default: break;
+        }
+
+        next_item = *usb_host_list_next(&next_item);
+        max_loop++;
+    }
+}
+
+static void qhd_xfer_error_isr(usb_host_handle_t *handle, hcd_qhd_t * p_qhd)
+{
+    xfer_result_t error_event;
+    hcd_qtd_t *p_setup;
+
+    if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || /* addr0 cannot be protocol STALL */
+          usb_host_qhd_has_xact_error(p_qhd)) {
+        /* no error bits are set, endpoint is halted due to STALL */
+        error_event = usb_host_qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED;
+
+        p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
+
+        /* TODO skip unplugged device */
+
+        p_qhd->p_qtd_list_head->used = 0; /* free QTD */
+        usb_host_qtd_remove_1st_from_qhd(p_qhd);
+
+        if (0 == p_qhd->ep_number) {
+            /* control cannot be halted --> clear all qtd list */
+            p_qhd->p_qtd_list_head = NULL;
+            p_qhd->p_qtd_list_tail = NULL;
+
+            p_qhd->qtd_overlay.next.terminate      = 1;
+            p_qhd->qtd_overlay.alternate.terminate = 1;
+            p_qhd->qtd_overlay.halted              = 0;
+
+            p_setup = usb_host_qtd_control(handle, p_qhd->dev_addr);
+            p_setup->used = 0;
+        }
+
+        /* invoke USBH callback */
+        hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->qtd_overlay.pid == usb_pid_in ? 1 : 0), p_qhd->total_xferred_bytes, error_event, true);
+
+        p_qhd->total_xferred_bytes = 0;
+    }
+}
+
+static void xfer_error_isr(usb_host_handle_t *handle)
+{
+    hcd_qhd_t * const async_head = usb_host_qhd_async_head(handle);
+    hcd_qhd_t *p_qhd = async_head;
+    hcd_qhd_t *p_qhd_int;
+    hcd_link_t next_item, *p;
+
+    /*------------- async list -------------*/
+    do {
+        qhd_xfer_error_isr(handle, p_qhd);
+        p_qhd = usb_host_qhd_next(p_qhd);
+        p_qhd = (hcd_qhd_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p_qhd);
+    } while(p_qhd != async_head); /* async list traversal, stop if loop around */
+
+    /*------------- TODO refractor period list -------------*/
+    uint32_t const period_1ms_addr = (uint32_t)usb_host_get_period_head(handle, 1);
+    for (uint8_t interval_ms = 1; interval_ms <= USB_HOST_FRAMELIST_SIZE; interval_ms *= 2) {
+        next_item = *usb_host_get_period_head(handle, interval_ms);
+
+        /* TODO abstract max loop guard for period */
+        while(!next_item.terminate &&
+              !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) ) {
+            switch (next_item.type) {
+                case usb_qtype_qhd:
+                    p_qhd_int = (hcd_qhd_t *)tu_align32(next_item.address);
+                    qhd_xfer_error_isr(handle, p_qhd_int);
+                    break;
+
+                case usb_qtype_itd:
+                case usb_qtype_sitd:
+                case usb_qtype_fstn:
+
+                default:
+                    break;
+            }
+
+            p = usb_host_list_next(&next_item);
+            p = (hcd_link_t *)sys_address_to_core_local_mem(USB_HOST_MCU_CORE, (uint32_t)p);
+            next_item = *p;
+        }
+    }
+}
+
+/*------------- Host Controller Driver's Interrupt Handler -------------*/
+void hcd_int_handler(uint8_t rhport)
+{
+    uint32_t status;
+    usb_host_handle_t *handle = &usb_host_handle;
+
+    /* Acknowledge handled interrupt */
+    status = usb_host_status_flags(handle);
+    status &= usb_host_interrupts(handle);
+    usb_host_clear_status_flags(handle, status);
+
+    if (status == 0) {
+        return;
+    }
+
+    if (status & hcd_int_mask_framelist_rollover) {
+        handle->hcd_data->uframe_number += (USB_HOST_FRAMELIST_SIZE << 3);
+    }
+
+    if (status & hcd_int_mask_port_change) {
+        if (usb_host_port_csc(handle)) {
+            port_connect_status_change_isr(handle);
+        }
+    }
+
+    if (status & hcd_int_mask_error) {
+        xfer_error_isr(handle);
+    }
+
+    /*------------- some QTD/SITD/ITD with IOC set is completed -------------*/
+    if (status & hcd_int_mask_async) {
+        async_list_xfer_complete_isr(usb_host_qhd_async_head(handle));
+    }
+
+    if (status & hcd_int_mask_periodic) {
+        for (uint8_t i = 1; i <= USB_HOST_FRAMELIST_SIZE; i *= 2) {
+            period_list_xfer_complete_isr(handle, i);
+        }
+    }
+
+    /*------------- There is some removed async previously -------------*/
+    if (status & hcd_int_mask_async_advance) { /* need to place after EHCI_INT_MASK_ASYNC */
+        async_advance_isr(handle);
+    }
+}
+
+#endif