diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc5b92b68..8ef19b0a0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,6 +51,7 @@ jobs: - 'samd51' - 'saml2x' - 'stm32f0' + - 'stm32f1' - 'stm32f4' - 'stm32f7' - 'stm32h7' diff --git a/README.rst b/README.rst index 2119c26d2..9a41e3ff1 100644 --- a/README.rst +++ b/README.rst @@ -56,11 +56,11 @@ Here is the list of `Supported Devices`_ that can be used with provided examples Device Stack ============ -Supports multiple device configurations by dynamically changing usb descriptors. Low power functions such like suspend, resume, and remote wakeup. Following device classes are supported: +Supports multiple device configurations by dynamically changing USB descriptors, low power functions such like suspend, resume, and remote wakeup. The following device classes are supported: - Audio Class 2.0 (UAC2) - Bluetooth Host Controller Interface (BTH HCI) -- Communication Class (CDC) +- Communication Device Class (CDC) - Device Firmware Update (DFU): DFU mode (WIP) and Runtinme - Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ... - Mass Storage Class (MSC): with multiple LUNs @@ -70,7 +70,7 @@ Supports multiple device configurations by dynamically changing usb descriptors. - Vendor-specific class support with generic In & Out endpoints. Can be used with MS OS 2.0 compatible descriptor to load winUSB driver without INF file. - `WebUSB `__ with vendor-specific class -If you have special need, `usbd_app_driver_get_cb()` can be used to write your own class driver without modifying the stack. Here is how RPi team add their reset interface [raspberrypi/pico-sdk#197](https://github.com/raspberrypi/pico-sdk/pull/197) +If you have a special requirement, `usbd_app_driver_get_cb()` can be used to write your own class driver without modifying the stack. Here is how the RPi team added their reset interface [raspberrypi/pico-sdk#197](https://github.com/raspberrypi/pico-sdk/pull/197) Host Stack ========== @@ -82,7 +82,7 @@ Host Stack OS Abstraction layer ==================== -TinyUSB is completely thread-safe by pushing all ISR events into a central queue, then process it later in the non-ISR context task function. It also uses semaphore/mutex to access shared resources such as CDC FIFO. Therefore the stack needs to use some of OS's basic APIs. Following OSes are already supported out of the box. +TinyUSB is completely thread-safe by pushing all Interrupt Service Request (ISR) events into a central queue, then processing them later in the non-ISR context task function. It also uses semaphore/mutex to access shared resources such as Communication Device Class (CDC) FIFO. Therefore the stack needs to use some of the OS's basic APIs. Following OSes are already supported out of the box. - **No OS** - **FreeRTOS** @@ -100,7 +100,7 @@ Local Docs - `Reference`_ - `Supported Devices`_ - - `Gettin Started`_ + - `Getting Started`_ - `Concurrency`_ - `Contributing`_ @@ -113,7 +113,7 @@ License ======= All TinyUSB sources in the ``src`` folder are licensed under MIT -license, `Full license is here `__. However, each file can be +license, the `Full license is here `__. However, each file can be individually licensed especially those in ``lib`` and ``hw/mcu`` folder. Please make sure you understand all the license term for files you use in your project. @@ -132,7 +132,7 @@ in your project. .. _Contributors: CONTRIBUTORS.rst .. _Reference: docs/reference/index.rst .. _Supported Devices: docs/reference/supported.rst -.. _Gettin Started: docs/reference/getting_started.rst +.. _Getting Started: docs/reference/getting_started.rst .. _Concurrency: docs/reference/concurrency.rst .. _Contributing: docs/contributing/index.rst .. _Code of Conduct: CODE_OF_CONDUCT.rst diff --git a/docs/reference/supported.rst b/docs/reference/supported.rst index 0028744f6..4c7d753c2 100644 --- a/docs/reference/supported.rst +++ b/docs/reference/supported.rst @@ -307,10 +307,11 @@ ST STM32 - `STM32 F070rb Nucleo `__ - `STM32 F072 Evaluation `__ - `STM32 F072rb Discovery `__ -- STM32 F103c Blue Pill +- `STM32 F103c8 Blue Pill `__ +- `STM32 F103rc Mini v2.0 `__ - `STM32 F207zg Nucleo `__ - `STM32 F303vc Discovery `__ -- STM32 F401cc Black Pill +- `STM32 F401cc Black Pill `__ - `STM32 F407vg Discovery `__ - `STM32 F411ce Black Pill `__ - `STM32 F411ve Discovery `__ diff --git a/examples/device/hid_boot_interface/CMakeLists.txt b/examples/device/hid_boot_interface/CMakeLists.txt new file mode 100644 index 000000000..abc4d91da --- /dev/null +++ b/examples/device/hid_boot_interface/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/examples/device/hid_boot_interface/Makefile b/examples/device/hid_boot_interface/Makefile new file mode 100644 index 000000000..138c27846 --- /dev/null +++ b/examples/device/hid_boot_interface/Makefile @@ -0,0 +1,15 @@ +include ../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += \ + src/main.c \ + src/usb_descriptors.c + +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../rules.mk diff --git a/examples/device/hid_boot_interface/src/main.c b/examples/device/hid_boot_interface/src/main.c new file mode 100644 index 000000000..e5e2f6856 --- /dev/null +++ b/examples/device/hid_boot_interface/src/main.c @@ -0,0 +1,255 @@ +/* + * 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. + * + */ + +#include +#include +#include + +#include "bsp/board.h" +#include "tusb.h" +#include "usb_descriptors.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ + +/* Blink pattern + * - 250 ms : device not mounted + * - 1000 ms : device mounted + * - 2500 ms : device is suspended + */ +enum { + BLINK_NOT_MOUNTED = 250, + BLINK_MOUNTED = 1000, + BLINK_SUSPENDED = 2500, +}; + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +void led_blinking_task(void); +void hid_task(void); + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + tusb_init(); + + while (1) + { + tud_task(); // tinyusb device task + led_blinking_task(); + + hid_task(); + } + + return 0; +} + +//--------------------------------------------------------------------+ +// Device callbacks +//--------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + blink_interval_ms = BLINK_MOUNTED; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) +{ + blink_interval_ms = BLINK_NOT_MOUNTED; +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wakeup +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) +{ + (void) remote_wakeup_en; + blink_interval_ms = BLINK_SUSPENDED; +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) +{ + blink_interval_ms = BLINK_MOUNTED; +} + +//--------------------------------------------------------------------+ +// USB HID +//--------------------------------------------------------------------+ + +// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..) +// tud_hid_report_complete_cb() is used to send the next report after previous one is complete +void hid_task(void) +{ + // Poll every 10ms + const uint32_t interval_ms = 10; + static uint32_t start_ms = 0; + + if ( board_millis() - start_ms < interval_ms) return; // not enough time + start_ms += interval_ms; + + uint32_t const btn = board_button_read(); + + if ( tud_suspended() && btn ) + { + // Wake up host if we are in suspend mode + // and REMOTE_WAKEUP feature is enabled by host + tud_remote_wakeup(); + } + else + { + // keyboard interface + if ( tud_hid_n_ready(ITF_NUM_KEYBOARD) ) + { + // used to avoid send multiple consecutive zero report for keyboard + static bool has_keyboard_key = false; + + uint8_t const report_id = 0; + uint8_t const modifier = 0; + + if ( btn ) + { + uint8_t keycode[6] = { 0 }; + keycode[0] = HID_KEY_ARROW_RIGHT; + + tud_hid_n_keyboard_report(ITF_NUM_KEYBOARD, report_id, modifier, keycode); + has_keyboard_key = true; + }else + { + // send empty key report if previously has key pressed + if (has_keyboard_key) tud_hid_n_keyboard_report(ITF_NUM_KEYBOARD, report_id, modifier, NULL); + has_keyboard_key = false; + } + } + + // mouse interface + if ( tud_hid_n_ready(ITF_NUM_MOUSE) ) + { + if ( btn ) + { + uint8_t const report_id = 0; + uint8_t const button_mask = 0; + uint8_t const veritical = 0; + uint8_t const horizontal = 0; + int8_t const delta = 5; + + tud_hid_n_mouse_report(ITF_NUM_MOUSE, report_id, button_mask, delta, delta, veritical, horizontal); + } + } + } +} + +// Invoked when received SET_PROTOCOL request +// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) +void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) +{ + (void) instance; + (void) protocol; + + // nothing to do since we use the same compatible boot report for both Boot and Report mode. + // TOOD set a indicator for user +} + +// Invoked when sent REPORT successfully to host +// Application can use this to send the next report +// Note: For composite reports, report[0] is report ID +void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len) +{ + (void) instance; + (void) report; + (void) len; + + // nothing to do +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + // TODO not Implemented + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) +{ + (void) report_id; + + // keyboard interface + if (instance == ITF_NUM_KEYBOARD) + { + // Set keyboard LED e.g Capslock, Numlock etc... + if (report_type == HID_REPORT_TYPE_OUTPUT) + { + // bufsize should be (at least) 1 + if ( bufsize < 1 ) return; + + uint8_t const kbd_leds = buffer[0]; + + if (kbd_leds & KEYBOARD_LED_CAPSLOCK) + { + // Capslock On: disable blink, turn led on + blink_interval_ms = 0; + board_led_write(true); + }else + { + // Caplocks Off: back to normal blink + board_led_write(false); + blink_interval_ms = BLINK_MOUNTED; + } + } + } +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void) +{ + static uint32_t start_ms = 0; + static bool led_state = false; + + // blink is disabled + if (!blink_interval_ms) return; + + // Blink every interval ms + if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time + start_ms += blink_interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle +} diff --git a/examples/device/hid_boot_interface/src/tusb_config.h b/examples/device/hid_boot_interface/src/tusb_config.h new file mode 100644 index 000000000..59fb9962c --- /dev/null +++ b/examples/device/hid_boot_interface/src/tusb_config.h @@ -0,0 +1,111 @@ +/* + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#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 + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* 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 + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 2 // 1 for boot keyboard, 1 for boot mouse +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 8 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/hid_boot_interface/src/usb_descriptors.c b/examples/device/hid_boot_interface/src/usb_descriptors.c new file mode 100644 index 000000000..3fa48d98d --- /dev/null +++ b/examples/device/hid_boot_interface/src/usb_descriptors.c @@ -0,0 +1,180 @@ +/* + * 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. + * + */ + +#include "tusb.h" +#include "usb_descriptors.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +uint8_t const desc_hid_keyboard_report[] = +{ + TUD_HID_REPORT_DESC_KEYBOARD() +}; + +uint8_t const desc_hid_mouse_report[] = +{ + TUD_HID_REPORT_DESC_MOUSE() +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) +{ + return (instance == 0) ? desc_hid_keyboard_report : desc_hid_mouse_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + 2*TUD_HID_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 1 Interrupt, 2 Bulk, 3 Iso, 4 Interrupt, 5 Bulk etc ... + #define EPNUM_KEYBOARD 0x81 + #define EPNUM_MOUSE 0x84 +#else + #define EPNUM_KEYBOARD 0x81 + #define EPNUM_MOUSE 0x82 +#endif + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_KEYBOARD, 0, HID_ITF_PROTOCOL_KEYBOARD, sizeof(desc_hid_keyboard_report), EPNUM_KEYBOARD, CFG_TUD_HID_EP_BUFSIZE, 10), + + // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval + TUD_HID_DESCRIPTOR(ITF_NUM_MOUSE, 0, HID_ITF_PROTOCOL_MOUSE, sizeof(desc_hid_mouse_report), EPNUM_MOUSE, CFG_TUD_HID_EP_BUFSIZE, 10) +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance); @@ -78,6 +80,13 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); printf("HID has %u reports \r\n", hid_info[instance].report_count); } + + // request to receive report + // tuh_hid_report_received_cb() will be invoked when report is available + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + printf("Error: cannot request to receive report\r\n"); + } } // Invoked when device with hid interface is un-mounted @@ -108,6 +117,12 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons process_generic_report(dev_addr, instance, report, len); break; } + + // continue to request to receive report + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + printf("Error: cannot request to receive report\r\n"); + } } //--------------------------------------------------------------------+ diff --git a/examples/host/cdc_msc_hid/src/main.c b/examples/host/cdc_msc_hid/src/main.c index 7ae814e38..a14be05ea 100644 --- a/examples/host/cdc_msc_hid/src/main.c +++ b/examples/host/cdc_msc_hid/src/main.c @@ -33,7 +33,6 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ -void print_greeting(void); void led_blinking_task(void); extern void cdc_task(void); @@ -43,7 +42,8 @@ extern void hid_app_task(void); int main(void) { board_init(); - print_greeting(); + + printf("TinyUSB Host CDC MSC HID Example\r\n"); tusb_init(); @@ -126,28 +126,3 @@ void led_blinking_task(void) board_led_write(led_state); led_state = 1 - led_state; // toggle } - -//--------------------------------------------------------------------+ -// HELPER FUNCTION -//--------------------------------------------------------------------+ -void print_greeting(void) -{ - char const * const rtos_name[] = - { - [OPT_OS_NONE] = "None", - [OPT_OS_FREERTOS] = "FreeRTOS", - [OPT_OS_MYNEWT] = "Mynewt OS", - [OPT_OS_CUSTOM] = "Custom OS implemnted by application", - [OPT_OS_PICO] = "Raspberry Pi Pico SDK", - [OPT_OS_RTTHREAD] = "RT-Thread" - }; - - printf("----------------------------------------------------\r\n"); - printf("TinyUSB Host Example\r\n"); - printf("If you find any bugs or problems, feel free to open\r\n"); - printf("an issue at https://github.com/hathach/tinyusb\r\n"); - printf("----------------------------------------------------\r\n\r\n"); - - printf("This Host demo is configured to support:\r\n"); - printf(" - RTOS = %s\r\n", rtos_name[CFG_TUSB_OS]); -} diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c index 3657a3d35..77a72052d 100644 --- a/examples/host/cdc_msc_hid/src/msc_app.c +++ b/examples/host/cdc_msc_hid/src/msc_app.c @@ -92,7 +92,7 @@ void tuh_msc_umount_cb(uint8_t dev_addr) // // if ( phy_disk == f_get_current_drive() ) // { // active drive is unplugged --> change to other drive -// for(uint8_t i=0; i-) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_host_example(${PROJECT}) \ No newline at end of file diff --git a/examples/host/hid_controller/Makefile b/examples/host/hid_controller/Makefile new file mode 100644 index 000000000..6f59faeee --- /dev/null +++ b/examples/host/hid_controller/Makefile @@ -0,0 +1,30 @@ +include ../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += \ + src/hid_app.c \ + src/main.c + +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +CFLAGS += -Wno-error=cast-align + +# TinyUSB Host Stack source +SRC_C += \ + src/class/cdc/cdc_host.c \ + src/class/hid/hid_host.c \ + src/class/msc/msc_host.c \ + src/host/hub.c \ + src/host/usbh.c \ + src/host/usbh_control.c \ + src/portable/ehci/ehci.c \ + src/portable/ohci/ohci.c \ + src/portable/nxp/transdimension/hcd_transdimension.c \ + src/portable/nxp/lpc17_40/hcd_lpc17_40.c + +include ../../rules.mk diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c new file mode 100644 index 000000000..c61ce70f3 --- /dev/null +++ b/examples/host/hid_controller/src/hid_app.c @@ -0,0 +1,249 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, 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. + * + */ + +#include "bsp/board.h" +#include "tusb.h" + +/* From https://www.kernel.org/doc/html/latest/input/gamepad.html + ____________________________ __ + / [__ZL__] [__ZR__] \ | + / [__ TL __] [__ TR __] \ | Front Triggers + __/________________________________\__ __| + / _ \ | + / /\ __ (N) \ | + / || __ |MO| __ _ _ \ | Main Pad + | <===DP===> |SE| |ST| (W) -|- (E) | | + \ || ___ ___ _ / | + /\ \/ / \ / \ (S) /\ __| + / \________ | LS | ____ | RS | ________/ \ | +| / \ \___/ / \ \___/ / \ | | Control Sticks +| / \_____/ \_____/ \ | __| +| / \ | + \_____/ \_____/ + + |________|______| |______|___________| + D-Pad Left Right Action Pad + Stick Stick + + |_____________| + Menu Pad + + Most gamepads have the following features: + - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST. + - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right. + - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START. + - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also + provide a digital button if you press them. + - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons + are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right. + - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors. + */ + +// Sony DS4 report layout detail https://www.psdevwiki.com/ps4/DS4-USB +typedef struct TU_ATTR_PACKED +{ + uint8_t x, y, z, rz; // joystick + + struct { + uint8_t dpad : 4; // (hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW) + uint8_t square : 1; // west + uint8_t cross : 1; // south + uint8_t circle : 1; // east + uint8_t triangle : 1; // north + }; + + struct { + uint8_t l1 : 1; + uint8_t r1 : 1; + uint8_t l2 : 1; + uint8_t r2 : 1; + uint8_t share : 1; + uint8_t option : 1; + uint8_t l3 : 1; + uint8_t r3 : 1; + }; + + struct { + uint8_t ps : 1; // playstation button + uint8_t tpad : 1; // track pad click + uint8_t counter : 6; // +1 each report + }; + + // comment out since not used by this example + // uint8_t l2_trigger; // 0 released, 0xff fully pressed + // uint8_t r2_trigger; // as above + + // uint16_t timestamp; + // uint8_t battery; + // + // int16_t gyro[3]; // x, y, z; + // int16_t accel[3]; // x, y, z + + // there is still lots more info + +} sony_ds4_report_t; + +// check if device is Sony DualShock 4 +static inline bool is_sony_ds4(uint8_t dev_addr) +{ + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + + return (vid == 0x054c && pid == 0x09cc); +} + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + +void hid_app_task(void) +{ + // nothing to do +} + +//--------------------------------------------------------------------+ +// TinyUSB Callbacks +//--------------------------------------------------------------------+ + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() +// can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + + printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance); + printf("VID = %04x, PID = %04x\r\n", vid, pid); + + // Sony DualShock 4 [CUH-ZCT2x] + if ( is_sony_ds4(dev_addr) ) + { + // request to receive report + // tuh_hid_report_received_cb() will be invoked when report is available + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + printf("Error: cannot request to receive report\r\n"); + } + } +} + +// Invoked when device with hid interface is un-mounted +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) +{ + printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance); + +} + +// check if different than 2 +bool diff_than_2(uint8_t x, uint8_t y) +{ + return (x - y > 2) || (y - x > 2); +} + +// check if 2 reports are different enough +bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2) +{ + bool result; + + // x, y, z, rz must different than 2 to be counted + result = diff_than_2(rpt1->x, rpt2->x) || diff_than_2(rpt1->y , rpt2->y ) || + diff_than_2(rpt1->z, rpt2->z) || diff_than_2(rpt1->rz, rpt2->rz); + + // check the reset with mem compare + result |= memcmp(&rpt1->rz + 1, &rpt2->rz + 1, sizeof(sony_ds4_report_t)-4); + + return result; +} + +void process_sony_ds4(uint8_t const* report, uint16_t len) +{ + const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" }; + + // previous report used to compare for changes + static sony_ds4_report_t prev_report = { 0 }; + + uint8_t const report_id = report[0]; + report++; + len--; + + // all buttons state is stored in ID 1 + if (report_id == 1) + { + sony_ds4_report_t ds4_report; + memcpy(&ds4_report, report, sizeof(ds4_report)); + + // counter is +1, assign to make it easier to compare 2 report + prev_report.counter = ds4_report.counter; + + // only print if changes since it is polled ~ 5ms + // Since count+1 after each report and x, y, z, rz fluctuate within 1 or 2 + // We need more than memcmp to check if report is different enough + if ( diff_report(&prev_report, &ds4_report) ) + { + printf("(x, y, z, rz) = (%u, %u, %u, %u)\r\n", ds4_report.x, ds4_report.y, ds4_report.z, ds4_report.rz); + printf("DPad = %s ", dpad_str[ds4_report.dpad]); + + if (ds4_report.square ) printf("Square "); + if (ds4_report.cross ) printf("Cross "); + if (ds4_report.circle ) printf("Circle "); + if (ds4_report.triangle ) printf("Triangle "); + + if (ds4_report.l1 ) printf("L1 "); + if (ds4_report.r1 ) printf("R1 "); + if (ds4_report.l2 ) printf("L2 "); + if (ds4_report.r2 ) printf("R2 "); + + if (ds4_report.share ) printf("Share "); + if (ds4_report.option ) printf("Option "); + if (ds4_report.l3 ) printf("L3 "); + if (ds4_report.r3 ) printf("R3 "); + + if (ds4_report.ps ) printf("PS "); + if (ds4_report.tpad ) printf("TPad "); + + printf("\r\n"); + } + + prev_report = ds4_report; + } +} + +// Invoked when received report from device via interrupt endpoint +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + if ( is_sony_ds4(dev_addr) ) + { + process_sony_ds4(report, len); + } + + // continue to request to receive report + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + printf("Error: cannot request to receive report\r\n"); + } +} diff --git a/examples/host/hid_controller/src/main.c b/examples/host/hid_controller/src/main.c new file mode 100644 index 000000000..e13fa818a --- /dev/null +++ b/examples/host/hid_controller/src/main.c @@ -0,0 +1,93 @@ +/* + * 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 example current worked and tested with following controller + * - Sony DualShock 4 [CUH-ZCT2x] VID = 0x054c, PID = 0x09cc + */ + + +#include +#include +#include + +#include "bsp/board.h" +#include "tusb.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +void led_blinking_task(void); + +extern void cdc_task(void); +extern void hid_app_task(void); + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + + printf("TinyUSB Host HID Controller Example\r\n"); + + tusb_init(); + + while (1) + { + // tinyusb host task + tuh_task(); + led_blinking_task(); + +#if CFG_TUH_CDC + cdc_task(); +#endif + +#if CFG_TUH_HID + hid_app_task(); +#endif + } + + return 0; +} + +//--------------------------------------------------------------------+ +// TinyUSB Callbacks +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// Blinking Task +//--------------------------------------------------------------------+ +void led_blinking_task(void) +{ + const uint32_t interval_ms = 1000; + static uint32_t start_ms = 0; + + static bool led_state = false; + + // Blink every interval ms + if ( board_millis() - start_ms < interval_ms) return; // not enough time + start_ms += interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle +} diff --git a/examples/host/hid_controller/src/tusb_config.h b/examples/host/hid_controller/src/tusb_config.h new file mode 100644 index 000000000..74b471ae4 --- /dev/null +++ b/examples/host/hid_controller/src/tusb_config.h @@ -0,0 +1,95 @@ +/* + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) +#else + #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* 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 + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// CONFIGURATION +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +#define CFG_TUH_HUB 0 +#define CFG_TUH_CDC 0 +#define CFG_TUH_HID 4 // typical keyboard + mouse device can have 3-4 HID interfaces +#define CFG_TUH_MSC 0 +#define CFG_TUH_VENDOR 0 + +// max device support (excluding hub device) +// 1 hub typically has 4 ports +#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) + +//------------- HID -------------// + +#define CFG_TUH_HID_EP_BUFSIZE 64 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/hw/bsp/board_mcu.h b/hw/bsp/board_mcu.h index acfd7348d..8cc50c808 100644 --- a/hw/bsp/board_mcu.h +++ b/hw/bsp/board_mcu.h @@ -46,7 +46,8 @@ #include "chip.h" #elif CFG_TUSB_MCU == OPT_MCU_LPC51UXX || CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \ - CFG_TUSB_MCU == OPT_MCU_LPC55XX || CFG_TUSB_MCU == OPT_MCU_MKL25ZXX + CFG_TUSB_MCU == OPT_MCU_LPC55XX || CFG_TUSB_MCU == OPT_MCU_MKL25ZXX || \ + CFG_TUSB_MCU == OPT_MCU_K32L2BXX #include "fsl_device_registers.h" #elif CFG_TUSB_MCU == OPT_MCU_NRF5X diff --git a/hw/bsp/frdm_k32l2b/board.h b/hw/bsp/frdm_k32l2b/board.h new file mode 100644 index 000000000..825367915 --- /dev/null +++ b/hw/bsp/frdm_k32l2b/board.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + + +#ifndef BOARD_H_ +#define BOARD_H_ + +#include "fsl_device_registers.h" + +// LED +#define LED_PIN_CLOCK kCLOCK_PortD +#define LED_GPIO GPIOD +#define LED_PORT PORTD +#define LED_PIN 5 +#define LED_STATE_ON 0 + +// SW3 button1 +#define BUTTON_PIN_CLOCK kCLOCK_PortC +#define BUTTON_GPIO GPIOC +#define BUTTON_PORT PORTC +#define BUTTON_PIN 3 +#define BUTTON_STATE_ACTIVE 0 + +// UART +#define UART_PORT LPUART0 +#define UART_PIN_CLOCK kCLOCK_PortA +#define UART_PIN_PORT PORTA +#define UART_PIN_RX 1u +#define UART_PIN_TX 2u +#define SOPT5_LPUART0RXSRC_LPUART_RX 0x00u /*!<@brief LPUART0 Receive Data Source Select: LPUART_RX pin */ +#define SOPT5_LPUART0TXSRC_LPUART_TX 0x00u /*!<@brief LPUART0 Transmit Data Source Select: LPUART0_TX pin */ + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/frdm_k32l2b/board.mk b/hw/bsp/frdm_k32l2b/board.mk new file mode 100644 index 000000000..56df553da --- /dev/null +++ b/hw/bsp/frdm_k32l2b/board.mk @@ -0,0 +1,51 @@ +SDK_DIR = hw/mcu/nxp/mcux-sdk +DEPS_SUBMODULES += $(SDK_DIR) + +CFLAGS += \ + -mthumb \ + -mabi=aapcs \ + -mcpu=cortex-m0plus \ + -DCPU_K32L2B31VLH0A \ + -DCFG_TUSB_MCU=OPT_MCU_K32L2BXX + +# mcu driver cause following warnings +CFLAGS += -Wno-error=unused-parameter + +MCU_DIR = $(SDK_DIR)/devices/K32L2B31A + +# All source paths should be relative to the top level. +LD_FILE = $(MCU_DIR)/gcc/K32L2B31xxxxA_flash.ld + +SRC_C += \ + src/portable/nxp/khci/dcd_khci.c \ + $(MCU_DIR)/system_K32L2B31A.c \ + $(MCU_DIR)/project_template/clock_config.c \ + $(MCU_DIR)/drivers/fsl_clock.c \ + $(SDK_DIR)/drivers/gpio/fsl_gpio.c \ + $(SDK_DIR)/drivers/lpuart/fsl_lpuart.c + +INC += \ + $(TOP)/hw/bsp/$(BOARD) \ + $(TOP)/$(SDK_DIR)/CMSIS/Include \ + $(TOP)/$(SDK_DIR)/drivers/smc \ + $(TOP)/$(SDK_DIR)/drivers/common \ + $(TOP)/$(SDK_DIR)/drivers/gpio \ + $(TOP)/$(SDK_DIR)/drivers/port \ + $(TOP)/$(SDK_DIR)/drivers/lpuart \ + $(TOP)/$(MCU_DIR) \ + $(TOP)/$(MCU_DIR)/drivers \ + $(TOP)/$(MCU_DIR)/project_template \ + +SRC_S += $(MCU_DIR)/gcc/startup_K32L2B31A.S + +# For freeRTOS port source +FREERTOS_PORT = ARM_CM0 + +# For flash-jlink target +JLINK_DEVICE = MKL25Z128xxx4 + +# For flash-pyocd target +PYOCD_TARGET = K32L2B + +# flash using pyocd +flash: flash-pyocd diff --git a/hw/bsp/frdm_k32l2b/frdm_k32l2b.c b/hw/bsp/frdm_k32l2b/frdm_k32l2b.c new file mode 100644 index 000000000..c45095caa --- /dev/null +++ b/hw/bsp/frdm_k32l2b/frdm_k32l2b.c @@ -0,0 +1,140 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach (tinyusb.org) + * Copyright (c) 2020, Koji Kitayama + * + * 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. + */ + +#include "../board.h" +#include "board.h" +#include "fsl_gpio.h" +#include "fsl_port.h" +#include "fsl_clock.h" +#include "fsl_lpuart.h" + +#include "clock_config.h" + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ +void USB0_IRQHandler(void) +{ + tud_int_handler(0); +} + +void board_init(void) +{ + /* Enable port clocks for UART/LED/Button pins */ + CLOCK_EnableClock(UART_PIN_CLOCK); + CLOCK_EnableClock(LED_PIN_CLOCK); + CLOCK_EnableClock(BUTTON_PIN_CLOCK); + + gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0 }; + GPIO_PinInit(LED_GPIO, LED_PIN, &led_config); + PORT_SetPinMux(LED_PORT, LED_PIN, kPORT_MuxAsGpio); + + gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0 }; + GPIO_PinInit(BUTTON_GPIO, BUTTON_PIN, &button_config); + const port_pin_config_t BUTTON_CFG = { + kPORT_PullUp, + kPORT_FastSlewRate, + kPORT_PassiveFilterDisable, + kPORT_LowDriveStrength, + kPORT_MuxAsGpio + }; + PORT_SetPinConfig(BUTTON_PORT, BUTTON_PIN, &BUTTON_CFG); + + /* PORTA1 (pin 23) is configured as LPUART0_RX */ + PORT_SetPinMux(PORTA, 1U, kPORT_MuxAlt2); + /* PORTA2 (pin 24) is configured as LPUART0_TX */ + PORT_SetPinMux(PORTA, 2U, kPORT_MuxAlt2); + + SIM->SOPT5 = ((SIM->SOPT5 & + /* Mask bits to zero which are setting */ + (~(SIM_SOPT5_LPUART0TXSRC_MASK | SIM_SOPT5_LPUART0RXSRC_MASK))) + /* LPUART0 Transmit Data Source Select: LPUART0_TX pin. */ + | SIM_SOPT5_LPUART0TXSRC(SOPT5_LPUART0TXSRC_LPUART_TX) + /* LPUART0 Receive Data Source Select: LPUART_RX pin. */ + | SIM_SOPT5_LPUART0RXSRC(SOPT5_LPUART0RXSRC_LPUART_RX)); + + BOARD_BootClockRUN(); + SystemCoreClockUpdate(); + CLOCK_SetLpuart0Clock(1); + +#if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config(SystemCoreClock / 1000); +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) + NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); +#endif + + lpuart_config_t uart_config; + LPUART_GetDefaultConfig(&uart_config); + uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE; + uart_config.enableTx = true; + uart_config.enableRx = true; + LPUART_Init(UART_PORT, &uart_config, CLOCK_GetFreq(kCLOCK_McgIrc48MClk)); + + // USB + CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U); +} + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) +{ + GPIO_PinWrite(LED_GPIO, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON)); +} + +uint32_t board_button_read(void) +{ + return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_GPIO, BUTTON_PIN); +} + +int board_uart_read(uint8_t* buf, int len) +{ + LPUART_ReadBlocking(UART_PORT, buf, len); + return len; +} + +int board_uart_write(void const * buf, int len) +{ + LPUART_WriteBlocking(UART_PORT, (uint8_t*)buf, len); + return len; +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler(void) +{ + system_ticks++; +} + +uint32_t board_millis(void) +{ + return system_ticks; +} +#endif diff --git a/hw/bsp/stm32f0/family.c b/hw/bsp/stm32f0/family.c index 351a7c8c6..68b924432 100644 --- a/hw/bsp/stm32f0/family.c +++ b/hw/bsp/stm32f0/family.c @@ -64,7 +64,7 @@ void board_init(void) SysTick->CTRL &= ~1U; // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) - NVIC_SetPriority(USB_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); + NVIC_SetPriority(USB_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); #endif // LED diff --git a/hw/bsp/stm32f103bluepill/STM32F103XB_FLASH.ld b/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld similarity index 98% rename from hw/bsp/stm32f103bluepill/STM32F103XB_FLASH.ld rename to hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld index 4be9df61b..ca1804905 100644 --- a/hw/bsp/stm32f103bluepill/STM32F103XB_FLASH.ld +++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/STM32F103X8_FLASH.ld @@ -41,7 +41,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ /* Specify the memory areas */ MEMORY { -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K } diff --git a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h new file mode 100644 index 000000000..57a607ed5 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.h @@ -0,0 +1,92 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, 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. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +// LED +#define LED_PORT GPIOC +#define LED_PIN GPIO_PIN_13 +#define LED_STATE_ON 0 + +// Button +#define BUTTON_PORT GPIOA +#define BUTTON_PIN GPIO_PIN_0 +#define BUTTON_STATE_ACTIVE 1 + +// UART +//#define UART_DEV USART1 +//#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE +//#define UART_GPIO_PORT GPIOA +//#define UART_GPIO_AF GPIO_AF1_USART1 +//#define UART_TX_PIN GPIO_PIN_9 +//#define UART_RX_PIN GPIO_PIN_10 + +//--------------------------------------------------------------------+ +// RCC Clock +//--------------------------------------------------------------------+ +static inline void board_stm32f1_clock_init(void) +{ + RCC_ClkInitTypeDef clkinitstruct = {0}; + RCC_OscInitTypeDef oscinitstruct = {0}; + RCC_PeriphCLKInitTypeDef rccperiphclkinit = {0}; + + /* Enable HSE Oscillator and activate PLL with HSE as source */ + oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + oscinitstruct.HSEState = RCC_HSE_ON; + oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; + oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9; + oscinitstruct.PLL.PLLState = RCC_PLL_ON; + oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + HAL_RCC_OscConfig(&oscinitstruct); + + /* USB clock selection */ + rccperiphclkinit.PeriphClockSelection = RCC_PERIPHCLK_USB; + rccperiphclkinit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; + HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); + + /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ + clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2; + clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1; + HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); +} + +static inline void board_vbus_sense_init(void) +{ +} + +#ifdef __cplusplus + } +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.mk b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.mk new file mode 100644 index 000000000..9c690ca7e --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103_bluepill/board.mk @@ -0,0 +1,11 @@ +CFLAGS += -DSTM32F103xB -DHSE_VALUE=8000000U + +# All source paths should be relative to the top level. +LD_FILE = $(BOARD_PATH)/STM32F103X8_FLASH.ld +SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32f103xb.s + +# For flash-jlink target +JLINK_DEVICE = stm32f103c8 + +# flash target ROM bootloader +flash: flash-dfu-util diff --git a/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld b/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld new file mode 100644 index 000000000..da40d1eb2 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103_mini_2/STM32F103XC_FLASH.ld @@ -0,0 +1,167 @@ +/* +***************************************************************************** +** +** File : STM32F103XB_FLASH.ld +** +** Abstract : Linker script for STM32F103xB Device with +** 128KByte FLASH, 20KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +** (c)Copyright Ac6. +** You may use this file as-is or modify it according to the needs of your +** project. Distribution of this file (unmodified or modified) is not +** permitted. Ac6 permit registered System Workbench for MCU users the +** rights to distribute the assembled, compiled & linked contents of this +** file as part of an application binary file, provided that it is built +** using the System Workbench for MCU toolchain. +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20004FFF; /* end of RAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM AT> FLASH + + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM + + + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.h b/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.h new file mode 100644 index 000000000..bedce7f14 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.h @@ -0,0 +1,92 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020, 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. + */ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +// LED +#define LED_PORT GPIOA +#define LED_PIN GPIO_PIN_8 +#define LED_STATE_ON 0 + +// Button +#define BUTTON_PORT GPIOA +#define BUTTON_PIN GPIO_PIN_0 +#define BUTTON_STATE_ACTIVE 1 + +// UART +//#define UART_DEV USART1 +//#define UART_CLK_EN __HAL_RCC_USART1_CLK_ENABLE +//#define UART_GPIO_PORT GPIOA +//#define UART_GPIO_AF GPIO_AF1_USART1 +//#define UART_TX_PIN GPIO_PIN_9 +//#define UART_RX_PIN GPIO_PIN_10 + +//--------------------------------------------------------------------+ +// RCC Clock +//--------------------------------------------------------------------+ +static inline void board_stm32f1_clock_init(void) +{ + RCC_ClkInitTypeDef clkinitstruct = {0}; + RCC_OscInitTypeDef oscinitstruct = {0}; + RCC_PeriphCLKInitTypeDef rccperiphclkinit = {0}; + + /* Enable HSE Oscillator and activate PLL with HSE as source */ + oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + oscinitstruct.HSEState = RCC_HSE_ON; + oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; + oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9; + oscinitstruct.PLL.PLLState = RCC_PLL_ON; + oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + HAL_RCC_OscConfig(&oscinitstruct); + + /* USB clock selection */ + rccperiphclkinit.PeriphClockSelection = RCC_PERIPHCLK_USB; + rccperiphclkinit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; + HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); + + /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ + clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2; + clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1; + HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); +} + +static inline void board_vbus_sense_init(void) +{ +} + +#ifdef __cplusplus + } +#endif + +#endif /* BOARD_H_ */ diff --git a/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.mk b/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.mk new file mode 100644 index 000000000..eeda87080 --- /dev/null +++ b/hw/bsp/stm32f1/boards/stm32f103_mini_2/board.mk @@ -0,0 +1,11 @@ +CFLAGS += -DSTM32F103xB -DHSE_VALUE=8000000U + +# All source paths should be relative to the top level. +LD_FILE = $(BOARD_PATH)/STM32F103XC_FLASH.ld +SRC_S += $(ST_CMSIS)/Source/Templates/gcc/startup_stm32f103xb.s + +# For flash-jlink target +JLINK_DEVICE = stm32f103rc + +# flash target ROM bootloader +flash: flash-jlink diff --git a/hw/bsp/stm32f103bluepill/stm32f103bluepill.c b/hw/bsp/stm32f1/family.c similarity index 64% rename from hw/bsp/stm32f103bluepill/stm32f103bluepill.c rename to hw/bsp/stm32f1/family.c index 600f3aaef..8fcf9ebd6 100644 --- a/hw/bsp/stm32f103bluepill/stm32f103bluepill.c +++ b/hw/bsp/stm32f1/family.c @@ -24,8 +24,9 @@ * This file is part of the TinyUSB stack. */ -#include "../board.h" #include "stm32f1xx_hal.h" +#include "bsp/board.h" +#include "board.h" //--------------------------------------------------------------------+ // Forward USB interrupt events to TinyUSB IRQ Handler @@ -48,90 +49,45 @@ void USBWakeUp_IRQHandler(void) //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM //--------------------------------------------------------------------+ -#define LED_PORT GPIOC -#define LED_PIN GPIO_PIN_13 -#define LED_STATE_ON 0 - -#define BUTTON_PORT GPIOA -#define BUTTON_PIN GPIO_PIN_0 -#define BUTTON_STATE_ACTIVE 1 - - -/** - * @brief System Clock Configuration - * The system Clock is configured as follow : - * System Clock source = PLL (HSE) - * SYSCLK(Hz) = 72000000 - * HCLK(Hz) = 72000000 - * AHB Prescaler = 1 - * APB1 Prescaler = 2 - * APB2 Prescaler = 1 - * HSE Frequency(Hz) = 8000000 - * HSE PREDIV1 = 1 - * PLLMUL = 9 - * Flash Latency(WS) = 2 - * @param None - * @retval None - */ -void SystemClock_Config(void) -{ - RCC_ClkInitTypeDef clkinitstruct = {0}; - RCC_OscInitTypeDef oscinitstruct = {0}; - RCC_PeriphCLKInitTypeDef rccperiphclkinit = {0}; - - /* Enable HSE Oscillator and activate PLL with HSE as source */ - oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; - oscinitstruct.HSEState = RCC_HSE_ON; - oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; - oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9; - oscinitstruct.PLL.PLLState = RCC_PLL_ON; - oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - HAL_RCC_OscConfig(&oscinitstruct); - - /* USB clock selection */ - rccperiphclkinit.PeriphClockSelection = RCC_PERIPHCLK_USB; - rccperiphclkinit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5; - HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); - - /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ - clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); - clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; - clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1; - clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2; - clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1; - HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); -} void board_init(void) { - - SystemClock_Config(); + board_stm32f1_clock_init(); - #if CFG_TUSB_OS == OPT_OS_NONE + // Enable All GPIOs clocks + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + +#if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); - #endif + +#elif CFG_TUSB_OS == OPT_OS_FREERTOS + // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) + NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); + NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); + NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); +#endif // LED - __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Pull = LED_STATE_ON ? GPIO_PULLDOWN : GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); // Button - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Pull = BUTTON_STATE_ACTIVE ? GPIO_PULLDOWN : GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); // USB Pins // Configure USB DM and DP pins. - __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; diff --git a/hw/bsp/stm32f103bluepill/board.mk b/hw/bsp/stm32f1/family.mk similarity index 84% rename from hw/bsp/stm32f103bluepill/board.mk rename to hw/bsp/stm32f1/family.mk index 656a37323..3fb2e6e70 100644 --- a/hw/bsp/stm32f103bluepill/board.mk +++ b/hw/bsp/stm32f1/family.mk @@ -4,6 +4,8 @@ DEPS_SUBMODULES += lib/CMSIS_5 hw/mcu/st/cmsis_device_$(ST_FAMILY) hw/mcu/st/stm ST_CMSIS = hw/mcu/st/cmsis_device_$(ST_FAMILY) ST_HAL_DRIVER = hw/mcu/st/stm32$(ST_FAMILY)xx_hal_driver +include $(TOP)/$(BOARD_PATH)/board.mk + CFLAGS += \ -flto \ -mthumb \ @@ -11,15 +13,12 @@ CFLAGS += \ -mcpu=cortex-m3 \ -mfloat-abi=soft \ -nostdlib -nostartfiles \ - -DSTM32F103xB \ -DCFG_TUSB_MCU=OPT_MCU_STM32F1 # mcu driver cause following warnings #CFLAGS += -Wno-error=unused-parameter # All source paths should be relative to the top level. -LD_FILE = hw/bsp/$(BOARD)/STM32F103XB_FLASH.ld - SRC_C += \ src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c \ $(ST_CMSIS)/Source/Templates/system_stm32$(ST_FAMILY)xx.c \ @@ -29,14 +28,11 @@ SRC_C += \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_rcc_ex.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_gpio.c -SRC_S += \ - $(ST_CMSIS)/Source/Templates/gcc/startup_stm32f103xb.s - INC += \ + $(TOP)/$(BOARD_PATH) \ $(TOP)/lib/CMSIS_5/CMSIS/Core/Include \ $(TOP)/$(ST_CMSIS)/Include \ - $(TOP)/$(ST_HAL_DRIVER)/Inc \ - $(TOP)/hw/bsp/$(BOARD) + $(TOP)/$(ST_HAL_DRIVER)/Inc # For freeRTOS port source FREERTOS_PORT = ARM_CM3 @@ -45,5 +41,5 @@ FREERTOS_PORT = ARM_CM3 JLINK_DEVICE = stm32f103c8 # flash target ROM bootloader -flash: $(BUILD)/$(PROJECT).bin +flash-dfu-util: $(BUILD)/$(PROJECT).bin dfu-util -R -a 0 --dfuse-address 0x08000000 -D $< diff --git a/hw/bsp/stm32f103bluepill/stm32f1xx_hal_conf.h b/hw/bsp/stm32f1/stm32f1xx_hal_conf.h similarity index 100% rename from hw/bsp/stm32f103bluepill/stm32f1xx_hal_conf.h rename to hw/bsp/stm32f1/stm32f1xx_hal_conf.h diff --git a/lib/fatfs/diskio.c b/lib/fatfs/diskio.c index 401f583a2..a6132f27b 100644 --- a/lib/fatfs/diskio.c +++ b/lib/fatfs/diskio.c @@ -36,7 +36,7 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ // TODO change it to portable init -static DSTATUS disk_state[CFG_TUSB_HOST_DEVICE_MAX]; +static DSTATUS disk_state[CFG_TUH_DEVICE_MAX]; //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION @@ -61,7 +61,7 @@ static DRESULT wait_for_io_complete(uint8_t usb_addr) void diskio_init(void) { - memset(disk_state, STA_NOINIT, CFG_TUSB_HOST_DEVICE_MAX); + memset(disk_state, STA_NOINIT, CFG_TUH_DEVICE_MAX); } //pdrv Specifies the physical drive number. diff --git a/lib/fatfs/diskio.h b/lib/fatfs/diskio.h index 99743538a..f1f4bd327 100644 --- a/lib/fatfs/diskio.h +++ b/lib/fatfs/diskio.h @@ -83,7 +83,7 @@ DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); static inline bool disk_is_ready(BYTE pdrv); static inline bool disk_is_ready(BYTE pdrv) { - return (pdrv < CFG_TUSB_HOST_DEVICE_MAX) && + return (pdrv < CFG_TUH_DEVICE_MAX) && ( (disk_status(pdrv) & (STA_NOINIT | STA_NODISK)) == 0 ); } diff --git a/lib/fatfs/ffconf.h b/lib/fatfs/ffconf.h index bc132d2c3..782057ea4 100644 --- a/lib/fatfs/ffconf.h +++ b/lib/fatfs/ffconf.h @@ -128,7 +128,7 @@ / Physical Drive Configurations /----------------------------------------------------------------------------*/ -#define _VOLUMES CFG_TUSB_HOST_DEVICE_MAX +#define _VOLUMES CFG_TUH_DEVICE_MAX /* Number of volumes (logical drives) to be used. */ diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index fc7100e81..e7eaf4d05 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -51,7 +51,7 @@ typedef struct { //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; +static cdch_data_t cdch_data[CFG_TUH_DEVICE_MAX]; static inline cdch_data_t* get_itf(uint8_t dev_addr) { @@ -146,10 +146,10 @@ bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_co //--------------------------------------------------------------------+ void cdch_init(void) { - tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX); + tu_memclr(cdch_data, sizeof(cdch_data)); } -uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { (void) max_len; @@ -157,7 +157,7 @@ uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const // Protocol 0xFF can be RNDIS device for windows XP TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass && - 0xFF != itf_desc->bInterfaceProtocol, 0); + 0xFF != itf_desc->bInterfaceProtocol); cdch_data_t * p_cdc = get_itf(dev_addr); @@ -186,7 +186,7 @@ uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const // notification endpoint tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 ); + TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); p_cdc->ep_notif = desc_ep->bEndpointAddress; drv_len += tu_desc_len(p_desc); @@ -205,9 +205,9 @@ uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const for(uint32_t i=0; i<2; i++) { tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer, 0); + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep), 0); + TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep)); if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) { @@ -222,7 +222,7 @@ uint16_t cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const } } - return drv_len; + return true; } bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num) @@ -240,6 +240,8 @@ bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32 void cdch_close(uint8_t dev_addr) { + TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); + cdch_data_t * p_cdc = get_itf(dev_addr); tu_memclr(p_cdc, sizeof(cdch_data_t)); } diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index edcd258a8..0d435138b 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -121,11 +121,11 @@ void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_i //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void cdch_init (void); -uint16_t cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num); -bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void cdch_close (uint8_t dev_addr); +void cdch_init (void); +bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num); +bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close (uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c index 42b9993bc..cc4ffd1cf 100644 --- a/src/class/cdc/cdc_rndis_host.c +++ b/src/class/cdc/cdc_rndis_host.c @@ -40,10 +40,10 @@ //--------------------------------------------------------------------+ #define RNDIS_MSG_PAYLOAD_MAX (1024*4) -CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUSB_HOST_DEVICE_MAX][8]; +CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8]; CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX]; -static rndish_data_t rndish_data[CFG_TUSB_HOST_DEVICE_MAX]; +static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX]; // TODO Microsoft requires message length for any get command must be at least 4096 bytes @@ -88,7 +88,7 @@ static tusb_error_t rndis_body_subtask(void) OSAL_SUBTASK_BEGIN - for (relative_addr = 0; relative_addr < CFG_TUSB_HOST_DEVICE_MAX; relative_addr++) + for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++) { } @@ -103,12 +103,12 @@ static tusb_error_t rndis_body_subtask(void) //--------------------------------------------------------------------+ void rndish_init(void) { - tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUSB_HOST_DEVICE_MAX); + tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX); //------------- Task creation -------------// //------------- semaphore creation for notificaiton pipe -------------// - for(uint8_t i=0; iep_in, hid_itf->epin_buf, hid_itf->epin_size); -} - //--------------------------------------------------------------------+ -// Application API +// Interface API //--------------------------------------------------------------------+ uint8_t tuh_hid_instance_count(uint8_t dev_addr) @@ -98,6 +93,10 @@ uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance) return hid_itf->itf_protocol; } +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance) { hidh_interface_t* hid_itf = get_instance(dev_addr, instance); @@ -186,6 +185,20 @@ bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, u return true; } +//--------------------------------------------------------------------+ +// Interrupt Endpoint API +//--------------------------------------------------------------------+ + +bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + + // claim endpoint + TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) ); + + return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size); +} + //bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance) //{ // TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance)); @@ -215,11 +228,8 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3 if ( dir == TUSB_DIR_IN ) { TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance); - TU_LOG1_MEM(hid_itf->epin_buf, 8, 2); + TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2); tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, xferred_bytes); - - // queue next report - hidh_get_report(dev_addr, hid_itf); }else { if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, xferred_bytes); @@ -230,10 +240,13 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3 void hidh_close(uint8_t dev_addr) { + TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); + hidh_device_t* hid_dev = get_dev(dev_addr); + if (tuh_hid_umount_cb) { - for ( uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst); + for (uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst); } tu_memclr(hid_dev, sizeof(hidh_device_t)); @@ -247,19 +260,26 @@ static bool config_set_protocol (uint8_t dev_addr, tusb_control_requ static bool config_get_report_desc (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool config_get_report_desc_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); -uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); + +bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { (void) max_len; - TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0); + TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass); + + TU_LOG2("HID opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr); + + // len = interface + hid + n*endpoints + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); + TU_ASSERT(max_len >= drv_len); - uint16_t drv_len = sizeof(tusb_desc_interface_t); uint8_t const *p_desc = (uint8_t const *) desc_itf; //------------- HID descriptor -------------// p_desc = tu_desc_next(p_desc); tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; - TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType, 0); + TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType); // not enough interface, try to increase CFG_TUH_HID // TODO multiple devices @@ -267,13 +287,12 @@ uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0); //------------- Endpoint Descriptor -------------// - drv_len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType, 0); + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType); // TODO also open endpoint OUT - TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep), 0 ); + TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) ); hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count); hid_dev->inst_count++; @@ -290,9 +309,7 @@ uint16_t hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const hid_itf->protocol_mode = HID_PROTOCOL_BOOT; if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol; - drv_len += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); - - return drv_len; + return true; } bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num) @@ -334,7 +351,7 @@ static bool config_set_protocol(uint8_t dev_addr, tusb_control_request_t const * uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - TU_LOG2("HID Set Protocol\r\n"); + TU_LOG2("HID Set Protocol to Boot Mode\r\n"); hid_itf->protocol_mode = HID_PROTOCOL_BOOT; tusb_control_request_t const new_request = { @@ -367,26 +384,34 @@ static bool config_get_report_desc(uint8_t dev_addr, tusb_control_request_t cons uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); hidh_interface_t* hid_itf = get_instance(dev_addr, instance); - // Get Report Descriptor + // Get Report Descriptor if possible // using usbh enumeration buffer since report descriptor can be very long - TU_ASSERT( hid_itf->report_desc_len <= CFG_TUH_ENUMERATION_BUFSIZE ); - - TU_LOG2("HID Get Report Descriptor\r\n"); - tusb_control_request_t const new_request = + if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE ) { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_IN - }, - .bRequest = TUSB_REQ_GET_DESCRIPTOR, - .wValue = tu_u16(hid_itf->report_desc_type, 0), - .wIndex = itf_num, - .wLength = hid_itf->report_desc_len - }; + TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len); + + // Driver is mounted without report descriptor + config_driver_mount_complete(dev_addr, instance, NULL, 0); + }else + { + TU_LOG2("HID Get Report Descriptor\r\n"); + tusb_control_request_t const new_request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_IN + }, + .bRequest = TUSB_REQ_GET_DESCRIPTOR, + .wValue = tu_u16(hid_itf->report_desc_type, 0), + .wIndex = itf_num, + .wLength = hid_itf->report_desc_len + }; + + TU_ASSERT(tuh_control_xfer(dev_addr, &new_request, usbh_get_enum_buf(), config_get_report_desc_complete)); + } - TU_ASSERT(tuh_control_xfer(dev_addr, &new_request, usbh_get_enum_buf(), config_get_report_desc_complete)); return true; } @@ -394,23 +419,26 @@ static bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_reque { TU_ASSERT(XFER_RESULT_SUCCESS == result); - uint8_t const itf_num = (uint8_t) request->wIndex; - uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); - hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + uint8_t const itf_num = (uint8_t) request->wIndex; + uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num); uint8_t const* desc_report = usbh_get_enum_buf(); uint16_t const desc_len = request->wLength; + config_driver_mount_complete(dev_addr, instance, desc_report, desc_len); + + return true; +} + +static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + hidh_interface_t* hid_itf = get_instance(dev_addr, instance); + // enumeration is complete tuh_hid_mount_cb(dev_addr, instance, desc_report, desc_len); - // queue transfer for IN endpoint - hidh_get_report(dev_addr, hid_itf); - // notify usbh that driver enumeration is complete - usbh_driver_set_config_complete(dev_addr, itf_num); - - return true; + usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num); } //--------------------------------------------------------------------+ diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index ef203123d..fe09b03b2 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -38,10 +38,15 @@ //--------------------------------------------------------------------+ // TODO Highspeed interrupt can be up to 512 bytes -#ifndef CFG_TUH_HID_EP_BUFSIZE -#define CFG_TUH_HID_EP_BUFSIZE 64 +#ifndef CFG_TUH_HID_EPIN_BUFSIZE +#define CFG_TUH_HID_EPIN_BUFSIZE 64 #endif +#ifndef CFG_TUH_HID_EPOUT_BUFSIZE +#define CFG_TUH_HID_EPOUT_BUFSIZE 64 +#endif + + typedef struct { uint8_t report_id; @@ -54,7 +59,7 @@ typedef struct } tuh_hid_report_info_t; //--------------------------------------------------------------------+ -// Application API +// Interface API //--------------------------------------------------------------------+ // Get the number of HID instances @@ -66,6 +71,14 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance); // Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance); +// Parse report descriptor into array of report_info struct and return number of reports. +// For complicated report, application should write its own parser. +uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED; + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + // Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) // Note: Device will be initialized in Boot protocol for simplicity. // Application can use set_protocol() to switch back to Report protocol. @@ -79,13 +92,18 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol); // report_type is either Intput, Output or Feature, (value from hid_report_type_t) bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len); -// Parse report descriptor into array of report_info struct and return number of reports. -// For complicated report, application should write its own parser. -uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED; +//--------------------------------------------------------------------+ +// Interrupt Endpoint API +//--------------------------------------------------------------------+ // Check if the interface is ready to use //bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance); +// Try to receive next report on Interrupt Endpoint. Immediately return +// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available +// - false if failed to queue the transfer e.g endpoint is busy +bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance); + // Send report using interrupt endpoint // If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent. //void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len); @@ -97,6 +115,8 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, // Invoked when device with hid interface is mounted // Report descriptor is also available for use. tuh_hid_parse_report_descriptor() // can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report_desc, uint16_t desc_len); // Invoked when device with hid interface is un-mounted @@ -119,11 +139,11 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t ins //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void hidh_init (void); -uint16_t hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); -bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num); -bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); -void hidh_close (uint8_t dev_addr); +void hidh_init (void); +bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); +bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num); +bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void hidh_close (uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 2c027deff..8069353cd 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -69,13 +69,14 @@ typedef struct msc_csw_t csw; }msch_interface_t; -CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUSB_HOST_DEVICE_MAX]; +CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX]; // buffer used to read scsi information when mounted // largest response data currently is inquiry TODO Inquiry is not part of enum anymore CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)]; +TU_ATTR_ALWAYS_INLINE static inline msch_interface_t* get_itf(uint8_t dev_addr) { return &_msch_itf[dev_addr-1]; @@ -291,11 +292,13 @@ bool tuh_msc_reset(uint8_t dev_addr) //--------------------------------------------------------------------+ void msch_init(void) { - tu_memclr(_msch_itf, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX); + tu_memclr(_msch_itf, sizeof(_msch_itf)); } void msch_close(uint8_t dev_addr) { + TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, ); + msch_interface_t* p_msc = get_itf(dev_addr); // invoke Application Callback @@ -360,22 +363,22 @@ static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* c static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw); -uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) +bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) { TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass && MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol); // msc driver length is fixed uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t); - TU_ASSERT(drv_len <= max_len, 0); + TU_ASSERT(drv_len <= max_len); msch_interface_t* p_msc = get_itf(dev_addr); tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf); for(uint32_t i=0; i<2; i++) { - TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer, 0); - TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc), 0); + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc)); if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) { @@ -390,7 +393,7 @@ uint16_t msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const p_msc->itf_num = desc_itf->bInterfaceNumber; - return drv_len; + return true; } bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index 9e4217ba5..7718ad4fe 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -106,11 +106,11 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr); // Internal Class Driver API //--------------------------------------------------------------------+ -void msch_init (void); -uint16_t msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); -bool msch_set_config (uint8_t dev_addr, uint8_t itf_num); -bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void msch_close (uint8_t dev_addr); +void msch_init (void); +bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); +bool msch_set_config (uint8_t dev_addr, uint8_t itf_num); +void msch_close (uint8_t dev_addr); +bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); #ifdef __cplusplus } diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index 6718a97bf..8c59b4ea7 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -103,6 +103,13 @@ uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) return num_read; } +void tud_vendor_n_read_flush (uint8_t itf) +{ + vendord_interface_t* p_itf = &_vendord_itf[itf]; + tu_fifo_clear(&p_itf->rx_ff); + _prep_out_transaction(p_itf); +} + //--------------------------------------------------------------------+ // Write API //--------------------------------------------------------------------+ @@ -199,6 +206,8 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, ui TU_BREAKPOINT(); } + maybe_transmit(p_vendor); + return drv_len; } diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 6d9c784c0..844693c68 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -45,6 +45,7 @@ bool tud_vendor_n_mounted (uint8_t itf); uint32_t tud_vendor_n_available (uint8_t itf); uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize); bool tud_vendor_n_peek (uint8_t itf, uint8_t* u8); +void tud_vendor_n_read_flush (uint8_t itf); uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); uint32_t tud_vendor_n_write_available (uint8_t itf); @@ -59,6 +60,7 @@ static inline bool tud_vendor_mounted (void); static inline uint32_t tud_vendor_available (void); static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize); static inline bool tud_vendor_peek (uint8_t* u8); +static inline void tud_vendor_read_flush (void); static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize); static inline uint32_t tud_vendor_write_str (char const* str); static inline uint32_t tud_vendor_write_available (void); @@ -99,6 +101,11 @@ static inline bool tud_vendor_peek (uint8_t* u8) return tud_vendor_n_peek(0, u8); } +static inline void tud_vendor_read_flush(void) +{ + tud_vendor_n_read_flush(0); +} + static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize) { return tud_vendor_n_write(0, buffer, bufsize); diff --git a/src/class/vendor/vendor_host.c b/src/class/vendor/vendor_host.c index f7a6b2bf0..1e28e9af4 100644 --- a/src/class/vendor/vendor_host.c +++ b/src/class/vendor/vendor_host.c @@ -41,7 +41,7 @@ //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -custom_interface_info_t custom_interface[CFG_TUSB_HOST_DEVICE_MAX]; +custom_interface_info_t custom_interface[CFG_TUH_DEVICE_MAX]; static tusb_error_t cush_validate_paras(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length) { @@ -90,7 +90,7 @@ tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t p //--------------------------------------------------------------------+ void cush_init(void) { - tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUSB_HOST_DEVICE_MAX); + tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUH_DEVICE_MAX); } tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index c8a66a879..c2356ffee 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -73,6 +73,23 @@ #include "tusb_error.h" // TODO remove #include "tusb_timeout.h" // TODO remove +//--------------------------------------------------------------------+ +// Internal Helper used by Host and Device Stack +//--------------------------------------------------------------------+ + +// Check if endpoint descriptor is valid per USB specs +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed); + +// Bind all endpoint of a interface descriptor to class driver +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id); + +// Calculate total length of n interfaces (depending on IAD) +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len); + +//--------------------------------------------------------------------+ +// Internal Inline Functions +//--------------------------------------------------------------------+ + //------------- Mem -------------// #define tu_memclr(buffer, size) memset((buffer), 0, (size)) #define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var))) diff --git a/src/device/dcd_attr.h b/src/device/dcd_attr.h index 7772a6d96..a35fc0ac5 100644 --- a/src/device/dcd_attr.h +++ b/src/device/dcd_attr.h @@ -60,7 +60,7 @@ #elif TU_CHECK_MCU(MIMXRT10XX) #define DCD_ATTR_ENDPOINT_MAX 8 -#elif TU_CHECK_MCU(MKL25ZXX) +#elif TU_CHECK_MCU(MKL25ZXX) || TU_CHECK_MCU(K32L2BXX) #define DCD_ATTR_ENDPOINT_MAX 16 //------------- Nordic -------------// diff --git a/src/device/usbd.c b/src/device/usbd.c index 8b4f4e7e3..e12dafcfd 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -274,7 +274,6 @@ static osal_mutex_t _usbd_mutex; //--------------------------------------------------------------------+ // Prototypes //--------------------------------------------------------------------+ -static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id); static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request); static bool process_set_config(uint8_t rhport, uint8_t cfg_num); static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request); @@ -857,12 +856,12 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) while( p_desc < desc_end ) { - tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL; + tusb_desc_interface_assoc_t const * desc_iad = NULL; // Class will always starts with Interface Association (if any) and then Interface descriptor if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) { - desc_itf_assoc = (tusb_desc_interface_assoc_t const *) p_desc; + desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; p_desc = tu_desc_next(p_desc); // next to Interface } @@ -871,6 +870,13 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) tusb_desc_interface_t const * desc_itf = (tusb_desc_interface_t const*) p_desc; uint16_t const remaining_len = desc_end-p_desc; + // Interface number must not be used already + TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]); + + // TODO usbd can calculate the total length used for driver --> driver open() does not need to calculate it + // uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, desc_iad ? desc_iad->bInterfaceCount : 1, desc_end-p_desc); + + // Find driver for this interface uint8_t drv_id; for (drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) { @@ -882,30 +888,30 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) // Open successfully, check if length is correct TU_ASSERT( sizeof(tusb_desc_interface_t) <= drv_len && drv_len <= remaining_len); - // Interface number must not be used already - TU_ASSERT(DRVID_INVALID == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]); - TU_LOG2(" %s opened\r\n", driver->name); + + // bind interface to found driver _usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id; - // If IAD exist, assign all interfaces to the same driver - if (desc_itf_assoc) + // If using IAD, bind all interfaces to the same driver + if (desc_iad) { // IAD's first interface number and class should match with opened interface - TU_ASSERT(desc_itf_assoc->bFirstInterface == desc_itf->bInterfaceNumber && - desc_itf_assoc->bFunctionClass == desc_itf->bInterfaceClass); + TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber && + desc_iad->bFunctionClass == desc_itf->bInterfaceClass); - for(uint8_t i=1; ibInterfaceCount; i++) + for(uint8_t i=1; ibInterfaceCount; i++) { _usbd_dev.itf2drv[desc_itf->bInterfaceNumber+i] = drv_id; } } - mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, drv_len, drv_id); // TODO refactor + // bind all endpoints to found driver + tu_edpt_bind_driver(_usbd_dev.ep2drv, desc_itf, drv_len, drv_id); p_desc += drv_len; // next interface - break; + break; // exit driver find loop } } @@ -919,25 +925,6 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) return true; } -// Helper marking endpoint of interface belongs to class driver -static void mark_interface_endpoint(uint8_t ep2drv[][2], uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id) -{ - uint16_t len = 0; - - while( len < desc_len ) - { - if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) - { - uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; - - ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; - } - - len = (uint16_t)(len + tu_desc_len(p_desc)); - p_desc = tu_desc_next(p_desc); - } -} - // return descriptor's buffer and update desc_len static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request) { @@ -1177,41 +1164,8 @@ void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) { - uint16_t const max_packet_size = tu_le16toh(desc_ep->wMaxPacketSize.size); - - TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size); TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX); - - switch (desc_ep->bmAttributes.xfer) - { - case TUSB_XFER_ISOCHRONOUS: - { - uint16_t const spec_size = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 1023); - TU_ASSERT(max_packet_size <= spec_size); - } - break; - - case TUSB_XFER_BULK: - if (_usbd_dev.speed == TUSB_SPEED_HIGH) - { - // Bulk highspeed must be EXACTLY 512 - TU_ASSERT(max_packet_size == 512); - }else - { - // TODO Bulk fullspeed can only be 8, 16, 32, 64 - TU_ASSERT(max_packet_size <= 64); - } - break; - - case TUSB_XFER_INTERRUPT: - { - uint16_t const spec_size = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 64); - TU_ASSERT(max_packet_size <= spec_size); - } - break; - - default: return false; - } + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed)); return dcd_edpt_open(rhport, desc_ep); } diff --git a/src/device/usbd.h b/src/device/usbd.h index c85616c95..9becf2d0d 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -734,7 +734,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb /* Primary Interface */ #define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ - 9, TUSB_DESC_INTERFACE, _itfnum, _stridx, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, _stridx, \ /* Endpoint In for events */ \ 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \ /* Endpoint In for ACL data */ \ diff --git a/src/host/hcd.h b/src/host/hcd.h index 7a9aadc73..eb53d2e80 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -30,6 +30,7 @@ #include "common/tusb_common.h" #include "osal/osal.h" #include "common/tusb_fifo.h" +#include "hcd_attr.h" #ifdef __cplusplus extern "C" { @@ -62,6 +63,7 @@ typedef struct struct { uint8_t hub_addr; uint8_t hub_port; + uint8_t speed; } connection; // XFER_COMPLETE @@ -84,12 +86,20 @@ typedef struct // Max number of endpoints per device enum { // TODO better computation - HCD_MAX_ENDPOINT = CFG_TUSB_HOST_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3), + HCD_MAX_ENDPOINT = CFG_TUH_DEVICE_MAX*(CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3), HCD_MAX_XFER = HCD_MAX_ENDPOINT*2, }; //#define HCD_MAX_ENDPOINT 16 //#define HCD_MAX_XFER 16 + +typedef struct { + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; +} hcd_devtree_info_t; + #endif //--------------------------------------------------------------------+ @@ -140,9 +150,16 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr); //--------------------------------------------------------------------+ -// Event API (implemented by stack) +// USBH implemented API //--------------------------------------------------------------------+ +// Get device tree information of a device +// USB device tree can be complicated and manged by USBH, this help HCD to retrieve +// needed topology info to carry out its work +extern void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info); + +//------------- Event API -------------// + // Called by HCD to notify stack extern void hcd_event_handler(hcd_event_t const* event, bool in_isr); diff --git a/src/host/hcd_attr.h b/src/host/hcd_attr.h new file mode 100644 index 000000000..729fc407b --- /dev/null +++ b/src/host/hcd_attr.h @@ -0,0 +1,105 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, 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. + */ + +#ifndef TUSB_HCD_ATTR_H_ +#define TUSB_HCD_ATTR_H_ + +#include "tusb_option.h" + +// Attribute includes +// - ENDPOINT_MAX: max (logical) number of endpoint +// - PORT_HIGHSPEED: mask to indicate which port support highspeed mode, bit0 for port0 and so on. + +//------------- NXP -------------// +#if TU_CHECK_MCU(LPC175X_6X) || TU_CHECK_MCU(LPC177X_8X) || TU_CHECK_MCU(LPC40XX) + #define HCD_ATTR_OHCI + +#elif TU_CHECK_MCU(LPC18XX) || TU_CHECK_MCU(LPC43XX) + #define HCD_ATTR_EHCI_TRANSDIMENSION + +#elif TU_CHECK_MCU(LPC54XXX) + // #define HCD_ATTR_EHCI_NXP_PTD + +#elif TU_CHECK_MCU(LPC55XX) + // #define HCD_ATTR_EHCI_NXP_PTD + +#elif TU_CHECK_MCU(MIMXRT10XX) + #define HCD_ATTR_EHCI_TRANSDIMENSION + +#elif TU_CHECK_MCU(MKL25ZXX) + +//------------- Microchip -------------// +#elif TU_CHECK_MCU(SAMD21) || TU_CHECK_MCU(SAMD51) || TU_CHECK_MCU(SAME5X) || \ + TU_CHECK_MCU(SAMD11) || TU_CHECK_MCU(SAML21) || TU_CHECK_MCU(SAML22) + +#elif TU_CHECK_MCU(SAMG) + +#elif TU_CHECK_MCU(SAMX7X) + +//------------- ST -------------// +#elif TU_CHECK_MCU(STM32F0) || TU_CHECK_MCU(STM32F1) || TU_CHECK_MCU(STM32F3) || \ + TU_CHECK_MCU(STM32L0) || TU_CHECK_MCU(STM32L1) || TU_CHECK_MCU(STM32L4) + +#elif TU_CHECK_MCU(STM32F2) || TU_CHECK_MCU(STM32F4) || TU_CHECK_MCU(STM32F3) + +#elif TU_CHECK_MCU(STM32F7) + +#elif TU_CHECK_MCU(STM32H7) + +//------------- Sony -------------// +#elif TU_CHECK_MCU(CXD56) + +//------------- Nuvoton -------------// +#elif TU_CHECK_MCU(NUC505) + +//------------- Espressif -------------// +#elif TU_CHECK_MCU(ESP32S2) || TU_CHECK_MCU(ESP32S3) + +//------------- Raspberry Pi -------------// +#elif TU_CHECK_MCU(RP2040) + +//------------- Silabs -------------// +#elif TU_CHECK_MCU(EFM32GG) || TU_CHECK_MCU(EFM32GG11) || TU_CHECK_MCU(EFM32GG12) + +//------------- Renesas -------------// +#elif TU_CHECK_MCU(RX63X) || TU_CHECK_MCU(RX65X) || TU_CHECK_MCU(RX72N) + +//#elif TU_CHECK_MCU(MM32F327X) +// #define DCD_ATTR_ENDPOINT_MAX not known yet + +//------------- GigaDevice -------------// +#elif TU_CHECK_MCU(GD32VF103) + +#else +// #warning "DCD_ATTR_ENDPOINT_MAX is not defined for this MCU, default to 8" +#endif + +// Default to fullspeed if not defined +//#ifndef PORT_HIGHSPEED +// #define DCD_ATTR_PORT_HIGHSPEED 0x00 +//#endif + +#endif diff --git a/src/host/hub.c b/src/host/hub.c index 2ead5bed1..fd4dbd04a 100644 --- a/src/host/hub.c +++ b/src/host/hub.c @@ -45,8 +45,14 @@ typedef struct hub_port_status_response_t port_status; } hub_interface_t; -CFG_TUSB_MEM_SECTION static hub_interface_t hub_data[CFG_TUSB_HOST_DEVICE_MAX]; -TU_ATTR_ALIGNED(4) CFG_TUSB_MEM_SECTION static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)]; +CFG_TUSB_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB]; +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)]; + +TU_ATTR_ALWAYS_INLINE +static inline hub_interface_t* get_itf(uint8_t dev_addr) +{ + return &hub_data[dev_addr-1-CFG_TUH_DEVICE_MAX]; +} #if CFG_TUSB_DEBUG static char const* const _hub_feature_str[] = @@ -144,19 +150,20 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp, tuh_con //--------------------------------------------------------------------+ void hub_init(void) { - tu_memclr(hub_data, CFG_TUSB_HOST_DEVICE_MAX*sizeof(hub_interface_t)); + tu_memclr(hub_data, sizeof(hub_data)); } -uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - // hub driver does not support multiple TT yet TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass && - 0 == itf_desc->bInterfaceSubClass && - 1 <= itf_desc->bInterfaceProtocol, 0); + 0 == itf_desc->bInterfaceSubClass); + + // hub driver does not support multiple TT yet + TU_VERIFY(itf_desc->bInterfaceProtocol <= 1); // msc driver length is fixed uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_desc_endpoint_t); - TU_ASSERT(drv_len <= max_len, 0); + TU_ASSERT(drv_len <= max_len); //------------- Interrupt Status endpoint -------------// tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); @@ -166,21 +173,26 @@ uint16_t hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep)); - hub_data[dev_addr-1].itf_num = itf_desc->bInterfaceNumber; - hub_data[dev_addr-1].ep_in = desc_ep->bEndpointAddress; + hub_interface_t* p_hub = get_itf(dev_addr); - return drv_len; + p_hub->itf_num = itf_desc->bInterfaceNumber; + p_hub->ep_in = desc_ep->bEndpointAddress; + + return true; } void hub_close(uint8_t dev_addr) { - tu_memclr(&hub_data[dev_addr-1], sizeof( hub_interface_t)); + TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, ); + hub_interface_t* p_hub = get_itf(dev_addr); + + if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t)); } bool hub_status_pipe_queue(uint8_t dev_addr) { - hub_interface_t * p_hub = &hub_data[dev_addr-1]; - return usbh_edpt_xfer(dev_addr, p_hub->ep_in, &p_hub->status_change, 1); + hub_interface_t* hub_itf = get_itf(dev_addr); + return usbh_edpt_xfer(dev_addr, hub_itf->ep_in, &hub_itf->status_change, 1); } @@ -193,7 +205,7 @@ static bool config_port_power_complete (uint8_t dev_addr, tusb_control_request_t bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) { - hub_interface_t* p_hub = &hub_data[dev_addr-1]; + hub_interface_t* p_hub = get_itf(dev_addr); TU_ASSERT(itf_num == p_hub->itf_num); // Get Hub Descriptor @@ -221,7 +233,7 @@ static bool config_set_port_power (uint8_t dev_addr, tusb_control_request_t cons (void) request; TU_ASSERT(XFER_RESULT_SUCCESS == result); - hub_interface_t* p_hub = &hub_data[dev_addr-1]; + hub_interface_t* p_hub = get_itf(dev_addr); // only use number of ports in hub descriptor descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer; @@ -237,7 +249,7 @@ static bool config_set_port_power (uint8_t dev_addr, tusb_control_request_t cons static bool config_port_power_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { TU_ASSERT(XFER_RESULT_SUCCESS == result); - hub_interface_t* p_hub = &hub_data[dev_addr-1]; + hub_interface_t* p_hub = get_itf(dev_addr); if (request->wIndex == p_hub->port_count) { @@ -271,7 +283,7 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32 (void) ep_addr; TU_ASSERT(result == XFER_RESULT_SUCCESS); - hub_interface_t * p_hub = &hub_data[dev_addr-1]; + hub_interface_t* p_hub = get_itf(dev_addr); TU_LOG2(" Port Status Change = 0x%02X\r\n", p_hub->status_change); @@ -293,7 +305,8 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32 static bool connection_get_status_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { TU_ASSERT(result == XFER_RESULT_SUCCESS); - hub_interface_t * p_hub = &hub_data[dev_addr-1]; + + hub_interface_t* p_hub = get_itf(dev_addr); uint8_t const port_num = (uint8_t) request->wIndex; // Connection change @@ -321,7 +334,7 @@ static bool connection_clear_conn_change_complete (uint8_t dev_addr, tusb_contro { TU_ASSERT(result == XFER_RESULT_SUCCESS); - hub_interface_t * p_hub = &hub_data[dev_addr-1]; + hub_interface_t* p_hub = get_itf(dev_addr); uint8_t const port_num = (uint8_t) request->wIndex; if ( p_hub->port_status.status.connection ) @@ -352,7 +365,7 @@ static bool connection_port_reset_complete (uint8_t dev_addr, tusb_control_reque { TU_ASSERT(result == XFER_RESULT_SUCCESS); - // usbh_hub_t * p_hub = &hub_data[dev_addr-1]; + // hub_interface_t* p_hub = get_itf(dev_addr); uint8_t const port_num = (uint8_t) request->wIndex; // submit attach event diff --git a/src/host/hub.h b/src/host/hub.h index c9ffe4985..c4d544193 100644 --- a/src/host/hub.h +++ b/src/host/hub.h @@ -181,11 +181,11 @@ bool hub_status_pipe_queue(uint8_t dev_addr); //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -void hub_init (void); -uint16_t hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -bool hub_set_config (uint8_t dev_addr, uint8_t itf_num); -bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void hub_close (uint8_t dev_addr); +void hub_init (void); +bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool hub_set_config (uint8_t dev_addr, uint8_t itf_num); +bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void hub_close (uint8_t dev_addr); #ifdef __cplusplus } diff --git a/src/host/usbh.c b/src/host/usbh.c index 93621c42a..51b8f85b6 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -28,19 +28,100 @@ #if TUSB_OPT_HOST_ENABLED -#ifndef CFG_TUH_TASK_QUEUE_SZ -#define CFG_TUH_TASK_QUEUE_SZ 16 -#endif - #include "tusb.h" #include "host/usbh.h" #include "host/usbh_classdriver.h" #include "hub.h" -#include "usbh_hcd.h" + +//--------------------------------------------------------------------+ +// USBH Configuration +//--------------------------------------------------------------------+ + +// TODO remove,update +#ifndef CFG_TUH_EP_MAX +#define CFG_TUH_EP_MAX 9 +#endif + +#ifndef CFG_TUH_TASK_QUEUE_SZ +#define CFG_TUH_TASK_QUEUE_SZ 16 +#endif + +// Debug level of USBD +#define USBH_DBG_LVL 2 + +//--------------------------------------------------------------------+ +// USBH-HCD common data structure +//--------------------------------------------------------------------+ + +typedef struct { + //------------- port -------------// + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; + + //------------- device descriptor -------------// + uint16_t vid; + uint16_t pid; + + uint8_t ep0_size; + uint8_t i_manufacturer; + uint8_t i_product; + uint8_t i_serial; + + //------------- configuration descriptor -------------// + // uint8_t interface_count; // bNumInterfaces alias + + //------------- device -------------// + struct TU_ATTR_PACKED + { + uint8_t connected : 1; + uint8_t addressed : 1; + uint8_t configured : 1; + uint8_t suspended : 1; + }; + + volatile uint8_t state; // device state, value from enum tusbh_device_state_t + + uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) + uint8_t ep2drv[CFG_TUH_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid ) + + struct TU_ATTR_PACKED + { + volatile bool busy : 1; + volatile bool stalled : 1; + volatile bool claimed : 1; + + // TODO merge ep2drv here, 4-bit should be sufficient + }ep_status[CFG_TUH_EP_MAX][2]; + + // Mutex for claiming endpoint, only needed when using with preempted RTOS +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_def_t mutexdef; + osal_mutex_t mutex; +#endif + +} usbh_device_t; + +typedef struct +{ + uint8_t rhport; + uint8_t hub_addr; + uint8_t hub_port; + uint8_t speed; + + volatile uint8_t connected; +} usbh_dev0_t; + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ + +// Invalid driver ID in itf2drv[] ep2drv[][] mapping +enum { DRVID_INVALID = 0xFFu }; +enum { ADDR_INVALID = 0xFFu }; + #if CFG_TUSB_DEBUG >= 2 #define DRIVER_NAME(_name) .name = _name, #else @@ -52,7 +133,6 @@ static usbh_class_driver_t const usbh_class_drivers[] = #if CFG_TUH_CDC { DRIVER_NAME("CDC") - .class_code = TUSB_CLASS_CDC, .init = cdch_init, .open = cdch_open, .set_config = cdch_set_config, @@ -64,7 +144,6 @@ static usbh_class_driver_t const usbh_class_drivers[] = #if CFG_TUH_MSC { DRIVER_NAME("MSC") - .class_code = TUSB_CLASS_MSC, .init = msch_init, .open = msch_open, .set_config = msch_set_config, @@ -76,7 +155,6 @@ static usbh_class_driver_t const usbh_class_drivers[] = #if CFG_TUH_HID { DRIVER_NAME("HID") - .class_code = TUSB_CLASS_HID, .init = hidh_init, .open = hidh_open, .set_config = hidh_set_config, @@ -88,7 +166,6 @@ static usbh_class_driver_t const usbh_class_drivers[] = #if CFG_TUH_HUB { DRIVER_NAME("HUB") - .class_code = TUSB_CLASS_HUB, .init = hub_init, .open = hub_open, .set_config = hub_set_config, @@ -100,7 +177,6 @@ static usbh_class_driver_t const usbh_class_drivers[] = #if CFG_TUH_VENDOR { DRIVER_NAME("VENDOR") - .class_code = TUSB_CLASS_VENDOR_SPECIFIC, .init = cush_init, .open = cush_open_subtask, .xfer_cb = cush_isr, @@ -122,8 +198,12 @@ enum { CONFIG_NUM = 1 }; // default to use configuration 1 static bool _usbh_initialized = false; -// including zero-address -CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; +// Device with address = 0 for enumeration +static usbh_dev0_t _dev0; + +// all devices excluding zero-address +// hub address start from CFG_TUH_DEVICE_MAX+1 +CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB]; // Event queue // role device/host is used by OS NONE for mutex (disable usb isr) @@ -132,9 +212,18 @@ static osal_queue_t _usbh_q; CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE]; -//------------- Helper Function Prototypes -------------// +//------------- Helper Function -------------// + +TU_ATTR_ALWAYS_INLINE +static inline usbh_device_t* get_device(uint8_t dev_addr) +{ + TU_ASSERT(dev_addr, NULL); + return &_usbh_devices[dev_addr-1]; +} + static bool enum_new_device(hcd_event_t* event); static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port); +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); // from usbh_control.c extern bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); @@ -142,15 +231,28 @@ extern bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result //--------------------------------------------------------------------+ // PUBLIC API (Parameter Verification is required) //--------------------------------------------------------------------+ -bool tuh_device_configured(uint8_t dev_addr) +bool tuh_mounted(uint8_t dev_addr) { - return _usbh_devices[dev_addr].configured; + return get_device(dev_addr)->configured; } -tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr) +bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid) { - TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_SPEED_INVALID); - return (tusb_speed_t) _usbh_devices[dev_addr].speed; + *vid = *pid = 0; + + TU_VERIFY(tuh_mounted(dev_addr)); + + usbh_device_t const* dev = get_device(dev_addr); + + *vid = dev->vid; + *pid = dev->pid; + + return true; +} + +tusb_speed_t tuh_speed_get (uint8_t dev_addr) +{ + return (tusb_speed_t) (dev_addr ? get_device(dev_addr)->speed : _dev0.speed); } #if CFG_TUSB_OS == OPT_OS_NONE @@ -179,24 +281,25 @@ bool tuh_init(uint8_t rhport) TU_LOG2("USBH init\r\n"); - tu_memclr(_usbh_devices, sizeof(usbh_device_t)*(CFG_TUSB_HOST_DEVICE_MAX+1)); + tu_memclr(_usbh_devices, sizeof(_usbh_devices)); + tu_memclr(&_dev0, sizeof(_dev0)); //------------- Enumeration & Reporter Task init -------------// _usbh_q = osal_queue_create( &_usbh_qdef ); TU_ASSERT(_usbh_q != NULL); //------------- Semaphore, Mutex for Control Pipe -------------// - for(uint8_t i=0; imutex = osal_mutex_create(&dev->mutexdef); TU_ASSERT(dev->mutex); #endif - memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping + memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping } // Class drivers init @@ -267,26 +370,35 @@ void tuh_task(void) case HCD_EVENT_XFER_COMPLETE: { - usbh_device_t* dev = &_usbh_devices[event.dev_addr]; uint8_t const ep_addr = event.xfer_complete.ep_addr; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const ep_dir = tu_edpt_dir(ep_addr); TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len); - dev->ep_status[epnum][ep_dir].busy = false; - dev->ep_status[epnum][ep_dir].claimed = 0; - - if ( 0 == epnum ) + if (event.dev_addr == 0) { + // device 0 only has control endpoint + TU_ASSERT(epnum == 0, ); usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); - }else + } + else { - uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; - TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, ); + usbh_device_t* dev = get_device(event.dev_addr); + dev->ep_status[epnum][ep_dir].busy = false; + dev->ep_status[epnum][ep_dir].claimed = 0; - TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); - usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + if ( 0 == epnum ) + { + usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + }else + { + uint8_t drv_id = dev->ep2drv[epnum][ep_dir]; + TU_ASSERT(drv_id < USBH_CLASS_DRIVER_COUNT, ); + + TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name); + usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len); + } } } break; @@ -306,7 +418,7 @@ void tuh_task(void) uint8_t usbh_get_rhport(uint8_t dev_addr) { - return _usbh_devices[dev_addr].rhport; + return (dev_addr == 0) ? _dev0.rhport : get_device(dev_addr)->rhport; } uint8_t* usbh_get_enum_buf(void) @@ -314,155 +426,29 @@ uint8_t* usbh_get_enum_buf(void) return _usbh_ctrl_buf; } -//--------------------------------------------------------------------+ -// Endpoint API -//--------------------------------------------------------------------+ - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = &_usbh_devices[dev_addr]; - -#if CFG_TUSB_OS != OPT_OS_NONE - // pre-check to help reducing mutex lock - TU_VERIFY((dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0)); - osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif - - // can only claim the endpoint if it is not busy and not claimed yet. - bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0); - if (ret) - { - dev->ep_status[epnum][dir].claimed = 1; - } - -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_unlock(dev->mutex); -#endif - - return ret; -} - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = &_usbh_devices[dev_addr]; - -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); -#endif - - // can only release the endpoint if it is claimed and not busy - bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 1); - if (ret) - { - dev->ep_status[epnum][dir].claimed = 0; - } - -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_unlock(dev->mutex); -#endif - - return ret; -} - -// TODO has some duplication code with device, refactor later -bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = &_usbh_devices[dev_addr]; - - TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); - - // Attempt to transfer on a busy endpoint, sound like an race condition ! - TU_ASSERT(dev->ep_status[epnum][dir].busy == 0); - - // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() - // could return and USBH task can preempt and clear the busy - dev->ep_status[epnum][dir].busy = true; - - if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) - { - TU_LOG2("OK\r\n"); - return true; - }else - { - // HCD error, mark endpoint as ready to allow next transfer - dev->ep_status[epnum][dir].busy = false; - dev->ep_status[epnum][dir].claimed = 0; - TU_LOG2("failed\r\n"); - TU_BREAKPOINT(); - return false; - } -} - -bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) -{ - TU_LOG2("Open EP Control with Size = %u\r\n", max_packet_size); - - tusb_desc_endpoint_t ep0_desc = - { - .bLength = sizeof(tusb_desc_endpoint_t), - .bDescriptorType = TUSB_DESC_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, - .wMaxPacketSize = { .size = max_packet_size }, - .bInterval = 0 - }; - - return hcd_edpt_open(_usbh_devices[dev_addr].rhport, dev_addr, &ep0_desc); -} - -bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) -{ - TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, desc_ep->wMaxPacketSize.size); - - bool ret = hcd_edpt_open(rhport, dev_addr, desc_ep); - - if (ret) - { - usbh_device_t* dev = &_usbh_devices[dev_addr]; - - // new endpoints belongs to latest interface (last valid value) - // TODO FIXME not true with ISO - uint8_t drvid = 0xff; - for(uint8_t i=0; i < sizeof(dev->itf2drv); i++) - { - if ( dev->itf2drv[i] == 0xff ) break; - drvid = dev->itf2drv[i]; - } - TU_ASSERT(drvid < USBH_CLASS_DRIVER_COUNT); - - uint8_t const ep_addr = desc_ep->bEndpointAddress; - dev->ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = drvid; - } - - return ret; -} - -bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - usbh_device_t* dev = &_usbh_devices[dev_addr]; - - return dev->ep_status[epnum][dir].busy; -} - - //--------------------------------------------------------------------+ // HCD Event Handler //--------------------------------------------------------------------+ +void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info) +{ + if (dev_addr) + { + usbh_device_t const* dev = get_device(dev_addr); + + devtree_info->rhport = dev->rhport; + devtree_info->hub_addr = dev->hub_addr; + devtree_info->hub_port = dev->hub_port; + devtree_info->speed = dev->speed; + }else + { + devtree_info->rhport = _dev0.rhport; + devtree_info->hub_addr = _dev0.hub_addr; + devtree_info->hub_port = _dev0.hub_port; + devtree_info->speed = _dev0.speed; + } +} + void hcd_event_handler(hcd_event_t const* event, bool in_isr) { switch (event->event_id) @@ -473,7 +459,6 @@ void hcd_event_handler(hcd_event_t const* event, bool in_isr) } } -// interrupt caused by a TD (with IOC=1) in pipe of class class_code void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr) { hcd_event_t event = @@ -526,9 +511,11 @@ void hcd_event_device_remove(uint8_t hostid, bool in_isr) void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) { //------------- find the all devices (star-network) under port that is unplugged -------------// - for (uint8_t dev_addr = 0; dev_addr <= CFG_TUSB_HOST_DEVICE_MAX; dev_addr ++) + // TODO mark as disconnected in ISR, also handle dev0 + for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ ) { - usbh_device_t* dev = &_usbh_devices[dev_addr]; + usbh_device_t* dev = &_usbh_devices[dev_id]; + uint8_t const dev_addr = dev_id+1; // TODO Hub multiple level if (dev->rhport == rhport && @@ -546,8 +533,8 @@ void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port usbh_class_drivers[drv_id].close(dev_addr); } - memset(dev->itf2drv, 0xff, sizeof(dev->itf2drv)); // invalid mapping - memset(dev->ep2drv , 0xff, sizeof(dev->ep2drv )); // invalid mapping + memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping + memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping hcd_device_close(rhport, dev_addr); @@ -559,24 +546,29 @@ void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port //--------------------------------------------------------------------+ // INTERNAL HELPER //--------------------------------------------------------------------+ -static uint8_t get_new_address(void) +static uint8_t get_new_address(bool is_hub) { - for (uint8_t addr=1; addr <= CFG_TUSB_HOST_DEVICE_MAX; addr++) + uint8_t const start = (is_hub ? CFG_TUH_DEVICE_MAX : 0) + 1; + uint8_t const count = (is_hub ? CFG_TUH_HUB : CFG_TUH_DEVICE_MAX); + + for (uint8_t i=0; i < count; i++) { - if (_usbh_devices[addr].state == TUSB_DEVICE_STATE_UNPLUG) return addr; + uint8_t const addr = start + i; + if (get_device(addr)->state == TUSB_DEVICE_STATE_UNPLUG) return addr; } - return CFG_TUSB_HOST_DEVICE_MAX+1; + return ADDR_INVALID; } void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { - usbh_device_t* dev = &_usbh_devices[dev_addr]; + usbh_device_t* dev = get_device(dev_addr); for(itf_num++; itf_num < sizeof(dev->itf2drv); itf_num++) { // continue with next valid interface + // TODO skip IAD binding interface such as CDCs uint8_t const drv_id = dev->itf2drv[itf_num]; - if (drv_id != 0xff) + if (drv_id != DRVID_INVALID) { usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num); @@ -613,6 +605,7 @@ static bool enum_get_config_desc_complete (uint8_t dev_addr, tusb_control_ static bool enum_set_config_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result); static bool parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg); +#if CFG_TUH_HUB static bool enum_hub_clear_reset0_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { (void) dev_addr; (void) request; @@ -625,12 +618,11 @@ static bool enum_hub_clear_reset1_complete(uint8_t dev_addr, tusb_control_reques { (void) dev_addr; (void) request; TU_ASSERT(XFER_RESULT_SUCCESS == result); - usbh_device_t* dev0 = &_usbh_devices[0]; enum_request_set_addr(); // done with hub, waiting for next data on status pipe - (void) hub_status_pipe_queue( dev0->hub_addr ); + (void) hub_status_pipe_queue( _dev0.hub_addr ); return true; } @@ -639,7 +631,6 @@ static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request { (void) dev_addr; (void) request; TU_ASSERT(XFER_RESULT_SUCCESS == result); - usbh_device_t* dev0 = &_usbh_devices[0]; hub_port_status_response_t port_status; memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); @@ -647,7 +638,7 @@ static bool enum_hub_get_status1_complete(uint8_t dev_addr, tusb_control_request // Acknowledge Port Reset Change if Reset Successful if (port_status.change.reset) { - TU_ASSERT( hub_port_clear_feature(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) ); + TU_ASSERT( hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset1_complete) ); } return true; @@ -657,7 +648,6 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request { (void) dev_addr; (void) request; TU_ASSERT(XFER_RESULT_SUCCESS == result); - usbh_device_t* dev0 = &_usbh_devices[0]; hub_port_status_response_t port_status; memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t)); @@ -668,74 +658,35 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request return hub_status_pipe_queue(dev_addr); } - dev0->speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : + _dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH : (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL; // Acknowledge Port Reset Change if (port_status.change.reset) { - hub_port_clear_feature(dev0->hub_addr, dev0->hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset0_complete); + hub_port_clear_feature(_dev0.hub_addr, _dev0.hub_port, HUB_FEATURE_PORT_RESET_CHANGE, enum_hub_clear_reset0_complete); } return true; } - - -static bool enum_request_set_addr(void) -{ - // Set Address - uint8_t const new_addr = get_new_address(); - TU_ASSERT(new_addr <= CFG_TUSB_HOST_DEVICE_MAX); // TODO notify application we reach max devices - - TU_LOG2("Set Address = %d\r\n", new_addr); - - usbh_device_t* dev0 = &_usbh_devices[0]; - usbh_device_t* new_dev = &_usbh_devices[new_addr]; - - new_dev->rhport = dev0->rhport; - new_dev->hub_addr = dev0->hub_addr; - new_dev->hub_port = dev0->hub_port; - new_dev->speed = dev0->speed; - new_dev->connected = 1; - new_dev->ep0_packet_size = ((tusb_desc_device_t*) _usbh_ctrl_buf)->bMaxPacketSize0; - - tusb_control_request_t const new_request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_STANDARD, - .direction = TUSB_DIR_OUT - }, - .bRequest = TUSB_REQ_SET_ADDRESS, - .wValue = new_addr, - .wIndex = 0, - .wLength = 0 - }; - - TU_ASSERT( tuh_control_xfer(0, &new_request, NULL, enum_set_address_complete) ); - - return true; -} +#endif static bool enum_new_device(hcd_event_t* event) { - usbh_device_t* dev0 = &_usbh_devices[0]; - dev0->rhport = event->rhport; // TODO refractor integrate to device_pool - dev0->hub_addr = event->connection.hub_addr; - dev0->hub_port = event->connection.hub_port; - dev0->state = TUSB_DEVICE_STATE_UNPLUG; + _dev0.rhport = event->rhport; // TODO refractor integrate to device_pool + _dev0.hub_addr = event->connection.hub_addr; + _dev0.hub_port = event->connection.hub_port; //------------- connected/disconnected directly with roothub -------------// - if (dev0->hub_addr == 0) + if (_dev0.hub_addr == 0) { // wait until device is stable TODO non blocking osal_task_delay(RESET_DELAY); // device unplugged while delaying - if ( !hcd_port_connect_status(dev0->rhport) ) return true; + if ( !hcd_port_connect_status(_dev0.rhport) ) return true; - dev0->speed = hcd_port_speed_get( dev0->rhport ); + _dev0.speed = hcd_port_speed_get(_dev0.rhport ); enum_request_addr0_device_desc(); } @@ -745,7 +696,7 @@ static bool enum_new_device(hcd_event_t* event) { // wait until device is stable osal_task_delay(RESET_DELAY); - TU_ASSERT( hub_port_get_status(dev0->hub_addr, dev0->hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete) ); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete) ); } #endif // CFG_TUH_HUB @@ -755,7 +706,8 @@ static bool enum_new_device(hcd_event_t* event) static bool enum_request_addr0_device_desc(void) { // TODO probably doesn't need to open/close each enumeration - TU_ASSERT( usbh_edpt_control_open(0, 8) ); + uint8_t const addr0 = 0; + TU_ASSERT( usbh_edpt_control_open(addr0, 8) ); //------------- Get first 8 bytes of device descriptor to get Control Endpoint Size -------------// TU_LOG2("Get 8 byte of Device Descriptor\r\n"); @@ -772,7 +724,7 @@ static bool enum_request_addr0_device_desc(void) .wIndex = 0, .wLength = 8 }; - TU_ASSERT( tuh_control_xfer(0, &request, _usbh_ctrl_buf, enum_get_addr0_device_desc_complete) ); + TU_ASSERT( tuh_control_xfer(addr0, &request, _usbh_ctrl_buf, enum_get_addr0_device_desc_complete) ); return true; } @@ -783,27 +735,26 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r (void) request; TU_ASSERT(0 == dev_addr); - usbh_device_t* dev0 = &_usbh_devices[0]; - if (XFER_RESULT_SUCCESS != result) { #if CFG_TUH_HUB // TODO remove, waiting for next data on status pipe - if (dev0->hub_addr != 0) hub_status_pipe_queue(dev0->hub_addr); + if (_dev0.hub_addr != 0) hub_status_pipe_queue(_dev0.hub_addr); #endif return false; } - TU_ASSERT(tu_desc_type(_usbh_ctrl_buf) == TUSB_DESC_DEVICE); + tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; + TU_ASSERT( tu_desc_type(desc_device) == TUSB_DESC_DEVICE ); // Reset device again before Set Address TU_LOG2("Port reset \r\n"); - if (dev0->hub_addr == 0) + if (_dev0.hub_addr == 0) { // connected directly to roothub - hcd_port_reset( dev0->rhport ); // reset port after 8 byte descriptor + hcd_port_reset( _dev0.rhport ); // reset port after 8 byte descriptor osal_task_delay(RESET_DELAY); enum_request_set_addr(); @@ -812,18 +763,57 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r else { // after RESET_DELAY the hub_port_reset() already complete - TU_ASSERT( hub_port_reset(dev0->hub_addr, dev0->hub_port, NULL) ); + TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, NULL) ); osal_task_delay(RESET_DELAY); tuh_task(); // FIXME temporarily to clean up port_reset control transfer - TU_ASSERT( hub_port_get_status(dev0->hub_addr, dev0->hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) ); + TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status1_complete) ); } #endif return true; } +static bool enum_request_set_addr(void) +{ + uint8_t const addr0 = 0; + tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; + + // Get new address + uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB); + TU_ASSERT(new_addr != ADDR_INVALID); + + TU_LOG2("Set Address = %d\r\n", new_addr); + + usbh_device_t* new_dev = get_device(new_addr); + + new_dev->rhport = _dev0.rhport; + new_dev->hub_addr = _dev0.hub_addr; + new_dev->hub_port = _dev0.hub_port; + new_dev->speed = _dev0.speed; + new_dev->connected = 1; + new_dev->ep0_size = desc_device->bMaxPacketSize0; + + tusb_control_request_t const new_request = + { + .bmRequestType_bit = + { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_STANDARD, + .direction = TUSB_DIR_OUT + }, + .bRequest = TUSB_REQ_SET_ADDRESS, + .wValue = new_addr, + .wIndex = 0, + .wLength = 0 + }; + + TU_ASSERT( tuh_control_xfer(addr0, &new_request, NULL, enum_set_address_complete) ); + + return true; +} + // After SET_ADDRESS is complete static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result) { @@ -832,16 +822,14 @@ static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t c uint8_t const new_addr = (uint8_t const) request->wValue; - usbh_device_t* new_dev = &_usbh_devices[new_addr]; + usbh_device_t* new_dev = get_device(new_addr); new_dev->addressed = 1; // TODO close device 0, may not be needed - usbh_device_t* dev0 = &_usbh_devices[0]; - hcd_device_close(dev0->rhport, 0); - dev0->state = TUSB_DEVICE_STATE_UNPLUG; + hcd_device_close(_dev0.rhport, 0); // open control pipe for new address - TU_ASSERT ( usbh_edpt_control_open(new_addr, new_dev->ep0_packet_size) ); + TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size) ); // Get full device descriptor TU_LOG2("Get Device Descriptor\r\n"); @@ -870,10 +858,13 @@ static bool enum_get_device_desc_complete(uint8_t dev_addr, tusb_control_request TU_ASSERT(XFER_RESULT_SUCCESS == result); tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf; - usbh_device_t* dev = &_usbh_devices[dev_addr]; + usbh_device_t* dev = get_device(dev_addr); - dev->vendor_id = desc_device->idVendor; - dev->product_id = desc_device->idProduct; + dev->vid = desc_device->idVendor; + dev->pid = desc_device->idProduct; + dev->i_manufacturer = desc_device->iManufacturer; + dev->i_product = desc_device->iProduct; + dev->i_serial = desc_device->iSerialNumber; // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf); @@ -968,22 +959,22 @@ static bool enum_set_config_complete(uint8_t dev_addr, tusb_control_request_t co TU_ASSERT(XFER_RESULT_SUCCESS == result); TU_LOG2("Device configured\r\n"); - usbh_device_t* dev = &_usbh_devices[dev_addr]; + usbh_device_t* dev = get_device(dev_addr); dev->configured = 1; dev->state = TUSB_DEVICE_STATE_CONFIGURED; - // Start the Set Configuration process for interfaces (itf = 0xff) + // Start the Set Configuration process for interfaces (itf = DRVID_INVALID) // Since driver can perform control transfer within its set_config, this is done asynchronously. // The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete() - // TODO use separated API instead of usig 0xff - usbh_driver_set_config_complete(dev_addr, 0xff); + // TODO use separated API instead of using DRVID_INVALID + usbh_driver_set_config_complete(dev_addr, DRVID_INVALID); return true; } static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) { - usbh_device_t* dev = &_usbh_devices[dev_addr]; + usbh_device_t* dev = get_device(dev_addr); uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength); uint8_t const* p_desc = tu_desc_next(desc_cfg); @@ -992,58 +983,206 @@ static bool parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configura while( p_desc < desc_end ) { // TODO Do we need to use IAD - // tusb_desc_interface_assoc_t const * desc_itf_assoc = NULL; + tusb_desc_interface_assoc_t const * desc_iad = NULL; // Class will always starts with Interface Association (if any) and then Interface descriptor if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) { - // desc_itf_assoc = (tusb_desc_interface_assoc_t const *) p_desc; + desc_iad = (tusb_desc_interface_assoc_t const *) p_desc; p_desc = tu_desc_next(p_desc); } TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; - uint16_t const remaining_len = desc_end-p_desc; - // Check if class is supported TODO drop class_code - uint8_t drv_id; - for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) - { - if ( usbh_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass ) break; - } + // Interface number must not be used already + TU_ASSERT( dev->itf2drv[desc_itf->bInterfaceNumber] == DRVID_INVALID ); - if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + uint16_t const drv_len = tu_desc_get_interface_total_len(desc_itf, desc_iad ? desc_iad->bInterfaceCount : 1, desc_end-p_desc); + TU_ASSERT(drv_len); + + if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0) { - // skip unsupported class - p_desc = tu_desc_next(p_desc); + // TODO Attach hub to Hub is not currently supported + // skip this interface + TU_LOG(USBH_DBG_LVL, "Only 1 level of HUB is supported\r\n"); } else { - usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; - - // Interface number must not be used already TODO alternate interface - TU_ASSERT( dev->itf2drv[desc_itf->bInterfaceNumber] == 0xff ); - dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; - - if (desc_itf->bInterfaceClass == TUSB_CLASS_HUB && dev->hub_addr != 0) + // Find driver for this interface + uint8_t drv_id; + for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++) { - // TODO Attach hub to Hub is not currently supported - // skip this interface - p_desc = tu_desc_next(p_desc); + usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id]; + + if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) ) + { + // open successfully + TU_LOG2(" Opened successfully\r\n"); + + // bind interface to found driver + dev->itf2drv[desc_itf->bInterfaceNumber] = drv_id; + + // If using IAD, bind all interfaces to the same driver + if (desc_iad) + { + // IAD's first interface number and class should match with opened interface + TU_ASSERT(desc_iad->bFirstInterface == desc_itf->bInterfaceNumber && + desc_iad->bFunctionClass == desc_itf->bInterfaceClass); + + for(uint8_t i=1; ibInterfaceCount; i++) + { + dev->itf2drv[desc_itf->bInterfaceNumber+i] = drv_id; + } + } + + // bind all endpoints to found driver + tu_edpt_bind_driver(dev->ep2drv, desc_itf, drv_len, drv_id); + + break; // exit driver find loop + } } - else - { - TU_LOG2("%s open\r\n", driver->name); - uint16_t const itf_len = driver->open(dev->rhport, dev_addr, desc_itf, remaining_len); - TU_ASSERT( sizeof(tusb_desc_interface_t) <= itf_len && itf_len <= remaining_len); - p_desc += itf_len; + if( drv_id >= USBH_CLASS_DRIVER_COUNT ) + { + TU_LOG(USBH_DBG_LVL, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n", + desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol); } } + + // next Interface or IAD descriptor + p_desc += drv_len; } return true; } +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + +#if CFG_TUSB_OS != OPT_OS_NONE + // pre-check to help reducing mutex lock + TU_VERIFY((dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0)); + osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); +#endif + + // can only claim the endpoint if it is not busy and not claimed yet. + bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 0); + if (ret) + { + dev->ep_status[epnum][dir].claimed = 1; + } + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_unlock(dev->mutex); +#endif + + return ret; +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_lock(dev->mutex, OSAL_TIMEOUT_WAIT_FOREVER); +#endif + + // can only release the endpoint if it is claimed and not busy + bool const ret = (dev->ep_status[epnum][dir].busy == 0) && (dev->ep_status[epnum][dir].claimed == 1); + if (ret) + { + dev->ep_status[epnum][dir].claimed = 0; + } + +#if CFG_TUSB_OS != OPT_OS_NONE + osal_mutex_unlock(dev->mutex); +#endif + + return ret; +} + +// TODO has some duplication code with device, refactor later +bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + + TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes); + + // Attempt to transfer on a busy endpoint, sound like an race condition ! + TU_ASSERT(dev->ep_status[epnum][dir].busy == 0); + + // Set busy first since the actual transfer can be complete before hcd_edpt_xfer() + // could return and USBH task can preempt and clear the busy + dev->ep_status[epnum][dir].busy = true; + + if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) ) + { + TU_LOG2("OK\r\n"); + return true; + }else + { + // HCD error, mark endpoint as ready to allow next transfer + dev->ep_status[epnum][dir].busy = false; + dev->ep_status[epnum][dir].claimed = 0; + TU_LOG2("failed\r\n"); + TU_BREAKPOINT(); + return false; + } +} + +static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) +{ + TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr); + + tusb_desc_endpoint_t ep0_desc = + { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = { .xfer = TUSB_XFER_CONTROL }, + .wMaxPacketSize = { .size = max_packet_size }, + .bInterval = 0 + }; + + return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc); +} + +bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) +{ + usbh_device_t* dev = get_device(dev_addr); + TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) dev->speed)); + + return hcd_edpt_open(rhport, dev_addr, desc_ep); +} + +bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + usbh_device_t* dev = get_device(dev_addr); + + return dev->ep_status[epnum][dir].busy; +} + + + #endif diff --git a/src/host/usbh.h b/src/host/usbh.h index 4de6e7ba6..8411cad28 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -57,16 +57,25 @@ void tuh_task(void); extern void hcd_int_handler(uint8_t rhport); #define tuh_int_handler hcd_int_handler -tusb_speed_t tuh_device_get_speed (uint8_t dev_addr); +bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid); +tusb_speed_t tuh_speed_get(uint8_t dev_addr); -// Check if device is configured -bool tuh_device_configured(uint8_t dev_addr); +// Check if device is connected and configured +bool tuh_mounted(uint8_t dev_addr); + +// Check if device is suspended +static inline bool tuh_suspended(uint8_t dev_addr) +{ + // TODO implement suspend & resume on host + (void) dev_addr; + return false; +} // Check if device is ready to communicate with TU_ATTR_ALWAYS_INLINE -static inline bool tuh_device_ready(uint8_t dev_addr) +static inline bool tuh_ready(uint8_t dev_addr) { - return tuh_device_configured(dev_addr); + return tuh_mounted(dev_addr) && !tuh_suspended(dev_addr); } // Carry out control transfer diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_classdriver.h index 0736fefa1..8bc2622aa 100644 --- a/src/host/usbh_classdriver.h +++ b/src/host/usbh_classdriver.h @@ -43,13 +43,11 @@ typedef struct { char const* name; #endif - uint8_t class_code; - - void (* const init )(void); - uint16_t (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); - bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num); - bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); - void (* const close )(uint8_t dev_addr); + void (* const init )(void); + bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); + bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num); + bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + void (* const close )(uint8_t dev_addr); } usbh_class_driver_t; // Call by class driver to tell USBH that it has complete the enumeration @@ -73,6 +71,8 @@ bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_ // If caller does not make any transfer, it must release endpoint for others. bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr); +bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr); + // Check if endpoint transferring is complete bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr); diff --git a/src/host/usbh_control.c b/src/host/usbh_control.c index 0bdb66fb5..9204576ac 100644 --- a/src/host/usbh_control.c +++ b/src/host/usbh_control.c @@ -29,7 +29,7 @@ #if TUSB_OPT_HOST_ENABLED #include "tusb.h" -#include "usbh_hcd.h" +#include "usbh_classdriver.h" enum { @@ -59,9 +59,7 @@ static usbh_control_xfer_t _ctrl_xfer; bool tuh_control_xfer (uint8_t dev_addr, tusb_control_request_t const* request, void* buffer, tuh_control_complete_cb_t complete_cb) { // TODO need to claim the endpoint first - - usbh_device_t* dev = &_usbh_devices[dev_addr]; - const uint8_t rhport = dev->rhport; + const uint8_t rhport = usbh_get_rhport(dev_addr); _ctrl_xfer.request = (*request); _ctrl_xfer.buffer = buffer; @@ -89,8 +87,7 @@ bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t resu (void) ep_addr; (void) xferred_bytes; - usbh_device_t* dev = &_usbh_devices[dev_addr]; - const uint8_t rhport = dev->rhport; + const uint8_t rhport = usbh_get_rhport(dev_addr); tusb_control_request_t const * request = &_ctrl_xfer.request; diff --git a/src/host/usbh_hcd.h b/src/host/usbh_hcd.h deleted file mode 100644 index b3856d6b7..000000000 --- a/src/host/usbh_hcd.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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. - */ - -/** \ingroup Group_HCD - * @{ */ - -#ifndef _TUSB_USBH_HCD_H_ -#define _TUSB_USBH_HCD_H_ - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "common/tusb_common.h" -#include "osal/osal.h" - -#ifndef CFG_TUH_EP_MAX -#define CFG_TUH_EP_MAX 9 -#endif - -//--------------------------------------------------------------------+ -// USBH-HCD common data structure -//--------------------------------------------------------------------+ - -// TODO move to usbh.c -typedef struct { - //------------- port -------------// - uint8_t rhport; - uint8_t hub_addr; - uint8_t hub_port; - uint8_t speed; - - //------------- device descriptor -------------// - uint16_t vendor_id; - uint16_t product_id; - uint8_t ep0_packet_size; - - //------------- configuration descriptor -------------// - // uint8_t interface_count; // bNumInterfaces alias - - //------------- device -------------// - struct TU_ATTR_PACKED - { - uint8_t connected : 1; - uint8_t addressed : 1; - uint8_t configured : 1; - uint8_t suspended : 1; - }; - - volatile uint8_t state; // device state, value from enum tusbh_device_state_t - - uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid) - uint8_t ep2drv[CFG_TUH_EP_MAX][2]; // map endpoint to driver ( 0xff is invalid ) - - struct TU_ATTR_PACKED - { - volatile bool busy : 1; - volatile bool stalled : 1; - volatile bool claimed : 1; - - // TODO merge ep2drv here, 4-bit should be sufficient - }ep_status[CFG_TUH_EP_MAX][2]; - - // Mutex for claiming endpoint, only needed when using with preempted RTOS -#if CFG_TUSB_OS != OPT_OS_NONE - osal_mutex_def_t mutexdef; - osal_mutex_t mutex; -#endif - -} usbh_device_t; - -extern usbh_device_t _usbh_devices[CFG_TUSB_HOST_DEVICE_MAX+1]; // including zero-address - -#ifdef __cplusplus - } -#endif - -#endif /* _TUSB_USBH_HCD_H_ */ - -/** @} */ diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 48788ba06..e3b7499cc 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -24,11 +24,9 @@ * This file is part of the TinyUSB stack. */ -#include "common/tusb_common.h" +#include "host/hcd_attr.h" -#if TUSB_OPT_HOST_ENABLED && \ - (CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || \ - CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX ) +#if TUSB_OPT_HOST_ENABLED && defined(HCD_ATTR_EHCI_TRANSDIMENSION) //--------------------------------------------------------------------+ // INCLUDE @@ -36,27 +34,44 @@ #include "osal/osal.h" #include "host/hcd.h" -#include "host/usbh_hcd.h" -#include "hcd_ehci.h" +#include "ehci_api.h" #include "ehci.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ +// Debug level of EHCI +#define EHCI_DBG 2 + +// Framelist size as small as possible to save SRAM +#ifdef HCD_ATTR_EHCI_TRANSDIMENSION + // NXP Transdimension: 8 elements + #define FRAMELIST_SIZE_BIT_VALUE 7u + #define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE) | \ + ((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB)) +#else + // STD EHCI: 256 elements + #define FRAMELIST_SIZE_BIT_VALUE 2u + #define FRAMELIST_SIZE_USBCMD_VALUE ((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE) +#endif + +#define FRAMELIST_SIZE (1024 >> FRAMELIST_SIZE_BIT_VALUE) + typedef struct { - ehci_link_t period_framelist[EHCI_FRAMELIST_SIZE]; + ehci_link_t period_framelist[FRAMELIST_SIZE]; - // for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist) + // TODO only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist) // [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms + // TODO better implementation without dummy head to save SRAM ehci_qhd_t period_head_arr[4]; // Note control qhd of dev0 is used as head of async list struct { ehci_qhd_t qhd; ehci_qtd_t qtd; - }control[CFG_TUSB_HOST_DEVICE_MAX+1]; + }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1]; ehci_qhd_t qhd_pool[HCD_MAX_ENDPOINT]; ehci_qtd_t qtd_pool[HCD_MAX_XFER] TU_ATTR_ALIGNED(32); @@ -65,19 +80,17 @@ typedef struct volatile uint32_t uframe_number; }ehci_data_t; -//--------------------------------------------------------------------+ -// INTERNAL OBJECT & FUNCTION DECLARATION -//--------------------------------------------------------------------+ + // Periodic frame list must be 4K alignment CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data; //--------------------------------------------------------------------+ // PROTOTYPE //--------------------------------------------------------------------+ -static inline ehci_link_t* get_period_head(uint8_t rhport, uint8_t interval_ms) +static inline ehci_link_t* get_period_head(uint8_t rhport, uint32_t interval_ms) { (void) rhport; - return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min8(EHCI_FRAMELIST_SIZE, interval_ms) ) ]; + return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ]; } static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) @@ -109,7 +122,7 @@ static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd) //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error } -static void qhd_init (ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); +static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc); static inline ehci_qtd_t* qtd_find_free (void); static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd); @@ -218,12 +231,13 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) ehci_data.regs->command_bm.async_adv_doorbell = 1; } -// EHCI controller init -bool hcd_ehci_init(uint8_t rhport) +bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg) { + (void) capability_reg; // not used yet + tu_memclr(&ehci_data, sizeof(ehci_data_t)); - ehci_data.regs = (ehci_registers_t* ) hcd_ehci_register_addr(rhport); + ehci_data.regs = (ehci_registers_t* ) operatial_reg; ehci_registers_t* regs = ehci_data.regs; @@ -249,37 +263,38 @@ bool hcd_ehci_init(uint8_t rhport) //------------- Periodic List -------------// // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only - for(uint32_t i=0; i<4; i++) + for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) { - ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero + ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive } ehci_link_t * const framelist = ehci_data.period_framelist; - ehci_link_t * const period_1ms = get_period_head(rhport, 1); + ehci_link_t * const period_1ms = get_period_head(rhport, 1u); + // all links --> period_head_arr[0] (1ms) // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms) // 1, 5 --> period_head_arr[2] (4ms) // 3 --> period_head_arr[3] (8ms) // TODO EHCI_FRAMELIST_SIZE with other size than 8 - for(uint32_t i=0; iterminate = 1; @@ -289,10 +304,9 @@ bool hcd_ehci_init(uint8_t rhport) regs->nxp_tt_control = 0; //------------- USB CMD Register -------------// - regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) - | TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) // TODO enable period list only there is int/iso endpoint - | ((EHCI_CFG_FRAMELIST_SIZE_BITS & TU_BIN8(011)) << EHCI_USBCMD_POS_FRAMELIST_SZIE) - | ((EHCI_CFG_FRAMELIST_SIZE_BITS >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB); + regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) | + TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) | // TODO enable period list only there is int/iso endpoint + FRAMELIST_SIZE_USBCMD_VALUE; //------------- ConfigFlag Register (skip) -------------// regs->portsc_bm.port_power = 1; // enable port power @@ -470,16 +484,16 @@ static void async_advance_isr(uint8_t rhport) } } -static void port_connect_status_change_isr(uint8_t hostid) +static void port_connect_status_change_isr(uint8_t rhport) { // NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device if (ehci_data.regs->portsc_bm.current_connect_status) { - hcd_port_reset(hostid); - hcd_event_device_attach(hostid, true); + hcd_port_reset(rhport); + hcd_event_device_attach(rhport, true); }else // device unplugged { - hcd_event_device_remove(hostid, true); + hcd_event_device_remove(rhport, true); } } @@ -519,16 +533,16 @@ static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head) }while(p_qhd != async_head); // async list traversal, stop if loop around } -static void period_list_xfer_complete_isr(uint8_t hostid, uint8_t interval_ms) +static void period_list_xfer_complete_isr(uint8_t hostid, uint32_t interval_ms) { uint16_t max_loop = 0; - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); + uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u); ehci_link_t next_item = * get_period_head(hostid, interval_ms); // TODO abstract max loop guard for period while( !next_item.terminate && !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) && - max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUSB_HOST_DEVICE_MAX) + max_loop < (HCD_MAX_ENDPOINT + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUH_DEVICE_MAX) { switch ( next_item.type ) { @@ -605,8 +619,8 @@ static void xfer_error_isr(uint8_t hostid) }while(p_qhd != async_head); // async list traversal, stop if loop around //------------- TODO refractor period list -------------// - uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1); - for (uint8_t interval_ms=1; interval_ms <= EHCI_FRAMELIST_SIZE; interval_ms *= 2) + uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u); + for (uint32_t interval_ms=1; interval_ms <= FRAMELIST_SIZE; interval_ms *= 2) { ehci_link_t next_item = * get_period_head(hostid, interval_ms); @@ -649,13 +663,15 @@ void hcd_int_handler(uint8_t rhport) if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) { - ehci_data.uframe_number += (EHCI_FRAMELIST_SIZE << 3); + ehci_data.uframe_number += (FRAMELIST_SIZE << 3); } if (int_status & EHCI_INT_MASK_PORT_CHANGE) { uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL; + TU_LOG_HEX(EHCI_DBG, regs->portsc); + if (regs->portsc_bm.connect_status_change) { port_connect_status_change_isr(rhport); @@ -677,7 +693,7 @@ void hcd_int_handler(uint8_t rhport) if (int_status & EHCI_INT_MASK_NXP_PERIODIC) { - for (uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2) + for (uint32_t i=1; i <= FRAMELIST_SIZE; i *= 2) { period_list_xfer_complete_isr( rhport, i ); } @@ -774,13 +790,16 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c tu_memclr(p_qhd, sizeof(ehci_qhd_t)); } + hcd_devtree_info_t devtree_info; + hcd_devtree_get_info(dev_addr, &devtree_info); + uint8_t const xfer_type = ep_desc->bmAttributes.xfer; uint8_t const interval = ep_desc->bInterval; p_qhd->dev_addr = dev_addr; p_qhd->fl_inactive_next_xact = 0; p_qhd->ep_number = tu_edpt_number(ep_desc->bEndpointAddress); - p_qhd->ep_speed = _usbh_devices[dev_addr].speed; + p_qhd->ep_speed = devtree_info.speed; p_qhd->data_toggle_control= (xfer_type == TUSB_XFER_CONTROL) ? 1 : 0; p_qhd->head_list_flag = (dev_addr == 0) ? 1 : 0; // addr0's endpoint is the static asyn list head p_qhd->max_packet_size = ep_desc->wMaxPacketSize.size; @@ -817,8 +836,8 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c p_qhd->int_smask = p_qhd->fl_int_cmask = 0; } - p_qhd->fl_hub_addr = _usbh_devices[dev_addr].hub_addr; - p_qhd->fl_hub_port = _usbh_devices[dev_addr].hub_port; + p_qhd->fl_hub_addr = devtree_info.hub_addr; + p_qhd->fl_hub_port = devtree_info.hub_port; p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet //------------- HCD Management Data -------------// diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h index 874195a0c..c2bee67a5 100644 --- a/src/portable/ehci/ehci.h +++ b/src/portable/ehci/ehci.h @@ -46,8 +46,6 @@ //--------------------------------------------------------------------+ // EHCI CONFIGURATION & CONSTANTS //--------------------------------------------------------------------+ -#define EHCI_CFG_FRAMELIST_SIZE_BITS 7 /// Framelist Size (NXP specific) (0:1024) - (1:512) - (2:256) - (3:128) - (4:64) - (5:32) - (6:16) - (7:8) -#define EHCI_FRAMELIST_SIZE (1024 >> EHCI_CFG_FRAMELIST_SIZE_BITS) // TODO merge OHCI with EHCI enum { @@ -55,9 +53,6 @@ enum { EHCI_MAX_SITD = 16 }; -//------------- Validation -------------// -TU_VERIFY_STATIC(EHCI_CFG_FRAMELIST_SIZE_BITS <= 7, "incorrect value"); - //--------------------------------------------------------------------+ // EHCI Data Structure //--------------------------------------------------------------------+ @@ -294,7 +289,7 @@ enum ehci_interrupt_mask_{ enum ehci_usbcmd_pos_ { EHCI_USBCMD_POS_RUN_STOP = 0, - EHCI_USBCMD_POS_FRAMELIST_SZIE = 2, + EHCI_USBCMD_POS_FRAMELIST_SIZE = 2, EHCI_USBCMD_POS_PERIOD_ENABLE = 4, EHCI_USBCMD_POS_ASYNC_ENABLE = 5, EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15, @@ -411,7 +406,7 @@ typedef volatile struct uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend – Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device. - uint32_t : 1; + uint32_t TU_RESERVED : 1; uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed uint32_t TU_RESERVED : 4; }portsc_bm; diff --git a/src/portable/ehci/hcd_ehci.h b/src/portable/ehci/ehci_api.h similarity index 79% rename from src/portable/ehci/hcd_ehci.h rename to src/portable/ehci/ehci_api.h index 480d11eda..12e0a73d7 100644 --- a/src/portable/ehci/hcd_ehci.h +++ b/src/portable/ehci/ehci_api.h @@ -24,27 +24,19 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_HCD_EHCI_H_ -#define _TUSB_HCD_EHCI_H_ +#ifndef _TUSB_EHCI_API_H_ +#define _TUSB_EHCI_API_H_ #ifdef __cplusplus extern "C" { #endif - -//--------------------------------------------------------------------+ -// API Implemented by HCD -//--------------------------------------------------------------------+ - -// Get operational address i.e EHCI Command register -uint32_t hcd_ehci_register_addr(uint8_t rhport); - //--------------------------------------------------------------------+ // API Implemented by EHCI //--------------------------------------------------------------------+ // Initialize EHCI driver -extern bool hcd_ehci_init (uint8_t rhport); +bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg); #ifdef __cplusplus } diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 20519ec28..a776e46f5 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -40,6 +40,12 @@ #include "device/dcd.h" #include "NuMicro.h" +// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) +// We disable SOF for now until needed later on +#ifndef USE_SOF +# define USE_SOF 0 +#endif + /* allocation of USBD RAM for Setup, EP0_IN, and and EP_OUT */ #define PERIPH_SETUP_BUF_BASE 0 #define PERIPH_SETUP_BUF_LEN 8 @@ -65,9 +71,6 @@ enum ep_enum PERIPH_MAX_EP, }; -/* set by dcd_set_address() */ -static volatile uint8_t assigned_address; - /* reset by dcd_init(), this is used by dcd_edpt_open() to assign USBD peripheral buffer addresses */ static uint32_t bufseg_addr; @@ -197,7 +200,11 @@ static void bus_reset(void) } /* centralized location for USBD interrupt enable bit mask */ +#if USE_SOF static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk | USBD_INTSTS_SOFIF_Msk; +#else +static const uint32_t enabled_irqs = USBD_INTSTS_VBDETIF_Msk | USBD_INTSTS_BUSIF_Msk | USBD_INTSTS_SETUP_Msk | USBD_INTSTS_USBIF_Msk; +#endif /* NUC121/NUC125/NUC126 TinyUSB API driver implementation @@ -238,8 +245,11 @@ void dcd_int_disable(uint8_t rhport) void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void) rhport; + (void) dev_addr; usb_control_send_zlp(); /* SET_ADDRESS is the one exception where TinyUSB doesn't use dcd_edpt_xfer() to generate a ZLP */ - assigned_address = dev_addr; + + // DCD can only set address after status for this request is complete. + // do it at dcd_edpt0_status_complete() } void dcd_remote_wakeup(uint8_t rhport) @@ -420,9 +430,6 @@ void dcd_int_handler(uint8_t rhport) { if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ { - /* given ACK from host has happened, we can now set the address (if not already done) */ - if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; - uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); @@ -495,6 +502,23 @@ void dcd_int_handler(uint8_t rhport) USBD->INTSTS = status & enabled_irqs; } +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && + request->bRequest == TUSB_REQ_SET_ADDRESS ) + { + uint8_t const dev_addr = (uint8_t) request->wValue; + + // Setting new address after the whole request is complete + USBD->FADDR = dev_addr; + } +} + void dcd_disconnect(uint8_t rhport) { (void) rhport; diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c index 519b8fb7b..dce464fd2 100644 --- a/src/portable/nxp/khci/dcd_khci.c +++ b/src/portable/nxp/khci/dcd_khci.c @@ -26,7 +26,9 @@ #include "tusb_option.h" -#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) +#if TUSB_OPT_DEVICE_ENABLED && ( \ + ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \ + ) #include "fsl_device_registers.h" #define KHCI USB0 diff --git a/src/portable/nxp/transdimension/common_transdimension.h b/src/portable/nxp/transdimension/common_transdimension.h index 7b94dac34..69074de41 100644 --- a/src/portable/nxp/transdimension/common_transdimension.h +++ b/src/portable/nxp/transdimension/common_transdimension.h @@ -127,7 +127,7 @@ typedef struct __I uint32_t ENDPTSTAT; ///< Endpoint Status __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7 -} dcd_registers_t; +} dcd_registers_t, hcd_registers_t; #ifdef __cplusplus } diff --git a/src/portable/nxp/transdimension/hcd_transdimension.c b/src/portable/nxp/transdimension/hcd_transdimension.c index db447ef23..d216f0728 100644 --- a/src/portable/nxp/transdimension/hcd_transdimension.c +++ b/src/portable/nxp/transdimension/hcd_transdimension.c @@ -43,7 +43,7 @@ #include "common/tusb_common.h" #include "common_transdimension.h" -#include "portable/ehci/hcd_ehci.h" +#include "portable/ehci/ehci_api.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -82,26 +82,26 @@ typedef struct bool hcd_init(uint8_t rhport) { - dcd_registers_t* dcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base; + hcd_registers_t* hcd_reg = (hcd_registers_t*) _hcd_controller[rhport].regs_base; // Reset controller - dcd_reg->USBCMD |= USBCMD_RESET; - while( dcd_reg->USBCMD & USBCMD_RESET ) {} + hcd_reg->USBCMD |= USBCMD_RESET; + while( hcd_reg->USBCMD & USBCMD_RESET ) {} // Set mode to device, must be set immediately after reset #if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX // LPC18XX/43XX need to set VBUS Power Select to HIGH // RHPORT1 is fullspeed only (need external PHY for Highspeed) - dcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT; - if (rhport == 1) dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED; + hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT; + if (rhport == 1) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED; #else - dcd_reg->USBMODE = USBMODE_CM_HOST; + hcd_reg->USBMODE = USBMODE_CM_HOST; #endif // FIXME force full speed, still have issue with Highspeed enumeration - dcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED; + hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED; - return hcd_ehci_init(rhport); + return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD); } void hcd_int_enable(uint8_t rhport) @@ -114,12 +114,4 @@ void hcd_int_disable(uint8_t rhport) NVIC_DisableIRQ(_hcd_controller[rhport].irqnum); } -uint32_t hcd_ehci_register_addr(uint8_t rhport) -{ - dcd_registers_t* hcd_reg = (dcd_registers_t*) _hcd_controller[rhport].regs_base; - - // EHCI USBCMD has same address within dcd_register_t - return (uint32_t) &hcd_reg->USBCMD; -} - #endif diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c index 73489c764..bcee34938 100644 --- a/src/portable/ohci/ohci.c +++ b/src/portable/ohci/ohci.c @@ -24,10 +24,9 @@ * This file is part of the TinyUSB stack. */ -#include +#include "host/hcd_attr.h" -#if TUSB_OPT_HOST_ENABLED && \ - (CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX) +#if TUSB_OPT_HOST_ENABLED && defined(HCD_ATTR_OHCI) //--------------------------------------------------------------------+ // INCLUDE @@ -35,7 +34,6 @@ #include "osal/osal.h" #include "host/hcd.h" -#include "host/usbh_hcd.h" #include "ohci.h" // TODO remove @@ -280,10 +278,13 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t tu_memclr(p_ed, sizeof(ohci_ed_t)); } + hcd_devtree_info_t devtree_info; + hcd_devtree_get_info(dev_addr, &devtree_info); + p_ed->dev_addr = dev_addr; p_ed->ep_number = ep_addr & 0x0F; p_ed->pid = (xfer_type == TUSB_XFER_CONTROL) ? PID_FROM_TD : (tu_edpt_dir(ep_addr) ? PID_IN : PID_OUT); - p_ed->speed = _usbh_devices[dev_addr].speed; + p_ed->speed = devtree_info.speed; p_ed->is_iso = (xfer_type == TUSB_XFER_ISOCHRONOUS) ? 1 : 0; p_ed->max_packet_size = ep_size; diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h index 6a634592a..cd90aa45a 100644 --- a/src/portable/ohci/ohci.h +++ b/src/portable/ohci/ohci.h @@ -159,7 +159,7 @@ typedef struct TU_ATTR_ALIGNED(256) struct { ohci_ed_t ed; ohci_gtd_t gtd; - }control[CFG_TUSB_HOST_DEVICE_MAX+1]; + }control[CFG_TUH_DEVICE_MAX+1]; // ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32 ohci_ed_t ed_pool[HCD_MAX_ENDPOINT]; diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c index 2c007ddd3..e51dfac2b 100644 --- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c @@ -39,7 +39,6 @@ #include "host/hcd.h" #include "host/usbh.h" -#include "host/usbh_hcd.h" #define ROOT_PORT 0 @@ -88,7 +87,7 @@ static bool need_pre(uint8_t dev_addr) { // If this device is different to the speed of the root device // (i.e. is a low speed device on a full speed hub) then need pre - return hcd_port_speed_get(0) != tuh_device_get_speed(dev_addr); + return hcd_port_speed_get(0) != tuh_speed_get(dev_addr); } static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result) diff --git a/src/portable/renesas/usba/dcd_usba.c b/src/portable/renesas/usba/dcd_usba.c index 1db1fdb32..095dcc136 100644 --- a/src/portable/renesas/usba/dcd_usba.c +++ b/src/portable/renesas/usba/dcd_usba.c @@ -27,6 +27,10 @@ #include "tusb_option.h" +// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval) +// We disable SOF for now until needed later on +#define USE_SOF 0 + #if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X || \ CFG_TUSB_MCU == OPT_MCU_RX65X || \ CFG_TUSB_MCU == OPT_MCU_RX72N ) @@ -139,7 +143,6 @@ typedef struct { pipe_state_t pipe[10]; uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */ - uint8_t suspended; } dcd_data_t; //--------------------------------------------------------------------+ @@ -627,7 +630,7 @@ void dcd_init(uint8_t rhport) /* Setup default control pipe */ USB0.DCPMAXP.BIT.MXPS = 64; USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP | - USB_IS0_DVST | USB_IS0_CTRT | USB_IS0_SOFR | USB_IS0_RESM; + USB_IS0_DVST | USB_IS0_CTRT | (USE_SOF ? USB_IS0_SOFR: 0) | USB_IS0_RESM; USB0.BEMPENB.WORD = 1; USB0.BRDYENB.WORD = 1; @@ -815,21 +818,16 @@ void dcd_int_handler(uint8_t rhport) } if (is0 & USB_IS0_RESM) { dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); - _dcd.suspended = 0; +#if (0==USE_SOF) + USB0.INTENB0.BIT.SOFE = 0; +#endif } - if (is0 & USB_IS0_SOFR) { - if (_dcd.suspended) { - /* When USB host resumes caused by `dcd_remote_wakeup()`, - * RESM interrupt does not rise. - * Therefore we need to manually send resume event. - * Of course, when USB host resumes on its own, - * RESM interrupt rise properly, then this statements are ignored. */ - // TODO can be dropped since this logic is implemented by usbd - // USBD will exit suspended mode when SOF event is received - dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); - _dcd.suspended = 0; - } + if ((is0 & USB_IS0_SOFR) && USB0.INTENB0.BIT.SOFE) { + // USBD will exit suspended mode when SOF event is received dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); +#if (0==USE_SOF) + USB0.INTENB0.BIT.SOFE = 0; +#endif } if (is0 & USB_IS0_DVST) { switch (is0 & USB_IS0_DVSQ) { @@ -844,7 +842,9 @@ void dcd_int_handler(uint8_t rhport) case USB_IS0_DVSQ_SUSP2: case USB_IS0_DVSQ_SUSP3: dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); - _dcd.suspended = 1; +#if (0==USE_SOF) + USB0.INTENB0.BIT.SOFE = 1; +#endif default: break; } diff --git a/src/tusb.c b/src/tusb.c index 0350fa1de..6494287d2 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -63,6 +63,99 @@ bool tusb_inited(void) return ret; } +//--------------------------------------------------------------------+ +// Internal Helper for both Host and Device stack +//--------------------------------------------------------------------+ + +bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed) +{ + uint16_t const max_packet_size = tu_le16toh(desc_ep->wMaxPacketSize.size); + TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size); + + switch (desc_ep->bmAttributes.xfer) + { + case TUSB_XFER_ISOCHRONOUS: + { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023); + TU_ASSERT(max_packet_size <= spec_size); + } + break; + + case TUSB_XFER_BULK: + if (speed == TUSB_SPEED_HIGH) + { + // Bulk highspeed must be EXACTLY 512 + TU_ASSERT(max_packet_size == 512); + }else + { + // TODO Bulk fullspeed can only be 8, 16, 32, 64 + TU_ASSERT(max_packet_size <= 64); + } + break; + + case TUSB_XFER_INTERRUPT: + { + uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64); + TU_ASSERT(max_packet_size <= spec_size); + } + break; + + default: return false; + } + + return true; +} + +void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, uint8_t driver_id) +{ + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while( len < desc_len ) + { + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id); + ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id; + } + + len = (uint16_t)(len + tu_desc_len(p_desc)); + p_desc = tu_desc_next(p_desc); + } +} + +uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) +{ + uint8_t const* p_desc = (uint8_t const*) desc_itf; + uint16_t len = 0; + + while (itf_count--) + { + // Next on interface desc + len += tu_desc_len(desc_itf); + p_desc = tu_desc_next(p_desc); + + while (len < max_len) + { + // return on IAD regardless of itf count + if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len; + + if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) && + ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 ) + { + break; + } + + len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + + return len; +} + /*------------------------------------------------------------------*/ /* Debug *------------------------------------------------------------------*/ diff --git a/src/tusb_option.h b/src/tusb_option.h index e189c65bb..da1a39f1e 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -108,6 +108,7 @@ // NXP Kinetis #define OPT_MCU_MKL25ZXX 1200 ///< NXP MKL25Zxx +#define OPT_MCU_K32L2BXX 1201 ///< NXP K32L2Bxx // Silabs #define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG @@ -266,14 +267,8 @@ // HOST OPTIONS //-------------------------------------------------------------------- #if TUSB_OPT_HOST_ENABLED - #ifndef CFG_TUSB_HOST_DEVICE_MAX - #define CFG_TUSB_HOST_DEVICE_MAX 1 - #warning CFG_TUSB_HOST_DEVICE_MAX is not defined, default value is 1 - #endif - - //------------- HUB CLASS -------------// - #if CFG_TUH_HUB && (CFG_TUSB_HOST_DEVICE_MAX == 1) - #error There is no benefit enable hub with max device is 1. Please disable hub or increase CFG_TUSB_HOST_DEVICE_MAX + #ifndef CFG_TUH_DEVICE_MAX + #define CFG_TUH_DEVICE_MAX 1 #endif #ifndef CFG_TUH_ENUMERATION_BUFSIZE