diff --git a/rt-thread/SConscript b/rt-thread/SConscript index 1c7ed170f4..fefe0a7e37 100644 --- a/rt-thread/SConscript +++ b/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 = '' diff --git a/rt-thread/bsp/hpmicro/drv_tinyusb.c b/rt-thread/bsp/hpmicro/drv_tinyusb.c index 17b422f20f..0cf9014b0e 100644 --- a/rt-thread/bsp/hpmicro/drv_tinyusb.c +++ b/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) diff --git a/rt-thread/port/msc_host_app.c b/rt-thread/port/msc_host_app.c new file mode 100644 index 0000000000..5b37b3b890 --- /dev/null +++ b/rt-thread/port/msc_host_app.c @@ -0,0 +1,328 @@ +#include + +#include + +#include +#include +#include +#include + +#define DBG_TAG "udisk" +#define DBG_LVL DBG_LOG +#include + +#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); +} diff --git a/rt-thread/tinyusb_port.c b/rt-thread/tinyusb_port.c index 8ae20b04e3..3ee4448e5c 100644 --- a/rt-thread/tinyusb_port.c +++ b/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 } } diff --git a/rt-thread/tusb_config.h b/rt-thread/tusb_config.h index 1ead75e2c9..95de60c9ef 100644 --- a/rt-thread/tusb_config.h +++ b/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 } diff --git a/src/host/usbh.c b/src/host/usbh.c index b6a03b82e9..ab75a338be 100644 --- a/src/host/usbh.c +++ b/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; diff --git a/src/portable/hpm/hcd_hpm.c b/src/portable/hpm/hcd_hpm.c new file mode 100644 index 0000000000..174b9f5b7e --- /dev/null +++ b/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