From 4f4ba3b16d9bcb98a1cb51cb6030f0be4dfd7513 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Sat, 17 May 2025 17:35:45 +0200 Subject: [stm32] Wait until the PHYC PLL is stable --- src/portable/synopsys/dwc2/dwc2_stm32.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index c11c1eb05..0bbad55f5 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -222,6 +222,10 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { // Enable PLL internal PHY USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; + + // Wait ~2ms until the PLL is ready (there's no RDY bit to query) + uint32_t count = (SystemCoreClock / 1000) * 2; + while (count--) __NOP(); #else #endif -- cgit v1.3.1 From 4ae433fa6ef451cccb2b09aa3748978b46aade5f Mon Sep 17 00:00:00 2001 From: milek7 Date: Wed, 25 Jun 2025 03:35:40 +0200 Subject: stm32_fsdev: Allow configuring single-buffered isochronous endpoints. Controlled by the embedder defining CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP in tusb_config.h. If hardware does have SBUF_ISO bit it will use only one half of endpoint pair register. --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 47 ++++++++++++++++++++++----- src/portable/st/stm32_fsdev/fsdev_ch32.h | 5 +++ src/portable/st/stm32_fsdev/fsdev_stm32.h | 38 ++++++++++++++++++++++ 3 files changed, 82 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index ef58957bb..ad733b16a 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -331,8 +331,13 @@ static void handle_ctr_rx(uint32_t ep_id) { xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT); uint8_t buf_id; - if (is_iso) { - buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered +#if FSDEV_USE_SBUF_ISO == 0 + bool const dbl_buf = is_iso; +#else + bool const dbl_buf = false; +#endif + if (dbl_buf) { + buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; } else { buf_id = BTABLE_BUF_RX; } @@ -527,10 +532,16 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) return i; } +#if FSDEV_USE_SBUF_ISO == 0 + bool const dbl_buf = ep_type == TUSB_XFER_ISOCHRONOUS; +#else + bool const dbl_buf = false; +#endif + // If EP of current direction is not allocated - // Except for ISO endpoint, both direction should be free + // For double-buffered mode both directions needs to be free if (!ep_alloc_status[i].allocated[dir] && - (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) { + (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) { // Check if EP number is the same if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { // One EP pair has to be the same type @@ -652,9 +663,7 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet uint8_t const dir = tu_edpt_dir(ep_addr); uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); - /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, - for smaller devices double buffering occupy too much space. */ -#if FSDEV_PMA_SIZE > 1024u +#if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0 uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true); uint16_t pma_addr2 = pma_addr >> 16; #else @@ -662,8 +671,12 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet uint16_t pma_addr2 = pma_addr; #endif +#if FSDEV_USE_SBUF_ISO == 0 btable_set_addr(ep_idx, 0, pma_addr); btable_set_addr(ep_idx, 1, pma_addr2); +#else + btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); +#endif xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); xfer->ep_idx = ep_idx; @@ -684,10 +697,23 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK; ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_TX | USB_EP_CTR_RX; +#if FSDEV_USE_SBUF_ISO != 0 + ep_reg |= USB_EP_KIND; + + ep_change_status(&ep_reg, dir, EP_STAT_DISABLED); + ep_change_dtog(&ep_reg, dir, 0); + + if (dir == TUSB_DIR_IN) { + ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX); + } else { + ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX); + } +#else ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED); ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED); ep_change_dtog(&ep_reg, dir, 0); ep_change_dtog(&ep_reg, (tusb_dir_t)(1 - dir), 1); +#endif ep_write(ep_idx, ep_reg, true); @@ -702,7 +728,12 @@ static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) { bool const is_iso = ep_is_iso(ep_reg); uint8_t buf_id; - if (is_iso) { +#if FSDEV_USE_SBUF_ISO == 0 + bool const dbl_buf = is_iso; +#else + bool const dbl_buf = false; +#endif + if (dbl_buf) { buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0; } else { buf_id = BTABLE_BUF_TX; diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h index 518197c47..ceebb6dab 100644 --- a/src/portable/st/stm32_fsdev/fsdev_ch32.h +++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h @@ -54,9 +54,14 @@ #endif #define FSDEV_PMA_SIZE (512u) +#define FSDEV_USE_SBUF_ISO 0 #define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL) #define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL) +#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP + #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 +#endif + /**************************** ISTR interrupt events *************************/ #define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ #define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index ccf31e035..770baa05a 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -36,6 +36,7 @@ #include "stm32f0xx.h" #define FSDEV_PMA_SIZE (1024u) #define FSDEV_REG_BASE USB_BASE + #define FSDEV_HAS_SBUF_ISO 0 // F0x2 models are crystal-less // All have internal D+ pull-up // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support @@ -44,6 +45,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 #include "stm32f1xx.h" #define FSDEV_PMA_SIZE (512u) + #define FSDEV_HAS_SBUF_ISO 0 // NO internal Pull-ups // *B, and *C: 2 x 16 bits/word @@ -55,6 +57,7 @@ defined(STM32F373xC) #include "stm32f3xx.h" #define FSDEV_PMA_SIZE (512u) + #define FSDEV_HAS_SBUF_ISO 0 // NO internal Pull-ups // *B, and *C: 1 x 16 bits/word // PMA dedicated to USB (no sharing with CAN) @@ -64,6 +67,7 @@ defined(STM32F303xD) || defined(STM32F303xE) #include "stm32f3xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 // NO internal Pull-ups // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support // When CAN clock is enabled, USB can use first 768 bytes ONLY. @@ -71,18 +75,22 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32L0 #include "stm32l0xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32L1 #include "stm32l1xx.h" #define FSDEV_PMA_SIZE (512u) + #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32G4 #include "stm32g4xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #include "stm32g0xx.h" #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_EP_VTRX @@ -107,6 +115,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 #include "stm32c0xx.h" #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_CHEP_VTRX #define USB_EP_CTR_TX USB_CHEP_VTTX @@ -121,6 +130,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 #include "stm32h5xx.h" #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_EP_VTRX @@ -145,16 +155,19 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32WB #include "stm32wbxx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 /* ST provided header has incorrect value of USB_PMAADDR */ #define FSDEV_PMA_BASE USB1_PMAADDR #elif CFG_TUSB_MCU == OPT_MCU_STM32L4 #include "stm32l4xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32L5 #include "stm32l5xx.h" #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_HAS_SBUF_ISO 0 #ifndef USB_PMAADDR #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) @@ -163,6 +176,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 #include "stm32u5xx.h" #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_EP_VTRX @@ -187,6 +201,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 #include "stm32u0xx.h" #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_EP_VTRX @@ -248,6 +263,29 @@ #define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) +#ifndef FSDEV_HAS_SBUF_ISO + #error "FSDEV_HAS_SBUF_ISO not defined" +#endif + +#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP + // Defaults to double-buffered isochronous endpoints on devices with >1KB PMA + #if FSDEV_PMA_SIZE > 1024u + #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1 + #else + #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 + #endif +#endif + +#if FSDEV_HAS_SBUF_ISO != 0 && CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP == 0 + // If hardware has SBUF_ISO bit single-buffered endpoints consume only + // one half of endpoint pair register. + #define FSDEV_USE_SBUF_ISO 1 +#else + // Otherwise it consumes entire endpoint pair but we can at least save + // memory by configuring the same buffer twice. + #define FSDEV_USE_SBUF_ISO 0 +#endif + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -- cgit v1.3.1 From 283b06bb543f513d964dc3a6953115df897a93a1 Mon Sep 17 00:00:00 2001 From: Maurizio Pesce Date: Wed, 12 Mar 2025 09:50:04 +0100 Subject: Add MTP class device --- examples/device/mtp/CMakeLists.txt | 39 + examples/device/mtp/CMakePresets.json | 6 + examples/device/mtp/Makefile | 11 + examples/device/mtp/prj.conf | 6 + examples/device/mtp/skip.txt | 0 examples/device/mtp/src/main.c | 113 +++ examples/device/mtp/src/mtp_fs_example.c | 572 ++++++++++++ examples/device/mtp/src/tusb_config.h | 112 +++ examples/device/mtp/src/usb_descriptors.c | 193 ++++ .../components/tinyusb_src/CMakeLists.txt | 1 + hw/bsp/rp2040/family.cmake | 1 + lib/rt-thread/SConscript | 2 + src/CMakeLists.txt | 1 + src/class/mtp/mtp.h | 438 +++++++++ src/class/mtp/mtp_device.c | 997 +++++++++++++++++++++ src/class/mtp/mtp_device.h | 75 ++ src/class/mtp/mtp_device_storage.h | 149 +++ src/device/usbd.c | 13 + src/device/usbd.h | 19 + src/tinyusb.mk | 1 + src/tusb.h | 4 + src/tusb_option.h | 4 + test/fuzz/rules.mk | 1 + tools/iar_template.ipcf | 6 + 24 files changed, 2764 insertions(+) create mode 100644 examples/device/mtp/CMakeLists.txt create mode 100644 examples/device/mtp/CMakePresets.json create mode 100644 examples/device/mtp/Makefile create mode 100644 examples/device/mtp/prj.conf create mode 100644 examples/device/mtp/skip.txt create mode 100644 examples/device/mtp/src/main.c create mode 100644 examples/device/mtp/src/mtp_fs_example.c create mode 100644 examples/device/mtp/src/tusb_config.h create mode 100644 examples/device/mtp/src/usb_descriptors.c create mode 100644 src/class/mtp/mtp.h create mode 100644 src/class/mtp/mtp_device.c create mode 100644 src/class/mtp/mtp_device.h create mode 100644 src/class/mtp/mtp_device_storage.h (limited to 'src') diff --git a/examples/device/mtp/CMakeLists.txt b/examples/device/mtp/CMakeLists.txt new file mode 100644 index 000000000..e91eb8fd9 --- /dev/null +++ b/examples/device/mtp/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.20) + +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} C CXX ASM) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +# Espressif has its own cmake build system +if(FAMILY STREQUAL "espressif") + return() +endif() + +if (RTOS STREQUAL zephyr) + set(EXE_NAME app) +else() + set(EXE_NAME ${PROJECT}) + add_executable(${EXE_NAME}) +endif() + +# Example source +target_sources(${EXE_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/mtp_fs_example.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${EXE_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example without RTOS. +# See the corresponding function in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${EXE_NAME} ${RTOS}) diff --git a/examples/device/mtp/CMakePresets.json b/examples/device/mtp/CMakePresets.json new file mode 100644 index 000000000..5cd8971e9 --- /dev/null +++ b/examples/device/mtp/CMakePresets.json @@ -0,0 +1,6 @@ +{ + "version": 6, + "include": [ + "../../../hw/bsp/BoardPresets.json" + ] +} diff --git a/examples/device/mtp/Makefile b/examples/device/mtp/Makefile new file mode 100644 index 000000000..7fa475da5 --- /dev/null +++ b/examples/device/mtp/Makefile @@ -0,0 +1,11 @@ +include ../../build_system/make/make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../build_system/make/rules.mk diff --git a/examples/device/mtp/prj.conf b/examples/device/mtp/prj.conf new file mode 100644 index 000000000..2f5139d9d --- /dev/null +++ b/examples/device/mtp/prj.conf @@ -0,0 +1,6 @@ +CONFIG_GPIO=y +CONFIG_FPU=y +CONFIG_NO_OPTIMIZATIONS=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_NRFX_POWER=y +CONFIG_NRFX_UARTE0=y diff --git a/examples/device/mtp/skip.txt b/examples/device/mtp/skip.txt new file mode 100644 index 000000000..e69de29bb diff --git a/examples/device/mtp/src/main.c b/examples/device/mtp/src/main.c new file mode 100644 index 000000000..709aeb16f --- /dev/null +++ b/examples/device/mtp/src/main.c @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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_api.h" +#include "tusb.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); + +/*------------- MAIN -------------*/ +int main(void) { + board_init(); + + // init device stack on configured roothub port + tusb_rhport_init_t dev_init = { + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO + }; + tusb_init(BOARD_TUD_RHPORT, &dev_init); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } + + while (1) { + tud_task(); // tinyusb device task + led_blinking_task(); + } +} + +//--------------------------------------------------------------------+ +// 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 = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; +} + +//--------------------------------------------------------------------+ +// BLINKING TASK +//--------------------------------------------------------------------+ +void led_blinking_task(void) { + static uint32_t start_ms = 0; + static bool led_state = false; + + // 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/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c new file mode 100644 index 000000000..41673ea53 --- /dev/null +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -0,0 +1,572 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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 "class/mtp/mtp_device_storage.h" +#include "tusb.h" + +#define MTPD_STORAGE_DESCRIPTION "storage" +#define MTPD_VOLUME_IDENTIFIER "volume" + +//--------------------------------------------------------------------+ +// RAM FILESYSTEM +//--------------------------------------------------------------------+ +#define FS_MAX_NODES 5UL +#define FS_MAX_NODE_BYTES 128UL +#define FS_MAX_NODE_NAME_LEN 64UL +#define FS_ISODATETIME_LEN 26UL + +typedef struct +{ + uint32_t handle; + uint32_t parent; + uint32_t size; + bool allocated; + bool association; + char name[FS_MAX_NODE_NAME_LEN]; + char created[FS_ISODATETIME_LEN]; + char modified[FS_ISODATETIME_LEN]; + uint8_t data[FS_MAX_NODE_BYTES]; +} fs_object_info_t; + +// Sample object file +static fs_object_info_t _fs_objects[FS_MAX_NODES] = { + { + .handle = 1, + .parent = 0, + .allocated = true, + .association = false, + .name = "readme.txt", + .created = "20240104T111134.0", + .modified = "20241214T121110.0", + .data = "USB MTP on RAM Filesystem example\n", + .size = 34 + } +}; + +//--------------------------------------------------------------------+ +// OPERATING STATUS +//--------------------------------------------------------------------+ +typedef struct +{ + // Session + uint32_t session_id; + // Association traversal + uint32_t traversal_parent; + uint32_t traversal_index; + // Object open for reading + uint32_t read_handle; + uint32_t read_pos; + // Object open for writing + uint32_t write_handle; + uint32_t write_pos; + // Unique identifier + uint32_t last_handle; +} fs_operation_t; + +static fs_operation_t _fs_operation = { + .last_handle = 1 +}; + +//--------------------------------------------------------------------+ +// INTERNAL FUNCTIONS +//--------------------------------------------------------------------+ + +// Get pointer to object info from handle +fs_object_info_t *fs_object_get_from_handle(uint32_t handle); + +// Get the number of allocated nodes in filesystem +unsigned int fs_get_object_count(void); + +fs_object_info_t *fs_object_get_from_handle(uint32_t handle) +{ + fs_object_info_t *obj; + for (unsigned int i=0; iallocated && obj->handle == handle) + return obj; + } + return NULL; +} + +unsigned int fs_get_object_count(void) +{ + unsigned int s = 0; + for (unsigned int i = 0; istorage_type = MTP_STORAGE_TYPE_FIXED_RAM; + info->filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL; + info->access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE; + info->max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES; + info->free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); + info->free_space_in_bytes = info->free_space_in_objects * FS_MAX_NODE_BYTES; + mtpd_gct_append_wstring(MTPD_STORAGE_DESCRIPTION); + mtpd_gct_append_wstring(MTPD_VOLUME_IDENTIFIER); + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_format(uint32_t storage_id) +{ + if (_fs_operation.session_id == 0) + { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESC_SESSION_NOT_OPEN; + } + if (storage_id != STORAGE_ID(0x0001, 0x0001)) + { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESC_INVALID_STORAGE_ID; + } + + // Simply deallocate all entries + for (unsigned int i=0; iallocated && obj->parent == parent_object_handle) + { + _fs_operation.traversal_index = i+1; + *next_child_handle = obj->handle; + TU_LOG1("Association %ld -> child %ld\r\n", parent_object_handle, obj->handle); + return MTP_RESC_OK; + } + } + TU_LOG1("Association traversal completed\r\n"); + _fs_operation.traversal_index = 0; + *next_child_handle = 0; + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_t *info) +{ + fs_object_info_t *obj = NULL; + + if (_fs_operation.session_id == 0) + { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESC_SESSION_NOT_OPEN; + } + // Accept command on default storage + if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) + { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESC_INVALID_STORAGE_ID; + } + + if (info->object_compressed_size > FS_MAX_NODE_BYTES) + { + TU_LOG1("Object size %ld is more than maximum %ld\r\n", info->object_compressed_size, FS_MAX_NODE_BYTES); + return MTP_RESC_STORE_FULL; + } + + // Request for objects with no parent (0xFFFFFFFF) are considered root objects + if (parent_object == 0xFFFFFFFF) + parent_object = 0; + + // Ensure we are not creating an orphaned object outside root + if (parent_object != 0) + { + obj = fs_object_get_from_handle(parent_object); + if (obj == NULL) + { + TU_LOG1("Parent %ld does not exist\r\n", parent_object); + return MTP_RESC_INVALID_PARENT_OBJECT; + } + if (!obj->association) + { + TU_LOG1("Parent %ld is not an association\r\n", parent_object); + return MTP_RESC_INVALID_PARENT_OBJECT; + } + } + + // Search for first free object + for (unsigned int i=0; iallocated = true; + obj->handle = ++_fs_operation.last_handle; + obj->parent = parent_object; + obj->size = info->object_compressed_size; + obj->association = info->object_format == MTP_OBJF_ASSOCIATION; + + // Extract variable data + uint16_t offset_data = sizeof(mtp_object_info_t); + mtpd_gct_get_string(&offset_data, obj->name, FS_MAX_NODE_NAME_LEN); + mtpd_gct_get_string(&offset_data, obj->created, FS_ISODATETIME_LEN); + mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); + + TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", + obj->association ? "association" : "object", + obj->name, obj->handle, obj->parent, obj->size); + *new_object_handle = obj->handle; + // Initialize operation + _fs_operation.write_handle = obj->handle; + _fs_operation.write_pos = 0; + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t *info) +{ + const fs_object_info_t *obj; + + if (_fs_operation.session_id == 0) + { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESC_SESSION_NOT_OPEN; + } + + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + + memset(info, 0, sizeof(mtp_object_info_t)); + info->storage_id = STORAGE_ID(0x0001, 0x0001); + if (obj->association) + { + info->object_format = MTP_OBJF_ASSOCIATION; + info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; + info->object_compressed_size = 0; + info->association_type = MTP_ASSOCIATION_UNDEFINED; + } + else + { + info->object_format = MTP_OBJF_UNDEFINED; + info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; + info->object_compressed_size = obj->size; + info->association_type = MTP_ASSOCIATION_UNDEFINED; + } + info->thumb_format = MTP_OBJF_UNDEFINED; + info->parent_object = obj->parent; + + mtpd_gct_append_wstring(obj->name); + mtpd_gct_append_wstring(obj->created); // date_created + mtpd_gct_append_wstring(obj->modified); // date_modified + mtpd_gct_append_wstring(""); // keywords, not used + + TU_LOG1("Retrieve object %s with handle %ld\r\n", obj->name, obj->handle); + + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t size) +{ + fs_object_info_t *obj; + + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + // It's a requirement that this command is preceded by a write info + if (object_handle != _fs_operation.write_handle) + { + TU_LOG1("ERR: Object %ld not open for write\r\n", object_handle); + return MTP_RESC_NO_VALID_OBJECTINFO; + } + + TU_LOG1("Write object %ld: data chunk at %ld/%ld bytes at offset %ld\r\n", object_handle, _fs_operation.write_pos, obj->size, size); + TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESC_INCOMPLETE_TRANSFER); + if (_fs_operation.write_pos + size < FS_MAX_NODE_BYTES) + memcpy(&obj->data[_fs_operation.write_pos], buffer, size); + _fs_operation.write_pos += size; + // Write operation completed + if (_fs_operation.write_pos == obj->size) + { + _fs_operation.write_handle = 0; + _fs_operation.write_pos = 0; + } + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *size) +{ + const fs_object_info_t *obj; + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + *size = obj->size; + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, uint32_t buffer_size, uint32_t *read_count) +{ + const fs_object_info_t *obj; + + obj = fs_object_get_from_handle(object_handle); + + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + // It's not a requirement that this command is preceded by a read info + if (object_handle != _fs_operation.read_handle) + { + TU_LOG1("ERR: Object %ld not open for read\r\n", object_handle); + _fs_operation.read_handle = object_handle; + _fs_operation.read_pos = 0; + } + + if (obj->size - _fs_operation.read_pos > buffer_size) + { + TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, buffer_size, _fs_operation.read_pos); + *read_count = buffer_size; + if (_fs_operation.read_pos + buffer_size < FS_MAX_NODE_BYTES) + memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); + _fs_operation.read_pos += *read_count; + } + else + { + TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, obj->size - _fs_operation.read_pos, _fs_operation.read_pos); + *read_count = obj->size - _fs_operation.read_pos; + if (_fs_operation.read_pos + buffer_size < FS_MAX_NODE_BYTES) + memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); + // Read operation completed + _fs_operation.read_handle = 0; + _fs_operation.read_pos = 0; + } + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) +{ + fs_object_info_t *obj; + + if (new_parent_object_handle == 0xFFFFFFFF) + new_parent_object_handle = 0; + + // Ensure we are not moving to an nonexisting parent + if (new_parent_object_handle != 0) + { + obj = fs_object_get_from_handle(new_parent_object_handle); + if (obj == NULL) + { + TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); + return MTP_RESC_INVALID_PARENT_OBJECT; + } + if (!obj->association) + { + TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); + return MTP_RESC_INVALID_PARENT_OBJECT; + } + } + + obj = fs_object_get_from_handle(object_handle); + + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + TU_LOG1("Move object %ld to new parent %ld\r\n", object_handle, new_parent_object_handle); + obj->parent = new_parent_object_handle; + return MTP_RESC_OK; +} + +mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) +{ + fs_object_info_t *obj; + + if (_fs_operation.session_id == 0) + { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESC_SESSION_NOT_OPEN; + } + + if (object_handle == 0xFFFFFFFF) + object_handle = 0; + + if (object_handle != 0) + { + obj = fs_object_get_from_handle(object_handle); + + if (obj == NULL) + { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESC_INVALID_OBJECT_HANDLE; + } + obj->allocated = false; + TU_LOG1("Delete object with handle %ld\r\n", object_handle); + } + + if (object_handle == 0 || obj->association) + { + // Delete also children + for (unsigned int i=0; iallocated && obj->parent == object_handle) + { + tud_mtp_storage_object_delete(obj->handle); + } + } + } + + return MTP_RESC_OK; +} + +void tud_mtp_storage_object_done(void) +{ +} + +void tud_mtp_storage_cancel(void) +{ + fs_object_info_t *obj; + + _fs_operation.traversal_parent = 0; + _fs_operation.traversal_index = 0; + _fs_operation.read_handle = 0; + _fs_operation.read_pos = 0; + // If write operation is canceled, discard object + if (_fs_operation.write_handle) + { + obj = fs_object_get_from_handle(_fs_operation.write_handle); + if (obj) + obj->allocated = false; + } + _fs_operation.write_handle = 0; + _fs_operation.write_pos = 0; +} + +void tud_mtp_storage_reset(void) +{ + tud_mtp_storage_cancel(); + _fs_operation.session_id = 0; +} diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h new file mode 100644 index 000000000..251a99fd3 --- /dev/null +++ b/examples/device/mtp/src/tusb_config.h @@ -0,0 +1,112 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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 + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* 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_MTP 1 + +#define CFG_TUD_MANUFACTURER "TinyUsb Manufacturer" +#define CFG_TUD_MODEL "TinyUsb Device" + +#define CFG_MTP_EP_SIZE 64 +#define CFG_MTP_EVT_EP_SIZE 64 +#define CFG_MTP_EVT_INTERVAL 100 + +#define CFG_MTP_DEVICE_VERSION "1.0" +#define CFG_MTP_SERIAL_NUMBER "0" +#define CFG_MTP_INTERFACE (CFG_TUD_MODEL " MTP") +#define CFG_MTP_STORAGE_ID_COUNT 1 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c new file mode 100644 index 000000000..73685c3fd --- /dev/null +++ b/examples/device/mtp/src/usb_descriptors.c @@ -0,0 +1,193 @@ +/* + * 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 "bsp/board_api.h" +#include "tusb.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] MTP | VENDOR | MIDI | 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) | _PID_MAP(MTP, 5)) + +#define USB_VID 0xCafe +#define USB_BCD 0x0200 + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + .bDeviceClass = TUSB_CLASS_UNSPECIFIED, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .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; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_MTP = 0, + ITF_NUM_TOTAL +}; + +#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 + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ... + #define EPNUM_MTP_EVT 0x81 + #define EPNUM_MTP_OUT 0x02 + #define EPNUM_MTP_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_CXD56 + // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number + // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN) + #define EPNUM_MTP_EVT 0x83 + #define EPNUM_MTP_OUT 0x02 + #define EPNUM_MTP_IN 0x81 + +#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_MTP_EVT 0x81 + #define EPNUM_MTP_OUT 0x03 + #define EPNUM_MTP_IN 0x82 + +#else + #define EPNUM_MTP_EVT 0x81 + #define EPNUM_MTP_OUT 0x02 + #define EPNUM_MTP_IN 0x82 +#endif + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MTP_DESC_LEN) + +uint8_t const desc_fs_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + TUD_MTP_DESCRIPTOR(ITF_NUM_MTP, 4, EPNUM_MTP_EVT, CFG_MTP_EVT_EP_SIZE, CFG_MTP_EVT_INTERVAL, EPNUM_MTP_OUT, EPNUM_MTP_IN, CFG_MTP_EP_SIZE), +}; + +// 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_fs_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// String Descriptor Index +enum { + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_MTP, +}; + +// array of pointer to string descriptors +char const *string_desc_arr[] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + CFG_TUD_MANUFACTURER, // 1: Manufacturer + CFG_TUD_MODEL, // 2: Product + NULL, // 3: Serials will use unique ID if possible + CFG_MTP_INTERFACE, // 4: MTP Interface +}; + +static uint16_t _desc_str[32 + 1]; + +// 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; + size_t chr_count; + + switch ( index ) { + case STRID_LANGID: + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + + case STRID_SERIAL: + chr_count = board_usb_get_serial(_desc_str + 1, 32); + break; + + default: + // 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); + size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) chr_count = max_count; + + // Convert ASCII string into UTF-16 + for ( size_t i = 0; i < chr_count; i++ ) { + _desc_str[1 + i] = str[i]; + } + break; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); + + return _desc_str; +} diff --git a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt index 00e288bad..beabcad9c 100644 --- a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt +++ b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND srcs ${tusb_src}/class/hid/hid_device.c ${tusb_src}/class/midi/midi_device.c ${tusb_src}/class/msc/msc_device.c + ${tusb_src}/class/mtp/mtp_device.c ${tusb_src}/class/net/ecm_rndis_device.c ${tusb_src}/class/net/ncm_device.c ${tusb_src}/class/usbtmc/usbtmc_device.c diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index 2182e9ad1..3bec5bf70 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -95,6 +95,7 @@ target_sources(tinyusb_device_base INTERFACE ${TOP}/src/class/hid/hid_device.c ${TOP}/src/class/midi/midi_device.c ${TOP}/src/class/msc/msc_device.c + ${TOP}/src/class/mtp/mtp_device.c ${TOP}/src/class/net/ecm_rndis_device.c ${TOP}/src/class/net/ncm_device.c ${TOP}/src/class/usbtmc/usbtmc_device.c diff --git a/lib/rt-thread/SConscript b/lib/rt-thread/SConscript index 34399fd45..99517a090 100644 --- a/lib/rt-thread/SConscript +++ b/lib/rt-thread/SConscript @@ -34,6 +34,8 @@ if GetDepend(["PKG_TINYUSB_DEVICE_ENABLE"]): src += ["../../src/class/cdc/cdc_device.c"] if GetDepend(["PKG_TINYUSB_DEVICE_MSC"]): src += ["../../src/class/msc/msc_device.c", "port/msc_device_port.c"] + if GetDepend(["PKG_TINYUSB_DEVICE_MTP"]): + src += ["../../src/class/mtp/mtp_device.c"] if GetDepend(["PKG_TINYUSB_DEVICE_HID"]): src += ["../../src/class/hid/hid_device.c"] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55c52033c..9918407b3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ function(tinyusb_target_add TARGET) ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_device.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/mtp/mtp_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ncm_device.c ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h new file mode 100644 index 000000000..33c6bb552 --- /dev/null +++ b/src/class/mtp/mtp.h @@ -0,0 +1,438 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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_MTP_H_ +#define _TUSB_MTP_H_ + +#include +#include +#include +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_MTP) + +#ifdef __cplusplus + extern "C" { +#endif + +#define TU_ARRAY_LEN(a) (sizeof(a)/sizeof(a[0])) +#define STORAGE_ID(physical_id, logical_id) ( (((uint32_t)physical_id & 0xFFFF) << 16) | ((uint32_t)logical_id & 0x0000FFFF) ) +typedef uint16_t wchar16_t; + +//--------------------------------------------------------------------+ +// Media Transfer Protocol Class Constant +//--------------------------------------------------------------------+ + +// Media Transfer Protocol Subclass +typedef enum +{ + MTP_SUBCLASS = 1 +} mtp_subclass_type_t; + +// MTP Protocol. +typedef enum +{ + MTP_PROTOCOL_STILL_IMAGE = 1, +} mtp_protocol_type_t; + +// PTP/MTP protocol phases +typedef enum +{ + MTP_PHASE_IDLE = 0, + MTP_PHASE_COMMAND, + MTP_PHASE_DATA_IN, + MTP_PHASE_DATA_OUT, + MTP_PHASE_RESPONSE, + MTP_PHASE_ERROR, + MTP_PHASE_NONE, +} mtp_phase_type_t; + +// PTP/MTP Class requests +typedef enum +{ + MTP_REQ_CANCEL = 0x64, + MTP_REQ_GET_EXT_EVENT_DATA = 0x65, + MTP_REQ_RESET = 0x66, + MTP_REQ_GET_DEVICE_STATUS = 0x67, +} mtp_class_request_t; + +#define MTP_GENERIC_DATA_BLOCK_LENGTH 12 +#define MTP_MAX_PACKET_SIZE 512 + +// PTP/MTP Generic container +typedef struct TU_ATTR_PACKED +{ + uint32_t container_length; + uint16_t container_type; + uint16_t code; + uint32_t transaction_id; + uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; +} mtp_generic_container_t; + +// PTP/MTP Container type +typedef enum +{ + MTP_CONTAINER_TYPE_UNDEFINED = 0, + MTP_CONTAINER_TYPE_COMMAND_BLOCK = 1, + MTP_CONTAINER_TYPE_DATA_BLOCK = 2, + MTP_CONTAINER_TYPE_RESPONSE_BLOCK = 3, + MTP_CONTAINER_TYPE_EVENT_BLOCK = 4, +} mtp_container_type_t; + +// Supported OperationCode +typedef enum +{ + MTP_OPEC_GET_DEVICE_INFO = 0x1001u, + MTP_OPEC_OPEN_SESSION = 0x1002u, + MTP_OPEC_CLOSE_SESSION = 0x1003u, + MTP_OPEC_GET_STORAGE_IDS = 0x1004u, + MTP_OPEC_GET_STORAGE_INFO = 0x1005u, + MTP_OPEC_GET_NUM_OBJECTS = 0x1006u, + MTP_OPEC_GET_OBJECT_HANDLES = 0x1007u, + MTP_OPEC_GET_OBJECT_INFO = 0x1008u, + MTP_OPEC_GET_OBJECT = 0x1009u, + MTP_OPEC_GET_THUMB = 0x100Au, + MTP_OPEC_DELETE_OBJECT = 0x100Bu, + MTP_OPEC_SEND_OBJECT_INFO = 0x100Cu, + MTP_OPEC_SEND_OBJECT = 0x100Du, + MTP_OPEC_INITIAL_CAPTURE = 0x100Eu, + MTP_OPEC_FORMAT_STORE = 0x100Fu, + MTP_OPEC_RESET_DEVICE = 0x1010u, + MTP_OPEC_SELF_TEST = 0x1011u, + MTP_OPEC_SET_OBJECT_PROTECTION = 0x1012u, + MTP_OPEC_POWER_DOWN = 0x1013u, + MTP_OPEC_GET_DEVICE_PROP_DESC = 0x1014u, + MTP_OPEC_GET_DEVICE_PROP_VALUE = 0x1015u, + MTP_OPEC_SET_DEVICE_PROP_VALUE = 0x1016u, + MTP_OPEC_RESET_DEVICE_PROP_VALUE = 0x1017u, + MTP_OPEC_TERMINATE_OPEN_CAPTURE = 0x1018u, + MTP_OPEC_MOVE_OBJECT = 0x1019u, + MTP_OPEC_COPY_OBJECT = 0x101Au, + MTP_OPEC_GET_PARTIAL_OBJECT = 0x101Bu, + MTP_OPEC_INITIATE_OPEN_CAPTURE = 0x101Bu, + MTP_OPEC_GET_OBJECT_PROPS_SUPPORTED = 0x9801u, + MTP_OPEC_GET_OBJECT_PROP_DESC = 0x9802u, + MTP_OPEC_GET_OBJECT_PROP_VALUE = 0x9803u, + MTP_OPEC_SET_OBJECT_PROP_VALUE = 0x9804u, + MTP_OPEC_GET_OBJECT_PROPLIST = 0x9805u, + MTP_OPEC_GET_OBJECT_PROP_REFERENCES = 0x9810u, + MTP_OPEC_GETSERVICEIDS = 0x9301u, + MTP_OPEC_GETSERVICEINFO = 0x9302u, + MTP_OPEC_GETSERVICECAPABILITIES = 0x9303u, + MTP_OPEC_GETSERVICEPROPDESC = 0x9304u, +} mtp_operation_code_t; + +// Supported EventCode +typedef enum +{ + MTP_EVTC_OBJECT_ADDED = 0x4002, +} mtp_event_code_t; + + +// Supported Device Properties +typedef enum +{ + MTP_DEVP_UNDEFINED = 0x5000u, + MTP_DEVP_BATTERY_LEVEL = 0x5001u, + MTP_DEVP_DEVICE_FRIENDLY_NAME = 0xD402u, +} mtp_event_properties_t; + +// Supported Object Properties +typedef enum +{ + MTP_OBJP_STORAGE_ID = 0xDC01u, + MTP_OBJP_OBJECT_FORMAT = 0xDC02u, + MTP_OBJP_PROTECTION_STATUS = 0xDC03u, + MTP_OBJP_OBJECT_SIZE = 0xDC04u, + MTP_OBJP_ASSOCIATION_TYPE = 0xDC05u, + MTP_OBJP_OBJECT_FILE_NAME = 0xDC07u, + MTP_OBJP_PARENT_OBJECT = 0xDC0Bu, + MTP_OBJP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41u, + MTP_OBJP_NAME = 0xDC44u, +} mtp_object_properties_t; + +// Object formats +typedef enum +{ + MTP_OBJF_UNDEFINED = 0x3000u, + MTP_OBJF_ASSOCIATION = 0x3001u, + MTP_OBJF_TEXT = 0x3004u, +} mtp_object_formats_t; + +// Predefined Object handles +typedef enum +{ + MTP_OBJH_ROOT = 0x0000, +} mtp_object_handles_t; + +// Datatypes +typedef enum +{ + MTP_TYPE_UNDEFINED = 0x0000u, + MTP_TYPE_INT8 = 0x0001u, + MTP_TYPE_UINT8 = 0x0002u, + MTP_TYPE_INT16 = 0x0003u, + MTP_TYPE_UINT16 = 0x0004u, + MTP_TYPE_INT32 = 0x0005u, + MTP_TYPE_UINT32 = 0x0006u, + MTP_TYPE_INT64 = 0x0007u, + MTP_TYPE_UINT64 = 0x0008u, + MTP_TYPE_STR = 0xFFFFu, +} mtp_datatypes_t; + +// Get/Set +typedef enum +{ + MTP_MODE_GET = 0x00u, + MTP_MODE_GET_SET = 0x01u, +} mtp_mode_get_set_t; + +tu_static const uint16_t mtp_operations_supported[] = { + MTP_OPEC_GET_DEVICE_INFO, + MTP_OPEC_OPEN_SESSION, + MTP_OPEC_CLOSE_SESSION, + MTP_OPEC_GET_STORAGE_IDS, + MTP_OPEC_GET_STORAGE_INFO, + MTP_OPEC_GET_NUM_OBJECTS, + MTP_OPEC_GET_OBJECT_HANDLES, + MTP_OPEC_GET_OBJECT_INFO, + MTP_OPEC_GET_OBJECT, + MTP_OPEC_DELETE_OBJECT, + MTP_OPEC_SEND_OBJECT_INFO, + MTP_OPEC_SEND_OBJECT, + MTP_OPEC_FORMAT_STORE, + MTP_OPEC_RESET_DEVICE, + MTP_OPEC_GET_DEVICE_PROP_DESC, + MTP_OPEC_GET_DEVICE_PROP_VALUE, + MTP_OPEC_SET_DEVICE_PROP_VALUE, +}; + +tu_static const uint16_t mtp_events_supported[] = { + MTP_EVTC_OBJECT_ADDED, +}; + +tu_static const uint16_t mtp_device_properties_supported[] = { + MTP_DEVP_DEVICE_FRIENDLY_NAME, +}; + +tu_static const uint16_t mtp_capture_formats[] = { + MTP_OBJF_UNDEFINED, + MTP_OBJF_ASSOCIATION, + MTP_OBJF_TEXT, +}; + +tu_static const uint16_t mtp_playback_formats[] = { + MTP_OBJF_UNDEFINED, + MTP_OBJF_ASSOCIATION, + MTP_OBJF_TEXT, +}; + +//--------------------------------------------------------------------+ +// Data structures +//--------------------------------------------------------------------+ + +// DeviceInfo Dataset +#define MTP_EXTENSIONS "microsoft.com: 1.0; " +typedef struct TU_ATTR_PACKED { + uint16_t standard_version; + uint32_t mtp_vendor_extension_id; + uint16_t mtp_version; + uint8_t mtp_extensions_len; + wchar16_t mtp_extensions[TU_ARRAY_LEN(MTP_EXTENSIONS)] TU_ATTR_PACKED; + + uint16_t functional_mode; + /* Operations supported */ + uint32_t operations_supported_len; + uint16_t operations_supported[TU_ARRAY_LEN(mtp_operations_supported)] TU_ATTR_PACKED; + /* Events supported */ + uint32_t events_supported_len; + uint16_t events_supported[TU_ARRAY_LEN(mtp_events_supported)] TU_ATTR_PACKED; + /* Device properties supported */ + uint32_t device_properties_supported_len; + uint16_t device_properties_supported[TU_ARRAY_LEN(mtp_device_properties_supported)] TU_ATTR_PACKED; + /* Capture formats */ + uint32_t capture_formats_len; + uint16_t capture_formats[TU_ARRAY_LEN(mtp_capture_formats)] TU_ATTR_PACKED; + /* Playback formats */ + uint32_t playback_formats_len; + uint16_t playback_formats[TU_ARRAY_LEN(mtp_playback_formats)] TU_ATTR_PACKED; +} mtp_device_info_t; +// The following fields will be dynamically added to the struct at runtime: +// - wstring manufacturer +// - wstring model +// - wstring device_version +// - wstring serial_number + + +#define MTP_STRING_DEF(name, string) \ + uint8_t name##_len; \ + wchar16_t name[TU_ARRAY_LEN(string)]; + +#define MTP_ARRAY_DEF(name, array) \ + uint16_t name##_len; \ + typeof(name) name[TU_ARRAY_LEN(array)]; + +// StorageInfo dataset +typedef struct TU_ATTR_PACKED { + uint16_t storage_type; + uint16_t filesystem_type; + uint16_t access_capability; + uint64_t max_capacity_in_bytes; + uint64_t free_space_in_bytes; + uint32_t free_space_in_objects; +} mtp_storage_info_t; +// The following fields will be dynamically added to the struct at runtime: +// - wstring storage_description +// - wstring volume_identifier + +// ObjectInfo Dataset +typedef struct TU_ATTR_PACKED { + uint32_t storage_id; + uint16_t object_format; + uint16_t protection_status; + uint32_t object_compressed_size; + uint16_t thumb_format; // unused + uint32_t thumb_compressed_size; // unused + uint32_t thumb_pix_width; // unused + uint32_t thumb_pix_height; // unused + uint32_t image_pix_width; // unused + uint32_t image_pix_height; // unused + uint32_t image_bit_depth; // unused + uint32_t parent_object; // 0: root + uint16_t association_type; + uint32_t association_description; // not used + uint32_t sequence_number; // not used +} mtp_object_info_t; +// The following fields will be dynamically added to the struct at runtime: +// - wstring filename; +// - datetime_wstring date_created; +// - datetime_wstring date_modified; +// - wstring keywords; + +// Storage IDs +typedef struct TU_ATTR_PACKED { + uint32_t storage_ids_len; + uint32_t storage_ids[CFG_MTP_STORAGE_ID_COUNT]; +} mtp_storage_ids_t; + +// DevicePropDesc Dataset +typedef struct TU_ATTR_PACKED { + uint16_t device_property_code; + uint16_t datatype; + uint8_t get_set; +} mtp_device_prop_desc_t; +// The following fields will be dynamically added to the struct at runtime: +// - wstring factory_def_value; +// - wstring current_value_len; +// - uint8_t form_flag; + +typedef struct TU_ATTR_PACKED { + uint16_t wLength; + uint16_t code; +} mtp_device_status_res_t; + +typedef struct TU_ATTR_PACKED { + uint32_t object_handle; + uint32_t storage_id; + uint32_t parent_object_handle; +} mtp_basic_object_info_t; + +//--------------------------------------------------------------------+ +// Definitions +//--------------------------------------------------------------------+ + +typedef enum { + MTP_STORAGE_TYPE_UNDEFINED = 0x0000u, + MTP_STORAGE_TYPE_FIXED_ROM = 0x0001u, + MTP_STORAGE_TYPE_REMOVABLE_ROM = 0x0002u, + MTP_STORAGE_TYPE_FIXED_RAM = 0x0003u, + MTP_STORAGE_TYPE_REMOVABLE_RAM = 0x0004u, +} mtp_storage_type_t; + +typedef enum { + MTP_FILESYSTEM_TYPE_UNDEFINED = 0x0000u, + MTP_FILESYSTEM_TYPE_GENERIC_FLAT = 0x0001u, + MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL = 0x0002u, + MTP_FILESYSTEM_TYPE_DCF = 0x0003u, +} mtp_filesystem_type_t; + +typedef enum { + MTP_ACCESS_CAPABILITY_READ_WRITE = 0x0000u, + MTP_ACCESS_CAPABILITY_READ_ONLY_WITHOUT_OBJECT_DELETION = 0x0001u, + MTP_ACCESS_CAPABILITY_READ_ONLY_WITH_OBJECT_DELETION = 0x0002u, +} mtp_access_capability_t; + +typedef enum { + MTP_PROTECTION_STATUS_NO_PROTECTION = 0x0000u, + MTP_PROTECTION_STATUS_READ_ONLY = 0x0001u, + MTP_PROTECTION_STATUS_READ_ONLY_DATA = 0x8002u, + MTP_PROTECTION_NON_TRANSFERABLE_DATA = 0x8003u, +} mtp_protection_status_t; + +typedef enum { + MTP_ASSOCIATION_UNDEFINED = 0x0000u, + MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001u, + MTP_ASSOCIATION_GENERIC_ALBUM = 0x0002u, + MTP_ASSOCIATION_TIME_SEQUENCE = 0x0003u, + MTP_ASSOCIATION_HORIZONTAL_PANORAMIC = 0x0004u, + MTP_ASSOCIATION_VERTICAL_PANORAMIC = 0x0005u, + MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u, +} mtp_association_t; + +// Responses +typedef enum { +// Supported ResponseCode + MTP_RESC_UNDEFINED = 0x2000u, + MTP_RESC_OK = 0x2001u, + MTP_RESC_GENERAL_ERROR = 0x2002u, + MTP_RESC_SESSION_NOT_OPEN = 0x2003u, + MTP_RESC_INVALID_TRANSACTION_ID = 0x2004u, + MTP_RESC_OPERATION_NOT_SUPPORTED = 0x2005u, + MTP_RESC_PARAMETER_NOT_SUPPORTED = 0x2006u, + MTP_RESC_INCOMPLETE_TRANSFER = 0x2007u, + MTP_RESC_INVALID_STORAGE_ID = 0x2008u, + MTP_RESC_INVALID_OBJECT_HANDLE = 0x2009u, + MTP_RESC_STORE_FULL = 0x200Cu, + MTP_RESC_OBJECT_WRITE_PROTECTED = 0x200Du, + MTP_RESC_STORE_NOT_AVAILABLE = 0x2013u, + MTP_RESC_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014u, + MTP_RESC_NO_VALID_OBJECTINFO = 0x2015u, + MTP_RESC_DEVICE_BUSY = 0x2019u, + MTP_RESC_INVALID_PARENT_OBJECT = 0x201Au, + MTP_RESC_INVALID_DEVICE_PROP_FORMAT = 0x201Bu, + MTP_RESC_INVALID_DEVICE_PROP_VALUE = 0x201Cu, + MTP_RESC_INVALID_PARAMETER = 0x201Du, + MTP_RESC_SESSION_ALREADY_OPEN = 0x201Eu, + MTP_RESC_TRANSACTION_CANCELLED = 0x201Fu, +} mtp_response_t; + +#ifdef __cplusplus + } +#endif + +#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ + +#endif /* _TUSB_MTP_H_ */ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c new file mode 100644 index 000000000..854d4f10e --- /dev/null +++ b/src/class/mtp/mtp_device.c @@ -0,0 +1,997 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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 "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_MTP) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "device/dcd.h" // for faking dcd_event_xfer_complete +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "mtp_device.h" +#include "mtp_device_storage.h" + +// Level where CFG_TUSB_DEBUG must be at least for this driver is logged +#ifndef CFG_TUD_MTP_LOG_LEVEL + #define CFG_TUD_MTP_LOG_LEVEL CFG_TUD_LOG_LEVEL +#endif + +#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MTP_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// STRUCT +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + uint8_t ep_evt; + + // Bulk Only Transfer (BOT) Protocol + uint8_t phase; + + uint32_t queued_len; // number of bytes queued from the DataIN Stage + uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw + uint32_t xferred_len; // number of bytes transferred so far in the Data Stage + uint32_t handled_len; // number of bytes already handled in the Data Stage + bool xfer_completed; // true when DATA-IN/DATA-OUT transfer is completed + +} mtpd_interface_t; + +typedef struct +{ + uint32_t session_id; + uint32_t transaction_id; +} mtpd_context_t; + +//--------------------------------------------------------------------+ +// INTERNAL FUNCTION DECLARATION +//--------------------------------------------------------------------+ +// Checker +tu_static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); +tu_static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); + +// MTP commands +tu_static mtp_phase_type_t mtpd_handle_cmd(void); +tu_static mtp_phase_type_t mtpd_handle_data(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_info(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_open_session(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_close_session(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_object(void); +tu_static mtp_phase_type_t mtpd_handle_dti_get_object(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); +tu_static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_send_object(void); +tu_static mtp_phase_type_t mtpd_handle_dto_send_object(void); +tu_static mtp_phase_type_t mtpd_handle_cmd_format_store(void); + +//--------------------------------------------------------------------+ +// MTP variable declaration +//--------------------------------------------------------------------+ +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtpd_interface_t _mtpd_itf; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_generic_container_t _mtpd_gct; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtpd_context_t _mtpd_ctx; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_device_status_res_t _mtpd_device_status_res; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint32_t _mtpd_get_object_handle; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_basic_object_info_t _mtpd_soi; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static char _mtp_datestr[20]; + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void mtpd_init(void) { + TU_LOG_DRV(" MTP mtpd_init\n"); + tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); + tu_memclr(&_mtpd_ctx, sizeof(mtpd_context_t)); + _mtpd_get_object_handle = 0; + tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); +} + +bool mtpd_deinit(void) { + TU_LOG_DRV(" MTP mtpd_deinit\n"); + // nothing to do + return true; +} + +void mtpd_reset(uint8_t rhport) +{ + TU_LOG_DRV(" MTP mtpd_reset\n"); + (void) rhport; + + // Close all endpoints + dcd_edpt_close_all(rhport); + tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); + tu_memclr(&_mtpd_ctx, sizeof(mtpd_context_t)); + tu_memclr(&_mtpd_gct, sizeof(mtp_generic_container_t)); + _mtpd_get_object_handle = 0; + tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); +} + +uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +{ + TU_LOG_DRV(" MTP mtpd_open\n"); + tusb_desc_endpoint_t const *ep_desc; + // only support SCSI's BOT protocol + TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass && + MTP_SUBCLASS == itf_desc->bInterfaceSubClass && + MTP_PROTOCOL_STILL_IMAGE == itf_desc->bInterfaceProtocol, 0); + + // mtp driver length is fixed + uint16_t const mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); + + // Max length must be at least 1 interface + 3 endpoints + TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= mtpd_itf_size); + + _mtpd_itf.itf_num = itf_desc->bInterfaceNumber; + + // Open interrupt IN endpoint + ep_desc = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); + TU_ASSERT(ep_desc->bDescriptorType == TUSB_DESC_ENDPOINT && ep_desc->bmAttributes.xfer == TUSB_XFER_INTERRUPT, 0); + TU_ASSERT(usbd_edpt_open(rhport, ep_desc), 0); + _mtpd_itf.ep_evt = ep_desc->bEndpointAddress; + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &_mtpd_itf.ep_out, &_mtpd_itf.ep_in), 0 ); + + // Prepare rx on bulk out EP + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + + return mtpd_itf_size; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) +{ + TU_LOG_DRV(" MTP mtpd_control_xfer_cb: bmRequest=0x%2x, bRequest=0x%2x\n", request->bmRequestType, request->bRequest); + // nothing to do with DATA & ACK stage + if (stage != CONTROL_STAGE_SETUP) return true; + + uint16_t len = 0; + + switch ( request->bRequest ) + { + case MTP_REQ_CANCEL: + TU_LOG_DRV(" MTP request: MTP_REQ_CANCEL\n"); + tud_mtp_storage_cancel(); + break; + case MTP_REQ_GET_EXT_EVENT_DATA: + TU_LOG_DRV(" MTP request: MTP_REQ_GET_EXT_EVENT_DATA\n"); + break; + case MTP_REQ_RESET: + TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); + tud_mtp_storage_reset(); + // Prepare for a new command + TU_ASSERT( usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE) ); + break; + case MTP_REQ_GET_DEVICE_STATUS: + TU_LOG_DRV(" MTP request: MTP_REQ_GET_DEVICE_STATUS\n"); + len = 4; + _mtpd_device_status_res.wLength = len; + // Cancel is synchronous, always answer OK + _mtpd_device_status_res.code = MTP_RESC_OK; + TU_ASSERT( tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len) ); + break; + + default: + TU_LOG_DRV(" MTP request: invalid request\r\n"); + return false; // stall unsupported request + } + return true; +} + +// Transfer on bulk endpoints +bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + const unsigned dir = tu_edpt_dir(ep_addr); + + if (event != XFER_RESULT_SUCCESS) + return false; + + // IN transfer completed + if (dir == TUSB_DIR_IN) + { + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) + { + // IN transfer completed, prepare for a new command + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + _mtpd_itf.phase = MTP_PHASE_IDLE; + } + else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) + { + _mtpd_itf.xferred_len += xferred_bytes; + _mtpd_itf.handled_len = _mtpd_itf.xferred_len; + + // Check if transfer completed + if (_mtpd_itf.xferred_len >= _mtpd_itf.total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) + { + _mtpd_itf.phase = MTP_PHASE_RESPONSE; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.transaction_id = _mtpd_ctx.transaction_id; + if (_mtpd_ctx.session_id != 0) + { + _mtpd_gct.data[0] = _mtpd_ctx.session_id; + _mtpd_gct.container_length += sizeof(uint32_t); + } + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length), 0); + } + else + // Send next block of DATA + { + // Send Zero-Lenght Packet + if (_mtpd_itf.xferred_len == _mtpd_itf.total_len) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), 0 )); + } + else + { + _mtpd_itf.phase = mtpd_handle_data(); + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); + else + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), (uint16_t)_mtpd_itf.queued_len)); + } + } + } + else + { + return false; + } + } + + if (dir == TUSB_DIR_OUT) + { + if (_mtpd_itf.phase == MTP_PHASE_IDLE) + { + // A new command has been received. Ensure this is the last of the sequence. + _mtpd_itf.total_len = _mtpd_gct.container_length; + // Stall in case of unexpected block + if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) + { + return false; + } + _mtpd_itf.phase = MTP_PHASE_COMMAND; + _mtpd_itf.total_len = _mtpd_gct.container_length; + _mtpd_itf.xferred_len = xferred_bytes; + _mtpd_itf.handled_len = 0; + _mtpd_itf.xfer_completed = false; + TU_ASSERT(_mtpd_itf.total_len < sizeof(mtp_generic_container_t)); + } + + if (_mtpd_itf.phase == MTP_PHASE_COMMAND) + { + // A zero-length or a short packet termination is expected + if (xferred_bytes == CFG_MTP_EP_SIZE || (_mtpd_itf.total_len - _mtpd_itf.xferred_len) > 0 ) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)(_mtpd_itf.total_len - _mtpd_itf.xferred_len))); + } + else + { + // Handle command block + _mtpd_itf.phase = mtpd_handle_cmd(); + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); + } + else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_itf.queued_len)); + _mtpd_itf.total_len = _mtpd_gct.container_length; + _mtpd_itf.xferred_len = 0; + _mtpd_itf.handled_len = 0; + _mtpd_itf.xfer_completed = false; + } + else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + _mtpd_itf.xferred_len = 0; + _mtpd_itf.handled_len = 0; + _mtpd_itf.xfer_completed = false; + } + else + { + usbd_edpt_stall(rhport, _mtpd_itf.ep_out); + usbd_edpt_stall(rhport, _mtpd_itf.ep_in); + } + } + return true; + } + + if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) + { + // First block of data + if (_mtpd_itf.xferred_len == 0) + { + _mtpd_itf.total_len = _mtpd_gct.container_length; + _mtpd_itf.handled_len = 0; + _mtpd_itf.xfer_completed = false; + } + _mtpd_itf.xferred_len += xferred_bytes; + // Stall in case of unexpected block + if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_DATA_BLOCK) + { + return false; + } + + // A zero-length or a short packet termination + if (xferred_bytes < CFG_MTP_EP_SIZE) + { + _mtpd_itf.xfer_completed = true; + // Handle data block + _mtpd_itf.phase = mtpd_handle_data(); + if (_mtpd_itf.phase == MTP_PHASE_DATA_IN || _mtpd_itf.phase == MTP_PHASE_RESPONSE) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); + } + else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + _mtpd_itf.xferred_len = 0; + _mtpd_itf.xfer_completed = false; + } + else + { + usbd_edpt_stall(rhport, _mtpd_itf.ep_out); + usbd_edpt_stall(rhport, _mtpd_itf.ep_in); + } + } + else + { + // Handle data block when container is full + if (_mtpd_itf.xferred_len - _mtpd_itf.handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) + { + _mtpd_itf.phase = mtpd_handle_data(); + _mtpd_itf.handled_len = _mtpd_itf.xferred_len; + } + // Transfer completed: wait for zero-lenght packet + // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if + // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. + if (_mtpd_itf.total_len == _mtpd_itf.xferred_len) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)), CFG_MTP_EP_SIZE), 0); + } + // First data block includes container header + container data + else if (_mtpd_itf.handled_len == 0) + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); + } + else + // Successive data block includes only container data + { + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)) + _mtpd_itf.xferred_len - _mtpd_itf.handled_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); + } + } + } + } + return true; +} + +//--------------------------------------------------------------------+ +// MTPD Internal functionality +//--------------------------------------------------------------------+ + +// Decode command and prepare response +mtp_phase_type_t mtpd_handle_cmd(void) +{ + TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + _mtpd_ctx.transaction_id = _mtpd_gct.transaction_id; + if (_mtpd_gct.code != MTP_OPEC_SEND_OBJECT) + _mtpd_soi.object_handle = 0; + + switch(_mtpd_gct.code) + { + case MTP_OPEC_GET_DEVICE_INFO: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_INFO\n"); + return mtpd_handle_cmd_get_device_info(); + case MTP_OPEC_OPEN_SESSION: + TU_LOG_DRV(" MTP command: MTP_OPEC_OPEN_SESSION\n"); + return mtpd_handle_cmd_open_session(); + case MTP_OPEC_CLOSE_SESSION: + TU_LOG_DRV(" MTP command: MTP_OPEC_CLOSE_SESSION\n"); + return mtpd_handle_cmd_close_session(); + case MTP_OPEC_GET_STORAGE_IDS: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_STORAGE_IDS\n"); + return mtpd_handle_cmd_get_storage_ids(); + case MTP_OPEC_GET_STORAGE_INFO: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_STORAGE_INFO for ID=%lu\n", _mtpd_gct.data[0]); + return mtpd_handle_cmd_get_storage_info(); + case MTP_OPEC_GET_OBJECT_HANDLES: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT_HANDLES\n"); + return mtpd_handle_cmd_get_object_handles(); + case MTP_OPEC_GET_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT_INFO\n"); + return mtpd_handle_cmd_get_object_info(); + case MTP_OPEC_GET_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT\n"); + return mtpd_handle_cmd_get_object(); + case MTP_OPEC_DELETE_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OPEC_DELETE_OBJECT\n"); + return mtpd_handle_cmd_delete_object(); + case MTP_OPEC_GET_DEVICE_PROP_DESC: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_PROP_DESC\n"); + return mtpd_handle_cmd_get_device_prop_desc(); + case MTP_OPEC_GET_DEVICE_PROP_VALUE: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_PROP_VALUE\n"); + return mtpd_handle_cmd_get_device_prop_value(); + case MTP_OPEC_SEND_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT_INFO\n"); + return mtpd_handle_cmd_send_object_info(); + case MTP_OPEC_SEND_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT\n"); + return mtpd_handle_cmd_send_object(); + case MTP_OPEC_FORMAT_STORE: + TU_LOG_DRV(" MTP command: MTP_OPEC_FORMAT_STORE\n"); + return mtpd_handle_cmd_format_store(); + default: + TU_LOG_DRV(" MTP command: MTP_OPEC_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + return false; + } + return true; +} + +mtp_phase_type_t mtpd_handle_data(void) +{ + TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_DATA_BLOCK); + _mtpd_ctx.transaction_id = _mtpd_gct.transaction_id; + + switch(_mtpd_gct.code) + { + case MTP_OPEC_GET_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT-DATA_IN\n"); + return mtpd_handle_dti_get_object(); + case MTP_OPEC_SEND_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT_INFO-DATA_OUT\n"); + return mtpd_handle_dto_send_object_info(); + case MTP_OPEC_SEND_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT-DATA_OUT\n"); + return mtpd_handle_dto_send_object(); + default: + TU_LOG_DRV(" MTP command: MTP_OPEC_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + return false; + } + return true; +} + +mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) +{ + TU_VERIFY_STATIC(sizeof(mtp_device_info_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_info_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_DEVICE_INFO; + mtp_device_info_t *d = (mtp_device_info_t *)_mtpd_gct.data; + d->standard_version = 100; + d->mtp_vendor_extension_id = 0x06; + d->mtp_version = 100; + d->mtp_extensions_len = TU_ARRAY_LEN(MTP_EXTENSIONS); + mtpd_wc16cpy((uint8_t *)d->mtp_extensions, MTP_EXTENSIONS); + d->functional_mode = 0x0000; + d->operations_supported_len = TU_ARRAY_LEN(mtp_operations_supported); + memcpy(d->operations_supported, mtp_operations_supported, sizeof(mtp_operations_supported)); + d->events_supported_len = TU_ARRAY_LEN(mtp_events_supported); + memcpy(d->events_supported, mtp_events_supported, sizeof(mtp_events_supported)); + d->device_properties_supported_len = TU_ARRAY_LEN(mtp_device_properties_supported); + memcpy(d->device_properties_supported, mtp_device_properties_supported, sizeof(mtp_device_properties_supported)); + d->capture_formats_len = TU_ARRAY_LEN(mtp_capture_formats); + memcpy(d->capture_formats, mtp_capture_formats, sizeof(mtp_capture_formats)); + d->playback_formats_len = TU_ARRAY_LEN(mtp_playback_formats); + memcpy(d->playback_formats, mtp_playback_formats, sizeof(mtp_playback_formats)); + mtpd_gct_append_wstring(CFG_TUD_MANUFACTURER); + mtpd_gct_append_wstring(CFG_TUD_MODEL); + mtpd_gct_append_wstring(CFG_MTP_DEVICE_VERSION); + mtpd_gct_append_wstring(CFG_MTP_SERIAL_NUMBER); + + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_open_session(void) +{ + uint32_t session_id = _mtpd_gct.data[0]; + + mtp_response_t res = tud_mtp_storage_open_session(&session_id); + if (res == MTP_RESC_SESSION_ALREADY_OPEN) + { + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = res; + _mtpd_gct.container_length += sizeof(_mtpd_gct.data[0]); + _mtpd_gct.data[0] = session_id; + _mtpd_ctx.session_id = session_id; + return MTP_PHASE_RESPONSE; + } + + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + _mtpd_ctx.session_id = session_id; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_OK; + + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_close_session(void) +{ + uint32_t session_id = _mtpd_gct.data[0]; + + mtp_response_t res = tud_mtp_storage_close_session(session_id); + + _mtpd_ctx.session_id = session_id; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = res; + + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) +{ + TU_VERIFY_STATIC(sizeof(mtp_storage_ids_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_ids_t shall fit in MTP_MAX_PACKET_SIZE"); + + uint32_t storage_id; + mtp_response_t res = tud_mtp_get_storage_id(&storage_id); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_ids_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_STORAGE_IDS; + mtp_storage_ids_t *d = (mtp_storage_ids_t *)_mtpd_gct.data; + if (storage_id == 0) + { + // Storage not accessible + d->storage_ids_len = 0; + d->storage_ids[0] = 0; + } + else + { + d->storage_ids_len = 1; + d->storage_ids[0] = storage_id; + } + + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) +{ + TU_VERIFY_STATIC(sizeof(mtp_storage_info_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_info_t shall fit in MTP_MAX_PACKET_SIZE"); + + uint32_t storage_id = _mtpd_gct.data[0]; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_info_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_STORAGE_INFO; + + mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)_mtpd_gct.data); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) +{ + uint32_t storage_id = _mtpd_gct.data[0]; + uint32_t object_format_code = _mtpd_gct.data[1]; // optional, not managed + uint32_t parent_object_handle = _mtpd_gct.data[2]; // folder specification, 0xffffffff=objects with no parent + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(uint32_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_OBJECT_HANDLES; + _mtpd_gct.data[0] = 0; + + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESC_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) return phase; + //list of all object handles on all storages, not managed + if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESC_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) return phase; + + tud_mtp_storage_object_done(); + uint32_t next_child_handle = 0; + while(true) + { + mtp_response_t res = tud_mtp_storage_association_get_object_handle(storage_id, parent_object_handle, &next_child_handle); + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if (next_child_handle == 0) + break; + mtpd_gct_append_object_handle(next_child_handle); + } + tud_mtp_storage_object_done(); + + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) +{ + TU_VERIFY_STATIC(sizeof(mtp_object_info_t) < MTP_MAX_PACKET_SIZE, "mtp_object_info_t shall fit in MTP_MAX_PACKET_SIZE"); + + uint32_t object_handle = _mtpd_gct.data[0]; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_object_info_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_OBJECT_INFO; + mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)_mtpd_gct.data); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_get_object(void) +{ + _mtpd_get_object_handle = _mtpd_gct.data[0]; + + // Continue with DATA-IN + return mtpd_handle_dti_get_object(); +} + +mtp_phase_type_t mtpd_handle_dti_get_object(void) +{ + mtp_response_t res; + mtp_phase_type_t phase; + uint32_t file_size = 0; + res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + file_size; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_OBJECT; + + uint32_t buffer_size; + uint32_t read_count; + // Data block must be multiple of EP size + if (_mtpd_itf.handled_len == 0) + { + // First data block: include container header + buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_GENERIC_DATA_BLOCK_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_GENERIC_DATA_BLOCK_LENGTH; + res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + _mtpd_itf.queued_len = MTP_GENERIC_DATA_BLOCK_LENGTH + read_count; + } + else + { + // Successive data block: consider only container data + buffer_size = (MTP_MAX_PACKET_SIZE / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE; + res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + _mtpd_itf.queued_len = read_count; + } + + // File completed + if (read_count < buffer_size) + { + tud_mtp_storage_object_done(); + } + + return MTP_PHASE_DATA_IN; +} + +mtp_phase_type_t mtpd_handle_cmd_delete_object(void) +{ + uint32_t object_handle = _mtpd_gct.data[0]; + uint32_t object_code_format = _mtpd_gct.data[1]; // not used + (void) object_code_format; + + mtp_response_t res = tud_mtp_storage_object_delete(object_handle); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) +{ + uint32_t device_prop_code = _mtpd_gct.data[0]; + + mtp_phase_type_t rt; + if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; + + switch(device_prop_code) + { + case MTP_DEVP_DEVICE_FRIENDLY_NAME: + { + TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_DEVICE_PROP_DESC; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_prop_desc_t); + mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)_mtpd_gct.data; + d->device_property_code = (uint16_t)(device_prop_code); + d->datatype = MTP_TYPE_STR; + d->get_set = MTP_MODE_GET; + mtpd_gct_append_wstring(CFG_TUD_MODEL); // factory_def_value + mtpd_gct_append_wstring(CFG_TUD_MODEL); // current_value_len + mtpd_gct_append_uint8(0x00); // form_flag + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; + } + default: + break; + } + + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_PARAMETER_NOT_SUPPORTED; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void) +{ + uint32_t device_prop_code = _mtpd_gct.data[0]; + + mtp_phase_type_t rt; + if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; + _mtpd_gct.code = MTP_OPEC_GET_DEVICE_PROP_VALUE; + + switch(device_prop_code) + { + // TODO support more device properties + case MTP_DEVP_DEVICE_FRIENDLY_NAME: + mtpd_gct_append_wstring(CFG_TUD_MODEL); + _mtpd_itf.queued_len = _mtpd_gct.container_length; + return MTP_PHASE_DATA_IN; + default: + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_PARAMETER_NOT_SUPPORTED; + return MTP_PHASE_RESPONSE; + } +} + +mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) +{ + _mtpd_soi.storage_id = _mtpd_gct.data[0]; + _mtpd_soi.parent_object_handle = (_mtpd_gct.data[1] == 0xFFFFFFFF ? 0 : _mtpd_gct.data[1]); + + // Enter OUT phase and wait for DATA BLOCK + return MTP_PHASE_DATA_OUT; +} + +mtp_phase_type_t mtpd_handle_dto_send_object_info(void) +{ + uint32_t new_object_handle = 0; + mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_t *)_mtpd_gct.data); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + + // Save send_object_info + _mtpd_soi.object_handle = new_object_handle; + + // Response + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + 3 * sizeof(uint32_t); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.data[0] = _mtpd_soi.storage_id; + _mtpd_gct.data[1] = _mtpd_soi.parent_object_handle; + _mtpd_gct.data[2] = _mtpd_soi.object_handle; + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_send_object(void) +{ + // Enter OUT phase and wait for DATA BLOCK + return MTP_PHASE_DATA_OUT; +} + +mtp_phase_type_t mtpd_handle_dto_send_object(void) +{ + uint8_t *buffer = (uint8_t *)&_mtpd_gct.data; + uint32_t buffer_size = _mtpd_itf.xferred_len - _mtpd_itf.handled_len; + // First block of DATA + if (_mtpd_itf.handled_len == 0) + { + buffer_size -= MTP_GENERIC_DATA_BLOCK_LENGTH; + } + + if (buffer_size > 0) + { + mtp_response_t res = tud_mtp_storage_object_write(_mtpd_soi.object_handle, buffer, buffer_size); + mtp_phase_type_t phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + } + + if (!_mtpd_itf.xfer_completed) + { + // Continue with next DATA BLOCK + return MTP_PHASE_DATA_OUT; + } + + // Send completed + tud_mtp_storage_object_done(); + + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_OK; + return MTP_PHASE_RESPONSE; +} + +mtp_phase_type_t mtpd_handle_cmd_format_store(void) +{ + uint32_t storage_id = _mtpd_gct.data[0]; + uint32_t file_system_format = _mtpd_gct.data[1]; // not used + (void) file_system_format; + + mtp_response_t res = tud_mtp_storage_format(storage_id); + + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = res; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + return MTP_PHASE_RESPONSE; +} + +//--------------------------------------------------------------------+ +// Checker +//--------------------------------------------------------------------+ +mtp_phase_type_t mtpd_chk_session_open(const char *func_name) +{ + (void)func_name; + if (_mtpd_ctx.session_id == 0) + { + TU_LOG_DRV(" MTP error: %s session not open\n", func_name); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = MTP_RESC_SESSION_NOT_OPEN; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + return MTP_PHASE_RESPONSE; + } + return MTP_PHASE_NONE; +} + +mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message) +{ + (void)func_name; + (void)message; + if (err_cd) + { + TU_LOG_DRV(" MTP error in %s: (%x) %s\n", func_name, ret_code, message); + _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + _mtpd_gct.code = ret_code; + _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + return MTP_PHASE_RESPONSE; + } + return MTP_PHASE_NONE; +} + +//--------------------------------------------------------------------+ +// Generic container data +//--------------------------------------------------------------------+ +void mtpd_wc16cpy(uint8_t *dest, const char *src) +{ + wchar16_t s; + while(true) + { + s = *src; + memcpy(dest, &s, sizeof(wchar16_t)); + if (*src == 0) break; + ++src; + dest += sizeof(wchar16_t); + } +} + +//--------------------------------------------------------------------+ +// Generic container function +//--------------------------------------------------------------------+ +bool mtpd_gct_append_uint8(const uint8_t value) +{ + uint8_t *p_value = ((uint8_t *)&_mtpd_gct) + _mtpd_gct.container_length; + _mtpd_gct.container_length += sizeof(uint8_t); + // Verify space requirement (8 bit string length, number of wide characters including terminator) + TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); + *p_value = value; + return true; +} + +bool mtpd_gct_append_object_handle(const uint32_t object_handle) +{ + _mtpd_gct.container_length += sizeof(uint32_t); + TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); + _mtpd_gct.data[0]++; + _mtpd_gct.data[_mtpd_gct.data[0]] = object_handle; + return true; +} + +bool mtpd_gct_append_wstring(const char *s) +{ + size_t len = strlen(s) + 1; + TU_ASSERT(len <= UINT8_MAX); + uint8_t *p_len = ((uint8_t *)&_mtpd_gct)+_mtpd_gct.container_length; + _mtpd_gct.container_length += sizeof(uint8_t) + sizeof(wchar16_t) * len; + // Verify space requirement (8 bit string length, number of wide characters including terminator) + TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); + *p_len = (uint8_t)len; + uint8_t *p_str = p_len + sizeof(uint8_t); + mtpd_wc16cpy(p_str, s); + return true; +} + +bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max_size) +{ + uint16_t size = *(((uint8_t *)&_mtpd_gct.data) + *offset_data); + if (size > max_size) + size = max_size; + TU_ASSERT(*offset_data + size < sizeof(_mtpd_gct.data)); + + uint8_t *s = ((uint8_t *)&_mtpd_gct.data) + *offset_data + sizeof(uint8_t); + for(uint16_t i = 0; i < size; i++) + { + string[i] = *s; + s += sizeof(wchar16_t); + } + *offset_data += (uint16_t)(sizeof(uint8_t) + size * sizeof(wchar16_t)); + return true; +} + +bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_size) +{ + TU_ASSERT(_mtpd_gct.container_length + sizeof(uint32_t) + array_size * type_size < sizeof(_mtpd_gct.data)); + uint8_t *p = ((uint8_t *)&_mtpd_gct) + _mtpd_gct.container_length; + memcpy(p, &array_size, sizeof(uint32_t)); + p += sizeof(uint32_t); + memcpy(p, data, array_size * type_size); + _mtpd_gct.container_length += sizeof(uint32_t) + array_size * type_size; + return true; +} + +bool mtpd_gct_append_date(struct tm *timeinfo) +{ + // strftime is not supported by all platform, this implementation is just for reference + int len = snprintf(_mtp_datestr, sizeof(_mtpd_gct.data) - _mtpd_gct.container_length, "%04d%02d%02dT%02d%02d%02dZ", + timeinfo->tm_year + 1900, + timeinfo->tm_mon + 1, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec); + if (len == 0) + return false; + return mtpd_gct_append_wstring(_mtp_datestr); +} + +#endif // (CFG_TUD_ENABLED && CFG_TUD_MTP) diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h new file mode 100644 index 000000000..6531b36df --- /dev/null +++ b/src/class/mtp/mtp_device.h @@ -0,0 +1,75 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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_MTP_DEVICE_H_ +#define _TUSB_MTP_DEVICE_H_ + +#include "common/tusb_common.h" +#include "mtp.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_MTP) + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void mtpd_init (void); +bool mtpd_deinit (void); +void mtpd_reset (uint8_t rhport); +uint16_t mtpd_open (uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool mtpd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *p_request); +bool mtpd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +//--------------------------------------------------------------------+ +// Helper functions +//--------------------------------------------------------------------+ +// Generic container function +void mtpd_wc16cpy(uint8_t *dest, const char *src); +bool mtpd_gct_append_uint8(const uint8_t value); +bool mtpd_gct_append_object_handle(const uint32_t object_handle); +bool mtpd_gct_append_wstring(const char *s); +bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max_size); + +// Append the given array to the global context buffer +// The function returns true if the data fits in the available buffer space. +bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_size); + +// Append an UTC date string to the global context buffer +// Required format is 'YYYYMMDDThhmmss.s' optionally added 'Z' for UTC or +/-hhmm for time zone +// This function is provided for reference and only supports UTC format without partial seconds +// The function returns true if the data fits in the available buffer space. +bool mtpd_gct_append_date(struct tm *timeinfo); + +#ifdef __cplusplus + } +#endif + +#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ + +#endif /* _TUSB_MTP_DEVICE_H_ */ diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h new file mode 100644 index 000000000..a9bbc9b90 --- /dev/null +++ b/src/class/mtp/mtp_device_storage.h @@ -0,0 +1,149 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) + * + * 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_MTP_DEVICE_STORAGE_H_ +#define _TUSB_MTP_DEVICE_STORAGE_H_ + +#include "common/tusb_common.h" +#include "mtp.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_MTP) + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Storage Application Callbacks +//--------------------------------------------------------------------+ + +/* + * The entire MTP functionality is based on object handles, as described in MTP Specs v. 1.1 under 3.4.1. + * The major weakness of the protocol is that those handles are supposed to be unique within a session + * and for every enumerated object. There is no specified lifetime limit or way to control the expiration: + * once given, they have to persist for an indefinite time and number of iterations. + * If the filesystem does not provide unique persistent object handle for every entry, the best approach + * would be to keep a full association between generated handles and full file paths. The suggested + * approach with memory constrained devices is to keep a hard ID associated with each file or a volatile + * ID generated on the fly and invalidated on each operation that may rearrange the order. + * In order to invalidate existing IDS, it might be necessary to invalidate the whole session from + * the device side. + * Depending on the application, the handle could be also be the file name or a tag (i.e. host-only file access) + */ + + +// Initialize MTP storage subsystem +// +// The function shall check if the session is already opened and, in case, set session_id to the +// ID of the current session. +mtp_response_t tud_mtp_storage_open_session(uint32_t *session_id); + +// Close an open session +mtp_response_t tud_mtp_storage_close_session(uint32_t session_id); + +// Get a storage ID valid within the current session +// +// TODO: while multiple storage IDs could be used, the implementation currently supports only 1. +mtp_response_t tud_mtp_get_storage_id(uint32_t *storage_id); + +// Get storage information for the given ID +// +// The implementation shall fill all the fields required by the specification. +// Note that the variable information (e.g. wstring file name, dates and tags shall be written by using the library functions) +// In addition to the fixed mtp_storage_info_t structure, the function shall add storage descriptor string and +// volume identifier string via tud_mtp_gct_append_wstring function. +mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t *info); + +// Format the specified storage +mtp_response_t tud_mtp_storage_format(uint32_t storage_id); + +// Traverse the given parent object handle and return a child handle for each call +// +// If the parent object has not been opened (or closed before) the function returns the first handle. +// When next_child_handle is 0 all the handles have been listed. +// TODO: traverse by ObjectFormatCode and ObjectHandle association. For now they are unsupported. +mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t session_handle, uint32_t parent_object_handle, uint32_t *next_child_handle); + +// Called with the creation of a new object is requested. +// The handle of the new object shall be returned in new_object_handle. +// The structure info contains the information to be used for file creation, as passted by the host. +// Note that the variable information (e.g. wstring file name, dates and tags shall be retrieved by using the library functions) +mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_t *info); + +// Get object information related to a given object handle +// +// The structure info shall be filled according to MTP specifications. Note that +// in addition to filling the fixed mtp_object_info_t structure, the caller must add the following fields via +// library calls +// - Filename (string, use tud_mtp_gct_append_wstring) +// - Date created (string, use tud_gct_append_date or empty string) +// - Date modified (string, use tud_gct_append_date or empty string) +// - Keywords (string containing list of kw, separated by space, use tud_mtp_gct_append_wstring) +// Note that the variable information (e.g. wstring file name, dates and tags shall be written by using the library functions) +mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t *info); + +// Get the object size. +// +// The object may be already open when this function is called. +// The implementation shall not assume a specific call order between this function and tud_mtp_storage_object_read. +// The function may leave the file open. +mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *size); + +// Write object data +// +// The function shall open the object for writing if not already open. +// The binary data shall be written to the file in full before this function is returned. +mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t buffer_size); + +// Get object data +// +// The function shall open the object for reading if not already open. +// The amount of data returned shall be the given size parameter. +// read_count shall contain the effective number of bytes written. Iteration is terminated when read_count < buffer_size. +mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, uint32_t buffer_size, uint32_t *read_count); + +// Move an object to a new parent +mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle); + +// Delete the specified object +mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle); + +// Issued when IO operation has been terminated (e.g. read, traverse), close open file handles +void tud_mtp_storage_object_done(void); + +// Cancel any pending operation. Current operation shall be discarded. +void tud_mtp_storage_cancel(void); + +// Restore the operation out of reset. Cancel any pending operation and close the session. +void tud_mtp_storage_reset(void); + +#ifdef __cplusplus + } +#endif + +#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ + +#endif /* _TUSB_MTP_DEVICE_STORAGE_H_ */ diff --git a/src/device/usbd.c b/src/device/usbd.c index fb5cec49d..8f50d815f 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -309,6 +309,19 @@ tu_static usbd_class_driver_t const _usbd_driver[] = { .sof = NULL }, #endif + + #if CFG_TUD_MTP + { + .name = DRIVER_NAME("MTP"), + .init = mtpd_init, + .deinit = mtpd_deinit, + .reset = mtpd_reset, + .open = mtpd_open, + .control_xfer_cb = mtpd_control_xfer_cb, + .xfer_cb = mtpd_xfer_cb, + .sof = NULL + }, + #endif }; enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; diff --git a/src/device/usbd.h b/src/device/usbd.h index de6007fb3..b89a0200b 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -269,6 +269,25 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 +//--------------------------------------------------------------------+ +// MTP Descriptor Templates +//--------------------------------------------------------------------+ + +// Length of template descriptor: 30 bytes +#define TUD_MTP_DESC_LEN (9 + 7 + 7 + 7) + +// Interface number, string index, EP Out & EP In address, EP size +#define TUD_MTP_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_polling_interval, _epout, _epin, _epsize) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUSB_CLASS_IMAGE, MTP_SUBCLASS, MTP_PROTOCOL_STILL_IMAGE, _stridx,\ + /* Endpoint Interrupt */\ + 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_polling_interval,\ + /* Endpoint Out */\ + 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\ + /* Endpoint In */\ + 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + + //--------------------------------------------------------------------+ // HID Descriptor Templates //--------------------------------------------------------------------+ diff --git a/src/tinyusb.mk b/src/tinyusb.mk index a9f623c24..8f9d52de9 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -12,6 +12,7 @@ TINYUSB_SRC_C += \ src/class/hid/hid_device.c \ src/class/midi/midi_device.c \ src/class/msc/msc_device.c \ + src/class/mtp/mtp_device.c \ src/class/net/ecm_rndis_device.c \ src/class/net/ncm_device.c \ src/class/usbtmc/usbtmc_device.c \ diff --git a/src/tusb.h b/src/tusb.h index dfba21ddf..abe3e3c9b 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -88,6 +88,10 @@ #include "class/msc/msc_device.h" #endif + #if CFG_TUD_MTP + #include "class/mtp/mtp_device.h" + #endif + #if CFG_TUD_AUDIO #include "class/audio/audio_device.h" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 29fdcb0d6..e36a56de9 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -493,6 +493,10 @@ #define CFG_TUD_MSC 0 #endif +#ifndef CFG_TUD_MTP + #define CFG_TUD_MTP 0 +#endif + #ifndef CFG_TUD_HID #define CFG_TUD_HID 0 #endif diff --git a/test/fuzz/rules.mk b/test/fuzz/rules.mk index ee91c706d..24d6cc0d7 100644 --- a/test/fuzz/rules.mk +++ b/test/fuzz/rules.mk @@ -31,6 +31,7 @@ SRC_C += \ src/class/hid/hid_device.c \ src/class/midi/midi_device.c \ src/class/msc/msc_device.c \ + src/class/mtp/mtp_device.c \ src/class/net/ecm_rndis_device.c \ src/class/net/ncm_device.c \ src/class/usbtmc/usbtmc_device.c \ diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf index 33a6ef045..c93795b9c 100644 --- a/tools/iar_template.ipcf +++ b/tools/iar_template.ipcf @@ -58,6 +58,12 @@ $TUSB_DIR$/src/class/msc/msc_device.h $TUSB_DIR$/src/class/msc/msc_host.h + + $TUSB_DIR$/src/class/mtp/mtp_device.c + $TUSB_DIR$/src/class/mtp/mtp.h + $TUSB_DIR$/src/class/mtp/mtp_device.h + $TUSB_DIR$/src/class/mtp/mtp_device_storage.h + $TUSB_DIR$/src/class/net/ecm_rndis_device.c $TUSB_DIR$/src/class/net/ncm_device.c -- cgit v1.3.1 From 5b9908a39ba089dfd56b3a23475be5d9691d13e8 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 7 Aug 2025 14:20:40 +0700 Subject: update at32f405 dwc2 info and phy width selection --- src/portable/synopsys/dwc2/dwc2_common.c | 24 +++---- src/portable/synopsys/dwc2/dwc2_info.md | 116 +++++++++++++++---------------- src/portable/synopsys/dwc2/dwc2_info.py | 2 + 3 files changed, 72 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index b001f343d..70876a64c 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -92,6 +92,14 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4}; + uint8_t phy_width; + if (CFG_TUSB_MCU != OPT_MCU_AT32F402_405 && // at32f402_405 does not support 16-bit + ghwcfg4.phy_data_width) { + phy_width = 16; // 16-bit PHY interface if supported + } else { + phy_width = 8; // 8-bit PHY interface + } + // De-select FS PHY gusbcfg &= ~GUSBCFG_PHYSEL; @@ -119,12 +127,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL; // Set 16-bit interface if supported - if (ghwcfg4.phy_data_width) { - #if CFG_TUSB_MCU != OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit - gusbcfg |= GUSBCFG_PHYIF16; // 16 bit - #endif + if (phy_width == 16) { + gusbcfg |= GUSBCFG_PHYIF16; } else { - gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit + gusbcfg &= ~GUSBCFG_PHYIF16; } } @@ -141,13 +147,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // - 9 if using 8-bit PHY interface // - 5 if using 16-bit PHY interface gusbcfg &= ~GUSBCFG_TRDT_Msk; - -#if CFG_TUSB_MCU == OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit - gusbcfg |= 9u << GUSBCFG_TRDT_Pos; -#else - gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; -#endif - + gusbcfg |= (phy_width == 16 ? 5u : 9u) << GUSBCFG_TRDT_Pos; dwc2->gusbcfg = gusbcfg; // MCU specific PHY update post reset diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md index 595c89b75..00685e7ad 100644 --- a/src/portable/synopsys/dwc2/dwc2_info.md +++ b/src/portable/synopsys/dwc2/dwc2_info.md @@ -1,58 +1,58 @@ -| | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | -|:---------------------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| -| GUID | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | -| GSNPSID | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | -| - specs version | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | -| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | -| GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | -| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | -| - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | -| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | -| - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | -| - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | -| - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - num_host_ch | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | -| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | -| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | -| - xfer_size_width | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | -| - packet_size_width | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | -| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - i2c_enable | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | -| - vendor_ctrl_itf | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | -| - optional_feature_removed | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - synch_reset | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - otg_adp_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - battery_charger_support | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | -| - dfifo_depth | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | -| GHWCFG4 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | -| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - partial_powerdown | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - enhanced_lpm_support | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - phy_data_width | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | -| - ctrl_ep_num | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - num_dev_in_eps | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - dma_desc_enable | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | -| - dma_desc_dynamic | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| | AT32 F405 FS | AT32 F405 HS | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | +|:---------------------------|:---------------|:---------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| +| GUID | 0x00002000 | 0x00000000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | +| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | +| - specs version | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | +| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | +| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | +| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | +| - arch | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | +| - single_point | hub | hub | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | +| - hs_phy_type | n/a | UTMI+/ULPI | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | +| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | +| - num_dev_ep | 7 | 7 | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - num_host_ch | 15 | 15 | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | +| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - nptx_q_depth | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | +| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | +| - xfer_size_width | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | +| - packet_size_width | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | +| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | +| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | +| - optional_feature_removed | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - synch_reset | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | +| - dfifo_depth | 512 | 1008 | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | +| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | +| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - partial_powerdown | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | +| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - num_dev_in_eps | 7 | 7 | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - dma_desc_enable | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index 3b1993cc5..bc5ad9a7b 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -9,6 +9,8 @@ import pandas as pd # Note: FS is FullSpeed, HS is HighSpeed dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4'] dwc2_reg_value = { + 'AT32 F405 FS': [0x00002000, 0x4F54400A, 0x00000000, 0x228FDD00, 0x020004E8, 0x1FF0A020], + 'AT32 F405 HS': [0x00000000, 0x4F54400A, 0x00000000, 0x229FDDD0, 0x03F006E8, 0x1FF0A020], 'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020], 'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030], 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030], -- cgit v1.3.1 From ddb8f0fe73fd733b0fd97462b14825dcb6039949 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 15 Aug 2025 10:15:24 -0700 Subject: Add ESP32-C5 and ESP32-C61 definitions --- src/common/tusb_mcu.h | 2 +- src/tusb_option.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 6632341d7..3ffc2d1a4 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -414,7 +414,7 @@ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64 -#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) +#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C5, OPT_MCU_ESP32C6, OPT_MCU_ESP32C61, OPT_MCU_ESP32H2) #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) #error "MCUs are only supported with CFG_TUH_MAX3421 enabled" #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index 17bf556a3..1533bb209 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -128,6 +128,8 @@ #define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2 #define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2 #define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4 +#define OPT_MCU_ESP32C5 908 ///< Espressif ESP32-C5 +#define OPT_MCU_ESP32C61 909 ///< Espressif ESP32-C61 #define TUSB_MCU_VENDOR_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU #define TUP_MCU_ESPRESSIF TUSB_MCU_VENDOR_ESPRESSIF // for backward compatibility -- cgit v1.3.1 From e1cd4aa91f184f79e3c069968c9ee1f634daba3e Mon Sep 17 00:00:00 2001 From: ohmdelta <64962148+ohmdelta@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:23:33 +0100 Subject: add some consumer page configs --- src/class/hid/hid.h | 393 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 388 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index c2434c20d..0fc0e1377 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -893,14 +893,51 @@ enum { /// HID Usage Table: Consumer Page (0x0C) /// Only contains controls that supported by Windows (whole list is too long) enum { + HID_USAGE_CONSUMER_UNASSIGNED = 0x0000, + // Generic Control HID_USAGE_CONSUMER_CONTROL = 0x0001, + HID_USAGE_CONSUMER_NUMERIC_KEY_PAD = 0x0002, + HID_USAGE_CONSUMER_PROGRAMMABLE_BUTTONS = 0x0003, + HID_USAGE_CONSUMER_MICROPHONE = 0x0004, + HID_USAGE_CONSUMER_HEADPHONE = 0x0005, + HID_USAGE_CONSUMER_GRAPHIC_EQUALIZER = 0x0006, + // 07-1F Reserved + + HID_USAGE_CONSUMER_PLUS_10 = 0x0020, + HID_USAGE_CONSUMER_PLUS_100 = 0x0021, + HID_USAGE_CONSUMER_AM_PM = 0x0022, + // 23-3F Reserved // Power Control HID_USAGE_CONSUMER_POWER = 0x0030, HID_USAGE_CONSUMER_RESET = 0x0031, HID_USAGE_CONSUMER_SLEEP = 0x0032, + HID_USAGE_CONSUMER_SLEEP_AFTER = 0x0033, + HID_USAGE_CONSUMER_SLEEP_MODE = 0x0034, + HID_USAGE_CONSUMER_ILLUMINATION = 0x0035, + HID_USAGE_CONSUMER_FUNCTION_BUTTONS = 0x0036, + // 37-3F Reserved + HID_USAGE_CONSUMER_MENU = 0x0040, + HID_USAGE_CONSUMER_MENU_PICK = 0x0041, + HID_USAGE_CONSUMER_MENU_UP = 0x0042, + HID_USAGE_CONSUMER_MENU_DOWN = 0x0043, + HID_USAGE_CONSUMER_MENU_LEFT = 0x0044, + HID_USAGE_CONSUMER_MENU_RIGHT = 0x0045, + HID_USAGE_CONSUMER_MENU_ESCAPE = 0x0046, + HID_USAGE_CONSUMER_MENU_VALUE_INCREASE = 0x0047, + HID_USAGE_CONSUMER_MENU_VALUE_DECREASE = 0x0048, + // 49-5F Reserved + HID_USAGE_CONSUMER_DATA_ON_SCREEN = 0x0060, + HID_USAGE_CONSUMER_CLOSED_CAPTION = 0x0061, + HID_USAGE_CONSUMER_CLOSED_CAPTION_SELECT = 0x0062, + HID_USAGE_CONSUMER_VCR_TV = 0x0063, + HID_USAGE_CONSUMER_BROADCAST_MODE = 0x0064, + HID_USAGE_CONSUMER_SNAPSHOT = 0x0065, + HID_USAGE_CONSUMER_STILL = 0x0066, + + // 67-7F Reserved // Screen Brightness HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, @@ -912,40 +949,386 @@ enum { HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, - // Media Control - HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + HID_USAGE_CONSUMER_SELECTION = 0x0080, + HID_USAGE_CONSUMER_ASSIGN_SELECTION = 0x0081, + HID_USAGE_CONSUMER_MODE_STEP = 0x0082, + HID_USAGE_CONSUMER_RECALL_LAST = 0x0083, + HID_USAGE_CONSUMER_ENTER_CHANNEL = 0x0084, + HID_USAGE_CONSUMER_ORDER_MOVIE = 0x0085, + HID_USAGE_CONSUMER_CHANNEL = 0x0086, + HID_USAGE_CONSUMER_MEDIA_SELECTION = 0x0087, + HID_USAGE_CONSUMER_MEDIA_SELECT_COMPUTER = 0x0088, + HID_USAGE_CONSUMER_MEDIA_SELECT_TV = 0x0089, + HID_USAGE_CONSUMER_MEDIA_SELECT_WWW = 0x008A, + HID_USAGE_CONSUMER_MEDIA_SELECT_DVD = 0x008B, + HID_USAGE_CONSUMER_MEDIA_SELECT_TELEPHONE = 0x008C, + HID_USAGE_CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE = 0x008D, + HID_USAGE_CONSUMER_MEDIA_SELECT_VIDEO_PHONE = 0x008E, + HID_USAGE_CONSUMER_MEDIA_SELECT_GAMES = 0x008F, + HID_USAGE_CONSUMER_MEDIA_SELECT_MESSAGES = 0x0090, + HID_USAGE_CONSUMER_MEDIA_SELECT_CD = 0x0091, + HID_USAGE_CONSUMER_MEDIA_SELECT_VCR = 0x0092, + HID_USAGE_CONSUMER_MEDIA_SELECT_TUNER = 0x0093, + HID_USAGE_CONSUMER_QUIT = 0x0094, + HID_USAGE_CONSUMER_HELP = 0x0095, + HID_USAGE_CONSUMER_MEDIA_SELECT_TAPE = 0x0096, + HID_USAGE_CONSUMER_MEDIA_SELECT_CABLE = 0x0097, + HID_USAGE_CONSUMER_MEDIA_SELECT_SATELLITE = 0x0098, + HID_USAGE_CONSUMER_MEDIA_SELECT_SECURITY = 0x0099, + HID_USAGE_CONSUMER_MEDIA_SELECT_HOME = 0x009A, + HID_USAGE_CONSUMER_MEDIA_SELECT_CALL = 0x009B, + HID_USAGE_CONSUMER_CHANNEL_INCREMENT = 0x009C, + HID_USAGE_CONSUMER_CHANNEL_DECREMENT = 0x009D, + HID_USAGE_CONSUMER_MEDIA_SELECT_SAP = 0x009E, + // 9F Reserved + HID_USAGE_CONSUMER_VCR_PLUS = 0x00A0, + HID_USAGE_CONSUMER_ONCE = 0x00A1, + HID_USAGE_CONSUMER_DAILY = 0x00A2, + HID_USAGE_CONSUMER_WEEKLY = 0x00A3, + HID_USAGE_CONSUMER_MONTHLY = 0x00A4, + // A5-AF Reserved + + HID_USAGE_CONSUMER_PLAY = 0x00B0, + HID_USAGE_CONSUMER_PAUSE = 0x00B1, + HID_USAGE_CONSUMER_RECORD = 0x00B2, + HID_USAGE_CONSUMER_FAST_FORWARD = 0x00B3, + HID_USAGE_CONSUMER_REWIND = 0x00B4, + HID_USAGE_CONSUMER_SCAN_NEXT_TRACK = 0x00B5, HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, + HID_USAGE_CONSUMER_SCAN_PREVIOUS_TRACK = 0x00B6, HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, HID_USAGE_CONSUMER_STOP = 0x00B7, + HID_USAGE_CONSUMER_EJECT = 0x00B8, + HID_USAGE_CONSUMER_RANDOM_PLAY = 0x00B9, + HID_USAGE_CONSUMER_SELECT_DISC = 0x00BA, + HID_USAGE_CONSUMER_ENTER_DISC = 0x00BB, + HID_USAGE_CONSUMER_REPEAT = 0x00BC, + HID_USAGE_CONSUMER_TRACKING = 0x00BD, + HID_USAGE_CONSUMER_TRACK_NORMAL = 0x00BE, + HID_USAGE_CONSUMER_SLOW_TRACKING = 0x00BF, + HID_USAGE_CONSUMER_FRAME_FORWARD = 0x00C0, + HID_USAGE_CONSUMER_FRAME_BACK = 0x00C1, + HID_USAGE_CONSUMER_MARK = 0x00C2, + HID_USAGE_CONSUMER_CLEAR_MARK = 0x00C3, + HID_USAGE_CONSUMER_REPEAT_FROM_MARK = 0x00C4, + HID_USAGE_CONSUMER_RETURN_TO_MARK = 0x00C5, + HID_USAGE_CONSUMER_SEARCH_MARK_FORWARD = 0x00C6, + HID_USAGE_CONSUMER_SEARCH_MARK_BACKWARDS = 0x00C7, + HID_USAGE_CONSUMER_COUNTER_RESET = 0x00C8, + HID_USAGE_CONSUMER_SHOW_COUNTER = 0x00C9, + HID_USAGE_CONSUMER_TRACKING_INCREMENT = 0x00CA, + HID_USAGE_CONSUMER_TRACKING_DECREMENT = 0x00CB, + HID_USAGE_CONSUMER_STOP_EJECT = 0x00CC, + + + // Media Control + HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + + HID_USAGE_CONSUMER_PLAY_SKIP = 0x00CE, + + // CF-DF Reserved HID_USAGE_CONSUMER_VOLUME = 0x00E0, + HID_USAGE_CONSUMER_BALANCE = 0x00E1, HID_USAGE_CONSUMER_MUTE = 0x00E2, HID_USAGE_CONSUMER_BASS = 0x00E3, HID_USAGE_CONSUMER_TREBLE = 0x00E4, HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, + HID_USAGE_CONSUMER_SURROUND_MODE = 0x00E6, + HID_USAGE_CONSUMER_LOUDNESS = 0x00E7, + HID_USAGE_CONSUMER_MPX = 0x00E8, HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, + // EB-EF Reserved + HID_USAGE_CONSUMER_SPEED_SELECT = 0x00F0, + HID_USAGE_CONSUMER_PLAYBACK_SPEED = 0x00F1, + HID_USAGE_CONSUMER_STANDARD_PLAY = 0x00F2, + HID_USAGE_CONSUMER_LONG_PLAY = 0x00F3, + HID_USAGE_CONSUMER_EXTENDED_PLAY = 0x00F4, + HID_USAGE_CONSUMER_SLOW = 0x00F5, + // F6-FF Reserved + HID_USAGE_CONSUMER_FAN_ENABLE = 0x0100, + HID_USAGE_CONSUMER_FAN_SPEED = 0x0101, + HID_USAGE_CONSUMER_LIGHT_ENABLE = 0x0102, + HID_USAGE_CONSUMER_LIGHT_ILLUMINATION_LEVEL = 0x0103, + HID_USAGE_CONSUMER_CLIMATE_CONTROL_ENABLE = 0x0104, + HID_USAGE_CONSUMER_ROOM_TEMPERATURE = 0x0105, + HID_USAGE_CONSUMER_SECURITY_ENABLE = 0x0106, + HID_USAGE_CONSUMER_FIRE_ALARM = 0x0107, + HID_USAGE_CONSUMER_POLICE_ALARM = 0x0108, + HID_USAGE_CONSUMER_PROXIMITY = 0x0109, + HID_USAGE_CONSUMER_MOTION = 0x010A, + HID_USAGE_CONSUMER_DURESS_ALARM = 0x010B, + HID_USAGE_CONSUMER_HOLDUP_ALARM = 0x010C, + HID_USAGE_CONSUMER_MEDICAL_ALARM = 0x010D, + // 10E-14F Reserved + HID_USAGE_CONSUMER_BALANCE_RIGHT = 0x0150, + HID_USAGE_CONSUMER_BALANCE_LEFT = 0x0151, HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, - // Application Launcher + // 156-15F Reserved + HID_USAGE_CONSUMER_SPEAKER_SYSTEM = 0x0160, + HID_USAGE_CONSUMER_CHANNEL_LEFT = 0x0161, + HID_USAGE_CONSUMER_CHANNEL_RIGHT = 0x0162, + HID_USAGE_CONSUMER_CHANNEL_CENTER = 0x0163, + HID_USAGE_CONSUMER_CHANNEL_FRONT = 0x0164, + HID_USAGE_CONSUMER_CHANNEL_CENTER_FRONT = 0x0165, + HID_USAGE_CONSUMER_CHANNEL_SIDE = 0x0166, + HID_USAGE_CONSUMER_CHANNEL_SURROUND = 0x0167, + HID_USAGE_CONSUMER_CHANNEL_LOW_FREQUENCY = 0x0168, + // Enhancement + // CL 15.12.1 + HID_USAGE_CONSUMER_CHANNEL_TOP = 0x0169, + HID_USAGE_CONSUMER_CHANNEL_UNKNOWN = 0x016A, + // 16B-16F Reserved + HID_USAGE_CONSUMER_SUB_CHANNEL = 0x0170, + HID_USAGE_CONSUMER_SUB_CHANNEL_INCREMENT = 0x0171, + HID_USAGE_CONSUMER_SUB_CHANNEL_DECREMENT = 0x0172, + HID_USAGE_CONSUMER_ALTERNATE_AUDIO_INCREMENT = 0x0173, + HID_USAGE_CONSUMER_ALTERNATE_AUDIO_DECREMENT = 0x0174, + // 175-17F Reserved + HID_USAGE_CONSUMER_APPLICATION_LAUNCH_BUTTONS = 0x0180, + HID_USAGE_CONSUMER_AL_LAUNCH_BUTTON_CONFIGURATION = 0x0181, + // Tool + // Sel 15.15 + HID_USAGE_CONSUMER_AL_PROGRAMMABLE_BUTTON = 0x0182, + // Configuration + // Sel 15.15 HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, + HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL = 0x0183, + // Configuration + // Sel 15.15 + HID_USAGE_CONSUMER_AL_WORD_PROCESSOR = 0x0184, + HID_USAGE_CONSUMER_AL_TEXT_EDITOR = 0x0185, + HID_USAGE_CONSUMER_AL_SPREADSHEET = 0x0186, + HID_USAGE_CONSUMER_AL_GRAPHICS_EDITOR = 0x0187, + HID_USAGE_CONSUMER_AL_PRESENTATION_APP = 0x0188, + HID_USAGE_CONSUMER_AL_DATABASE_APP = 0x0189, HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, + HID_USAGE_CONSUMER_AL_NEWSREADER = 0x018B, + HID_USAGE_CONSUMER_AL_VOICEMAIL = 0x018C, + HID_USAGE_CONSUMER_AL_CONTACTS_ADDRESS_BOOK = 0x018D, + HID_USAGE_CONSUMER_AL_CALENDAR_SCHEDULE = 0x018E, + HID_USAGE_CONSUMER_AL_TASK_PROJECT_MANAGER = 0x018F, + HID_USAGE_CONSUMER_AL_LOG_JOURNAL_TIMECARD = 0x0190, + HID_USAGE_CONSUMER_AL_CHECKBOOK_FINANCE = 0x0191, HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, + HID_USAGE_CONSUMER_AL_A_V_CAPTURE_PLAYBACK = 0x0193, + HID_USAGE_CONSUMER_AL_LOCAL_MACHINE_BROWSER = 0x0194, HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, - + HID_USAGE_CONSUMER_AL_LAN_WAN_BROWSER = 0x0195, + HID_USAGE_CONSUMER_AL_INTERNET_BROWSER = 0x0196, + HID_USAGE_CONSUMER_AL_REMOTE_NETWORKING_ISP = 0x0197, + // Connect + // Sel 15.15 + HID_USAGE_CONSUMER_AL_NETWORK_CONFERENCE = 0x0198, + HID_USAGE_CONSUMER_AL_NETWORK_CHAT = 0x0199, + HID_USAGE_CONSUMER_AL_TELEPHONY_DIALER = 0x019A, + HID_USAGE_CONSUMER_AL_LOGON = 0x019B, + HID_USAGE_CONSUMER_AL_LOGOFF = 0x019C, + HID_USAGE_CONSUMER_AL_LOGON_LOGOFF = 0x019D, + HID_USAGE_CONSUMER_AL_TERMINAL_LOCK_SCREENSAVER = 0x019E, + HID_USAGE_CONSUMER_AL_CONTROL_PANEL = 0x019F, + HID_USAGE_CONSUMER_AL_COMMAND_LINE_PROCESSOR_RUN = 0x01A0, + HID_USAGE_CONSUMER_AL_PROCESS_TASK_MANAGER = 0x01A1, + HID_USAGE_CONSUMER_AL_SELECT_TASK_APPLICATION = 0x01A2, + HID_USAGE_CONSUMER_AL_NEXT_TASK_APPLICATION = 0x01A3, + HID_USAGE_CONSUMER_AL_PREVIOUS_TASK_APPLICATION = 0x01A4, + HID_USAGE_CONSUMER_AL_PREEMPTIVE_HALT = 0x01A5, + // Task_Application + // Sel 15.15 + HID_USAGE_CONSUMER_AL_INTEGRATED_HELP_CENTER = 0x01A6, + HID_USAGE_CONSUMER_AL_DOCUMENTS = 0x01A7, + HID_USAGE_CONSUMER_AL_THESAURUS = 0x01A8, + HID_USAGE_CONSUMER_AL_DICTIONARY = 0x01A9, + HID_USAGE_CONSUMER_AL_DESKTOP = 0x01AA, + HID_USAGE_CONSUMER_AL_SPELL_CHECK = 0x01AB, + HID_USAGE_CONSUMER_AL_GRAMMAR_CHECK = 0x01AC, + HID_USAGE_CONSUMER_AL_WIRELESS_STATUS = 0x01AD, + HID_USAGE_CONSUMER_AL_KEYBOARD_LAYOUT = 0x01AE, + HID_USAGE_CONSUMER_AL_VIRUS_PROTECTION = 0x01AF, + HID_USAGE_CONSUMER_AL_ENCRYPTION = 0x01B0, + HID_USAGE_CONSUMER_AL_SCREEN_SAVER = 0x01B1, + HID_USAGE_CONSUMER_AL_ALARMS = 0x01B2, + HID_USAGE_CONSUMER_AL_CLOCK = 0x01B3, + HID_USAGE_CONSUMER_AL_FILE_BROWSER = 0x01B4, + HID_USAGE_CONSUMER_AL_POWER_STATUS = 0x01B5, + HID_USAGE_CONSUMER_AL_IMAGE_BROWSER = 0x01B6, + HID_USAGE_CONSUMER_AL_AUDIO_BROWSER = 0x01B7, + HID_USAGE_CONSUMER_AL_MOVIE_BROWSER = 0x01B8, + HID_USAGE_CONSUMER_AL_DIGITAL_RIGHTS_MANAGER = 0x01B9, + HID_USAGE_CONSUMER_AL_DIGITAL_WALLET = 0x01BA, + // 1BB Reserved + HID_USAGE_CONSUMER_AL_INSTANT_MESSAGING = 0x01BC, + HID_USAGE_CONSUMER_AL_OEM_FEATURES_TIPS_TUTORIAL = 0x01BD, + // Browser + // Sel 15.15 + HID_USAGE_CONSUMER_AL_OEM_HELP = 0x01BE, + HID_USAGE_CONSUMER_AL_ONLINE_COMMUNITY = 0x01BF, + HID_USAGE_CONSUMER_AL_ENTERTAINMENT_CONTENT = 0x01C0, + // Browser + // Sel 15.15 + HID_USAGE_CONSUMER_AL_ONLINE_SHOPPING_BROWSER = 0x01C1, + HID_USAGE_CONSUMER_AL_SMARTCARD_INFORMATION_HELP = 0x01C2, + HID_USAGE_CONSUMER_AL_MARKET_MONITOR_FINANCE = 0x01C3, + // Browser + // Sel 15.15 + HID_USAGE_CONSUMER_AL_CUSTOMIZED_CORPORATE_NEWS = 0x01C4, + // Browser + // Sel 15.15 + HID_USAGE_CONSUMER_AL_ONLINE_ACTIVITY_BROWSER = 0x01C5, + HID_USAGE_CONSUMER_AL_RESEARCH_SEARCH_BROWSER = 0x01C6, + HID_USAGE_CONSUMER_AL_AUDIO_PLAYER = 0x01C7, + // 1C8-1FF Reserved + HID_USAGE_CONSUMER_GENERIC_GUI_APPLICATION = 0x0200, + // ' Controls + // ' + HID_USAGE_CONSUMER_AC_NEW = 0x0201, + HID_USAGE_CONSUMER_AC_OPEN = 0x0202, + HID_USAGE_CONSUMER_AC_CLOSE = 0x0203, + HID_USAGE_CONSUMER_AC_EXIT = 0x0204, + HID_USAGE_CONSUMER_AC_MAXIMIZE = 0x0205, + HID_USAGE_CONSUMER_AC_MINIMIZE = 0x0206, + HID_USAGE_CONSUMER_AC_SAVE = 0x0207, + HID_USAGE_CONSUMER_AC_PRINT = 0x0208, + HID_USAGE_CONSUMER_AC_PROPERTIES = 0x0209, + HID_USAGE_CONSUMER_AC_UNDO = 0x021A, + HID_USAGE_CONSUMER_AC_COPY = 0x021B, + HID_USAGE_CONSUMER_AC_CUT = 0x021C, + HID_USAGE_CONSUMER_AC_PASTE = 0x021D, + HID_USAGE_CONSUMER_AC_SELECT_ALL = 0x021E, + HID_USAGE_CONSUMER_AC_FIND = 0x021F, + HID_USAGE_CONSUMER_AC_FIND_AND_REPLACE = 0x0220, // Browser/Explorer Specific HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, + HID_USAGE_CONSUMER_AC_GO_TO = 0x0222, HID_USAGE_CONSUMER_AC_HOME = 0x0223, HID_USAGE_CONSUMER_AC_BACK = 0x0224, HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, HID_USAGE_CONSUMER_AC_STOP = 0x0226, HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, + HID_USAGE_CONSUMER_AC_PREVIOUS_LINK = 0x0228, + HID_USAGE_CONSUMER_AC_NEXT_LINK = 0x0229, HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, - + HID_USAGE_CONSUMER_AC_HISTORY = 0x022B, + HID_USAGE_CONSUMER_AC_SUBSCRIPTIONS = 0x022C, + HID_USAGE_CONSUMER_AC_ZOOM_IN = 0x022D, + HID_USAGE_CONSUMER_AC_ZOOM_OUT = 0x022E, + HID_USAGE_CONSUMER_AC_ZOOM = 0x022F, + HID_USAGE_CONSUMER_AC_FULL_SCREEN_VIEW = 0x0230, + HID_USAGE_CONSUMER_AC_NORMAL_VIEW = 0x0231, + HID_USAGE_CONSUMER_AC_VIEW_TOGGLE = 0x0232, + HID_USAGE_CONSUMER_AC_SCROLL_UP = 0x0233, + HID_USAGE_CONSUMER_AC_SCROLL_DOWN = 0x0234, + HID_USAGE_CONSUMER_AC_SCROLL = 0x0235, + HID_USAGE_CONSUMER_AC_PAN_LEFT = 0x0236, + HID_USAGE_CONSUMER_AC_PAN_RIGHT = 0x0237, // Mouse Horizontal scroll HID_USAGE_CONSUMER_AC_PAN = 0x0238, + HID_USAGE_CONSUMER_AC_NEW_WINDOW = 0x0239, + HID_USAGE_CONSUMER_AC_TILE_HORIZONTALLY = 0x023A, + HID_USAGE_CONSUMER_AC_TILE_VERTICALLY = 0x023B, + HID_USAGE_CONSUMER_AC_FORMAT = 0x023C, + HID_USAGE_CONSUMER_AC_EDIT = 0x023D, + HID_USAGE_CONSUMER_AC_BOLD = 0x023E, + HID_USAGE_CONSUMER_AC_ITALICS = 0x023F, + HID_USAGE_CONSUMER_AC_UNDERLINE = 0x0240, + HID_USAGE_CONSUMER_AC_STRIKETHROUGH = 0x0241, + HID_USAGE_CONSUMER_AC_SUBSCRIPT = 0x0242, + HID_USAGE_CONSUMER_AC_SUPERSCRIPT = 0x0243, + HID_USAGE_CONSUMER_AC_ALL_CAPS = 0x0244, + HID_USAGE_CONSUMER_AC_ROTATE = 0x0245, + HID_USAGE_CONSUMER_AC_RESIZE = 0x0246, + HID_USAGE_CONSUMER_AC_FLIP_HORIZONTAL = 0x0247, + HID_USAGE_CONSUMER_AC_FLIP_VERTICAL = 0x0248, + HID_USAGE_CONSUMER_AC_MIRROR_HORIZONTAL = 0x0249, + HID_USAGE_CONSUMER_AC_MIRROR_VERTICAL = 0x024A, + HID_USAGE_CONSUMER_AC_FONT_SELECT = 0x024B, + HID_USAGE_CONSUMER_AC_FONT_COLOR = 0x024C, + HID_USAGE_CONSUMER_AC_FONT_SIZE = 0x024D, + HID_USAGE_CONSUMER_AC_JUSTIFY_LEFT = 0x024E, + HID_USAGE_CONSUMER_AC_JUSTIFY_CENTER_H = 0x024F, + HID_USAGE_CONSUMER_AC_JUSTIFY_RIGHT = 0x0250, + HID_USAGE_CONSUMER_AC_JUSTIFY_BLOCK_H = 0x0251, + HID_USAGE_CONSUMER_AC_JUSTIFY_TOP = 0x0252, + HID_USAGE_CONSUMER_AC_JUSTIFY_CENTER_V = 0x0253, + HID_USAGE_CONSUMER_AC_JUSTIFY_BOTTOM = 0x0254, + HID_USAGE_CONSUMER_AC_JUSTIFY_BLOCK_V = 0x0255, + HID_USAGE_CONSUMER_AC_INDENT_DECREASE = 0x0256, + HID_USAGE_CONSUMER_AC_INDENT_INCREASE = 0x0257, + HID_USAGE_CONSUMER_AC_NUMBERED_LIST = 0x0258, + HID_USAGE_CONSUMER_AC_RESTART_NUMBERING = 0x0259, + HID_USAGE_CONSUMER_AC_BULLETED_LIST = 0x025A, + HID_USAGE_CONSUMER_AC_PROMOTE = 0x025B, + HID_USAGE_CONSUMER_AC_DEMOTE = 0x025C, + HID_USAGE_CONSUMER_AC_YES = 0x025D, + HID_USAGE_CONSUMER_AC_NO = 0x025E, + HID_USAGE_CONSUMER_AC_CANCEL = 0x025F, + HID_USAGE_CONSUMER_AC_CATALOG = 0x0260, + HID_USAGE_CONSUMER_AC_BUY_CHECKOUT = 0x0261, + HID_USAGE_CONSUMER_AC_ADD_TO_CART = 0x0262, + HID_USAGE_CONSUMER_AC_EXPAND = 0x0263, + HID_USAGE_CONSUMER_AC_EXPAND_ALL = 0x0264, + HID_USAGE_CONSUMER_AC_COLLAPSE = 0x0265, + HID_USAGE_CONSUMER_AC_COLLAPSE_ALL = 0x0266, + HID_USAGE_CONSUMER_AC_PRINT_PREVIEW = 0x0267, + HID_USAGE_CONSUMER_AC_PASTE_SPECIAL = 0x0268, + HID_USAGE_CONSUMER_AC_INSERT_MODE = 0x0269, + HID_USAGE_CONSUMER_AC_DELETE = 0x026A, + HID_USAGE_CONSUMER_AC_LOCK = 0x026B, + HID_USAGE_CONSUMER_AC_UNLOCK = 0x026C, + HID_USAGE_CONSUMER_AC_PROTECT = 0x026D, + HID_USAGE_CONSUMER_AC_UNPROTECT = 0x026E, + HID_USAGE_CONSUMER_AC_ATTACH_COMMENT = 0x026F, + HID_USAGE_CONSUMER_AC_DELETE_COMMENT = 0x0270, + HID_USAGE_CONSUMER_AC_VIEW_COMMENT = 0x0271, + HID_USAGE_CONSUMER_AC_SELECT_WORD = 0x0272, + HID_USAGE_CONSUMER_AC_SELECT_SENTENCE = 0x0273, + HID_USAGE_CONSUMER_AC_SELECT_PARAGRAPH = 0x0274, + HID_USAGE_CONSUMER_AC_SELECT_COLUMN = 0x0275, + HID_USAGE_CONSUMER_AC_SELECT_ROW = 0x0276, + HID_USAGE_CONSUMER_AC_SELECT_TABLE = 0x0277, + HID_USAGE_CONSUMER_AC_SELECT_OBJECT = 0x0278, + HID_USAGE_CONSUMER_AC_REDO_REPEAT = 0x0279, + HID_USAGE_CONSUMER_AC_SORT = 0x027A, + HID_USAGE_CONSUMER_AC_SORT_ASCENDING = 0x027B, + HID_USAGE_CONSUMER_AC_SORT_DESCENDING = 0x027C, + HID_USAGE_CONSUMER_AC_FILTER = 0x027D, + HID_USAGE_CONSUMER_AC_SET_CLOCK = 0x027E, + HID_USAGE_CONSUMER_AC_VIEW_CLOCK = 0x027F, + HID_USAGE_CONSUMER_AC_SELECT_TIME_ZONE = 0x0280, + HID_USAGE_CONSUMER_AC_EDIT_TIME_ZONES = 0x0281, + HID_USAGE_CONSUMER_AC_SET_ALARM = 0x0282, + HID_USAGE_CONSUMER_AC_CLEAR_ALARM = 0x0283, + HID_USAGE_CONSUMER_AC_SNOOZE_ALARM = 0x0284, + HID_USAGE_CONSUMER_AC_RESET_ALARM = 0x0285, + HID_USAGE_CONSUMER_AC_SYNCHRONIZE = 0x0286, + HID_USAGE_CONSUMER_AC_SEND_RECEIVE = 0x0287, + HID_USAGE_CONSUMER_AC_SEND_TO = 0x0288, + HID_USAGE_CONSUMER_AC_REPLY = 0x0289, + HID_USAGE_CONSUMER_AC_REPLY_ALL = 0x028A, + HID_USAGE_CONSUMER_AC_FORWARD_MSG = 0x028B, + HID_USAGE_CONSUMER_AC_SEND = 0x028C, + HID_USAGE_CONSUMER_AC_ATTACH_FILE = 0x028D, + HID_USAGE_CONSUMER_AC_UPLOAD = 0x028E, + HID_USAGE_CONSUMER_AC_DOWNLOAD_SAVE_TARGET_AS = 0x028F, + HID_USAGE_CONSUMER_AC_SET_BORDERS = 0x0290, + HID_USAGE_CONSUMER_AC_INSERT_ROW = 0x0291, + HID_USAGE_CONSUMER_AC_INSERT_COLUMN = 0x0292, + HID_USAGE_CONSUMER_AC_INSERT_FILE = 0x0293, + HID_USAGE_CONSUMER_AC_INSERT_PICTURE = 0x0294, + HID_USAGE_CONSUMER_AC_INSERT_OBJECT = 0x0295, + HID_USAGE_CONSUMER_AC_INSERT_SYMBOL = 0x0296, + HID_USAGE_CONSUMER_AC_SAVE_AND_CLOSE = 0x0297, + HID_USAGE_CONSUMER_AC_RENAME = 0x0298, + HID_USAGE_CONSUMER_AC_MERGE = 0x0299, + HID_USAGE_CONSUMER_AC_SPLIT = 0x029A, + HID_USAGE_CONSUMER_AC_DISRIBUTE_HORIZONTALLY = 0x029B, + HID_USAGE_CONSUMER_AC_DISTRIBUTE_VERTICALLY = 0x029C, + // 29D-FFFF Reserved + }; /// HID Usage Table: Digitizer Page (0x0D) -- cgit v1.3.1 From bd9dd75da8718dfd56b2e91b658f81613ed1bf3a Mon Sep 17 00:00:00 2001 From: Mitsumine Suzu <60875431+verylowfreq@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:26:08 +0900 Subject: Small cleanups. --- src/portable/wch/hcd_ch32_usbfs.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c index d176f40c5..fc52597c2 100644 --- a/src/portable/wch/hcd_ch32_usbfs.c +++ b/src/portable/wch/hcd_ch32_usbfs.c @@ -120,9 +120,6 @@ static usb_edpt_t *get_empty_record_slot(void) { static usb_edpt_t *add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t max_packet_size, uint8_t xfer_type) { usb_edpt_t *slot = get_empty_record_slot(); - if (slot == NULL) { - PANIC("add_edpt_record(0x%02x, 0x%02x, ...) no slot for new record\r\n", dev_addr, ep_addr); - } TU_ASSERT(slot != NULL, NULL); slot->dev_addr = dev_addr; @@ -412,10 +409,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } else { LOG_CH32_USBFSH("USB_PID_OUT continue...\r\n"); usb_current_xfer_info.buffer += tx_len; - uint16_t copylen = USBFS_TX_BUF_LEN; - if (copylen > usb_current_xfer_info.bufferlen) { - copylen = usb_current_xfer_info.bufferlen; - } + uint16_t copylen = TU_MIN(edpt_info->max_packet_size, usb_current_xfer_info.bufferlen); memcpy(USBFS_TX_Buf, usb_current_xfer_info.buffer, copylen); hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle); return; @@ -445,7 +439,10 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } } default: { - PANIC("Unknown PID: 0x%02x\n", request_pid); + LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid); + usb_current_xfer_info.is_busy = false; + hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); + return; } } } else { @@ -474,7 +471,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); return; } else { - LOG_CH32_USBFSH("In USBHD_IRQHandler, unexpected response PID: 0x%02x\r\n", response_pid); + LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid); usb_current_xfer_info.is_busy = false; hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); return; @@ -519,9 +516,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_current_xfer_info.is_busy = true; usb_edpt_t *edpt_info = get_edpt_record(dev_addr, ep_addr); - if (edpt_info == NULL) { - PANIC("get_edpt_record() returned NULL in hcd_edpt_xfer()\r\n"); - } + TU_ASSERT(edpt_info != NULL); hardware_set_port_address_speed(dev_addr); @@ -537,10 +532,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b return hardware_start_xfer(USB_PID_IN, ep_addr, edpt_info->data_toggle); } else { LOG_CH32_USBFSH("hcd_edpt_xfer(): WRITE, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen); - uint16_t copylen = USBFS_TX_BUF_LEN; - if (copylen > buflen) { - copylen = buflen; - } + uint16_t copylen = TU_MIN(edpt_info->max_packet_size, buflen); USBOTG_H_FS->HOST_TX_LEN = copylen; memcpy(USBFS_TX_Buf, buffer, copylen); return hardware_start_xfer(USB_PID_OUT, ep_addr, edpt_info->data_toggle); @@ -596,7 +588,6 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { (void) rhport; (void) dev_addr; LOG_CH32_USBFSH("hcd_edpt_clear_stall(rhport=%d, dev_addr=0x%02x, ep_addr=0x%02x)\r\n", rhport, dev_addr, ep_addr); - // PANIC("\r\install\r\n"); uint8_t edpt_num = tu_edpt_number(ep_addr); uint8_t setup_request_clear_stall[8] = { 0x02, 0x01, 0x00, 0x00, edpt_num, 0x00, 0x00, 0x00 -- cgit v1.3.1 From 522a34ee28bcee6674ccd91973cb87869be98909 Mon Sep 17 00:00:00 2001 From: Mitsumine Suzu <60875431+verylowfreq@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:28:32 +0900 Subject: Insert small delay for LowSpeed device --- src/portable/wch/hcd_ch32_usbfs.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c index fc52597c2..38bb0ed59 100644 --- a/src/portable/wch/hcd_ch32_usbfs.c +++ b/src/portable/wch/hcd_ch32_usbfs.c @@ -55,18 +55,18 @@ TU_ATTR_ALIGNED(4) static uint8_t USBFS_TX_Buf[USBFS_TX_BUF_LEN]; #define LOG_CH32_USBFSH(...) TU_LOG3(__VA_ARGS__) // Busywait for delay microseconds/nanoseconds -// static void loopdelay(uint32_t count) -// { -// volatile uint32_t c = count / 3; -// if (c == 0) { return; } -// // while (c-- != 0); -// asm volatile( -// "1: \n" // loop label -// " addi %0, %0, -1 \n" // c-- -// " bne %0, zero, 1b \n" // if (c != 0) goto loop -// : "+r"(c) // c is input/output operand -// ); -// } +static void loopdelay(uint32_t count) +{ + volatile uint32_t c = count / 3; + if (c == 0) { return; } + // while (c-- != 0); + asm volatile( + "1: \n" // loop label + " addi %0, %0, -1 \n" // c-- + " bne %0, zero, 1b \n" // if (c != 0) goto loop + : "+r"(c) // c is input/output operand + ); +} // Endpoint status @@ -194,10 +194,13 @@ static bool hardware_start_xfer(uint8_t pid, uint8_t ep_addr, uint8_t data_toggl : "(other)", pid, ep_addr, data_toggle); - // if (pid == USB_PID_IN) - // { // FIXME: long delay needed (at release build) about 30msec - // loopdelay(SystemCoreClock / 1000 * 30); - // } + //WORKAROUND: For LowSpeed device, insert small delay + bool is_lowspeed_device = tuh_speed_get(usb_current_xfer_info.dev_addr) == TUSB_SPEED_LOW; + if (is_lowspeed_device) { + //NOTE: worked -> SystemCoreClock / 1000000 * 50, 25 + // NOT worked -> 20 and less (at 144MHz internal clock) + loopdelay(SystemCoreClock / 1000000 * 40); + } uint8_t pid_edpt = (pid << 4) | (tu_edpt_number(ep_addr) & 0x0f); USBOTG_H_FS->HOST_TX_CTRL = (data_toggle != 0) ? USBFS_UH_T_TOG : 0; -- cgit v1.3.1 From a3bb7a90b3478f43d448443140b68c3473331f93 Mon Sep 17 00:00:00 2001 From: Mitsumine Suzu <60875431+verylowfreq@users.noreply.github.com> Date: Sun, 24 Aug 2025 16:46:12 +0900 Subject: Improve retry operation at NAK response. --- src/portable/wch/hcd_ch32_usbfs.c | 76 +++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c index 38bb0ed59..449908658 100644 --- a/src/portable/wch/hcd_ch32_usbfs.c +++ b/src/portable/wch/hcd_ch32_usbfs.c @@ -28,6 +28,8 @@ #if CFG_TUH_ENABLED && defined(TUP_USBIP_WCH_USBFS) && defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS +#include + #include "host/hcd.h" #include "host/usbh.h" #include "host/usbh_pvt.h" @@ -95,6 +97,7 @@ typedef struct usb_current_xfer_st { uint8_t *buffer; uint16_t bufferlen; uint16_t xferred_len; + bool nak_pending; } usb_current_xfer_t; static volatile usb_current_xfer_t usb_current_xfer_info = {}; @@ -351,6 +354,34 @@ void hcd_int_disable(uint8_t rhport) { interrupt_enabled = false; } +typedef struct { + uint8_t rhport; + uint8_t dev_addr; + uint8_t ep_addr; + uint16_t buflen; + uint8_t* buf; +} xfer_retry_param_t; + +static void xfer_retry(void* _params) { + LOG_CH32_USBFSH("xfer_retry()\r\n"); + xfer_retry_param_t* params = (xfer_retry_param_t*)_params; + if (usb_current_xfer_info.nak_pending) { + usb_current_xfer_info.nak_pending = false; + + uint8_t dev_addr = params->dev_addr; + uint8_t ep_addr = params->ep_addr; + uint16_t buflen = params->buflen; + uint8_t* buf = params->buf; + free(params); + + usb_edpt_t* edpt_info = get_edpt_record(dev_addr, ep_addr); + if (edpt_info) { + hcd_edpt_xfer(0, dev_addr, ep_addr, buf, buflen); + } + } +} + + void hcd_int_handler(uint8_t rhport, bool in_isr) { (void) rhport; (void) in_isr; @@ -407,7 +438,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (usb_current_xfer_info.bufferlen == 0) { LOG_CH32_USBFSH("USB_PID_%s completed %d bytes\r\n", request_pid == USB_PID_OUT ? "OUT" : "SETUP", usb_current_xfer_info.xferred_len); usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, usb_current_xfer_info.xferred_len, XFER_RESULT_SUCCESS, true); + hcd_event_xfer_complete(dev_addr, ep_addr, usb_current_xfer_info.xferred_len, XFER_RESULT_SUCCESS, in_isr); return; } else { LOG_CH32_USBFSH("USB_PID_OUT continue...\r\n"); @@ -432,7 +463,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { // USB device sent all data. LOG_CH32_USBFSH("USB_PID_IN completed\r\n"); usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, XFER_RESULT_SUCCESS, true); + hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, XFER_RESULT_SUCCESS, in_isr); return; } else { // USB device may send more data. @@ -444,7 +475,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { default: { LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid); usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); + hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr); return; } } @@ -458,25 +489,46 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } else if (response_pid == USB_PID_NAK) { LOG_CH32_USBFSH("NAK reposense\r\n"); uint32_t elapsed_time = board_millis() - usb_current_xfer_info.start_ms; + (void)elapsed_time; if (edpt_info->xfer_type == TUSB_XFER_INTERRUPT) { usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, true); - } else if (elapsed_time > USB_XFER_TIMEOUT_MILLIS) { - usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); + hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_SUCCESS, in_isr); + } else { - hardware_start_xfer(request_pid, ep_addr, edpt_info->data_toggle); + usb_current_xfer_info.is_busy = false; + usb_current_xfer_info.nak_pending = true; + + xfer_retry_param_t* param_buf = malloc(sizeof(xfer_retry_param_t)); + xfer_retry_param_t param = { + .rhport = 0, + .dev_addr = dev_addr, + .ep_addr = ep_addr, + .buflen = usb_current_xfer_info.bufferlen, + .buf = usb_current_xfer_info.buffer + }; + memcpy(param_buf, ¶m, sizeof(xfer_retry_param_t)); + + hcd_event_t event = { + .rhport = rhport, + .dev_addr = dev_addr, + .event_id = USBH_EVENT_FUNC_CALL, + .func_call = { + .func = xfer_retry, + .param = param_buf + } + }; + hcd_event_handler(&event, in_isr); } return; } else if (response_pid == USB_PID_DATA0 || response_pid == USB_PID_DATA1) { LOG_CH32_USBFSH("Data toggle mismatched and DATA0/1 (not STALL). RX_LEN=%d\r\n", USBOTG_H_FS->RX_LEN); usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); + hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr); return; } else { LOG_CH32_USBFSH("hcd_int_handler() L%d: unexpected response PID: 0x%02x\r\n", __LINE__, response_pid); usb_current_xfer_info.is_busy = false; - hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, true); + hcd_event_xfer_complete(dev_addr, ep_addr, 0, XFER_RESULT_FAILED, in_isr); return; } } @@ -495,6 +547,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const uint8_t xfer_type = ep_desc->bmAttributes.xfer; LOG_CH32_USBFSH("hcd_edpt_open(rhport=%d, dev_addr=0x%02x, %p) EndpointAdderss=0x%02x,maxPacketSize=%d,xfer_type=%d\r\n", rhport, dev_addr, ep_desc, ep_addr, max_packet_size, xfer_type); + while (usb_current_xfer_info.is_busy) { } + if (ep_num == 0x00) { TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x00, max_packet_size, xfer_type) != NULL, false); TU_ASSERT(get_or_add_edpt_record(dev_addr, 0x80, max_packet_size, xfer_type) != NULL, false); @@ -529,6 +583,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_current_xfer_info.bufferlen = buflen; usb_current_xfer_info.start_ms = board_millis(); usb_current_xfer_info.xferred_len = 0; + usb_current_xfer_info.nak_pending = false; if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { LOG_CH32_USBFSH("hcd_edpt_xfer(): READ, dev_addr=0x%02x, ep_addr=0x%02x, len=%d\r\n", dev_addr, ep_addr, buflen); @@ -581,6 +636,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet usb_current_xfer_info.buffer = USBFS_TX_Buf; usb_current_xfer_info.bufferlen = setup_packet_datalen; usb_current_xfer_info.xferred_len = 0; + usb_current_xfer_info.nak_pending = false; hardware_start_xfer(USB_PID_SETUP, 0, 0); -- cgit v1.3.1 From 6f2b5fc4952e0fc730466a8756ce6427c33ce2b7 Mon Sep 17 00:00:00 2001 From: Mitsumine Suzu <60875431+verylowfreq@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:52:52 +0900 Subject: Remove dynamic memory allocation --- src/portable/wch/hcd_ch32_usbfs.c | 46 ++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c index 449908658..ff34ae17a 100644 --- a/src/portable/wch/hcd_ch32_usbfs.c +++ b/src/portable/wch/hcd_ch32_usbfs.c @@ -84,6 +84,10 @@ typedef struct usb_edpt { // Data toggle (0 or not 0) for DATA0/1 uint8_t data_toggle; + + bool is_nak_pending; + uint16_t buflen; + uint8_t* buf; } usb_edpt_t; static usb_edpt_t usb_edpt_list[CFG_TUH_DEVICE_MAX * 6] = {}; @@ -130,6 +134,9 @@ static usb_edpt_t *add_edpt_record(uint8_t dev_addr, uint8_t ep_addr, uint16_t m slot->max_packet_size = max_packet_size; slot->xfer_type = xfer_type; slot->data_toggle = 0; + slot->is_nak_pending = false; + slot->buflen = 0; + slot->buf = NULL; slot->configured = true; @@ -354,28 +361,22 @@ void hcd_int_disable(uint8_t rhport) { interrupt_enabled = false; } -typedef struct { - uint8_t rhport; - uint8_t dev_addr; - uint8_t ep_addr; - uint16_t buflen; - uint8_t* buf; -} xfer_retry_param_t; static void xfer_retry(void* _params) { LOG_CH32_USBFSH("xfer_retry()\r\n"); - xfer_retry_param_t* params = (xfer_retry_param_t*)_params; + usb_edpt_t* edpt_info = (usb_edpt_t*)_params; if (usb_current_xfer_info.nak_pending) { usb_current_xfer_info.nak_pending = false; + edpt_info->is_nak_pending = false; - uint8_t dev_addr = params->dev_addr; - uint8_t ep_addr = params->ep_addr; - uint16_t buflen = params->buflen; - uint8_t* buf = params->buf; - free(params); + uint8_t dev_addr = edpt_info->dev_addr; + uint8_t ep_addr = edpt_info->ep_addr; + uint16_t buflen = edpt_info->buflen; + uint8_t* buf = edpt_info->buf; - usb_edpt_t* edpt_info = get_edpt_record(dev_addr, ep_addr); - if (edpt_info) { + // Check connectivity + usb_edpt_t* edpt_info_current = get_edpt_record(dev_addr, ep_addr); + if (edpt_info_current) { hcd_edpt_xfer(0, dev_addr, ep_addr, buf, buflen); } } @@ -498,15 +499,10 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { usb_current_xfer_info.is_busy = false; usb_current_xfer_info.nak_pending = true; - xfer_retry_param_t* param_buf = malloc(sizeof(xfer_retry_param_t)); - xfer_retry_param_t param = { - .rhport = 0, - .dev_addr = dev_addr, - .ep_addr = ep_addr, - .buflen = usb_current_xfer_info.bufferlen, - .buf = usb_current_xfer_info.buffer - }; - memcpy(param_buf, ¶m, sizeof(xfer_retry_param_t)); + + edpt_info->is_nak_pending = true; + edpt_info->buflen = usb_current_xfer_info.bufferlen; + edpt_info->buf = usb_current_xfer_info.buffer; hcd_event_t event = { .rhport = rhport, @@ -514,7 +510,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { .event_id = USBH_EVENT_FUNC_CALL, .func_call = { .func = xfer_retry, - .param = param_buf + .param = edpt_info } }; hcd_event_handler(&event, in_isr); -- cgit v1.3.1 From 12ee78df308aa80d6c2ef34bff1be0df38939027 Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Sun, 24 Aug 2025 17:19:51 +0200 Subject: Fix osal_spin_unlock for mynewt Mynewt version for osal_spin_unlock() called OS_ENTER_CRITICAL instead of OS_EXIT_CRITICAL. Signed-off-by: Jerzy Kasenberg --- src/osal/osal_mynewt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h index ee95e684f..6d51f8ec3 100644 --- a/src/osal/osal_mynewt.h +++ b/src/osal/osal_mynewt.h @@ -63,7 +63,7 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, if (!TUP_MCU_MULTIPLE_CORE && in_isr) { return; // single core MCU does not need to lock in ISR } - OS_ENTER_CRITICAL(*ctx); + OS_EXIT_CRITICAL(*ctx); } //--------------------------------------------------------------------+ -- cgit v1.3.1 From 38f41f5fa28943086cd36653e0914fd59cb7ed03 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Thu, 28 Aug 2025 15:35:55 +0200 Subject: fix(dcd/dwc2): Fix reset procedure for versions >=4.20a --- src/portable/synopsys/dwc2/dwc2_common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index b001f343d..9b8333ad2 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2024 Ha Thach (tinyusb.org) + * Copyright (c) 2024-2025 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 @@ -45,11 +45,14 @@ // //-------------------------------------------------------------------- static void reset_core(dwc2_regs_t* dwc2) { + // load gsnpsid (it is not readable after reset is asserted) + uint32_t gsnpsid = dwc2->gsnpsid; + // reset core dwc2->grstctl |= GRSTCTL_CSRST; - if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { - // prior v42.0 CSRST is self-clearing + if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { + // prior v4.20a CSRST is self-clearing while (dwc2->grstctl & GRSTCTL_CSRST) {} } else { // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking. -- cgit v1.3.1 From f5d04833bb5841930476cc666193f2f362aef228 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 3 Sep 2025 13:30:20 +0700 Subject: use tusb_time_millis_api() instead of board_millis() make loopdelay() always inline --- src/portable/wch/hcd_ch32_usbfs.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/portable/wch/hcd_ch32_usbfs.c b/src/portable/wch/hcd_ch32_usbfs.c index ff34ae17a..200136906 100644 --- a/src/portable/wch/hcd_ch32_usbfs.c +++ b/src/portable/wch/hcd_ch32_usbfs.c @@ -57,20 +57,18 @@ TU_ATTR_ALIGNED(4) static uint8_t USBFS_TX_Buf[USBFS_TX_BUF_LEN]; #define LOG_CH32_USBFSH(...) TU_LOG3(__VA_ARGS__) // Busywait for delay microseconds/nanoseconds -static void loopdelay(uint32_t count) -{ - volatile uint32_t c = count / 3; - if (c == 0) { return; } - // while (c-- != 0); - asm volatile( - "1: \n" // loop label - " addi %0, %0, -1 \n" // c-- - " bne %0, zero, 1b \n" // if (c != 0) goto loop - : "+r"(c) // c is input/output operand +TU_ATTR_ALWAYS_INLINE static inline void loopdelay(uint32_t count) { + volatile uint32_t c = count / 3; + if (c == 0) { return; } + // while (c-- != 0); + asm volatile( + "1: \n" // loop label + " addi %0, %0, -1 \n" // c-- + " bne %0, zero, 1b \n" // if (c != 0) goto loop + : "+r"(c) // c is input/output operand ); } - // Endpoint status typedef struct usb_edpt { // Is this a valid struct @@ -346,7 +344,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { uint32_t hcd_frame_number(uint8_t rhport) { (void) rhport; - return board_millis(); + return tusb_time_millis_api(); } void hcd_int_enable(uint8_t rhport) { @@ -489,7 +487,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { return; } else if (response_pid == USB_PID_NAK) { LOG_CH32_USBFSH("NAK reposense\r\n"); - uint32_t elapsed_time = board_millis() - usb_current_xfer_info.start_ms; + uint32_t elapsed_time = tusb_time_millis_api() - usb_current_xfer_info.start_ms; (void)elapsed_time; if (edpt_info->xfer_type == TUSB_XFER_INTERRUPT) { usb_current_xfer_info.is_busy = false; @@ -577,7 +575,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b usb_current_xfer_info.ep_addr = ep_addr; usb_current_xfer_info.buffer = buffer; usb_current_xfer_info.bufferlen = buflen; - usb_current_xfer_info.start_ms = board_millis(); + usb_current_xfer_info.start_ms = tusb_time_millis_api(); usb_current_xfer_info.xferred_len = 0; usb_current_xfer_info.nak_pending = false; @@ -628,7 +626,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet uint8_t ep_addr = (setup_packet[0] & 0x80) ? 0x80 : 0x00; usb_current_xfer_info.dev_addr = dev_addr; usb_current_xfer_info.ep_addr = ep_addr; - usb_current_xfer_info.start_ms = board_millis(); + usb_current_xfer_info.start_ms = tusb_time_millis_api(); usb_current_xfer_info.buffer = USBFS_TX_Buf; usb_current_xfer_info.bufferlen = setup_packet_datalen; usb_current_xfer_info.xferred_len = 0; -- cgit v1.3.1 From c5a390950f8616f42917bc6ecd8c8e528cdda49e Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 3 Sep 2025 17:20:54 +0700 Subject: add at32f415 dwc2 info --- src/portable/synopsys/dwc2/dwc2_info.md | 116 ++++++++++++++++---------------- src/portable/synopsys/dwc2/dwc2_info.py | 1 + 2 files changed, 59 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md index 00685e7ad..e6b7820bc 100644 --- a/src/portable/synopsys/dwc2/dwc2_info.md +++ b/src/portable/synopsys/dwc2/dwc2_info.md @@ -1,58 +1,58 @@ -| | AT32 F405 FS | AT32 F405 HS | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | -|:---------------------------|:---------------|:---------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| -| GUID | 0x00002000 | 0x00000000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | -| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | -| - specs version | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | -| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | -| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | -| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | -| - arch | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | -| - single_point | hub | hub | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | -| - hs_phy_type | n/a | UTMI+/ULPI | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | -| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | -| - num_dev_ep | 7 | 7 | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - num_host_ch | 15 | 15 | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | -| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - nptx_q_depth | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | -| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | -| - xfer_size_width | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | -| - packet_size_width | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | -| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | -| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | -| - optional_feature_removed | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - synch_reset | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | -| - dfifo_depth | 512 | 1008 | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | -| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | -| - num_dev_period_in_ep | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - partial_powerdown | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - ahb_freq_min | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | -| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - iddg_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - vbus_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - a_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - b_valid_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - session_end_filter | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - dedicated_fifos | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - num_dev_in_eps | 7 | 7 | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - dma_desc_enable | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | -| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | +|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| +| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | +| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | +| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | +| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | +| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | +| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | +| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | +| - single_point | hub | hub | hub | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | +| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | +| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | +| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | +| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | +| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | +| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | +| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | +| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | +| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | +| - optional_feature_removed | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | +| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | +| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | +| - num_dev_period_in_ep | 0 | 0 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - ahb_freq_min | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | +| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - vbus_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index bc5ad9a7b..a90150632 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -11,6 +11,7 @@ dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4'] dwc2_reg_value = { 'AT32 F405 FS': [0x00002000, 0x4F54400A, 0x00000000, 0x228FDD00, 0x020004E8, 0x1FF0A020], 'AT32 F405 HS': [0x00000000, 0x4F54400A, 0x00000000, 0x229FDDD0, 0x03F006E8, 0x1FF0A020], + 'AT32 F415': [0x00001000, 0x4F54400A, 0x00000000, 0x228DCD00, 0x020004E8, 0x0F], 'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020], 'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030], 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030], -- cgit v1.3.1 From da9284e88baefc6d0e5f7c624f8f7da7f4d6aed0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:42:17 +0000 Subject: Fix obsolete cnt assignment in _tu_fifo_peek() overflow check Co-authored-by: hathach <249515+hathach@users.noreply.github.com> --- src/common/tusb_fifo.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index ecf002b09..f7679556f 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -428,7 +428,6 @@ static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16 if ( cnt > f->depth ) { rd_idx = _ff_correct_read_index(f, wr_idx); - cnt = f->depth; } uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); -- cgit v1.3.1 From a96ee81a7ddc03f04a837d949c291f22362d6cb4 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 4 Sep 2025 10:36:15 +0700 Subject: remove duplicated enum --- src/class/hid/hid.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 0fc0e1377..bbc58af80 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -994,9 +994,7 @@ enum { HID_USAGE_CONSUMER_FAST_FORWARD = 0x00B3, HID_USAGE_CONSUMER_REWIND = 0x00B4, HID_USAGE_CONSUMER_SCAN_NEXT_TRACK = 0x00B5, - HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, HID_USAGE_CONSUMER_SCAN_PREVIOUS_TRACK = 0x00B6, - HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, HID_USAGE_CONSUMER_STOP = 0x00B7, HID_USAGE_CONSUMER_EJECT = 0x00B8, HID_USAGE_CONSUMER_RANDOM_PLAY = 0x00B9, @@ -1097,7 +1095,6 @@ enum { // Configuration // Sel 15.15 HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, - HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL = 0x0183, // Configuration // Sel 15.15 HID_USAGE_CONSUMER_AL_WORD_PROCESSOR = 0x0184, @@ -1117,7 +1114,6 @@ enum { HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, HID_USAGE_CONSUMER_AL_A_V_CAPTURE_PLAYBACK = 0x0193, HID_USAGE_CONSUMER_AL_LOCAL_MACHINE_BROWSER = 0x0194, - HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, HID_USAGE_CONSUMER_AL_LAN_WAN_BROWSER = 0x0195, HID_USAGE_CONSUMER_AL_INTERNET_BROWSER = 0x0196, HID_USAGE_CONSUMER_AL_REMOTE_NETWORKING_ISP = 0x0197, -- cgit v1.3.1 From 0263cfc01ab63d6799e80cbbf691af1979cf3ab7 Mon Sep 17 00:00:00 2001 From: rppicomidi Date: Sat, 6 Sep 2025 06:58:35 -0700 Subject: fix #3239: discard poorly formed packets --- src/class/midi/midi_host.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 50bda1e36..e6ace316c 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -575,6 +575,11 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff } } } + else { + // bad packet discard + nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4); + continue; + } } else if (status < MIDI_STATUS_SYSEX_START) { // then it is a channel message either three bytes or two uint8_t fake_cin = (status & 0xf0) >> 4; @@ -617,6 +622,11 @@ uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buff bytes_to_add_to_stream = 1; } } + else { + // bad packet discard + nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4); + continue; + } for (uint8_t i = 1; i <= bytes_to_add_to_stream; i++) { *p_buffer++ = p_midi->stream_read.buffer[i]; -- cgit v1.3.1 From d70d4043dcccb1628424c39c231ed1f95fb52f8e Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 10 Sep 2025 16:59:26 +0700 Subject: use tusb_time_delay_ms_api for delay, also move tusb_time api to common.h --- src/common/tusb_common.h | 8 +++++++- src/portable/synopsys/dwc2/dwc2_stm32.h | 3 +-- src/tusb.h | 10 ---------- 3 files changed, 8 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 0351a3d8f..b5bdb76e3 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -78,10 +78,16 @@ #include "tusb_debug.h" //--------------------------------------------------------------------+ -// Optional API implemented by application if needed +// API implemented by application if needed // TODO move to a more obvious place/file //--------------------------------------------------------------------+ +// Get current milliseconds, required by some port/configuration without RTOS +extern uint32_t tusb_time_millis_api(void); + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +extern void tusb_time_delay_ms_api(uint32_t ms); + // flush data cache TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index 0bbad55f5..14d95184e 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -224,8 +224,7 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; // Wait ~2ms until the PLL is ready (there's no RDY bit to query) - uint32_t count = (SystemCoreClock / 1000) * 2; - while (count--) __NOP(); + tusb_time_delay_ms_api(2); #else #endif diff --git a/src/tusb.h b/src/tusb.h index dfba21ddf..88815def1 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -169,16 +169,6 @@ void tusb_int_handler(uint8_t rhport, bool in_isr); #endif -//--------------------------------------------------------------------+ -// API Implemented by user -//--------------------------------------------------------------------+ - -// Get current milliseconds, required by some port/configuration without RTOS -uint32_t tusb_time_millis_api(void); - -// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS -void tusb_time_delay_ms_api(uint32_t ms); - #ifdef __cplusplus } #endif -- cgit v1.3.1 From 19f67ffc22bcf9a59125b0f992bf6e8ee9819eec Mon Sep 17 00:00:00 2001 From: Dalton Caron Date: Tue, 9 Sep 2025 14:09:26 -0700 Subject: Initial STM32WBARI eval support Clean up includes definitions Remove wait that is not required Remove redundant settings Clean up clock configuration to look like other modules Remove MSP_Init that is not required Clean up driver code dhcp: Fix DHCP_OFFER/DHCP_ACK destinaton. In RFC 2131, the destination of DHCP OFFER/ACK is defined in Section 4.1. Fix the destination error by following the rule of RFC 2131. TODO: We implement all rule but the last one. ARP table is required to associate client's macaddr. Currently, fallback to broadcast. Signed-off-by: Elwin Huang Fix compile error. Fix goto indentation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Fix osal_spin_unlock for mynewt Mynewt version for osal_spin_unlock() called OS_ENTER_CRITICAL instead of OS_EXIT_CRITICAL. Signed-off-by: Jerzy Kasenberg Add ESP32-C5 and ESP32-C61 definitions fix(dcd/dwc2): Fix reset procedure for versions >=4.20a Fix STM32L4 GPIOD clock enable for variants without GPIOD Add weact blackpill support Add to boards.rst file Fix file Small cleanups. Insert small delay for LowSpeed device Improve retry operation at NAK response. Remove dynamic memory allocation use tusb_time_millis_api() instead of board_millis() make loopdelay() always inline update at32f405 dwc2 info and phy width selection add at32f415 dwc2 info add some consumer page configs remove duplicated enum Initial plan Fix obsolete cnt assignment in _tu_fifo_peek() overflow check Co-authored-by: hathach <249515+hathach@users.noreply.github.com> Initial plan Update STM32 CMSIS dependencies to fix HSITRIM register bug Co-authored-by: hathach <249515+hathach@users.noreply.github.com> Remove accidentally committed dependency directories Co-authored-by: hathach <249515+hathach@users.noreply.github.com> Update all STM32 HAL driver dependencies to latest versions Co-authored-by: hathach <249515+hathach@users.noreply.github.com> fix linker h745 issue with clang fix linker h745 issue with clang Update all STM32 CMSIS device dependencies to latest versions Co-authored-by: hathach <249515+hathach@users.noreply.github.com> update pio-usb Create comprehensive GitHub Copilot instructions and fix pre-commit configuration for TinyUSB (#3234) * Initial plan * Create comprehensive GitHub Copilot instructions for TinyUSB Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Remove accidentally committed dependencies, use tools/get_deps.py instead Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Fix .gitignore: ignore vendor/ directory not ceedling script Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Revert .gitignore changes and add README_processed.rst Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Remove README_processed.rst and revert ceedling file permissions Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Changes before error encountered Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * Remove redundant manual validation step, keep only pre-commit and build validation Co-authored-by: hathach <249515+hathach@users.noreply.github.com> * fix pre-commit --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hathach <249515+hathach@users.noreply.github.com> modified the bsp files of at32 to make them work better the family name error of at32 has been corrected fix pre-commit STM32N6570-DK board is added. Build with DEBUG=1 (make BOARD=stm32n6570dk DEBUG=1 all), otherwise it does not work for now. Tested with examples/device/cdc_dual_ports --- docs/reference/boards.rst | 1 + docs/reference/dependencies.rst | 1 + hw/bsp/stm32wba/FreeRTOSConfig/FreeRTOSConfig.h | 153 +++++++++ hw/bsp/stm32wba/boards/stm32wba_eval/board.cmake | 10 + hw/bsp/stm32wba/boards/stm32wba_eval/board.h | 136 ++++++++ hw/bsp/stm32wba/boards/stm32wba_eval/board.mk | 6 + .../stm32wba_eval/include/stm32wbaxx_hal_conf.h | 368 +++++++++++++++++++++ hw/bsp/stm32wba/family.c | 233 +++++++++++++ hw/bsp/stm32wba/family.mk | 59 ++++ hw/bsp/stm32wba/stm32wbaxx_hal_conf.h | 367 ++++++++++++++++++++ src/common/tusb_mcu.h | 6 + src/portable/st/stm32_fsdev/fsdev_stm32.h | 8 + src/portable/synopsys/dwc2/dwc2_common.c | 5 + src/portable/synopsys/dwc2/dwc2_stm32.h | 23 +- src/tusb_option.h | 1 + tools/get_deps.py | 6 + 16 files changed, 1381 insertions(+), 2 deletions(-) create mode 100644 hw/bsp/stm32wba/FreeRTOSConfig/FreeRTOSConfig.h create mode 100644 hw/bsp/stm32wba/boards/stm32wba_eval/board.cmake create mode 100644 hw/bsp/stm32wba/boards/stm32wba_eval/board.h create mode 100644 hw/bsp/stm32wba/boards/stm32wba_eval/board.mk create mode 100644 hw/bsp/stm32wba/boards/stm32wba_eval/include/stm32wbaxx_hal_conf.h create mode 100644 hw/bsp/stm32wba/family.c create mode 100644 hw/bsp/stm32wba/family.mk create mode 100644 hw/bsp/stm32wba/stm32wbaxx_hal_conf.h (limited to 'src') diff --git a/docs/reference/boards.rst b/docs/reference/boards.rst index 71dca8e94..a930f485b 100644 --- a/docs/reference/boards.rst +++ b/docs/reference/boards.rst @@ -303,6 +303,7 @@ stm32u575eval STM32 U575 Eval stm32u5 https://www.s stm32u575nucleo STM32 U575 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u575zi-q.html stm32u5a5nucleo STM32 U5a5 Nucleo stm32u5 https://www.st.com/en/evaluation-tools/nucleo-u5a5zj-q.html stm32wb55nucleo STM32 P-NUCLEO-WB55 stm32wb https://www.st.com/en/evaluation-tools/p-nucleo-wb55.html +stm32wba65nucleo STM32 NUCLEO-WBA65RI stm32wba https://www.st.com/en/evaluation-tools/nucleo-wba65ri.html =================== ================================= ========= ================================================================= ====== Sunxi diff --git a/docs/reference/dependencies.rst b/docs/reference/dependencies.rst index d75139aae..8a65eee86 100644 --- a/docs/reference/dependencies.rst +++ b/docs/reference/dependencies.rst @@ -71,6 +71,7 @@ hw/mcu/st/stm32l5xx_hal_driver https://github.com/STMicroelectronics/ hw/mcu/st/stm32n6xx_hal_driver https://github.com/STMicroelectronics/stm32n6xx-hal-driver.git 49f9989d10cf6817d4b07ac01848956b46bd0fd6 stm32n6 hw/mcu/st/stm32u5xx_hal_driver https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git 4d93097a67928e9377e655ddd14622adc31b9770 stm32u5 hw/mcu/st/stm32wbxx_hal_driver https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git 2c5f06638be516c1b772f768456ba637f077bac8 stm32wb +hw/mcu/st/stm32wbaxx_hal_driver https://github.com/STMicroelectronics/stm32wbaxx-hal-driver.git 9442fbb71f855ff2e64fbf662b7726beba511a24 stm32wba hw/mcu/ti https://github.com/hathach/ti_driver.git 143ed6cc20a7615d042b03b21e070197d473e6e5 msp430 msp432e4 tm4c hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.git 77c4095087e5ed2c548ec9058e655d0b8757663b ch32f20x hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x diff --git a/hw/bsp/stm32wba/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32wba/FreeRTOSConfig/FreeRTOSConfig.h new file mode 100644 index 000000000..471d56aec --- /dev/null +++ b/hw/bsp/stm32wba/FreeRTOSConfig/FreeRTOSConfig.h @@ -0,0 +1,153 @@ +/* + * FreeRTOS Kernel V10.0.0 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. If you wish to use our Amazon + * FreeRTOS name, please do so in a fair use way that does not cause confusion. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +// skip if included from IAR assembler +#ifndef __IASMARM__ + #include "stm32wbaxx.h" +#endif + +/* Cortex M23/M33 port configuration. */ +#define configENABLE_MPU 0 +#if defined(__ARM_FP) && __ARM_FP >= 4 + #define configENABLE_FPU 1 +#else + #define configENABLE_FPU 0 +#endif +#define configENABLE_TRUSTZONE 0 +#define configMINIMAL_SECURE_STACK_SIZE (1024) + +#define configUSE_PREEMPTION 1 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configCPU_CLOCK_HZ SystemCoreClock +#define configTICK_RATE_HZ ( 1000 ) +#define configMAX_PRIORITIES ( 5 ) +#define configMINIMAL_STACK_SIZE ( 128 ) +#define configTOTAL_HEAP_SIZE ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 ) +#define configMAX_TASK_NAME_LEN 16 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 4 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TIME_SLICING 0 +#define configUSE_NEWLIB_REENTRANT 0 +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 + +#define configSUPPORT_STATIC_ALLOCATION 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 0 + +/* Hook function related definitions. */ +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_MALLOC_FAILED_HOOK 0 // cause nested extern warning +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configCHECK_HANDLER_INSTALLATION 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_TRACE_FACILITY 1 // legacy trace +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 2 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-2) +#define configTIMER_QUEUE_LENGTH 32 +#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE + +/* Optional functions - most linkers will remove unused functions anyway. */ +#define INCLUDE_vTaskPrioritySet 0 +#define INCLUDE_uxTaskPriorityGet 0 +#define INCLUDE_vTaskDelete 0 +#define INCLUDE_vTaskSuspend 1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY +#define INCLUDE_xResumeFromISR 0 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 0 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 0 +#define INCLUDE_xTaskGetIdleTaskHandle 0 +#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 +#define INCLUDE_pcTaskGetTaskName 0 +#define INCLUDE_eTaskGetState 0 +#define INCLUDE_xEventGroupSetBitFromISR 0 +#define INCLUDE_xTimerPendFunctionCall 0 + +/* FreeRTOS hooks to NVIC vectors */ +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler +#define vPortSVCHandler SVC_Handler + +//--------------------------------------------------------------------+ +// Interrupt nesting behavior configuration. +//--------------------------------------------------------------------+ + +// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header +#define configPRIO_BITS 4 + +/* The lowest interrupt priority that can be used in a call to a "set priority" function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1< + +#include "stm32wbaxx_hal.h" +#include "bsp/board_api.h" +#include "board.h" + + //--------------------------------------------------------------------+ + // MACRO TYPEDEF CONSTANT ENUM + //--------------------------------------------------------------------+ +#ifndef CFG_BOARD_UART_BAUDRATE +#define CFG_BOARD_UART_BAUDRATE 115200 +#endif + +#ifndef USART_TIMEOUT_TICKS +#define USART_TIMEOUT_TICKS 1000 +#endif + +static UART_HandleTypeDef uart_handle; + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ +void USB_OTG_HS_IRQHandler( void ) +{ + tud_int_handler( 0 ); +} + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +static void board_gpio_configuration( void ) +{ + GPIO_InitTypeDef gpio_init = { 0 }; + + USART_GPIO_CLK_EN(); + USART_CLK_EN(); + + // Configure USART TX pin + gpio_init.Pin = USART_TX_PIN; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; + gpio_init.Alternate = USART_GPIO_AF; + HAL_GPIO_Init( USART_TX_GPIO_PORT, &gpio_init ); + + // Configure USART RX pin + gpio_init.Pin = USART_RX_PIN; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Pull = GPIO_PULLUP; + gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; + gpio_init.Alternate = USART_GPIO_AF; + HAL_GPIO_Init( USART_RX_GPIO_PORT, &gpio_init ); + + // Configure the LED + LED_CLK_EN(); + gpio_init.Pin = LED_PIN; + gpio_init.Mode = GPIO_MODE_OUTPUT_PP; + gpio_init.Pull = GPIO_PULLUP; + gpio_init.Speed = GPIO_SPEED_FREQ_LOW; + gpio_init.Alternate = 0; + HAL_GPIO_Init( LED_PORT, &gpio_init ); + + // Default LED state is off + board_led_write( false ); + + // Configure the button + BUTTON_CLK_EN(); + gpio_init.Pin = BUTTON_PIN; + gpio_init.Mode = GPIO_MODE_INPUT; + gpio_init.Pull = GPIO_PULLUP; + gpio_init.Speed = GPIO_SPEED_FREQ_LOW; + gpio_init.Alternate = 0; + HAL_GPIO_Init( BUTTON_PORT, &gpio_init ); + + // Configure USB DM and DP pins. This is optional, and maintained only for user guidance. + gpio_init.Pin = (GPIO_PIN_7 | GPIO_PIN_6); + gpio_init.Mode = GPIO_MODE_INPUT; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; + HAL_GPIO_Init( GPIOD, &gpio_init ); +} + +static void board_uart_configuration( void ) +{ + uart_handle = ( UART_HandleTypeDef ) { + .Instance = USART_DEV, + .Init.BaudRate = CFG_BOARD_UART_BAUDRATE, + .Init.WordLength = UART_WORDLENGTH_8B, + .Init.StopBits = UART_STOPBITS_1, + .Init.Parity = UART_PARITY_NONE, + .Init.HwFlowCtl = UART_HWCONTROL_NONE, + .Init.Mode = UART_MODE_TX_RX, + .Init.OverSampling = UART_OVERSAMPLING_16 + }; + HAL_UART_Init( &uart_handle ); +} + +void board_init( void ) +{ + board_system_clock_config(); + board_gpio_configuration(); + board_uart_configuration(); + +#ifdef USB_OTG_HS + // STM32WBA65/64/62 only has 1 USB HS port + + #if CFG_TUSB_OS == OPT_OS_NONE + // 1ms tick timer + SysTick_Config( SystemCoreClock / 1000 ); + #elif CFG_TUSB_OS == OPT_OS_FREERTOS + // Explicitly disable systick to prevent its ISR runs before scheduler start + SysTick->CTRL &= ~1U; + + // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) + NVIC_SetPriority( OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); + #endif + + // USB clock enable + __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); + __HAL_RCC_USB_OTG_HS_PHY_CLK_ENABLE(); + + // See the reference manual section 11.4.7 for the USB OTG powering sequence + + // Remove the VDDUSB power isolation + PWR->SVMCR |= PWR_SVMCR_USV; + + // Enable VDD11USB supply by clearing VDD11USBDIS to 0 + PWR->VOSR &= ~PWR_VOSR_VDD11USBDIS; + + // Enable USB OTG internal power by setting USBPWREN to 1 + PWR->VOSR |= PWR_VOSR_USBPWREN; + + // Wait for VDD11USB supply to be ready in VDD11USBRDY = 1 + while ((PWR->VOSR & PWR_VOSR_VDD11USBRDY) == 0) {} + + // Enable USB OTG booster by setting USBBOOSTEN to 1 + PWR->VOSR |= PWR_VOSR_USBBOOSTEN; + + // Wait for USB OTG booster to be ready in USBBOOSTRDY = 1 + while ((PWR->VOSR & PWR_VOSR_USBBOOSTRDY) == 0) {} + + // Enable USB power on Pwrctrl CR2 register + PWR->SVMCR |= PWR_SVMCR_USV; + + // Set the reference clock selection (must match the clock source) + SYSCFG->OTGHSPHYCR &= ~SYSCFG_OTGHSPHYCR_CLKSEL; + SYSCFG->OTGHSPHYCR |= SYSCFG_OTGHSPHYCR_CLKSEL_0 | SYSCFG_OTGHSPHYCR_CLKSEL_1 | + SYSCFG_OTGHSPHYCR_CLKSEL_3; // 32MHz clock + + // Configuring the SYSCFG registers OTG_HS PHY + SYSCFG->OTGHSPHYCR |= SYSCFG_OTGHSPHYCR_EN; + + // Disable VBUS sense (B device) + USB_OTG_HS->GCCFG &= ~USB_OTG_GCCFG_VBDEN; + + // B-peripheral session valid override enable + USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALEXTOEN; + USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALOVAL; +#endif // USB_OTG_FS +} + +void board_led_write( bool state ) +{ + HAL_GPIO_WritePin( LED_PORT, LED_PIN, state ? LED_STATE_ON : ( 1 - LED_STATE_ON ) ); +} + +uint32_t board_button_read( void ) +{ + return HAL_GPIO_ReadPin( BUTTON_PORT, BUTTON_PIN ) == BUTTON_STATE_ACTIVE; +} + +int board_uart_read( uint8_t *buf, int len ) +{ + ( void ) buf; + ( void ) len; + return 0; +} + +int board_uart_write( void const *buf, int len ) +{ + ( void ) HAL_UART_Transmit( &uart_handle, ( const uint8_t * ) buf, len, USART_TIMEOUT_TICKS ); + return len; +} + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; +void SysTick_Handler( void ) +{ + HAL_IncTick(); + system_ticks++; +} + +uint32_t board_millis( void ) +{ + return system_ticks; +} +#endif + +void HardFault_Handler( void ) +{ + asm( "bkpt 1" ); +} + +// Required by __libc_init_array in startup code if we are compiling using -nostdlib/-nostartfiles. +void _init( void ); +void _init( void ) +{ } diff --git a/hw/bsp/stm32wba/family.mk b/hw/bsp/stm32wba/family.mk new file mode 100644 index 000000000..ca6459ab2 --- /dev/null +++ b/hw/bsp/stm32wba/family.mk @@ -0,0 +1,59 @@ +UF2_FAMILY_ID = 0x70d16657 +ST_FAMILY = wba + +ST_PREFIX = stm32${ST_FAMILY}xx +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 +CPU_CORE ?= cortex-m33 + +CFLAGS += \ + -DCFG_TUSB_MCU=OPT_MCU_STM32WBA + +CFLAGS_GCC += \ + -flto \ + -nostdlib -nostartfiles \ + -Wno-error=cast-align -Wno-unused-parameter + +LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs -Wl,--gc-sections + +SRC_C += \ + src/portable/synopsys/dwc2/dcd_dwc2.c \ + src/portable/synopsys/dwc2/hcd_dwc2.c \ + src/portable/synopsys/dwc2/dwc2_common.c \ + $(ST_CMSIS)/Source/Templates/system_${ST_PREFIX}.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_cortex.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_icache.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_pwr.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_pwr_ex.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_rcc.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_rcc_ex.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_uart.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_gpio.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_pcd.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal_pcd_ex.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_ll_usb.c \ + $(ST_HAL_DRIVER)/Src/${ST_PREFIX}_hal.c + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(BOARD_PATH)/include \ + $(TOP)/lib/CMSIS_5/CMSIS/Core/Include \ + $(TOP)/$(ST_CMSIS)/Include \ + $(TOP)/$(ST_HAL_DRIVER)/Inc + +# STM32WBA HAL uses uppercase MCU_VARIANT (excluding the x's) for linking and lowercase MCU_VARIANT for startup. +UPPERCASE_MCU_VARIANT = $(shell echo $(MCU_VARIANT) | sed 's/\([^x]\)/\U\1/g') + +# Startup - Manually specify lowercase version for startup file +SRC_S_GCC += $(ST_CMSIS)/Source/Templates/gcc/startup_$(MCU_VARIANT).s +SRC_S_IAR += $(ST_CMSIS)/Source/Templates/iar/startup_$(MCU_VARIANT).s + +# Linker +LD_FILE_GCC ?= ${ST_CMSIS}/Source/Templates/gcc/linker/${UPPERCASE_MCU_VARIANT}_FLASH_ns.ld +LD_FILE_IAR ?= $(ST_CMSIS)/Source/Templates/iar/linker/$(MCU_VARIANT)_flash_cm33.icf + +# flash target using on-board stlink +flash: flash-stlink \ No newline at end of file diff --git a/hw/bsp/stm32wba/stm32wbaxx_hal_conf.h b/hw/bsp/stm32wba/stm32wbaxx_hal_conf.h new file mode 100644 index 000000000..f4e49f341 --- /dev/null +++ b/hw/bsp/stm32wba/stm32wbaxx_hal_conf.h @@ -0,0 +1,367 @@ +/** + ****************************************************************************** + * @file stm32wbaxx_hal_conf_template.h + * @author MCD Application Team + * @brief HAL configuration template file. + * This file should be copied to the application folder and renamed + * to stm32wbaxx_hal_conf.h. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32WBAxx_HAL_CONF_H +#define STM32WBAxx_HAL_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* ########################## Module Selection ############################## */ +/** + * @brief This is the list of modules to be used in the HAL driver + */ +#define HAL_MODULE_ENABLED +// #define HAL_ADC_MODULE_ENABLED +// #define HAL_COMP_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +// #define HAL_CRC_MODULE_ENABLED +// #define HAL_CRYP_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_EXTI_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_GTZC_MODULE_ENABLED +#define HAL_HASH_MODULE_ENABLED +#define HAL_HCD_MODULE_ENABLED +#define HAL_HSEM_MODULE_ENABLED +// #define HAL_I2C_MODULE_ENABLED +#define HAL_ICACHE_MODULE_ENABLED +// #define HAL_IRDA_MODULE_ENABLED +// #define HAL_IWDG_MODULE_ENABLED +// #define HAL_LPTIM_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +// #define HAL_PKA_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RAMCFG_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +// #define HAL_RNG_MODULE_ENABLED +// #define HAL_RTC_MODULE_ENABLED +// #define HAL_SAI_MODULE_ENABLED +// #define HAL_SMARTCARD_MODULE_ENABLED +// #define HAL_SMBUS_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +// #define HAL_TSC_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED +// #define HAL_WWDG_MODULE_ENABLED +// #define HAL_XSPI_MODULE_ENABLED + +/* ########################## Oscillator Values adaptation ####################*/ +/** + * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSE is used as system clock source, directly or through the PLL). + */ +#if !defined (HSE_VALUE) +#define HSE_VALUE 32000000UL /*!< Value of the External oscillator in Hz */ +#endif /* HSE_VALUE */ + +#if !defined (HSE_STARTUP_TIMEOUT) +#define HSE_STARTUP_TIMEOUT 100UL /*!< Time out for HSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSI is used as system clock source, directly or through the PLL). + */ +#if !defined (HSI_VALUE) +#define HSI_VALUE 16000000UL /*!< Value of the Internal oscillator in Hz*/ +#endif /* HSI_VALUE */ + +/** + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined (LSI_VALUE) +#define LSI_VALUE 32000UL /*!< LSI Typical Value in Hz*/ +#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz + The real value may vary depending on the variations in voltage + and temperature.*/ + +#if defined (RCC_LSI2_SUPPORT) +#if !defined (LSI2_VALUE) +#define LSI2_VALUE 32000UL /*!< LSI2 Typical Value in Hz*/ +#endif /* LSI2_VALUE */ +#endif + +/** + * @brief External Low Speed oscillator (LSE) value. + * This value is used by the UART, RTC HAL module to compute the system frequency + */ +#if !defined (LSE_VALUE) +#define LSE_VALUE 32768UL /*!< Value of the External oscillator in Hz*/ +#endif /* LSE_VALUE */ + +#if !defined (LSE_STARTUP_TIMEOUT) +#define LSE_STARTUP_TIMEOUT 5000UL /*!< Time out for LSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief External clock source for SAI1 peripheral + * This value is used by the RCC HAL module to compute the SAI1 & SAI2 clock source + * frequency. + */ +#if !defined (EXTERNAL_SAI1_CLOCK_VALUE) +#define EXTERNAL_SAI1_CLOCK_VALUE 48000UL /*!< Value of the SAI1 External clock source in Hz*/ +#endif /* EXTERNAL_SAI1_CLOCK_VALUE */ + +/* Tip: To avoid modifying this file each time you need to use different HSE, + === you can define the HSE value in your toolchain compiler preprocessor. */ + +/* ########################### System Configuration ######################### */ +/** + * @brief This is the HAL system configuration section + */ +#define VDD_VALUE 3300UL /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY ((1UL<<__NVIC_PRIO_BITS) - 1UL) /*!< tick interrupt priority (lowest by default) */ +#define USE_RTOS 0U +#define PREFETCH_ENABLE 1U /*!< Enable prefetch */ + +/* ########################## Assert Selection ############################## */ +/** + * @brief Uncomment the line below to expanse the "assert_param" macro in the + * HAL drivers code + */ +/* #define USE_FULL_ASSERT 1U */ + +/* ################## Register callback feature configuration ############### */ +/** + * @brief Set below the peripheral configuration to "1U" to add the support + * of HAL callback registration/unregistration feature for the HAL + * driver(s). This allows user application to provide specific callback + * functions thanks to HAL_PPP_RegisterCallback() rather than overwriting + * the default weak callback functions (see each stm32wbaxx_hal_ppp.h file + * for possible callback identifiers defined in HAL_PPP_CallbackIDTypeDef + * for each PPP peripheral). + */ +#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */ +#define USE_HAL_COMP_REGISTER_CALLBACKS 0U /* COMP register callback disabled */ +#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U /* CRYP register callback disabled */ +#define USE_HAL_HASH_REGISTER_CALLBACKS 0U /* HASH register callback disabled */ +#define USE_HAL_HCD_REGISTER_CALLBACKS 0U /* HCD register callback disabled */ +#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */ +#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */ +#define USE_HAL_IWDG_REGISTER_CALLBACKS 0U /* IWDG register callback disabled */ +#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U /* LPTIM register callback disabled */ +#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */ +#define USE_HAL_PKA_REGISTER_CALLBACKS 0U /* PKA register callback disabled */ +#define USE_HAL_RAMCFG_REGISTER_CALLBACKS 0U /* RAMCFG register callback disabled */ +#define USE_HAL_RNG_REGISTER_CALLBACKS 0U /* RNG register callback disabled */ +#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */ +#define USE_HAL_SAI_REGISTER_CALLBACKS 0U /* SAI register callback disabled */ +#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */ +#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */ +#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */ +#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */ +#define USE_HAL_TSC_REGISTER_CALLBACKS 0U /* TSC register callback disabled */ +#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */ +#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */ +#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */ +#define USE_HAL_XSPI_REGISTER_CALLBACKS 0U /* XSPI register callback disabled */ + +/* ################## SPI peripheral configuration ########################## */ + +/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver + * Activated: CRC code is present inside driver + * Deactivated: CRC code cleaned from driver + */ +#define USE_SPI_CRC 1U + +/* ################## CRYP peripheral configuration ########################## */ + +#define USE_HAL_CRYP_SUSPEND_RESUME 0U + +/* ################## HASH peripheral configuration ########################## */ + +#define USE_HAL_HASH_SUSPEND_RESUME 0U + +/* Includes ------------------------------------------------------------------*/ +/** + * @brief Include module's header file + */ +#ifdef HAL_DMA_MODULE_ENABLED +#include "stm32wbaxx_hal_dma.h" +#endif /* HAL_DMA_MODULE_ENABLED */ + +#ifdef HAL_RCC_MODULE_ENABLED +#include "stm32wbaxx_hal_rcc.h" +#endif /* HAL_RCC_MODULE_ENABLED */ + +#ifdef HAL_ADC_MODULE_ENABLED +#include "stm32wbaxx_hal_adc.h" +#endif /* HAL_ADC_MODULE_ENABLED */ + +#ifdef HAL_COMP_MODULE_ENABLED +#include "stm32wbaxx_hal_comp.h" +#endif /* HAL_COMP_MODULE_ENABLED */ + +#ifdef HAL_CORTEX_MODULE_ENABLED +#include "stm32wbaxx_hal_cortex.h" +#endif /* HAL_CORTEX_MODULE_ENABLED */ + +#ifdef HAL_CRC_MODULE_ENABLED +#include "stm32wbaxx_hal_crc.h" +#endif /* HAL_CRC_MODULE_ENABLED */ + +#ifdef HAL_CRYP_MODULE_ENABLED +#include "stm32wbaxx_hal_cryp.h" +#endif /* HAL_CRYP_MODULE_ENABLED */ + +#ifdef HAL_EXTI_MODULE_ENABLED +#include "stm32wbaxx_hal_exti.h" +#endif /* HAL_EXTI_MODULE_ENABLED */ + +#ifdef HAL_FLASH_MODULE_ENABLED +#include "stm32wbaxx_hal_flash.h" +#endif /* HAL_FLASH_MODULE_ENABLED */ + +#ifdef HAL_GPIO_MODULE_ENABLED +#include "stm32wbaxx_hal_gpio.h" +#endif /* HAL_GPIO_MODULE_ENABLED */ + +#ifdef HAL_GTZC_MODULE_ENABLED +#include "stm32wbaxx_hal_gtzc.h" +#endif /* HAL_GTZC_MODULE_ENABLED */ + +#ifdef HAL_HASH_MODULE_ENABLED +#include "stm32wbaxx_hal_hash.h" +#endif /* HAL_HASH_MODULE_ENABLED */ + +#ifdef HAL_HCD_MODULE_ENABLED +#include "stm32wbaxx_hal_hcd.h" +#endif /* HAL_HCD_MODULE_ENABLED */ + +#ifdef HAL_HSEM_MODULE_ENABLED +#include "stm32wbaxx_hal_hsem.h" +#endif /* HAL_HSEM_MODULE_ENABLED */ + +#ifdef HAL_I2C_MODULE_ENABLED +#include "stm32wbaxx_hal_i2c.h" +#endif /* HAL_I2C_MODULE_ENABLED */ + +#ifdef HAL_ICACHE_MODULE_ENABLED +#include "stm32wbaxx_hal_icache.h" +#endif /* HAL_ICACHE_MODULE_ENABLED */ + +#ifdef HAL_IRDA_MODULE_ENABLED +#include "stm32wbaxx_hal_irda.h" +#endif /* HAL_IRDA_MODULE_ENABLED */ + +#ifdef HAL_IWDG_MODULE_ENABLED +#include "stm32wbaxx_hal_iwdg.h" +#endif /* HAL_IWDG_MODULE_ENABLED */ + +#ifdef HAL_LPTIM_MODULE_ENABLED +#include "stm32wbaxx_hal_lptim.h" +#endif /* HAL_LPTIM_MODULE_ENABLED */ + +#ifdef HAL_PCD_MODULE_ENABLED +#include "stm32wbaxx_hal_pcd.h" +#endif /* HAL_PCD_MODULE_ENABLED */ + +#ifdef HAL_PKA_MODULE_ENABLED +#include "stm32wbaxx_hal_pka.h" +#endif /* HAL_PKA_MODULE_ENABLED */ + +#ifdef HAL_PWR_MODULE_ENABLED +#include "stm32wbaxx_hal_pwr.h" +#endif /* HAL_PWR_MODULE_ENABLED */ + +#ifdef HAL_RAMCFG_MODULE_ENABLED +#include "stm32wbaxx_hal_ramcfg.h" +#endif /* HAL_RAMCFG_MODULE_ENABLED */ + +#ifdef HAL_RNG_MODULE_ENABLED +#include "stm32wbaxx_hal_rng.h" +#endif /* HAL_RNG_MODULE_ENABLED */ + +#ifdef HAL_RTC_MODULE_ENABLED +#include "stm32wbaxx_hal_rtc.h" +#endif /* HAL_RTC_MODULE_ENABLED */ + +#ifdef HAL_SAI_MODULE_ENABLED +#include "stm32wbaxx_hal_sai.h" +#endif /* HAL_SAI_MODULE_ENABLED */ + +#ifdef HAL_SMARTCARD_MODULE_ENABLED +#include "stm32wbaxx_hal_smartcard.h" +#endif /* HAL_SMARTCARD_MODULE_ENABLED */ + +#ifdef HAL_SMBUS_MODULE_ENABLED +#include "stm32wbaxx_hal_smbus.h" +#endif /* HAL_SMBUS_MODULE_ENABLED */ + +#ifdef HAL_SPI_MODULE_ENABLED +#include "stm32wbaxx_hal_spi.h" +#endif /* HAL_SPI_MODULE_ENABLED */ + +#ifdef HAL_TIM_MODULE_ENABLED +#include "stm32wbaxx_hal_tim.h" +#endif /* HAL_TIM_MODULE_ENABLED */ + +#ifdef HAL_TSC_MODULE_ENABLED +#include "stm32wbaxx_hal_tsc.h" +#endif /* HAL_TSC_MODULE_ENABLED */ + +#ifdef HAL_UART_MODULE_ENABLED +#include "stm32wbaxx_hal_uart.h" +#endif /* HAL_UART_MODULE_ENABLED */ + +#ifdef HAL_USART_MODULE_ENABLED +#include "stm32wbaxx_hal_usart.h" +#endif /* HAL_USART_MODULE_ENABLED */ + +#ifdef HAL_WWDG_MODULE_ENABLED +#include "stm32wbaxx_hal_wwdg.h" +#endif /* HAL_WWDG_MODULE_ENABLED */ + +#ifdef HAL_XSPI_MODULE_ENABLED +#include "stm32wbaxx_hal_xspi.h" +#endif /* HAL_XSPI_MODULE_ENABLED */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ +#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ +void assert_failed(uint8_t *file, uint32_t line); +#else +#define assert_param(expr) ((void)0U) +#endif /* USE_FULL_ASSERT */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32WBAxx_HAL_CONF_H */ diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 3ffc2d1a4..7722ca1e6 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -295,6 +295,12 @@ #define TUP_USBIP_FSDEV_STM32 #define TUP_DCD_ENDPOINT_MAX 8 +#elif TU_CHECK_MCU(OPT_MCU_STM32WBA) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_STM32 + #define TUP_DCD_ENDPOINT_MAX 9 + #define TUP_RHPORT_HIGHSPEED 1 + #elif TU_CHECK_MCU(OPT_MCU_STM32U5) #if defined (STM32U535xx) || defined (STM32U545xx) #define TUP_USBIP_FSDEV diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index ccf31e035..01baf50ae 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -148,6 +148,12 @@ /* ST provided header has incorrect value of USB_PMAADDR */ #define FSDEV_PMA_BASE USB1_PMAADDR +#elif defined(STM32WBA62xx) || defined(STM32WBA64xx) || defined(STM32WBA65xx) + // Available only on STM32WBA62/64/65xx devices + #include "stm32wbaxx.h" + #define FSDEV_PMA_SIZE (2048u) + #define USB USB_DRD_FS + #elif CFG_TUSB_MCU == OPT_MCU_STM32L4 #include "stm32l4xx.h" #define FSDEV_PMA_SIZE (1024u) @@ -289,6 +295,8 @@ static const IRQn_Type fsdev_irq[] = { USB_LP_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 USB_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32WBA + USB_OTG_HS_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 USB_DRD_FS_IRQn, #else diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index d7d157149..f81cfceda 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -49,6 +49,11 @@ static void reset_core(dwc2_regs_t* dwc2) { uint32_t gsnpsid = dwc2->gsnpsid; // reset core + + // On the STM32WBA peripheral, the device seems to get stuck in reset unless + // this shadow register is utilized. + uint32_t gsnpsid = dwc2->gsnpsid; + dwc2->grstctl |= GRSTCTL_CSRST; if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index 672891561..ca5ce5a7c 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -113,6 +113,22 @@ extern "C" { #define EP_MAX_HS 9 #define EP_FIFO_SIZE_HS 4096 #endif + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WBA + #if defined(STM32WBA62xx) + #include "stm32wba62xx.h" + #elif defined(STM32WBA64xx) + #include "stm32wba64xx.h" + #elif defined(STM32WBA65xx) + #include "stm32wba65xx.h" + #else + #error "The selected STM32WBA series chip does not support OTG USB HS" + #endif + + #define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE_NS + #define OTG_HS_IRQn USB_OTG_HS_IRQn + #define EP_MAX_HS 9 + #define EP_FIFO_SIZE_HS 4096 #else #error "Unsupported MCUs" #endif @@ -166,6 +182,7 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { // MCU specific PHY init, called BEFORE core reset // - dwc2 3.30a (H5) use USB_HS_PHYC // - dwc2 4.11a (U5) use femtoPHY +// - dwc2 x.xxx (WBA) use USB_OTG_HS static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) { // Enable on-chip FS PHY @@ -194,11 +211,10 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { #endif } else { -#if CFG_TUSB_MCU != OPT_MCU_STM32U5 +#if CFG_TUSB_MCU != OPT_MCU_STM32U5 && CFG_TUSB_MCU != OPT_MCU_STM32WBA // Disable FS PHY, TODO on U5A5 (dwc2 4.11a) 16th bit is 'Host CDP behavior enable' dwc2->stm32_gccfg &= ~STM32_GCCFG_PWRDWN; #endif - // Enable on-chip HS PHY if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) { #ifdef USB_HS_PHYC @@ -233,11 +249,14 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { // Enable PLL internal PHY USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; +<<<<<<< HEAD // Wait ~2ms until the PLL is ready (there's no RDY bit to query) tusb_time_delay_ms_api(2); #else +======= +>>>>>>> 246a92c65 (It worksgit status!) #endif } } diff --git a/src/tusb_option.h b/src/tusb_option.h index 1533bb209..b2dd5fcb2 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -95,6 +95,7 @@ #define OPT_MCU_STM32H7RS 317 ///< ST F7RS #define OPT_MCU_STM32C0 318 ///< ST C0 #define OPT_MCU_STM32N6 319 ///< ST N6 +#define OPT_MCU_STM32WBA 320 ///< ST WBA // Sony #define OPT_MCU_CXD56 400 ///< SONY CXD56 diff --git a/tools/get_deps.py b/tools/get_deps.py index 36b4d2d59..37d541785 100755 --- a/tools/get_deps.py +++ b/tools/get_deps.py @@ -130,6 +130,9 @@ deps_optional = { 'hw/mcu/st/cmsis_device_wb': ['https://github.com/STMicroelectronics/cmsis_device_wb.git', 'cda2cb9fc4a5232ab18efece0bb06b0b60910083', 'stm32wb'], + 'hw/mcu/st/cmsis_device_wba': ['https://github.com/STMicroelectronics/cmsis-device-wba', + '647d8522e5fd15049e9a1cc30ed19d85e5911eaf', + 'stm32wba'], 'hw/mcu/st/stm32-mfxstm32l152': ['https://github.com/STMicroelectronics/stm32-mfxstm32l152.git', '7f4389efee9c6a655b55e5df3fceef5586b35f9b', 'stm32h7'], @@ -193,6 +196,9 @@ deps_optional = { 'hw/mcu/st/stm32wbxx_hal_driver': ['https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git', 'd60dd46996876506f1d2e9abd6b1cc110c8004cd', 'stm32wb'], + 'hw/mcu/st/stm32wbaxx_hal_driver': ['https://github.com/STMicroelectronics/stm32wbaxx_hal_driver.git', + '9442fbb71f855ff2e64fbf662b7726beba511a24', + 'stm32wba'], 'hw/mcu/ti': ['https://github.com/hathach/ti_driver.git', '143ed6cc20a7615d042b03b21e070197d473e6e5', 'msp430 msp432e4 tm4c'], -- cgit v1.3.1 From 48f6b95bd8b0c3d33832f289b02b53da410d5ef9 Mon Sep 17 00:00:00 2001 From: Dalton Caron Date: Tue, 9 Sep 2025 15:41:12 -0700 Subject: Incorporate upstream changes to dwc2_common.c --- src/portable/synopsys/dwc2/dwc2_common.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index f81cfceda..9b8333ad2 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -49,11 +49,6 @@ static void reset_core(dwc2_regs_t* dwc2) { uint32_t gsnpsid = dwc2->gsnpsid; // reset core - - // On the STM32WBA peripheral, the device seems to get stuck in reset unless - // this shadow register is utilized. - uint32_t gsnpsid = dwc2->gsnpsid; - dwc2->grstctl |= GRSTCTL_CSRST; if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { @@ -100,14 +95,6 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4}; - uint8_t phy_width; - if (CFG_TUSB_MCU != OPT_MCU_AT32F402_405 && // at32f402_405 does not support 16-bit - ghwcfg4.phy_data_width) { - phy_width = 16; // 16-bit PHY interface if supported - } else { - phy_width = 8; // 8-bit PHY interface - } - // De-select FS PHY gusbcfg &= ~GUSBCFG_PHYSEL; @@ -135,10 +122,12 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL; // Set 16-bit interface if supported - if (phy_width == 16) { - gusbcfg |= GUSBCFG_PHYIF16; + if (ghwcfg4.phy_data_width) { + #if CFG_TUSB_MCU != OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit + gusbcfg |= GUSBCFG_PHYIF16; // 16 bit + #endif } else { - gusbcfg &= ~GUSBCFG_PHYIF16; + gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit } } @@ -155,7 +144,13 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // - 9 if using 8-bit PHY interface // - 5 if using 16-bit PHY interface gusbcfg &= ~GUSBCFG_TRDT_Msk; - gusbcfg |= (phy_width == 16 ? 5u : 9u) << GUSBCFG_TRDT_Pos; + +#if CFG_TUSB_MCU == OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit + gusbcfg |= 9u << GUSBCFG_TRDT_Pos; +#else + gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; +#endif + dwc2->gusbcfg = gusbcfg; // MCU specific PHY update post reset -- cgit v1.3.1 From c9527bc0964dcd883df7ec485e2d71333ed780c0 Mon Sep 17 00:00:00 2001 From: Dalton Caron Date: Wed, 10 Sep 2025 11:14:52 -0700 Subject: Bring up to date with master --- src/portable/synopsys/dwc2/dwc2_stm32.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index ca5ce5a7c..08950ccc0 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -249,14 +249,11 @@ static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { // Enable PLL internal PHY USB_HS_PHYC->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN; -<<<<<<< HEAD // Wait ~2ms until the PLL is ready (there's no RDY bit to query) tusb_time_delay_ms_api(2); #else -======= ->>>>>>> 246a92c65 (It worksgit status!) #endif } } -- cgit v1.3.1 From 5e661eac8809f3bfdafd357d00013d94b71b4000 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 11 Sep 2025 10:57:02 +0700 Subject: Revert "Incorporate upstream changes to dwc2_common.c" This reverts commit 48f6b95bd8b0c3d33832f289b02b53da410d5ef9. also revert gsnpsid shadown since it is already in the upstream --- src/portable/synopsys/dwc2/dwc2_common.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 9b8333ad2..d7d157149 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -95,6 +95,14 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2}; const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4}; + uint8_t phy_width; + if (CFG_TUSB_MCU != OPT_MCU_AT32F402_405 && // at32f402_405 does not support 16-bit + ghwcfg4.phy_data_width) { + phy_width = 16; // 16-bit PHY interface if supported + } else { + phy_width = 8; // 8-bit PHY interface + } + // De-select FS PHY gusbcfg &= ~GUSBCFG_PHYSEL; @@ -122,12 +130,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL; // Set 16-bit interface if supported - if (ghwcfg4.phy_data_width) { - #if CFG_TUSB_MCU != OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit - gusbcfg |= GUSBCFG_PHYIF16; // 16 bit - #endif + if (phy_width == 16) { + gusbcfg |= GUSBCFG_PHYIF16; } else { - gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit + gusbcfg &= ~GUSBCFG_PHYIF16; } } @@ -144,13 +150,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // - 9 if using 8-bit PHY interface // - 5 if using 16-bit PHY interface gusbcfg &= ~GUSBCFG_TRDT_Msk; - -#if CFG_TUSB_MCU == OPT_MCU_AT32F402_405 // at32f402_405 does not actually support 16-bit - gusbcfg |= 9u << GUSBCFG_TRDT_Pos; -#else - gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; -#endif - + gusbcfg |= (phy_width == 16 ? 5u : 9u) << GUSBCFG_TRDT_Pos; dwc2->gusbcfg = gusbcfg; // MCU specific PHY update post reset -- cgit v1.3.1 From af7310b925c031d23ba490e8f70965f345da1a4d Mon Sep 17 00:00:00 2001 From: Dalton Caron Date: Thu, 11 Sep 2025 09:13:54 -0700 Subject: Revert fsdev_stm32.h changes --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 01baf50ae..ccf31e035 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -148,12 +148,6 @@ /* ST provided header has incorrect value of USB_PMAADDR */ #define FSDEV_PMA_BASE USB1_PMAADDR -#elif defined(STM32WBA62xx) || defined(STM32WBA64xx) || defined(STM32WBA65xx) - // Available only on STM32WBA62/64/65xx devices - #include "stm32wbaxx.h" - #define FSDEV_PMA_SIZE (2048u) - #define USB USB_DRD_FS - #elif CFG_TUSB_MCU == OPT_MCU_STM32L4 #include "stm32l4xx.h" #define FSDEV_PMA_SIZE (1024u) @@ -295,8 +289,6 @@ static const IRQn_Type fsdev_irq[] = { USB_LP_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 USB_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32WBA - USB_OTG_HS_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 USB_DRD_FS_IRQn, #else -- cgit v1.3.1 From 5755afa690f242988c02932068339ad5ffc4404e Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Sep 2025 11:39:41 +0200 Subject: Fix some IAR warnings Signed-off-by: HiFiPhile --- src/class/hid/hid_host.c | 3 ++- src/host/usbh.c | 29 +++++++++++++++-------------- src/portable/ehci/ehci.c | 5 +++-- 3 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 56fccdd22..a44c83433 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -519,7 +519,8 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_ // Assume bNumDescriptors = 1 p_hid->report_desc_type = desc_hid->bReportType; - p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength); + // Use offsetof to avoid pointer to the odd/misaligned address + p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*)desc_hid + offsetof(tusb_hid_descriptor_hid_t, wReportLength)); // Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config p_hid->protocol_mode = _hidh_default_protocol; diff --git a/src/host/usbh.c b/src/host/usbh.c index ce83977c5..1c63ac712 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -400,7 +400,7 @@ bool tuh_descriptor_get_device_local(uint8_t daddr, tusb_desc_device_t* desc_dev tusb_speed_t tuh_speed_get(uint8_t daddr) { tuh_bus_info_t bus_info; tuh_bus_info_get(daddr, &bus_info); - return bus_info.speed; + return (tusb_speed_t)bus_info.speed; } bool tuh_rhport_is_active(uint8_t rhport) { @@ -651,7 +651,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { tuh_xfer_t xfer = { .daddr = event.dev_addr, .ep_addr = ep_addr, - .result = event.xfer_complete.result, + .result = (xfer_result_t)event.xfer_complete.result, .actual_len = event.xfer_complete.len, .buflen = 0, // not available .buffer = NULL, // not available @@ -832,18 +832,19 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t } TU_ATTR_FALLTHROUGH; - case CONTROL_STAGE_DATA: - if (request->wLength) { - TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr); - TU_LOG_MEM_USBH(ctrl_info->buffer, xferred_bytes, 2); - } - ctrl_info->actual_len = (uint16_t) xferred_bytes; - - // ACK stage: toggle is always 1 - _control_set_xfer_stage(CONTROL_STAGE_ACK); - const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction); - TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0)); - break; + case CONTROL_STAGE_DATA: { + if (request->wLength) { + TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr); + TU_LOG_MEM_USBH(ctrl_info->buffer, xferred_bytes, 2); + } + ctrl_info->actual_len = (uint16_t) xferred_bytes; + + // ACK stage: toggle is always 1 + _control_set_xfer_stage(CONTROL_STAGE_ACK); + const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction); + TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0)); + break; + } case CONTROL_STAGE_ACK: { // Abort all pending transfers if SET_CONFIGURATION request diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index da9f49d29..b372fe635 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -184,7 +184,8 @@ static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) { //--------------------------------------------------------------------+ uint32_t hcd_frame_number(uint8_t rhport) { (void) rhport; - return (ehci_data.uframe_number + ehci_data.regs->frame_index) >> 3; + uint32_t uframe = ehci_data.regs->frame_index; + return (ehci_data.uframe_number + uframe) >> 3; } void hcd_port_reset(uint8_t rhport) { @@ -896,7 +897,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c p_qhd->used = 1; p_qhd->removing = 0; p_qhd->attached_qtd = NULL; - p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint + p_qhd->pid = tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ? EHCI_PID_IN : EHCI_PID_OUT; // PID for TD under this endpoint //------------- active, but no TD list -------------// p_qhd->qtd_overlay.halted = 0; -- cgit v1.3.1 From 802819d271314298ea5a786a37ec6a1e65ef31d6 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 12 Sep 2025 17:13:52 +0700 Subject: add all constant from MTP specs appendix, rename some e.g OPEC to OP, EVTC to EVENT --- examples/device/CMakeLists.txt | 1 + examples/device/mtp/src/mtp_fs_example.c | 92 ++-- src/class/mtp/mtp.h | 748 +++++++++++++++++++++++-------- src/class/mtp/mtp_device.c | 146 +++--- src/device/usbd.h | 2 +- 5 files changed, 682 insertions(+), 307 deletions(-) (limited to 'src') diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index bb7dd0a0f..eb625ea51 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -26,6 +26,7 @@ family_add_subdirectory(hid_generic_inout) family_add_subdirectory(hid_multiple_interface) family_add_subdirectory(midi_test) family_add_subdirectory(msc_dual_lun) +family_add_subdirectory(mtp) family_add_subdirectory(net_lwip_webserver) family_add_subdirectory(uac2_headset) family_add_subdirectory(uac2_speaker_fb) diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 41673ea53..87730bcd3 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -130,17 +130,17 @@ mtp_response_t tud_mtp_storage_open_session(uint32_t *session_id) if (*session_id == 0) { TU_LOG1("Invalid session ID\r\n"); - return MTP_RESC_INVALID_PARAMETER; + return MTP_RESP_INVALID_PARAMETER; } if (_fs_operation.session_id != 0) { *session_id = _fs_operation.session_id; TU_LOG1("ERR: Session %ld already open\r\n", _fs_operation.session_id); - return MTP_RESC_SESSION_ALREADY_OPEN; + return MTP_RESP_SESSION_ALREADY_OPEN; } _fs_operation.session_id = *session_id; TU_LOG1("Open session with id %ld\r\n", _fs_operation.session_id); - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) @@ -148,11 +148,11 @@ mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) if (session_id != _fs_operation.session_id) { TU_LOG1("ERR: Session %ld not open\r\n", session_id); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } _fs_operation.session_id = 0; TU_LOG1("Session closed\r\n"); - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_get_storage_id(uint32_t *storage_id) @@ -160,11 +160,11 @@ mtp_response_t tud_mtp_get_storage_id(uint32_t *storage_id) if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } *storage_id = STORAGE_ID(0x0001, 0x0001); TU_LOG1("Retrieved storage identifier %ld\r\n", *storage_id); - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t *info) @@ -172,12 +172,12 @@ mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } if (storage_id != STORAGE_ID(0x0001, 0x0001)) { TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESC_INVALID_STORAGE_ID; + return MTP_RESP_INVALID_STORAGE_ID; } info->storage_type = MTP_STORAGE_TYPE_FIXED_RAM; info->filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL; @@ -187,7 +187,7 @@ mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t info->free_space_in_bytes = info->free_space_in_objects * FS_MAX_NODE_BYTES; mtpd_gct_append_wstring(MTPD_STORAGE_DESCRIPTION); mtpd_gct_append_wstring(MTPD_VOLUME_IDENTIFIER); - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_format(uint32_t storage_id) @@ -195,19 +195,19 @@ mtp_response_t tud_mtp_storage_format(uint32_t storage_id) if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } if (storage_id != STORAGE_ID(0x0001, 0x0001)) { TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESC_INVALID_STORAGE_ID; + return MTP_RESP_INVALID_STORAGE_ID; } // Simply deallocate all entries for (unsigned int i=0; ihandle; TU_LOG1("Association %ld -> child %ld\r\n", parent_object_handle, obj->handle); - return MTP_RESC_OK; + return MTP_RESP_OK; } } TU_LOG1("Association traversal completed\r\n"); _fs_operation.traversal_index = 0; *next_child_handle = 0; - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_t *info) @@ -261,19 +261,19 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } // Accept command on default storage if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) { TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESC_INVALID_STORAGE_ID; + return MTP_RESP_INVALID_STORAGE_ID; } if (info->object_compressed_size > FS_MAX_NODE_BYTES) { TU_LOG1("Object size %ld is more than maximum %ld\r\n", info->object_compressed_size, FS_MAX_NODE_BYTES); - return MTP_RESC_STORE_FULL; + return MTP_RESP_STORE_FULL; } // Request for objects with no parent (0xFFFFFFFF) are considered root objects @@ -287,12 +287,12 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", parent_object); - return MTP_RESC_INVALID_PARENT_OBJECT; + return MTP_RESP_INVALID_PARENT_OBJECT; } if (!obj->association) { TU_LOG1("Parent %ld is not an association\r\n", parent_object); - return MTP_RESC_INVALID_PARENT_OBJECT; + return MTP_RESP_INVALID_PARENT_OBJECT; } } @@ -309,7 +309,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p if (obj == NULL) { TU_LOG1("No space left on device\r\n"); - return MTP_RESC_STORE_FULL; + return MTP_RESP_STORE_FULL; } // Fill-in structure @@ -317,7 +317,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p obj->handle = ++_fs_operation.last_handle; obj->parent = parent_object; obj->size = info->object_compressed_size; - obj->association = info->object_format == MTP_OBJF_ASSOCIATION; + obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; // Extract variable data uint16_t offset_data = sizeof(mtp_object_info_t); @@ -332,7 +332,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // Initialize operation _fs_operation.write_handle = obj->handle; _fs_operation.write_pos = 0; - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t *info) @@ -342,33 +342,33 @@ mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_obje if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } obj = fs_object_get_from_handle(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } memset(info, 0, sizeof(mtp_object_info_t)); info->storage_id = STORAGE_ID(0x0001, 0x0001); if (obj->association) { - info->object_format = MTP_OBJF_ASSOCIATION; + info->object_format = MTP_OBJ_FORMAT_ASSOCIATION; info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; info->object_compressed_size = 0; info->association_type = MTP_ASSOCIATION_UNDEFINED; } else { - info->object_format = MTP_OBJF_UNDEFINED; + info->object_format = MTP_OBJ_FORMAT_UNDEFINED; info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; info->object_compressed_size = obj->size; info->association_type = MTP_ASSOCIATION_UNDEFINED; } - info->thumb_format = MTP_OBJF_UNDEFINED; + info->thumb_format = MTP_OBJ_FORMAT_UNDEFINED; info->parent_object = obj->parent; mtpd_gct_append_wstring(obj->name); @@ -378,7 +378,7 @@ mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_obje TU_LOG1("Retrieve object %s with handle %ld\r\n", obj->name, obj->handle); - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t size) @@ -389,17 +389,17 @@ mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_ if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } // It's a requirement that this command is preceded by a write info if (object_handle != _fs_operation.write_handle) { TU_LOG1("ERR: Object %ld not open for write\r\n", object_handle); - return MTP_RESC_NO_VALID_OBJECTINFO; + return MTP_RESP_NO_VALID_OBJECTINFO; } TU_LOG1("Write object %ld: data chunk at %ld/%ld bytes at offset %ld\r\n", object_handle, _fs_operation.write_pos, obj->size, size); - TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESC_INCOMPLETE_TRANSFER); + TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESP_INCOMPLETE_TRANSFER); if (_fs_operation.write_pos + size < FS_MAX_NODE_BYTES) memcpy(&obj->data[_fs_operation.write_pos], buffer, size); _fs_operation.write_pos += size; @@ -409,7 +409,7 @@ mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_ _fs_operation.write_handle = 0; _fs_operation.write_pos = 0; } - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *size) @@ -419,10 +419,10 @@ mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *siz if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } *size = obj->size; - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, uint32_t buffer_size, uint32_t *read_count) @@ -434,7 +434,7 @@ mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } // It's not a requirement that this command is preceded by a read info if (object_handle != _fs_operation.read_handle) @@ -462,7 +462,7 @@ mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, _fs_operation.read_handle = 0; _fs_operation.read_pos = 0; } - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) @@ -479,12 +479,12 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); - return MTP_RESC_INVALID_PARENT_OBJECT; + return MTP_RESP_INVALID_PARENT_OBJECT; } if (!obj->association) { TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); - return MTP_RESC_INVALID_PARENT_OBJECT; + return MTP_RESP_INVALID_PARENT_OBJECT; } } @@ -493,11 +493,11 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } TU_LOG1("Move object %ld to new parent %ld\r\n", object_handle, new_parent_object_handle); obj->parent = new_parent_object_handle; - return MTP_RESC_OK; + return MTP_RESP_OK; } mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) @@ -507,7 +507,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESC_SESSION_NOT_OPEN; + return MTP_RESP_SESSION_NOT_OPEN; } if (object_handle == 0xFFFFFFFF) @@ -520,7 +520,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESC_INVALID_OBJECT_HANDLE; + return MTP_RESP_INVALID_OBJECT_HANDLE; } obj->allocated = false; TU_LOG1("Delete object with handle %ld\r\n", object_handle); @@ -539,7 +539,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) } } - return MTP_RESC_OK; + return MTP_RESP_OK; } void tud_mtp_storage_object_done(void) diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 33c6bb552..b5db2cd6c 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -47,20 +47,17 @@ typedef uint16_t wchar16_t; //--------------------------------------------------------------------+ // Media Transfer Protocol Subclass -typedef enum -{ - MTP_SUBCLASS = 1 +typedef enum { + MTP_SUBCLASS_STILL_IMAGE = 1 } mtp_subclass_type_t; // MTP Protocol. -typedef enum -{ - MTP_PROTOCOL_STILL_IMAGE = 1, +typedef enum { + MTP_PROTOCOL_PIMA_15470 = 1, ///< Picture Transfer Protocol (PIMA 15470) } mtp_protocol_type_t; // PTP/MTP protocol phases -typedef enum -{ +typedef enum { MTP_PHASE_IDLE = 0, MTP_PHASE_COMMAND, MTP_PHASE_DATA_IN, @@ -70,7 +67,7 @@ typedef enum MTP_PHASE_NONE, } mtp_phase_type_t; -// PTP/MTP Class requests +// PTP/MTP Class requests, PIMA 15740-2000: D.5.2 typedef enum { MTP_REQ_CANCEL = 0x64, @@ -79,19 +76,6 @@ typedef enum MTP_REQ_GET_DEVICE_STATUS = 0x67, } mtp_class_request_t; -#define MTP_GENERIC_DATA_BLOCK_LENGTH 12 -#define MTP_MAX_PACKET_SIZE 512 - -// PTP/MTP Generic container -typedef struct TU_ATTR_PACKED -{ - uint32_t container_length; - uint16_t container_type; - uint16_t code; - uint32_t transaction_id; - uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; -} mtp_generic_container_t; - // PTP/MTP Container type typedef enum { @@ -102,85 +86,493 @@ typedef enum MTP_CONTAINER_TYPE_EVENT_BLOCK = 4, } mtp_container_type_t; -// Supported OperationCode +// MTP 1.1 Appendix A: Object formats typedef enum { - MTP_OPEC_GET_DEVICE_INFO = 0x1001u, - MTP_OPEC_OPEN_SESSION = 0x1002u, - MTP_OPEC_CLOSE_SESSION = 0x1003u, - MTP_OPEC_GET_STORAGE_IDS = 0x1004u, - MTP_OPEC_GET_STORAGE_INFO = 0x1005u, - MTP_OPEC_GET_NUM_OBJECTS = 0x1006u, - MTP_OPEC_GET_OBJECT_HANDLES = 0x1007u, - MTP_OPEC_GET_OBJECT_INFO = 0x1008u, - MTP_OPEC_GET_OBJECT = 0x1009u, - MTP_OPEC_GET_THUMB = 0x100Au, - MTP_OPEC_DELETE_OBJECT = 0x100Bu, - MTP_OPEC_SEND_OBJECT_INFO = 0x100Cu, - MTP_OPEC_SEND_OBJECT = 0x100Du, - MTP_OPEC_INITIAL_CAPTURE = 0x100Eu, - MTP_OPEC_FORMAT_STORE = 0x100Fu, - MTP_OPEC_RESET_DEVICE = 0x1010u, - MTP_OPEC_SELF_TEST = 0x1011u, - MTP_OPEC_SET_OBJECT_PROTECTION = 0x1012u, - MTP_OPEC_POWER_DOWN = 0x1013u, - MTP_OPEC_GET_DEVICE_PROP_DESC = 0x1014u, - MTP_OPEC_GET_DEVICE_PROP_VALUE = 0x1015u, - MTP_OPEC_SET_DEVICE_PROP_VALUE = 0x1016u, - MTP_OPEC_RESET_DEVICE_PROP_VALUE = 0x1017u, - MTP_OPEC_TERMINATE_OPEN_CAPTURE = 0x1018u, - MTP_OPEC_MOVE_OBJECT = 0x1019u, - MTP_OPEC_COPY_OBJECT = 0x101Au, - MTP_OPEC_GET_PARTIAL_OBJECT = 0x101Bu, - MTP_OPEC_INITIATE_OPEN_CAPTURE = 0x101Bu, - MTP_OPEC_GET_OBJECT_PROPS_SUPPORTED = 0x9801u, - MTP_OPEC_GET_OBJECT_PROP_DESC = 0x9802u, - MTP_OPEC_GET_OBJECT_PROP_VALUE = 0x9803u, - MTP_OPEC_SET_OBJECT_PROP_VALUE = 0x9804u, - MTP_OPEC_GET_OBJECT_PROPLIST = 0x9805u, - MTP_OPEC_GET_OBJECT_PROP_REFERENCES = 0x9810u, - MTP_OPEC_GETSERVICEIDS = 0x9301u, - MTP_OPEC_GETSERVICEINFO = 0x9302u, - MTP_OPEC_GETSERVICECAPABILITIES = 0x9303u, - MTP_OPEC_GETSERVICEPROPDESC = 0x9304u, -} mtp_operation_code_t; + // ---- Base formats ---- + MTP_OBJ_FORMAT_UNDEFINED = 0x3000u, // Undefined object + MTP_OBJ_FORMAT_ASSOCIATION = 0x3001u, // Association (for example, a folder) + MTP_OBJ_FORMAT_SCRIPT = 0x3002u, // Device model-specific script + MTP_OBJ_FORMAT_EXECUTABLE = 0x3003u, // Device model-specific binary executable + MTP_OBJ_FORMAT_TEXT = 0x3004u, // Text file + MTP_OBJ_FORMAT_HTML = 0x3005u, // Hypertext Markup Language file (text) + MTP_OBJ_FORMAT_DPOF = 0x3006u, // Digital Print Order Format file (text) + MTP_OBJ_FORMAT_AIFF = 0x3007u, // Audio clip (AIFF) + MTP_OBJ_FORMAT_WAV = 0x3008u, // Audio clip (WAV) + MTP_OBJ_FORMAT_MP3 = 0x3009u, // MPEG-1 Layer III audio (ISO/IEC 13818-3) + MTP_OBJ_FORMAT_AVI = 0x300Au, // Video clip (AVI) + MTP_OBJ_FORMAT_MPEG = 0x300Bu, // Video clip (MPEG) + MTP_OBJ_FORMAT_ASF = 0x300Cu, // Microsoft Advanced Streaming Format (video) + + // ---- Image formats ---- + MTP_OBJ_FORMAT_UNDEFINED_IMAGE = 0x3800u, // Undefined image object + MTP_OBJ_FORMAT_EXIF_JPEG = 0x3801u, // Exchangeable Image Format, JEIDA standard + MTP_OBJ_FORMAT_TIFF_EP = 0x3802u, // Tag Image File Format for Electronic Photography + MTP_OBJ_FORMAT_FLASHPIX = 0x3803u, // Structured Storage Image Format (FlashPix) + MTP_OBJ_FORMAT_BMP = 0x3804u, // Microsoft Windows Bitmap file + MTP_OBJ_FORMAT_CIFF = 0x3805u, // Canon Camera Image File Format + MTP_OBJ_FORMAT_UNDEFINED_3806 = 0x3806u, // Reserved / Undefined + MTP_OBJ_FORMAT_GIF = 0x3807u, // Graphics Interchange Format + MTP_OBJ_FORMAT_JFIF = 0x3808u, // JPEG File Interchange Format + MTP_OBJ_FORMAT_CD = 0x3809u, // PhotoCD Image Pac + MTP_OBJ_FORMAT_PICT = 0x380Au, // Quickdraw Image Format + MTP_OBJ_FORMAT_PNG = 0x380Bu, // Portable Network Graphics + MTP_OBJ_FORMAT_UNDEFINED_380C = 0x380Cu, // Reserved / Undefined + MTP_OBJ_FORMAT_TIFF = 0x380Du, // Tag Image File Format (baseline) + MTP_OBJ_FORMAT_TIFF_IT = 0x380Eu, // Tag Image File Format for IT (graphic arts) + MTP_OBJ_FORMAT_JP2 = 0x380Fu, // JPEG2000 Baseline File Format + MTP_OBJ_FORMAT_JPX = 0x3810u, // JPEG2000 Extended File Format + + // ---- Firmware & misc ---- + MTP_OBJ_FORMAT_UNDEFINED_FIRMWARE = 0xB802u, // Undefined Firmware + MTP_OBJ_FORMAT_WBMP = 0xB803u, // Wireless Application Protocol Bitmap Format (.wbmp) + MTP_OBJ_FORMAT_WINDOWS_IMAGE = 0xB881u, // Windows Image Format + MTP_OBJ_FORMAT_JPEGXR = 0xB804u, // JPEG XR (.hdp, .jxr, .wdp) + + // ---- Audio formats ---- + MTP_OBJ_FORMAT_UNDEFINED_AUDIO = 0xB900u, // Undefined audio object + MTP_OBJ_FORMAT_WMA = 0xB901u, // Windows Media Audio + MTP_OBJ_FORMAT_OGG = 0xB902u, // OGG container + MTP_OBJ_FORMAT_AAC = 0xB903u, // Advanced Audio Coding (.aac) + MTP_OBJ_FORMAT_AUDIBLE = 0xB904u, // Audible format + MTP_OBJ_FORMAT_FLAC = 0xB906u, // Free Lossless Audio Codec + MTP_OBJ_FORMAT_QCELP = 0xB907u, // Qualcomm Code Excited Linear Prediction (.qcp) + MTP_OBJ_FORMAT_AMR = 0xB908u, // Adaptive Multi-Rate audio (.amr) + + // ---- Video formats ---- + MTP_OBJ_FORMAT_UNDEFINED_VIDEO = 0xB980u, // Undefined video object + MTP_OBJ_FORMAT_WMV = 0xB981u, // Windows Media Video + MTP_OBJ_FORMAT_MP4 = 0xB982u, // MP4 Container (ISO 14496-1) + MTP_OBJ_FORMAT_MP2 = 0xB983u, // MPEG-1 Layer II audio + MTP_OBJ_FORMAT_3GP = 0xB984u, // 3GP Container + MTP_OBJ_FORMAT_3G2 = 0xB985u, // 3GPP2 Container + MTP_OBJ_FORMAT_AVCHD = 0xB986u, // AVCHD (MPEG-4 AVC + Dolby Digital) + MTP_OBJ_FORMAT_ATSC_TS = 0xB987u, // ATSC-compliant MPEG-2 Transport Stream + MTP_OBJ_FORMAT_DVB_TS = 0xB988u, // DVB-compliant MPEG-2 Transport Stream + + // ---- Collections ---- + MTP_OBJ_FORMAT_UNDEFINED_COLLECTION = 0xBA00u, // Undefined collection + MTP_OBJ_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM = 0xBA01u, // Abstract Multimedia Album + MTP_OBJ_FORMAT_ABSTRACT_IMAGE_ALBUM = 0xBA02u, // Abstract Image Album + MTP_OBJ_FORMAT_ABSTRACT_AUDIO_ALBUM = 0xBA03u, // Abstract Audio Album + MTP_OBJ_FORMAT_ABSTRACT_VIDEO_ALBUM = 0xBA04u, // Abstract Video Album + MTP_OBJ_FORMAT_ABSTRACT_AV_PLAYLIST = 0xBA05u, // Abstract Audio & Video Playlist + MTP_OBJ_FORMAT_ABSTRACT_CONTACT_GROUP = 0xBA06u, // Abstract Contact Group + MTP_OBJ_FORMAT_ABSTRACT_MESSAGE_FOLDER = 0xBA07u, // Abstract Message Folder + MTP_OBJ_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION = 0xBA08u, // Abstract Chaptered Production + MTP_OBJ_FORMAT_ABSTRACT_AUDIO_PLAYLIST = 0xBA09u, // Abstract Audio Playlist + MTP_OBJ_FORMAT_ABSTRACT_VIDEO_PLAYLIST = 0xBA0Au, // Abstract Video Playlist + MTP_OBJ_FORMAT_ABSTRACT_MEDIACAST = 0xBA0Bu, // Abstract Mediacast (RSS enclosure) + + // ---- Playlist formats ---- + MTP_OBJ_FORMAT_WPL_PLAYLIST = 0xBA10u, // Windows Media Player Playlist (.wpl) + MTP_OBJ_FORMAT_M3U_PLAYLIST = 0xBA11u, // M3U Playlist + MTP_OBJ_FORMAT_MPL_PLAYLIST = 0xBA12u, // MPL Playlist + MTP_OBJ_FORMAT_ASX_PLAYLIST = 0xBA13u, // ASX Playlist + MTP_OBJ_FORMAT_PLS_PLAYLIST = 0xBA14u, // PLS Playlist + + // ---- Document formats ---- + MTP_OBJ_FORMAT_UNDEFINED_DOC = 0xBA80u, // Undefined Document + MTP_OBJ_FORMAT_ABSTRACT_DOC = 0xBA81u, // Abstract Document + MTP_OBJ_FORMAT_XML_DOC = 0xBA82u, // XML Document + MTP_OBJ_FORMAT_DOC = 0xBA83u, // Microsoft Word Document + MTP_OBJ_FORMAT_MHT_DOC = 0xBA84u, // MHT Compiled HTML Document + MTP_OBJ_FORMAT_XLS = 0xBA85u, // Microsoft Excel Spreadsheet + MTP_OBJ_FORMAT_PPT = 0xBA86u, // Microsoft PowerPoint Presentation + + // ---- Messaging ---- + MTP_OBJ_FORMAT_UNDEFINED_MSG = 0xBB00u, // Undefined Message + MTP_OBJ_FORMAT_ABSTRACT_MSG = 0xBB01u, // Abstract Message + + // ---- Bookmarks ---- + MTP_OBJ_FORMAT_UNDEFINED_BOOKMARK = 0xBB10u, // Undefined Bookmark + MTP_OBJ_FORMAT_ABSTRACT_BOOKMARK = 0xBB11u, // Abstract Bookmark + + // ---- Appointments ---- + MTP_OBJ_FORMAT_UNDEFINED_APPT = 0xBB20u, // Undefined Appointment + MTP_OBJ_FORMAT_ABSTRACT_APPT = 0xBB21u, // Abstract Appointment + MTP_OBJ_FORMAT_VCALENDAR1 = 0xBB22u, // vCalendar 1.0 + + // ---- Tasks ---- + MTP_OBJ_FORMAT_UNDEFINED_TASK = 0xBB40u, // Undefined Task + MTP_OBJ_FORMAT_ABSTRACT_TASK = 0xBB41u, // Abstract Task + MTP_OBJ_FORMAT_ICALENDAR = 0xBB42u, // iCalendar + + // ---- Notes ---- + MTP_OBJ_FORMAT_UNDEFINED_NOTE = 0xBB60u, // Undefined Note + MTP_OBJ_FORMAT_ABSTRACT_NOTE = 0xBB61u, // Abstract Note + + // ---- Contacts ---- + MTP_OBJ_FORMAT_UNDEFINED_CONTACT= 0xBB80u, // Undefined Contact + MTP_OBJ_FORMAT_ABSTRACT_CONTACT = 0xBB81u, // Abstract Contact + MTP_OBJ_FORMAT_VCARD2 = 0xBB82u, // vCard 2.1 + MTP_OBJ_FORMAT_VCARD3 = 0xBB83u, // vCard 3.0 +} mtp_object_formats_t; -// Supported EventCode -typedef enum -{ - MTP_EVTC_OBJECT_ADDED = 0x4002, -} mtp_event_code_t; +// MTP 1.1 Appendix B: Object Properties +typedef enum { + MTP_OBJ_PROP_STORAGE_ID = 0xDC01u, // StorageID + MTP_OBJ_PROP_OBJECT_FORMAT = 0xDC02u, // Object Format + MTP_OBJ_PROP_PROTECTION_STATUS = 0xDC03u, // Protection Status + MTP_OBJ_PROP_OBJECT_SIZE = 0xDC04u, // Object Size + MTP_OBJ_PROP_ASSOCIATION_TYPE = 0xDC05u, // Association Type + MTP_OBJ_PROP_ASSOCIATION_DESC = 0xDC06u, // Association Description + MTP_OBJ_PROP_OBJECT_FILE_NAME = 0xDC07u, // Object File Name + MTP_OBJ_PROP_DATE_CREATED = 0xDC08u, // Date Created + MTP_OBJ_PROP_DATE_MODIFIED = 0xDC09u, // Date Modified + MTP_OBJ_PROP_KEYWORDS = 0xDC0Au, // Keywords + MTP_OBJ_PROP_PARENT_OBJECT = 0xDC0Bu, // Parent Object + MTP_OBJ_PROP_ALLOWED_FOLDER_CONTENTS = 0xDC0Cu, // Allowed Folder Contents + MTP_OBJ_PROP_HIDDEN = 0xDC0Du, // Hidden + MTP_OBJ_PROP_SYSTEM_OBJECT = 0xDC0Eu, // System Object + // 0xDC0F-0xDC40 is reserved + + MTP_OBJ_PROP_PERSISTENT_UID = 0xDC41u, // Persistent Unique Object Identifier + MTP_OBJ_PROP_SYNC_ID = 0xDC42u, // SyncID + MTP_OBJ_PROP_PROPERTY_BAG = 0xDC43u, // Property Bag + MTP_OBJ_PROP_NAME = 0xDC44u, // Name + MTP_OBJ_PROP_CREATED_BY = 0xDC45u, // Created By + MTP_OBJ_PROP_ARTIST = 0xDC46u, // Artist + MTP_OBJ_PROP_DATE_AUTHORED = 0xDC47u, // Date Authored + MTP_OBJ_PROP_DESCRIPTION = 0xDC48u, // Description + MTP_OBJ_PROP_URL_REFERENCE = 0xDC49u, // URL Reference + MTP_OBJ_PROP_LANGUAGE_LOCALE = 0xDC4Au, // Language-Locale + MTP_OBJ_PROP_COPYRIGHT_INFO = 0xDC4Bu, // Copyright Information + MTP_OBJ_PROP_SOURCE = 0xDC4Cu, // Source + MTP_OBJ_PROP_ORIGIN_LOCATION = 0xDC4Du, // Origin Location + MTP_OBJ_PROP_DATE_ADDED = 0xDC4Eu, // Date Added + MTP_OBJ_PROP_NON_CONSUMABLE = 0xDC4Fu, // Non-Consumable + MTP_OBJ_PROP_CORRUPT_UNPLAYABLE = 0xDC50u, // Corrupt/Unplayable + MTP_OBJ_PROP_PRODUCER_SERIAL_NUMBER = 0xDC51u, // ProducerSerialNumber + // 0xDC52-0xDC80 is reserved + + MTP_OBJ_PROP_REP_SAMPLE_FORMAT = 0xDC81u, // Representative Sample Format + MTP_OBJ_PROP_REP_SAMPLE_SIZE = 0xDC82u, // Representative Sample Size + MTP_OBJ_PROP_REP_SAMPLE_HEIGHT = 0xDC83u, // Representative Sample Height + MTP_OBJ_PROP_REP_SAMPLE_WIDTH = 0xDC84u, // Representative Sample Width + MTP_OBJ_PROP_REP_SAMPLE_DURATION = 0xDC85u, // Representative Sample Duration + MTP_OBJ_PROP_REP_SAMPLE_DATA = 0xDC86u, // Representative Sample Data + MTP_OBJ_PROP_WIDTH = 0xDC87u, // Width + MTP_OBJ_PROP_HEIGHT = 0xDC88u, // Height + MTP_OBJ_PROP_DURATION = 0xDC89u, // Duration + MTP_OBJ_PROP_RATING = 0xDC8Au, // Rating + MTP_OBJ_PROP_TRACK = 0xDC8Bu, // Track + MTP_OBJ_PROP_GENRE = 0xDC8Cu, // Genre + MTP_OBJ_PROP_CREDITS = 0xDC8Du, // Credits + MTP_OBJ_PROP_LYRICS = 0xDC8Eu, // Lyrics + MTP_OBJ_PROP_SUBSCRIPTION_CONTENT_ID = 0xDC8Fu, // Subscription Content ID + MTP_OBJ_PROP_PRODUCED_BY = 0xDC90u, // Produced By + MTP_OBJ_PROP_USE_COUNT = 0xDC91u, // Use Count + MTP_OBJ_PROP_SKIP_COUNT = 0xDC92u, // Skip Count + MTP_OBJ_PROP_LAST_ACCESSED = 0xDC93u, // Last Accessed + MTP_OBJ_PROP_PARENTAL_RATING = 0xDC94u, // Parental Rating + MTP_OBJ_PROP_META_GENRE = 0xDC95u, // Meta Genre + MTP_OBJ_PROP_COMPOSER = 0xDC96u, // Composer + MTP_OBJ_PROP_EFFECTIVE_RATING = 0xDC97u, // Effective Rating + MTP_OBJ_PROP_SUBTITLE = 0xDC98u, // Subtitle + MTP_OBJ_PROP_ORIGINAL_RELEASE_DATE = 0xDC99u, // Original Release Date + MTP_OBJ_PROP_ALBUM_NAME = 0xDC9Au, // Album Name + MTP_OBJ_PROP_ALBUM_ARTIST = 0xDC9Bu, // Album Artist + MTP_OBJ_PROP_MOOD = 0xDC9Cu, // Mood + MTP_OBJ_PROP_DRM_STATUS = 0xDC9Du, // DRM Status + MTP_OBJ_PROP_SUB_DESCRIPTION = 0xDC9Eu, // Sub Description + // 0xDC9F-0xDCD0 is reserved + + MTP_OBJ_PROP_IS_CROPPED = 0xDCD1u, // Is Cropped + MTP_OBJ_PROP_IS_COLOUR_CORRECTED = 0xDCD2u, // Is Colour Corrected + MTP_OBJ_PROP_IMAGE_BIT_DEPTH = 0xDCD3u, // Image Bit Depth + MTP_OBJ_PROP_FNUMBER = 0xDCD4u, // Fnumber (aperture ×100) + MTP_OBJ_PROP_EXPOSURE_TIME = 0xDCD5u, // Exposure Time (sec ×10,000) + MTP_OBJ_PROP_EXPOSURE_INDEX = 0xDCD6u, // Exposure Index (ISO) + // 0xDCD7-0xDCDF is reserved + + MTP_OBJ_PROP_DISPLAY_NAME = 0xDCE0u, // Display Name + MTP_OBJ_PROP_BODY_TEXT = 0xDCE1u, // Body Text + MTP_OBJ_PROP_SUBJECT = 0xDCE2u, // Subject + MTP_OBJ_PROP_PRIORITY = 0xDCE3u, // Priority + // 0xDCE4-0xDCFF is reserved + + MTP_OBJ_PROP_GIVEN_NAME = 0xDD00u, // Given Name + MTP_OBJ_PROP_MIDDLE_NAMES = 0xDD01u, // Middle Names + MTP_OBJ_PROP_FAMILY_NAME = 0xDD02u, // Family Name + MTP_OBJ_PROP_PREFIX = 0xDD03u, // Prefix + MTP_OBJ_PROP_SUFFIX = 0xDD04u, // Suffix + MTP_OBJ_PROP_PHONETIC_GIVEN_NAME = 0xDD05u, // Phonetic Given Name + MTP_OBJ_PROP_PHONETIC_FAMILY_NAME = 0xDD06u, // Phonetic Family Name + MTP_OBJ_PROP_EMAIL_PRIMARY = 0xDD07u, // Email Primary + MTP_OBJ_PROP_EMAIL_PERSONAL_1 = 0xDD08u, // Email Personal 1 + MTP_OBJ_PROP_EMAIL_PERSONAL_2 = 0xDD09u, // Email Personal 2 + MTP_OBJ_PROP_EMAIL_BUSINESS_1 = 0xDD0Au, // Email Business 1 + MTP_OBJ_PROP_EMAIL_BUSINESS_2 = 0xDD0Bu, // Email Business 2 + MTP_OBJ_PROP_EMAIL_OTHERS = 0xDD0Cu, // Email Others + MTP_OBJ_PROP_PHONE_PRIMARY = 0xDD0Du, // Phone Number Primary + MTP_OBJ_PROP_PHONE_PERSONAL_1 = 0xDD0Eu, // Phone Number Personal + MTP_OBJ_PROP_PHONE_PERSONAL_2 = 0xDD0Fu, // Phone Number Personal 2 + MTP_OBJ_PROP_PHONE_BUSINESS_1 = 0xDD10u, // Phone Number Business + MTP_OBJ_PROP_PHONE_BUSINESS_2 = 0xDD11u, // Phone Number Business 2 + MTP_OBJ_PROP_PHONE_MOBILE_1 = 0xDD12u, // Phone Number Mobile + MTP_OBJ_PROP_PHONE_MOBILE_2 = 0xDD13u, // Phone Number Mobile 2 + MTP_OBJ_PROP_FAX_PRIMARY = 0xDD14u, // Fax Number Primary + MTP_OBJ_PROP_FAX_PERSONAL = 0xDD15u, // Fax Number Personal + MTP_OBJ_PROP_FAX_BUSINESS = 0xDD16u, // Fax Number Business + MTP_OBJ_PROP_PAGER_NUMBER = 0xDD17u, // Pager Number + MTP_OBJ_PROP_PHONE_OTHERS = 0xDD18u, // Phone Number Others + MTP_OBJ_PROP_WEB_PRIMARY = 0xDD19u, // Primary Web Address + MTP_OBJ_PROP_WEB_PERSONAL = 0xDD1Au, // Personal Web Address + MTP_OBJ_PROP_WEB_BUSINESS = 0xDD1Bu, // Business Web Address + MTP_OBJ_PROP_IM_ADDRESS_1 = 0xDD1Cu, // Instant Messenger Address + MTP_OBJ_PROP_IM_ADDRESS_2 = 0xDD1Du, // Instant Messenger Address 2 + MTP_OBJ_PROP_IM_ADDRESS_3 = 0xDD1Eu, // Instant Messenger Address 3 + MTP_OBJ_PROP_ADDR_PERSONAL_FULL = 0xDD1Fu, // Postal Address Personal Full + MTP_OBJ_PROP_ADDR_PERSONAL_LINE1 = 0xDD20u, // Postal Address Personal Line 1 + MTP_OBJ_PROP_ADDR_PERSONAL_LINE2 = 0xDD21u, // Postal Address Personal Line 2 + MTP_OBJ_PROP_ADDR_PERSONAL_CITY = 0xDD22u, // Postal Address Personal City + MTP_OBJ_PROP_ADDR_PERSONAL_REGION = 0xDD23u, // Postal Address Personal Region + MTP_OBJ_PROP_ADDR_PERSONAL_POSTAL_CODE = 0xDD24u, // Postal Address Personal Postal Code + MTP_OBJ_PROP_ADDR_PERSONAL_COUNTRY = 0xDD25u, // Postal Address Personal Country + MTP_OBJ_PROP_ADDR_BUSINESS_FULL = 0xDD26u, // Postal Address Business Full + MTP_OBJ_PROP_ADDR_BUSINESS_LINE1 = 0xDD27u, // Postal Address Business Line 1 + MTP_OBJ_PROP_ADDR_BUSINESS_LINE2 = 0xDD28u, // Postal Address Business Line 2 + MTP_OBJ_PROP_ADDR_BUSINESS_CITY = 0xDD29u, // Postal Address Business City + MTP_OBJ_PROP_ADDR_BUSINESS_REGION = 0xDD2Au, // Postal Address Business Region + MTP_OBJ_PROP_ADDR_BUSINESS_POSTAL_CODE = 0xDD2Bu, // Postal Address Business Postal Code + MTP_OBJ_PROP_ADDR_BUSINESS_COUNTRY = 0xDD2Cu, // Postal Address Business Country + MTP_OBJ_PROP_ADDR_OTHER_FULL = 0xDD2Du, // Postal Address Other Full + MTP_OBJ_PROP_ADDR_OTHER_LINE1 = 0xDD2Eu, // Postal Address Other Line 1 + MTP_OBJ_PROP_ADDR_OTHER_LINE2 = 0xDD2Fu, // Postal Address Other Line 2 + MTP_OBJ_PROP_ADDR_OTHER_CITY = 0xDD30u, // Postal Address Other City + MTP_OBJ_PROP_ADDR_OTHER_REGION = 0xDD31u, // Postal Address Other Region + MTP_OBJ_PROP_ADDR_OTHER_POSTAL_CODE = 0xDD32u, // Postal Address Other Postal Code + MTP_OBJ_PROP_ADDR_OTHER_COUNTRY = 0xDD33u, // Postal Address Other Country + MTP_OBJ_PROP_ORGANIZATION_NAME = 0xDD34u, // Organization Name + MTP_OBJ_PROP_PHONETIC_ORG_NAME = 0xDD35u, // Phonetic Organization Name + MTP_OBJ_PROP_ROLE = 0xDD36u, // Role + MTP_OBJ_PROP_BIRTHDATE = 0xDD37u, // Birthdate + // 0xDD38-0xDD3F is reserved + + MTP_OBJ_PROP_MESSAGE_TO = 0xDD40u, // Message To + MTP_OBJ_PROP_MESSAGE_CC = 0xDD41u, // Message CC + MTP_OBJ_PROP_MESSAGE_BCC = 0xDD42u, // Message BCC + MTP_OBJ_PROP_MESSAGE_READ = 0xDD43u, // Message Read + MTP_OBJ_PROP_MESSAGE_RECEIVED_TIME = 0xDD44u, // Message Received Time + MTP_OBJ_PROP_MESSAGE_SENDER = 0xDD45u, // Message Sender + // 0xDD46-0xDD4F is reserved + + MTP_OBJ_PROP_ACTIVITY_BEGIN_TIME = 0xDD50u, // Activity Begin Time + MTP_OBJ_PROP_ACTIVITY_END_TIME = 0xDD51u, // Activity End Time + MTP_OBJ_PROP_ACTIVITY_LOCATION = 0xDD52u, // Activity Location + // 0xDD53 is reserved + MTP_OBJ_PROP_ACTIVITY_REQUIRED_ATTENDEES= 0xDD54u, // Activity Required Attendees + MTP_OBJ_PROP_ACTIVITY_OPTIONAL_ATTENDEES= 0xDD55u, // Activity Optional Attendees + MTP_OBJ_PROP_ACTIVITY_RESOURCES = 0xDD56u, // Activity Resources + MTP_OBJ_PROP_ACTIVITY_ACCEPTED = 0xDD57u, // Activity Accepted + MTP_OBJ_PROP_ACTIVITY_TENTATIVE = 0xDD58u, // Activity Tentative + MTP_OBJ_PROP_ACTIVITY_DECLINED = 0xDD59u, // Activity Declined + MTP_OBJ_PROP_ACTIVITY_REMINDER_TIME = 0xDD5Au, // Activity Reminder Time + MTP_OBJ_PROP_ACTIVITY_OWNER = 0xDD5Bu, // Activity Owner + MTP_OBJ_PROP_ACTIVITY_STATUS = 0xDD5Cu, // Activity Status + MTP_OBJ_PROP_OWNER = 0xDD5Du, // Owner + MTP_OBJ_PROP_EDITOR = 0xDD5Eu, // Editor + MTP_OBJ_PROP_WEBMASTER = 0xDD5Fu, // Webmaster + + MTP_OBJ_PROP_URL_SOURCE = 0xDD60u, // URL Source + MTP_OBJ_PROP_URL_DESTINATION = 0xDD61u, // URL Destination + MTP_OBJ_PROP_TIME_BOOKMARK = 0xDD62u, // Time Bookmark + MTP_OBJ_PROP_OBJECT_BOOKMARK = 0xDD63u, // Object Bookmark + MTP_OBJ_PROP_BYTE_BOOKMARK = 0xDD64u, // Byte Bookmark + // 0xDD65-0xDD6F is reserved + + MTP_OBJ_PROP_LAST_BUILD_DATE = 0xDD70u, // Last Build Date + MTP_OBJ_PROP_TIME_TO_LIVE = 0xDD71u, // Time to Live (minutes) + MTP_OBJ_PROP_MEDIA_GUID = 0xDD72u, // Media GUID + // 0xDD73-0xDDFF is reserved + + // media encoding + MTP_OBJ_PROP_TOTAL_BITRATE = 0xDE91u, // Total BitRate + MTP_OBJ_PROP_BITRATE_TYPE = 0xDE92u, // Bitrate Type + MTP_OBJ_PROP_SAMPLE_RATE = 0xDE93u, // Sample Rate + MTP_OBJ_PROP_NUM_CHANNELS = 0xDE94u, // Number Of Channels + MTP_OBJ_PROP_AUDIO_BITDEPTH = 0xDE95u, // Audio BitDepth + // 0xDE96 is reserved + MTP_OBJ_PROP_SCAN_TYPE = 0xDE97u, // Scan Type + // 0xDE98 is reserved + MTP_OBJ_PROP_AUDIO_WAVE_CODEC = 0xDE99u, // Audio WAVE Codec + MTP_OBJ_PROP_AUDIO_BITRATE = 0xDE9Au, // Audio BitRate + MTP_OBJ_PROP_VIDEO_FOURCC_CODEC = 0xDE9Bu, // Video FourCC Codec + MTP_OBJ_PROP_VIDEO_BITRATE = 0xDE9Cu, // Video BitRate + MTP_OBJ_PROP_FRAMES_PER_KSEC = 0xDE9Du, // Frames Per Thousand Seconds + MTP_OBJ_PROP_KEYFRAME_DISTANCE = 0xDE9Eu, // KeyFrame Distance (ms) + MTP_OBJ_PROP_BUFFER_SIZE = 0xDE9Fu, // Buffer Size + MTP_OBJ_PROP_ENCODING_QUALITY = 0xDEA0u, // Encoding Quality + MTP_OBJ_PROP_ENCODING_PROFILE = 0xDEA1u // Encoding Profile +} mtp_object_properties_t; -// Supported Device Properties -typedef enum -{ - MTP_DEVP_UNDEFINED = 0x5000u, - MTP_DEVP_BATTERY_LEVEL = 0x5001u, - MTP_DEVP_DEVICE_FRIENDLY_NAME = 0xD402u, +// MTP 1.1 Appendeix C: Device Properties +typedef enum { + MTP_DEV_PROP_UNDEFINED = 0x5000u, + MTP_DEV_PROP_BATTERY_LEVEL = 0x5001u, + MTP_DEV_PROP_FUNCTIONAL_MODE = 0x5002u, + MTP_DEV_PROP_IMAGE_SIZE = 0x5003u, + MTP_DEV_PROP_COMPRESSION_SETTING = 0x5004u, + MTP_DEV_PROP_WHITE_BALANCE = 0x5005u, + MTP_DEV_PROP_RGB_GAIN = 0x5006u, + MTP_DEV_PROP_F_NUMBER = 0x5007u, + MTP_DEV_PROP_FOCAL_LENGTH = 0x5008u, + MTP_DEV_PROP_FOCUS_DISTANCE = 0x5009u, + MTP_DEV_PROP_FOCUS_MODE = 0x500Au, + MTP_DEV_PROP_EXPOSURE_METERING_MODE = 0x500Bu, + MTP_DEV_PROP_FLASH_MODE = 0x500Cu, + MTP_DEV_PROP_EXPOSURE_TIME = 0x500Du, + MTP_DEV_PROP_EXPOSURE_PROGRAM_MODE = 0x500Eu, + MTP_DEV_PROP_EXPOSURE_INDEX = 0x500Fu, + MTP_DEV_PROP_EXPOSURE_BIAS_COMPENSATION = 0x5010u, + MTP_DEV_PROP_DATE_TIME = 0x5011u, + MTP_DEV_PROP_CAPTURE_DELAY = 0x5012u, + MTP_DEV_PROP_STILL_CAPTURE_MODE = 0x5013u, + MTP_DEV_PROP_CONTRAST = 0x5014u, + MTP_DEV_PROP_SHARPNESS = 0x5015u, + MTP_DEV_PROP_DIGITAL_ZOOM = 0x5016u, + MTP_DEV_PROP_EFFECT_MODE = 0x5017u, + MTP_DEV_PROP_BURST_NUMBER = 0x5018u, + MTP_DEV_PROP_BURST_INTERVAL = 0x5019u, + MTP_DEV_PROP_TIMELAPSE_NUMBER = 0x501Au, + MTP_DEV_PROP_TIMELAPSE_INTERVAL = 0x501Bu, + MTP_DEV_PROP_FOCUS_METERING_MODE = 0x501Cu, + MTP_DEV_PROP_UPLOAD_URL = 0x501Du, + MTP_DEV_PROP_ARTIST = 0x501Eu, + MTP_DEV_PROP_COPYRIGHT_INFO = 0x501Fu, + MTP_DEV_PROP_SYNCHRONIZTION_PARTNER = 0xD401, + MTP_DEV_PROP_DEVICE_FRIENDLY_NAME = 0xD402u, + MTP_DEV_PROP_VOLUME = 0xD403u, + MTP_DEV_PROP_SUPPORTED_FORMATS_ORDERED = 0xD404u, + MTP_DEV_PROP_DEVICE_ICON = 0xD405u, + MTP_DEV_PROP_SECTION_INITIATOR_VERSION_INFO = 0xD406u, + MTP_DEV_PROP_PERCEIVED_DEVICE_TYPE = 0xD407u, + MTP_DEV_PROP_PLAYBACK_RATE = 0xD410u, + MTP_DEV_PROP_PLAYBACK_OBJECT = 0xD411u, + MTP_DEV_PROP_PLAYBACK_CONTAINER_INDEX = 0xD412u, } mtp_event_properties_t; -// Supported Object Properties -typedef enum -{ - MTP_OBJP_STORAGE_ID = 0xDC01u, - MTP_OBJP_OBJECT_FORMAT = 0xDC02u, - MTP_OBJP_PROTECTION_STATUS = 0xDC03u, - MTP_OBJP_OBJECT_SIZE = 0xDC04u, - MTP_OBJP_ASSOCIATION_TYPE = 0xDC05u, - MTP_OBJP_OBJECT_FILE_NAME = 0xDC07u, - MTP_OBJP_PARENT_OBJECT = 0xDC0Bu, - MTP_OBJP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER = 0xDC41u, - MTP_OBJP_NAME = 0xDC44u, -} mtp_object_properties_t; +// MTP 1.1 Appendix D: Operations +typedef enum { + MTP_OP_UNDEFINED = 0x1000u, + MTP_OP_GET_DEVICE_INFO = 0x1001u, + MTP_OP_OPEN_SESSION = 0x1002u, + MTP_OP_CLOSE_SESSION = 0x1003u, + MTP_OP_GET_STORAGE_IDS = 0x1004u, + MTP_OP_GET_STORAGE_INFO = 0x1005u, + MTP_OP_GET_NUM_OBJECTS = 0x1006u, + MTP_OP_GET_OBJECT_HANDLES = 0x1007u, + MTP_OP_GET_OBJECT_INFO = 0x1008u, + MTP_OP_GET_OBJECT = 0x1009u, + MTP_OP_GET_THUMB = 0x100Au, + MTP_OP_DELETE_OBJECT = 0x100Bu, + MTP_OP_SEND_OBJECT_INFO = 0x100Cu, + MTP_OP_SEND_OBJECT = 0x100Du, + MTP_OP_INITIAL_CAPTURE = 0x100Eu, + MTP_OP_FORMAT_STORE = 0x100Fu, + MTP_OP_RESET_DEVICE = 0x1010u, + MTP_OP_SELF_TEST = 0x1011u, + MTP_OP_SET_OBJECT_PROTECTION = 0x1012u, + MTP_OP_POWER_DOWN = 0x1013u, + MTP_OP_GET_DEVICE_PROP_DESC = 0x1014u, + MTP_OP_GET_DEVICE_PROP_VALUE = 0x1015u, + MTP_OP_SET_DEVICE_PROP_VALUE = 0x1016u, + MTP_OP_RESET_DEVICE_PROP_VALUE = 0x1017u, + MTP_OP_TERMINATE_OPEN_CAPTURE = 0x1018u, + MTP_OP_MOVE_OBJECT = 0x1019u, + MTP_OP_COPY_OBJECT = 0x101Au, + MTP_OP_GET_PARTIAL_OBJECT = 0x101Bu, + MTP_OP_INITIATE_OPEN_CAPTURE = 0x101Bu, + MTP_OP_GET_OBJECT_PROPS_SUPPORTED = 0x9801u, + MTP_OP_GET_OBJECT_PROP_DESC = 0x9802u, + MTP_OP_GET_OBJECT_PROP_VALUE = 0x9803u, + MTP_OP_SET_OBJECT_PROP_VALUE = 0x9804u, + MTP_OP_GET_OBJECT_PROPLIST = 0x9805u, + MTP_OP_GET_OBJECT_PROP_REFERENCES = 0x9810u, + + MTP_OP_GET_SERVICE_IDS = 0x9301u, + MTP_OP_GET_SERVICE_INFO = 0x9302u, + MTP_OP_GET_SERVICE_CAPABILITIES = 0x9303u, + MTP_OP_GET_SERVICE_PROP_DESC = 0x9304u, + + // Appendix E: Enhanced Operations + MTP_OP_GET_OBJECT_PROP_LIST = 0x9805u, + MTP_OP_SET_OBJECT_PROP_LIST = 0x9806u, + MTP_OP_GET_INTERDEPENDENT_PROP_DESC = 0x9807u, + MTP_OP_SEND_OBJECT_PROP_LIST = 0x9808u, +} mtp_operation_code_t; -// Object formats -typedef enum -{ - MTP_OBJF_UNDEFINED = 0x3000u, - MTP_OBJF_ASSOCIATION = 0x3001u, - MTP_OBJF_TEXT = 0x3004u, -} mtp_object_formats_t; +// Appendix F: Responses +typedef enum { + MTP_RESP_UNDEFINED = 0x2000u, + MTP_RESP_OK = 0x2001u, + MTP_RESP_GENERAL_ERROR = 0x2002u, + MTP_RESP_SESSION_NOT_OPEN = 0x2003u, + MTP_RESP_INVALID_TRANSACTION_ID = 0x2004u, + MTP_RESP_OPERATION_NOT_SUPPORTED = 0x2005u, + MTP_RESP_PARAMETER_NOT_SUPPORTED = 0x2006u, + MTP_RESP_INCOMPLETE_TRANSFER = 0x2007u, + MTP_RESP_INVALID_STORAGE_ID = 0x2008u, + MTP_RESP_INVALID_OBJECT_HANDLE = 0x2009u, + MTP_RESP_DEVICE_PROP_NOT_SUPPORTED = 0x200Au, + MTP_RESP_INVALID_OBJECT_FORMAT_CODE = 0x200Bu, + MTP_RESP_STORE_FULL = 0x200Cu, + MTP_RESP_OBJECT_WRITE_PROTECTED = 0x200Du, + MPT_RESC_STORE_READ_ONLY = 0x200Eu, + MTP_RESP_ACCESS_DENIED = 0x200Fu, + MTP_RESP_NO_THUMBNAIL_PRESENT = 0x2010u, + MTP_RESP_SELF_TEST_FAILED = 0x2011u, + MTP_RESP_PARTIAL_DELETION = 0x2012u, + MTP_RESP_STORE_NOT_AVAILABLE = 0x2013u, + MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014u, + MTP_RESP_NO_VALID_OBJECTINFO = 0x2015u, + MTP_RESP_INVALID_CODE_FORMAT = 0x2016u, + MTP_RESP_UNKNOWN_VENDOR_CODE = 0x2017u, + MTP_RESP_CAPTURE_ALREADY_TERMINATED = 0x2018u, + MTP_RESP_DEVICE_BUSY = 0x2019u, + MTP_RESP_INVALID_PARENT_OBJECT = 0x201Au, + MTP_RESP_INVALID_DEVICE_PROP_FORMAT = 0x201Bu, + MTP_RESP_INVALID_DEVICE_PROP_VALUE = 0x201Cu, + MTP_RESP_INVALID_PARAMETER = 0x201Du, + MTP_RESP_SESSION_ALREADY_OPEN = 0x201Eu, + MTP_RESP_TRANSACTION_CANCELLED = 0x201Fu, + MTP_RESP_SPEC_OF_DESTINATION_UNSUPPORTED = 0x2020u, + + MTP_RESP_INVALID_OBJECT_PROP_CODE = 0xA801u, + MTP_RESP_INVALID_OBJECT_PROP_FORMAT = 0xA802u, + MTP_RESP_INVALID_OBJECT_PROP_VALUE = 0xA803u, + MTP_RESP_INVALID_OBJECT_REFERENCE = 0xA804u, + MTP_RESP_GROUP_NOT_SUPPORTED = 0xA805u, + MTP_RESP_INVALID_DATASET = 0xA806u, + MTP_RESP_SPEC_BY_GROUP_UNSUPPORTED = 0xA807u, + MTP_RESP_SPEC_BY_DEPTH_UNSUPPORTED = 0xA808u, + MTP_RESP_OBJECT_TOO_LARGE = 0xA809u, + MTP_RESP_OBJECT_PROP_NOT_SUPPORTED = 0xA80Au, +} mtp_response_t; + +// Appendix G: Events +typedef enum { + MTP_EVENT_UNDEFINED = 0x4000, + MTP_EVENT_CANCEL_TRANSACTION = 0x4001, + MTP_EVENT_OBJECT_ADDED = 0x4002, + MTP_EVENT_OBJECT_REMOVED = 0x4003, + MTP_EVENT_STORE_ADDED = 0x4004, + MTP_EVENT_STORE_REMOVED = 0x4005, + MTP_EVENT_DEVICE_PROP_CHANGED = 0x4006, + MTP_EVENT_OBJECT_INFO_CHANGED = 0x4007, + MTP_EVENT_DEVICE_INFO_CHANGED = 0x4008, + MTP_EVENT_REQUEST_OBJECT_TRANSFER = 0x4009, + MTP_EVENT_STORE_FULL = 0x400Au, + MTP_EVENT_DEVICE_RESET = 0x400Bu, + MTP_EVENT_STORAGE_INFO_CHANGED = 0x400Cu, + MTP_EVENT_CAPTURE_COMPLETE = 0x400Du, + MTP_EVENT_UNREPORTED_STATUS = 0x400Eu, + MTP_EVENT_OBJECT_PROP_CHANGED = 0xC801u, + MTP_EVENT_OBJECT_PROP_DESC_CHANGED = 0xC802u, + MTP_EVENT_OBJECT_REFERENCES_CHANGED = 0xC803u, +} mtp_event_code_t; // Predefined Object handles typedef enum @@ -210,50 +602,101 @@ typedef enum MTP_MODE_GET_SET = 0x01u, } mtp_mode_get_set_t; +typedef enum { + MTP_STORAGE_TYPE_UNDEFINED = 0x0000u, + MTP_STORAGE_TYPE_FIXED_ROM = 0x0001u, + MTP_STORAGE_TYPE_REMOVABLE_ROM = 0x0002u, + MTP_STORAGE_TYPE_FIXED_RAM = 0x0003u, + MTP_STORAGE_TYPE_REMOVABLE_RAM = 0x0004u, +} mtp_storage_type_t; + +typedef enum { + MTP_FILESYSTEM_TYPE_UNDEFINED = 0x0000u, + MTP_FILESYSTEM_TYPE_GENERIC_FLAT = 0x0001u, + MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL = 0x0002u, + MTP_FILESYSTEM_TYPE_DCF = 0x0003u, +} mtp_filesystem_type_t; + +typedef enum { + MTP_ACCESS_CAPABILITY_READ_WRITE = 0x0000u, + MTP_ACCESS_CAPABILITY_READ_ONLY_WITHOUT_OBJECT_DELETION = 0x0001u, + MTP_ACCESS_CAPABILITY_READ_ONLY_WITH_OBJECT_DELETION = 0x0002u, +} mtp_access_capability_t; + +typedef enum { + MTP_PROTECTION_STATUS_NO_PROTECTION = 0x0000u, + MTP_PROTECTION_STATUS_READ_ONLY = 0x0001u, + MTP_PROTECTION_STATUS_READ_ONLY_DATA = 0x8002u, + MTP_PROTECTION_NON_TRANSFERABLE_DATA = 0x8003u, +} mtp_protection_status_t; + +typedef enum { + MTP_ASSOCIATION_UNDEFINED = 0x0000u, + MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001u, + MTP_ASSOCIATION_GENERIC_ALBUM = 0x0002u, + MTP_ASSOCIATION_TIME_SEQUENCE = 0x0003u, + MTP_ASSOCIATION_HORIZONTAL_PANORAMIC = 0x0004u, + MTP_ASSOCIATION_VERTICAL_PANORAMIC = 0x0005u, + MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u, +} mtp_association_t; + + tu_static const uint16_t mtp_operations_supported[] = { - MTP_OPEC_GET_DEVICE_INFO, - MTP_OPEC_OPEN_SESSION, - MTP_OPEC_CLOSE_SESSION, - MTP_OPEC_GET_STORAGE_IDS, - MTP_OPEC_GET_STORAGE_INFO, - MTP_OPEC_GET_NUM_OBJECTS, - MTP_OPEC_GET_OBJECT_HANDLES, - MTP_OPEC_GET_OBJECT_INFO, - MTP_OPEC_GET_OBJECT, - MTP_OPEC_DELETE_OBJECT, - MTP_OPEC_SEND_OBJECT_INFO, - MTP_OPEC_SEND_OBJECT, - MTP_OPEC_FORMAT_STORE, - MTP_OPEC_RESET_DEVICE, - MTP_OPEC_GET_DEVICE_PROP_DESC, - MTP_OPEC_GET_DEVICE_PROP_VALUE, - MTP_OPEC_SET_DEVICE_PROP_VALUE, + MTP_OP_GET_DEVICE_INFO, + MTP_OP_OPEN_SESSION, + MTP_OP_CLOSE_SESSION, + MTP_OP_GET_STORAGE_IDS, + MTP_OP_GET_STORAGE_INFO, + MTP_OP_GET_NUM_OBJECTS, + MTP_OP_GET_OBJECT_HANDLES, + MTP_OP_GET_OBJECT_INFO, + MTP_OP_GET_OBJECT, + MTP_OP_DELETE_OBJECT, + MTP_OP_SEND_OBJECT_INFO, + MTP_OP_SEND_OBJECT, + MTP_OP_FORMAT_STORE, + MTP_OP_RESET_DEVICE, + MTP_OP_GET_DEVICE_PROP_DESC, + MTP_OP_GET_DEVICE_PROP_VALUE, + MTP_OP_SET_DEVICE_PROP_VALUE, }; tu_static const uint16_t mtp_events_supported[] = { - MTP_EVTC_OBJECT_ADDED, + MTP_EVENT_OBJECT_ADDED, }; tu_static const uint16_t mtp_device_properties_supported[] = { - MTP_DEVP_DEVICE_FRIENDLY_NAME, + MTP_DEV_PROP_DEVICE_FRIENDLY_NAME, }; tu_static const uint16_t mtp_capture_formats[] = { - MTP_OBJF_UNDEFINED, - MTP_OBJF_ASSOCIATION, - MTP_OBJF_TEXT, + MTP_OBJ_FORMAT_UNDEFINED, + MTP_OBJ_FORMAT_ASSOCIATION, + MTP_OBJ_FORMAT_TEXT, }; tu_static const uint16_t mtp_playback_formats[] = { - MTP_OBJF_UNDEFINED, - MTP_OBJF_ASSOCIATION, - MTP_OBJF_TEXT, + MTP_OBJ_FORMAT_UNDEFINED, + MTP_OBJ_FORMAT_ASSOCIATION, + MTP_OBJ_FORMAT_TEXT, }; //--------------------------------------------------------------------+ // Data structures //--------------------------------------------------------------------+ +#define MTP_GENERIC_DATA_BLOCK_LENGTH 12 +#define MTP_MAX_PACKET_SIZE 512 + +// PTP/MTP Generic container +typedef struct TU_ATTR_PACKED { + uint32_t container_length; + uint16_t container_type; + uint16_t code; + uint32_t transaction_id; + uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; +} mtp_generic_container_t; + // DeviceInfo Dataset #define MTP_EXTENSIONS "microsoft.com: 1.0; " typedef struct TU_ATTR_PACKED { @@ -360,75 +803,6 @@ typedef struct TU_ATTR_PACKED { uint32_t parent_object_handle; } mtp_basic_object_info_t; -//--------------------------------------------------------------------+ -// Definitions -//--------------------------------------------------------------------+ - -typedef enum { - MTP_STORAGE_TYPE_UNDEFINED = 0x0000u, - MTP_STORAGE_TYPE_FIXED_ROM = 0x0001u, - MTP_STORAGE_TYPE_REMOVABLE_ROM = 0x0002u, - MTP_STORAGE_TYPE_FIXED_RAM = 0x0003u, - MTP_STORAGE_TYPE_REMOVABLE_RAM = 0x0004u, -} mtp_storage_type_t; - -typedef enum { - MTP_FILESYSTEM_TYPE_UNDEFINED = 0x0000u, - MTP_FILESYSTEM_TYPE_GENERIC_FLAT = 0x0001u, - MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL = 0x0002u, - MTP_FILESYSTEM_TYPE_DCF = 0x0003u, -} mtp_filesystem_type_t; - -typedef enum { - MTP_ACCESS_CAPABILITY_READ_WRITE = 0x0000u, - MTP_ACCESS_CAPABILITY_READ_ONLY_WITHOUT_OBJECT_DELETION = 0x0001u, - MTP_ACCESS_CAPABILITY_READ_ONLY_WITH_OBJECT_DELETION = 0x0002u, -} mtp_access_capability_t; - -typedef enum { - MTP_PROTECTION_STATUS_NO_PROTECTION = 0x0000u, - MTP_PROTECTION_STATUS_READ_ONLY = 0x0001u, - MTP_PROTECTION_STATUS_READ_ONLY_DATA = 0x8002u, - MTP_PROTECTION_NON_TRANSFERABLE_DATA = 0x8003u, -} mtp_protection_status_t; - -typedef enum { - MTP_ASSOCIATION_UNDEFINED = 0x0000u, - MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001u, - MTP_ASSOCIATION_GENERIC_ALBUM = 0x0002u, - MTP_ASSOCIATION_TIME_SEQUENCE = 0x0003u, - MTP_ASSOCIATION_HORIZONTAL_PANORAMIC = 0x0004u, - MTP_ASSOCIATION_VERTICAL_PANORAMIC = 0x0005u, - MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u, -} mtp_association_t; - -// Responses -typedef enum { -// Supported ResponseCode - MTP_RESC_UNDEFINED = 0x2000u, - MTP_RESC_OK = 0x2001u, - MTP_RESC_GENERAL_ERROR = 0x2002u, - MTP_RESC_SESSION_NOT_OPEN = 0x2003u, - MTP_RESC_INVALID_TRANSACTION_ID = 0x2004u, - MTP_RESC_OPERATION_NOT_SUPPORTED = 0x2005u, - MTP_RESC_PARAMETER_NOT_SUPPORTED = 0x2006u, - MTP_RESC_INCOMPLETE_TRANSFER = 0x2007u, - MTP_RESC_INVALID_STORAGE_ID = 0x2008u, - MTP_RESC_INVALID_OBJECT_HANDLE = 0x2009u, - MTP_RESC_STORE_FULL = 0x200Cu, - MTP_RESC_OBJECT_WRITE_PROTECTED = 0x200Du, - MTP_RESC_STORE_NOT_AVAILABLE = 0x2013u, - MTP_RESC_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014u, - MTP_RESC_NO_VALID_OBJECTINFO = 0x2015u, - MTP_RESC_DEVICE_BUSY = 0x2019u, - MTP_RESC_INVALID_PARENT_OBJECT = 0x201Au, - MTP_RESC_INVALID_DEVICE_PROP_FORMAT = 0x201Bu, - MTP_RESC_INVALID_DEVICE_PROP_VALUE = 0x201Cu, - MTP_RESC_INVALID_PARAMETER = 0x201Du, - MTP_RESC_SESSION_ALREADY_OPEN = 0x201Eu, - MTP_RESC_TRANSACTION_CANCELLED = 0x201Fu, -} mtp_response_t; - #ifdef __cplusplus } #endif diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 854d4f10e..68a7c6c2a 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -147,9 +147,9 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16 TU_LOG_DRV(" MTP mtpd_open\n"); tusb_desc_endpoint_t const *ep_desc; // only support SCSI's BOT protocol - TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass && - MTP_SUBCLASS == itf_desc->bInterfaceSubClass && - MTP_PROTOCOL_STILL_IMAGE == itf_desc->bInterfaceProtocol, 0); + TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass && + MTP_SUBCLASS_STILL_IMAGE == itf_desc->bInterfaceSubClass && + MTP_PROTOCOL_PIMA_15470 == itf_desc->bInterfaceProtocol, 0); // mtp driver length is fixed uint16_t const mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); @@ -205,7 +205,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t len = 4; _mtpd_device_status_res.wLength = len; // Cancel is synchronous, always answer OK - _mtpd_device_status_res.code = MTP_RESC_OK; + _mtpd_device_status_res.code = MTP_RESP_OK; TU_ASSERT( tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len) ); break; @@ -243,7 +243,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t { _mtpd_itf.phase = MTP_PHASE_RESPONSE; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.code = MTP_RESP_OK; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.transaction_id = _mtpd_ctx.transaction_id; if (_mtpd_ctx.session_id != 0) @@ -413,55 +413,55 @@ mtp_phase_type_t mtpd_handle_cmd(void) { TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); _mtpd_ctx.transaction_id = _mtpd_gct.transaction_id; - if (_mtpd_gct.code != MTP_OPEC_SEND_OBJECT) + if (_mtpd_gct.code != MTP_OP_SEND_OBJECT) _mtpd_soi.object_handle = 0; switch(_mtpd_gct.code) { - case MTP_OPEC_GET_DEVICE_INFO: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_INFO\n"); + case MTP_OP_GET_DEVICE_INFO: + TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); return mtpd_handle_cmd_get_device_info(); - case MTP_OPEC_OPEN_SESSION: - TU_LOG_DRV(" MTP command: MTP_OPEC_OPEN_SESSION\n"); + case MTP_OP_OPEN_SESSION: + TU_LOG_DRV(" MTP command: MTP_OP_OPEN_SESSION\n"); return mtpd_handle_cmd_open_session(); - case MTP_OPEC_CLOSE_SESSION: - TU_LOG_DRV(" MTP command: MTP_OPEC_CLOSE_SESSION\n"); + case MTP_OP_CLOSE_SESSION: + TU_LOG_DRV(" MTP command: MTP_OP_CLOSE_SESSION\n"); return mtpd_handle_cmd_close_session(); - case MTP_OPEC_GET_STORAGE_IDS: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_STORAGE_IDS\n"); + case MTP_OP_GET_STORAGE_IDS: + TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); return mtpd_handle_cmd_get_storage_ids(); - case MTP_OPEC_GET_STORAGE_INFO: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_STORAGE_INFO for ID=%lu\n", _mtpd_gct.data[0]); + case MTP_OP_GET_STORAGE_INFO: + TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", _mtpd_gct.data[0]); return mtpd_handle_cmd_get_storage_info(); - case MTP_OPEC_GET_OBJECT_HANDLES: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT_HANDLES\n"); + case MTP_OP_GET_OBJECT_HANDLES: + TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_HANDLES\n"); return mtpd_handle_cmd_get_object_handles(); - case MTP_OPEC_GET_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT_INFO\n"); + case MTP_OP_GET_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_INFO\n"); return mtpd_handle_cmd_get_object_info(); - case MTP_OPEC_GET_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT\n"); + case MTP_OP_GET_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT\n"); return mtpd_handle_cmd_get_object(); - case MTP_OPEC_DELETE_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OPEC_DELETE_OBJECT\n"); + case MTP_OP_DELETE_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OP_DELETE_OBJECT\n"); return mtpd_handle_cmd_delete_object(); - case MTP_OPEC_GET_DEVICE_PROP_DESC: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_PROP_DESC\n"); + case MTP_OP_GET_DEVICE_PROP_DESC: + TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_DESC\n"); return mtpd_handle_cmd_get_device_prop_desc(); - case MTP_OPEC_GET_DEVICE_PROP_VALUE: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_DEVICE_PROP_VALUE\n"); + case MTP_OP_GET_DEVICE_PROP_VALUE: + TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_VALUE\n"); return mtpd_handle_cmd_get_device_prop_value(); - case MTP_OPEC_SEND_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT_INFO\n"); + case MTP_OP_SEND_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO\n"); return mtpd_handle_cmd_send_object_info(); - case MTP_OPEC_SEND_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT\n"); + case MTP_OP_SEND_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT\n"); return mtpd_handle_cmd_send_object(); - case MTP_OPEC_FORMAT_STORE: - TU_LOG_DRV(" MTP command: MTP_OPEC_FORMAT_STORE\n"); + case MTP_OP_FORMAT_STORE: + TU_LOG_DRV(" MTP command: MTP_OP_FORMAT_STORE\n"); return mtpd_handle_cmd_format_store(); default: - TU_LOG_DRV(" MTP command: MTP_OPEC_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); return false; } return true; @@ -474,17 +474,17 @@ mtp_phase_type_t mtpd_handle_data(void) switch(_mtpd_gct.code) { - case MTP_OPEC_GET_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OPEC_GET_OBJECT-DATA_IN\n"); + case MTP_OP_GET_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT-DATA_IN\n"); return mtpd_handle_dti_get_object(); - case MTP_OPEC_SEND_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT_INFO-DATA_OUT\n"); + case MTP_OP_SEND_OBJECT_INFO: + TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO-DATA_OUT\n"); return mtpd_handle_dto_send_object_info(); - case MTP_OPEC_SEND_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OPEC_SEND_OBJECT-DATA_OUT\n"); + case MTP_OP_SEND_OBJECT: + TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT-DATA_OUT\n"); return mtpd_handle_dto_send_object(); default: - TU_LOG_DRV(" MTP command: MTP_OPEC_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); return false; } return true; @@ -496,7 +496,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_info_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_DEVICE_INFO; + _mtpd_gct.code = MTP_OP_GET_DEVICE_INFO; mtp_device_info_t *d = (mtp_device_info_t *)_mtpd_gct.data; d->standard_version = 100; d->mtp_vendor_extension_id = 0x06; @@ -528,7 +528,7 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) uint32_t session_id = _mtpd_gct.data[0]; mtp_response_t res = tud_mtp_storage_open_session(&session_id); - if (res == MTP_RESC_SESSION_ALREADY_OPEN) + if (res == MTP_RESP_SESSION_ALREADY_OPEN) { _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; @@ -540,13 +540,13 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) } mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_ctx.session_id = session_id; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.code = MTP_RESP_OK; return MTP_PHASE_RESPONSE; } @@ -573,11 +573,11 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) uint32_t storage_id; mtp_response_t res = tud_mtp_get_storage_id(&storage_id); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_ids_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_STORAGE_IDS; + _mtpd_gct.code = MTP_OP_GET_STORAGE_IDS; mtp_storage_ids_t *d = (mtp_storage_ids_t *)_mtpd_gct.data; if (storage_id == 0) { @@ -603,11 +603,11 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_info_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_STORAGE_INFO; + _mtpd_gct.code = MTP_OP_GET_STORAGE_INFO; mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)_mtpd_gct.data); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = _mtpd_gct.container_length; return MTP_PHASE_DATA_IN; @@ -621,20 +621,20 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(uint32_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_OBJECT_HANDLES; + _mtpd_gct.code = MTP_OP_GET_OBJECT_HANDLES; _mtpd_gct.data[0] = 0; mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESC_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) return phase; //list of all object handles on all storages, not managed - if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESC_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESP_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) return phase; tud_mtp_storage_object_done(); uint32_t next_child_handle = 0; while(true) { mtp_response_t res = tud_mtp_storage_association_get_object_handle(storage_id, parent_object_handle, &next_child_handle); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; if (next_child_handle == 0) break; mtpd_gct_append_object_handle(next_child_handle); @@ -653,10 +653,10 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_object_info_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_OBJECT_INFO; + _mtpd_gct.code = MTP_OP_GET_OBJECT_INFO; mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)_mtpd_gct.data); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = _mtpd_gct.container_length; return MTP_PHASE_DATA_IN; @@ -676,10 +676,10 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) mtp_phase_type_t phase; uint32_t file_size = 0; res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + file_size; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_OBJECT; + _mtpd_gct.code = MTP_OP_GET_OBJECT; uint32_t buffer_size; uint32_t read_count; @@ -689,7 +689,7 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) // First data block: include container header buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_GENERIC_DATA_BLOCK_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_GENERIC_DATA_BLOCK_LENGTH; res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = MTP_GENERIC_DATA_BLOCK_LENGTH + read_count; } else @@ -697,7 +697,7 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) // Successive data block: consider only container data buffer_size = (MTP_MAX_PACKET_SIZE / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE; res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = read_count; } @@ -718,10 +718,10 @@ mtp_phase_type_t mtpd_handle_cmd_delete_object(void) mtp_response_t res = tud_mtp_storage_object_delete(object_handle); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.code = MTP_RESP_OK; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } @@ -735,11 +735,11 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) switch(device_prop_code) { - case MTP_DEVP_DEVICE_FRIENDLY_NAME: + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: { TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_DEVICE_PROP_DESC; + _mtpd_gct.code = MTP_OP_GET_DEVICE_PROP_DESC; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_prop_desc_t); mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)_mtpd_gct.data; d->device_property_code = (uint16_t)(device_prop_code); @@ -756,7 +756,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) } _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_PARAMETER_NOT_SUPPORTED; + _mtpd_gct.code = MTP_RESP_PARAMETER_NOT_SUPPORTED; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } @@ -770,18 +770,18 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OPEC_GET_DEVICE_PROP_VALUE; + _mtpd_gct.code = MTP_OP_GET_DEVICE_PROP_VALUE; switch(device_prop_code) { // TODO support more device properties - case MTP_DEVP_DEVICE_FRIENDLY_NAME: + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: mtpd_gct_append_wstring(CFG_TUD_MODEL); _mtpd_itf.queued_len = _mtpd_gct.container_length; return MTP_PHASE_DATA_IN; default: _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_PARAMETER_NOT_SUPPORTED; + _mtpd_gct.code = MTP_RESP_PARAMETER_NOT_SUPPORTED; return MTP_PHASE_RESPONSE; } } @@ -800,7 +800,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object_info(void) uint32_t new_object_handle = 0; mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_t *)_mtpd_gct.data); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; // Save send_object_info _mtpd_soi.object_handle = new_object_handle; @@ -808,7 +808,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object_info(void) // Response _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + 3 * sizeof(uint32_t); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.code = MTP_RESP_OK; _mtpd_gct.data[0] = _mtpd_soi.storage_id; _mtpd_gct.data[1] = _mtpd_soi.parent_object_handle; _mtpd_gct.data[2] = _mtpd_soi.object_handle; @@ -835,7 +835,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) { mtp_response_t res = tud_mtp_storage_object_write(_mtpd_soi.object_handle, buffer, buffer_size); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESC_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; } if (!_mtpd_itf.xfer_completed) @@ -849,7 +849,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_OK; + _mtpd_gct.code = MTP_RESP_OK; return MTP_PHASE_RESPONSE; } @@ -877,7 +877,7 @@ mtp_phase_type_t mtpd_chk_session_open(const char *func_name) { TU_LOG_DRV(" MTP error: %s session not open\n", func_name); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESC_SESSION_NOT_OPEN; + _mtpd_gct.code = MTP_RESP_SESSION_NOT_OPEN; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } diff --git a/src/device/usbd.h b/src/device/usbd.h index b89a0200b..921d5c0d0 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -279,7 +279,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ // Interface number, string index, EP Out & EP In address, EP size #define TUD_MTP_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_polling_interval, _epout, _epin, _epsize) \ /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUSB_CLASS_IMAGE, MTP_SUBCLASS, MTP_PROTOCOL_STILL_IMAGE, _stridx,\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUSB_CLASS_IMAGE, MTP_SUBCLASS_STILL_IMAGE, MTP_PROTOCOL_PIMA_15470, _stridx,\ /* Endpoint Interrupt */\ 7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_polling_interval,\ /* Endpoint Out */\ -- cgit v1.3.1 From 4db2bdad07f55b03df8c40928a2b56536fa823e3 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Sep 2025 14:08:54 +0200 Subject: echi: fix NXP USBPHY disconnection detection Signed-off-by: HiFiPhile --- src/portable/ehci/ehci.c | 60 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index b372fe635..76b0142b2 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -38,6 +38,11 @@ #include "ehci_api.h" #include "ehci.h" +// NXP specific fixes +#if TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX, OPT_MCU_LPC55, OPT_MCU_MCXN9) +#include "fsl_device_registers.h" +#endif + //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ @@ -179,6 +184,36 @@ static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) { } } +#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U)) +static void nxp_usbphy_disconn_detector_set(uint8_t port, bool enable) { + // unify naming convention +#if !defined(USBPHY1) && defined(USBPHY) + #define USBPHY1 USBPHY +#endif + + if (port == 0) { + if (enable) { + USBPHY1->CTRL_SET = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK; + } else { + USBPHY1->CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK; + } + } +#if FSL_FEATURE_SOC_USBPHY_COUNT > 1U + else if (port == 1) { + if (enable) { + USBPHY2->CTRL_SET = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK; + } else { + USBPHY2->CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT_MASK; + } + } +#endif + +#if !defined(USBPHY1) && defined(USBPHY) + #undef USBPHY1 +#endif +} +#endif + //--------------------------------------------------------------------+ // HCD API //--------------------------------------------------------------------+ @@ -213,16 +248,21 @@ void hcd_port_reset_end(uint8_t rhport) { (void) rhport; ehci_registers_t* regs = ehci_data.regs; - // skip if reset is already complete - if (!regs->portsc_bm.port_reset) { - return; - } + // stop reset only if is not complete yet + if (regs->portsc_bm.port_reset) { + // mask out all change bits since they are Write 1 to clear + uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C; + portsc &= ~EHCI_PORTSC_MASK_PORT_RESET; - // mask out all change bits since they are Write 1 to clear - uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C; - portsc &= ~EHCI_PORTSC_MASK_PORT_RESET; + regs->portsc = portsc; + } - regs->portsc = portsc; +#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U)) + // Enable disconnect detector for highspeed device only + if (hcd_port_speed_get(rhport) == TUSB_SPEED_HIGH) { + nxp_usbphy_disconn_detector_set(rhport, true); + } +#endif } bool hcd_port_connect_status(uint8_t rhport) { @@ -579,6 +619,10 @@ void port_connect_status_change_isr(uint8_t rhport) { hcd_event_device_attach(rhport, true); } else // device unplugged { +#if ((defined FSL_FEATURE_SOC_USBPHY_COUNT) && (FSL_FEATURE_SOC_USBPHY_COUNT > 0U)) + // Disable disconnect detector + nxp_usbphy_disconn_detector_set(rhport, false); +#endif hcd_event_device_remove(rhport, true); } } -- cgit v1.3.1 From 99bee6a900db60cf232766531e785108f50e614d Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Sep 2025 14:25:44 +0200 Subject: ehci: fix removed qhd get reused Signed-off-by: HiFiPhile --- src/host/usbh.c | 2 +- src/portable/ehci/ehci.c | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/host/usbh.c b/src/host/usbh.c index 1c63ac712..d09874d6e 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -844,7 +844,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t const uint8_t ep_status = tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction); TU_ASSERT(hcd_edpt_xfer(rhport, daddr, ep_status, NULL, 0)); break; - } + } case CONTROL_STAGE_ACK: { // Abort all pending transfers if SET_CONFIGURATION request diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c index 76b0142b2..953483583 100644 --- a/src/portable/ehci/ehci.c +++ b/src/portable/ehci/ehci.c @@ -35,6 +35,7 @@ #include "host/hcd.h" #include "host/usbh.h" +#include "host/usbh_pvt.h" #include "ehci_api.h" #include "ehci.h" @@ -866,14 +867,21 @@ static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) { } ehci_qhd_t *qhd_pool = ehci_data.qhd_pool; + + // protect qhd_pool since 'used' and 'removing' can be changed in isr + ehci_qhd_t *result = NULL; + usbh_spin_lock(false); for (uint32_t i = 0; i < QHD_MAX; i++) { if ((qhd_pool[i].dev_addr == dev_addr) && - ep_addr == qhd_ep_addr(&qhd_pool[i])) { - return &qhd_pool[i]; + ep_addr == qhd_ep_addr(&qhd_pool[i]) && + qhd_pool[i].used && !qhd_pool[i].removing) { + result = &qhd_pool[i]; + break; } } + usbh_spin_unlock(false); - return NULL; + return result; } // Init queue head with endpoint descriptor -- cgit v1.3.1 From 8515e47ad9980f8ad380913bda72425b1bb83648 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Sep 2025 16:49:55 +0200 Subject: Update comment Signed-off-by: HiFiPhile --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 770baa05a..95f40269a 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -268,7 +268,10 @@ #endif #ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP - // Defaults to double-buffered isochronous endpoints on devices with >1KB PMA + // Default configuration for double-buffered isochronous endpoints: + // - Enable double buffering on devices with >1KB Packet Memory Area (PMA) + // to improve isochronous transfer reliability and performance + // - Disable on devices with limited PMA to conserve memory space #if FSDEV_PMA_SIZE > 1024u #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1 #else @@ -277,12 +280,16 @@ #endif #if FSDEV_HAS_SBUF_ISO != 0 && CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP == 0 - // If hardware has SBUF_ISO bit single-buffered endpoints consume only - // one half of endpoint pair register. + // SBUF_ISO configuration: + // - Some STM32 devices have special hardware support for single-buffered isochronous endpoints + // - When SBUF_ISO bit is available and double buffering is disabled: + // Enable SBUF_ISO to optimize endpoint register usage (one half of endpoint pair register) #define FSDEV_USE_SBUF_ISO 1 #else - // Otherwise it consumes entire endpoint pair but we can at least save - // memory by configuring the same buffer twice. + // When either: + // - Hardware doesn't support SBUF_ISO feature, or + // - Double buffering is enabled for isochronous endpoints + // We must use the entire endpoint pair register #define FSDEV_USE_SBUF_ISO 0 #endif -- cgit v1.3.1 From cf6cbf0d1aca1985dcc8d1b88279028ec044e7a8 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Sep 2025 21:30:53 +0200 Subject: Fix AT32 compile after #3152 Signed-off-by: HiFiPhile --- src/portable/st/stm32_fsdev/fsdev_at32.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_at32.h b/src/portable/st/stm32_fsdev/fsdev_at32.h index 3a785bbbd..f7ee89995 100644 --- a/src/portable/st/stm32_fsdev/fsdev_at32.h +++ b/src/portable/st/stm32_fsdev/fsdev_at32.h @@ -36,9 +36,14 @@ #endif #define FSDEV_PMA_SIZE (512u) +#define FSDEV_USE_SBUF_ISO 0 #define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL) #define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL) +#ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP + #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 +#endif + /**************************** ISTR interrupt events *************************/ #define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ #define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ -- cgit v1.3.1 From cc19c02f860f96299ceccdfcaf1befb308c9fab8 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 15 Sep 2025 15:47:06 +0700 Subject: dwc2: wait for ahb idle before core reset --- .idea/debugServers/AT32F423VCT7.xml | 13 +++++++++++++ .idea/debugServers/ST_LINK.xml | 13 +++++++++++++ .idea/debugServers/at32f403acgu7.xml | 13 +++++++++++++ .idea/debugServers/max32690.xml | 13 +++++++++++++ .idea/debugServers/s3.xml | 6 ++++++ .idea/debugServers/wch_riscv.xml | 14 ++++++++++++++ src/common/tusb_common.h | 4 ++-- src/portable/synopsys/dwc2/dwc2_common.c | 16 +++++++++++----- 8 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 .idea/debugServers/AT32F423VCT7.xml create mode 100644 .idea/debugServers/ST_LINK.xml create mode 100644 .idea/debugServers/at32f403acgu7.xml create mode 100644 .idea/debugServers/max32690.xml create mode 100644 .idea/debugServers/s3.xml create mode 100644 .idea/debugServers/wch_riscv.xml (limited to 'src') diff --git a/.idea/debugServers/AT32F423VCT7.xml b/.idea/debugServers/AT32F423VCT7.xml new file mode 100644 index 000000000..38b3d76ef --- /dev/null +++ b/.idea/debugServers/AT32F423VCT7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/ST_LINK.xml b/.idea/debugServers/ST_LINK.xml new file mode 100644 index 000000000..7c21d3879 --- /dev/null +++ b/.idea/debugServers/ST_LINK.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/at32f403acgu7.xml b/.idea/debugServers/at32f403acgu7.xml new file mode 100644 index 000000000..9df65140f --- /dev/null +++ b/.idea/debugServers/at32f403acgu7.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/max32690.xml b/.idea/debugServers/max32690.xml new file mode 100644 index 000000000..3551f591e --- /dev/null +++ b/.idea/debugServers/max32690.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/s3.xml b/.idea/debugServers/s3.xml new file mode 100644 index 000000000..a03abf744 --- /dev/null +++ b/.idea/debugServers/s3.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/debugServers/wch_riscv.xml b/.idea/debugServers/wch_riscv.xml new file mode 100644 index 000000000..2e147f1b6 --- /dev/null +++ b/.idea/debugServers/wch_riscv.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 1fb93da11..e35d3e6fe 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -168,8 +168,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); } //------------- Bits -------------// -TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); } +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); } TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; } //------------- Min -------------// diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index d7d157149..5ff18ab94 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -45,20 +45,26 @@ // //-------------------------------------------------------------------- static void reset_core(dwc2_regs_t* dwc2) { + // The software must check that bit 31 in this register is set to 1 (AHB Master is Idle) before starting any operation + while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) { + } + // load gsnpsid (it is not readable after reset is asserted) - uint32_t gsnpsid = dwc2->gsnpsid; + const uint32_t gsnpsid = dwc2->gsnpsid; // reset core dwc2->grstctl |= GRSTCTL_CSRST; if ((gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { - // prior v4.20a CSRST is self-clearing + // prior v4.20a: CSRST is self-clearing and the core clears this bit after all the necessary logic is reset in + // the core, which can take several clocks, depending on the current state of the core. Once this bit has been + // cleared, the software must wait at least 3 PHY clocks before accessing the PHY domain (synchronization delay). while (dwc2->grstctl & GRSTCTL_CSRST) {} } else { - // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking. - // CSRST must also be explicitly cleared + // From v4.20a: CSRST bit is write only. The application must clear this bit after checking the bit 29 of this + // register i.e Core Soft Reset Done CSRT_DONE (w1c) while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {} - dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE; + dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE; } while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE -- cgit v1.3.1 From 6f06effd03d1163b3e4069d61cf158fc55eb9133 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 15 Sep 2025 16:18:07 +0700 Subject: add nrf54 to dwc2 info --- src/portable/synopsys/dwc2/dwc2_info.md | 116 ++++++++++++++++---------------- src/portable/synopsys/dwc2/dwc2_info.py | 1 + 2 files changed, 59 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md index e6b7820bc..f655e4dba 100644 --- a/src/portable/synopsys/dwc2/dwc2_info.md +++ b/src/portable/synopsys/dwc2/dwc2_info.md @@ -1,58 +1,58 @@ -| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | -|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| -| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | -| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | -| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | -| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | -| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | -| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | -| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | -| - single_point | hub | hub | hub | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | -| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | -| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | -| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | -| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | -| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | -| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | -| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | -| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | -| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | -| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | -| - optional_feature_removed | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | -| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | -| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | -| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | -| - num_dev_period_in_ep | 0 | 0 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - ahb_freq_min | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | -| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | -| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - vbus_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | -| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | -| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | -| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | -| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| | AT32 F405 FS | AT32 F405 HS | AT32 F415 | BCM2711 (Pi4) | EFM32GG | ESP32-S2/S3 | ESP32-P4 | nRF54 | ST F207/F407/411/429 FS | ST F407/429 HS | ST F412/76x FS | ST F723/L4P5 FS | ST F723 HS | ST F76x HS | ST H743/H750 | ST L476 FS | ST U5A5/H7RS/N6 HS | XMC4500 | GD32VF103 | +|:---------------------------|:---------------|:---------------|:------------|:----------------|:-------------|:--------------|:-------------|:-------------|:--------------------------|:-----------------|:-----------------|:------------------|:-------------|:-------------|:---------------|:-------------|:---------------------|:-------------|:------------| +| GUID | 0x00002000 | 0x00000000 | 0x00001000 | 0x2708A000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00001200 | 0x00001100 | 0x00002000 | 0x00003000 | 0x00003100 | 0x00002100 | 0x00002300 | 0x00002000 | 0x00005000 | 0x00AEC000 | 0x00001000 | +| GSNPSID | 0x4F54400A | 0x4F54400A | 0x4F54400A | 0x4F54280A | 0x4F54330A | 0x4F54400A | 0x4F54400A | 0x4F54430A | 0x4F54281A | 0x4F54281A | 0x4F54320A | 0x4F54330A | 0x4F54330A | 0x4F54320A | 0x4F54330A | 0x4F54310A | 0x4F54411A | 0x4F54292A | 0x00000000 | +| - specs version | 4.00a | 4.00a | 4.00a | 2.80a | 3.30a | 4.00a | 4.00a | 4.30a | 2.81a | 2.81a | 3.20a | 3.30a | 3.30a | 3.20a | 3.30a | 3.10a | 4.11a | 2.92a | 0.00W | +| GHWCFG1 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0xAA555000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | 0x00000000 | +| GHWCFG2 | 0x228FDD00 | 0x229FDDD0 | 0x228DCD00 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x228BFC72 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x228F5930 | 0x00000000 | +| - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | +| - arch | Slave only | DMA internal | Slave only | DMA internal | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | DMA internal | Slave only | +| - single_point | hub | hub | hub | hub | hub | n/a | hub | n/a | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | n/a | hub | +| - hs_phy_type | n/a | UTMI+/ULPI | n/a | UTMI+ | n/a | n/a | UTMI+/ULPI | UTMI+ | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | +| - fs_phy_type | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Shared ULPI | n/a | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | Dedicated | n/a | +| - num_dev_ep | 7 | 7 | 3 | 7 | 6 | 6 | 15 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - num_host_ch | 15 | 15 | 7 | 7 | 13 | 7 | 15 | 15 | 7 | 11 | 11 | 11 | 15 | 15 | 15 | 11 | 15 | 13 | 0 | +| - period_channel_support | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - mul_proc_intrpt | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - nptx_q_depth | 8 | 8 | 8 | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - ptx_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | +| - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | +| - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| GHWCFG3 | 0x020004E8 | 0x03F006E8 | 0x020004E8 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x0BEAC0E8 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x027A01E5 | 0x00000000 | +| - xfer_size_width | 8 | 8 | 8 | 8 | 8 | 5 | 5 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 5 | 0 | +| - packet_size_width | 6 | 6 | 6 | 6 | 6 | 3 | 3 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 0 | +| - otg_enable | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - i2c_enable | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | +| - vendor_ctrl_itf | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | +| - optional_feature_removed | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - synch_reset | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - otg_adp_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - otg_enable_hsic | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - battery_charger_support | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | +| - lpm_mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | +| - dfifo_depth | 512 | 1008 | 512 | 4080 | 498 | 200 | 896 | 3050 | 512 | 1012 | 512 | 512 | 1006 | 1006 | 952 | 512 | 952 | 634 | 0 | +| GHWCFG4 | 0x1FF0A020 | 0x1FF0A020 | 0x0000000F | 0x1FF00020 | 0x1BF08030 | 0xD3F0A030 | 0xDFF1A030 | 0x1E10AA60 | 0x0FF08030 | 0x17F00030 | 0x17F08030 | 0x17F08030 | 0x23F00030 | 0x23F00030 | 0xE3F00030 | 0x17F08030 | 0xE2103E30 | 0xDBF08030 | 0x00000000 | +| - num_dev_period_in_ep | 0 | 0 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - partial_powerdown | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - ahb_freq_min | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - extended_hibernation | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - reserved8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - enhanced_lpm_support1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - service_interval_flow | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - ipg_isoc_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - acg_support | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - enhanced_lpm_support | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +| - phy_data_width | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8/16 bit | 8 bit | 8/16 bit | 8/16 bit | 8 bit | 8 bit | 8 bit | 8/16 bit | 8 bit | 8/16 bit | 8 bit | +| - ctrl_ep_num | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +| - iddg_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - vbus_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - a_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - b_valid_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - session_end_filter | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | +| - dedicated_fifos | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | +| - num_dev_in_eps | 7 | 7 | 0 | 7 | 6 | 4 | 7 | 7 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 6 | 0 | +| - dma_desc_enable | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | +| - dma_desc_dynamic | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index a90150632..f6bd2785a 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -16,6 +16,7 @@ dwc2_reg_value = { 'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030], 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030], 'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030], + 'nRF54': [0, 0x4F54430A, 0xAA555000, 0x228BFC72, 0x0BEAC0E8, 0x1E10AA60], 'ST F207/F407/411/429 FS': [0x1200, 0x4F54281A, 0, 0x229DCD20, 0x020001E8, 0x0FF08030], 'ST F407/429 HS': [0x1100, 0x4F54281A, 0, 0x229ED590, 0x03F403E8, 0x17F00030], 'ST F412/76x FS': [0x2000, 0x4F54320A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030], -- cgit v1.3.1 From 5fb8c57f5cea8fc4e3609f59d64496e9f49153df Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 16 Sep 2025 09:42:07 +0700 Subject: merge context into interface and refactor, reformat --- src/class/mtp/mtp_device.c | 304 ++++++++++++++++++--------------------------- 1 file changed, 122 insertions(+), 182 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 68a7c6c2a..d8db17e79 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -53,7 +53,7 @@ typedef struct uint8_t itf_num; uint8_t ep_in; uint8_t ep_out; - uint8_t ep_evt; + uint8_t ep_event; // Bulk Only Transfer (BOT) Protocol uint8_t phase; @@ -64,112 +64,95 @@ typedef struct uint32_t handled_len; // number of bytes already handled in the Data Stage bool xfer_completed; // true when DATA-IN/DATA-OUT transfer is completed + struct { + uint32_t session_id; + uint32_t transaction_id; + } context; } mtpd_interface_t; -typedef struct -{ - uint32_t session_id; - uint32_t transaction_id; -} mtpd_context_t; - //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ // Checker -tu_static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); -tu_static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); +static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); +static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); // MTP commands -tu_static mtp_phase_type_t mtpd_handle_cmd(void); -tu_static mtp_phase_type_t mtpd_handle_data(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_info(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_open_session(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_close_session(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_object(void); -tu_static mtp_phase_type_t mtpd_handle_dti_get_object(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); -tu_static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_send_object(void); -tu_static mtp_phase_type_t mtpd_handle_dto_send_object(void); -tu_static mtp_phase_type_t mtpd_handle_cmd_format_store(void); +static mtp_phase_type_t mtpd_handle_cmd(void); +static mtp_phase_type_t mtpd_handle_data(void); +static mtp_phase_type_t mtpd_handle_cmd_get_device_info(void); +static mtp_phase_type_t mtpd_handle_cmd_open_session(void); +static mtp_phase_type_t mtpd_handle_cmd_close_session(void); +static mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void); +static mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void); +static mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void); +static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); +static mtp_phase_type_t mtpd_handle_cmd_get_object(void); +static mtp_phase_type_t mtpd_handle_dti_get_object(void); +static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); +static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void); +static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void); +static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); +static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); +static mtp_phase_type_t mtpd_handle_cmd_send_object(void); +static mtp_phase_type_t mtpd_handle_dto_send_object(void); +static mtp_phase_type_t mtpd_handle_cmd_format_store(void); //--------------------------------------------------------------------+ // MTP variable declaration //--------------------------------------------------------------------+ -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtpd_interface_t _mtpd_itf; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_generic_container_t _mtpd_gct; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtpd_context_t _mtpd_ctx; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_device_status_res_t _mtpd_device_status_res; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint32_t _mtpd_get_object_handle; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mtp_basic_object_info_t _mtpd_soi; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static char _mtp_datestr[20]; +static mtpd_interface_t _mtpd_itf; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_generic_container_t _mtpd_gct; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_device_status_res_t _mtpd_device_status_res; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint32_t _mtpd_get_object_handle; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; +CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static char _mtp_datestr[20]; //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void mtpd_init(void) { - TU_LOG_DRV(" MTP mtpd_init\n"); tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); - tu_memclr(&_mtpd_ctx, sizeof(mtpd_context_t)); - _mtpd_get_object_handle = 0; tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); + _mtpd_get_object_handle = 0; } bool mtpd_deinit(void) { - TU_LOG_DRV(" MTP mtpd_deinit\n"); - // nothing to do - return true; + return true; // nothing to do } -void mtpd_reset(uint8_t rhport) -{ - TU_LOG_DRV(" MTP mtpd_reset\n"); - (void) rhport; - - // Close all endpoints - dcd_edpt_close_all(rhport); +void mtpd_reset(uint8_t rhport) { tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); - tu_memclr(&_mtpd_ctx, sizeof(mtpd_context_t)); tu_memclr(&_mtpd_gct, sizeof(mtp_generic_container_t)); - _mtpd_get_object_handle = 0; tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); + _mtpd_get_object_handle = 0; } -uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) -{ - TU_LOG_DRV(" MTP mtpd_open\n"); - tusb_desc_endpoint_t const *ep_desc; - // only support SCSI's BOT protocol - TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass && +uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { + // only support PIMA 15470 protocol + TU_VERIFY(TUSB_CLASS_IMAGE == itf_desc->bInterfaceClass && MTP_SUBCLASS_STILL_IMAGE == itf_desc->bInterfaceSubClass && - MTP_PROTOCOL_PIMA_15470 == itf_desc->bInterfaceProtocol, 0); + MTP_PROTOCOL_PIMA_15470 == itf_desc->bInterfaceProtocol, 0); // mtp driver length is fixed uint16_t const mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); // Max length must be at least 1 interface + 3 endpoints TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= mtpd_itf_size); - - _mtpd_itf.itf_num = itf_desc->bInterfaceNumber; + mtpd_interface_t* p_mtp = &_mtpd_itf; + p_mtp->itf_num = itf_desc->bInterfaceNumber; // Open interrupt IN endpoint - ep_desc = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); + const tusb_desc_endpoint_t* ep_desc = (const tusb_desc_endpoint_t*) tu_desc_next(itf_desc); TU_ASSERT(ep_desc->bDescriptorType == TUSB_DESC_ENDPOINT && ep_desc->bmAttributes.xfer == TUSB_XFER_INTERRUPT, 0); TU_ASSERT(usbd_edpt_open(rhport, ep_desc), 0); - _mtpd_itf.ep_evt = ep_desc->bEndpointAddress; + p_mtp->ep_event = ep_desc->bEndpointAddress; // Open endpoint pair - TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &_mtpd_itf.ep_out, &_mtpd_itf.ep_in), 0 ); + TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0); // Prepare rx on bulk out EP - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); return mtpd_itf_size; } @@ -177,115 +160,102 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16 // Invoked when a control transfer occurred on an interface of this class // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) -bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) -{ - TU_LOG_DRV(" MTP mtpd_control_xfer_cb: bmRequest=0x%2x, bRequest=0x%2x\n", request->bmRequestType, request->bRequest); - // nothing to do with DATA & ACK stage - if (stage != CONTROL_STAGE_SETUP) return true; - - uint16_t len = 0; +bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { + if (stage != CONTROL_STAGE_SETUP) { + return true; // nothing to do with DATA & ACK stage + } - switch ( request->bRequest ) - { + switch (request->bRequest) { case MTP_REQ_CANCEL: TU_LOG_DRV(" MTP request: MTP_REQ_CANCEL\n"); tud_mtp_storage_cancel(); - break; + break; + case MTP_REQ_GET_EXT_EVENT_DATA: TU_LOG_DRV(" MTP request: MTP_REQ_GET_EXT_EVENT_DATA\n"); - break; + break; + case MTP_REQ_RESET: TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); tud_mtp_storage_reset(); // Prepare for a new command - TU_ASSERT( usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE) ); - break; - case MTP_REQ_GET_DEVICE_STATUS: + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE)); + break; + + case MTP_REQ_GET_DEVICE_STATUS: { TU_LOG_DRV(" MTP request: MTP_REQ_GET_DEVICE_STATUS\n"); - len = 4; + uint16_t len = 4; _mtpd_device_status_res.wLength = len; // Cancel is synchronous, always answer OK _mtpd_device_status_res.code = MTP_RESP_OK; - TU_ASSERT( tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len) ); - break; + TU_ASSERT(tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len)); + break; + } default: TU_LOG_DRV(" MTP request: invalid request\r\n"); return false; // stall unsupported request - } + } + return true; } // Transfer on bulk endpoints -bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) -{ - const unsigned dir = tu_edpt_dir(ep_addr); +bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { + TU_ASSERT(event == XFER_RESULT_SUCCESS); - if (event != XFER_RESULT_SUCCESS) - return false; + if (ep_addr == _mtpd_itf.ep_event) { + // nothing to do + return true; + } // IN transfer completed - if (dir == TUSB_DIR_IN) - { - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) - { + if (ep_addr == _mtpd_itf.ep_in) { + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { // IN transfer completed, prepare for a new command TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); _mtpd_itf.phase = MTP_PHASE_IDLE; - } - else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) - { + } else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) { _mtpd_itf.xferred_len += xferred_bytes; _mtpd_itf.handled_len = _mtpd_itf.xferred_len; // Check if transfer completed - if (_mtpd_itf.xferred_len >= _mtpd_itf.total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) - { + if (_mtpd_itf.xferred_len >= _mtpd_itf.total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { _mtpd_itf.phase = MTP_PHASE_RESPONSE; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; _mtpd_gct.code = MTP_RESP_OK; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.transaction_id = _mtpd_ctx.transaction_id; - if (_mtpd_ctx.session_id != 0) - { - _mtpd_gct.data[0] = _mtpd_ctx.session_id; + _mtpd_gct.transaction_id = _mtpd_itf.context.transaction_id; + if (_mtpd_itf.context.session_id != 0) { + _mtpd_gct.data[0] = _mtpd_itf.context.session_id; _mtpd_gct.container_length += sizeof(uint32_t); } TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length), 0); - } - else - // Send next block of DATA - { - // Send Zero-Lenght Packet - if (_mtpd_itf.xferred_len == _mtpd_itf.total_len) - { + } else { + // Send next block of DATA + // Send Zero-Length Packet + if (_mtpd_itf.xferred_len == _mtpd_itf.total_len) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), 0 )); - } - else - { + } else { _mtpd_itf.phase = mtpd_handle_data(); - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); - else + } else { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), (uint16_t)_mtpd_itf.queued_len)); + } } } - } - else - { + } else { return false; } } - if (dir == TUSB_DIR_OUT) - { - if (_mtpd_itf.phase == MTP_PHASE_IDLE) - { + if (ep_addr == _mtpd_itf.ep_out) { + if (_mtpd_itf.phase == MTP_PHASE_IDLE) { // A new command has been received. Ensure this is the last of the sequence. _mtpd_itf.total_len = _mtpd_gct.container_length; // Stall in case of unexpected block - if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) - { + if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) { return false; } _mtpd_itf.phase = MTP_PHASE_COMMAND; @@ -296,38 +266,27 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT(_mtpd_itf.total_len < sizeof(mtp_generic_container_t)); } - if (_mtpd_itf.phase == MTP_PHASE_COMMAND) - { + if (_mtpd_itf.phase == MTP_PHASE_COMMAND) { // A zero-length or a short packet termination is expected - if (xferred_bytes == CFG_MTP_EP_SIZE || (_mtpd_itf.total_len - _mtpd_itf.xferred_len) > 0 ) - { + if (xferred_bytes == CFG_MTP_EP_SIZE || (_mtpd_itf.total_len - _mtpd_itf.xferred_len) > 0) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)(_mtpd_itf.total_len - _mtpd_itf.xferred_len))); - } - else - { + } else { // Handle command block _mtpd_itf.phase = mtpd_handle_cmd(); - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) - { + if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); - } - else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) - { + } else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_itf.queued_len)); _mtpd_itf.total_len = _mtpd_gct.container_length; _mtpd_itf.xferred_len = 0; _mtpd_itf.handled_len = 0; _mtpd_itf.xfer_completed = false; - } - else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) - { + } else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); _mtpd_itf.xferred_len = 0; _mtpd_itf.handled_len = 0; _mtpd_itf.xfer_completed = false; - } - else - { + } else { usbd_edpt_stall(rhport, _mtpd_itf.ep_out); usbd_edpt_stall(rhport, _mtpd_itf.ep_in); } @@ -335,72 +294,54 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t return true; } - if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) - { + if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { // First block of data - if (_mtpd_itf.xferred_len == 0) - { + if (_mtpd_itf.xferred_len == 0) { _mtpd_itf.total_len = _mtpd_gct.container_length; _mtpd_itf.handled_len = 0; _mtpd_itf.xfer_completed = false; } _mtpd_itf.xferred_len += xferred_bytes; // Stall in case of unexpected block - if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_DATA_BLOCK) - { - return false; - } + if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_DATA_BLOCK) { return false; } // A zero-length or a short packet termination - if (xferred_bytes < CFG_MTP_EP_SIZE) - { + if (xferred_bytes < CFG_MTP_EP_SIZE) { _mtpd_itf.xfer_completed = true; // Handle data block _mtpd_itf.phase = mtpd_handle_data(); - if (_mtpd_itf.phase == MTP_PHASE_DATA_IN || _mtpd_itf.phase == MTP_PHASE_RESPONSE) - { + if (_mtpd_itf.phase == MTP_PHASE_DATA_IN || _mtpd_itf.phase == MTP_PHASE_RESPONSE) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); - } - else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) - { + } else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); _mtpd_itf.xferred_len = 0; _mtpd_itf.xfer_completed = false; - } - else - { + } else { usbd_edpt_stall(rhport, _mtpd_itf.ep_out); usbd_edpt_stall(rhport, _mtpd_itf.ep_in); } - } - else - { + } else { // Handle data block when container is full - if (_mtpd_itf.xferred_len - _mtpd_itf.handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) - { + if (_mtpd_itf.xferred_len - _mtpd_itf.handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { _mtpd_itf.phase = mtpd_handle_data(); _mtpd_itf.handled_len = _mtpd_itf.xferred_len; } // Transfer completed: wait for zero-lenght packet // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. - if (_mtpd_itf.total_len == _mtpd_itf.xferred_len) - { + if (_mtpd_itf.total_len == _mtpd_itf.xferred_len) { TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)), CFG_MTP_EP_SIZE), 0); - } - // First data block includes container header + container data - else if (_mtpd_itf.handled_len == 0) - { + } else if (_mtpd_itf.handled_len == 0) { + // First data block includes container header + container data TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); - } - else - // Successive data block includes only container data - { + } else { + // Successive data block includes only container data TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)) + _mtpd_itf.xferred_len - _mtpd_itf.handled_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); } } } } + return true; } @@ -409,15 +350,14 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // Decode command and prepare response -mtp_phase_type_t mtpd_handle_cmd(void) -{ +mtp_phase_type_t mtpd_handle_cmd(void) { TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); - _mtpd_ctx.transaction_id = _mtpd_gct.transaction_id; - if (_mtpd_gct.code != MTP_OP_SEND_OBJECT) + _mtpd_itf.context.transaction_id = _mtpd_gct.transaction_id; + if (_mtpd_gct.code != MTP_OP_SEND_OBJECT) { _mtpd_soi.object_handle = 0; + } - switch(_mtpd_gct.code) - { + switch (_mtpd_gct.code) { case MTP_OP_GET_DEVICE_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); return mtpd_handle_cmd_get_device_info(); @@ -470,7 +410,7 @@ mtp_phase_type_t mtpd_handle_cmd(void) mtp_phase_type_t mtpd_handle_data(void) { TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_DATA_BLOCK); - _mtpd_ctx.transaction_id = _mtpd_gct.transaction_id; + _mtpd_itf.context.transaction_id = _mtpd_gct.transaction_id; switch(_mtpd_gct.code) { @@ -535,14 +475,14 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) _mtpd_gct.code = res; _mtpd_gct.container_length += sizeof(_mtpd_gct.data[0]); _mtpd_gct.data[0] = session_id; - _mtpd_ctx.session_id = session_id; + _mtpd_itf.context.session_id = session_id; return MTP_PHASE_RESPONSE; } mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_ctx.session_id = session_id; + _mtpd_itf.context.session_id = session_id; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; @@ -557,7 +497,7 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) mtp_response_t res = tud_mtp_storage_close_session(session_id); - _mtpd_ctx.session_id = session_id; + _mtpd_itf.context.session_id = session_id; _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; @@ -873,7 +813,7 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) mtp_phase_type_t mtpd_chk_session_open(const char *func_name) { (void)func_name; - if (_mtpd_ctx.session_id == 0) + if (_mtpd_itf.context.session_id == 0) { TU_LOG_DRV(" MTP error: %s session not open\n", func_name); _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; -- cgit v1.3.1 From 10298f0b275971db349c68049454994b15eabf46 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 16 Sep 2025 14:14:36 +0700 Subject: refactor generic container to use EPBUF declaration simplify container field name --- src/class/mtp/mtp.h | 4 +- src/class/mtp/mtp_device.c | 443 ++++++++++++++++++++++++--------------------- src/device/usbd.c | 1 + 3 files changed, 241 insertions(+), 207 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index b5db2cd6c..f697193d2 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -690,8 +690,8 @@ tu_static const uint16_t mtp_playback_formats[] = { // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { - uint32_t container_length; - uint16_t container_type; + uint32_t len; + uint16_t type; uint16_t code; uint32_t transaction_id; uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index d8db17e79..5aa2bf1f1 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -70,6 +70,10 @@ typedef struct } context; } mtpd_interface_t; +typedef struct { + TUD_EPBUF_TYPE_DEF(mtp_generic_container_t, container); +} mtpd_epbuf_t; + //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ @@ -102,7 +106,8 @@ static mtp_phase_type_t mtpd_handle_cmd_format_store(void); // MTP variable declaration //--------------------------------------------------------------------+ static mtpd_interface_t _mtpd_itf; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_generic_container_t _mtpd_gct; +CFG_TUD_MEM_SECTION static mtpd_epbuf_t _mtpd_epbuf; + CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_device_status_res_t _mtpd_device_status_res; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint32_t _mtpd_get_object_handle; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; @@ -122,8 +127,9 @@ bool mtpd_deinit(void) { } void mtpd_reset(uint8_t rhport) { + (void) rhport; tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); - tu_memclr(&_mtpd_gct, sizeof(mtp_generic_container_t)); + tu_memclr(&_mtpd_epbuf, sizeof(mtpd_epbuf_t)); tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); _mtpd_get_object_handle = 0; } @@ -135,7 +141,7 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 MTP_PROTOCOL_PIMA_15470 == itf_desc->bInterfaceProtocol, 0); // mtp driver length is fixed - uint16_t const mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); + const uint16_t mtpd_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); // Max length must be at least 1 interface + 3 endpoints TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= mtpd_itf_size); @@ -152,7 +158,7 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0); // Prepare rx on bulk out EP - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), CFG_MTP_EP_SIZE), 0); return mtpd_itf_size; } @@ -179,7 +185,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); tud_mtp_storage_reset(); // Prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE)); + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, (uint8_t *)(&_mtpd_epbuf.container), CFG_MTP_EP_SIZE)); break; case MTP_REQ_GET_DEVICE_STATUS: { @@ -209,39 +215,42 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t return true; } + mtpd_interface_t* p_mtp = &_mtpd_itf; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + // IN transfer completed - if (ep_addr == _mtpd_itf.ep_in) { - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { + if (ep_addr == p_mtp->ep_in) { + if (p_mtp->phase == MTP_PHASE_RESPONSE) { // IN transfer completed, prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); - _mtpd_itf.phase = MTP_PHASE_IDLE; - } else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) { - _mtpd_itf.xferred_len += xferred_bytes; - _mtpd_itf.handled_len = _mtpd_itf.xferred_len; + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + p_mtp->phase = MTP_PHASE_IDLE; + } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { + p_mtp->xferred_len += xferred_bytes; + p_mtp->handled_len = p_mtp->xferred_len; // Check if transfer completed - if (_mtpd_itf.xferred_len >= _mtpd_itf.total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { - _mtpd_itf.phase = MTP_PHASE_RESPONSE; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_OK; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.transaction_id = _mtpd_itf.context.transaction_id; - if (_mtpd_itf.context.session_id != 0) { - _mtpd_gct.data[0] = _mtpd_itf.context.session_id; - _mtpd_gct.container_length += sizeof(uint32_t); + if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { + p_mtp->phase = MTP_PHASE_RESPONSE; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->transaction_id = p_mtp->context.transaction_id; + if (p_mtp->context.session_id != 0) { + p_container->data[0] = p_mtp->context.session_id; + p_container->len += sizeof(uint32_t); } - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len), 0); } else { // Send next block of DATA // Send Zero-Length Packet - if (_mtpd_itf.xferred_len == _mtpd_itf.total_len) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), 0 )); + if (p_mtp->xferred_len == p_mtp->total_len) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), 0 )); } else { - _mtpd_itf.phase = mtpd_handle_data(); - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); + p_mtp->phase = mtpd_handle_data(); + if (p_mtp->phase == MTP_PHASE_RESPONSE) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); } else { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct.data)), (uint16_t)_mtpd_itf.queued_len)); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), (uint16_t)p_mtp->queued_len)); } } } @@ -250,93 +259,93 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } } - if (ep_addr == _mtpd_itf.ep_out) { - if (_mtpd_itf.phase == MTP_PHASE_IDLE) { + if (ep_addr == p_mtp->ep_out) { + if (p_mtp->phase == MTP_PHASE_IDLE) { // A new command has been received. Ensure this is the last of the sequence. - _mtpd_itf.total_len = _mtpd_gct.container_length; + p_mtp->total_len = p_container->len; // Stall in case of unexpected block - if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) { + if (p_container->type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) { return false; } - _mtpd_itf.phase = MTP_PHASE_COMMAND; - _mtpd_itf.total_len = _mtpd_gct.container_length; - _mtpd_itf.xferred_len = xferred_bytes; - _mtpd_itf.handled_len = 0; - _mtpd_itf.xfer_completed = false; - TU_ASSERT(_mtpd_itf.total_len < sizeof(mtp_generic_container_t)); + p_mtp->phase = MTP_PHASE_COMMAND; + p_mtp->total_len = p_container->len; + p_mtp->xferred_len = xferred_bytes; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + TU_ASSERT(p_mtp->total_len < sizeof(mtp_generic_container_t)); } - if (_mtpd_itf.phase == MTP_PHASE_COMMAND) { + if (p_mtp->phase == MTP_PHASE_COMMAND) { // A zero-length or a short packet termination is expected - if (xferred_bytes == CFG_MTP_EP_SIZE || (_mtpd_itf.total_len - _mtpd_itf.xferred_len) > 0) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)(_mtpd_itf.total_len - _mtpd_itf.xferred_len))); + if (xferred_bytes == CFG_MTP_EP_SIZE || (p_mtp->total_len - p_mtp->xferred_len) > 0) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container + p_mtp->xferred_len, (uint16_t)(p_mtp->total_len - p_mtp->xferred_len))); } else { // Handle command block - _mtpd_itf.phase = mtpd_handle_cmd(); - if (_mtpd_itf.phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); - } else if (_mtpd_itf.phase == MTP_PHASE_DATA_IN) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_itf.queued_len)); - _mtpd_itf.total_len = _mtpd_gct.container_length; - _mtpd_itf.xferred_len = 0; - _mtpd_itf.handled_len = 0; - _mtpd_itf.xfer_completed = false; - } else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); - _mtpd_itf.xferred_len = 0; - _mtpd_itf.handled_len = 0; - _mtpd_itf.xfer_completed = false; + p_mtp->phase = mtpd_handle_cmd(); + if (p_mtp->phase == MTP_PHASE_RESPONSE) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); + } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_mtp->queued_len)); + p_mtp->total_len = p_container->len; + p_mtp->xferred_len = 0; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + p_mtp->xferred_len = 0; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; } else { - usbd_edpt_stall(rhport, _mtpd_itf.ep_out); - usbd_edpt_stall(rhport, _mtpd_itf.ep_in); + usbd_edpt_stall(rhport, p_mtp->ep_out); + usbd_edpt_stall(rhport, p_mtp->ep_in); } } return true; } - if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { + if (p_mtp->phase == MTP_PHASE_DATA_OUT) { // First block of data - if (_mtpd_itf.xferred_len == 0) { - _mtpd_itf.total_len = _mtpd_gct.container_length; - _mtpd_itf.handled_len = 0; - _mtpd_itf.xfer_completed = false; + if (p_mtp->xferred_len == 0) { + p_mtp->total_len = p_container->len; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; } - _mtpd_itf.xferred_len += xferred_bytes; + p_mtp->xferred_len += xferred_bytes; // Stall in case of unexpected block - if (_mtpd_gct.container_type != MTP_CONTAINER_TYPE_DATA_BLOCK) { return false; } + if (p_container->type != MTP_CONTAINER_TYPE_DATA_BLOCK) { return false; } // A zero-length or a short packet termination if (xferred_bytes < CFG_MTP_EP_SIZE) { - _mtpd_itf.xfer_completed = true; + p_mtp->xfer_completed = true; // Handle data block - _mtpd_itf.phase = mtpd_handle_data(); - if (_mtpd_itf.phase == MTP_PHASE_DATA_IN || _mtpd_itf.phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_in, ((uint8_t *)(&_mtpd_gct)), (uint16_t)_mtpd_gct.container_length)); - } else if (_mtpd_itf.phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)), CFG_MTP_EP_SIZE), 0); - _mtpd_itf.xferred_len = 0; - _mtpd_itf.xfer_completed = false; + p_mtp->phase = mtpd_handle_data(); + if (p_mtp->phase == MTP_PHASE_DATA_IN || p_mtp->phase == MTP_PHASE_RESPONSE) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); + } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + p_mtp->xferred_len = 0; + p_mtp->xfer_completed = false; } else { - usbd_edpt_stall(rhport, _mtpd_itf.ep_out); - usbd_edpt_stall(rhport, _mtpd_itf.ep_in); + usbd_edpt_stall(rhport, p_mtp->ep_out); + usbd_edpt_stall(rhport, p_mtp->ep_in); } } else { // Handle data block when container is full - if (_mtpd_itf.xferred_len - _mtpd_itf.handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { - _mtpd_itf.phase = mtpd_handle_data(); - _mtpd_itf.handled_len = _mtpd_itf.xferred_len; + if (p_mtp->xferred_len - p_mtp->handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { + p_mtp->phase = mtpd_handle_data(); + p_mtp->handled_len = p_mtp->xferred_len; } // Transfer completed: wait for zero-lenght packet // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. - if (_mtpd_itf.total_len == _mtpd_itf.xferred_len) { - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)), CFG_MTP_EP_SIZE), 0); - } else if (_mtpd_itf.handled_len == 0) { + if (p_mtp->total_len == p_mtp->xferred_len) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)), CFG_MTP_EP_SIZE), 0); + } else if (p_mtp->handled_len == 0) { // First data block includes container header + container data - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct)) + _mtpd_itf.xferred_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container + p_mtp->xferred_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); } else { // Successive data block includes only container data - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, ((uint8_t *)(&_mtpd_gct.data)) + _mtpd_itf.xferred_len - _mtpd_itf.handled_len, (uint16_t)TU_MIN(_mtpd_itf.total_len - _mtpd_itf.xferred_len, CFG_MTP_EP_SIZE))); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); } } } @@ -351,13 +360,14 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Decode command and prepare response mtp_phase_type_t mtpd_handle_cmd(void) { - TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); - _mtpd_itf.context.transaction_id = _mtpd_gct.transaction_id; - if (_mtpd_gct.code != MTP_OP_SEND_OBJECT) { + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + _mtpd_itf.context.transaction_id = p_container->transaction_id; + if (p_container->code != MTP_OP_SEND_OBJECT) { _mtpd_soi.object_handle = 0; } - switch (_mtpd_gct.code) { + switch (p_container->code) { case MTP_OP_GET_DEVICE_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); return mtpd_handle_cmd_get_device_info(); @@ -371,7 +381,7 @@ mtp_phase_type_t mtpd_handle_cmd(void) { TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); return mtpd_handle_cmd_get_storage_ids(); case MTP_OP_GET_STORAGE_INFO: - TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", _mtpd_gct.data[0]); + TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", p_container->data[0]); return mtpd_handle_cmd_get_storage_info(); case MTP_OP_GET_OBJECT_HANDLES: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_HANDLES\n"); @@ -401,7 +411,7 @@ mtp_phase_type_t mtpd_handle_cmd(void) { TU_LOG_DRV(" MTP command: MTP_OP_FORMAT_STORE\n"); return mtpd_handle_cmd_format_store(); default: - TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", p_container->code); return false; } return true; @@ -409,10 +419,11 @@ mtp_phase_type_t mtpd_handle_cmd(void) { mtp_phase_type_t mtpd_handle_data(void) { - TU_ASSERT(_mtpd_gct.container_type == MTP_CONTAINER_TYPE_DATA_BLOCK); - _mtpd_itf.context.transaction_id = _mtpd_gct.transaction_id; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); + _mtpd_itf.context.transaction_id = p_container->transaction_id; - switch(_mtpd_gct.code) + switch(p_container->code) { case MTP_OP_GET_OBJECT: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT-DATA_IN\n"); @@ -424,7 +435,7 @@ mtp_phase_type_t mtpd_handle_data(void) TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT-DATA_OUT\n"); return mtpd_handle_dto_send_object(); default: - TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", _mtpd_gct.code); + TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", p_container->code); return false; } return true; @@ -433,11 +444,12 @@ mtp_phase_type_t mtpd_handle_data(void) mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) { TU_VERIFY_STATIC(sizeof(mtp_device_info_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_info_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_DEVICE_INFO; - mtp_device_info_t *d = (mtp_device_info_t *)_mtpd_gct.data; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_info_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_DEVICE_INFO; + mtp_device_info_t *d = (mtp_device_info_t *)p_container->data; d->standard_version = 100; d->mtp_vendor_extension_id = 0x06; d->mtp_version = 100; @@ -459,22 +471,23 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) mtpd_gct_append_wstring(CFG_MTP_DEVICE_VERSION); mtpd_gct_append_wstring(CFG_MTP_SERIAL_NUMBER); - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } mtp_phase_type_t mtpd_handle_cmd_open_session(void) { - uint32_t session_id = _mtpd_gct.data[0]; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t session_id = p_container->data[0]; mtp_response_t res = tud_mtp_storage_open_session(&session_id); if (res == MTP_RESP_SESSION_ALREADY_OPEN) { - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = res; - _mtpd_gct.container_length += sizeof(_mtpd_gct.data[0]); - _mtpd_gct.data[0] = session_id; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = res; + p_container->len += sizeof(p_container->data[0]); + p_container->data[0] = session_id; _mtpd_itf.context.session_id = session_id; return MTP_PHASE_RESPONSE; } @@ -484,24 +497,25 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) _mtpd_itf.context.session_id = session_id; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_OK; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; return MTP_PHASE_RESPONSE; } mtp_phase_type_t mtpd_handle_cmd_close_session(void) { - uint32_t session_id = _mtpd_gct.data[0]; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t session_id = p_container->data[0]; mtp_response_t res = tud_mtp_storage_close_session(session_id); _mtpd_itf.context.session_id = session_id; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = res; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = res; return MTP_PHASE_RESPONSE; } @@ -509,16 +523,17 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) { TU_VERIFY_STATIC(sizeof(mtp_storage_ids_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_ids_t shall fit in MTP_MAX_PACKET_SIZE"); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; uint32_t storage_id; mtp_response_t res = tud_mtp_get_storage_id(&storage_id); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_ids_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_STORAGE_IDS; - mtp_storage_ids_t *d = (mtp_storage_ids_t *)_mtpd_gct.data; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_ids_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_STORAGE_IDS; + mtp_storage_ids_t *d = (mtp_storage_ids_t *)p_container->data; if (storage_id == 0) { // Storage not accessible @@ -531,38 +546,39 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) d->storage_ids[0] = storage_id; } - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) { TU_VERIFY_STATIC(sizeof(mtp_storage_info_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_info_t shall fit in MTP_MAX_PACKET_SIZE"); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t storage_id = p_container->data[0]; - uint32_t storage_id = _mtpd_gct.data[0]; - - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_info_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_STORAGE_INFO; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_info_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_STORAGE_INFO; - mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)_mtpd_gct.data); + mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)p_container->data); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) { - uint32_t storage_id = _mtpd_gct.data[0]; - uint32_t object_format_code = _mtpd_gct.data[1]; // optional, not managed - uint32_t parent_object_handle = _mtpd_gct.data[2]; // folder specification, 0xffffffff=objects with no parent + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t storage_id = p_container->data[0]; + uint32_t object_format_code = p_container->data[1]; // optional, not managed + uint32_t parent_object_handle = p_container->data[2]; // folder specification, 0xffffffff=objects with no parent - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(uint32_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_OBJECT_HANDLES; - _mtpd_gct.data[0] = 0; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(uint32_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_OBJECT_HANDLES; + p_container->data[0] = 0; mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) return phase; @@ -581,30 +597,31 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) } tud_mtp_storage_object_done(); - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) { TU_VERIFY_STATIC(sizeof(mtp_object_info_t) < MTP_MAX_PACKET_SIZE, "mtp_object_info_t shall fit in MTP_MAX_PACKET_SIZE"); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t object_handle = p_container->data[0]; - uint32_t object_handle = _mtpd_gct.data[0]; - - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_object_info_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_OBJECT_INFO; - mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)_mtpd_gct.data); + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_object_info_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_OBJECT_INFO; + mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)p_container->data); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } mtp_phase_type_t mtpd_handle_cmd_get_object(void) { - _mtpd_get_object_handle = _mtpd_gct.data[0]; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + _mtpd_get_object_handle = p_container->data[0]; // Continue with DATA-IN return mtpd_handle_dti_get_object(); @@ -615,11 +632,12 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) mtp_response_t res; mtp_phase_type_t phase; uint32_t file_size = 0; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + file_size; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_OBJECT; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + file_size; + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_OBJECT; uint32_t buffer_size; uint32_t read_count; @@ -628,7 +646,7 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) { // First data block: include container header buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_GENERIC_DATA_BLOCK_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_GENERIC_DATA_BLOCK_LENGTH; - res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); + res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = MTP_GENERIC_DATA_BLOCK_LENGTH + read_count; } @@ -636,7 +654,7 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) { // Successive data block: consider only container data buffer_size = (MTP_MAX_PACKET_SIZE / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE; - res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&_mtpd_gct.data, buffer_size, &read_count); + res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; _mtpd_itf.queued_len = read_count; } @@ -652,23 +670,25 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) mtp_phase_type_t mtpd_handle_cmd_delete_object(void) { - uint32_t object_handle = _mtpd_gct.data[0]; - uint32_t object_code_format = _mtpd_gct.data[1]; // not used + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t object_handle = p_container->data[0]; + uint32_t object_code_format = p_container->data[1]; // not used (void) object_code_format; mtp_response_t res = tud_mtp_storage_object_delete(object_handle); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_OK; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) { - uint32_t device_prop_code = _mtpd_gct.data[0]; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t device_prop_code = p_container->data[0]; mtp_phase_type_t rt; if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; @@ -678,58 +698,60 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: { TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_DEVICE_PROP_DESC; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_prop_desc_t); - mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)_mtpd_gct.data; + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_DEVICE_PROP_DESC; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_prop_desc_t); + mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)p_container->data; d->device_property_code = (uint16_t)(device_prop_code); d->datatype = MTP_TYPE_STR; d->get_set = MTP_MODE_GET; mtpd_gct_append_wstring(CFG_TUD_MODEL); // factory_def_value mtpd_gct_append_wstring(CFG_TUD_MODEL); // current_value_len mtpd_gct_append_uint8(0x00); // form_flag - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; } default: break; } - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_PARAMETER_NOT_SUPPORTED; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void) { - uint32_t device_prop_code = _mtpd_gct.data[0]; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t device_prop_code = p_container->data[0]; mtp_phase_type_t rt; if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_DATA_BLOCK; - _mtpd_gct.code = MTP_OP_GET_DEVICE_PROP_VALUE; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_DEVICE_PROP_VALUE; switch(device_prop_code) { // TODO support more device properties case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: mtpd_gct_append_wstring(CFG_TUD_MODEL); - _mtpd_itf.queued_len = _mtpd_gct.container_length; + _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; default: - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_PARAMETER_NOT_SUPPORTED; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_PARAMETER_NOT_SUPPORTED; return MTP_PHASE_RESPONSE; } } mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) { - _mtpd_soi.storage_id = _mtpd_gct.data[0]; - _mtpd_soi.parent_object_handle = (_mtpd_gct.data[1] == 0xFFFFFFFF ? 0 : _mtpd_gct.data[1]); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + _mtpd_soi.storage_id = p_container->data[0]; + _mtpd_soi.parent_object_handle = (p_container->data[1] == 0xFFFFFFFF ? 0 : p_container->data[1]); // Enter OUT phase and wait for DATA BLOCK return MTP_PHASE_DATA_OUT; @@ -737,8 +759,9 @@ mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) mtp_phase_type_t mtpd_handle_dto_send_object_info(void) { + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; uint32_t new_object_handle = 0; - mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_t *)_mtpd_gct.data); + mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_t *)p_container->data); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; @@ -746,12 +769,12 @@ mtp_phase_type_t mtpd_handle_dto_send_object_info(void) _mtpd_soi.object_handle = new_object_handle; // Response - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH + 3 * sizeof(uint32_t); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_OK; - _mtpd_gct.data[0] = _mtpd_soi.storage_id; - _mtpd_gct.data[1] = _mtpd_soi.parent_object_handle; - _mtpd_gct.data[2] = _mtpd_soi.object_handle; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + 3 * sizeof(uint32_t); + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; + p_container->data[0] = _mtpd_soi.storage_id; + p_container->data[1] = _mtpd_soi.parent_object_handle; + p_container->data[2] = _mtpd_soi.object_handle; return MTP_PHASE_RESPONSE; } @@ -763,7 +786,8 @@ mtp_phase_type_t mtpd_handle_cmd_send_object(void) mtp_phase_type_t mtpd_handle_dto_send_object(void) { - uint8_t *buffer = (uint8_t *)&_mtpd_gct.data; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint8_t *buffer = (uint8_t *)&p_container->data; uint32_t buffer_size = _mtpd_itf.xferred_len - _mtpd_itf.handled_len; // First block of DATA if (_mtpd_itf.handled_len == 0) @@ -787,23 +811,24 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) // Send completed tud_mtp_storage_object_done(); - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_OK; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; return MTP_PHASE_RESPONSE; } mtp_phase_type_t mtpd_handle_cmd_format_store(void) { - uint32_t storage_id = _mtpd_gct.data[0]; - uint32_t file_system_format = _mtpd_gct.data[1]; // not used + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint32_t storage_id = p_container->data[0]; + uint32_t file_system_format = p_container->data[1]; // not used (void) file_system_format; mtp_response_t res = tud_mtp_storage_format(storage_id); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = res; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = res; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } @@ -813,12 +838,13 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) mtp_phase_type_t mtpd_chk_session_open(const char *func_name) { (void)func_name; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; if (_mtpd_itf.context.session_id == 0) { TU_LOG_DRV(" MTP error: %s session not open\n", func_name); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = MTP_RESP_SESSION_NOT_OPEN; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_SESSION_NOT_OPEN; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } return MTP_PHASE_NONE; @@ -828,12 +854,13 @@ mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, cons { (void)func_name; (void)message; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; if (err_cd) { TU_LOG_DRV(" MTP error in %s: (%x) %s\n", func_name, ret_code, message); - _mtpd_gct.container_type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - _mtpd_gct.code = ret_code; - _mtpd_gct.container_length = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = ret_code; + p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; return MTP_PHASE_RESPONSE; } return MTP_PHASE_NONE; @@ -860,31 +887,34 @@ void mtpd_wc16cpy(uint8_t *dest, const char *src) //--------------------------------------------------------------------+ bool mtpd_gct_append_uint8(const uint8_t value) { - uint8_t *p_value = ((uint8_t *)&_mtpd_gct) + _mtpd_gct.container_length; - _mtpd_gct.container_length += sizeof(uint8_t); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint8_t *p_value = ((uint8_t *)p_container) + p_container->len; + p_container->len += sizeof(uint8_t); // Verify space requirement (8 bit string length, number of wide characters including terminator) - TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); + TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); *p_value = value; return true; } bool mtpd_gct_append_object_handle(const uint32_t object_handle) { - _mtpd_gct.container_length += sizeof(uint32_t); - TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); - _mtpd_gct.data[0]++; - _mtpd_gct.data[_mtpd_gct.data[0]] = object_handle; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + p_container->len += sizeof(uint32_t); + TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); + p_container->data[0]++; + p_container->data[p_container->data[0]] = object_handle; return true; } bool mtpd_gct_append_wstring(const char *s) { + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; size_t len = strlen(s) + 1; TU_ASSERT(len <= UINT8_MAX); - uint8_t *p_len = ((uint8_t *)&_mtpd_gct)+_mtpd_gct.container_length; - _mtpd_gct.container_length += sizeof(uint8_t) + sizeof(wchar16_t) * len; + uint8_t *p_len = ((uint8_t *)p_container)+p_container->len; + p_container->len += sizeof(uint8_t) + sizeof(wchar16_t) * len; // Verify space requirement (8 bit string length, number of wide characters including terminator) - TU_ASSERT(_mtpd_gct.container_length < sizeof(mtp_generic_container_t)); + TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); *p_len = (uint8_t)len; uint8_t *p_str = p_len + sizeof(uint8_t); mtpd_wc16cpy(p_str, s); @@ -893,12 +923,13 @@ bool mtpd_gct_append_wstring(const char *s) bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max_size) { - uint16_t size = *(((uint8_t *)&_mtpd_gct.data) + *offset_data); + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + uint16_t size = *(((uint8_t *)&p_container->data) + *offset_data); if (size > max_size) size = max_size; - TU_ASSERT(*offset_data + size < sizeof(_mtpd_gct.data)); + TU_ASSERT(*offset_data + size < sizeof(p_container->data)); - uint8_t *s = ((uint8_t *)&_mtpd_gct.data) + *offset_data + sizeof(uint8_t); + uint8_t *s = ((uint8_t *)&p_container->data) + *offset_data + sizeof(uint8_t); for(uint16_t i = 0; i < size; i++) { string[i] = *s; @@ -910,19 +941,21 @@ bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_size) { - TU_ASSERT(_mtpd_gct.container_length + sizeof(uint32_t) + array_size * type_size < sizeof(_mtpd_gct.data)); - uint8_t *p = ((uint8_t *)&_mtpd_gct) + _mtpd_gct.container_length; + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + TU_ASSERT(p_container->len + sizeof(uint32_t) + array_size * type_size < sizeof(p_container->data)); + uint8_t *p = ((uint8_t *)p_container) + p_container->len; memcpy(p, &array_size, sizeof(uint32_t)); p += sizeof(uint32_t); memcpy(p, data, array_size * type_size); - _mtpd_gct.container_length += sizeof(uint32_t) + array_size * type_size; + p_container->len += sizeof(uint32_t) + array_size * type_size; return true; } bool mtpd_gct_append_date(struct tm *timeinfo) { + mtp_generic_container_t* p_container = &_mtpd_epbuf.container; // strftime is not supported by all platform, this implementation is just for reference - int len = snprintf(_mtp_datestr, sizeof(_mtpd_gct.data) - _mtpd_gct.container_length, "%04d%02d%02dT%02d%02d%02dZ", + int len = snprintf(_mtp_datestr, sizeof(p_container->data) - p_container->len, "%04d%02d%02dT%02d%02d%02dZ", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, diff --git a/src/device/usbd.c b/src/device/usbd.c index 8620c3b6f..e5542914a 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -331,6 +331,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = { .open = mtpd_open, .control_xfer_cb = mtpd_control_xfer_cb, .xfer_cb = mtpd_xfer_cb, + .xfer_isr = NULL, .sof = NULL }, #endif -- cgit v1.3.1 From 4182342112d92898d2b55ef9562c545a9ebf52ac Mon Sep 17 00:00:00 2001 From: "igor.masar" Date: Tue, 16 Sep 2025 12:18:50 +0200 Subject: Add ESP32-H4 as a supported MCU in TinyUSB and wire it into build/runtime: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tusb_option.h: define OPT_MCU_ESP32H4 - tusb_mcu.h: enable DWC2 USBIP for H4 - dcd_dwc2.c: add H4 USB_WRAP field aliases (wrap_* → legacy names) - dwc2_esp32.h: add H4 controller entry (FS base/IRQ, ep caps) - family.c: include H4 in USB init/PHY setup --- README.rst | 2 ++ hw/bsp/espressif/boards/family.c | 6 +++--- src/common/tusb_mcu.h | 2 +- src/portable/synopsys/dwc2/dwc2_esp32.h | 17 +++++++++++++++++ src/tusb_option.h | 1 + 5 files changed, 24 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/README.rst b/README.rst index d6830e707..4a17e1d1f 100644 --- a/README.rst +++ b/README.rst @@ -124,6 +124,8 @@ Supported CPUs | Espressif | S2, S3 | ✔ | ✔ | ✖ | dwc2 or esp32sx | | | ESP32 +-----------------------------+--------+------+-----------+------------------------+-------------------+ | | P4 | ✔ | ✔ | ✔ | dwc2 | | + +-----------------------------+--------+------+-----------+------------------------+-------------------+ +| | H4 | ✔ | ✔ | ✖ | dwc2 | | +--------------+----+------------------------+--------+------+-----------+------------------------+-------------------+ | GigaDevice | GD32VF103 | ✔ | | ✖ | dwc2 | | +--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+ diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c index 2a5deed26..28dd49143 100644 --- a/hw/bsp/espressif/boards/family.c +++ b/hw/bsp/espressif/boards/family.c @@ -49,7 +49,7 @@ static led_strip_handle_t led_strip; static void max3421_init(void); #endif -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32P4) +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4) static bool usb_init(void); #endif @@ -105,7 +105,7 @@ void board_init(void) { #endif } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4) #endif @@ -164,7 +164,7 @@ void board_putchar(int c) { // PHY Init //-------------------------------------------------------------------- -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32P4) +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #include "esp_private/usb_phy.h" diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 7722ca1e6..b647b2a7f 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -387,7 +387,7 @@ //--------------------------------------------------------------------+ // Espressif //--------------------------------------------------------------------+ -#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) +#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_ESP32 #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 49b8c54cb..a4e0d1770 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -47,6 +47,23 @@ static const dwc2_controller_t _dwc2_controller[] = { { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 } }; +#elif TU_CHECK_MCU(OPT_MCU_ESP32H4) +// H4's USB_WRAP register block uses "wrap_*" field names. Map them to the +// names used by TinyUSB's DWC2 port to keep the source unchanged. +#define otg_conf wrap_otg_conf +#define pad_pull_override wrap_pad_pull_override +#define dp_pullup wrap_dp_pullup +#define dp_pulldown wrap_dp_pulldown +#define dm_pullup wrap_dm_pullup +#define dm_pulldown wrap_dm_pulldown + +#define DWC2_FS_REG_BASE 0x60040000UL +#define DWC2_EP_MAX 7 + +static const dwc2_controller_t _dwc2_controller[] = { + { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 } +}; + #elif TU_CHECK_MCU(OPT_MCU_ESP32P4) #define DWC2_FS_REG_BASE 0x50040000UL #define DWC2_HS_REG_BASE 0x50000000UL diff --git a/src/tusb_option.h b/src/tusb_option.h index b2dd5fcb2..fe7e34111 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -131,6 +131,7 @@ #define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4 #define OPT_MCU_ESP32C5 908 ///< Espressif ESP32-C5 #define OPT_MCU_ESP32C61 909 ///< Espressif ESP32-C61 +#define OPT_MCU_ESP32H4 910 ///< Espressif ESP32-H4 #define TUSB_MCU_VENDOR_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU #define TUP_MCU_ESPRESSIF TUSB_MCU_VENDOR_ESPRESSIF // for backward compatibility -- cgit v1.3.1 From 541c62ceaee39bc5896f16c0f71251863adb7bc4 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 16 Sep 2025 22:23:57 +0700 Subject: refactor mtp xfer callback --- src/class/mtp/mtp.h | 12 ++- src/class/mtp/mtp_device.c | 211 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 186 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index f697193d2..c9d2b27a9 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -60,9 +60,11 @@ typedef enum { typedef enum { MTP_PHASE_IDLE = 0, MTP_PHASE_COMMAND, + MTP_PHASE_DATA, MTP_PHASE_DATA_IN, MTP_PHASE_DATA_OUT, MTP_PHASE_RESPONSE, + MTP_PHASE_RESPONSE_QUEUED, MTP_PHASE_ERROR, MTP_PHASE_NONE, } mtp_phase_type_t; @@ -685,9 +687,17 @@ tu_static const uint16_t mtp_playback_formats[] = { // Data structures //--------------------------------------------------------------------+ -#define MTP_GENERIC_DATA_BLOCK_LENGTH 12 +#define MTP_CONTAINER_HEADER_LENGTH 12 #define MTP_MAX_PACKET_SIZE 512 +typedef struct TU_ATTR_PACKED { + uint32_t len; + uint16_t type; + uint16_t code; + uint32_t transaction_id; +} mtp_container_header_t; +TU_VERIFY_STATIC(sizeof(mtp_container_header_t) == 12, "size is not correct"); + // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { uint32_t len; diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 5aa2bf1f1..6b37f50c2 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -45,6 +45,8 @@ #define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MTP_LOG_LEVEL, __VA_ARGS__) +#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + //--------------------------------------------------------------------+ // STRUCT //--------------------------------------------------------------------+ @@ -68,6 +70,8 @@ typedef struct uint32_t session_id; uint32_t transaction_id; } context; + + mtp_container_header_t cmd_header; } mtpd_interface_t; typedef struct { @@ -113,6 +117,17 @@ CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint32_t _mtpd_get_object_handle; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static char _mtp_datestr[20]; + +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ + +static bool prepare_new_command(uint8_t rhport, mtpd_interface_t* p_mtp) { + p_mtp->phase = MTP_PHASE_IDLE; + return usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t)); +} + + //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -157,8 +172,7 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 // Open endpoint pair TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0); - // Prepare rx on bulk out EP - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), CFG_MTP_EP_SIZE), 0); + TU_ASSERT(prepare_new_command(rhport, p_mtp), 0); return mtpd_itf_size; } @@ -185,7 +199,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); tud_mtp_storage_reset(); // Prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, (uint8_t *)(&_mtpd_epbuf.container), CFG_MTP_EP_SIZE)); + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t))); break; case MTP_REQ_GET_DEVICE_STATUS: { @@ -218,28 +232,152 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t mtpd_interface_t* p_mtp = &_mtpd_itf; mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + switch (p_mtp->phase) { + case MTP_PHASE_IDLE: + // received new command + TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + p_mtp->phase = MTP_PHASE_COMMAND; + TU_ATTR_FALLTHROUGH; // handle in the next case + + case MTP_PHASE_COMMAND: + // Handle command block + memcpy(&p_mtp->cmd_header, p_container, sizeof(mtp_container_header_t)); + p_mtp->phase = mtpd_handle_cmd(); + if (p_mtp->phase == MTP_PHASE_DATA_IN) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_mtp->queued_len)); + p_mtp->total_len = p_container->len; + p_mtp->xferred_len = 0; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { + p_mtp->xferred_len = 0; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); + } + break; + + case MTP_PHASE_DATA_IN: + p_mtp->xferred_len += xferred_bytes; + p_mtp->handled_len = p_mtp->xferred_len; + + // Check if transfer completed TODO check ZLP with FS/HS bulk size + if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { + p_mtp->phase = MTP_PHASE_RESPONSE; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->code = MTP_RESP_OK; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; + p_container->transaction_id = p_mtp->context.transaction_id; + if (p_mtp->context.session_id != 0) { // is this needed ? + p_container->data[0] = p_mtp->context.session_id; + p_container->len += sizeof(uint32_t); + } + } else { + // Send next block of DATA + if (p_mtp->xferred_len == p_mtp->total_len) { + // send Zero-Length Packet + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, NULL, 0 )); + } else { + p_mtp->phase = mtpd_handle_data(); + if (p_mtp->phase == MTP_PHASE_DATA_IN) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), (uint16_t)p_mtp->queued_len)); + } + } + } + break; + + case MTP_PHASE_DATA_OUT: + // First block of data + if (p_mtp->xferred_len == 0) { + p_mtp->total_len = p_container->len; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); + } + p_mtp->xferred_len += xferred_bytes; + + // A zero-length or a short packet termination + if (xferred_bytes < CFG_MTP_EP_SIZE) { + p_mtp->xfer_completed = true; + // Handle data block + p_mtp->phase = mtpd_handle_data(); + if (p_mtp->phase == MTP_PHASE_DATA_OUT) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); + p_mtp->xferred_len = 0; + p_mtp->xfer_completed = false; + } + } else { + // Handle data block when container is full + if (p_mtp->xferred_len - p_mtp->handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { + p_mtp->phase = mtpd_handle_data(); + p_mtp->handled_len = p_mtp->xferred_len; + } + // Transfer completed: wait for zero-length packet + // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if + // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. + if (p_mtp->total_len == p_mtp->xferred_len) { + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)), CFG_MTP_EP_SIZE), 0); + } else if (p_mtp->handled_len == 0) { + // First data block includes container header + container data + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, + (uint8_t*) p_container + p_mtp->xferred_len, + (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); + } else { + // Successive data block includes only container data + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, + ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, + (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); + } + } + break; + + case MTP_PHASE_RESPONSE_QUEUED: + // response phase is complete -> prepare for new command + TU_ASSERT(ep_addr == p_mtp->ep_in); + prepare_new_command(rhport, p_mtp); + break; + + case MTP_PHASE_RESPONSE: + case MTP_PHASE_ERROR: + // processed immediately after this switch, supposedly to be empty + break; + default: return false; + } + + if (p_mtp->phase == MTP_PHASE_RESPONSE) { + p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; + p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->transaction_id = p_mtp->context.transaction_id; + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); + } else if (p_mtp->phase == MTP_PHASE_ERROR) { + // stall both IN & OUT endpoints + usbd_edpt_stall(rhport, p_mtp->ep_out); + usbd_edpt_stall(rhport, p_mtp->ep_in); + } + +#if 0 // IN transfer completed if (ep_addr == p_mtp->ep_in) { if (p_mtp->phase == MTP_PHASE_RESPONSE) { // IN transfer completed, prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, CFG_MTP_EP_SIZE), 0); p_mtp->phase = MTP_PHASE_IDLE; } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { p_mtp->xferred_len += xferred_bytes; p_mtp->handled_len = p_mtp->xferred_len; - // Check if transfer completed + // Check if transfer completed. if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { p_mtp->phase = MTP_PHASE_RESPONSE; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->transaction_id = p_mtp->context.transaction_id; if (p_mtp->context.session_id != 0) { p_container->data[0] = p_mtp->context.session_id; p_container->len += sizeof(uint32_t); } - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); } else { // Send next block of DATA // Send Zero-Length Packet @@ -248,7 +386,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } else { p_mtp->phase = mtpd_handle_data(); if (p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); } else { TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), (uint16_t)p_mtp->queued_len)); } @@ -278,20 +416,20 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (p_mtp->phase == MTP_PHASE_COMMAND) { // A zero-length or a short packet termination is expected if (xferred_bytes == CFG_MTP_EP_SIZE || (p_mtp->total_len - p_mtp->xferred_len) > 0) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container + p_mtp->xferred_len, (uint16_t)(p_mtp->total_len - p_mtp->xferred_len))); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container + p_mtp->xferred_len, (uint16_t)(p_mtp->total_len - p_mtp->xferred_len))); } else { // Handle command block p_mtp->phase = mtpd_handle_cmd(); if (p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_mtp->queued_len)); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_mtp->queued_len)); p_mtp->total_len = p_container->len; p_mtp->xferred_len = 0; p_mtp->handled_len = 0; p_mtp->xfer_completed = false; } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); p_mtp->xferred_len = 0; p_mtp->handled_len = 0; p_mtp->xfer_completed = false; @@ -320,9 +458,9 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Handle data block p_mtp->phase = mtpd_handle_data(); if (p_mtp->phase == MTP_PHASE_DATA_IN || p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) &_mtpd_epbuf.container, (uint16_t)p_container->len)); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container, CFG_MTP_EP_SIZE), 0); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); p_mtp->xferred_len = 0; p_mtp->xfer_completed = false; } else { @@ -342,7 +480,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)), CFG_MTP_EP_SIZE), 0); } else if (p_mtp->handled_len == 0) { // First data block includes container header + container data - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) &_mtpd_epbuf.container + p_mtp->xferred_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); + TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container + p_mtp->xferred_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); } else { // Successive data block includes only container data TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); @@ -350,6 +488,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } } } +#endif return true; } @@ -446,7 +585,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) TU_VERIFY_STATIC(sizeof(mtp_device_info_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_info_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_info_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_DEVICE_INFO; mtp_device_info_t *d = (mtp_device_info_t *)p_container->data; @@ -483,7 +622,7 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) mtp_response_t res = tud_mtp_storage_open_session(&session_id); if (res == MTP_RESP_SESSION_ALREADY_OPEN) { - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = res; p_container->len += sizeof(p_container->data[0]); @@ -497,7 +636,7 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) _mtpd_itf.context.session_id = session_id; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; @@ -513,7 +652,7 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) _mtpd_itf.context.session_id = session_id; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = res; @@ -530,7 +669,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_ids_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_storage_ids_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_STORAGE_IDS; mtp_storage_ids_t *d = (mtp_storage_ids_t *)p_container->data; @@ -556,7 +695,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) mtp_generic_container_t* p_container = &_mtpd_epbuf.container; uint32_t storage_id = p_container->data[0]; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_storage_info_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_storage_info_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_STORAGE_INFO; @@ -575,7 +714,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) uint32_t object_format_code = p_container->data[1]; // optional, not managed uint32_t parent_object_handle = p_container->data[2]; // folder specification, 0xffffffff=objects with no parent - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(uint32_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(uint32_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_OBJECT_HANDLES; p_container->data[0] = 0; @@ -607,7 +746,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) mtp_generic_container_t* p_container = &_mtpd_epbuf.container; uint32_t object_handle = p_container->data[0]; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_object_info_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_object_info_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_OBJECT_INFO; mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)p_container->data); @@ -635,7 +774,7 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) mtp_generic_container_t* p_container = &_mtpd_epbuf.container; res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + file_size; + p_container->len = MTP_CONTAINER_HEADER_LENGTH + file_size; p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_OBJECT; @@ -645,10 +784,10 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) if (_mtpd_itf.handled_len == 0) { // First data block: include container header - buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_GENERIC_DATA_BLOCK_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_GENERIC_DATA_BLOCK_LENGTH; + buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_CONTAINER_HEADER_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_CONTAINER_HEADER_LENGTH; res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_itf.queued_len = MTP_GENERIC_DATA_BLOCK_LENGTH + read_count; + _mtpd_itf.queued_len = MTP_CONTAINER_HEADER_LENGTH + read_count; } else { @@ -681,7 +820,7 @@ mtp_phase_type_t mtpd_handle_cmd_delete_object(void) p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } @@ -700,7 +839,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_DEVICE_PROP_DESC; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + sizeof(mtp_device_prop_desc_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_prop_desc_t); mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)p_container->data; d->device_property_code = (uint16_t)(device_prop_code); d->datatype = MTP_TYPE_STR; @@ -717,7 +856,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } @@ -729,7 +868,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void) mtp_phase_type_t rt; if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_DEVICE_PROP_VALUE; @@ -769,7 +908,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object_info(void) _mtpd_soi.object_handle = new_object_handle; // Response - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH + 3 * sizeof(uint32_t); + p_container->len = MTP_CONTAINER_HEADER_LENGTH + 3 * sizeof(uint32_t); p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; p_container->data[0] = _mtpd_soi.storage_id; @@ -792,7 +931,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) // First block of DATA if (_mtpd_itf.handled_len == 0) { - buffer_size -= MTP_GENERIC_DATA_BLOCK_LENGTH; + buffer_size -= MTP_CONTAINER_HEADER_LENGTH; } if (buffer_size > 0) @@ -811,7 +950,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) // Send completed tud_mtp_storage_object_done(); - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; return MTP_PHASE_RESPONSE; @@ -828,7 +967,7 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = res; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } @@ -844,7 +983,7 @@ mtp_phase_type_t mtpd_chk_session_open(const char *func_name) TU_LOG_DRV(" MTP error: %s session not open\n", func_name); p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_SESSION_NOT_OPEN; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } return MTP_PHASE_NONE; @@ -860,7 +999,7 @@ mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, cons TU_LOG_DRV(" MTP error in %s: (%x) %s\n", func_name, ret_code, message); p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = ret_code; - p_container->len = MTP_GENERIC_DATA_BLOCK_LENGTH; + p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } return MTP_PHASE_NONE; -- cgit v1.3.1 From aea56dc7760c1a1f4caedc983e2c46b8928e7388 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 16 Sep 2025 22:43:18 +0700 Subject: remove mtp context --- src/class/mtp/mtp_device.c | 63 ++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 6b37f50c2..7e9574ab1 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -66,11 +66,7 @@ typedef struct uint32_t handled_len; // number of bytes already handled in the Data Stage bool xfer_completed; // true when DATA-IN/DATA-OUT transfer is completed - struct { - uint32_t session_id; - uint32_t transaction_id; - } context; - + uint32_t session_id; mtp_container_header_t cmd_header; } mtpd_interface_t; @@ -264,12 +260,10 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Check if transfer completed TODO check ZLP with FS/HS bulk size if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { p_mtp->phase = MTP_PHASE_RESPONSE; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = MTP_RESP_OK; p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->transaction_id = p_mtp->context.transaction_id; - if (p_mtp->context.session_id != 0) { // is this needed ? - p_container->data[0] = p_mtp->context.session_id; + if (p_mtp->session_id != 0) { // is this needed ? + p_container->data[0] = p_mtp->session_id; p_container->len += sizeof(uint32_t); } } else { @@ -347,7 +341,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (p_mtp->phase == MTP_PHASE_RESPONSE) { p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->transaction_id = p_mtp->context.transaction_id; + p_container->transaction_id = p_mtp->cmd_header.transaction_id; TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); } else if (p_mtp->phase == MTP_PHASE_ERROR) { // stall both IN & OUT endpoints @@ -373,8 +367,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_container->code = MTP_RESP_OK; p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->transaction_id = p_mtp->context.transaction_id; - if (p_mtp->context.session_id != 0) { - p_container->data[0] = p_mtp->context.session_id; + if (p_mtp->session_id != 0) { + p_container->data[0] = p_mtp->session_id; p_container->len += sizeof(uint32_t); } TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); @@ -501,7 +495,6 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t mtp_phase_type_t mtpd_handle_cmd(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); - _mtpd_itf.context.transaction_id = p_container->transaction_id; if (p_container->code != MTP_OP_SEND_OBJECT) { _mtpd_soi.object_handle = 0; } @@ -560,7 +553,6 @@ mtp_phase_type_t mtpd_handle_data(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); - _mtpd_itf.context.transaction_id = p_container->transaction_id; switch(p_container->code) { @@ -627,14 +619,14 @@ mtp_phase_type_t mtpd_handle_cmd_open_session(void) p_container->code = res; p_container->len += sizeof(p_container->data[0]); p_container->data[0] = session_id; - _mtpd_itf.context.session_id = session_id; + _mtpd_itf.session_id = session_id; return MTP_PHASE_RESPONSE; } mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - _mtpd_itf.context.session_id = session_id; + _mtpd_itf.session_id = session_id; p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; @@ -650,7 +642,7 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) mtp_response_t res = tud_mtp_storage_close_session(session_id); - _mtpd_itf.context.session_id = session_id; + _mtpd_itf.session_id = session_id; p_container->len = MTP_CONTAINER_HEADER_LENGTH; p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; @@ -701,7 +693,9 @@ mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)p_container->data); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; @@ -720,18 +714,25 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) p_container->data[0] = 0; mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) { + return phase; + } //list of all object handles on all storages, not managed - if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESP_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESP_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) { + return phase; + } tud_mtp_storage_object_done(); uint32_t next_child_handle = 0; while(true) { mtp_response_t res = tud_mtp_storage_association_get_object_handle(storage_id, parent_object_handle, &next_child_handle); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - if (next_child_handle == 0) + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } + if (next_child_handle == 0) { break; + } mtpd_gct_append_object_handle(next_child_handle); } tud_mtp_storage_object_done(); @@ -751,7 +752,9 @@ mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) p_container->code = MTP_OP_GET_OBJECT_INFO; mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)p_container->data); mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } _mtpd_itf.queued_len = p_container->len; return MTP_PHASE_DATA_IN; @@ -773,7 +776,9 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) uint32_t file_size = 0; mtp_generic_container_t* p_container = &_mtpd_epbuf.container; res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } p_container->len = MTP_CONTAINER_HEADER_LENGTH + file_size; p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_OBJECT; @@ -786,7 +791,9 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) // First data block: include container header buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_CONTAINER_HEADER_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_CONTAINER_HEADER_LENGTH; res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } _mtpd_itf.queued_len = MTP_CONTAINER_HEADER_LENGTH + read_count; } else @@ -794,7 +801,9 @@ mtp_phase_type_t mtpd_handle_dti_get_object(void) // Successive data block: consider only container data buffer_size = (MTP_MAX_PACKET_SIZE / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE; res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; + if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { + return phase; + } _mtpd_itf.queued_len = read_count; } @@ -978,7 +987,7 @@ mtp_phase_type_t mtpd_chk_session_open(const char *func_name) { (void)func_name; mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - if (_mtpd_itf.context.session_id == 0) + if (_mtpd_itf.session_id == 0) { TU_LOG_DRV(" MTP error: %s session not open\n", func_name); p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; -- cgit v1.3.1 From f18b750550ef5c242f84924cde23f7712c3a0670 Mon Sep 17 00:00:00 2001 From: Peter Lawrence <12226419+majbthrd@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:38:47 -0500 Subject: use existing overloadable Pico SDK section macro instead of creating a custom one --- src/common/tusb_mcu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index b647b2a7f..1c11df114 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -442,7 +442,7 @@ #define TUP_DCD_ENDPOINT_MAX 16 #define TUP_MCU_MULTIPLE_CORE 1 - #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) + #define TU_ATTR_FAST_FUNC __not_in_flash("tinyusb") //--------------------------------------------------------------------+ // Silabs -- cgit v1.3.1 From 369a1ff5153a472327d2847c7bc7386309486b34 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 17 Sep 2025 22:37:45 +0200 Subject: Update weak callbacks to new syntax Signed-off-by: HiFiPhile --- hw/bsp/board_api.h | 6 +- src/class/bth/bth_device.c | 130 ++++++++++++++++++++------------------- src/class/bth/bth_device.h | 8 +-- src/class/cdc/cdc_device.c | 60 +++++++++++++----- src/class/cdc/cdc_device.h | 14 ++--- src/class/cdc/cdc_host.c | 39 +++++++----- src/class/cdc/cdc_host.h | 8 +-- src/class/dfu/dfu_device.c | 27 +++++--- src/class/dfu/dfu_device.h | 6 +- src/class/hid/hid_host.c | 84 +++++++++++++++++-------- src/class/hid/hid_host.h | 12 ++-- src/class/midi/midi_device.c | 12 +++- src/class/midi/midi_device.h | 4 +- src/class/msc/msc_device.c | 111 ++++++++++++++++++++------------- src/class/msc/msc_device.h | 16 ++--- src/class/msc/msc_host.c | 19 ++++-- src/class/msc/msc_host.h | 4 +- src/class/usbtmc/usbtmc_device.c | 29 ++++++--- src/class/usbtmc/usbtmc_device.h | 8 +-- src/class/vendor/vendor_device.c | 22 +++++-- src/class/vendor/vendor_device.h | 4 +- src/class/video/video_device.c | 33 +++++++--- src/class/video/video_device.h | 6 +- src/common/tusb_common.h | 8 +-- src/device/dcd.h | 2 +- src/device/usbd.c | 13 ++-- src/device/usbd_pvt.h | 2 +- src/host/usbh.c | 25 +++++--- src/host/usbh.h | 6 +- src/host/usbh_pvt.h | 2 +- src/typec/usbc.c | 25 ++++++-- src/typec/usbc.h | 4 +- 32 files changed, 467 insertions(+), 282 deletions(-) (limited to 'src') diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h index 328fe9363..111829b4f 100644 --- a/hw/bsp/board_api.h +++ b/hw/bsp/board_api.h @@ -72,10 +72,10 @@ extern "C" { void board_init(void); // Init board after tinyusb is initialized -void board_init_after_tusb(void) TU_ATTR_WEAK; +void board_init_after_tusb(void); // Jump to bootloader -void board_reset_to_bootloader(void) TU_ATTR_WEAK; +void board_reset_to_bootloader(void); // Turn LED on or off void board_led_write(bool state); @@ -89,7 +89,7 @@ void board_led_write(bool state); uint32_t board_button_read(void); // Get board unique ID for USB serial number. Return number of bytes. Note max_len is typically 16 -TU_ATTR_WEAK size_t board_get_unique_id(uint8_t id[], size_t max_len); +size_t board_get_unique_id(uint8_t id[], size_t max_len); // Get characters from UART. Return number of read bytes int board_uart_read(uint8_t *buf, int len); diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c index 45cbf2d98..3f1529cb6 100755 --- a/src/class/bth/bth_device.c +++ b/src/class/bth/bth_device.c @@ -43,7 +43,7 @@ typedef struct { uint8_t ep_acl_in; uint16_t ep_acl_in_pkt_sz; uint8_t ep_acl_out; - uint8_t ep_voice[2]; // Not used yet + uint8_t ep_voice[2];// Not used yet uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT]; // Previous amount of bytes sent when issuing ZLP @@ -61,8 +61,7 @@ typedef struct { static btd_interface_t _btd_itf; CFG_TUD_MEM_SECTION static btd_epbuf_t _btd_epbuf; -static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) -{ +static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) { uint8_t const rhport = 0; // skip if previous transfer not complete @@ -73,6 +72,27 @@ static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) return true; } +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len) { + (void) hci_cmd; + (void) cmd_len; +} + +TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len) { + (void) acl_data; + (void) data_len; +} + +TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes) { + (void) sent_bytes; +} + +TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes) { + (void) sent_bytes; +} + //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ @@ -82,13 +102,11 @@ static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) // WRITE API //--------------------------------------------------------------------+ -bool tud_bt_event_send(void *event, uint16_t event_len) -{ +bool tud_bt_event_send(void *event, uint16_t event_len) { return bt_tx_data(_btd_itf.ep_ev, event, event_len); } -bool tud_bt_acl_data_send(void *event, uint16_t event_len) -{ +bool tud_bt_acl_data_send(void *event, uint16_t event_len) { return bt_tx_data(_btd_itf.ep_acl_in, event, event_len); } @@ -103,13 +121,11 @@ bool btd_deinit(void) { return true; } -void btd_reset(uint8_t rhport) -{ - (void)rhport; +void btd_reset(uint8_t rhport) { + (void) rhport; } -uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) -{ +uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { tusb_desc_endpoint_t const *desc_ep; uint16_t drv_len = 0; // Size of single alternative of ISO interface @@ -118,8 +134,9 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); // Ensure this is BT Primary Controller TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass && - TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass && - TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0); + TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass && + TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, + 0); TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size); @@ -131,10 +148,10 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); _btd_itf.ep_ev = desc_ep->bEndpointAddress; - desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep); // Open endpoint pair - TU_ASSERT(usbd_open_edpt_pair(rhport, (uint8_t const *)desc_ep, 2, + TU_ASSERT(usbd_open_edpt_pair(rhport, (uint8_t const *) desc_ep, 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out, &_btd_itf.ep_acl_in), 0); @@ -146,10 +163,10 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ _btd_itf.ep_acl_in_pkt_sz = tu_edpt_packet_size(desc_ep_acl_in); break; } - desc_ep_acl_in = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep_acl_in); + desc_ep_acl_in = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep_acl_in); } - itf_desc = (tusb_desc_interface_t const *)tu_desc_next(tu_desc_next(desc_ep)); + itf_desc = (tusb_desc_interface_t const *) tu_desc_next(tu_desc_next(desc_ep)); // Prepare for incoming data from host TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0); @@ -158,13 +175,14 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ // Ensure this is still BT Primary Controller TU_ASSERT(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass && - TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass && - TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0); + TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass && + TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, + 0); TU_ASSERT(itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size + drv_len); uint8_t dir; - desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0); TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0); dir = tu_edpt_dir(desc_ep->bEndpointAddress); @@ -172,7 +190,7 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ // Store endpoint size for alternative _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep); - desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep); TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0); dir = tu_edpt_dir(desc_ep->bEndpointAddress); _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress; @@ -182,29 +200,30 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) { // Make sure rest of alternatives matches - itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep); + itf_desc = (tusb_desc_interface_t const *) tu_desc_next(desc_ep); if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE || TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass || TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass || - TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol) - { + TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol) { // Not an Iso interface instance break; } TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0); - desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); dir = tu_edpt_dir(desc_ep->bEndpointAddress); // Verify that alternative endpoint are same as first ones TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT && - _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0); + _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, + 0); _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep); - desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep); dir = tu_edpt_dir(desc_ep->bEndpointAddress); // Verify that alternative endpoint are same as first ones TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT && - _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0); + _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, + 0); _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep); drv_len += iso_alt_itf_size; } @@ -215,44 +234,32 @@ uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_ // Invoked when a control transfer occurred on an interface of this class // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) -bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) -{ - (void)rhport; +bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) { + (void) rhport; - if ( stage == CONTROL_STAGE_SETUP ) - { + if (stage == CONTROL_STAGE_SETUP) { if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && - request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE) - { + request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE) { // HCI command packet addressing for single function Primary Controllers // also compatible with historical mode if enabled TU_VERIFY((request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0) || (CFG_TUD_BTH_HISTORICAL_COMPATIBLE && request->bRequest == 0xe0)); - } - else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) - { - if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex) - { + } else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) { + if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex) { // TODO: Set interface it would involve changing size of endpoint size - } - else - { + } else { // HCI command packet for Primary Controller function in a composite device TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num); } - } - else return false; + } else + return false; return tud_control_xfer(rhport, request, &_btd_epbuf.hci_cmd, sizeof(bt_hci_cmd_t)); - } - else if ( stage == CONTROL_STAGE_DATA ) - { + } else if (stage == CONTROL_STAGE_DATA) { // Handle class request only TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); - if (tud_bt_hci_cmd_cb) { - tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t))); - } + tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t))); } return true; @@ -261,19 +268,14 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { // received new data from host - if (ep_addr == _btd_itf.ep_acl_out) - { - if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes); + if (ep_addr == _btd_itf.ep_acl_out) { + tud_bt_acl_data_received_cb(_btd_epbuf.epout_buf, xferred_bytes); // prepare for next data TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE)); - } - else if (ep_addr == _btd_itf.ep_ev) - { - if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes); - } - else if (ep_addr == _btd_itf.ep_acl_in) - { + } else if (ep_addr == _btd_itf.ep_ev) { + tud_bt_event_sent_cb((uint16_t) xferred_bytes); + } else if (ep_addr == _btd_itf.ep_acl_in) { if ((result == XFER_RESULT_SUCCESS) && (xferred_bytes > 0) && ((xferred_bytes & (_btd_itf.ep_acl_in_pkt_sz - 1)) == 0)) { // Save number of transferred bytes @@ -281,12 +283,12 @@ bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, // Send zero-length packet tud_bt_acl_data_send(NULL, 0); - } else if (tud_bt_acl_data_sent_cb) { + } else { if (xferred_bytes == 0) { xferred_bytes = _btd_itf.prev_xferred_bytes; _btd_itf.prev_xferred_bytes = 0; } - tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes); + tud_bt_acl_data_sent_cb((uint16_t) xferred_bytes); } } diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h index 4f6350839..68f073bff 100755 --- a/src/class/bth/bth_device.h +++ b/src/class/bth/bth_device.h @@ -67,23 +67,23 @@ typedef struct TU_ATTR_PACKED // Part E, 5.4.1. // Length of the command is from 3 bytes (2 bytes for OpCode, // 1 byte for parameter total length) to 258. -TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len); +void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len); // Invoked when ACL data was received over USB from Bluetooth host. // Detailed format is described in Bluetooth core specification Vol 2, // Part E, 5.4.2. // Length is from 4 bytes, (12 bits for Handle, 4 bits for flags // and 16 bits for data total length) to endpoint size. -TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len); +void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len); // Called when event sent with tud_bt_event_send() was delivered to BT stack. // Controller can release/reuse buffer with Event packet at this point. -TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes); +void tud_bt_event_sent_cb(uint16_t sent_bytes); // Called when ACL data that was sent with tud_bt_acl_data_send() // was delivered to BT stack. // Controller can release/reuse buffer with ACL packet at this point. -TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes); +void tud_bt_acl_data_sent_cb(uint16_t sent_bytes); // Bluetooth controller calls this function when it wants to send even packet // as described in Bluetooth core specification Vol 2, Part E, 5.4.4. diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 4e4e01eaf..f1c4a3bbf 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -119,6 +119,42 @@ static bool _prep_out_transaction(uint8_t itf) { } } +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf) { + (void) itf; +} + +TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { + (void) itf; + (void) wanted_char; +} + +TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf) { + (void) itf; +} + +TU_ATTR_WEAK void tud_cdc_notify_complete_cb(uint8_t itf) { + (void) itf; +} + +TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + (void) itf; + (void) dtr; + (void) rts; +} + +TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) { + (void) itf; + (void) p_line_coding; +} + +TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { + (void) itf; + (void) duration_ms; +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -419,9 +455,7 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ TU_LOG_DRV(" Set Line Coding\r\n"); tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); } else if (stage == CONTROL_STAGE_ACK) { - if (tud_cdc_line_coding_cb) { - tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); - } + tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); } break; @@ -456,9 +490,7 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); // Invoke callback - if (tud_cdc_line_state_cb) { - tud_cdc_line_state_cb(itf, dtr, rts); - } + tud_cdc_line_state_cb(itf, dtr, rts); } break; @@ -467,9 +499,7 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ tud_control_status(rhport, request); } else if (stage == CONTROL_STAGE_ACK) { TU_LOG_DRV(" Send Break\r\n"); - if (tud_cdc_send_break_cb) { - tud_cdc_send_break_cb(itf, request->wValue); - } + tud_cdc_send_break_cb(itf, request->wValue); } break; @@ -501,7 +531,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ tu_fifo_write_n(&p_cdc->rx_ff, p_epbuf->epout, (uint16_t) xferred_bytes); // Check for wanted char and invoke callback if needed - if (tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1)) { + if (((signed char) p_cdc->wanted_char) != -1) { for (uint32_t i = 0; i < xferred_bytes; i++) { if ((p_cdc->wanted_char == p_epbuf->epout[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) { tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); @@ -510,7 +540,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ } // invoke receive callback (if there is still data) - if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) { + if (!tu_fifo_empty(&p_cdc->rx_ff)) { tud_cdc_rx_cb(itf); } @@ -523,9 +553,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ // Though maybe the baudrate is not really important !!! if (ep_addr == p_cdc->ep_in) { // invoke transmit callback to possibly refill tx fifo - if (tud_cdc_tx_complete_cb) { - tud_cdc_tx_complete_cb(itf); - } + tud_cdc_tx_complete_cb(itf); if (0 == tud_cdc_n_write_flush(itf)) { // If there is no data left, a ZLP should be sent if @@ -540,9 +568,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ // Sent notification to host if (ep_addr == p_cdc->ep_notify) { - if (tud_cdc_notify_complete_cb) { - tud_cdc_notify_complete_cb(itf); - } + tud_cdc_notify_complete_cb(itf); } return true; diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index a34e07e1d..9673b9807 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -220,28 +220,28 @@ TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) { //--------------------------------------------------------------------+ // Invoked when received new data -TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); +void tud_cdc_rx_cb(uint8_t itf); // Invoked when received `wanted_char` -TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); +void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); // Invoked when a TX is complete and therefore space becomes available in TX buffer -TU_ATTR_WEAK void tud_cdc_tx_complete_cb(uint8_t itf); +void tud_cdc_tx_complete_cb(uint8_t itf); // Invoked when a notification is sent to host -TU_ATTR_WEAK void tud_cdc_notify_complete_cb(uint8_t itf); +void tud_cdc_notify_complete_cb(uint8_t itf); // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE -TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); // Invoked when line coding is change via SET_LINE_CODING -TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); // Invoked when received send break // \param[in] itf interface for which send break was received. // \param[in] duration_ms the length of time, in milliseconds, of the break signal. If a value of FFFFh, then the // device will send a break until another SendBreak request is received with value 0000h. -TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms); +void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms); //--------------------------------------------------------------------+ // INTERNAL USBD-CLASS DRIVER API diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f9a37ed35..beef03eff 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -391,6 +391,25 @@ static cdch_interface_t * make_new_itf(uint8_t daddr, tusb_desc_interface_t cons static bool open_ep_stream_pair(cdch_interface_t * p_cdc , tusb_desc_endpoint_t const *desc_ep); +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tuh_cdc_mount_cb(uint8_t idx) { + (void) idx; +} + +TU_ATTR_WEAK void tuh_cdc_umount_cb(uint8_t idx) { + (void) idx; +} + +TU_ATTR_WEAK void tuh_cdc_rx_cb(uint8_t idx) { + (void) idx; +} + +TU_ATTR_WEAK void tuh_cdc_tx_complete_cb(uint8_t idx) { + (void) idx; +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -657,9 +676,7 @@ void cdch_close(uint8_t daddr) { TU_LOG_CDC(p_cdc, "close"); // Invoke application callback - if (tuh_cdc_umount_cb) { - tuh_cdc_umount_cb(idx); - } + tuh_cdc_umount_cb(idx); p_cdc->daddr = 0; p_cdc->bInterfaceNumber = 0; @@ -680,9 +697,7 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t if (ep_addr == p_cdc->stream.tx.ep_addr) { // invoke tx complete callback to possibly refill tx fifo - if (tuh_cdc_tx_complete_cb) { - tuh_cdc_tx_complete_cb(idx); - } + tuh_cdc_tx_complete_cb(idx); if (0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx)) { // If there is no data left, a ZLP should be sent if: @@ -697,18 +712,14 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t if (xferred_bytes > 2) { tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf + 2, xferred_bytes - 2); - if (tuh_cdc_rx_cb) { - tuh_cdc_rx_cb(idx); // invoke receive callback - } + tuh_cdc_rx_cb(idx); // invoke receive callback } } else #endif { tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); - if (tuh_cdc_rx_cb) { - tuh_cdc_rx_cb(idx); // invoke receive callback - } + tuh_cdc_rx_cb(idx); // invoke receive callback } // prepare for next transfer if needed @@ -794,9 +805,7 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) { if (success) { const uint8_t idx = get_idx_by_ptr(p_cdc); p_cdc->mounted = true; - if (tuh_cdc_mount_cb) { - tuh_cdc_mount_cb(idx); - } + tuh_cdc_mount_cb(idx); // Prepare for incoming data tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx); } else { diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 37bfca270..bf6711d7e 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -230,16 +230,16 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_disconnect_sync(u // Invoked when a device with CDC interface is mounted // idx is index of cdc interface in the internal pool. -TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx); +extern void tuh_cdc_mount_cb(uint8_t idx); // Invoked when a device with CDC interface is unmounted -TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx); +extern void tuh_cdc_umount_cb(uint8_t idx); // Invoked when received new data -TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx); +extern void tuh_cdc_rx_cb(uint8_t idx); // Invoked when a TX is complete and therefore space becomes available in TX buffer -TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx); +extern void tuh_cdc_tx_complete_cb(uint8_t idx); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c index d9e2d3f2f..0d2b63b57 100644 --- a/src/class/dfu/dfu_device.c +++ b/src/class/dfu/dfu_device.c @@ -76,6 +76,24 @@ static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* reques static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request); static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request); +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_dfu_detach_cb(void) { +} + +TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt) { + (void) alt; +} + +TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length) { + (void) alt; + (void) block_num; + (void) data; + (void) length; + return 0; +} + //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ @@ -234,9 +252,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control if (stage == CONTROL_STAGE_SETUP) { tud_control_status(rhport, request); } else if (stage == CONTROL_STAGE_ACK) { - if (tud_dfu_detach_cb) { - tud_dfu_detach_cb(); - } + tud_dfu_detach_cb(); } break; @@ -258,16 +274,13 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control reset_state(); tud_control_status(rhport, request); } else if (stage == CONTROL_STAGE_ACK) { - if (tud_dfu_abort_cb) { - tud_dfu_abort_cb(_dfu_ctx.alt); - } + tud_dfu_abort_cb(_dfu_ctx.alt); } break; case DFU_REQUEST_UPLOAD: if (stage == CONTROL_STAGE_SETUP) { TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD); - TU_VERIFY(tud_dfu_upload_cb); TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE); const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf, diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h index 00c22ea8b..e59e61ce9 100644 --- a/src/class/dfu/dfu_device.h +++ b/src/class/dfu/dfu_device.h @@ -74,13 +74,13 @@ void tud_dfu_manifest_cb(uint8_t alt); // Invoked when received DFU_UPLOAD request // Application must populate data with up to length bytes and // Return the number of written bytes -TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length); +uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length); // Invoked when a DFU_DETACH request is received -TU_ATTR_WEAK void tud_dfu_detach_cb(void); +void tud_dfu_detach_cb(void); // Invoked when the Host has terminated a download or upload transfer -TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt); +void tud_dfu_abort_cb(uint8_t alt); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index a44c83433..da776d04c 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -70,6 +70,50 @@ CFG_TUH_MEM_SECTION static hidh_epbuf_t _hidh_epbuf[CFG_TUH_HID]; static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT; +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len) { + (void) dev_addr; + (void) idx; + (void) report_desc; + (void) desc_len; +} + +TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) { + (void) dev_addr; + (void) idx; +} + +TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len) { + (void) dev_addr; + (void) idx; + (void) report; + (void) len; +} + +TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) { + (void) dev_addr; + (void) idx; + (void) report_id; + (void) report_type; + (void) len; +} + +TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) { + (void) dev_addr; + (void) idx; + (void) report_id; + (void) report_type; + (void) len; +} + +TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) { + (void) dev_addr; + (void) idx; + (void) protocol; +} + //--------------------------------------------------------------------+ // Helper //--------------------------------------------------------------------+ @@ -183,9 +227,7 @@ static void set_protocol_complete(tuh_xfer_t* xfer) { p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue); } - if (tuh_hid_set_protocol_complete_cb) { - tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode); - } + tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode); } void tuh_hid_set_default_protocol(uint8_t protocol) { @@ -230,16 +272,14 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) { static void get_report_complete(tuh_xfer_t* xfer) { TU_LOG_DRV("HID Get Report complete\r\n"); - if (tuh_hid_get_report_complete_cb) { - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num); + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num); - uint8_t const report_type = tu_u16_high(xfer->setup->wValue); + uint8_t const report_type = tu_u16_high(xfer->setup->wValue); uint8_t const report_id = tu_u16_low(xfer->setup->wValue); - tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type, - (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); - } + tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type, + (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); } bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) { @@ -274,16 +314,14 @@ bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r static void set_report_complete(tuh_xfer_t* xfer) { TU_LOG_DRV("HID Set Report complete\r\n"); - if (tuh_hid_set_report_complete_cb) { - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num); + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num); - uint8_t const report_type = tu_u16_high(xfer->setup->wValue); - uint8_t const report_id = tu_u16_low(xfer->setup->wValue); + uint8_t const report_type = tu_u16_high(xfer->setup->wValue); + uint8_t const report_id = tu_u16_low(xfer->setup->wValue); - tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type, - (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); - } + tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type, + (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0); } bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) { @@ -448,9 +486,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2); tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes); } else { - if (tuh_hid_report_sent_cb) { - tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes); - } + tuh_hid_report_sent_cb(daddr, idx, epbuf->epout, (uint16_t) xferred_bytes); } return true; @@ -461,9 +497,7 @@ void hidh_close(uint8_t daddr) { hidh_interface_t* p_hid = &_hidh_itf[i]; if (p_hid->daddr == daddr) { TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i); - if (tuh_hid_umount_cb) { - tuh_hid_umount_cb(daddr, i); - } + tuh_hid_umount_cb(daddr, i); tu_memclr(p_hid, sizeof(hidh_interface_t)); } } @@ -625,7 +659,7 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con p_hid->mounted = true; // enumeration is complete - if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len); + tuh_hid_mount_cb(daddr, idx, desc_report, desc_len); // notify usbh that driver enumeration is complete usbh_driver_set_config_complete(daddr, p_hid->itf_num); diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index 9681c704b..032827af1 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -145,28 +145,28 @@ bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const // 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 -TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len); +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len); // Invoked when device with hid interface is un-mounted -TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx); +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx); // Invoked when received report from device via interrupt endpoint // Note: if there is report ID (composite), it is 1st byte of report void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len); // Invoked when sent report to device successfully via interrupt endpoint -TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len); +void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len); // Invoked when Get Report to device via either control endpoint // len = 0 indicate there is error in the transfer e.g stalled response -TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len); +void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len); // Invoked when Sent Report to device via either control endpoint // len = 0 indicate there is error in the transfer e.g stalled response -TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len); +void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len); // Invoked when Set Protocol request is complete -TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol); +void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index 0bbb3caf4..7dac7c4a5 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -107,6 +107,14 @@ static void _prep_out_transaction(uint8_t idx) { } } + +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf) { + (void) itf; +} + //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ @@ -528,9 +536,7 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32 tu_fifo_write_n(&p_midi->rx_ff, _midid_epbuf[idx].epout, (uint16_t)xferred_bytes); // invoke receive callback if available - if (tud_midi_rx_cb) { - tud_midi_rx_cb(idx); - } + tud_midi_rx_cb(idx); // prepare for next // TODO for now ep_out is not used by public API therefore there is no race condition, diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h index 3e89cc0a3..c2c6e9859 100644 --- a/src/class/midi/midi_device.h +++ b/src/class/midi/midi_device.h @@ -116,9 +116,9 @@ static inline bool tud_midi_receive(uint8_t packet[4]) } //--------------------------------------------------------------------+ -// Application Callback API (weak is optional) +// Application Callback API (optional) //--------------------------------------------------------------------+ -TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf); +void tud_midi_rx_cb(uint8_t itf); //--------------------------------------------------------------------+ // Inline Functions diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index a32014c2d..b0eafd5da 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -223,6 +223,53 @@ static bool proc_stage_status(mscd_interface_t *p_msc) { return true; } +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun) { + (void) lun; +} + +TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun) { + (void) lun; +} + +TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]) { + (void) lun; + (void) scsi_cmd; +} + +TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void) { + return 1; +} + +TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + (void) lun; + (void) power_condition; + (void) start; + (void) load_eject; + return true; +} + +TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control) { + (void) lun; + (void) prohibit_removal; + (void) control; + return true; +} + +TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize) { + (void) lun; + (void) buffer; + (void) bufsize; + return sizeof(scsi_sense_fixed_resp_t); +} + +TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun) { + (void) lun; + return true; +} + //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ @@ -403,10 +450,7 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t TU_LOG_DRV(" MSC Get Max Lun\r\n"); TU_VERIFY(request->wValue == 0 && request->wLength == 1); - uint8_t maxlun = 1; - if (tud_msc_get_maxlun_cb) { - maxlun = tud_msc_get_maxlun_cb(); - } + uint8_t maxlun = tud_msc_get_maxlun_cb(); TU_VERIFY(maxlun); maxlun--; // MAX LUN is minus 1 by specs tud_control_xfer(rhport, request, &maxlun, 1); @@ -584,21 +628,15 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // if complete_cb() is invoked after queuing the status. switch (p_cbw->command[0]) { case SCSI_CMD_READ_10: - if (tud_msc_read10_complete_cb) { - tud_msc_read10_complete_cb(p_cbw->lun); - } + tud_msc_read10_complete_cb(p_cbw->lun); break; case SCSI_CMD_WRITE_10: - if (tud_msc_write10_complete_cb) { - tud_msc_write10_complete_cb(p_cbw->lun); - } + tud_msc_write10_complete_cb(p_cbw->lun); break; default: - if (tud_msc_scsi_complete_cb) { - tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command); - } + tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command); break; } @@ -648,16 +686,14 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ case SCSI_CMD_START_STOP_UNIT: resplen = 0; - if (tud_msc_start_stop_cb) { - scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd; - if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) { - // Failed status response - resplen = -1; + scsi_start_stop_unit_t const* start_stop = (scsi_start_stop_unit_t const*)scsi_cmd; + if (!tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject)) { + // Failed status response + resplen = -1; - // set default sense if not set by callback - if (p_msc->sense_key == 0) { - set_sense_medium_not_present(lun); - } + // set default sense if not set by callback + if (p_msc->sense_key == 0) { + set_sense_medium_not_present(lun); } } break; @@ -665,16 +701,14 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: resplen = 0; - if (tud_msc_prevent_allow_medium_removal_cb) { - scsi_prevent_allow_medium_removal_t const* prevent_allow = (scsi_prevent_allow_medium_removal_t const*)scsi_cmd; - if (!tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control)) { - // Failed status response - resplen = -1; + scsi_prevent_allow_medium_removal_t const* prevent_allow = (scsi_prevent_allow_medium_removal_t const*)scsi_cmd; + if (!tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control)) { + // Failed status response + resplen = -1; - // set default sense if not set by callback - if (p_msc->sense_key == 0) { - set_sense_medium_not_present(lun); - } + // set default sense if not set by callback + if (p_msc->sense_key == 0) { + set_sense_medium_not_present(lun); } } break; @@ -767,10 +801,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ .block_descriptor_len = 0 // no block descriptor are included }; - bool writable = true; - if (tud_msc_is_writable_cb) { - writable = tud_msc_is_writable_cb(lun); - } + bool writable = tud_msc_is_writable_cb(lun); mode_resp.write_protected = !writable; @@ -794,9 +825,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen)); // request sense callback could overwrite the sense data - if (tud_msc_request_sense_cb) { - resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize); - } + resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize); // Clear sense data after copy tud_msc_set_sense(lun, 0, 0, 0); @@ -854,11 +883,7 @@ static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) { static void proc_write10_cmd(mscd_interface_t* p_msc) { msc_cbw_t const* p_cbw = &p_msc->cbw; - bool writable = true; - - if (tud_msc_is_writable_cb) { - writable = tud_msc_is_writable_cb(p_cbw->lun); - } + bool writable = tud_msc_is_writable_cb(p_cbw->lun); if (!writable) { // Not writable, complete this SCSI op with error diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 144b74f71..7d898e988 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -128,30 +128,30 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, /*------------- Optional callbacks -------------*/ // Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation -TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void); +uint8_t tud_msc_get_maxlun_cb(void); // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage -TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); //Invoked when we receive the Prevent / Allow Medium Removal command -TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control); +bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control); // Invoked when received REQUEST_SENSE -TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize); +int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize); // Invoked when Read10 command is complete -TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun); +void tud_msc_read10_complete_cb(uint8_t lun); // Invoke when Write10 command is complete, can be used to flush flash caching -TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun); +void tud_msc_write10_complete_cb(uint8_t lun); // Invoked when command in tud_msc_scsi_cb is complete -TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]); +void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]); // Invoked to check if device is writable as part of SCSI WRITE10 -TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun); +bool tud_msc_is_writable_cb(uint8_t lun); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index ef0635bbe..eb69ae400 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -87,6 +87,17 @@ TU_ATTR_ALWAYS_INLINE static inline msch_epbuf_t* get_epbuf(uint8_t daddr) { return &_msch_epbuf[daddr - 1]; } +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr) { + (void) dev_addr; +} + +TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr) { + (void) dev_addr; +} + //--------------------------------------------------------------------+ // PUBLIC API //--------------------------------------------------------------------+ @@ -304,9 +315,7 @@ void msch_close(uint8_t dev_addr) { // invoke Application Callback if (p_msc->mounted) { - if (tuh_msc_umount_cb) { - tuh_msc_umount_cb(dev_addr); - } + tuh_msc_umount_cb(dev_addr); } tu_memclr(p_msc, sizeof(msch_interface_t)); @@ -497,9 +506,7 @@ static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_dat // Mark enumeration is complete p_msc->mounted = true; - if (tuh_msc_mount_cb) { - tuh_msc_mount_cb(dev_addr); - } + tuh_msc_mount_cb(dev_addr); // notify usbh that driver enumeration is complete usbh_driver_set_config_complete(dev_addr, p_msc->itf_num); diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index 09d777066..b5fd55547 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -109,10 +109,10 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r //------------- Application Callback -------------// // Invoked when a device with MassStorage interface is mounted -TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr); +void tuh_msc_mount_cb(uint8_t dev_addr); // Invoked when a device with MassStorage interface is unmounted -TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr); +void tuh_msc_umount_cb(uint8_t dev_addr); //--------------------------------------------------------------------+ // Internal Class Driver API diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c index 7ec939b64..3f6bedd4c 100644 --- a/src/class/usbtmc/usbtmc_device.c +++ b/src/class/usbtmc/usbtmc_device.c @@ -159,6 +159,25 @@ TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u, "USBTMC dev buffer size too small") static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len); static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen); + +// USBTMC Device Callbacks weak implementations +TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void) { + return true; +} + +TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult) { + (void) msg; + (void) tmcResult; + return true; +} + +#if (CFG_TUD_USBTMC_ENABLE_488) +TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg) { + (void) msg; + return true; +} +#endif + #ifndef NDEBUG tu_static uint8_t termChar; #endif @@ -262,16 +281,10 @@ void usbtmcd_init_cb(void) { usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb(); #ifndef NDEBUG #if CFG_TUD_USBTMC_ENABLE_488 - if (usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger) { - TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL, ); - } // Per USB488 spec: table 8 TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly, ); TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly, ); #endif - if (usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse) { - TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL, ); - } #endif usbtmcLock = osal_mutex_create(&usbtmcLockBuffer); @@ -587,9 +600,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint TU_ASSERT(false); } } else if (ep_addr == usbtmc_state.ep_int_in) { - if (tud_usbtmc_notification_complete_cb) { - TU_VERIFY(tud_usbtmc_notification_complete_cb()); - } + TU_VERIFY(tud_usbtmc_notification_complete_cb()); return true; } return false; diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h index b85ef12b5..8238f579f 100644 --- a/src/class/usbtmc/usbtmc_device.h +++ b/src/class/usbtmc/usbtmc_device.h @@ -75,15 +75,15 @@ bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp); // The interrupt-IN endpoint buffer was transmitted to the host. Use // tud_usbtmc_transmit_notification_data to send another notification. -TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void); +bool tud_usbtmc_notification_complete_cb(void); // Indicator pulse should be 0.5 to 1.0 seconds long -TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult); +bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult); #if (CFG_TUD_USBTMC_ENABLE_488) uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult); -TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg); -//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb(); +bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg); +//void tud_usbtmc_app_go_to_local_cb(); #endif // Called from app diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c index b0332b0fe..27724b194 100644 --- a/src/class/vendor/vendor_device.c +++ b/src/class/vendor/vendor_device.c @@ -67,6 +67,20 @@ typedef struct { CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR]; +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize) { + (void) itf; + (void) buffer; + (void) bufsize; +} + +TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { + (void) itf; + (void) sent_bytes; +} + //-------------------------------------------------------------------- // Application API //-------------------------------------------------------------------- @@ -259,16 +273,12 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint tu_edpt_stream_read_xfer_complete(&p_vendor->rx.stream, xferred_bytes); // Invoked callback if any - if (tud_vendor_rx_cb) { - tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes); - } + tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes); tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream); } else if ( ep_addr == p_vendor->tx.stream.ep_addr ) { // Send complete - if (tud_vendor_tx_cb) { - tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes); - } + tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes); #if CFG_TUD_VENDOR_TX_BUFSIZE > 0 // try to send more if possible diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h index 149ae2d56..5fe4fc9ff 100644 --- a/src/class/vendor/vendor_device.h +++ b/src/class/vendor/vendor_device.h @@ -119,9 +119,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) { //--------------------------------------------------------------------+ // Invoked when received new data -TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize); +void tud_vendor_rx_cb(uint8_t itf, uint8_t const* buffer, uint16_t bufsize); // Invoked when last rx transfer finished -TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes); +void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes); //--------------------------------------------------------------------+ // Inline Functions diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 3124d5596..5c00cc358 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -192,6 +192,28 @@ static char const* const tu_str_video_vs_control_selector[] = { #endif +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { + (void) ctl_idx; + (void) stm_idx; +} + +TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod) { + (void) ctl_idx; + (void) power_mod; + return VIDEO_ERROR_NONE; +} + +TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, + video_probe_and_commit_control_t const *parameters) { + (void) ctl_idx; + (void) stm_idx; + (void) parameters; + return VIDEO_ERROR_NONE; +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -902,7 +924,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN); TU_VERIFY(tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode)), VIDEO_ERROR_UNKNOWN); } else if (stage == CONTROL_STAGE_DATA) { - if (tud_video_power_mode_cb) return tud_video_power_mode_cb(ctl_idx, self->power_mode); + return tud_video_power_mode_cb(ctl_idx, self->power_mode); } return VIDEO_ERROR_NONE; @@ -1104,10 +1126,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, TU_VERIFY(_update_streaming_parameters(stm, param), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE); /* Set the negotiated value */ stm->max_payload_transfer_size = param->dwMaxPayloadTransferSize; - int ret = VIDEO_ERROR_NONE; - if (tud_video_commit_cb) { - ret = tud_video_commit_cb(stm->index_vc, stm->index_vs, param); - } + int ret = tud_video_commit_cb(stm->index_vc, stm->index_vs, param); if (VIDEO_ERROR_NONE == ret) { stm->state = VS_STATE_COMMITTED; stm->buffer = NULL; @@ -1419,9 +1438,7 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 stm->buffer = NULL; stm->bufsize = 0; stm->offset = 0; - if (tud_video_frame_xfer_complete_cb) { - tud_video_frame_xfer_complete_cb(stm->index_vc, stm->index_vs); - } + tud_video_frame_xfer_complete_cb(stm->index_vc, stm->index_vs); } return true; } diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h index 648a221d5..2b41c3bfe 100644 --- a/src/class/video/video_device.h +++ b/src/class/video/video_device.h @@ -61,7 +61,7 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu * * @param[in] ctl_idx Destination control interface index * @param[in] stm_idx Destination streaming interface index */ -TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx); +void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx); //--------------------------------------------------------------------+ // Application Callback API (weak is optional) @@ -72,7 +72,7 @@ TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fa * @param[in] ctl_idx Destination control interface index * @param[in] stm_idx Destination streaming interface index * @return video_error_code_t */ -TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod); +int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod); /** Invoked when VS_COMMIT_CONTROL(SET_CUR) request received * @@ -80,7 +80,7 @@ TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod * @param[in] stm_idx Destination streaming interface index * @param[in] parameters Video streaming parameters * @return video_error_code_t */ -TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, +int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, video_probe_and_commit_control_t const *parameters); //--------------------------------------------------------------------+ diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index e35d3e6fe..9c4699362 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -89,14 +89,14 @@ extern uint32_t tusb_time_millis_api(void); extern void tusb_time_delay_ms_api(uint32_t ms); // flush data cache -TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); +extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size); // invalidate data cache -TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size); +extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size); // Optional physical <-> virtual address translation -TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr); -TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr); +extern void* tusb_app_virt_to_phys(void *virt_addr); +extern void* tusb_app_phys_to_virt(void *phys_addr); //--------------------------------------------------------------------+ // Internal Inline Functions diff --git a/src/device/dcd.h b/src/device/dcd.h index 789552d47..400f62bff 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -162,7 +162,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer // Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack // This API is optional, may be useful for register-based for transferring data. -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK; +bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes); // Stall endpoint, any queuing transfer should be removed from endpoint void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr); diff --git a/src/device/usbd.c b/src/device/usbd.c index b09d02fbd..6b5c3d846 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -393,6 +393,13 @@ void usbd_control_set_request(tusb_control_request_t const *request); void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp ); bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { + (void) driver_count; + return NULL; +} //--------------------------------------------------------------------+ // Debug @@ -516,10 +523,8 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { TU_ASSERT(_usbd_q); // Get application driver if available - if (usbd_app_driver_get_cb) { - _app_driver = usbd_app_driver_get_cb(&_app_driver_count); - TU_ASSERT(_app_driver_count + BUILTIN_DRIVER_COUNT <= UINT8_MAX); - } + _app_driver = usbd_app_driver_get_cb(&_app_driver_count); + TU_ASSERT(_app_driver_count + BUILTIN_DRIVER_COUNT <= UINT8_MAX); // Init class drivers for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) { diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h index f1797bf0d..a688cf497 100644 --- a/src/device/usbd_pvt.h +++ b/src/device/usbd_pvt.h @@ -64,7 +64,7 @@ typedef struct { // Invoked when initializing device stack to get additional class drivers. // Can be implemented by application to extend/overwrite class driver support. // Note: The drivers array must be accessible at all time when stack is active -usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR_WEAK; +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count); typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); diff --git a/src/host/usbh.c b/src/host/usbh.c index d09874d6e..fafcc0fef 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -88,6 +88,19 @@ TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_si return false; } +TU_ATTR_WEAK usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count) { + (void) driver_count; + return NULL; +} + +TU_ATTR_WEAK void tuh_mount_cb(uint8_t daddr) { + (void) daddr; +} + +TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr) { + (void) daddr; +} + //--------------------------------------------------------------------+ // Data Structure //--------------------------------------------------------------------+ @@ -481,9 +494,7 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { #endif // Get application driver if available - if (usbh_app_driver_get_cb) { - _app_driver = usbh_app_driver_get_cb(&_app_driver_count); - } + _app_driver = usbh_app_driver_get_cb(&_app_driver_count); // Device tu_memclr(_usbh_devices, sizeof(_usbh_devices)); @@ -1319,9 +1330,7 @@ static void process_removed_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub #endif { // Invoke callback before closing driver (maybe call it later ?) - if (tuh_umount_cb) { - tuh_umount_cb(daddr); - } + tuh_umount_cb(daddr); } // Close class driver @@ -1910,9 +1919,7 @@ void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { TU_LOG_USBH("HUB address = %u is mounted\r\n", dev_addr); }else { // Invoke callback if available - if (tuh_mount_cb) { - tuh_mount_cb(dev_addr); - } + tuh_mount_cb(dev_addr); } } } diff --git a/src/host/usbh.h b/src/host/usbh.h index 1e9bb26bc..8d48bf90d 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -123,13 +123,13 @@ void tuh_enum_descriptor_device_cb(uint8_t daddr, const tusb_desc_device_t *desc bool tuh_enum_descriptor_configuration_cb(uint8_t daddr, uint8_t cfg_index, const tusb_desc_configuration_t *desc_config); // Invoked when a device is mounted (configured) -TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr); +void tuh_mount_cb (uint8_t daddr); // Invoked when a device failed to mount during enumeration process -// TU_ATTR_WEAK void tuh_mount_failed_cb (uint8_t daddr); +// void tuh_mount_failed_cb (uint8_t daddr); // Invoked when a device is unmounted (detached) -TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr); +void tuh_umount_cb(uint8_t daddr); // Invoked when there is a new usb event, which need to be processed by tuh_task()/tuh_task_ext() void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr); diff --git a/src/host/usbh_pvt.h b/src/host/usbh_pvt.h index cb092e5f3..9d91e52e8 100644 --- a/src/host/usbh_pvt.h +++ b/src/host/usbh_pvt.h @@ -58,7 +58,7 @@ typedef struct { // Invoked when initializing host stack to get additional class drivers. // Can be implemented by application to extend/overwrite class driver support. // Note: The drivers array must be accessible at all time when stack is active -usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count) TU_ATTR_WEAK; +usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count); // Call by class driver to tell USBH that it has complete the enumeration void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num); diff --git a/src/typec/usbc.c b/src/typec/usbc.c index fdf2a0cd6..20abd1700 100644 --- a/src/typec/usbc.c +++ b/src/typec/usbc.c @@ -59,6 +59,23 @@ bool usbc_msg_send(uint8_t rhport, pd_header_t const* header, void const* data); bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end); bool parse_msg_control(uint8_t rhport, pd_header_t const* header); +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) { + (void) rhport; + (void) header; + (void) dobj; + (void) p_end; + return false; +} + +TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header) { + (void) rhport; + (void) header; + return false; +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -136,17 +153,13 @@ void tuc_task_ext(uint32_t timeout_ms, bool in_isr) { } bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) { - if (tuc_pd_data_received_cb) { - tuc_pd_data_received_cb(rhport, header, dobj, p_end); - } + tuc_pd_data_received_cb(rhport, header, dobj, p_end); return true; } bool parse_msg_control(uint8_t rhport, pd_header_t const* header) { - if (tuc_pd_control_received_cb) { - tuc_pd_control_received_cb(rhport, header); - } + tuc_pd_control_received_cb(rhport, header); return true; } diff --git a/src/typec/usbc.h b/src/typec/usbc.h index 9fbff9bc6..448542aab 100644 --- a/src/typec/usbc.h +++ b/src/typec/usbc.h @@ -74,8 +74,8 @@ extern void tcd_int_handler(uint8_t rhport); // Callbacks //--------------------------------------------------------------------+ -TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end); -TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header); +bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end); +bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header); //--------------------------------------------------------------------+ // -- cgit v1.3.1 From 73f3900b2da5d00e707078a591067f0329118dcb Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 17 Sep 2025 22:45:25 +0200 Subject: Replace old delay functions by tusb_time_delay_ms_api Signed-off-by: HiFiPhile --- src/class/cdc/cdc_rndis_host.c | 2 +- src/osal/osal_none.h | 9 --------- src/portable/microchip/pic32mz/dcd_pic32mz.c | 8 +------- src/portable/ohci/ohci.c | 12 ++---------- src/portable/renesas/rusb2/rusb2_common.c | 4 ---- src/portable/sunxi/dcd_sunxi_musb.c | 12 +----------- src/portable/ti/msp430x5xx/dcd_msp430x5xx.c | 18 ++---------------- 7 files changed, 7 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c index 11a5355aa..e975ea440 100644 --- a/src/class/cdc/cdc_rndis_host.c +++ b/src/class/cdc/cdc_rndis_host.c @@ -103,7 +103,7 @@ static tusb_error_t rndis_body_subtask(void) } - osal_task_delay(100); + tusb_time_delay_ms_api(100); OSAL_SUBTASK_END } diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h index a8eb1042b..3e397ef35 100644 --- a/src/osal/osal_none.h +++ b/src/osal/osal_none.h @@ -31,15 +31,6 @@ extern "C" { #endif -//--------------------------------------------------------------------+ -// TASK API -//--------------------------------------------------------------------+ - -#if CFG_TUH_ENABLED -// currently only needed/available in host mode -TU_ATTR_WEAK void osal_task_delay(uint32_t msec); -#endif - //--------------------------------------------------------------------+ // Spinlock API //--------------------------------------------------------------------+ diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c index 8709baf67..cbd157d6b 100644 --- a/src/portable/microchip/pic32mz/dcd_pic32mz.c +++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c @@ -162,13 +162,7 @@ void dcd_remote_wakeup(uint8_t rhport) (void) rhport; USB_REGS->POWERbits.RESUME = 1; -#if CFG_TUSB_OS != OPT_OS_NONE - osal_task_delay(10); -#else - // TODO: Wait in non blocking mode - unsigned cnt = 2000; - while (cnt--) __asm__("nop"); -#endif + tusb_time_delay_ms_api(10); USB_REGS->POWERbits.RESUME = 0; } diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c index 81091c9a7..f517e044e 100644 --- a/src/portable/ohci/ohci.c +++ b/src/portable/ohci/ohci.c @@ -208,12 +208,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { //Wait 20 ms. (Ref Usb spec 7.1.7.7) OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_RESUME; -#if CFG_TUSB_OS != OPT_OS_NONE - // os_none implement task delay using usb frame counter which is not started yet - // therefore cause infinite delay. - // TODO find a way to delay in case of os none e.g __nop - osal_task_delay(20); -#endif + tusb_time_delay_ms_api(20); } // reset controller @@ -241,10 +236,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { OHCI_REG->control_bit.hc_functional_state = OHCI_CONTROL_FUNCSTATE_OPERATIONAL; // make HC's state to operational state TODO use this to suspend (save power) OHCI_REG->rh_status_bit.local_power_status_change = 1; // set global power for ports -#if CFG_TUSB_OS != OPT_OS_NONE - // TODO as above delay - osal_task_delay(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up -#endif + tusb_time_delay_ms_api(OHCI_REG->rh_descriptorA_bit.power_on_to_good_time * 2); // Wait POTG after power up return true; } diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c index 72e65736b..856f9714f 100644 --- a/src/portable/renesas/rusb2/rusb2_common.c +++ b/src/portable/renesas/rusb2/rusb2_common.c @@ -50,10 +50,6 @@ void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum) { rusb2_controller[rhport].irqnum = irqnum; } -// void osal_task_delay(uint32_t msec) { -// R_BSP_SoftwareDelay(msec, BSP_DELAY_UNITS_MILLISECONDS); -// } - #else #error "Unsupported MCU" #endif diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c index 21f13b279..9801a485f 100644 --- a/src/portable/sunxi/dcd_sunxi_musb.c +++ b/src/portable/sunxi/dcd_sunxi_musb.c @@ -99,22 +99,12 @@ static void usb_phy_write(int addr, int data, int len) } } -static void delay_ms(uint32_t ms) -{ -#if CFG_TUSB_OS == OPT_OS_NONE - int now = board_millis(); - while (board_millis() - now <= ms) asm("nop"); -#else - osal_task_delay(ms); -#endif -} - static void USBC_HardwareReset(void) { // Reset phy and controller USBC_REG_set_bit_l(USBPHY_CLK_RST_BIT, USBPHY_CLK_REG); USBC_REG_set_bit_l(BUS_RST_USB_BIT, BUS_CLK_RST_REG); - delay_ms(2); + tusb_time_delay_ms_api(2); USBC_REG_set_bit_l(USBPHY_CLK_GAT_BIT, USBPHY_CLK_REG); USBC_REG_set_bit_l(USBPHY_CLK_RST_BIT, USBPHY_CLK_REG); diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c index 3752ae251..64cbc5087 100644 --- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c +++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c @@ -662,24 +662,10 @@ static void handle_setup_packet(void) dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true); } -#if CFG_TUSB_OS == OPT_OS_NONE -TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) { - // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles - // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop - volatile uint32_t cycles = (25000 * ms) >> 2; - while (cycles > 0) { - cycles--; - asm("nop"); - } -} -#else -#define tu_delay(ms) osal_task_delay(ms) -#endif - static void handle_bus_power_event(void *param) { (void) param; - tu_delay(5); // Bus power settling delay. + tusb_time_delay_ms_api(5); // Bus power settling delay. USBKEYPID = USBKEY; @@ -694,7 +680,7 @@ static void handle_bus_power_event(void *param) { uint16_t attempts = 0; do { // Poll the PLL, checking for a successful lock. USBPLLIR = 0; - tu_delay(1); + tusb_time_delay_ms_api(1); attempts++; } while ((attempts < 10) && (USBPLLIR != 0)); -- cgit v1.3.1 From ce41292f1b49fe39d54a9d4c2951fec56b8c13f5 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 17 Sep 2025 23:48:58 +0200 Subject: Fix address translation functions Signed-off-by: HiFiPhile --- src/portable/ohci/ohci.c | 6 ++---- src/tusb.c | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c index f517e044e..f1689b5b4 100644 --- a/src/portable/ohci/ohci.c +++ b/src/portable/ohci/ohci.c @@ -168,14 +168,12 @@ static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd); // tusb_app_virt_to_phys and tusb_app_virt_to_phys in your application. TU_ATTR_ALWAYS_INLINE static inline void *_phys_addr(void *virtual_address) { - if (tusb_app_virt_to_phys) return tusb_app_virt_to_phys(virtual_address); - return virtual_address; + return tusb_app_virt_to_phys(virtual_address); } TU_ATTR_ALWAYS_INLINE static inline void *_virt_addr(void *physical_address) { - if (tusb_app_phys_to_virt) return tusb_app_phys_to_virt(physical_address); - return physical_address; + return tusb_app_phys_to_virt(physical_address); } // Initialization according to 5.1.1.4 diff --git a/src/tusb.c b/src/tusb.c index 5a3ea356b..083e6d861 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -55,6 +55,14 @@ TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { #endif } +TU_ATTR_WEAK void* tusb_app_virt_to_phys(void *virt_addr) { + return virt_addr; +} + +TU_ATTR_WEAK void* tusb_app_phys_to_virt(void *phys_addr) { + return phys_addr; +} + //--------------------------------------------------------------------+ // Public API //--------------------------------------------------------------------+ -- cgit v1.3.1 From 9d13c8fce0f26a161e7ef27565a6f2f54db91813 Mon Sep 17 00:00:00 2001 From: Zixun LI Date: Thu, 18 Sep 2025 00:16:27 +0200 Subject: Potential fix for code scanning alert no. 914: Commented-out code Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/class/usbtmc/usbtmc_device.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h index 8238f579f..235f1276e 100644 --- a/src/class/usbtmc/usbtmc_device.h +++ b/src/class/usbtmc/usbtmc_device.h @@ -83,7 +83,6 @@ bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t * #if (CFG_TUD_USBTMC_ENABLE_488) uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult); bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg); -//void tud_usbtmc_app_go_to_local_cb(); #endif // Called from app -- cgit v1.3.1 From e621f2f77b19dce0a9f288ebd608ebf98d2f817b Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Thu, 18 Sep 2025 19:09:10 +0200 Subject: set driver count to 0 Signed-off-by: HiFiPhile --- src/device/usbd.c | 2 +- src/host/usbh.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/device/usbd.c b/src/device/usbd.c index 6b5c3d846..5792359f6 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -397,7 +397,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, // Weak stubs: invoked if no strong implementation is available //--------------------------------------------------------------------+ TU_ATTR_WEAK usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { - (void) driver_count; + *driver_count = 0; return NULL; } diff --git a/src/host/usbh.c b/src/host/usbh.c index fafcc0fef..6bafde368 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -89,7 +89,7 @@ TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_si } TU_ATTR_WEAK usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count) { - (void) driver_count; + *driver_count = 0; return NULL; } -- cgit v1.3.1 From 0961e06845591792031b69515bc56f2e5b3fa60d Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Thu, 18 Sep 2025 19:14:27 +0200 Subject: Add usbd_edpt_xfer_fifo stub Signed-off-by: HiFiPhile --- src/device/usbd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/device/usbd.c b/src/device/usbd.c index 5792359f6..b1aef0b38 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -401,6 +401,11 @@ TU_ATTR_WEAK usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_c return NULL; } +TU_ATTR_WEAK bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) { + (void) rhport; (void) ep_addr; (void) ff; (void) total_bytes; + return false; +} + //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ -- cgit v1.3.1 From f99f203c28b79f00c5eb82e34c869efb3c5034a0 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Sep 2025 11:44:21 +0700 Subject: reworking MTP API, adding callback, getting GetDeviceInfo working --- examples/device/mtp/src/mtp_fs_example.c | 942 ++++++++++++++++--------------- src/class/mtp/mtp.h | 181 ++++-- src/class/mtp/mtp_device.c | 315 ++++------- src/class/mtp/mtp_device.h | 71 ++- 4 files changed, 787 insertions(+), 722 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 9dbac746b..68140b1ae 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -29,6 +29,58 @@ #define MTPD_STORAGE_DESCRIPTION "storage" #define MTPD_VOLUME_IDENTIFIER "volume" +//--------------------------------------------------------------------+ +// Device Info +//--------------------------------------------------------------------+ + +// device info string (including terminating null) +const uint16_t dev_info_manufacturer[] = { 'T', 'i', 'n', 'y', 'U', 'S', 'B', 0 }; +const uint16_t dev_info_model[] = { 'M', 'T', 'P', ' ', 'E', 'x', 'a', 'm', 'p', 'l', 'e', 0 }; +const uint16_t dev_info_version[] = { '1', '.', '0', 0 }; +const uint16_t dev_info_serial[] = { '1', '2', '3', '4', '5', '6', 0 }; + + +static const uint16_t supported_operations[] = { + MTP_OP_GET_DEVICE_INFO, + MTP_OP_OPEN_SESSION, + MTP_OP_CLOSE_SESSION, + MTP_OP_GET_STORAGE_IDS, + MTP_OP_GET_STORAGE_INFO, + MTP_OP_GET_NUM_OBJECTS, + MTP_OP_GET_OBJECT_HANDLES, + MTP_OP_GET_OBJECT_INFO, + MTP_OP_GET_OBJECT, + MTP_OP_DELETE_OBJECT, + MTP_OP_SEND_OBJECT_INFO, + MTP_OP_SEND_OBJECT, + MTP_OP_FORMAT_STORE, + MTP_OP_RESET_DEVICE, + MTP_OP_GET_DEVICE_PROP_DESC, + MTP_OP_GET_DEVICE_PROP_VALUE, + MTP_OP_SET_DEVICE_PROP_VALUE +}; + +static const uint16_t supported_events[] = { + MTP_EVENT_OBJECT_ADDED, +}; + +static const uint16_t supported_device_properties[] = { + MTP_DEV_PROP_DEVICE_FRIENDLY_NAME, +}; + +static const uint16_t capture_formats[] = { + MTP_OBJ_FORMAT_UNDEFINED, + MTP_OBJ_FORMAT_ASSOCIATION, + MTP_OBJ_FORMAT_TEXT, +}; + +static const uint16_t playback_formats[] = { + MTP_OBJ_FORMAT_UNDEFINED, + MTP_OBJ_FORMAT_ASSOCIATION, + MTP_OBJ_FORMAT_TEXT, +}; + + //--------------------------------------------------------------------+ // RAM FILESYSTEM //--------------------------------------------------------------------+ @@ -37,56 +89,54 @@ #define FS_MAX_NODE_NAME_LEN 64UL #define FS_ISODATETIME_LEN 26UL -typedef struct -{ - uint32_t handle; - uint32_t parent; - uint32_t size; - bool allocated; - bool association; - char name[FS_MAX_NODE_NAME_LEN]; - char created[FS_ISODATETIME_LEN]; - char modified[FS_ISODATETIME_LEN]; - uint8_t data[FS_MAX_NODE_BYTES]; +typedef struct { + uint32_t handle; + uint32_t parent; + uint32_t size; + bool allocated; + bool association; + char name[FS_MAX_NODE_NAME_LEN]; + char created[FS_ISODATETIME_LEN]; + char modified[FS_ISODATETIME_LEN]; + uint8_t data[FS_MAX_NODE_BYTES]; } fs_object_info_t; // Sample object file static fs_object_info_t _fs_objects[FS_MAX_NODES] = { - { - .handle = 1, - .parent = 0, - .allocated = true, - .association = false, - .name = "readme.txt", - .created = "20240104T111134.0", - .modified = "20241214T121110.0", - .data = "USB MTP on RAM Filesystem example\n", - .size = 34 - } + { + .handle = 1, + .parent = 0, + .allocated = true, + .association = false, + .name = "readme.txt", + .created = "20240104T111134.0", + .modified = "20241214T121110.0", + .data = "USB MTP on RAM Filesystem example\n", + .size = 34 + } }; //--------------------------------------------------------------------+ // OPERATING STATUS //--------------------------------------------------------------------+ -typedef struct -{ - // Session - uint32_t session_id; - // Association traversal - uint32_t traversal_parent; - uint32_t traversal_index; - // Object open for reading - uint32_t read_handle; - uint32_t read_pos; - // Object open for writing - uint32_t write_handle; - uint32_t write_pos; - // Unique identifier - uint32_t last_handle; +typedef struct { + // Session + uint32_t session_id; + // Association traversal + uint32_t traversal_parent; + uint32_t traversal_index; + // Object open for reading + uint32_t read_handle; + uint32_t read_pos; + // Object open for writing + uint32_t write_handle; + uint32_t write_pos; + // Unique identifier + uint32_t last_handle; } fs_operation_t; static fs_operation_t _fs_operation = { - .last_handle = 1 + .last_handle = 1 }; //--------------------------------------------------------------------+ @@ -94,481 +144,461 @@ static fs_operation_t _fs_operation = { //--------------------------------------------------------------------+ // Get pointer to object info from handle -fs_object_info_t *fs_object_get_from_handle(uint32_t handle); +fs_object_info_t* fs_object_get_from_handle(uint32_t handle); // Get the number of allocated nodes in filesystem unsigned int fs_get_object_count(void); -fs_object_info_t *fs_object_get_from_handle(uint32_t handle) -{ - fs_object_info_t *obj; - for (unsigned int i=0; iallocated && obj->handle == handle) - return obj; - } - return NULL; +fs_object_info_t* fs_object_get_from_handle(uint32_t handle) { + fs_object_info_t* obj; + for (unsigned int i = 0; i < FS_MAX_NODES; i++) { + obj = &_fs_objects[i]; + if (obj->allocated && obj->handle == handle) + return obj; + } + return NULL; } -unsigned int fs_get_object_count(void) -{ - unsigned int s = 0; - for (unsigned int i = 0; icode) { + // default: break; + // } + resp_block->len = MTP_CONTAINER_HEADER_LENGTH; + resp_block->code = (xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; -mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) -{ - if (session_id != _fs_operation.session_id) - { - TU_LOG1("ERR: Session %ld not open\r\n", session_id); - return MTP_RESP_SESSION_NOT_OPEN; - } - _fs_operation.session_id = 0; - TU_LOG1("Session closed\r\n"); - return MTP_RESP_OK; -} + tud_mtp_response_send(resp_block); -mtp_response_t tud_mtp_get_storage_id(uint32_t *storage_id) -{ - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - *storage_id = STORAGE_ID(0x0001, 0x0001); - TU_LOG1("Retrieved storage identifier %ld\r\n", *storage_id); - return MTP_RESP_OK; + return 0; } -mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t *info) -{ - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - if (storage_id != STORAGE_ID(0x0001, 0x0001)) - { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } - info->storage_type = MTP_STORAGE_TYPE_FIXED_RAM; - info->filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL; - info->access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE; - info->max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES; - info->free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); - info->free_space_in_bytes = info->free_space_in_objects * FS_MAX_NODE_BYTES; - mtpd_gct_append_wstring(MTPD_STORAGE_DESCRIPTION); - mtpd_gct_append_wstring(MTPD_VOLUME_IDENTIFIER); - return MTP_RESP_OK; -} +int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block) { + (void)idx; + switch (cmd_block->code) { + case MTP_OP_GET_DEVICE_INFO: { + // Device info is already prepared up to playback formats. Application need to add string fields + mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_manufacturer), dev_info_manufacturer); + mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_model), dev_info_model); + mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_version), dev_info_version); + mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_serial), dev_info_serial); -mtp_response_t tud_mtp_storage_format(uint32_t storage_id) -{ - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - if (storage_id != STORAGE_ID(0x0001, 0x0001)) - { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; + tud_mtp_data_send(out_block); + break; } - // Simply deallocate all entries - for (unsigned int i=0; ilen = MTP_CONTAINER_HEADER_LENGTH; + out_block->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + // TODO check if session is already opened + out_block->code = MTP_RESP_OK; -mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t storage_id, uint32_t parent_object_handle, uint32_t *next_child_handle) -{ - fs_object_info_t *obj; + tud_mtp_response_send(out_block); + break; - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - // We just have one storage, same reply if querying all storages - if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) - { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } + default: return -1; + } - // Request for objects with no parent (0xFFFFFFFF) are considered root objects - // Note: implementation may pass 0 as parent_object_handle - if (parent_object_handle == 0xFFFFFFFF) - parent_object_handle = 0; + return 0; +} - if (parent_object_handle != _fs_operation.traversal_parent) - { - _fs_operation.traversal_parent = parent_object_handle; - _fs_operation.traversal_index = 0; - } +//--------------------------------------------------------------------+ +// API +//--------------------------------------------------------------------+ +mtp_response_t tud_mtp_storage_open_session(uint32_t* session_id) { + if (*session_id == 0) { + TU_LOG1("Invalid session ID\r\n"); + return MTP_RESP_INVALID_PARAMETER; + } + if (_fs_operation.session_id != 0) { + *session_id = _fs_operation.session_id; + TU_LOG1("ERR: Session %ld already open\r\n", _fs_operation.session_id); + return MTP_RESP_SESSION_ALREADY_OPEN; + } + _fs_operation.session_id = *session_id; + TU_LOG1("Open session with id %ld\r\n", _fs_operation.session_id); + return MTP_RESP_OK; +} - for (unsigned int i=_fs_operation.traversal_index; iallocated && obj->parent == parent_object_handle) - { - _fs_operation.traversal_index = i+1; - *next_child_handle = obj->handle; - TU_LOG1("Association %ld -> child %ld\r\n", parent_object_handle, obj->handle); - return MTP_RESP_OK; - } - } - TU_LOG1("Association traversal completed\r\n"); - _fs_operation.traversal_index = 0; - *next_child_handle = 0; - return MTP_RESP_OK; +mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) { + if (session_id != _fs_operation.session_id) { + TU_LOG1("ERR: Session %ld not open\r\n", session_id); + return MTP_RESP_SESSION_NOT_OPEN; + } + _fs_operation.session_id = 0; + TU_LOG1("Session closed\r\n"); + return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_t *info) -{ - fs_object_info_t *obj = NULL; +mtp_response_t tud_mtp_get_storage_id(uint32_t* storage_id) { + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } + *storage_id = STORAGE_ID(0x0001, 0x0001); + TU_LOG1("Retrieved storage identifier %ld\r\n", *storage_id); + return MTP_RESP_OK; +} - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - // Accept command on default storage - if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) - { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } +mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t* info) { + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } + if (storage_id != STORAGE_ID(0x0001, 0x0001)) { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESP_INVALID_STORAGE_ID; + } + info->storage_type = MTP_STORAGE_TYPE_FIXED_RAM; + info->filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL; + info->access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE; + info->max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES; + info->free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); + info->free_space_in_bytes = info->free_space_in_objects * FS_MAX_NODE_BYTES; + mtpd_gct_append_wstring(MTPD_STORAGE_DESCRIPTION); + mtpd_gct_append_wstring(MTPD_VOLUME_IDENTIFIER); + return MTP_RESP_OK; +} - if (info->object_compressed_size > FS_MAX_NODE_BYTES) - { - TU_LOG1("Object size %ld is more than maximum %ld\r\n", info->object_compressed_size, FS_MAX_NODE_BYTES); - return MTP_RESP_STORE_FULL; - } +mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } + if (storage_id != STORAGE_ID(0x0001, 0x0001)) { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESP_INVALID_STORAGE_ID; + } + + // Simply deallocate all entries + for (unsigned int i = 0; i < FS_MAX_NODES; i++) + _fs_objects[i].allocated = false; + TU_LOG1("Format completed\r\n"); + return MTP_RESP_OK; +} - // Request for objects with no parent (0xFFFFFFFF) are considered root objects - if (parent_object == 0xFFFFFFFF) - parent_object = 0; - - // Ensure we are not creating an orphaned object outside root - if (parent_object != 0) - { - obj = fs_object_get_from_handle(parent_object); - if (obj == NULL) - { - TU_LOG1("Parent %ld does not exist\r\n", parent_object); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - if (!obj->association) - { - TU_LOG1("Parent %ld is not an association\r\n", parent_object); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - } +mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t storage_id, uint32_t parent_object_handle, + uint32_t* next_child_handle) { + fs_object_info_t* obj; + + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } + // We just have one storage, same reply if querying all storages + if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESP_INVALID_STORAGE_ID; + } + + // Request for objects with no parent (0xFFFFFFFF) are considered root objects + // Note: implementation may pass 0 as parent_object_handle + if (parent_object_handle == 0xFFFFFFFF) + parent_object_handle = 0; + + if (parent_object_handle != _fs_operation.traversal_parent) { + _fs_operation.traversal_parent = parent_object_handle; + _fs_operation.traversal_index = 0; + } + + for (unsigned int i = _fs_operation.traversal_index; i < FS_MAX_NODES; i++) { + obj = &_fs_objects[i]; + if (obj->allocated && obj->parent == parent_object_handle) { + _fs_operation.traversal_index = i + 1; + *next_child_handle = obj->handle; + TU_LOG1("Association %ld -> child %ld\r\n", parent_object_handle, obj->handle); + return MTP_RESP_OK; + } + } + TU_LOG1("Association traversal completed\r\n"); + _fs_operation.traversal_index = 0; + *next_child_handle = 0; + return MTP_RESP_OK; +} - // Search for first free object - for (unsigned int i=0; iobject_compressed_size > FS_MAX_NODE_BYTES) { + TU_LOG1("Object size %ld is more than maximum %ld\r\n", info->object_compressed_size, FS_MAX_NODE_BYTES); + return MTP_RESP_STORE_FULL; + } + + // Request for objects with no parent (0xFFFFFFFF) are considered root objects + if (parent_object == 0xFFFFFFFF) + parent_object = 0; + + // Ensure we are not creating an orphaned object outside root + if (parent_object != 0) { + obj = fs_object_get_from_handle(parent_object); + if (obj == NULL) { + TU_LOG1("Parent %ld does not exist\r\n", parent_object); + return MTP_RESP_INVALID_PARENT_OBJECT; + } + if (!obj->association) { + TU_LOG1("Parent %ld is not an association\r\n", parent_object); + return MTP_RESP_INVALID_PARENT_OBJECT; + } + } + + // Search for first free object + for (unsigned int i = 0; i < FS_MAX_NODES; i++) { + if (!_fs_objects[i].allocated) { + obj = &_fs_objects[i]; + break; + } + } + + if (obj == NULL) { + TU_LOG1("No space left on device\r\n"); + return MTP_RESP_STORE_FULL; + } + + // Fill-in structure + obj->allocated = true; + obj->handle = ++_fs_operation.last_handle; + obj->parent = parent_object; + obj->size = info->object_compressed_size; + obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; + + // Extract variable data + uint16_t offset_data = sizeof(mtp_object_info_t); + mtpd_gct_get_string(&offset_data, obj->name, FS_MAX_NODE_NAME_LEN); + mtpd_gct_get_string(&offset_data, obj->created, FS_ISODATETIME_LEN); + mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); + + TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", + obj->association ? "association" : "object", + obj->name, obj->handle, obj->parent, obj->size); + *new_object_handle = obj->handle; + // Initialize operation + _fs_operation.write_handle = obj->handle; + _fs_operation.write_pos = 0; + return MTP_RESP_OK; +} - if (obj == NULL) - { - TU_LOG1("No space left on device\r\n"); - return MTP_RESP_STORE_FULL; - } +mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t* info) { + const fs_object_info_t* obj; + + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } + + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + + memset(info, 0, sizeof(mtp_object_info_t)); + info->storage_id = STORAGE_ID(0x0001, 0x0001); + if (obj->association) { + info->object_format = MTP_OBJ_FORMAT_ASSOCIATION; + info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; + info->object_compressed_size = 0; + info->association_type = MTP_ASSOCIATION_UNDEFINED; + } else { + info->object_format = MTP_OBJ_FORMAT_UNDEFINED; + info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; + info->object_compressed_size = obj->size; + info->association_type = MTP_ASSOCIATION_UNDEFINED; + } + info->thumb_format = MTP_OBJ_FORMAT_UNDEFINED; + info->parent_object = obj->parent; + + mtpd_gct_append_wstring(obj->name); + mtpd_gct_append_wstring(obj->created); // date_created + mtpd_gct_append_wstring(obj->modified); // date_modified + mtpd_gct_append_wstring(""); // keywords, not used + + TU_LOG1("Retrieve object %s with handle %ld\r\n", obj->name, obj->handle); + + return MTP_RESP_OK; +} - // Fill-in structure - obj->allocated = true; - obj->handle = ++_fs_operation.last_handle; - obj->parent = parent_object; - obj->size = info->object_compressed_size; - obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; - - // Extract variable data - uint16_t offset_data = sizeof(mtp_object_info_t); - mtpd_gct_get_string(&offset_data, obj->name, FS_MAX_NODE_NAME_LEN); - mtpd_gct_get_string(&offset_data, obj->created, FS_ISODATETIME_LEN); - mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); - - TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", - obj->association ? "association" : "object", - obj->name, obj->handle, obj->parent, obj->size); - *new_object_handle = obj->handle; - // Initialize operation - _fs_operation.write_handle = obj->handle; +mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { + fs_object_info_t* obj; + + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + // It's a requirement that this command is preceded by a write info + if (object_handle != _fs_operation.write_handle) { + TU_LOG1("ERR: Object %ld not open for write\r\n", object_handle); + return MTP_RESP_NO_VALID_OBJECTINFO; + } + + TU_LOG1("Write object %ld: data chunk at %ld/%ld bytes at offset %ld\r\n", object_handle, _fs_operation.write_pos, + obj->size, size); + TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESP_INCOMPLETE_TRANSFER); + if (_fs_operation.write_pos + size < FS_MAX_NODE_BYTES) + memcpy(&obj->data[_fs_operation.write_pos], buffer, size); + _fs_operation.write_pos += size; + // Write operation completed + if (_fs_operation.write_pos == obj->size) { + _fs_operation.write_handle = 0; _fs_operation.write_pos = 0; - return MTP_RESP_OK; + } + return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t *info) -{ - const fs_object_info_t *obj; - - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - - obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - - memset(info, 0, sizeof(mtp_object_info_t)); - info->storage_id = STORAGE_ID(0x0001, 0x0001); - if (obj->association) - { - info->object_format = MTP_OBJ_FORMAT_ASSOCIATION; - info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; - info->object_compressed_size = 0; - info->association_type = MTP_ASSOCIATION_UNDEFINED; - } - else - { - info->object_format = MTP_OBJ_FORMAT_UNDEFINED; - info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; - info->object_compressed_size = obj->size; - info->association_type = MTP_ASSOCIATION_UNDEFINED; - } - info->thumb_format = MTP_OBJ_FORMAT_UNDEFINED; - info->parent_object = obj->parent; - - mtpd_gct_append_wstring(obj->name); - mtpd_gct_append_wstring(obj->created); // date_created - mtpd_gct_append_wstring(obj->modified); // date_modified - mtpd_gct_append_wstring(""); // keywords, not used - - TU_LOG1("Retrieve object %s with handle %ld\r\n", obj->name, obj->handle); - - return MTP_RESP_OK; +mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t* size) { + const fs_object_info_t* obj; + obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + *size = obj->size; + return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t size) -{ - fs_object_info_t *obj; +mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void* buffer, uint32_t buffer_size, + uint32_t* read_count) { + const fs_object_info_t* obj; - obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - // It's a requirement that this command is preceded by a write info - if (object_handle != _fs_operation.write_handle) - { - TU_LOG1("ERR: Object %ld not open for write\r\n", object_handle); - return MTP_RESP_NO_VALID_OBJECTINFO; - } - - TU_LOG1("Write object %ld: data chunk at %ld/%ld bytes at offset %ld\r\n", object_handle, _fs_operation.write_pos, obj->size, size); - TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESP_INCOMPLETE_TRANSFER); - if (_fs_operation.write_pos + size < FS_MAX_NODE_BYTES) - memcpy(&obj->data[_fs_operation.write_pos], buffer, size); - _fs_operation.write_pos += size; - // Write operation completed - if (_fs_operation.write_pos == obj->size) - { - _fs_operation.write_handle = 0; - _fs_operation.write_pos = 0; - } - return MTP_RESP_OK; -} + obj = fs_object_get_from_handle(object_handle); -mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *size) -{ - const fs_object_info_t *obj; - obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - *size = obj->size; - return MTP_RESP_OK; + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + // It's not a requirement that this command is preceded by a read info + if (object_handle != _fs_operation.read_handle) { + TU_LOG1("ERR: Object %ld not open for read\r\n", object_handle); + _fs_operation.read_handle = object_handle; + _fs_operation.read_pos = 0; + } + + if (obj->size - _fs_operation.read_pos > buffer_size) { + TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, buffer_size, _fs_operation.read_pos); + *read_count = buffer_size; + if (_fs_operation.read_pos + buffer_size < FS_MAX_NODE_BYTES) { + memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); + } + _fs_operation.read_pos += *read_count; + } else { + TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, obj->size - _fs_operation.read_pos, + _fs_operation.read_pos); + *read_count = obj->size - _fs_operation.read_pos; + if (_fs_operation.read_pos + *read_count < FS_MAX_NODE_BYTES) { + memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); + } + // Read operation completed + _fs_operation.read_handle = 0; + _fs_operation.read_pos = 0; + } + return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, uint32_t buffer_size, uint32_t *read_count) -{ - const fs_object_info_t *obj; - - obj = fs_object_get_from_handle(object_handle); +mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) { + fs_object_info_t* obj; - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - // It's not a requirement that this command is preceded by a read info - if (object_handle != _fs_operation.read_handle) - { - TU_LOG1("ERR: Object %ld not open for read\r\n", object_handle); - _fs_operation.read_handle = object_handle; - _fs_operation.read_pos = 0; - } + if (new_parent_object_handle == 0xFFFFFFFF) + new_parent_object_handle = 0; - if (obj->size - _fs_operation.read_pos > buffer_size) - { - TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, buffer_size, _fs_operation.read_pos); - *read_count = buffer_size; - if (_fs_operation.read_pos + buffer_size < FS_MAX_NODE_BYTES) { - memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); - } - _fs_operation.read_pos += *read_count; + // Ensure we are not moving to an nonexisting parent + if (new_parent_object_handle != 0) { + obj = fs_object_get_from_handle(new_parent_object_handle); + if (obj == NULL) { + TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); + return MTP_RESP_INVALID_PARENT_OBJECT; } - else - { - TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, obj->size - _fs_operation.read_pos, _fs_operation.read_pos); - *read_count = obj->size - _fs_operation.read_pos; - if (_fs_operation.read_pos + *read_count < FS_MAX_NODE_BYTES) { - memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); - } - // Read operation completed - _fs_operation.read_handle = 0; - _fs_operation.read_pos = 0; + if (!obj->association) { + TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); + return MTP_RESP_INVALID_PARENT_OBJECT; } - return MTP_RESP_OK; -} + } -mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) -{ - fs_object_info_t *obj; - - if (new_parent_object_handle == 0xFFFFFFFF) - new_parent_object_handle = 0; - - // Ensure we are not moving to an nonexisting parent - if (new_parent_object_handle != 0) - { - obj = fs_object_get_from_handle(new_parent_object_handle); - if (obj == NULL) - { - TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - if (!obj->association) - { - TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - } + obj = fs_object_get_from_handle(object_handle); - obj = fs_object_get_from_handle(object_handle); - - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - TU_LOG1("Move object %ld to new parent %ld\r\n", object_handle, new_parent_object_handle); - obj->parent = new_parent_object_handle; - return MTP_RESP_OK; + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + TU_LOG1("Move object %ld to new parent %ld\r\n", object_handle, new_parent_object_handle); + obj->parent = new_parent_object_handle; + return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) -{ - fs_object_info_t *obj; +mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { + fs_object_info_t* obj; - if (_fs_operation.session_id == 0) - { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; + } - if (object_handle == 0xFFFFFFFF) - object_handle = 0; + if (object_handle == 0xFFFFFFFF) + object_handle = 0; - if (object_handle != 0) - { - obj = fs_object_get_from_handle(object_handle); + if (object_handle != 0) { + obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) - { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - obj->allocated = false; - TU_LOG1("Delete object with handle %ld\r\n", object_handle); + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; } + obj->allocated = false; + TU_LOG1("Delete object with handle %ld\r\n", object_handle); + } - if (object_handle == 0 || obj->association) - { - // Delete also children - for (unsigned int i=0; iallocated && obj->parent == object_handle) - { - tud_mtp_storage_object_delete(obj->handle); - } - } + if (object_handle == 0 || obj->association) { + // Delete also children + for (unsigned int i = 0; i < FS_MAX_NODES; i++) { + obj = &_fs_objects[i]; + if (obj->allocated && obj->parent == object_handle) { + tud_mtp_storage_object_delete(obj->handle); + } } + } - return MTP_RESP_OK; + return MTP_RESP_OK; } -void tud_mtp_storage_object_done(void) -{ +void tud_mtp_storage_object_done(void) { } -void tud_mtp_storage_cancel(void) -{ - fs_object_info_t *obj; - - _fs_operation.traversal_parent = 0; - _fs_operation.traversal_index = 0; - _fs_operation.read_handle = 0; - _fs_operation.read_pos = 0; - // If write operation is canceled, discard object - if (_fs_operation.write_handle) - { - obj = fs_object_get_from_handle(_fs_operation.write_handle); - if (obj) - obj->allocated = false; - } - _fs_operation.write_handle = 0; - _fs_operation.write_pos = 0; +void tud_mtp_storage_cancel(void) { + fs_object_info_t* obj; + + _fs_operation.traversal_parent = 0; + _fs_operation.traversal_index = 0; + _fs_operation.read_handle = 0; + _fs_operation.read_pos = 0; + // If write operation is canceled, discard object + if (_fs_operation.write_handle) { + obj = fs_object_get_from_handle(_fs_operation.write_handle); + if (obj) + obj->allocated = false; + } + _fs_operation.write_handle = 0; + _fs_operation.write_pos = 0; } -void tud_mtp_storage_reset(void) -{ - tud_mtp_storage_cancel(); - _fs_operation.session_id = 0; +void tud_mtp_storage_reset(void) { + tud_mtp_storage_cancel(); + _fs_operation.session_id = 0; } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index c9d2b27a9..d61f329c4 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -38,7 +38,6 @@ extern "C" { #endif -#define TU_ARRAY_LEN(a) (sizeof(a)/sizeof(a[0])) #define STORAGE_ID(physical_id, logical_id) ( (((uint32_t)physical_id & 0xFFFF) << 16) | ((uint32_t)logical_id & 0x0000FFFF) ) typedef uint16_t wchar16_t; @@ -583,19 +582,32 @@ typedef enum } mtp_object_handles_t; // Datatypes -typedef enum -{ - MTP_TYPE_UNDEFINED = 0x0000u, - MTP_TYPE_INT8 = 0x0001u, - MTP_TYPE_UINT8 = 0x0002u, - MTP_TYPE_INT16 = 0x0003u, - MTP_TYPE_UINT16 = 0x0004u, - MTP_TYPE_INT32 = 0x0005u, - MTP_TYPE_UINT32 = 0x0006u, - MTP_TYPE_INT64 = 0x0007u, - MTP_TYPE_UINT64 = 0x0008u, - MTP_TYPE_STR = 0xFFFFu, -} mtp_datatypes_t; +typedef enum { + MTP_DATA_TYPE_UNDEFINED = 0x0000u, + // scalars + MTP_DATA_TYPE_INT8 = 0x0001u, + MTP_DATA_TYPE_UINT8 = 0x0002u, + MTP_DATA_TYPE_INT16 = 0x0003u, + MTP_DATA_TYPE_UINT16 = 0x0004u, + MTP_DATA_TYPE_INT32 = 0x0005u, + MTP_DATA_TYPE_UINT32 = 0x0006u, + MTP_DATA_TYPE_INT64 = 0x0007u, + MTP_DATA_TYPE_UINT64 = 0x0008u, + MTP_DATA_TYPE_INT128 = 0x0009u, + MTP_DATA_TYPE_UINT128 = 0x000Au, + // array + MTP_DATA_TYPE_AINT8 = 0x4001u, + MTP_DATA_TYPE_AUINT8 = 0x4002u, + MTP_DATA_TYPE_AINT16 = 0x4003u, + MTP_DATA_TYPE_AUINT16 = 0x4004u, + MTP_DATA_TYPE_AINT32 = 0x4005u, + MTP_DATA_TYPE_AUINT32 = 0x4006u, + MTP_DATA_TYPE_AINT64 = 0x4007u, + MTP_DATA_TYPE_AUINT64 = 0x4008u, + MTP_DATA_TYPE_AINT128 = 0x4009u, + MTP_DATA_TYPE_AUINT128 = 0x400Au, + MTP_DATA_TYPE_STR = 0xFFFFu, +} mtp_data_type_t; // Get/Set typedef enum @@ -707,46 +719,24 @@ typedef struct TU_ATTR_PACKED { uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; } mtp_generic_container_t; -// DeviceInfo Dataset -#define MTP_EXTENSIONS "microsoft.com: 1.0; " -typedef struct TU_ATTR_PACKED { - uint16_t standard_version; - uint32_t mtp_vendor_extension_id; - uint16_t mtp_version; - uint8_t mtp_extensions_len; - wchar16_t mtp_extensions[TU_ARRAY_LEN(MTP_EXTENSIONS)] TU_ATTR_PACKED; - - uint16_t functional_mode; - /* Operations supported */ - uint32_t operations_supported_len; - uint16_t operations_supported[TU_ARRAY_LEN(mtp_operations_supported)] TU_ATTR_PACKED; - /* Events supported */ - uint32_t events_supported_len; - uint16_t events_supported[TU_ARRAY_LEN(mtp_events_supported)] TU_ATTR_PACKED; - /* Device properties supported */ - uint32_t device_properties_supported_len; - uint16_t device_properties_supported[TU_ARRAY_LEN(mtp_device_properties_supported)] TU_ATTR_PACKED; - /* Capture formats */ - uint32_t capture_formats_len; - uint16_t capture_formats[TU_ARRAY_LEN(mtp_capture_formats)] TU_ATTR_PACKED; - /* Playback formats */ - uint32_t playback_formats_len; - uint16_t playback_formats[TU_ARRAY_LEN(mtp_playback_formats)] TU_ATTR_PACKED; -} mtp_device_info_t; -// The following fields will be dynamically added to the struct at runtime: -// - wstring manufacturer -// - wstring model -// - wstring device_version -// - wstring serial_number +#define mtp_string_t(_nchars) \ + struct TU_ATTR_PACKED { \ + uint8_t count; /* in characters including null */ \ + uint16_t utf16[_nchars]; \ + } +#define mtp_array_t(_type, _count) \ + struct TU_ATTR_PACKED { \ + uint32_t count; \ + _type arr[_count];\ + } -#define MTP_STRING_DEF(name, string) \ - uint8_t name##_len; \ - wchar16_t name[TU_ARRAY_LEN(string)]; +#define mtp_auint16_t(_count) mtp_array_t(uint16_t, _count) -#define MTP_ARRAY_DEF(name, array) \ - uint16_t name##_len; \ - typeof(name) name[TU_ARRAY_LEN(array)]; +typedef struct TU_ATTR_PACKED { + uint8_t count; + uint16_t utf16[]; +} mtp_flexible_string_t; // StorageInfo dataset typedef struct TU_ATTR_PACKED { @@ -813,6 +803,95 @@ typedef struct TU_ATTR_PACKED { uint32_t parent_object_handle; } mtp_basic_object_info_t; +//--------------------------------------------------------------------+ +// Generic Container function +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add(mtp_generic_container_t* p_container, mtp_data_type_t type, const void* data) { + TU_VERIFY(type != MTP_DATA_TYPE_UNDEFINED, 0); + uint8_t scalar_size; // size of single scalar + uint8_t count_width; // size of count field (0, 1 or 4 bytes) + + if (type == MTP_DATA_TYPE_STR) { + scalar_size = 2; + count_width = 1; + } else { + uint8_t scalar_type = type & 0x3F; + count_width = (type & 0x4000u) ? 4 : 0; + scalar_size = 1u << ((scalar_type - 1u) >> 1); + } + + uint32_t data_len; + if (count_width) { + const uint32_t count = *(const uint32_t*) data; + data_len = count_width + count*scalar_size; + } else { + data_len = scalar_size; + } + + memcpy(((uint8_t*)p_container) + p_container->len, data, data_len); + p_container->len += data_len; + + return data_len; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_field(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { + const uint32_t prev_len = p_container->len; + uint8_t* container8 = (uint8_t*) p_container; + if (count == 0) { + // count = 0 means scalar + memcpy(container8 + p_container->len, data, scalar_size); + p_container->len += scalar_size; + } else { + tu_unaligned_write32(container8 + p_container->len, count); + p_container->len += 4; + memcpy(container8 + p_container->len, data, count * scalar_size); + } + + return p_container->len - prev_len; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { + const uint32_t prev_len = p_container->len; + uint8_t* container8 = (uint8_t*) p_container; + *(container8 + p_container->len) = count; + p_container->len += 1; + + memcpy(container8 + p_container->len, utf16, 2 * count); + p_container->len += 2 * count; + + return p_container->len - prev_len; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_generic_container_t* p_container, uint8_t data) { + return mtp_container_add_field(p_container, sizeof(uint8_t), 0, &data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint16(mtp_generic_container_t* p_container, uint16_t data) { + return mtp_container_add_field(p_container, sizeof(uint16_t), 0, &data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint32(mtp_generic_container_t* p_container, uint32_t data) { + return mtp_container_add_field(p_container, sizeof(uint32_t), 0, &data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint64(mtp_generic_container_t* p_container, uint64_t data) { + return mtp_container_add_field(p_container, 8, 0, &data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint8(mtp_generic_container_t* p_container, uint32_t count, const uint8_t* data) { + return mtp_container_add_field(p_container, sizeof(uint8_t), count, data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint16(mtp_generic_container_t* p_container, uint32_t count, const uint16_t* data) { + return mtp_container_add_field(p_container, sizeof(uint16_t), count, data); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_generic_container_t* p_container, uint32_t count, const uint32_t* data) { + return mtp_container_add_field(p_container, sizeof(uint32_t), count, data); +} + + #ifdef __cplusplus } #endif diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 7e9574ab1..b64f36f2e 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -50,8 +50,8 @@ //--------------------------------------------------------------------+ // STRUCT //--------------------------------------------------------------------+ -typedef struct -{ +typedef struct { + uint8_t rhport; uint8_t itf_num; uint8_t ep_in; uint8_t ep_out; @@ -82,7 +82,7 @@ static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_c static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); // MTP commands -static mtp_phase_type_t mtpd_handle_cmd(void); +static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_get_device_info(void); static mtp_phase_type_t mtpd_handle_cmd_open_session(void); @@ -118,9 +118,9 @@ CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static char _mtp_datestr[20]; // Helper //--------------------------------------------------------------------+ -static bool prepare_new_command(uint8_t rhport, mtpd_interface_t* p_mtp) { +static bool prepare_new_command(mtpd_interface_t* p_mtp) { p_mtp->phase = MTP_PHASE_IDLE; - return usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t)); + return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t)); } @@ -157,6 +157,8 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 // Max length must be at least 1 interface + 3 endpoints TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= mtpd_itf_size); mtpd_interface_t* p_mtp = &_mtpd_itf; + tu_memclr(p_mtp, sizeof(mtpd_interface_t)); + p_mtp->rhport = rhport; p_mtp->itf_num = itf_desc->bInterfaceNumber; // Open interrupt IN endpoint @@ -168,7 +170,7 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 // Open endpoint pair TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0); - TU_ASSERT(prepare_new_command(rhport, p_mtp), 0); + TU_ASSERT(prepare_new_command(p_mtp), 0); return mtpd_itf_size; } @@ -216,6 +218,29 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } +bool tud_mtp_data_send(mtp_generic_container_t* data_block) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + p_mtp->phase = MTP_PHASE_DATA; + p_mtp->total_len = data_block->len; + p_mtp->xferred_len = 0; + p_mtp->handled_len = 0; + p_mtp->xfer_completed = false; + + data_block->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + data_block->transaction_id = p_mtp->cmd_header.transaction_id; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t*) data_block, (uint16_t)data_block->len)); + return true; +} + +bool tud_mtp_response_send(mtp_generic_container_t* resp_block) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; + resp_block->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + resp_block->transaction_id = p_mtp->cmd_header.transaction_id; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t*) resp_block, (uint16_t)resp_block->len)); + return true; +} + // Transfer on bulk endpoints bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { TU_ASSERT(event == XFER_RESULT_SUCCESS); @@ -235,23 +260,25 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_mtp->phase = MTP_PHASE_COMMAND; TU_ATTR_FALLTHROUGH; // handle in the next case - case MTP_PHASE_COMMAND: - // Handle command block - memcpy(&p_mtp->cmd_header, p_container, sizeof(mtp_container_header_t)); - p_mtp->phase = mtpd_handle_cmd(); - if (p_mtp->phase == MTP_PHASE_DATA_IN) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_mtp->queued_len)); - p_mtp->total_len = p_container->len; - p_mtp->xferred_len = 0; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - p_mtp->xferred_len = 0; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); + case MTP_PHASE_COMMAND: { + mtpd_handle_cmd(p_mtp); + break; + } + + case MTP_PHASE_DATA: { + const uint16_t bulk_mps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; + p_mtp->xferred_len += xferred_bytes; + + // transfer complete if ZLP or short packet or overflow + if (xferred_bytes == 0 || // ZLP + (xferred_bytes & (bulk_mps - 1)) || // short packet + p_mtp->xferred_len > p_mtp->total_len) { + tud_mtp_data_complete_cb(0, &p_mtp->cmd_header, p_container, event, p_mtp->xferred_len); + } else { + TU_ASSERT(false); } break; + } case MTP_PHASE_DATA_IN: p_mtp->xferred_len += xferred_bytes; @@ -328,7 +355,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t case MTP_PHASE_RESPONSE_QUEUED: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); - prepare_new_command(rhport, p_mtp); + prepare_new_command(p_mtp); break; case MTP_PHASE_RESPONSE: @@ -339,151 +366,16 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } if (p_mtp->phase == MTP_PHASE_RESPONSE) { - p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->transaction_id = p_mtp->cmd_header.transaction_id; - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); + // p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; + // p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + // p_container->transaction_id = p_mtp->cmd_header.transaction_id; + // TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); } else if (p_mtp->phase == MTP_PHASE_ERROR) { // stall both IN & OUT endpoints usbd_edpt_stall(rhport, p_mtp->ep_out); usbd_edpt_stall(rhport, p_mtp->ep_in); } -#if 0 - // IN transfer completed - if (ep_addr == p_mtp->ep_in) { - if (p_mtp->phase == MTP_PHASE_RESPONSE) { - // IN transfer completed, prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, CFG_MTP_EP_SIZE), 0); - p_mtp->phase = MTP_PHASE_IDLE; - } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { - p_mtp->xferred_len += xferred_bytes; - p_mtp->handled_len = p_mtp->xferred_len; - - // Check if transfer completed. - if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { - p_mtp->phase = MTP_PHASE_RESPONSE; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_OK; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->transaction_id = p_mtp->context.transaction_id; - if (p_mtp->session_id != 0) { - p_container->data[0] = p_mtp->session_id; - p_container->len += sizeof(uint32_t); - } - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); - } else { - // Send next block of DATA - // Send Zero-Length Packet - if (p_mtp->xferred_len == p_mtp->total_len) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), 0 )); - } else { - p_mtp->phase = mtpd_handle_data(); - if (p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); - } else { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), (uint16_t)p_mtp->queued_len)); - } - } - } - } else { - return false; - } - } - - if (ep_addr == p_mtp->ep_out) { - if (p_mtp->phase == MTP_PHASE_IDLE) { - // A new command has been received. Ensure this is the last of the sequence. - p_mtp->total_len = p_container->len; - // Stall in case of unexpected block - if (p_container->type != MTP_CONTAINER_TYPE_COMMAND_BLOCK) { - return false; - } - p_mtp->phase = MTP_PHASE_COMMAND; - p_mtp->total_len = p_container->len; - p_mtp->xferred_len = xferred_bytes; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - TU_ASSERT(p_mtp->total_len < sizeof(mtp_generic_container_t)); - } - - if (p_mtp->phase == MTP_PHASE_COMMAND) { - // A zero-length or a short packet termination is expected - if (xferred_bytes == CFG_MTP_EP_SIZE || (p_mtp->total_len - p_mtp->xferred_len) > 0) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container + p_mtp->xferred_len, (uint16_t)(p_mtp->total_len - p_mtp->xferred_len))); - } else { - // Handle command block - p_mtp->phase = mtpd_handle_cmd(); - if (p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); - } else if (p_mtp->phase == MTP_PHASE_DATA_IN) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_mtp->queued_len)); - p_mtp->total_len = p_container->len; - p_mtp->xferred_len = 0; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); - p_mtp->xferred_len = 0; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - } else { - usbd_edpt_stall(rhport, p_mtp->ep_out); - usbd_edpt_stall(rhport, p_mtp->ep_in); - } - } - return true; - } - - if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - // First block of data - if (p_mtp->xferred_len == 0) { - p_mtp->total_len = p_container->len; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - } - p_mtp->xferred_len += xferred_bytes; - // Stall in case of unexpected block - if (p_container->type != MTP_CONTAINER_TYPE_DATA_BLOCK) { return false; } - - // A zero-length or a short packet termination - if (xferred_bytes < CFG_MTP_EP_SIZE) { - p_mtp->xfer_completed = true; - // Handle data block - p_mtp->phase = mtpd_handle_data(); - if (p_mtp->phase == MTP_PHASE_DATA_IN || p_mtp->phase == MTP_PHASE_RESPONSE) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len)); - } else if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); - p_mtp->xferred_len = 0; - p_mtp->xfer_completed = false; - } else { - usbd_edpt_stall(rhport, p_mtp->ep_out); - usbd_edpt_stall(rhport, p_mtp->ep_in); - } - } else { - // Handle data block when container is full - if (p_mtp->xferred_len - p_mtp->handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { - p_mtp->phase = mtpd_handle_data(); - p_mtp->handled_len = p_mtp->xferred_len; - } - // Transfer completed: wait for zero-lenght packet - // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if - // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. - if (p_mtp->total_len == p_mtp->xferred_len) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)), CFG_MTP_EP_SIZE), 0); - } else if (p_mtp->handled_len == 0) { - // First data block includes container header + container data - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container + p_mtp->xferred_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); - } else { - // Successive data block includes only container data - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); - } - } - } - } -#endif - return true; } @@ -492,26 +384,77 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // Decode command and prepare response -mtp_phase_type_t mtpd_handle_cmd(void) { +mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + + mtp_generic_container_t cmd_block; // copy command block for callback + memcpy(&cmd_block, p_container, p_container->len); + memcpy(&p_mtp->cmd_header, p_container, sizeof(mtp_container_header_t)); + // p_container->len = MTP_CONTAINER_HEADER_LENGTH; // default data/response length + if (p_container->code != MTP_OP_SEND_OBJECT) { _mtpd_soi.object_handle = 0; } + mtp_phase_type_t ret = MTP_PHASE_RESPONSE; + switch (p_container->code) { - case MTP_OP_GET_DEVICE_INFO: + case MTP_OP_GET_DEVICE_INFO: { TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); - return mtpd_handle_cmd_get_device_info(); + tud_mtp_device_info_t dev_info = { + .standard_version = 100, + .mtp_vendor_extension_id = 0xFFFFFFFFU, + .mtp_version = 100, + .mtp_extensions = { + .count = sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), + .utf16 = { 0 } + }, + .functional_mode = 0x0000, + .supported_operations = { + .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), + .arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS } + }, + .supported_events = { + .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), + .arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS } + }, + .supported_device_properties = { + .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), + .arr = { CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES } + }, + .capture_formats = { + .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), + .arr = { CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS } + }, + .playback_formats = { + .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS), + .arr = { CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS } + } + }; + for (uint8_t i=0; i < dev_info.mtp_extensions.count; i++) { + dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; + } + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(tud_mtp_device_info_t); + p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->code = MTP_OP_GET_DEVICE_INFO; + memcpy(p_container->data, &dev_info, sizeof(tud_mtp_device_info_t)); + + ret = MTP_PHASE_RESPONSE; + break; + } + case MTP_OP_OPEN_SESSION: TU_LOG_DRV(" MTP command: MTP_OP_OPEN_SESSION\n"); - return mtpd_handle_cmd_open_session(); + break; + case MTP_OP_CLOSE_SESSION: TU_LOG_DRV(" MTP command: MTP_OP_CLOSE_SESSION\n"); return mtpd_handle_cmd_close_session(); + case MTP_OP_GET_STORAGE_IDS: TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); return mtpd_handle_cmd_get_storage_ids(); + case MTP_OP_GET_STORAGE_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", p_container->data[0]); return mtpd_handle_cmd_get_storage_info(); @@ -546,7 +489,9 @@ mtp_phase_type_t mtpd_handle_cmd(void) { TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", p_container->code); return false; } - return true; + + tud_mtp_command_received_cb(0, &cmd_block, p_container); + return ret; } mtp_phase_type_t mtpd_handle_data(void) @@ -572,40 +517,6 @@ mtp_phase_type_t mtpd_handle_data(void) return true; } -mtp_phase_type_t mtpd_handle_cmd_get_device_info(void) -{ - TU_VERIFY_STATIC(sizeof(mtp_device_info_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_info_t); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_DEVICE_INFO; - mtp_device_info_t *d = (mtp_device_info_t *)p_container->data; - d->standard_version = 100; - d->mtp_vendor_extension_id = 0x06; - d->mtp_version = 100; - d->mtp_extensions_len = TU_ARRAY_LEN(MTP_EXTENSIONS); - mtpd_wc16cpy((uint8_t *)d->mtp_extensions, MTP_EXTENSIONS); - d->functional_mode = 0x0000; - d->operations_supported_len = TU_ARRAY_LEN(mtp_operations_supported); - memcpy(d->operations_supported, mtp_operations_supported, sizeof(mtp_operations_supported)); - d->events_supported_len = TU_ARRAY_LEN(mtp_events_supported); - memcpy(d->events_supported, mtp_events_supported, sizeof(mtp_events_supported)); - d->device_properties_supported_len = TU_ARRAY_LEN(mtp_device_properties_supported); - memcpy(d->device_properties_supported, mtp_device_properties_supported, sizeof(mtp_device_properties_supported)); - d->capture_formats_len = TU_ARRAY_LEN(mtp_capture_formats); - memcpy(d->capture_formats, mtp_capture_formats, sizeof(mtp_capture_formats)); - d->playback_formats_len = TU_ARRAY_LEN(mtp_playback_formats); - memcpy(d->playback_formats, mtp_playback_formats, sizeof(mtp_playback_formats)); - mtpd_gct_append_wstring(CFG_TUD_MANUFACTURER); - mtpd_gct_append_wstring(CFG_TUD_MODEL); - mtpd_gct_append_wstring(CFG_MTP_DEVICE_VERSION); - mtpd_gct_append_wstring(CFG_MTP_SERIAL_NUMBER); - - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; -} - mtp_phase_type_t mtpd_handle_cmd_open_session(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; @@ -851,7 +762,7 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_prop_desc_t); mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)p_container->data; d->device_property_code = (uint16_t)(device_prop_code); - d->datatype = MTP_TYPE_STR; + d->datatype = MTP_DATA_TYPE_STR; d->get_set = MTP_MODE_GET; mtpd_gct_append_wstring(CFG_TUD_MODEL); // factory_def_value mtpd_gct_append_wstring(CFG_TUD_MODEL); // current_value_len diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 6531b36df..b39322f22 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -18,14 +18,14 @@ * 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 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN0 * THE SOFTWARE. * * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_MTP_DEVICE_H_ -#define _TUSB_MTP_DEVICE_H_ +#ifndef TUSB_MTP_DEVICE_H_ +#define TUSB_MTP_DEVICE_H_ #include "common/tusb_common.h" #include "mtp.h" @@ -36,15 +36,51 @@ extern "C" { #endif +typedef struct { + const mtp_container_header_t* cmd_header; + tusb_xfer_result_t xfer_result; + uint32_t xferred_bytes; +} tud_mtp_cb_complete_data_t; + +// Number of supported operations, events, device properties, capture formats, playback formats +// and max number of characters for strings manufacturer, model, device_version, serial_number +#define MTP_DEVICE_INFO_TYPEDEF(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ + struct TU_ATTR_PACKED { \ + uint16_t standard_version; \ + uint32_t mtp_vendor_extension_id; \ + uint16_t mtp_version; \ + mtp_string_t(_extension_nchars) mtp_extensions; \ + uint16_t functional_mode; \ + mtp_auint16_t(_op_count) supported_operations; \ + mtp_auint16_t(_event_count) supported_events; \ + mtp_auint16_t(_devprop_count) supported_device_properties; \ + mtp_auint16_t(_capture_count) capture_formats; \ + mtp_auint16_t(_playback_count) playback_formats; \ + /* string fields will be added using append function */ \ + } + +typedef MTP_DEVICE_INFO_TYPEDEF( + sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) + ) tud_mtp_device_info_t; + //--------------------------------------------------------------------+ -// Internal Class Driver API +// Application API //--------------------------------------------------------------------+ -void mtpd_init (void); -bool mtpd_deinit (void); -void mtpd_reset (uint8_t rhport); -uint16_t mtpd_open (uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -bool mtpd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *p_request); -bool mtpd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +bool tud_mtp_data_send(mtp_generic_container_t* data_block); +// bool tud_mtp_block_data_receive(); +bool tud_mtp_response_send(mtp_generic_container_t* resp_block); + +//--------------------------------------------------------------------+ +// Application Callbacks +//--------------------------------------------------------------------+ + +// Invoked when new command is received +int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block); + +// Invoked when data phase is complete +int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes); //--------------------------------------------------------------------+ // Helper functions @@ -66,10 +102,19 @@ bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_si // The function returns true if the data fits in the available buffer space. bool mtpd_gct_append_date(struct tm *timeinfo); +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void mtpd_init (void); +bool mtpd_deinit (void); +void mtpd_reset (uint8_t rhport); +uint16_t mtpd_open (uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool mtpd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *p_request); +bool mtpd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + #ifdef __cplusplus } #endif -#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ - -#endif /* _TUSB_MTP_DEVICE_H_ */ +#endif +#endif -- cgit v1.3.1 From e76d09bb4213921c8957af22033c9451fe0ed123 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Sep 2025 16:00:36 +0700 Subject: rework get storageIDs and get storage info --- examples/device/mtp/src/mtp_fs_example.c | 103 ++++++++++++++----------------- src/class/mtp/mtp.h | 72 +++++++++++---------- src/class/mtp/mtp_device.c | 89 ++------------------------ src/class/mtp/mtp_device_storage.h | 14 ----- src/common/tusb_common.h | 1 + 5 files changed, 87 insertions(+), 192 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 68140b1ae..fcd01f54a 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -34,11 +34,10 @@ //--------------------------------------------------------------------+ // device info string (including terminating null) -const uint16_t dev_info_manufacturer[] = { 'T', 'i', 'n', 'y', 'U', 'S', 'B', 0 }; -const uint16_t dev_info_model[] = { 'M', 'T', 'P', ' ', 'E', 'x', 'a', 'm', 'p', 'l', 'e', 0 }; -const uint16_t dev_info_version[] = { '1', '.', '0', 0 }; -const uint16_t dev_info_serial[] = { '1', '2', '3', '4', '5', '6', 0 }; - +static const uint16_t dev_info_manufacturer[] = { 'T', 'i', 'n', 'y', 'U', 'S', 'B', 0 }; +static const uint16_t dev_info_model[] = { 'M', 'T', 'P', ' ', 'E', 'x', 'a', 'm', 'p', 'l', 'e', 0 }; +static const uint16_t dev_info_version[] = { '1', '.', '0', 0 }; +static const uint16_t dev_info_serial[] = { '1', '2', '3', '4', '5', '6', 0 }; static const uint16_t supported_operations[] = { MTP_OP_GET_DEVICE_INFO, @@ -80,7 +79,6 @@ static const uint16_t playback_formats[] = { MTP_OBJ_FORMAT_TEXT, }; - //--------------------------------------------------------------------+ // RAM FILESYSTEM //--------------------------------------------------------------------+ @@ -116,6 +114,32 @@ static fs_object_info_t _fs_objects[FS_MAX_NODES] = { } }; +//------------- Storage Info -------------// +#define STORAGE_DESCRIPTRION { 'd', 'i', 's', 'k', 0 } +#define VOLUME_IDENTIFIER { 'v', 'o', 'l', 0 } + +typedef MTP_STORAGE_INFO_TYPEDEF(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION), + TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER)) +) storage_info_t; + +storage_info_t storage_info = { + .storage_type = MTP_STORAGE_TYPE_FIXED_RAM, + .filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL, + .access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE, + .max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES, + .free_space_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES, + .free_space_in_objects = FS_MAX_NODES, + .storage_description = { + .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t), + .utf16 = STORAGE_DESCRIPTRION + }, + .volume_identifier = { + .count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t), + .utf16 = VOLUME_IDENTIFIER + } +}; + + //--------------------------------------------------------------------+ // OPERATING STATUS //--------------------------------------------------------------------+ @@ -185,7 +209,7 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl (void)idx; switch (cmd_block->code) { case MTP_OP_GET_DEVICE_INFO: { - // Device info is already prepared up to playback formats. Application need to add string fields + // Device info is already prepared up to playback formats. Application only need to add string fields mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_manufacturer), dev_info_manufacturer); mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_model), dev_info_model); mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_version), dev_info_version); @@ -196,14 +220,26 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl } case MTP_OP_OPEN_SESSION: - out_block->len = MTP_CONTAINER_HEADER_LENGTH; - out_block->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - // TODO check if session is already opened out_block->code = MTP_RESP_OK; - tud_mtp_response_send(out_block); break; + case MTP_OP_GET_STORAGE_IDS: { + uint32_t storage_ids [] = { 0x00010001u }; // physical = 1, logical = 1 + mtp_container_add_auint32(out_block, 1, storage_ids); + tud_mtp_data_send(out_block); + break; + } + + case MTP_OP_GET_STORAGE_INFO: { + // update storage info with current free space + storage_info.free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); + storage_info.free_space_in_bytes = storage_info.free_space_in_objects * FS_MAX_NODE_BYTES; + mtp_container_add_raw(out_block, &storage_info, sizeof(storage_info)); + tud_mtp_data_send(out_block); + break; + } + default: return -1; } @@ -213,21 +249,6 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl //--------------------------------------------------------------------+ // API //--------------------------------------------------------------------+ -mtp_response_t tud_mtp_storage_open_session(uint32_t* session_id) { - if (*session_id == 0) { - TU_LOG1("Invalid session ID\r\n"); - return MTP_RESP_INVALID_PARAMETER; - } - if (_fs_operation.session_id != 0) { - *session_id = _fs_operation.session_id; - TU_LOG1("ERR: Session %ld already open\r\n", _fs_operation.session_id); - return MTP_RESP_SESSION_ALREADY_OPEN; - } - _fs_operation.session_id = *session_id; - TU_LOG1("Open session with id %ld\r\n", _fs_operation.session_id); - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) { if (session_id != _fs_operation.session_id) { TU_LOG1("ERR: Session %ld not open\r\n", session_id); @@ -238,36 +259,6 @@ mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) { return MTP_RESP_OK; } -mtp_response_t tud_mtp_get_storage_id(uint32_t* storage_id) { - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - *storage_id = STORAGE_ID(0x0001, 0x0001); - TU_LOG1("Retrieved storage identifier %ld\r\n", *storage_id); - return MTP_RESP_OK; -} - -mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t* info) { - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - if (storage_id != STORAGE_ID(0x0001, 0x0001)) { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } - info->storage_type = MTP_STORAGE_TYPE_FIXED_RAM; - info->filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL; - info->access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE; - info->max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES; - info->free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); - info->free_space_in_bytes = info->free_space_in_objects * FS_MAX_NODE_BYTES; - mtpd_gct_append_wstring(MTPD_STORAGE_DESCRIPTION); - mtpd_gct_append_wstring(MTPD_VOLUME_IDENTIFIER); - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index d61f329c4..a28ce40df 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -738,7 +738,16 @@ typedef struct TU_ATTR_PACKED { uint16_t utf16[]; } mtp_flexible_string_t; -// StorageInfo dataset + typedef union TU_ATTR_PACKED { + struct { + uint16_t physical; // physical location + uint16_t logical; // logical within physical + }; + + uint32_t id; +} mtp_storage_id_t; + +// StorageInfo dataset (excluding storage description and volume identifier) typedef struct TU_ATTR_PACKED { uint16_t storage_type; uint16_t filesystem_type; @@ -746,10 +755,20 @@ typedef struct TU_ATTR_PACKED { uint64_t max_capacity_in_bytes; uint64_t free_space_in_bytes; uint32_t free_space_in_objects; -} mtp_storage_info_t; -// The following fields will be dynamically added to the struct at runtime: -// - wstring storage_description -// - wstring volume_identifier + // storage description and volume identifier are added dynamically +} mtp_storage_info_nostring_t; + +#define MTP_STORAGE_INFO_TYPEDEF(_storage_desc_chars, _volume_id_chars) \ + struct TU_ATTR_PACKED { \ + uint16_t storage_type; \ + uint16_t filesystem_type; \ + uint16_t access_capability; \ + uint64_t max_capacity_in_bytes; \ + uint64_t free_space_in_bytes; \ + uint32_t free_space_in_objects; \ + mtp_string_t(_storage_desc_chars) storage_description; \ + mtp_string_t(_volume_id_chars) volume_identifier; \ + } // ObjectInfo Dataset typedef struct TU_ATTR_PACKED { @@ -807,48 +826,27 @@ typedef struct TU_ATTR_PACKED { // Generic Container function //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add(mtp_generic_container_t* p_container, mtp_data_type_t type, const void* data) { - TU_VERIFY(type != MTP_DATA_TYPE_UNDEFINED, 0); - uint8_t scalar_size; // size of single scalar - uint8_t count_width; // size of count field (0, 1 or 4 bytes) - - if (type == MTP_DATA_TYPE_STR) { - scalar_size = 2; - count_width = 1; - } else { - uint8_t scalar_type = type & 0x3F; - count_width = (type & 0x4000u) ? 4 : 0; - scalar_size = 1u << ((scalar_type - 1u) >> 1); - } - - uint32_t data_len; - if (count_width) { - const uint32_t count = *(const uint32_t*) data; - data_len = count_width + count*scalar_size; - } else { - data_len = scalar_size; - } - - memcpy(((uint8_t*)p_container) + p_container->len, data, data_len); - p_container->len += data_len; - - return data_len; +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_generic_container_t* p_container, const void* data, uint32_t len) { + memcpy((uint8_t*) p_container + p_container->len, data, len); + p_container->len += len; + return len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_field(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { - const uint32_t prev_len = p_container->len; - uint8_t* container8 = (uint8_t*) p_container; if (count == 0) { // count = 0 means scalar - memcpy(container8 + p_container->len, data, scalar_size); - p_container->len += scalar_size; + return mtp_container_add_raw(p_container, data, scalar_size); } else { + uint8_t* container8 = (uint8_t*) p_container; + tu_unaligned_write32(container8 + p_container->len, count); p_container->len += 4; + memcpy(container8 + p_container->len, data, count * scalar_size); - } + p_container->len += count * scalar_size; - return p_container->len - prev_len; + return 4 + count * scalar_size; + } } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index b64f36f2e..857fafd0c 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -84,11 +84,7 @@ static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); // MTP commands static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); static mtp_phase_type_t mtpd_handle_data(void); -static mtp_phase_type_t mtpd_handle_cmd_get_device_info(void); -static mtp_phase_type_t mtpd_handle_cmd_open_session(void); static mtp_phase_type_t mtpd_handle_cmd_close_session(void); -static mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void); -static mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void); static mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void); static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); static mtp_phase_type_t mtpd_handle_cmd_get_object(void); @@ -390,7 +386,7 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { mtp_generic_container_t cmd_block; // copy command block for callback memcpy(&cmd_block, p_container, p_container->len); memcpy(&p_mtp->cmd_header, p_container, sizeof(mtp_container_header_t)); - // p_container->len = MTP_CONTAINER_HEADER_LENGTH; // default data/response length + p_container->len = MTP_CONTAINER_HEADER_LENGTH; // default data/response length if (p_container->code != MTP_OP_SEND_OBJECT) { _mtpd_soi.object_handle = 0; @@ -453,11 +449,12 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_GET_STORAGE_IDS: TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); - return mtpd_handle_cmd_get_storage_ids(); + break; case MTP_OP_GET_STORAGE_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", p_container->data[0]); - return mtpd_handle_cmd_get_storage_info(); + break; + case MTP_OP_GET_OBJECT_HANDLES: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_HANDLES\n"); return mtpd_handle_cmd_get_object_handles(); @@ -517,34 +514,6 @@ mtp_phase_type_t mtpd_handle_data(void) return true; } -mtp_phase_type_t mtpd_handle_cmd_open_session(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t session_id = p_container->data[0]; - - mtp_response_t res = tud_mtp_storage_open_session(&session_id); - if (res == MTP_RESP_SESSION_ALREADY_OPEN) - { - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = res; - p_container->len += sizeof(p_container->data[0]); - p_container->data[0] = session_id; - _mtpd_itf.session_id = session_id; - return MTP_PHASE_RESPONSE; - } - - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - - _mtpd_itf.session_id = session_id; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_OK; - - return MTP_PHASE_RESPONSE; -} mtp_phase_type_t mtpd_handle_cmd_close_session(void) { @@ -562,56 +531,6 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) return MTP_PHASE_RESPONSE; } -mtp_phase_type_t mtpd_handle_cmd_get_storage_ids(void) -{ - TU_VERIFY_STATIC(sizeof(mtp_storage_ids_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_ids_t shall fit in MTP_MAX_PACKET_SIZE"); - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - - uint32_t storage_id; - mtp_response_t res = tud_mtp_get_storage_id(&storage_id); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_storage_ids_t); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_STORAGE_IDS; - mtp_storage_ids_t *d = (mtp_storage_ids_t *)p_container->data; - if (storage_id == 0) - { - // Storage not accessible - d->storage_ids_len = 0; - d->storage_ids[0] = 0; - } - else - { - d->storage_ids_len = 1; - d->storage_ids[0] = storage_id; - } - - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; -} - -mtp_phase_type_t mtpd_handle_cmd_get_storage_info(void) -{ - TU_VERIFY_STATIC(sizeof(mtp_storage_info_t) < MTP_MAX_PACKET_SIZE, "mtp_storage_info_t shall fit in MTP_MAX_PACKET_SIZE"); - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t storage_id = p_container->data[0]; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_storage_info_t); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_STORAGE_INFO; - - mtp_response_t res = tud_mtp_get_storage_info(storage_id, (mtp_storage_info_t *)p_container->data); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; -} - mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h index a9bbc9b90..6c12d38e3 100644 --- a/src/class/mtp/mtp_device_storage.h +++ b/src/class/mtp/mtp_device_storage.h @@ -59,24 +59,10 @@ // // The function shall check if the session is already opened and, in case, set session_id to the // ID of the current session. -mtp_response_t tud_mtp_storage_open_session(uint32_t *session_id); // Close an open session mtp_response_t tud_mtp_storage_close_session(uint32_t session_id); -// Get a storage ID valid within the current session -// -// TODO: while multiple storage IDs could be used, the implementation currently supports only 1. -mtp_response_t tud_mtp_get_storage_id(uint32_t *storage_id); - -// Get storage information for the given ID -// -// The implementation shall fill all the fields required by the specification. -// Note that the variable information (e.g. wstring file name, dates and tags shall be written by using the library functions) -// In addition to the fixed mtp_storage_info_t structure, the function shall add storage descriptor string and -// volume identifier string via tud_mtp_gct_append_wstring function. -mtp_response_t tud_mtp_get_storage_info(uint32_t storage_id, mtp_storage_info_t *info); - // Format the specified storage mtp_response_t tud_mtp_storage_format(uint32_t storage_id); diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index e35d3e6fe..76764bbba 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -35,6 +35,7 @@ // Macros Helper //--------------------------------------------------------------------+ #define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) +#define TU_FIELD_SZIE(_type, _field) (sizeof(((_type *)0)->_field)) #define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) #define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) #define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d)) -- cgit v1.3.1 From 57c5e5516a7e6711085e3d5f57d2023d6ce1b237 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Sep 2025 16:41:29 +0700 Subject: rework get storageIDs and get storage info --- src/class/mtp/mtp.h | 18 ------------------ src/class/mtp/mtp_device.c | 12 +++++------- src/class/mtp/mtp_device.h | 3 +++ 3 files changed, 8 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index a28ce40df..c48a2469c 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -743,21 +743,9 @@ typedef struct TU_ATTR_PACKED { uint16_t physical; // physical location uint16_t logical; // logical within physical }; - uint32_t id; } mtp_storage_id_t; -// StorageInfo dataset (excluding storage description and volume identifier) -typedef struct TU_ATTR_PACKED { - uint16_t storage_type; - uint16_t filesystem_type; - uint16_t access_capability; - uint64_t max_capacity_in_bytes; - uint64_t free_space_in_bytes; - uint32_t free_space_in_objects; - // storage description and volume identifier are added dynamically -} mtp_storage_info_nostring_t; - #define MTP_STORAGE_INFO_TYPEDEF(_storage_desc_chars, _volume_id_chars) \ struct TU_ATTR_PACKED { \ uint16_t storage_type; \ @@ -794,12 +782,6 @@ typedef struct TU_ATTR_PACKED { // - datetime_wstring date_modified; // - wstring keywords; -// Storage IDs -typedef struct TU_ATTR_PACKED { - uint32_t storage_ids_len; - uint32_t storage_ids[CFG_MTP_STORAGE_ID_COUNT]; -} mtp_storage_ids_t; - // DevicePropDesc Dataset typedef struct TU_ATTR_PACKED { uint16_t device_property_code; diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 857fafd0c..d0a1d6c40 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -351,22 +351,18 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t case MTP_PHASE_RESPONSE_QUEUED: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); + tud_mtp_response_complete_cb(0, &p_mtp->cmd_header, p_container, event, xferred_bytes); prepare_new_command(p_mtp); break; case MTP_PHASE_RESPONSE: case MTP_PHASE_ERROR: - // processed immediately after this switch, supposedly to be empty + // supposedly to be empty break; default: return false; } - if (p_mtp->phase == MTP_PHASE_RESPONSE) { - // p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; - // p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - // p_container->transaction_id = p_mtp->cmd_header.transaction_id; - // TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, (uint8_t*) p_container, (uint16_t)p_container->len), 0); - } else if (p_mtp->phase == MTP_PHASE_ERROR) { + if (p_mtp->phase == MTP_PHASE_ERROR) { // stall both IN & OUT endpoints usbd_edpt_stall(rhport, p_mtp->ep_out); usbd_edpt_stall(rhport, p_mtp->ep_in); @@ -467,9 +463,11 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_DELETE_OBJECT: TU_LOG_DRV(" MTP command: MTP_OP_DELETE_OBJECT\n"); return mtpd_handle_cmd_delete_object(); + case MTP_OP_GET_DEVICE_PROP_DESC: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_DESC\n"); return mtpd_handle_cmd_get_device_prop_desc(); + case MTP_OP_GET_DEVICE_PROP_VALUE: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_VALUE\n"); return mtpd_handle_cmd_get_device_prop_value(); diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index b39322f22..538e3c8d0 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -82,6 +82,9 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl // Invoked when data phase is complete int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes); +// Invoked when response phase is complete +int32_t tud_mtp_response_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes); + //--------------------------------------------------------------------+ // Helper functions //--------------------------------------------------------------------+ -- cgit v1.3.1 From b70804b0c4212f67625e945986b91bbe1d965059 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 20 Sep 2025 00:42:06 +0700 Subject: implement get device properties value --- examples/device/mtp/src/mtp_fs_example.c | 86 ++++++++++++++------------------ src/class/mtp/mtp.h | 38 +++++++++++--- src/class/mtp/mtp_device.c | 11 ++-- 3 files changed, 76 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index fcd01f54a..89814fb8f 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -34,50 +34,12 @@ //--------------------------------------------------------------------+ // device info string (including terminating null) -static const uint16_t dev_info_manufacturer[] = { 'T', 'i', 'n', 'y', 'U', 'S', 'B', 0 }; -static const uint16_t dev_info_model[] = { 'M', 'T', 'P', ' ', 'E', 'x', 'a', 'm', 'p', 'l', 'e', 0 }; -static const uint16_t dev_info_version[] = { '1', '.', '0', 0 }; -static const uint16_t dev_info_serial[] = { '1', '2', '3', '4', '5', '6', 0 }; - -static const uint16_t supported_operations[] = { - MTP_OP_GET_DEVICE_INFO, - MTP_OP_OPEN_SESSION, - MTP_OP_CLOSE_SESSION, - MTP_OP_GET_STORAGE_IDS, - MTP_OP_GET_STORAGE_INFO, - MTP_OP_GET_NUM_OBJECTS, - MTP_OP_GET_OBJECT_HANDLES, - MTP_OP_GET_OBJECT_INFO, - MTP_OP_GET_OBJECT, - MTP_OP_DELETE_OBJECT, - MTP_OP_SEND_OBJECT_INFO, - MTP_OP_SEND_OBJECT, - MTP_OP_FORMAT_STORE, - MTP_OP_RESET_DEVICE, - MTP_OP_GET_DEVICE_PROP_DESC, - MTP_OP_GET_DEVICE_PROP_VALUE, - MTP_OP_SET_DEVICE_PROP_VALUE -}; - -static const uint16_t supported_events[] = { - MTP_EVENT_OBJECT_ADDED, -}; - -static const uint16_t supported_device_properties[] = { - MTP_DEV_PROP_DEVICE_FRIENDLY_NAME, -}; +#define DEV_INFO_MANUFACTURER "TinyUSB" +#define DEV_INFO_MODEL "MTP Example" +#define DEV_INFO_VERSION "1.0" +#define DEV_INFO_SERIAL "123456" -static const uint16_t capture_formats[] = { - MTP_OBJ_FORMAT_UNDEFINED, - MTP_OBJ_FORMAT_ASSOCIATION, - MTP_OBJ_FORMAT_TEXT, -}; - -static const uint16_t playback_formats[] = { - MTP_OBJ_FORMAT_UNDEFINED, - MTP_OBJ_FORMAT_ASSOCIATION, - MTP_OBJ_FORMAT_TEXT, -}; +#define DEV_PROP_FRIENDLY_NAME "TinyUSB MTP" //--------------------------------------------------------------------+ // RAM FILESYSTEM @@ -139,6 +101,10 @@ storage_info_t storage_info = { } }; +enum { + SUPPORTED_STORAGE_ID = 0x00010001u // physical = 1, logical = 1 +}; + //--------------------------------------------------------------------+ // OPERATING STATUS @@ -205,15 +171,24 @@ int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header return 0; } +int32_t tud_mtp_response_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes) { + (void) idx; + (void) cmd_header; + (void) resp_block; + (void) xfer_result; + (void) xferred_bytes; + return 0; // nothing to do +} + int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block) { (void)idx; switch (cmd_block->code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields - mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_manufacturer), dev_info_manufacturer); - mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_model), dev_info_model); - mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_version), dev_info_version); - mtp_container_add_string(out_block, TU_ARRAY_SIZE(dev_info_serial), dev_info_serial); + mtp_container_add_cstring(out_block, DEV_INFO_MANUFACTURER); + mtp_container_add_cstring(out_block, DEV_INFO_MODEL); + mtp_container_add_cstring(out_block, DEV_INFO_VERSION); + mtp_container_add_cstring(out_block, DEV_INFO_SERIAL); tud_mtp_data_send(out_block); break; @@ -225,13 +200,14 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; case MTP_OP_GET_STORAGE_IDS: { - uint32_t storage_ids [] = { 0x00010001u }; // physical = 1, logical = 1 + uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; // physical = 1, logical = 1 mtp_container_add_auint32(out_block, 1, storage_ids); tud_mtp_data_send(out_block); break; } case MTP_OP_GET_STORAGE_INFO: { + TU_VERIFY(SUPPORTED_STORAGE_ID == cmd_block->data[0], -1); // update storage info with current free space storage_info.free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); storage_info.free_space_in_bytes = storage_info.free_space_in_objects * FS_MAX_NODE_BYTES; @@ -240,6 +216,20 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; } + case MTP_OP_GET_DEVICE_PROP_VALUE: { + const uint16_t dev_prop_code = (uint16_t) cmd_block->data[0]; + switch (dev_prop_code) { + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: + mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); + tud_mtp_data_send(out_block); + break; + + default: return -1; + } + break; + } + + default: return -1; } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index c48a2469c..138cb1b86 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -782,17 +782,30 @@ typedef struct TU_ATTR_PACKED { // - datetime_wstring date_modified; // - wstring keywords; -// DevicePropDesc Dataset +// Device property desc up to get/set typedef struct TU_ATTR_PACKED { uint16_t device_property_code; uint16_t datatype; uint8_t get_set; -} mtp_device_prop_desc_t; +} mtp_device_prop_desc_header_t; + // The following fields will be dynamically added to the struct at runtime: // - wstring factory_def_value; // - wstring current_value_len; // - uint8_t form_flag; +// no form +#define MTP_DEVICE_PROPERTIES_TYPEDEF(_type) \ + struct TU_ATTR_PACKED { \ + uint16_t device_property_code; \ + uint16_t datatype; \ + uint8_t get_set; \ + _type factory_default; \ + _type current_value; \ + uint8_t form_flag; /* 0: none, 1: range, 2: enum */ \ + }; + + typedef struct TU_ATTR_PACKED { uint16_t wLength; uint16_t code; @@ -832,15 +845,28 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_field(mtp_generic } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { - const uint32_t prev_len = p_container->len; uint8_t* container8 = (uint8_t*) p_container; - *(container8 + p_container->len) = count; - p_container->len += 1; + container8[p_container->len] = count; + p_container->len++; memcpy(container8 + p_container->len, utf16, 2 * count); p_container->len += 2 * count; - return p_container->len - prev_len; + return 1 + 2 * count; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_generic_container_t* p_container, const char* str) { + uint8_t* container8 = (uint8_t*) p_container; + const uint8_t len = (uint8_t) (strlen(str) + 1); // include null + container8[p_container->len] = len; + p_container->len++; + + for (uint8_t i = 0; i < len; i++) { + container8[p_container->len] = str[i]; + container8[p_container->len + 1] = 0; + p_container->len += 2; + } + return 1 + 2 * len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_generic_container_t* p_container, uint8_t data) { diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index d0a1d6c40..2e9ac9a92 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -466,11 +466,12 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_GET_DEVICE_PROP_DESC: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_DESC\n"); - return mtpd_handle_cmd_get_device_prop_desc(); + break; case MTP_OP_GET_DEVICE_PROP_VALUE: TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_VALUE\n"); - return mtpd_handle_cmd_get_device_prop_value(); + break; + case MTP_OP_SEND_OBJECT_INFO: TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO\n"); return mtpd_handle_cmd_send_object_info(); @@ -673,11 +674,11 @@ mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) { case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: { - TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); + TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_header_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_DEVICE_PROP_DESC; - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_prop_desc_t); - mtp_device_prop_desc_t *d = (mtp_device_prop_desc_t *)p_container->data; + p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_prop_desc_header_t); + mtp_device_prop_desc_header_t *d = (mtp_device_prop_desc_header_t *)p_container->data; d->device_property_code = (uint16_t)(device_prop_code); d->datatype = MTP_DATA_TYPE_STR; d->get_set = MTP_MODE_GET; -- cgit v1.3.1 From d9c6dfbe2b2ede8c4147cef2f82765a16ec803b7 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 20 Sep 2025 17:37:47 +0700 Subject: implement get device properties describer and device properties value --- examples/device/mtp/src/mtp_fs_example.c | 29 +++++++++++++-- src/class/mtp/mtp_device.c | 64 -------------------------------- 2 files changed, 26 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 89814fb8f..e0dafb5ab 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -216,6 +216,26 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; } + case MTP_OP_GET_DEVICE_PROP_DESC: { + const uint16_t dev_prop_code = (uint16_t) cmd_block->data[0]; + mtp_device_prop_desc_header_t device_prop_header; + device_prop_header.device_property_code = dev_prop_code; + switch (dev_prop_code) { + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: + device_prop_header.datatype = MTP_DATA_TYPE_STR; + device_prop_header.get_set = MTP_MODE_GET; + mtp_container_add_raw(out_block, &device_prop_header, sizeof(device_prop_header)); + mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); // factory + mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); // current + mtp_container_add_uint8(out_block, 0); // no form + tud_mtp_data_send(out_block); + break; + + default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; + } + break; + } + case MTP_OP_GET_DEVICE_PROP_VALUE: { const uint16_t dev_prop_code = (uint16_t) cmd_block->data[0]; switch (dev_prop_code) { @@ -224,16 +244,19 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl tud_mtp_data_send(out_block); break; - default: return -1; + default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; } break; } + case MTP_OP_GET_OBJECT_HANDLES: - default: return -1; + break; + + default: return MTP_RESP_OPERATION_NOT_SUPPORTED; } - return 0; + return MTP_RESP_OK; } //--------------------------------------------------------------------+ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 2e9ac9a92..cb7bb9574 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -90,8 +90,6 @@ static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); static mtp_phase_type_t mtpd_handle_cmd_get_object(void); static mtp_phase_type_t mtpd_handle_dti_get_object(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); -static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void); -static mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); static mtp_phase_type_t mtpd_handle_cmd_send_object(void); @@ -662,68 +660,6 @@ mtp_phase_type_t mtpd_handle_cmd_delete_object(void) return MTP_PHASE_RESPONSE; } -mtp_phase_type_t mtpd_handle_cmd_get_device_prop_desc(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t device_prop_code = p_container->data[0]; - - mtp_phase_type_t rt; - if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; - - switch(device_prop_code) - { - case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - { - TU_VERIFY_STATIC(sizeof(mtp_device_prop_desc_header_t) < MTP_MAX_PACKET_SIZE, "mtp_device_info_t shall fit in MTP_MAX_PACKET_SIZE"); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_DEVICE_PROP_DESC; - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_device_prop_desc_header_t); - mtp_device_prop_desc_header_t *d = (mtp_device_prop_desc_header_t *)p_container->data; - d->device_property_code = (uint16_t)(device_prop_code); - d->datatype = MTP_DATA_TYPE_STR; - d->get_set = MTP_MODE_GET; - mtpd_gct_append_wstring(CFG_TUD_MODEL); // factory_def_value - mtpd_gct_append_wstring(CFG_TUD_MODEL); // current_value_len - mtpd_gct_append_uint8(0x00); // form_flag - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; - } - default: - break; - } - - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - return MTP_PHASE_RESPONSE; -} - -mtp_phase_type_t mtpd_handle_cmd_get_device_prop_value(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t device_prop_code = p_container->data[0]; - - mtp_phase_type_t rt; - if ((rt = mtpd_chk_session_open(__func__)) != MTP_PHASE_NONE) return rt; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_DEVICE_PROP_VALUE; - - switch(device_prop_code) - { - // TODO support more device properties - case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - mtpd_gct_append_wstring(CFG_TUD_MODEL); - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; - default: - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - return MTP_PHASE_RESPONSE; - } -} - mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; -- cgit v1.3.1 From f5a3f25456bcfae65de9b60c8073986f4cdc8f16 Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 21 Sep 2025 14:10:36 +0700 Subject: implement get objection handles --- examples/device/mtp/src/mtp_fs_example.c | 37 +++++++++++---- src/class/mtp/mtp.h | 81 ++++++++------------------------ src/class/mtp/mtp_device.c | 44 +---------------- 3 files changed, 48 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index e0dafb5ab..add7d6bf5 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -62,10 +62,10 @@ typedef struct { } fs_object_info_t; // Sample object file -static fs_object_info_t _fs_objects[FS_MAX_NODES] = { +static fs_object_info_t fs_objects[FS_MAX_NODES] = { { .handle = 1, - .parent = 0, + .parent = 0xffffffff, .allocated = true, .association = false, .name = "readme.txt", @@ -142,7 +142,7 @@ unsigned int fs_get_object_count(void); fs_object_info_t* fs_object_get_from_handle(uint32_t handle) { fs_object_info_t* obj; for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - obj = &_fs_objects[i]; + obj = &fs_objects[i]; if (obj->allocated && obj->handle == handle) return obj; } @@ -152,7 +152,7 @@ fs_object_info_t* fs_object_get_from_handle(uint32_t handle) { unsigned int fs_get_object_count(void) { unsigned int s = 0; for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - if (_fs_objects[i].allocated) + if (fs_objects[i].allocated) s++; } return s; @@ -249,9 +249,26 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; } - case MTP_OP_GET_OBJECT_HANDLES: + case MTP_OP_GET_OBJECT_HANDLES: { + const uint32_t storage_id = cmd_block->data[0]; + const uint32_t obj_format = cmd_block->data[1]; // optional + (void) obj_format; + const uint32_t parent_handle = cmd_block->data[2]; // folder handle, 0xFFFFFFFF is root + if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { + return MTP_RESP_INVALID_STORAGE_ID; + } + uint32_t handles[FS_MAX_NODES] = { 0 }; + uint32_t count = 0; + for (uint8_t i = 0, h = 0; i < FS_MAX_NODES; i++) { + if (fs_objects[i].allocated && parent_handle == fs_objects[i].parent) { + handles[count++] = fs_objects[i].handle; + } + } + mtp_container_add_auint32(out_block, count, handles); + tud_mtp_data_send(out_block); break; + } default: return MTP_RESP_OPERATION_NOT_SUPPORTED; } @@ -284,7 +301,7 @@ mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { // Simply deallocate all entries for (unsigned int i = 0; i < FS_MAX_NODES; i++) - _fs_objects[i].allocated = false; + fs_objects[i].allocated = false; TU_LOG1("Format completed\r\n"); return MTP_RESP_OK; } @@ -314,7 +331,7 @@ mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t storage_id } for (unsigned int i = _fs_operation.traversal_index; i < FS_MAX_NODES; i++) { - obj = &_fs_objects[i]; + obj = &fs_objects[i]; if (obj->allocated && obj->parent == parent_object_handle) { _fs_operation.traversal_index = i + 1; *next_child_handle = obj->handle; @@ -366,8 +383,8 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // Search for first free object for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - if (!_fs_objects[i].allocated) { - obj = &_fs_objects[i]; + if (!fs_objects[i].allocated) { + obj = &fs_objects[i]; break; } } @@ -572,7 +589,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { if (object_handle == 0 || obj->association) { // Delete also children for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - obj = &_fs_objects[i]; + obj = &fs_objects[i]; if (obj->allocated && obj->parent == object_handle) { tud_mtp_storage_object_delete(obj->handle); } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 138cb1b86..c6ff2946a 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -654,47 +654,6 @@ typedef enum { MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u, } mtp_association_t; - -tu_static const uint16_t mtp_operations_supported[] = { - MTP_OP_GET_DEVICE_INFO, - MTP_OP_OPEN_SESSION, - MTP_OP_CLOSE_SESSION, - MTP_OP_GET_STORAGE_IDS, - MTP_OP_GET_STORAGE_INFO, - MTP_OP_GET_NUM_OBJECTS, - MTP_OP_GET_OBJECT_HANDLES, - MTP_OP_GET_OBJECT_INFO, - MTP_OP_GET_OBJECT, - MTP_OP_DELETE_OBJECT, - MTP_OP_SEND_OBJECT_INFO, - MTP_OP_SEND_OBJECT, - MTP_OP_FORMAT_STORE, - MTP_OP_RESET_DEVICE, - MTP_OP_GET_DEVICE_PROP_DESC, - MTP_OP_GET_DEVICE_PROP_VALUE, - MTP_OP_SET_DEVICE_PROP_VALUE, -}; - -tu_static const uint16_t mtp_events_supported[] = { - MTP_EVENT_OBJECT_ADDED, -}; - -tu_static const uint16_t mtp_device_properties_supported[] = { - MTP_DEV_PROP_DEVICE_FRIENDLY_NAME, -}; - -tu_static const uint16_t mtp_capture_formats[] = { - MTP_OBJ_FORMAT_UNDEFINED, - MTP_OBJ_FORMAT_ASSOCIATION, - MTP_OBJ_FORMAT_TEXT, -}; - -tu_static const uint16_t mtp_playback_formats[] = { - MTP_OBJ_FORMAT_UNDEFINED, - MTP_OBJ_FORMAT_ASSOCIATION, - MTP_OBJ_FORMAT_TEXT, -}; - //--------------------------------------------------------------------+ // Data structures //--------------------------------------------------------------------+ @@ -738,7 +697,7 @@ typedef struct TU_ATTR_PACKED { uint16_t utf16[]; } mtp_flexible_string_t; - typedef union TU_ATTR_PACKED { +typedef union TU_ATTR_PACKED { struct { uint16_t physical; // physical location uint16_t logical; // logical within physical @@ -827,21 +786,16 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_generic_c return len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_field(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { - if (count == 0) { - // count = 0 means scalar - return mtp_container_add_raw(p_container, data, scalar_size); - } else { - uint8_t* container8 = (uint8_t*) p_container; +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { + uint8_t* container8 = (uint8_t*)p_container; - tu_unaligned_write32(container8 + p_container->len, count); - p_container->len += 4; + tu_unaligned_write32(container8 + p_container->len, count); + p_container->len += 4; - memcpy(container8 + p_container->len, data, count * scalar_size); - p_container->len += count * scalar_size; + memcpy(container8 + p_container->len, data, count * scalar_size); + p_container->len += count * scalar_size; - return 4 + count * scalar_size; - } + return 4 + count * scalar_size; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { @@ -870,34 +824,37 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_gener } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_generic_container_t* p_container, uint8_t data) { - return mtp_container_add_field(p_container, sizeof(uint8_t), 0, &data); + return mtp_container_add_raw(p_container, &data, 1); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint16(mtp_generic_container_t* p_container, uint16_t data) { - return mtp_container_add_field(p_container, sizeof(uint16_t), 0, &data); + return mtp_container_add_raw(p_container, &data, 2); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint32(mtp_generic_container_t* p_container, uint32_t data) { - return mtp_container_add_field(p_container, sizeof(uint32_t), 0, &data); + return mtp_container_add_raw(p_container, &data, 4); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint64(mtp_generic_container_t* p_container, uint64_t data) { - return mtp_container_add_field(p_container, 8, 0, &data); + return mtp_container_add_raw(p_container, &data, 8); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint128(mtp_generic_container_t* p_container, const void* data) { + return mtp_container_add_raw(p_container, data, 16); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint8(mtp_generic_container_t* p_container, uint32_t count, const uint8_t* data) { - return mtp_container_add_field(p_container, sizeof(uint8_t), count, data); + return mtp_container_add_array(p_container, sizeof(uint8_t), count, data); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint16(mtp_generic_container_t* p_container, uint32_t count, const uint16_t* data) { - return mtp_container_add_field(p_container, sizeof(uint16_t), count, data); + return mtp_container_add_array(p_container, sizeof(uint16_t), count, data); } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_generic_container_t* p_container, uint32_t count, const uint32_t* data) { - return mtp_container_add_field(p_container, sizeof(uint32_t), count, data); + return mtp_container_add_array(p_container, sizeof(uint32_t), count, data); } - #ifdef __cplusplus } #endif diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index cb7bb9574..8e403f9ac 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -85,7 +85,6 @@ static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_close_session(void); -static mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void); static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); static mtp_phase_type_t mtpd_handle_cmd_get_object(void); static mtp_phase_type_t mtpd_handle_dti_get_object(void); @@ -451,7 +450,8 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_GET_OBJECT_HANDLES: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_HANDLES\n"); - return mtpd_handle_cmd_get_object_handles(); + break; + case MTP_OP_GET_OBJECT_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_INFO\n"); return mtpd_handle_cmd_get_object_info(); @@ -528,46 +528,6 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) return MTP_PHASE_RESPONSE; } -mtp_phase_type_t mtpd_handle_cmd_get_object_handles(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t storage_id = p_container->data[0]; - uint32_t object_format_code = p_container->data[1]; // optional, not managed - uint32_t parent_object_handle = p_container->data[2]; // folder specification, 0xffffffff=objects with no parent - - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(uint32_t); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_OBJECT_HANDLES; - p_container->data[0] = 0; - - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (object_format_code != 0), MTP_RESP_SPECIFICATION_BY_FORMAT_UNSUPPORTED, "specification by format unsupported")) != MTP_PHASE_NONE) { - return phase; - } - //list of all object handles on all storages, not managed - if ((phase = mtpd_chk_generic(__func__, (storage_id == 0xFFFFFFFF), MTP_RESP_OPERATION_NOT_SUPPORTED, "list of all object handles on all storages unsupported")) != MTP_PHASE_NONE) { - return phase; - } - - tud_mtp_storage_object_done(); - uint32_t next_child_handle = 0; - while(true) - { - mtp_response_t res = tud_mtp_storage_association_get_object_handle(storage_id, parent_object_handle, &next_child_handle); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - if (next_child_handle == 0) { - break; - } - mtpd_gct_append_object_handle(next_child_handle); - } - tud_mtp_storage_object_done(); - - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; -} - mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) { TU_VERIFY_STATIC(sizeof(mtp_object_info_t) < MTP_MAX_PACKET_SIZE, "mtp_object_info_t shall fit in MTP_MAX_PACKET_SIZE"); -- cgit v1.3.1 From 4c818998d499a5a22c27ddfafc618e04ddcb823f Mon Sep 17 00:00:00 2001 From: hathach Date: Sun, 21 Sep 2025 21:39:27 +0700 Subject: implement get object info --- examples/device/mtp/src/mtp_fs_example.c | 85 +++++++++++++++----------------- src/class/mtp/mtp.h | 35 ++++++------- src/class/mtp/mtp_device.c | 25 ++-------- src/class/mtp/mtp_device_storage.h | 4 +- 4 files changed, 60 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index add7d6bf5..20d09508f 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -65,7 +65,7 @@ typedef struct { static fs_object_info_t fs_objects[FS_MAX_NODES] = { { .handle = 1, - .parent = 0xffffffff, + .parent = 0, .allocated = true, .association = false, .name = "readme.txt", @@ -80,7 +80,7 @@ static fs_object_info_t fs_objects[FS_MAX_NODES] = { #define STORAGE_DESCRIPTRION { 'd', 'i', 's', 'k', 0 } #define VOLUME_IDENTIFIER { 'v', 'o', 'l', 0 } -typedef MTP_STORAGE_INFO_TYPEDEF(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION), +typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION), TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER)) ) storage_info_t; @@ -261,7 +261,8 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl uint32_t handles[FS_MAX_NODES] = { 0 }; uint32_t count = 0; for (uint8_t i = 0, h = 0; i < FS_MAX_NODES; i++) { - if (fs_objects[i].allocated && parent_handle == fs_objects[i].parent) { + if (fs_objects[i].allocated && parent_handle == fs_objects[i].parent || + (parent_handle == 0xFFFFFFFF && fs_objects[i].parent == 0)) { handles[count++] = fs_objects[i].handle; } } @@ -270,6 +271,40 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; } + case MTP_OP_GET_OBJECT_INFO: { + const uint32_t object_handle = cmd_block->data[0]; + fs_object_info_t* obj = fs_object_get_from_handle(object_handle); + if (obj == NULL) { + TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + mtp_object_info_header_t object_info_header = { + .storage_id = SUPPORTED_STORAGE_ID, + .object_format = MTP_OBJ_FORMAT_TEXT, + .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, + .object_compressed_size = obj->size, + .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, + .thumb_compressed_size = 0, + .thumb_pix_width = 0, + .thumb_pix_height = 0, + .image_pix_width = 0, + .image_pix_height = 0, + .image_bit_depth = 0, + .parent_object = obj->parent, + .association_type = MTP_ASSOCIATION_UNDEFINED, + .association_desc = 0, + .sequence_number = 0 + }; + mtp_container_add_raw(out_block, &object_info_header, sizeof(object_info_header)); + mtp_container_add_cstring(out_block, obj->name); + mtp_container_add_cstring(out_block, obj->created); + mtp_container_add_cstring(out_block, obj->modified); + mtp_container_add_cstring(out_block, ""); // keywords, not used + + tud_mtp_data_send(out_block); + break; + } + default: return MTP_RESP_OPERATION_NOT_SUPPORTED; } @@ -346,7 +381,7 @@ mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t storage_id } mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, - uint32_t* new_object_handle, const mtp_object_info_t* info) { + uint32_t* new_object_handle, const mtp_object_info_header_t* info) { fs_object_info_t* obj = NULL; if (_fs_operation.session_id == 0) { @@ -402,7 +437,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; // Extract variable data - uint16_t offset_data = sizeof(mtp_object_info_t); + uint16_t offset_data = sizeof(mtp_object_info_header_t); mtpd_gct_get_string(&offset_data, obj->name, FS_MAX_NODE_NAME_LEN); mtpd_gct_get_string(&offset_data, obj->created, FS_ISODATETIME_LEN); mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); @@ -417,46 +452,6 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t* info) { - const fs_object_info_t* obj; - - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - - obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - - memset(info, 0, sizeof(mtp_object_info_t)); - info->storage_id = STORAGE_ID(0x0001, 0x0001); - if (obj->association) { - info->object_format = MTP_OBJ_FORMAT_ASSOCIATION; - info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; - info->object_compressed_size = 0; - info->association_type = MTP_ASSOCIATION_UNDEFINED; - } else { - info->object_format = MTP_OBJ_FORMAT_UNDEFINED; - info->protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION; - info->object_compressed_size = obj->size; - info->association_type = MTP_ASSOCIATION_UNDEFINED; - } - info->thumb_format = MTP_OBJ_FORMAT_UNDEFINED; - info->parent_object = obj->parent; - - mtpd_gct_append_wstring(obj->name); - mtpd_gct_append_wstring(obj->created); // date_created - mtpd_gct_append_wstring(obj->modified); // date_modified - mtpd_gct_append_wstring(""); // keywords, not used - - TU_LOG1("Retrieve object %s with handle %ld\r\n", obj->name, obj->handle); - - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { fs_object_info_t* obj; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index c6ff2946a..61adc29e4 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -647,11 +647,12 @@ typedef enum { typedef enum { MTP_ASSOCIATION_UNDEFINED = 0x0000u, MTP_ASSOCIATION_GENERIC_FOLDER = 0x0001u, - MTP_ASSOCIATION_GENERIC_ALBUM = 0x0002u, + MTP_ASSOCIATION_ALBUM = 0x0002u, MTP_ASSOCIATION_TIME_SEQUENCE = 0x0003u, MTP_ASSOCIATION_HORIZONTAL_PANORAMIC = 0x0004u, MTP_ASSOCIATION_VERTICAL_PANORAMIC = 0x0005u, MTP_ASSOCIATION_2D_PANORAMIC = 0x0006u, + MTP_ASSOCIATION_ANCILLARY_DATA = 0x0007u, } mtp_association_t; //--------------------------------------------------------------------+ @@ -705,7 +706,7 @@ typedef union TU_ATTR_PACKED { uint32_t id; } mtp_storage_id_t; -#define MTP_STORAGE_INFO_TYPEDEF(_storage_desc_chars, _volume_id_chars) \ +#define MTP_STORAGE_INFO_STRUCT(_storage_desc_chars, _volume_id_chars) \ struct TU_ATTR_PACKED { \ uint16_t storage_type; \ uint16_t filesystem_type; \ @@ -717,29 +718,24 @@ typedef union TU_ATTR_PACKED { mtp_string_t(_volume_id_chars) volume_identifier; \ } -// ObjectInfo Dataset +// Object Info Dataset without dynamic string: filename, date_created, date_modified, keywords typedef struct TU_ATTR_PACKED { uint32_t storage_id; uint16_t object_format; uint16_t protection_status; uint32_t object_compressed_size; - uint16_t thumb_format; // unused - uint32_t thumb_compressed_size; // unused - uint32_t thumb_pix_width; // unused - uint32_t thumb_pix_height; // unused - uint32_t image_pix_width; // unused - uint32_t image_pix_height; // unused - uint32_t image_bit_depth; // unused + uint16_t thumb_format; + uint32_t thumb_compressed_size; + uint32_t thumb_pix_width; + uint32_t thumb_pix_height; + uint32_t image_pix_width; + uint32_t image_pix_height; + uint32_t image_bit_depth; uint32_t parent_object; // 0: root uint16_t association_type; - uint32_t association_description; // not used - uint32_t sequence_number; // not used -} mtp_object_info_t; -// The following fields will be dynamically added to the struct at runtime: -// - wstring filename; -// - datetime_wstring date_created; -// - datetime_wstring date_modified; -// - wstring keywords; + uint32_t association_desc; + uint32_t sequence_number; +} mtp_object_info_header_t; // Device property desc up to get/set typedef struct TU_ATTR_PACKED { @@ -754,7 +750,7 @@ typedef struct TU_ATTR_PACKED { // - uint8_t form_flag; // no form -#define MTP_DEVICE_PROPERTIES_TYPEDEF(_type) \ +#define MTP_DEVICE_PROPERTIES_STRUCT(_type) \ struct TU_ATTR_PACKED { \ uint16_t device_property_code; \ uint16_t datatype; \ @@ -764,7 +760,6 @@ typedef struct TU_ATTR_PACKED { uint8_t form_flag; /* 0: none, 1: range, 2: enum */ \ }; - typedef struct TU_ATTR_PACKED { uint16_t wLength; uint16_t code; diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 8e403f9ac..22ef45cd9 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -85,7 +85,6 @@ static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_close_session(void); -static mtp_phase_type_t mtpd_handle_cmd_get_object_info(void); static mtp_phase_type_t mtpd_handle_cmd_get_object(void); static mtp_phase_type_t mtpd_handle_dti_get_object(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); @@ -454,7 +453,8 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_GET_OBJECT_INFO: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_INFO\n"); - return mtpd_handle_cmd_get_object_info(); + break; + case MTP_OP_GET_OBJECT: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT\n"); return mtpd_handle_cmd_get_object(); @@ -528,25 +528,6 @@ mtp_phase_type_t mtpd_handle_cmd_close_session(void) return MTP_PHASE_RESPONSE; } -mtp_phase_type_t mtpd_handle_cmd_get_object_info(void) -{ - TU_VERIFY_STATIC(sizeof(mtp_object_info_t) < MTP_MAX_PACKET_SIZE, "mtp_object_info_t shall fit in MTP_MAX_PACKET_SIZE"); - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t object_handle = p_container->data[0]; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(mtp_object_info_t); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_OBJECT_INFO; - mtp_response_t res = tud_mtp_storage_object_read_info(object_handle, (mtp_object_info_t *)p_container->data); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - - _mtpd_itf.queued_len = p_container->len; - return MTP_PHASE_DATA_IN; -} - mtp_phase_type_t mtpd_handle_cmd_get_object(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; @@ -634,7 +615,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object_info(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; uint32_t new_object_handle = 0; - mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_t *)p_container->data); + mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_header_t *)p_container->data); mtp_phase_type_t phase; if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h index 6c12d38e3..29d4b0bd5 100644 --- a/src/class/mtp/mtp_device_storage.h +++ b/src/class/mtp/mtp_device_storage.h @@ -77,7 +77,7 @@ mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t session_ha // The handle of the new object shall be returned in new_object_handle. // The structure info contains the information to be used for file creation, as passted by the host. // Note that the variable information (e.g. wstring file name, dates and tags shall be retrieved by using the library functions) -mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_t *info); +mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_header_t *info); // Get object information related to a given object handle // @@ -89,7 +89,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // - Date modified (string, use tud_gct_append_date or empty string) // - Keywords (string containing list of kw, separated by space, use tud_mtp_gct_append_wstring) // Note that the variable information (e.g. wstring file name, dates and tags shall be written by using the library functions) -mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_t *info); +mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_header_t *info); // Get the object size. // -- cgit v1.3.1 From 6fa5268a9c013c9d38fc71649400d9ecc8c2dddf Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 22 Sep 2025 12:26:03 +0700 Subject: implement get object, close session --- examples/device/mtp/src/mtp_fs_example.c | 165 +++++++++---------------------- src/class/mtp/mtp.h | 1 - src/class/mtp/mtp_device.c | 116 +++------------------- src/class/mtp/mtp_device.h | 32 +++++- src/class/mtp/mtp_device_storage.h | 42 -------- 5 files changed, 90 insertions(+), 266 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 20d09508f..a1c45c6e4 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -30,10 +30,10 @@ #define MTPD_VOLUME_IDENTIFIER "volume" //--------------------------------------------------------------------+ -// Device Info +// Dataset //--------------------------------------------------------------------+ -// device info string (including terminating null) +//------------- device info -------------// #define DEV_INFO_MANUFACTURER "TinyUSB" #define DEV_INFO_MODEL "MTP Example" #define DEV_INFO_VERSION "1.0" @@ -41,6 +41,14 @@ #define DEV_PROP_FRIENDLY_NAME "TinyUSB MTP" +//------------- storage info -------------// +#define STORAGE_DESCRIPTRION { 'd', 'i', 's', 'k', 0 } +#define VOLUME_IDENTIFIER { 'v', 'o', 'l', 0 } + +typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION), + TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER)) +) storage_info_t; + //--------------------------------------------------------------------+ // RAM FILESYSTEM //--------------------------------------------------------------------+ @@ -77,12 +85,7 @@ static fs_object_info_t fs_objects[FS_MAX_NODES] = { }; //------------- Storage Info -------------// -#define STORAGE_DESCRIPTRION { 'd', 'i', 's', 'k', 0 } -#define VOLUME_IDENTIFIER { 'v', 'o', 'l', 0 } -typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION), - TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER)) -) storage_info_t; storage_info_t storage_info = { .storage_type = MTP_STORAGE_TYPE_FIXED_RAM, @@ -105,6 +108,7 @@ enum { SUPPORTED_STORAGE_ID = 0x00010001u // physical = 1, logical = 1 }; +static bool is_session_opened = false; //--------------------------------------------------------------------+ // OPERATING STATUS @@ -160,14 +164,9 @@ unsigned int fs_get_object_count(void) { int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes) { (void) idx; - // switch (cmd_header->code) { - // default: break; - // } resp_block->len = MTP_CONTAINER_HEADER_LENGTH; resp_block->code = (xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - tud_mtp_response_send(resp_block); - return 0; } @@ -195,7 +194,24 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl } case MTP_OP_OPEN_SESSION: - out_block->code = MTP_RESP_OK; + if (is_session_opened) { + //return MTP_RESP_SESSION_ALREADY_OPEN; + out_block->code = MTP_RESP_SESSION_ALREADY_OPEN; + }else { + out_block->code = MTP_RESP_OK; + } + is_session_opened = true; + tud_mtp_response_send(out_block); + break; + + case MTP_OP_CLOSE_SESSION: + if (!is_session_opened) { + // return MTP_RESP_SESSION_NOT_OPEN; + out_block->code = MTP_RESP_SESSION_NOT_OPEN; + } else { + out_block->code = MTP_RESP_OK; + } + is_session_opened = false; tud_mtp_response_send(out_block); break; @@ -272,13 +288,12 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl } case MTP_OP_GET_OBJECT_INFO: { - const uint32_t object_handle = cmd_block->data[0]; - fs_object_info_t* obj = fs_object_get_from_handle(object_handle); + const uint32_t obj_handle = cmd_block->data[0]; + fs_object_info_t* obj = fs_object_get_from_handle(obj_handle); if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); return MTP_RESP_INVALID_OBJECT_HANDLE; } - mtp_object_info_header_t object_info_header = { + mtp_object_info_header_t obj_info_header = { .storage_id = SUPPORTED_STORAGE_ID, .object_format = MTP_OBJ_FORMAT_TEXT, .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, @@ -295,7 +310,7 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl .association_desc = 0, .sequence_number = 0 }; - mtp_container_add_raw(out_block, &object_info_header, sizeof(object_info_header)); + mtp_container_add_raw(out_block, &obj_info_header, sizeof(obj_info_header)); mtp_container_add_cstring(out_block, obj->name); mtp_container_add_cstring(out_block, obj->created); mtp_container_add_cstring(out_block, obj->modified); @@ -305,6 +320,18 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl break; } + case MTP_OP_GET_OBJECT: { + const uint32_t obj_handle = cmd_block->data[0]; + fs_object_info_t* obj = fs_object_get_from_handle(obj_handle); + if (obj == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + + mtp_container_add_raw(out_block, obj->data, obj->size); + tud_mtp_data_send(out_block); + break; + } + default: return MTP_RESP_OPERATION_NOT_SUPPORTED; } @@ -314,22 +341,12 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl //--------------------------------------------------------------------+ // API //--------------------------------------------------------------------+ -mtp_response_t tud_mtp_storage_close_session(uint32_t session_id) { - if (session_id != _fs_operation.session_id) { - TU_LOG1("ERR: Session %ld not open\r\n", session_id); - return MTP_RESP_SESSION_NOT_OPEN; - } - _fs_operation.session_id = 0; - TU_LOG1("Session closed\r\n"); - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); return MTP_RESP_SESSION_NOT_OPEN; } - if (storage_id != STORAGE_ID(0x0001, 0x0001)) { + if (storage_id != SUPPORTED_STORAGE_ID) { TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); return MTP_RESP_INVALID_STORAGE_ID; } @@ -341,45 +358,6 @@ mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t storage_id, uint32_t parent_object_handle, - uint32_t* next_child_handle) { - fs_object_info_t* obj; - - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - // We just have one storage, same reply if querying all storages - if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } - - // Request for objects with no parent (0xFFFFFFFF) are considered root objects - // Note: implementation may pass 0 as parent_object_handle - if (parent_object_handle == 0xFFFFFFFF) - parent_object_handle = 0; - - if (parent_object_handle != _fs_operation.traversal_parent) { - _fs_operation.traversal_parent = parent_object_handle; - _fs_operation.traversal_index = 0; - } - - for (unsigned int i = _fs_operation.traversal_index; i < FS_MAX_NODES; i++) { - obj = &fs_objects[i]; - if (obj->allocated && obj->parent == parent_object_handle) { - _fs_operation.traversal_index = i + 1; - *next_child_handle = obj->handle; - TU_LOG1("Association %ld -> child %ld\r\n", parent_object_handle, obj->handle); - return MTP_RESP_OK; - } - } - TU_LOG1("Association traversal completed\r\n"); - _fs_operation.traversal_index = 0; - *next_child_handle = 0; - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t* new_object_handle, const mtp_object_info_header_t* info) { fs_object_info_t* obj = NULL; @@ -389,7 +367,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p return MTP_RESP_SESSION_NOT_OPEN; } // Accept command on default storage - if (storage_id != 0xFFFFFFFF && storage_id != STORAGE_ID(0x0001, 0x0001)) { + if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); return MTP_RESP_INVALID_STORAGE_ID; } @@ -480,55 +458,6 @@ mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_ return MTP_RESP_OK; } -mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t* size) { - const fs_object_info_t* obj; - obj = fs_object_get_from_handle(object_handle); - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - *size = obj->size; - return MTP_RESP_OK; -} - -mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void* buffer, uint32_t buffer_size, - uint32_t* read_count) { - const fs_object_info_t* obj; - - obj = fs_object_get_from_handle(object_handle); - - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - // It's not a requirement that this command is preceded by a read info - if (object_handle != _fs_operation.read_handle) { - TU_LOG1("ERR: Object %ld not open for read\r\n", object_handle); - _fs_operation.read_handle = object_handle; - _fs_operation.read_pos = 0; - } - - if (obj->size - _fs_operation.read_pos > buffer_size) { - TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, buffer_size, _fs_operation.read_pos); - *read_count = buffer_size; - if (_fs_operation.read_pos + buffer_size < FS_MAX_NODE_BYTES) { - memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); - } - _fs_operation.read_pos += *read_count; - } else { - TU_LOG1("Read object %ld: %ld bytes at offset %ld\r\n", object_handle, obj->size - _fs_operation.read_pos, - _fs_operation.read_pos); - *read_count = obj->size - _fs_operation.read_pos; - if (_fs_operation.read_pos + *read_count < FS_MAX_NODE_BYTES) { - memcpy(buffer, &obj->data[_fs_operation.read_pos], *read_count); - } - // Read operation completed - _fs_operation.read_handle = 0; - _fs_operation.read_pos = 0; - } - return MTP_RESP_OK; -} - mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) { fs_object_info_t* obj; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 61adc29e4..c844b08ab 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -38,7 +38,6 @@ extern "C" { #endif -#define STORAGE_ID(physical_id, logical_id) ( (((uint32_t)physical_id & 0xFFFF) << 16) | ((uint32_t)logical_id & 0x0000FFFF) ) typedef uint16_t wchar16_t; //--------------------------------------------------------------------+ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 22ef45cd9..ca594158e 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -79,14 +79,10 @@ typedef struct { //--------------------------------------------------------------------+ // Checker static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); -static mtp_phase_type_t mtpd_chk_session_open(const char *func_name); // MTP commands static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); static mtp_phase_type_t mtpd_handle_data(void); -static mtp_phase_type_t mtpd_handle_cmd_close_session(void); -static mtp_phase_type_t mtpd_handle_cmd_get_object(void); -static mtp_phase_type_t mtpd_handle_dti_get_object(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); @@ -272,6 +268,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } +#if 0 case MTP_PHASE_DATA_IN: p_mtp->xferred_len += xferred_bytes; p_mtp->handled_len = p_mtp->xferred_len; @@ -333,16 +330,17 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } else if (p_mtp->handled_len == 0) { // First data block includes container header + container data TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, - (uint8_t*) p_container + p_mtp->xferred_len, + (uint8_t*) p_container + p_mtp->xferred_len, (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); } else { // Successive data block includes only container data TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, - ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, - (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); + ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, + (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); } } break; +#endif case MTP_PHASE_RESPONSE_QUEUED: // response phase is complete -> prepare for new command @@ -391,12 +389,16 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); tud_mtp_device_info_t dev_info = { .standard_version = 100, - .mtp_vendor_extension_id = 0xFFFFFFFFU, + .mtp_vendor_extension_id = 6, // MTP specs say 0xFFFFFFFF but libMTP check for value 6 .mtp_version = 100, +#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS .mtp_extensions = { .count = sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), .utf16 = { 0 } }, +#else + .mtp_extensions = 0, +#endif .functional_mode = 0x0000, .supported_operations = { .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), @@ -419,9 +421,11 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { .arr = { CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS } } }; +#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS for (uint8_t i=0; i < dev_info.mtp_extensions.count; i++) { dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; } +#endif p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(tud_mtp_device_info_t); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; p_container->code = MTP_OP_GET_DEVICE_INFO; @@ -437,7 +441,7 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_CLOSE_SESSION: TU_LOG_DRV(" MTP command: MTP_OP_CLOSE_SESSION\n"); - return mtpd_handle_cmd_close_session(); + break; case MTP_OP_GET_STORAGE_IDS: TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); @@ -457,7 +461,8 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { case MTP_OP_GET_OBJECT: TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT\n"); - return mtpd_handle_cmd_get_object(); + break; + case MTP_OP_DELETE_OBJECT: TU_LOG_DRV(" MTP command: MTP_OP_DELETE_OBJECT\n"); return mtpd_handle_cmd_delete_object(); @@ -495,9 +500,6 @@ mtp_phase_type_t mtpd_handle_data(void) switch(p_container->code) { - case MTP_OP_GET_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT-DATA_IN\n"); - return mtpd_handle_dti_get_object(); case MTP_OP_SEND_OBJECT_INFO: TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO-DATA_OUT\n"); return mtpd_handle_dto_send_object_info(); @@ -511,79 +513,6 @@ mtp_phase_type_t mtpd_handle_data(void) return true; } - -mtp_phase_type_t mtpd_handle_cmd_close_session(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint32_t session_id = p_container->data[0]; - - mtp_response_t res = tud_mtp_storage_close_session(session_id); - - _mtpd_itf.session_id = session_id; - - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = res; - - return MTP_PHASE_RESPONSE; -} - -mtp_phase_type_t mtpd_handle_cmd_get_object(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - _mtpd_get_object_handle = p_container->data[0]; - - // Continue with DATA-IN - return mtpd_handle_dti_get_object(); -} - -mtp_phase_type_t mtpd_handle_dti_get_object(void) -{ - mtp_response_t res; - mtp_phase_type_t phase; - uint32_t file_size = 0; - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - res = tud_mtp_storage_object_size(_mtpd_get_object_handle, &file_size); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - p_container->len = MTP_CONTAINER_HEADER_LENGTH + file_size; - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_OBJECT; - - uint32_t buffer_size; - uint32_t read_count; - // Data block must be multiple of EP size - if (_mtpd_itf.handled_len == 0) - { - // First data block: include container header - buffer_size = ((MTP_MAX_PACKET_SIZE + MTP_CONTAINER_HEADER_LENGTH) / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE - MTP_CONTAINER_HEADER_LENGTH; - res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - _mtpd_itf.queued_len = MTP_CONTAINER_HEADER_LENGTH + read_count; - } - else - { - // Successive data block: consider only container data - buffer_size = (MTP_MAX_PACKET_SIZE / CFG_MTP_EP_SIZE) * CFG_MTP_EP_SIZE; - res = tud_mtp_storage_object_read(_mtpd_get_object_handle, (void *)&p_container->data, buffer_size, &read_count); - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) { - return phase; - } - _mtpd_itf.queued_len = read_count; - } - - // File completed - if (read_count < buffer_size) - { - tud_mtp_storage_object_done(); - } - - return MTP_PHASE_DATA_IN; -} - mtp_phase_type_t mtpd_handle_cmd_delete_object(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; @@ -689,21 +618,6 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) //--------------------------------------------------------------------+ // Checker //--------------------------------------------------------------------+ -mtp_phase_type_t mtpd_chk_session_open(const char *func_name) -{ - (void)func_name; - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - if (_mtpd_itf.session_id == 0) - { - TU_LOG_DRV(" MTP error: %s session not open\n", func_name); - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_SESSION_NOT_OPEN; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - return MTP_PHASE_RESPONSE; - } - return MTP_PHASE_NONE; -} - mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message) { (void)func_name; diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 538e3c8d0..b85b1706e 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -44,7 +44,7 @@ typedef struct { // Number of supported operations, events, device properties, capture formats, playback formats // and max number of characters for strings manufacturer, model, device_version, serial_number -#define MTP_DEVICE_INFO_TYPEDEF(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ +#define MTP_DEVICE_INFO_STRUCT(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ struct TU_ATTR_PACKED { \ uint16_t standard_version; \ uint32_t mtp_vendor_extension_id; \ @@ -59,11 +59,34 @@ typedef struct { /* string fields will be added using append function */ \ } -typedef MTP_DEVICE_INFO_TYPEDEF( - sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), +#define MTP_DEVICE_INFO_NO_EXTENSION_STRUCT(_op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ + struct TU_ATTR_PACKED { \ + uint16_t standard_version; \ + uint32_t mtp_vendor_extension_id; \ + uint16_t mtp_version; \ + uint8_t mtp_extensions; \ + uint16_t functional_mode; \ + mtp_auint16_t(_op_count) supported_operations; \ + mtp_auint16_t(_event_count) supported_events; \ + mtp_auint16_t(_devprop_count) supported_device_properties; \ + mtp_auint16_t(_capture_count) capture_formats; \ + mtp_auint16_t(_playback_count) playback_formats; \ + /* string fields will be added using append function */ \ + } + +#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS + typedef MTP_DEVICE_INFO_STRUCT( + sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) + ) tud_mtp_device_info_t; +#else + typedef MTP_DEVICE_INFO_NO_EXTENSION_STRUCT( + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) ) tud_mtp_device_info_t; +#endif //--------------------------------------------------------------------+ // Application API @@ -76,7 +99,8 @@ bool tud_mtp_response_send(mtp_generic_container_t* resp_block); // Application Callbacks //--------------------------------------------------------------------+ -// Invoked when new command is received +// Invoked when new command is received. Application fill the out_block with either DATA or RESPONSE container +// return MTP response code int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block); // Invoked when data phase is complete diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h index 29d4b0bd5..30038978f 100644 --- a/src/class/mtp/mtp_device_storage.h +++ b/src/class/mtp/mtp_device_storage.h @@ -54,63 +54,21 @@ * Depending on the application, the handle could be also be the file name or a tag (i.e. host-only file access) */ - -// Initialize MTP storage subsystem -// -// The function shall check if the session is already opened and, in case, set session_id to the -// ID of the current session. - -// Close an open session -mtp_response_t tud_mtp_storage_close_session(uint32_t session_id); - // Format the specified storage mtp_response_t tud_mtp_storage_format(uint32_t storage_id); -// Traverse the given parent object handle and return a child handle for each call -// -// If the parent object has not been opened (or closed before) the function returns the first handle. -// When next_child_handle is 0 all the handles have been listed. -// TODO: traverse by ObjectFormatCode and ObjectHandle association. For now they are unsupported. -mtp_response_t tud_mtp_storage_association_get_object_handle(uint32_t session_handle, uint32_t parent_object_handle, uint32_t *next_child_handle); - // Called with the creation of a new object is requested. // The handle of the new object shall be returned in new_object_handle. // The structure info contains the information to be used for file creation, as passted by the host. // Note that the variable information (e.g. wstring file name, dates and tags shall be retrieved by using the library functions) mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_header_t *info); -// Get object information related to a given object handle -// -// The structure info shall be filled according to MTP specifications. Note that -// in addition to filling the fixed mtp_object_info_t structure, the caller must add the following fields via -// library calls -// - Filename (string, use tud_mtp_gct_append_wstring) -// - Date created (string, use tud_gct_append_date or empty string) -// - Date modified (string, use tud_gct_append_date or empty string) -// - Keywords (string containing list of kw, separated by space, use tud_mtp_gct_append_wstring) -// Note that the variable information (e.g. wstring file name, dates and tags shall be written by using the library functions) -mtp_response_t tud_mtp_storage_object_read_info(uint32_t object_handle, mtp_object_info_header_t *info); - -// Get the object size. -// -// The object may be already open when this function is called. -// The implementation shall not assume a specific call order between this function and tud_mtp_storage_object_read. -// The function may leave the file open. -mtp_response_t tud_mtp_storage_object_size(uint32_t object_handle, uint32_t *size); - // Write object data // // The function shall open the object for writing if not already open. // The binary data shall be written to the file in full before this function is returned. mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t buffer_size); -// Get object data -// -// The function shall open the object for reading if not already open. -// The amount of data returned shall be the given size parameter. -// read_count shall contain the effective number of bytes written. Iteration is terminated when read_count < buffer_size. -mtp_response_t tud_mtp_storage_object_read(uint32_t object_handle, void *buffer, uint32_t buffer_size, uint32_t *read_count); - // Move an object to a new parent mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle); -- cgit v1.3.1 From 7162caee205252f60ca150abc5f67c711094f308 Mon Sep 17 00:00:00 2001 From: Terje Runde Date: Mon, 22 Sep 2025 12:55:18 +0200 Subject: Fix erroneous documentation of TU_ASSERT/TU_VERIFY --- src/common/tusb_verify.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h index 6d02d3572..db91a73d9 100644 --- a/src/common/tusb_verify.h +++ b/src/common/tusb_verify.h @@ -53,11 +53,11 @@ * The difference in behavior is that ASSERT triggers a breakpoint while * verify does not. * - * #define TU_VERIFY(cond) if(cond) return false; - * #define TU_VERIFY(cond,ret) if(cond) return ret; + * #define TU_VERIFY(cond) if (!cond) return false; + * #define TU_VERIFY(cond,ret) if (!cond) return ret; * - * #define TU_ASSERT(cond) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;} - * #define TU_ASSERT(cond,ret) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;} + * #define TU_ASSERT(cond) if (!cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;} + * #define TU_ASSERT(cond,ret) if (!cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;} *------------------------------------------------------------------*/ #ifdef __cplusplus -- cgit v1.3.1 From 1ab45bc52557a85f488566ee89ef48b7332b7ef0 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 23 Sep 2025 09:34:03 +0700 Subject: try to add logo png to mtp example --- examples/device/mtp/src/mtp_fs_example.c | 138 ++++++++++++----------- examples/device/mtp/src/tinyusb_logo_png.h | 174 +++++++++++++++++++++++++++++ examples/device/mtp/src/tusb_config.h | 48 ++++++-- src/class/mtp/mtp.h | 36 ++++-- src/class/mtp/mtp_device.c | 120 +++++++++++--------- 5 files changed, 380 insertions(+), 136 deletions(-) create mode 100644 examples/device/mtp/src/tinyusb_logo_png.h (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index a1c45c6e4..104260373 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -25,6 +25,7 @@ #include "class/mtp/mtp_device_storage.h" #include "tusb.h" +#include "tinyusb_logo_png.h" #define MTPD_STORAGE_DESCRIPTION "storage" #define MTPD_VOLUME_IDENTIFIER "volume" @@ -52,48 +53,55 @@ typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION) //--------------------------------------------------------------------+ // RAM FILESYSTEM //--------------------------------------------------------------------+ -#define FS_MAX_NODES 5UL -#define FS_MAX_NODE_BYTES 128UL -#define FS_MAX_NODE_NAME_LEN 64UL -#define FS_ISODATETIME_LEN 26UL +#define FS_MAX_FILE_COUNT 5UL +#define FS_MAX_CAPACITY_BYTES (2 * 1024UL) +#define FS_MAX_FILENAME_LEN 16UL +#define FS_FIXED_DATETIME "20250808T173500.0" // "YYYYMMDDTHHMMSS.s" + +#define README_TXT_CONTENT "TinyUSB MTP on RAM Filesystem example" typedef struct { - uint32_t handle; + // uint32_t handle; + char name[FS_MAX_FILENAME_LEN]; + mtp_object_formats_t format; uint32_t parent; - uint32_t size; - bool allocated; bool association; - char name[FS_MAX_NODE_NAME_LEN]; - char created[FS_ISODATETIME_LEN]; - char modified[FS_ISODATETIME_LEN]; - uint8_t data[FS_MAX_NODE_BYTES]; + uint32_t size; + uint8_t* data; } fs_object_info_t; -// Sample object file -static fs_object_info_t fs_objects[FS_MAX_NODES] = { +// object data buffer (excluding 2 predefined files) +uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; +uint8_t fs_buf_head = 0; // simple allocation pointer + +// Files system, handle is index + 1 +static fs_object_info_t fs_objects[FS_MAX_FILE_COUNT] = { { - .handle = 1, + .name = "readme.txt", + .format = MTP_OBJ_FORMAT_TEXT, .parent = 0, - .allocated = true, .association = false, - .name = "readme.txt", - .created = "20240104T111134.0", - .modified = "20241214T121110.0", - .data = "USB MTP on RAM Filesystem example\n", - .size = 34 + .data = (uint8_t*) README_TXT_CONTENT, + .size = sizeof(README_TXT_CONTENT) + }, + { + .name = "tinyusb.png", + .format = MTP_OBJ_FORMAT_PNG, + .parent = 0, + .association = false, + .data = logo_bin, + .size = logo_len, } }; //------------- Storage Info -------------// - - storage_info_t storage_info = { .storage_type = MTP_STORAGE_TYPE_FIXED_RAM, .filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL, .access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE, - .max_capacity_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES, - .free_space_in_bytes = FS_MAX_NODES * FS_MAX_NODE_BYTES, - .free_space_in_objects = FS_MAX_NODES, + .max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES, + .free_space_in_bytes = 0, // calculated at runtime + .free_space_in_objects = 0, // calculated at runtime .storage_description = { .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t), .utf16 = STORAGE_DESCRIPTRION @@ -138,32 +146,27 @@ static fs_operation_t _fs_operation = { //--------------------------------------------------------------------+ // Get pointer to object info from handle -fs_object_info_t* fs_object_get_from_handle(uint32_t handle); - -// Get the number of allocated nodes in filesystem -unsigned int fs_get_object_count(void); - -fs_object_info_t* fs_object_get_from_handle(uint32_t handle) { - fs_object_info_t* obj; - for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - obj = &fs_objects[i]; - if (obj->allocated && obj->handle == handle) - return obj; +static inline fs_object_info_t* fs_get_object(uint32_t handle) { + if (handle == 0 || handle > FS_MAX_FILE_COUNT) { + return NULL; } - return NULL; + return &fs_objects[handle-1]; } -unsigned int fs_get_object_count(void) { - unsigned int s = 0; - for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - if (fs_objects[i].allocated) - s++; +// Get the number of allocated nodes in filesystem +uint32_t fs_get_object_count(void) { + uint32_t count = 0; + for (unsigned int i = 0; i < FS_MAX_FILE_COUNT; i++) { + if (fs_objects[i].name[0] != 0) { + count++; + } } - return s; + return count; } int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes) { (void) idx; + (void) cmd_header; resp_block->len = MTP_CONTAINER_HEADER_LENGTH; resp_block->code = (xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; tud_mtp_response_send(resp_block); @@ -225,8 +228,8 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl case MTP_OP_GET_STORAGE_INFO: { TU_VERIFY(SUPPORTED_STORAGE_ID == cmd_block->data[0], -1); // update storage info with current free space - storage_info.free_space_in_objects = FS_MAX_NODES - fs_get_object_count(); - storage_info.free_space_in_bytes = storage_info.free_space_in_objects * FS_MAX_NODE_BYTES; + storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_object_count(); + storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; mtp_container_add_raw(out_block, &storage_info, sizeof(storage_info)); tud_mtp_data_send(out_block); break; @@ -274,12 +277,13 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl return MTP_RESP_INVALID_STORAGE_ID; } - uint32_t handles[FS_MAX_NODES] = { 0 }; + uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; uint32_t count = 0; - for (uint8_t i = 0, h = 0; i < FS_MAX_NODES; i++) { - if (fs_objects[i].allocated && parent_handle == fs_objects[i].parent || - (parent_handle == 0xFFFFFFFF && fs_objects[i].parent == 0)) { - handles[count++] = fs_objects[i].handle; + for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + fs_object_info_t* obj = &fs_objects[i]; + if (obj->name[0] != 0 && + (parent_handle == obj->parent || (parent_handle == 0xFFFFFFFF && obj->parent == 0))) { + handles[count++] = i + 1; // handle is index + 1 } } mtp_container_add_auint32(out_block, count, handles); @@ -289,22 +293,22 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl case MTP_OP_GET_OBJECT_INFO: { const uint32_t obj_handle = cmd_block->data[0]; - fs_object_info_t* obj = fs_object_get_from_handle(obj_handle); + fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { return MTP_RESP_INVALID_OBJECT_HANDLE; } mtp_object_info_header_t obj_info_header = { .storage_id = SUPPORTED_STORAGE_ID, - .object_format = MTP_OBJ_FORMAT_TEXT, + .object_format = obj->format, .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, .object_compressed_size = obj->size, .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, .thumb_compressed_size = 0, .thumb_pix_width = 0, .thumb_pix_height = 0, - .image_pix_width = 0, - .image_pix_height = 0, - .image_bit_depth = 0, + .image_pix_width = 128, + .image_pix_height = 64, + .image_bit_depth = 32, .parent_object = obj->parent, .association_type = MTP_ASSOCIATION_UNDEFINED, .association_desc = 0, @@ -312,8 +316,8 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl }; mtp_container_add_raw(out_block, &obj_info_header, sizeof(obj_info_header)); mtp_container_add_cstring(out_block, obj->name); - mtp_container_add_cstring(out_block, obj->created); - mtp_container_add_cstring(out_block, obj->modified); + mtp_container_add_cstring(out_block, FS_FIXED_DATETIME); + mtp_container_add_cstring(out_block, FS_FIXED_DATETIME); mtp_container_add_cstring(out_block, ""); // keywords, not used tud_mtp_data_send(out_block); @@ -322,7 +326,7 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl case MTP_OP_GET_OBJECT: { const uint32_t obj_handle = cmd_block->data[0]; - fs_object_info_t* obj = fs_object_get_from_handle(obj_handle); + fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { return MTP_RESP_INVALID_OBJECT_HANDLE; } @@ -341,6 +345,7 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl //--------------------------------------------------------------------+ // API //--------------------------------------------------------------------+ +#if 0 mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); @@ -383,7 +388,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // Ensure we are not creating an orphaned object outside root if (parent_object != 0) { - obj = fs_object_get_from_handle(parent_object); + obj = fs_get_object(parent_object); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", parent_object); return MTP_RESP_INVALID_PARENT_OBJECT; @@ -433,7 +438,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { fs_object_info_t* obj; - obj = fs_object_get_from_handle(object_handle); + obj = fs_get_object(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); return MTP_RESP_INVALID_OBJECT_HANDLE; @@ -466,7 +471,7 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ // Ensure we are not moving to an nonexisting parent if (new_parent_object_handle != 0) { - obj = fs_object_get_from_handle(new_parent_object_handle); + obj = fs_get_object(new_parent_object_handle); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); return MTP_RESP_INVALID_PARENT_OBJECT; @@ -477,7 +482,7 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ } } - obj = fs_object_get_from_handle(object_handle); + obj = fs_get_object(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -500,7 +505,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { object_handle = 0; if (object_handle != 0) { - obj = fs_object_get_from_handle(object_handle); + obj = fs_get_object(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -525,6 +530,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { void tud_mtp_storage_object_done(void) { } +#endif void tud_mtp_storage_cancel(void) { fs_object_info_t* obj; @@ -535,9 +541,9 @@ void tud_mtp_storage_cancel(void) { _fs_operation.read_pos = 0; // If write operation is canceled, discard object if (_fs_operation.write_handle) { - obj = fs_object_get_from_handle(_fs_operation.write_handle); - if (obj) - obj->allocated = false; + obj = fs_get_object(_fs_operation.write_handle); + // if (obj) + // obj->allocated = false; } _fs_operation.write_handle = 0; _fs_operation.write_pos = 0; diff --git a/examples/device/mtp/src/tinyusb_logo_png.h b/examples/device/mtp/src/tinyusb_logo_png.h new file mode 100644 index 000000000..d1071c6d3 --- /dev/null +++ b/examples/device/mtp/src/tinyusb_logo_png.h @@ -0,0 +1,174 @@ +const size_t logo_len = 2733; +const uint8_t logo_bin[] __attribute__((aligned(16))) = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0xd2, 0xd6, 0x7f, + 0x7f, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xa0, + 0xbd, 0xa7, 0x93, 0x00, 0x00, 0x0a, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x9c, 0x7d, + 0x54, 0x54, 0x65, 0x1a, 0xc0, 0x7f, 0xc3, 0x00, 0x29, 0x1f, 0x31, 0x88, 0x61, 0x90, 0xd6, 0xa1, + 0xb3, 0xba, 0x1a, 0x18, 0x7e, 0xf2, 0xad, 0x40, 0x82, 0xb8, 0x9b, 0x2b, 0x43, 0x1e, 0xd2, 0x00, + 0x53, 0xc0, 0xea, 0xec, 0xc9, 0x70, 0x18, 0x3d, 0x5b, 0xb1, 0x9e, 0x4a, 0x6d, 0x13, 0x2d, 0xcb, + 0x76, 0xcd, 0x00, 0xf3, 0x8b, 0x10, 0x8a, 0x8e, 0xd6, 0xee, 0xda, 0x8a, 0x96, 0x30, 0x66, 0xa2, + 0xa0, 0x91, 0x64, 0xae, 0x16, 0x98, 0x45, 0x9d, 0x2c, 0x93, 0x8f, 0x05, 0x11, 0x92, 0x81, 0xbb, + 0x7f, 0xa8, 0x93, 0x33, 0x23, 0xcc, 0xdc, 0x61, 0x60, 0x46, 0xb8, 0xbf, 0xbf, 0x78, 0xdf, 0xfb, + 0x3c, 0xcf, 0xfb, 0xc0, 0x7d, 0xee, 0x73, 0xdf, 0xf7, 0xb9, 0xef, 0x8b, 0x8c, 0x6b, 0x08, 0x82, + 0x90, 0x08, 0x2c, 0x03, 0x02, 0x81, 0x21, 0x48, 0x0c, 0x44, 0xda, 0x81, 0x6a, 0xe0, 0x55, 0x99, + 0x4c, 0x56, 0x0c, 0x20, 0x03, 0x10, 0x04, 0x61, 0x35, 0xb0, 0xc2, 0x86, 0x8e, 0x49, 0xf4, 0x3f, + 0x6b, 0x64, 0x32, 0x59, 0x96, 0x4c, 0x10, 0x84, 0x19, 0xc0, 0x47, 0x5c, 0x0b, 0x06, 0x89, 0x41, + 0x45, 0xac, 0x03, 0xa0, 0x42, 0xba, 0xf9, 0x83, 0x15, 0x95, 0x4c, 0x10, 0x84, 0x8b, 0x80, 0x97, + 0xad, 0x3d, 0x91, 0xb0, 0x09, 0x4d, 0x32, 0x41, 0x10, 0x3a, 0x01, 0x07, 0x5b, 0x7b, 0x22, 0x61, + 0x1b, 0x1c, 0x90, 0xd2, 0xff, 0xa0, 0x46, 0x7a, 0xf2, 0x07, 0x39, 0x52, 0x00, 0x0c, 0x72, 0x1c, + 0x2d, 0x55, 0x3c, 0x52, 0x5e, 0x4e, 0x53, 0x63, 0x93, 0x28, 0x9d, 0x49, 0x93, 0x27, 0x31, 0xe2, + 0xce, 0x3b, 0x4d, 0xca, 0x5d, 0xba, 0x74, 0x89, 0xea, 0x13, 0xd5, 0x26, 0xe5, 0x46, 0x8c, 0xf0, + 0xe6, 0x77, 0xa3, 0x47, 0x8b, 0xf2, 0x41, 0x42, 0x1f, 0x99, 0x20, 0x08, 0x5d, 0x58, 0x30, 0x0f, + 0x48, 0x7c, 0x68, 0x2e, 0x9f, 0x57, 0x55, 0x89, 0xd2, 0xd9, 0x94, 0x9b, 0xc3, 0xcc, 0xb8, 0x38, + 0x93, 0x72, 0x27, 0xbf, 0x38, 0x49, 0xc2, 0x9c, 0x39, 0x78, 0x78, 0x78, 0x74, 0x2b, 0xd3, 0xd6, + 0xd6, 0x46, 0xbc, 0x52, 0xc9, 0x9a, 0x75, 0x6b, 0x45, 0xf9, 0x20, 0xa1, 0x8f, 0xc5, 0x19, 0xa0, + 0x3f, 0xa8, 0xfc, 0xec, 0x38, 0x72, 0xc7, 0x9b, 0xbb, 0xb8, 0x22, 0x2b, 0x8b, 0x4e, 0x6d, 0x67, + 0x3f, 0x7b, 0x34, 0xf0, 0x90, 0xe6, 0x00, 0x83, 0x1c, 0xbb, 0x0e, 0x80, 0xb6, 0xb6, 0x36, 0x5e, + 0x5c, 0xb5, 0x8a, 0x0f, 0x76, 0xbf, 0x0f, 0x40, 0xdd, 0x77, 0xdf, 0xf1, 0xe2, 0xaa, 0x55, 0x1c, + 0xfe, 0xf4, 0xb0, 0x8d, 0x3d, 0x1b, 0x38, 0xd8, 0x75, 0x00, 0x68, 0xb5, 0x5a, 0x34, 0xa5, 0x65, + 0x9c, 0x3a, 0x75, 0x0a, 0x80, 0xa6, 0xa6, 0xff, 0xa1, 0x29, 0x2d, 0xa3, 0xae, 0xee, 0x3b, 0x1b, + 0x7b, 0x36, 0x70, 0xb0, 0xeb, 0x39, 0x80, 0xbb, 0xbb, 0x3b, 0x1f, 0x6b, 0xca, 0x74, 0xed, 0xfb, + 0x03, 0xef, 0xd7, 0xb5, 0x57, 0x64, 0x65, 0xd9, 0xca, 0xad, 0x01, 0x85, 0x5d, 0x07, 0x80, 0xbd, + 0x70, 0xb9, 0xf5, 0x32, 0x3f, 0x5f, 0xf8, 0x19, 0xb9, 0x83, 0x03, 0x6e, 0xee, 0xee, 0x28, 0x14, + 0x0a, 0x1c, 0x1c, 0xec, 0x3a, 0x79, 0x9a, 0x8d, 0xd9, 0xcb, 0xc0, 0x8e, 0x8e, 0x0e, 0x56, 0x3e, + 0xff, 0xbc, 0xae, 0xfd, 0xf1, 0xfe, 0x8f, 0xb8, 0x78, 0xf1, 0xa2, 0xa8, 0xc1, 0x22, 0xa6, 0x4d, + 0x63, 0xe4, 0xa8, 0x91, 0x7a, 0x7d, 0xa1, 0x61, 0x61, 0x3c, 0x38, 0x7b, 0xb6, 0xae, 0xfd, 0xf7, + 0x0d, 0x1b, 0x38, 0x73, 0xfa, 0x0c, 0xfb, 0xf7, 0xed, 0xe3, 0xe1, 0xf9, 0xf3, 0xba, 0xfd, 0x43, + 0x1f, 0xab, 0x3c, 0x86, 0xd0, 0xd5, 0x45, 0x50, 0x48, 0x30, 0x69, 0x8b, 0x17, 0x73, 0xef, 0xbd, + 0xf7, 0xea, 0xae, 0x7d, 0xb4, 0x7f, 0x3f, 0x07, 0x35, 0x1a, 0x51, 0xbe, 0xad, 0x5c, 0xbd, 0x1a, + 0xb9, 0x5c, 0x0e, 0x5c, 0x9d, 0x6b, 0x94, 0xec, 0x2d, 0xe1, 0xa0, 0x46, 0x43, 0xf5, 0x89, 0x13, + 0xb4, 0xb7, 0xb7, 0xeb, 0xc9, 0xba, 0xb9, 0xb9, 0xe1, 0x1f, 0x10, 0x40, 0x4c, 0x6c, 0x0c, 0xf1, + 0x09, 0x09, 0x0c, 0x1b, 0x36, 0x4c, 0x77, 0xed, 0xc7, 0x1f, 0x7f, 0x64, 0xd3, 0xc6, 0x8d, 0xa2, + 0xc6, 0x06, 0x70, 0x90, 0x39, 0xf0, 0xec, 0x5f, 0xb3, 0x18, 0xea, 0xe2, 0x22, 0x4a, 0xef, 0xdd, + 0xa2, 0x77, 0x38, 0x79, 0xf2, 0x0b, 0xbd, 0xbe, 0xe4, 0x94, 0x14, 0xc6, 0xdd, 0x77, 0x9f, 0x59, + 0xfa, 0x66, 0x07, 0x40, 0x5b, 0x5b, 0x1b, 0xe3, 0xc7, 0x99, 0x67, 0x54, 0x0c, 0x8b, 0xd2, 0x52, + 0x59, 0xf1, 0xdc, 0x73, 0xba, 0x76, 0x5c, 0x4c, 0x2c, 0x67, 0x6b, 0x6b, 0x45, 0xd9, 0x28, 0x28, + 0x2a, 0x24, 0x24, 0x34, 0x54, 0xd7, 0x5e, 0xff, 0xf2, 0x2b, 0xbc, 0xf9, 0xc6, 0x1b, 0xa2, 0x6c, + 0x9c, 0xa9, 0xad, 0xa1, 0xa1, 0xa1, 0x81, 0x7f, 0x6c, 0x78, 0x9d, 0xe2, 0xe2, 0x77, 0xcd, 0x5e, + 0x62, 0x0e, 0x19, 0x32, 0x84, 0xa5, 0x99, 0x99, 0xa4, 0xa5, 0xa7, 0xe9, 0x96, 0xac, 0x73, 0xe3, + 0x95, 0x54, 0x57, 0x9b, 0x2e, 0x64, 0x19, 0xf2, 0xf4, 0xb3, 0xcf, 0xf2, 0xd8, 0x13, 0x8f, 0x9b, + 0x2d, 0xdf, 0xdc, 0xdc, 0x4c, 0x44, 0x68, 0x28, 0x97, 0x5b, 0x2f, 0xeb, 0xfa, 0x14, 0x0a, 0x05, + 0x87, 0xca, 0x0f, 0x9b, 0x1d, 0x48, 0x03, 0x23, 0x8f, 0x59, 0x81, 0xea, 0x13, 0x27, 0x98, 0x3d, + 0xeb, 0x0f, 0x14, 0x15, 0x16, 0x8a, 0xaa, 0x2f, 0xb4, 0xb7, 0xb7, 0xb3, 0x76, 0xcd, 0x1a, 0x32, + 0x96, 0x3c, 0x45, 0xa7, 0x56, 0x0b, 0xc0, 0x53, 0xaa, 0xa5, 0x16, 0xf9, 0x90, 0x97, 0x9b, 0xa3, + 0x77, 0x33, 0x4d, 0x51, 0xb4, 0xb3, 0xd0, 0x48, 0x3e, 0xe5, 0xd1, 0x05, 0xa2, 0xb2, 0x88, 0x14, + 0x00, 0xd7, 0x78, 0xe2, 0xb1, 0xc7, 0x69, 0x68, 0x68, 0xb0, 0x58, 0x7f, 0x5f, 0x49, 0x09, 0x6b, + 0xb3, 0xaf, 0x56, 0x25, 0xa3, 0xa2, 0xa3, 0x09, 0x9c, 0x30, 0x41, 0xb4, 0x8d, 0xc6, 0x86, 0x46, + 0x76, 0x16, 0x14, 0x98, 0x25, 0xab, 0xd5, 0x6a, 0x29, 0xc8, 0xcf, 0xd7, 0xeb, 0x73, 0x72, 0x72, + 0x22, 0x29, 0x25, 0x45, 0xd4, 0x98, 0x66, 0x4f, 0x02, 0x9d, 0x9d, 0x9d, 0xd9, 0x71, 0x83, 0x73, + 0xab, 0x57, 0xae, 0xa4, 0xb6, 0xa6, 0x46, 0xd4, 0x60, 0x19, 0x2a, 0x15, 0x93, 0xa7, 0x4c, 0xd1, + 0xeb, 0xf3, 0xf1, 0xf5, 0x11, 0x65, 0xa3, 0xaf, 0x68, 0x6a, 0x6c, 0xec, 0xb5, 0x8d, 0x1d, 0xdb, + 0xb7, 0x91, 0xf8, 0x70, 0x22, 0xa3, 0xc7, 0x8c, 0x61, 0x69, 0xa6, 0x8a, 0xb4, 0x85, 0x8b, 0x44, + 0xdb, 0xc8, 0xcb, 0xcd, 0x21, 0x39, 0x25, 0x05, 0x17, 0xd7, 0x9e, 0x9f, 0xe2, 0x0f, 0xf7, 0xec, + 0xe1, 0xfc, 0xf9, 0xf3, 0x7a, 0x7d, 0x73, 0xe2, 0xe3, 0xf1, 0xf6, 0xf6, 0x16, 0x35, 0x9e, 0xd9, + 0x01, 0x20, 0x97, 0xcb, 0x09, 0x8f, 0x08, 0xd7, 0xb5, 0xdd, 0xdd, 0xdd, 0x45, 0x0d, 0x04, 0x30, + 0x76, 0xdc, 0x58, 0x3d, 0x1b, 0xf6, 0xc4, 0x5d, 0x77, 0xdd, 0x45, 0x70, 0x48, 0x08, 0x23, 0x47, + 0x8d, 0xa4, 0xab, 0xab, 0x8b, 0xe3, 0xc7, 0x8e, 0x73, 0xf4, 0xc8, 0x11, 0x51, 0x36, 0x3a, 0xb5, + 0x9d, 0x14, 0x15, 0x16, 0xf2, 0xdc, 0x0b, 0x2f, 0x30, 0x3d, 0x32, 0x92, 0x29, 0x53, 0xa7, 0x70, + 0xfc, 0xd8, 0x71, 0x51, 0x36, 0xae, 0x67, 0x01, 0x53, 0x73, 0x81, 0xfc, 0x6d, 0xdb, 0x8d, 0xfa, + 0x16, 0xa5, 0xa5, 0x8a, 0x1a, 0x0b, 0xa4, 0x57, 0x80, 0x8e, 0x98, 0x99, 0xb1, 0xac, 0x5b, 0xff, + 0x0a, 0x19, 0x2a, 0x15, 0x2a, 0xb5, 0x9a, 0x82, 0xa2, 0x42, 0xfe, 0xfc, 0xe4, 0x93, 0xa2, 0xed, + 0x94, 0x95, 0xfe, 0x56, 0xb7, 0x58, 0x92, 0x91, 0x61, 0x91, 0x2f, 0xa6, 0xe6, 0x02, 0x95, 0x15, + 0x15, 0x46, 0x93, 0xcc, 0xb0, 0xf0, 0x70, 0xb3, 0x67, 0xfe, 0x37, 0x22, 0x05, 0x40, 0x0f, 0x2c, + 0x4a, 0x4b, 0xd5, 0x2d, 0x0d, 0xcd, 0xe5, 0xfb, 0xba, 0x3a, 0x9a, 0x9a, 0xae, 0x7e, 0x26, 0x8f, + 0x98, 0x36, 0x8d, 0xa9, 0x41, 0x53, 0x45, 0x8f, 0x6b, 0x6a, 0x2e, 0xb0, 0xe5, 0xad, 0xb7, 0x8c, + 0xfa, 0x52, 0xd3, 0xd3, 0x44, 0x8f, 0x03, 0x52, 0x00, 0xf4, 0x88, 0x97, 0x97, 0x17, 0x0a, 0x85, + 0x42, 0xb4, 0x5e, 0x7d, 0x7d, 0xbd, 0xee, 0xe7, 0x25, 0x19, 0xd6, 0x5d, 0x11, 0x7c, 0x7b, 0xee, + 0x5b, 0xca, 0x0e, 0x94, 0xea, 0xf5, 0xf9, 0xf9, 0xf9, 0x11, 0x19, 0x15, 0x65, 0xd1, 0x38, 0x76, + 0x1b, 0x00, 0x81, 0x81, 0x81, 0x6c, 0xdc, 0xb4, 0x89, 0xdc, 0xcd, 0x9b, 0x89, 0x57, 0x2a, 0x01, + 0x58, 0x98, 0xba, 0x88, 0x3d, 0x25, 0x7b, 0xd9, 0xf5, 0xcf, 0x0f, 0x48, 0x4d, 0x4b, 0x43, 0x26, + 0xeb, 0xfb, 0xed, 0x8c, 0xb7, 0xdf, 0x7e, 0xbb, 0x68, 0x9d, 0xc6, 0x86, 0xdf, 0x26, 0x94, 0xe1, + 0x11, 0xe1, 0x4c, 0x0d, 0x0a, 0xb2, 0xc8, 0xc6, 0xcd, 0xb2, 0xc0, 0xb6, 0xad, 0x5b, 0xe9, 0xea, + 0xea, 0xd2, 0xeb, 0x4b, 0x7b, 0x6c, 0xb1, 0xc5, 0x95, 0x49, 0xbb, 0x2c, 0x05, 0xfb, 0xfa, 0xfa, + 0x52, 0x50, 0x54, 0xa8, 0x5b, 0xcf, 0xce, 0x88, 0x8d, 0x61, 0x7a, 0x64, 0x24, 0xf1, 0x09, 0x4a, + 0x9d, 0x4c, 0x60, 0x60, 0x20, 0x1d, 0x37, 0x59, 0x0a, 0x59, 0x1b, 0x4b, 0x82, 0xac, 0xab, 0x4b, + 0xbf, 0x8e, 0x90, 0xa1, 0x5a, 0xca, 0x82, 0xa4, 0x64, 0xd1, 0x76, 0x0c, 0x57, 0x04, 0x4d, 0x4d, + 0x4d, 0xec, 0xde, 0xb5, 0x4b, 0x4f, 0x46, 0xa1, 0x50, 0xa0, 0x54, 0x2a, 0x6f, 0xa6, 0x6e, 0x16, + 0x76, 0x99, 0x01, 0xe6, 0x28, 0x95, 0x0c, 0x75, 0x71, 0x61, 0x5d, 0x76, 0x36, 0xe1, 0x21, 0xa1, + 0xe4, 0x6f, 0xdf, 0x41, 0x7c, 0x82, 0x92, 0xa3, 0x47, 0x8e, 0x70, 0xbf, 0xbf, 0x3f, 0x33, 0x1f, + 0x98, 0x41, 0x4b, 0x4b, 0x0b, 0xf3, 0x93, 0x1e, 0xb1, 0xb5, 0xab, 0x66, 0x11, 0x1a, 0x16, 0x46, + 0x50, 0x70, 0xb0, 0x68, 0x3d, 0xc3, 0x2c, 0x50, 0x58, 0x50, 0x40, 0xdb, 0x65, 0xfd, 0xd7, 0x42, + 0xf2, 0x82, 0x14, 0xd1, 0xe5, 0xe3, 0x1b, 0xb1, 0xcb, 0x00, 0x18, 0x35, 0x6a, 0x14, 0x70, 0x75, + 0x69, 0xf6, 0xe8, 0xc2, 0x85, 0xb8, 0xb9, 0xbb, 0x01, 0x57, 0x9f, 0xc6, 0x27, 0x97, 0x3c, 0xc5, + 0xdc, 0xc4, 0x44, 0x5a, 0x2f, 0xb5, 0x72, 0xcf, 0xdd, 0x77, 0xdb, 0xd2, 0x4d, 0x51, 0xa8, 0x97, + 0x2f, 0xb7, 0x48, 0xef, 0xfa, 0x5c, 0x40, 0xab, 0xd5, 0x52, 0xb8, 0x73, 0xa7, 0xde, 0x35, 0x27, + 0x27, 0x27, 0x92, 0x17, 0x2c, 0xe8, 0x95, 0x5f, 0x76, 0xf9, 0x0a, 0x70, 0x76, 0x76, 0x06, 0x30, + 0xfa, 0xe5, 0x82, 0x43, 0x42, 0x08, 0x0e, 0x09, 0xd1, 0xb5, 0xaf, 0x97, 0x5e, 0x6f, 0x05, 0xa6, + 0x4c, 0x9d, 0x42, 0x70, 0x48, 0x08, 0x15, 0x47, 0x8f, 0x8a, 0xd2, 0xbb, 0x9e, 0x05, 0xbc, 0x86, + 0x7b, 0xf1, 0xd3, 0xf9, 0x9f, 0xf4, 0xae, 0x59, 0x52, 0xf8, 0x31, 0xc4, 0x2e, 0x03, 0xa0, 0x20, + 0x3f, 0x9f, 0xd2, 0xd2, 0x03, 0x26, 0xe5, 0x04, 0x41, 0xe8, 0x07, 0x6f, 0xac, 0x87, 0x4a, 0x9d, + 0xc9, 0x23, 0x0f, 0xcf, 0x13, 0xad, 0xf7, 0x56, 0x5e, 0x1e, 0xc3, 0xef, 0x18, 0x6e, 0xd4, 0x6f, + 0x49, 0xe1, 0xc7, 0x10, 0xbb, 0x0c, 0x80, 0xea, 0xea, 0xea, 0x9b, 0x7e, 0x4d, 0xf3, 0xf0, 0xf0, + 0x20, 0x2a, 0x3a, 0x9a, 0xdb, 0x86, 0xdc, 0x46, 0xd5, 0x67, 0x55, 0xa2, 0x4b, 0xd1, 0xb6, 0x66, + 0x6a, 0x50, 0x10, 0xa1, 0x61, 0x61, 0x1c, 0x29, 0x2f, 0x17, 0xa5, 0x57, 0x5f, 0x5f, 0xaf, 0xb7, + 0xb4, 0x04, 0xcb, 0x0b, 0x3f, 0x86, 0xd8, 0xe5, 0x1c, 0xe0, 0x99, 0xac, 0x2c, 0x6a, 0xbf, 0x3d, + 0x47, 0x4c, 0x6c, 0xac, 0xae, 0x2f, 0x6e, 0xd6, 0x2c, 0x0e, 0x68, 0xca, 0x58, 0xbf, 0xe1, 0x35, + 0x5e, 0xca, 0xce, 0xe6, 0xc3, 0x92, 0xbd, 0x24, 0xce, 0x13, 0xff, 0x34, 0xd9, 0x9a, 0xcc, 0x65, + 0x6a, 0xab, 0xd8, 0xb1, 0xb4, 0xf0, 0x63, 0x88, 0x5d, 0x06, 0xc0, 0xdd, 0xf7, 0x5c, 0x9d, 0xdc, + 0xd5, 0xd6, 0xd4, 0x20, 0x93, 0xc9, 0xc8, 0x50, 0xa9, 0xd8, 0xf8, 0xe6, 0x26, 0x14, 0x9e, 0x9e, + 0x1c, 0xd4, 0x68, 0x78, 0x79, 0xed, 0x5a, 0xae, 0x5c, 0xb9, 0xc2, 0x5f, 0x9e, 0x7e, 0xda, 0xc6, + 0x9e, 0x8a, 0x67, 0xd2, 0xe4, 0xc9, 0xbd, 0xfe, 0x1e, 0xd2, 0x9b, 0xc2, 0x8f, 0x21, 0x76, 0xf9, + 0x0a, 0xa8, 0xad, 0xa9, 0x61, 0x66, 0x5c, 0x1c, 0x1b, 0x73, 0xde, 0xa4, 0x53, 0xab, 0xc5, 0x3f, + 0x20, 0x00, 0xad, 0x56, 0x4b, 0xf6, 0x4b, 0x2f, 0xb1, 0x7d, 0xeb, 0x36, 0x00, 0x66, 0xc4, 0xc4, + 0x30, 0x61, 0xe2, 0x44, 0x9c, 0x9c, 0x9c, 0x6c, 0xec, 0xad, 0x78, 0x54, 0x6a, 0x75, 0xaf, 0x76, + 0x36, 0xa7, 0x2e, 0x4e, 0xb7, 0xda, 0x96, 0x34, 0xbb, 0xcc, 0x00, 0x9b, 0x73, 0xf3, 0xa8, 0xac, + 0xa8, 0x60, 0xec, 0xd8, 0xb1, 0xf8, 0x07, 0x04, 0xf0, 0x7d, 0x5d, 0x1d, 0x49, 0xf3, 0xe6, 0xeb, + 0x6e, 0xbe, 0xe7, 0x30, 0x4f, 0xc6, 0x8d, 0x1b, 0x47, 0xcd, 0xd7, 0x35, 0x74, 0x74, 0x74, 0xd8, + 0xd8, 0x5b, 0xf1, 0x4c, 0x9c, 0x34, 0x89, 0x88, 0x69, 0xd3, 0x2c, 0xd2, 0x55, 0x28, 0x14, 0x24, + 0x24, 0x24, 0x58, 0xcd, 0x17, 0x8b, 0x33, 0x80, 0x25, 0x25, 0x52, 0x73, 0x69, 0x69, 0x69, 0x21, + 0x69, 0xde, 0x7c, 0x7c, 0x7c, 0x7c, 0x70, 0x75, 0x73, 0xe3, 0xdc, 0x37, 0xdf, 0xd0, 0xd9, 0xf9, + 0x5b, 0x75, 0xad, 0xa5, 0xb9, 0x85, 0x88, 0xd0, 0x30, 0xae, 0xdc, 0x82, 0x37, 0xff, 0x3a, 0x2a, + 0x75, 0x26, 0x9f, 0x1e, 0x3a, 0x24, 0x5a, 0xaf, 0xb7, 0x85, 0x1f, 0x43, 0x2c, 0x0e, 0x80, 0x61, + 0x5e, 0xc3, 0x4c, 0x0b, 0x19, 0xf0, 0xeb, 0xaf, 0xbf, 0x9a, 0x25, 0xf7, 0xc7, 0xd9, 0x0f, 0xe2, + 0xef, 0x1f, 0xd0, 0xed, 0xf5, 0x96, 0xe6, 0x66, 0xaa, 0xaa, 0xaa, 0xa8, 0xac, 0xa8, 0x10, 0xed, + 0x83, 0xbd, 0x30, 0x61, 0xe2, 0x44, 0xa6, 0x47, 0x46, 0xf2, 0xc9, 0xc1, 0x83, 0x66, 0xeb, 0x38, + 0x3a, 0x3a, 0x92, 0x94, 0x2c, 0xbe, 0xa4, 0xdc, 0xa3, 0x4d, 0x4b, 0x15, 0xc7, 0x8c, 0xf9, 0xbd, + 0x68, 0x1d, 0xc3, 0x42, 0x46, 0x77, 0x44, 0x47, 0x3f, 0x40, 0xc2, 0xdc, 0x87, 0x4c, 0xca, 0xe5, + 0xe5, 0xe4, 0xb2, 0x2e, 0x3b, 0x5b, 0xb4, 0x1f, 0xf6, 0x82, 0x7a, 0xf9, 0x32, 0x0e, 0x7d, 0xf2, + 0x89, 0xd9, 0xf5, 0x8c, 0x39, 0xf1, 0xf1, 0x66, 0x9d, 0xae, 0x16, 0x83, 0xc5, 0x01, 0x10, 0x33, + 0x33, 0x96, 0xb5, 0x6b, 0xd6, 0x88, 0xd2, 0x29, 0x7e, 0xe7, 0x1d, 0x62, 0x66, 0xc6, 0xe2, 0xe7, + 0xe7, 0xd7, 0xa3, 0x5c, 0x5e, 0x6e, 0x2e, 0x1f, 0xbc, 0xbf, 0xbb, 0xdb, 0xeb, 0x1e, 0x0a, 0x05, + 0xea, 0x65, 0xcb, 0x49, 0x5f, 0x9c, 0xce, 0xeb, 0xaf, 0xbd, 0x26, 0xca, 0x07, 0x7b, 0x22, 0x60, + 0xfc, 0x78, 0x22, 0xa3, 0xa2, 0xd0, 0x94, 0x95, 0x99, 0x16, 0xc6, 0x7a, 0x4b, 0xbf, 0x1b, 0xb1, + 0x38, 0x00, 0xfc, 0xfc, 0xfc, 0x88, 0x9b, 0x35, 0x8b, 0x7d, 0x25, 0x25, 0x66, 0xeb, 0x9c, 0x3b, + 0x77, 0x8e, 0xb8, 0x19, 0x31, 0x78, 0x8f, 0xf0, 0xc6, 0xd5, 0xc5, 0x95, 0xfa, 0x86, 0x06, 0x94, + 0x09, 0x4a, 0xbd, 0x6d, 0xe1, 0x00, 0x0d, 0xf5, 0xf5, 0x46, 0x7b, 0xf1, 0x6f, 0x64, 0xc8, 0x85, + 0x5f, 0x68, 0x6e, 0x6e, 0xc6, 0x41, 0x2e, 0xc7, 0xb1, 0x9b, 0xd3, 0xc3, 0xb7, 0x0a, 0x2a, 0x75, + 0x26, 0x07, 0x35, 0x1a, 0x93, 0x59, 0xc0, 0x5a, 0x85, 0x1f, 0x43, 0x7a, 0xf5, 0xd7, 0x7b, 0x7e, + 0xe5, 0x0b, 0x9c, 0xfa, 0xf2, 0x4b, 0x7e, 0xf8, 0xe1, 0x07, 0xb3, 0x75, 0xba, 0xba, 0xba, 0x4c, + 0xbe, 0x0a, 0x9e, 0xc9, 0xca, 0x32, 0xeb, 0x15, 0xb0, 0xf7, 0xc3, 0xff, 0xd0, 0xda, 0xda, 0x6a, + 0xf6, 0xd8, 0xf6, 0x48, 0xc0, 0xf8, 0xf1, 0x44, 0x45, 0x47, 0x53, 0x56, 0x5a, 0xda, 0xa3, 0x5c, + 0x5f, 0x3c, 0xfd, 0xd0, 0xcb, 0x00, 0xf0, 0x1e, 0x31, 0x82, 0xe2, 0xdd, 0xbb, 0x58, 0x9e, 0xa9, + 0xa6, 0xfc, 0xb0, 0xf5, 0x4e, 0xec, 0xd6, 0xd4, 0x7c, 0xdd, 0xe3, 0x3a, 0xf9, 0xe2, 0xc5, 0x5f, + 0xf8, 0xec, 0xd8, 0x71, 0xde, 0x2b, 0x2e, 0xb6, 0xda, 0x98, 0xb6, 0x44, 0xa5, 0xce, 0x44, 0x53, + 0x56, 0xd6, 0x6d, 0x16, 0xb0, 0x66, 0xe1, 0xc7, 0x90, 0x5e, 0xe7, 0x4f, 0x6f, 0x6f, 0x6f, 0xf2, + 0x77, 0x16, 0x70, 0xac, 0xb2, 0x92, 0x3d, 0xff, 0xfa, 0x37, 0xe5, 0x87, 0x0f, 0x53, 0xf7, 0x7d, + 0x5d, 0x8f, 0x87, 0x2b, 0xe4, 0x72, 0x39, 0xc3, 0xef, 0xb8, 0x03, 0x1f, 0x1f, 0x1f, 0x46, 0x8f, + 0x1e, 0x63, 0x74, 0x3d, 0x2f, 0x27, 0x97, 0xbc, 0x9c, 0xdc, 0xde, 0xba, 0x76, 0xcb, 0xe0, 0x1f, + 0x10, 0x40, 0xf4, 0x8c, 0x07, 0x28, 0xfd, 0xf8, 0xe6, 0x1f, 0xc0, 0xac, 0x59, 0xf8, 0x31, 0xc4, + 0xe2, 0x7f, 0x11, 0xd3, 0x13, 0x9d, 0x5a, 0x2d, 0x3f, 0x5f, 0xb8, 0x40, 0x73, 0x73, 0x33, 0xda, + 0x6b, 0x6b, 0xf5, 0xa1, 0x2e, 0x2e, 0x38, 0x3b, 0x39, 0x31, 0xd4, 0xc5, 0x05, 0x4f, 0x4f, 0x4f, + 0xd1, 0x9b, 0x2d, 0x07, 0x3a, 0xff, 0x3d, 0x75, 0x8a, 0xf8, 0xd9, 0x7f, 0x32, 0xca, 0x02, 0x62, + 0x8f, 0x7a, 0x89, 0xa5, 0x4f, 0x66, 0x50, 0x72, 0x47, 0x47, 0x7c, 0x7d, 0x7d, 0xf1, 0xf5, 0xf5, + 0xed, 0x0b, 0xf3, 0x03, 0x92, 0xfb, 0xfc, 0xfd, 0x09, 0x09, 0x0d, 0x35, 0xfa, 0x52, 0xf8, 0x48, + 0x72, 0x72, 0x9f, 0xdd, 0x7c, 0xb0, 0xd3, 0x52, 0xf0, 0x60, 0xa4, 0xad, 0xad, 0x8d, 0xd3, 0xa7, + 0x4f, 0xeb, 0xf5, 0x39, 0x3a, 0x3a, 0x92, 0x2c, 0xf2, 0xa8, 0x97, 0x58, 0xa4, 0x00, 0xb0, 0x13, + 0x76, 0xbd, 0xf7, 0x9e, 0xd1, 0xf1, 0xb4, 0x39, 0xf1, 0xf1, 0xdc, 0xe9, 0x63, 0xdd, 0xc2, 0x8f, + 0x21, 0x52, 0x00, 0xd8, 0x01, 0x82, 0x20, 0xf0, 0xf6, 0x0e, 0xe3, 0xdd, 0xcd, 0x7d, 0xb5, 0xf4, + 0xbb, 0x11, 0x29, 0x00, 0xec, 0x80, 0xb2, 0x03, 0xa5, 0x9c, 0x3d, 0x7b, 0x56, 0xaf, 0x2f, 0x34, + 0x2c, 0xac, 0x4f, 0x0a, 0x3f, 0x86, 0x48, 0x01, 0x60, 0x07, 0x6c, 0xdd, 0xb2, 0xc5, 0xa8, 0x2f, + 0x35, 0x3d, 0xbd, 0x5f, 0xc6, 0x96, 0x02, 0xc0, 0xc6, 0x7c, 0x75, 0xe6, 0x2b, 0xa3, 0x9d, 0xc2, + 0x7e, 0x7e, 0x7e, 0x44, 0x45, 0x47, 0xf5, 0xcb, 0xf8, 0x52, 0x00, 0xd8, 0x98, 0x2d, 0x9b, 0x37, + 0x1b, 0xad, 0xfd, 0xfb, 0xb2, 0xf0, 0x63, 0x48, 0x9f, 0x14, 0x82, 0x24, 0xcc, 0xa3, 0xbe, 0xbe, + 0x9e, 0xe9, 0x61, 0xe1, 0x7a, 0xfb, 0x24, 0xfa, 0xba, 0xf0, 0x63, 0x88, 0x94, 0x01, 0x6c, 0xc8, + 0xdb, 0x3b, 0x76, 0x18, 0x6d, 0x92, 0x49, 0x4a, 0xb1, 0xee, 0x8e, 0x1f, 0x53, 0x48, 0x19, 0xc0, + 0x86, 0xb4, 0xb6, 0xb6, 0xa2, 0x35, 0x38, 0xdd, 0xe4, 0xea, 0xea, 0xda, 0xaf, 0x9f, 0xb8, 0xa5, + 0x00, 0x18, 0xe4, 0x38, 0x00, 0xb7, 0xd6, 0xf9, 0x2a, 0x09, 0xab, 0xe2, 0x00, 0xf4, 0xfe, 0xdf, + 0x63, 0x49, 0xdc, 0xaa, 0x34, 0x3a, 0x00, 0xe2, 0x0e, 0xaa, 0x49, 0x0c, 0x24, 0xca, 0x65, 0x82, + 0x20, 0x44, 0x03, 0x07, 0x90, 0xe6, 0x01, 0x83, 0x0d, 0x01, 0x88, 0x75, 0x90, 0xc9, 0x64, 0x65, + 0xc0, 0x8b, 0xb6, 0xf6, 0x46, 0xa2, 0xdf, 0xf9, 0x9b, 0x4c, 0x26, 0x3b, 0xa0, 0x7b, 0xea, 0x05, + 0x41, 0x98, 0x0b, 0xa8, 0x81, 0x89, 0xc0, 0x50, 0x9b, 0xb9, 0x25, 0xd1, 0x97, 0xb4, 0x03, 0x9f, + 0x03, 0xeb, 0x65, 0x32, 0xd9, 0x2e, 0x80, 0xff, 0x03, 0xff, 0x08, 0x81, 0xdd, 0xa8, 0xcb, 0xf5, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index 251a99fd3..7e2c5b670 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -92,18 +92,52 @@ //------------- CLASS -------------// #define CFG_TUD_MTP 1 - -#define CFG_TUD_MANUFACTURER "TinyUsb Manufacturer" -#define CFG_TUD_MODEL "TinyUsb Device" - #define CFG_MTP_EP_SIZE 64 #define CFG_MTP_EVT_EP_SIZE 64 #define CFG_MTP_EVT_INTERVAL 100 -#define CFG_MTP_DEVICE_VERSION "1.0" -#define CFG_MTP_SERIAL_NUMBER "0" +//------------- MTP device info -------------// +#define CFG_TUD_MTP_DEVICEINFO_EXTENSIONS "microsoft.com: 1.0; " +#define CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS \ + MTP_OP_GET_DEVICE_INFO, \ + MTP_OP_OPEN_SESSION, \ + MTP_OP_CLOSE_SESSION, \ + MTP_OP_GET_STORAGE_IDS, \ + MTP_OP_GET_STORAGE_INFO, \ + MTP_OP_GET_NUM_OBJECTS, \ + MTP_OP_GET_OBJECT_HANDLES, \ + MTP_OP_GET_OBJECT_INFO, \ + MTP_OP_GET_OBJECT, \ + MTP_OP_DELETE_OBJECT, \ + MTP_OP_SEND_OBJECT_INFO, \ + MTP_OP_SEND_OBJECT, \ + MTP_OP_FORMAT_STORE, \ + MTP_OP_RESET_DEVICE, \ + MTP_OP_GET_DEVICE_PROP_DESC, \ + MTP_OP_GET_DEVICE_PROP_VALUE, \ + MTP_OP_SET_DEVICE_PROP_VALUE + +#define CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS \ + MTP_EVENT_OBJECT_ADDED + +#define CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES \ + MTP_DEV_PROP_DEVICE_FRIENDLY_NAME + +#define CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS \ + MTP_OBJ_FORMAT_UNDEFINED, \ + MTP_OBJ_FORMAT_ASSOCIATION, \ + MTP_OBJ_FORMAT_TEXT, \ + MTP_OBJ_FORMAT_PNG + +#define CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS \ + MTP_OBJ_FORMAT_UNDEFINED, \ + MTP_OBJ_FORMAT_ASSOCIATION, \ + MTP_OBJ_FORMAT_TEXT, \ + MTP_OBJ_FORMAT_PNG + +#define CFG_TUD_MANUFACTURER "TinyUsb Manufacturer" +#define CFG_TUD_MODEL "TinyUsb Device" #define CFG_MTP_INTERFACE (CFG_TUD_MODEL " MTP") -#define CFG_MTP_STORAGE_ID_COUNT 1 #ifdef __cplusplus } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index c844b08ab..c44b99fda 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -470,7 +470,7 @@ typedef enum { MTP_OP_DELETE_OBJECT = 0x100Bu, MTP_OP_SEND_OBJECT_INFO = 0x100Cu, MTP_OP_SEND_OBJECT = 0x100Du, - MTP_OP_INITIAL_CAPTURE = 0x100Eu, + MTP_OP_INITIATE_CAPTURE = 0x100Eu, MTP_OP_FORMAT_STORE = 0x100Fu, MTP_OP_RESET_DEVICE = 0x1010u, MTP_OP_SELF_TEST = 0x1011u, @@ -734,6 +734,10 @@ typedef struct TU_ATTR_PACKED { uint16_t association_type; uint32_t association_desc; uint32_t sequence_number; + // mtp_string_t() filename + // mtp_string_t() date_created + // mtp_string_t() date_modified + // mtp_string_t() keywords } mtp_object_info_header_t; // Device property desc up to get/set @@ -775,12 +779,15 @@ typedef struct TU_ATTR_PACKED { //--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_generic_container_t* p_container, const void* data, uint32_t len) { + TU_ASSERT(p_container->len + len < sizeof(mtp_generic_container_t), 0); memcpy((uint8_t*) p_container + p_container->len, data, len); p_container->len += len; return len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { + const uint32_t added_len = 4 + count * scalar_size; + TU_ASSERT(p_container->len + added_len < sizeof(mtp_generic_container_t), 0); uint8_t* container8 = (uint8_t*)p_container; tu_unaligned_write32(container8 + p_container->len, count); @@ -789,32 +796,43 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_generic memcpy(container8 + p_container->len, data, count * scalar_size); p_container->len += count * scalar_size; - return 4 + count * scalar_size; + return added_len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { + const uint32_t added_len = 1 + 2 * count; + TU_ASSERT(p_container->len + added_len < sizeof(mtp_generic_container_t), 0); uint8_t* container8 = (uint8_t*) p_container; + container8[p_container->len] = count; p_container->len++; memcpy(container8 + p_container->len, utf16, 2 * count); p_container->len += 2 * count; - return 1 + 2 * count; + return added_len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_generic_container_t* p_container, const char* str) { - uint8_t* container8 = (uint8_t*) p_container; const uint8_t len = (uint8_t) (strlen(str) + 1); // include null + TU_ASSERT(p_container->len + 1 + 2 * len < sizeof(mtp_generic_container_t), 0); + + uint8_t* container8 = (uint8_t*) p_container; container8[p_container->len] = len; p_container->len++; - for (uint8_t i = 0; i < len; i++) { - container8[p_container->len] = str[i]; - container8[p_container->len + 1] = 0; - p_container->len += 2; + if (len == 1) { + // empty string (null only) + container8[p_container->len] = 0; + return 1; + } else { + for (uint8_t i = 0; i < len; i++) { + container8[p_container->len] = str[i]; + container8[p_container->len + 1] = 0; + p_container->len += 2; + } + return 1 + 2 * len; } - return 1 + 2 * len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_generic_container_t* p_container, uint8_t data) { diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index ca594158e..91df13111 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -102,6 +102,65 @@ CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static char _mtp_datestr[20]; +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL + +TU_ATTR_UNUSED static tu_lookup_entry_t const _mpt_op_lookup[] = { +{.key = MTP_OP_UNDEFINED , .data = "Undefined" } , +{.key = MTP_OP_GET_DEVICE_INFO , .data = "GetDeviceInfo" } , +{.key = MTP_OP_OPEN_SESSION , .data = "OpenSession" } , +{.key = MTP_OP_CLOSE_SESSION , .data = "CloseSession" } , +{.key = MTP_OP_GET_STORAGE_IDS , .data = "GetStorageIDs" } , +{.key = MTP_OP_GET_STORAGE_INFO , .data = "GetStorageInfo" } , +{.key = MTP_OP_GET_NUM_OBJECTS , .data = "GetNumObjects" } , +{.key = MTP_OP_GET_OBJECT_HANDLES , .data = "GetObjectHandles" } , +{.key = MTP_OP_GET_OBJECT_INFO , .data = "GetObjectInfo" } , +{.key = MTP_OP_GET_OBJECT , .data = "GetObject" } , +{.key = MTP_OP_GET_THUMB , .data = "GetThumb" } , +{.key = MTP_OP_DELETE_OBJECT , .data = "DeleteObject" } , +{.key = MTP_OP_SEND_OBJECT_INFO , .data = "SendObjectInfo" } , +{.key = MTP_OP_SEND_OBJECT , .data = "SendObject" } , +{.key = MTP_OP_INITIATE_CAPTURE , .data = "InitiateCapture" } , +{.key = MTP_OP_FORMAT_STORE , .data = "FormatStore" } , +{.key = MTP_OP_RESET_DEVICE , .data = "ResetDevice" } , +{.key = MTP_OP_SELF_TEST , .data = "SelfTest" } , +{.key = MTP_OP_SET_OBJECT_PROTECTION , .data = "SetObjectProtection" } , +{.key = MTP_OP_POWER_DOWN , .data = "PowerDown" } , +{.key = MTP_OP_GET_DEVICE_PROP_DESC , .data = "GetDevicePropDesc" } , +{.key = MTP_OP_GET_DEVICE_PROP_VALUE , .data = "GetDevicePropValue" } , +{.key = MTP_OP_SET_DEVICE_PROP_VALUE , .data = "SetDevicePropValue" } , +{.key = MTP_OP_RESET_DEVICE_PROP_VALUE , .data = "ResetDevicePropValue" } , +{.key = MTP_OP_TERMINATE_OPEN_CAPTURE , .data = "TerminateOpenCapture" } , +{.key = MTP_OP_MOVE_OBJECT , .data = "MoveObject" } , +{.key = MTP_OP_COPY_OBJECT , .data = "CopyObject" } , +{.key = MTP_OP_GET_PARTIAL_OBJECT , .data = "GetPartialObject" } , +{.key = MTP_OP_INITIATE_OPEN_CAPTURE , .data = "InitiateOpenCapture" } , +{.key = MTP_OP_GET_OBJECT_PROPS_SUPPORTED , .data = "GetObjectPropsSupported" } , +{.key = MTP_OP_GET_OBJECT_PROP_DESC , .data = "GetObjectPropDesc" } , +{.key = MTP_OP_GET_OBJECT_PROP_VALUE , .data = "GetObjectPropValue" } , +{.key = MTP_OP_SET_OBJECT_PROP_VALUE , .data = "SetObjectPropValue" } , +{.key = MTP_OP_GET_OBJECT_PROPLIST , .data = "GetObjectPropList" } , +{.key = MTP_OP_GET_OBJECT_PROP_REFERENCES , .data = "GetObjectPropReferences" } , +{.key = MTP_OP_GET_SERVICE_IDS , .data = "GetServiceIDs" } , +{.key = MTP_OP_GET_SERVICE_INFO , .data = "GetServiceInfo" } , +{.key = MTP_OP_GET_SERVICE_CAPABILITIES , .data = "GetServiceCapabilities" } , +{.key = MTP_OP_GET_SERVICE_PROP_DESC , .data = "GetServicePropDesc" } , +{.key = MTP_OP_GET_OBJECT_PROP_LIST , .data = "GetObjectPropList" } , +{.key = MTP_OP_SET_OBJECT_PROP_LIST , .data = "SetObjectPropList" } , +{.key = MTP_OP_GET_INTERDEPENDENT_PROP_DESC , .data = "GetInterdependentPropDesc" } , +{.key = MTP_OP_SEND_OBJECT_PROP_LIST , .data = "SendObjectPropList" } +}; + +TU_ATTR_UNUSED static tu_lookup_table_t const _mtp_op_table = { + .count = TU_ARRAY_SIZE(_mpt_op_lookup), + .items = _mpt_op_lookup +}; + +#endif + + //--------------------------------------------------------------------+ // Helper //--------------------------------------------------------------------+ @@ -382,11 +441,12 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { _mtpd_soi.object_handle = 0; } - mtp_phase_type_t ret = MTP_PHASE_RESPONSE; + tu_lookup_find(&_mtp_op_table, cmd_block.code); + TU_LOG_DRV(" MTP command: %s\r\n", (char const*) tu_lookup_find(&_mtp_op_table, cmd_block.code)); - switch (p_container->code) { + mtp_phase_type_t ret = MTP_PHASE_RESPONSE; + switch (cmd_block.code) { case MTP_OP_GET_DEVICE_INFO: { - TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_INFO\n"); tud_mtp_device_info_t dev_info = { .standard_version = 100, .mtp_vendor_extension_id = 6, // MTP specs say 0xFFFFFFFF but libMTP check for value 6 @@ -435,63 +495,14 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { break; } - case MTP_OP_OPEN_SESSION: - TU_LOG_DRV(" MTP command: MTP_OP_OPEN_SESSION\n"); - break; - - case MTP_OP_CLOSE_SESSION: - TU_LOG_DRV(" MTP command: MTP_OP_CLOSE_SESSION\n"); - break; - - case MTP_OP_GET_STORAGE_IDS: - TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_IDS\n"); - break; - - case MTP_OP_GET_STORAGE_INFO: - TU_LOG_DRV(" MTP command: MTP_OP_GET_STORAGE_INFO for ID=%lu\n", p_container->data[0]); - break; - - case MTP_OP_GET_OBJECT_HANDLES: - TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_HANDLES\n"); - break; - - case MTP_OP_GET_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT_INFO\n"); - break; - - case MTP_OP_GET_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OP_GET_OBJECT\n"); - break; - - case MTP_OP_DELETE_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OP_DELETE_OBJECT\n"); - return mtpd_handle_cmd_delete_object(); - - case MTP_OP_GET_DEVICE_PROP_DESC: - TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_DESC\n"); - break; - - case MTP_OP_GET_DEVICE_PROP_VALUE: - TU_LOG_DRV(" MTP command: MTP_OP_GET_DEVICE_PROP_VALUE\n"); - break; - - case MTP_OP_SEND_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO\n"); - return mtpd_handle_cmd_send_object_info(); - case MTP_OP_SEND_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT\n"); - return mtpd_handle_cmd_send_object(); - case MTP_OP_FORMAT_STORE: - TU_LOG_DRV(" MTP command: MTP_OP_FORMAT_STORE\n"); - return mtpd_handle_cmd_format_store(); default: - TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", p_container->code); - return false; + break; } tud_mtp_command_received_cb(0, &cmd_block, p_container); return ret; } +#if 0 mtp_phase_type_t mtpd_handle_data(void) { @@ -614,6 +625,7 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) p_container->len = MTP_CONTAINER_HEADER_LENGTH; return MTP_PHASE_RESPONSE; } +#endif //--------------------------------------------------------------------+ // Checker -- cgit v1.3.1 From 3c39f60f63109f7828009ebda135fc675288d610 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 23 Sep 2025 15:23:16 +0700 Subject: refactor API --- examples/device/mtp/src/mtp_fs_example.c | 107 ++++++++++++++-------------- examples/device/mtp/src/tusb_config.h | 4 +- examples/device/mtp/src/usb_descriptors.c | 10 ++- src/class/mtp/mtp.h | 111 ++++++++++++++++++------------ src/class/mtp/mtp_device.c | 67 +++++++++--------- src/class/mtp/mtp_device.h | 33 ++++++--- 6 files changed, 179 insertions(+), 153 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 104260373..5b9b5b2eb 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -164,90 +164,87 @@ uint32_t fs_get_object_count(void) { return count; } -int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes) { - (void) idx; - (void) cmd_header; - resp_block->len = MTP_CONTAINER_HEADER_LENGTH; - resp_block->code = (xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - tud_mtp_response_send(resp_block); +int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { + mtp_container_info_t* reply = &cb_data->reply; + reply->header->len = sizeof(mtp_container_header_t); + reply->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; + tud_mtp_response_send(reply); return 0; } -int32_t tud_mtp_response_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes) { - (void) idx; - (void) cmd_header; - (void) resp_block; - (void) xfer_result; - (void) xferred_bytes; +int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; return 0; // nothing to do } -int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block) { - (void)idx; - switch (cmd_block->code) { +int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command; + mtp_container_info_t* reply = &cb_data->reply; + switch (command->header.code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields - mtp_container_add_cstring(out_block, DEV_INFO_MANUFACTURER); - mtp_container_add_cstring(out_block, DEV_INFO_MODEL); - mtp_container_add_cstring(out_block, DEV_INFO_VERSION); - mtp_container_add_cstring(out_block, DEV_INFO_SERIAL); + mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MANUFACTURER); + mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MODEL); + mtp_container_add_cstring(&cb_data->reply, DEV_INFO_VERSION); + mtp_container_add_cstring(&cb_data->reply, DEV_INFO_SERIAL); - tud_mtp_data_send(out_block); + tud_mtp_data_send(&cb_data->reply); break; } case MTP_OP_OPEN_SESSION: if (is_session_opened) { //return MTP_RESP_SESSION_ALREADY_OPEN; - out_block->code = MTP_RESP_SESSION_ALREADY_OPEN; + reply->header->code = MTP_RESP_SESSION_ALREADY_OPEN; }else { - out_block->code = MTP_RESP_OK; + reply->header->code = MTP_RESP_OK; } is_session_opened = true; - tud_mtp_response_send(out_block); + tud_mtp_response_send(&cb_data->reply); break; case MTP_OP_CLOSE_SESSION: if (!is_session_opened) { // return MTP_RESP_SESSION_NOT_OPEN; - out_block->code = MTP_RESP_SESSION_NOT_OPEN; + reply->header->code = MTP_RESP_SESSION_NOT_OPEN; } else { - out_block->code = MTP_RESP_OK; + reply->header->code = MTP_RESP_OK; } is_session_opened = false; - tud_mtp_response_send(out_block); + tud_mtp_response_send(&cb_data->reply); break; case MTP_OP_GET_STORAGE_IDS: { uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; // physical = 1, logical = 1 - mtp_container_add_auint32(out_block, 1, storage_ids); - tud_mtp_data_send(out_block); + mtp_container_add_auint32(&cb_data->reply, 1, storage_ids); + tud_mtp_data_send(&cb_data->reply); break; } case MTP_OP_GET_STORAGE_INFO: { - TU_VERIFY(SUPPORTED_STORAGE_ID == cmd_block->data[0], -1); + uint32_t storage_id = command->params[0]; + TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); // update storage info with current free space storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_object_count(); storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; - mtp_container_add_raw(out_block, &storage_info, sizeof(storage_info)); - tud_mtp_data_send(out_block); + mtp_container_add_raw(&cb_data->reply, &storage_info, sizeof(storage_info)); + tud_mtp_data_send(&cb_data->reply); break; } case MTP_OP_GET_DEVICE_PROP_DESC: { - const uint16_t dev_prop_code = (uint16_t) cmd_block->data[0]; + const uint16_t dev_prop_code = (uint16_t) command->params[0]; mtp_device_prop_desc_header_t device_prop_header; device_prop_header.device_property_code = dev_prop_code; switch (dev_prop_code) { case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: device_prop_header.datatype = MTP_DATA_TYPE_STR; device_prop_header.get_set = MTP_MODE_GET; - mtp_container_add_raw(out_block, &device_prop_header, sizeof(device_prop_header)); - mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); // factory - mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); // current - mtp_container_add_uint8(out_block, 0); // no form - tud_mtp_data_send(out_block); + mtp_container_add_raw(&cb_data->reply, &device_prop_header, sizeof(device_prop_header)); + mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // factory + mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // current + mtp_container_add_uint8(&cb_data->reply, 0); // no form + tud_mtp_data_send(&cb_data->reply); break; default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; @@ -256,11 +253,11 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl } case MTP_OP_GET_DEVICE_PROP_VALUE: { - const uint16_t dev_prop_code = (uint16_t) cmd_block->data[0]; + const uint16_t dev_prop_code = (uint16_t) command->params[0]; switch (dev_prop_code) { case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - mtp_container_add_cstring(out_block, DEV_PROP_FRIENDLY_NAME); - tud_mtp_data_send(out_block); + mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); + tud_mtp_data_send(&cb_data->reply); break; default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; @@ -269,10 +266,10 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl } case MTP_OP_GET_OBJECT_HANDLES: { - const uint32_t storage_id = cmd_block->data[0]; - const uint32_t obj_format = cmd_block->data[1]; // optional + const uint32_t storage_id = command->params[0]; + const uint32_t obj_format = command->params[1]; // optional (void) obj_format; - const uint32_t parent_handle = cmd_block->data[2]; // folder handle, 0xFFFFFFFF is root + const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { return MTP_RESP_INVALID_STORAGE_ID; } @@ -286,13 +283,13 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl handles[count++] = i + 1; // handle is index + 1 } } - mtp_container_add_auint32(out_block, count, handles); - tud_mtp_data_send(out_block); + mtp_container_add_auint32(&cb_data->reply, count, handles); + tud_mtp_data_send(&cb_data->reply); break; } case MTP_OP_GET_OBJECT_INFO: { - const uint32_t obj_handle = cmd_block->data[0]; + const uint32_t obj_handle = command->params[0]; fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { return MTP_RESP_INVALID_OBJECT_HANDLE; @@ -314,25 +311,25 @@ int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_bl .association_desc = 0, .sequence_number = 0 }; - mtp_container_add_raw(out_block, &obj_info_header, sizeof(obj_info_header)); - mtp_container_add_cstring(out_block, obj->name); - mtp_container_add_cstring(out_block, FS_FIXED_DATETIME); - mtp_container_add_cstring(out_block, FS_FIXED_DATETIME); - mtp_container_add_cstring(out_block, ""); // keywords, not used + mtp_container_add_raw(&cb_data->reply, &obj_info_header, sizeof(obj_info_header)); + mtp_container_add_cstring(&cb_data->reply, obj->name); + mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); + mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); + mtp_container_add_cstring(&cb_data->reply, ""); // keywords, not used - tud_mtp_data_send(out_block); + tud_mtp_data_send(&cb_data->reply); break; } case MTP_OP_GET_OBJECT: { - const uint32_t obj_handle = cmd_block->data[0]; + const uint32_t obj_handle = command->params[0]; fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { return MTP_RESP_INVALID_OBJECT_HANDLE; } - mtp_container_add_raw(out_block, obj->data, obj->size); - tud_mtp_data_send(out_block); + mtp_container_add_raw(&cb_data->reply, obj->data, obj->size); + tud_mtp_data_send(&cb_data->reply); break; } diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index 7e2c5b670..ef03e0480 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -92,9 +92,7 @@ //------------- CLASS -------------// #define CFG_TUD_MTP 1 -#define CFG_MTP_EP_SIZE 64 -#define CFG_MTP_EVT_EP_SIZE 64 -#define CFG_MTP_EVT_INTERVAL 100 +#define CFG_TUD_MTP_EP_BUFSIZE 512 //------------- MTP device info -------------// #define CFG_TUD_MTP_DEVICEINFO_EXTENSIONS "microsoft.com: 1.0; " diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c index 73685c3fd..4a43a0dcc 100644 --- a/examples/device/mtp/src/usb_descriptors.c +++ b/examples/device/mtp/src/usb_descriptors.c @@ -109,18 +109,16 @@ enum #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MTP_DESC_LEN) -uint8_t const desc_fs_configuration[] = -{ +uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - TUD_MTP_DESCRIPTOR(ITF_NUM_MTP, 4, EPNUM_MTP_EVT, CFG_MTP_EVT_EP_SIZE, CFG_MTP_EVT_INTERVAL, EPNUM_MTP_OUT, EPNUM_MTP_IN, CFG_MTP_EP_SIZE), + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + TUD_MTP_DESCRIPTOR(ITF_NUM_MTP, 4, EPNUM_MTP_EVT, 64, 1, EPNUM_MTP_OUT, EPNUM_MTP_IN, 64), }; // 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) -{ +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations return desc_fs_configuration; } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index c44b99fda..8e519bdc0 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -658,7 +658,6 @@ typedef enum { // Data structures //--------------------------------------------------------------------+ -#define MTP_CONTAINER_HEADER_LENGTH 12 #define MTP_MAX_PACKET_SIZE 512 typedef struct TU_ATTR_PACKED { @@ -669,14 +668,33 @@ typedef struct TU_ATTR_PACKED { } mtp_container_header_t; TU_VERIFY_STATIC(sizeof(mtp_container_header_t) == 12, "size is not correct"); +typedef struct TU_ATTR_PACKED { + mtp_container_header_t header; + uint32_t params[5]; +} mtp_container_command_t; +TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); + // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { uint32_t len; uint16_t type; uint16_t code; uint32_t transaction_id; - uint32_t data[MTP_MAX_PACKET_SIZE / sizeof(uint32_t)]; + // union { + uint32_t data[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)) / sizeof(uint32_t)]; + // uint8_t data[CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)]; + // }; } mtp_generic_container_t; +TU_VERIFY_STATIC(sizeof(mtp_generic_container_t) == CFG_TUD_MTP_EP_BUFSIZE, "size is not correct"); + +typedef struct { + mtp_container_header_t* header; + union { + uint8_t* payload; + uint16_t* payload16; + uint32_t* payload32; + }; +} mtp_container_info_t; #define mtp_string_t(_nchars) \ struct TU_ATTR_PACKED { \ @@ -690,12 +708,10 @@ typedef struct TU_ATTR_PACKED { _type arr[_count];\ } +#define mtp_aint8_t(_count) mtp_array_t(int8_t, _count) #define mtp_auint16_t(_count) mtp_array_t(uint16_t, _count) - -typedef struct TU_ATTR_PACKED { - uint8_t count; - uint16_t utf16[]; -} mtp_flexible_string_t; +#define mtp_auint32_t(_count) mtp_array_t(uint32_t, _count) +#define mtp_auint64_t(_count) mtp_array_t(uint64_t, _count) typedef union TU_ATTR_PACKED { struct { @@ -775,95 +791,100 @@ typedef struct TU_ATTR_PACKED { } mtp_basic_object_info_t; //--------------------------------------------------------------------+ -// Generic Container function +// Container helper function +// return number of bytes added //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_generic_container_t* p_container, const void* data, uint32_t len) { - TU_ASSERT(p_container->len + len < sizeof(mtp_generic_container_t), 0); - memcpy((uint8_t*) p_container + p_container->len, data, len); - p_container->len += len; +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) { + TU_ASSERT(p_container->header->len + len < sizeof(mtp_generic_container_t), 0); + uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); + memcpy(buf, data, len); + p_container->header->len += len; return len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_generic_container_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_container_info_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { const uint32_t added_len = 4 + count * scalar_size; - TU_ASSERT(p_container->len + added_len < sizeof(mtp_generic_container_t), 0); - uint8_t* container8 = (uint8_t*)p_container; + TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); - tu_unaligned_write32(container8 + p_container->len, count); - p_container->len += 4; + tu_unaligned_write32(buf, count); + p_container->header->len += 4; + buf += 4; - memcpy(container8 + p_container->len, data, count * scalar_size); - p_container->len += count * scalar_size; + memcpy(buf, data, count * scalar_size); + p_container->header->len += count * scalar_size; return added_len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_generic_container_t* p_container, uint8_t count, uint16_t* utf16) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint8_t count, uint16_t* utf16) { const uint32_t added_len = 1 + 2 * count; - TU_ASSERT(p_container->len + added_len < sizeof(mtp_generic_container_t), 0); - uint8_t* container8 = (uint8_t*) p_container; + TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); - container8[p_container->len] = count; - p_container->len++; + *buf++ = count; + p_container->header->len++; - memcpy(container8 + p_container->len, utf16, 2 * count); - p_container->len += 2 * count; + memcpy(buf, utf16, 2 * count); + p_container->header->len += 2 * count; return added_len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_generic_container_t* p_container, const char* str) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) { const uint8_t len = (uint8_t) (strlen(str) + 1); // include null - TU_ASSERT(p_container->len + 1 + 2 * len < sizeof(mtp_generic_container_t), 0); - - uint8_t* container8 = (uint8_t*) p_container; - container8[p_container->len] = len; - p_container->len++; + TU_ASSERT(p_container->header->len + 1 + 2 * len < sizeof(mtp_generic_container_t), 0); + uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); if (len == 1) { - // empty string (null only) - container8[p_container->len] = 0; + // empty string (null only): single zero byte + *buf = 0; + p_container->header->len++; return 1; } else { + *buf++ = len; + p_container->header->len++; + for (uint8_t i = 0; i < len; i++) { - container8[p_container->len] = str[i]; - container8[p_container->len + 1] = 0; - p_container->len += 2; + buf[0] = str[i]; + buf[1] = 0; + buf += 2; + p_container->header->len += 2; } return 1 + 2 * len; } } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_generic_container_t* p_container, uint8_t data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint8(mtp_container_info_t* p_container, uint8_t data) { return mtp_container_add_raw(p_container, &data, 1); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint16(mtp_generic_container_t* p_container, uint16_t data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint16(mtp_container_info_t* p_container, uint16_t data) { return mtp_container_add_raw(p_container, &data, 2); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint32(mtp_generic_container_t* p_container, uint32_t data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint32(mtp_container_info_t* p_container, uint32_t data) { return mtp_container_add_raw(p_container, &data, 4); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint64(mtp_generic_container_t* p_container, uint64_t data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint64(mtp_container_info_t* p_container, uint64_t data) { return mtp_container_add_raw(p_container, &data, 8); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint128(mtp_generic_container_t* p_container, const void* data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_uint128(mtp_container_info_t* p_container, const void* data) { return mtp_container_add_raw(p_container, data, 16); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint8(mtp_generic_container_t* p_container, uint32_t count, const uint8_t* data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint8(mtp_container_info_t* p_container, uint32_t count, const uint8_t* data) { return mtp_container_add_array(p_container, sizeof(uint8_t), count, data); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint16(mtp_generic_container_t* p_container, uint32_t count, const uint16_t* data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint16(mtp_container_info_t* p_container, uint32_t count, const uint16_t* data) { return mtp_container_add_array(p_container, sizeof(uint16_t), count, data); } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_generic_container_t* p_container, uint32_t count, const uint32_t* data) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_container_info_t* p_container, uint32_t count, const uint32_t* data) { return mtp_container_add_array(p_container, sizeof(uint32_t), count, data); } diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 91df13111..71ead732d 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -67,7 +67,8 @@ typedef struct { bool xfer_completed; // true when DATA-IN/DATA-OUT transfer is completed uint32_t session_id; - mtp_container_header_t cmd_header; + mtp_container_command_t command; + // mtp_container_header_t reply_header; } mtpd_interface_t; typedef struct { @@ -81,7 +82,7 @@ typedef struct { static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); // MTP commands -static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp); +static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); @@ -265,26 +266,24 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -bool tud_mtp_data_send(mtp_generic_container_t* data_block) { +bool tud_mtp_data_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_DATA; - p_mtp->total_len = data_block->len; + p_mtp->total_len = p_container->header->len; p_mtp->xferred_len = 0; p_mtp->handled_len = 0; p_mtp->xfer_completed = false; - data_block->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - data_block->transaction_id = p_mtp->cmd_header.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t*) data_block, (uint16_t)data_block->len)); + p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), (uint16_t)p_container->header->len)); return true; } -bool tud_mtp_response_send(mtp_generic_container_t* resp_block) { +bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; - resp_block->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - resp_block->transaction_id = p_mtp->cmd_header.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t*) resp_block, (uint16_t)resp_block->len)); + p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), (uint16_t)p_container->header->len)); return true; } @@ -300,6 +299,13 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t mtpd_interface_t* p_mtp = &_mtpd_itf; mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + tud_mtp_cb_data_t cb_data; + cb_data.idx = 0; + cb_data.command = &p_mtp->command; + cb_data.reply.header = (mtp_container_header_t*) p_container; + cb_data.reply.payload32 = p_container->data; + cb_data.offset = 0; + switch (p_mtp->phase) { case MTP_PHASE_IDLE: // received new command @@ -308,7 +314,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ATTR_FALLTHROUGH; // handle in the next case case MTP_PHASE_COMMAND: { - mtpd_handle_cmd(p_mtp); + memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // copy new command + mtpd_handle_cmd(p_mtp, &cb_data); break; } @@ -320,7 +327,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (xferred_bytes == 0 || // ZLP (xferred_bytes & (bulk_mps - 1)) || // short packet p_mtp->xferred_len > p_mtp->total_len) { - tud_mtp_data_complete_cb(0, &p_mtp->cmd_header, p_container, event, p_mtp->xferred_len); + tud_mtp_data_complete_cb(&cb_data); } else { TU_ASSERT(false); } @@ -404,7 +411,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t case MTP_PHASE_RESPONSE_QUEUED: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); - tud_mtp_response_complete_cb(0, &p_mtp->cmd_header, p_container, event, xferred_bytes); + tud_mtp_response_complete_cb(&cb_data); prepare_new_command(p_mtp); break; @@ -429,23 +436,15 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // Decode command and prepare response -mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { +mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + p_container->len = sizeof(mtp_container_header_t); // default data/response length - mtp_generic_container_t cmd_block; // copy command block for callback - memcpy(&cmd_block, p_container, p_container->len); - memcpy(&p_mtp->cmd_header, p_container, sizeof(mtp_container_header_t)); - p_container->len = MTP_CONTAINER_HEADER_LENGTH; // default data/response length - - if (p_container->code != MTP_OP_SEND_OBJECT) { - _mtpd_soi.object_handle = 0; - } - - tu_lookup_find(&_mtp_op_table, cmd_block.code); - TU_LOG_DRV(" MTP command: %s\r\n", (char const*) tu_lookup_find(&_mtp_op_table, cmd_block.code)); + tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code); + TU_LOG_DRV(" MTP command: %s\r\n", (char const*) tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code)); - mtp_phase_type_t ret = MTP_PHASE_RESPONSE; - switch (cmd_block.code) { + // pre-processed commands + switch (p_mtp->command.header.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { .standard_version = 100, @@ -486,12 +485,8 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; } #endif - p_container->len = MTP_CONTAINER_HEADER_LENGTH + sizeof(tud_mtp_device_info_t); + mtp_container_add_raw(&cb_data->reply, &dev_info, sizeof(tud_mtp_device_info_t)); p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->code = MTP_OP_GET_DEVICE_INFO; - memcpy(p_container->data, &dev_info, sizeof(tud_mtp_device_info_t)); - - ret = MTP_PHASE_RESPONSE; break; } @@ -499,8 +494,8 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp) { break; } - tud_mtp_command_received_cb(0, &cmd_block, p_container); - return ret; + tud_mtp_command_received_cb(cb_data); + return MTP_PHASE_RESPONSE; } #if 0 @@ -640,7 +635,7 @@ mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, cons TU_LOG_DRV(" MTP error in %s: (%x) %s\n", func_name, ret_code, message); p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->code = ret_code; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; + p_container->len = sizeof(mtp_container_header_t); return MTP_PHASE_RESPONSE; } return MTP_PHASE_NONE; diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index b85b1706e..0524edcf6 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -37,10 +37,22 @@ #endif typedef struct { - const mtp_container_header_t* cmd_header; + uint8_t idx; // mtp instance + const mtp_container_command_t* command; + mtp_container_info_t reply; + + union { + uint8_t* buffer; + uint16_t* buffer16; + uint32_t* buffer32; + }; + uint32_t bufsize; + uint32_t offset; // offset from start of header, since data can span multiple xfers + + tusb_xfer_result_t xfer_result; uint32_t xferred_bytes; -} tud_mtp_cb_complete_data_t; +} tud_mtp_cb_data_t; // Number of supported operations, events, device properties, capture formats, playback formats // and max number of characters for strings manufacturer, model, device_version, serial_number @@ -91,23 +103,28 @@ typedef struct { //--------------------------------------------------------------------+ // Application API //--------------------------------------------------------------------+ -bool tud_mtp_data_send(mtp_generic_container_t* data_block); +bool tud_mtp_data_send(mtp_container_info_t* p_container); // bool tud_mtp_block_data_receive(); -bool tud_mtp_response_send(mtp_generic_container_t* resp_block); +bool tud_mtp_response_send(mtp_container_info_t* p_container); + +//--------------------------------------------------------------------+ +// Control request Callbacks +//--------------------------------------------------------------------+ +// bool tud_mtp_control_xfer_cb(uint8_t idx, uint8_t stage, tusb_control_request_t const *p_request); //--------------------------------------------------------------------+ -// Application Callbacks +// Bulk only protocol Callbacks //--------------------------------------------------------------------+ // Invoked when new command is received. Application fill the out_block with either DATA or RESPONSE container // return MTP response code -int32_t tud_mtp_command_received_cb(uint8_t idx, mtp_generic_container_t* cmd_block, mtp_generic_container_t* out_block); +int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); // Invoked when data phase is complete -int32_t tud_mtp_data_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes); +int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); // Invoked when response phase is complete -int32_t tud_mtp_response_complete_cb(uint8_t idx, mtp_container_header_t* cmd_header, mtp_generic_container_t* resp_block, tusb_xfer_result_t xfer_result, uint32_t xferred_bytes); +int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data); //--------------------------------------------------------------------+ // Helper functions -- cgit v1.3.1 From 6317730be6d7a97fc12376d6f80c90a6dbfc20ea Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 24 Sep 2025 12:49:52 +0700 Subject: unify callback argument. support multiple packet get object --- examples/device/mtp/src/mtp_fs_example.c | 26 ++++++++++++++++++ examples/device/mtp/src/tusb_config.h | 2 +- src/class/mtp/mtp.h | 35 +++++++++++++------------ src/class/mtp/mtp_device.c | 44 ++++++++++++++++++++----------- src/class/mtp/mtp_device.h | 13 +++------ tools/file2carray.py | 45 ++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 41 deletions(-) create mode 100644 tools/file2carray.py (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 5b9b5b2eb..19ca71d62 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -177,6 +177,30 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { return 0; // nothing to do } +int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { + // only a few command that need more data e.g GetObject and SendObject + const mtp_container_command_t* command = cb_data->command; + mtp_container_info_t* reply = &cb_data->reply; + switch (command->header.code) { + case MTP_OP_GET_OBJECT: { + const uint32_t obj_handle = command->params[0]; + fs_object_info_t* obj = fs_get_object(obj_handle); + if (obj == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + // file contents offset is xferred byte minus header size + const uint32_t offset = cb_data->xferred_bytes - sizeof(mtp_container_header_t); + const uint32_t xact_len = tu_min32(obj->size - offset, reply->payload_size); + memcpy(reply->payload, obj->data + offset, xact_len); + tud_mtp_data_send(&cb_data->reply); + } + + default: return MTP_RESP_OPERATION_NOT_SUPPORTED; + } + + return MTP_RESP_OK; +} + int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command; mtp_container_info_t* reply = &cb_data->reply; @@ -328,6 +352,8 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { return MTP_RESP_INVALID_OBJECT_HANDLE; } + // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here + // the rest will be sent in tud_mtp_data_more_cb mtp_container_add_raw(&cb_data->reply, obj->data, obj->size); tud_mtp_data_send(&cb_data->reply); break; diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index ef03e0480..7db5235d0 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -92,7 +92,7 @@ //------------- CLASS -------------// #define CFG_TUD_MTP 1 -#define CFG_TUD_MTP_EP_BUFSIZE 512 +#define CFG_TUD_MTP_EP_BUFSIZE 512 //------------- MTP device info -------------// #define CFG_TUD_MTP_DEVICEINFO_EXTENSIONS "microsoft.com: 1.0; " diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 8e519bdc0..d4d81492e 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -657,9 +657,6 @@ typedef enum { //--------------------------------------------------------------------+ // Data structures //--------------------------------------------------------------------+ - -#define MTP_MAX_PACKET_SIZE 512 - typedef struct TU_ATTR_PACKED { uint32_t len; uint16_t type; @@ -694,6 +691,7 @@ typedef struct { uint16_t* payload16; uint32_t* payload32; }; + uint32_t payload_size; } mtp_container_info_t; #define mtp_string_t(_nchars) \ @@ -713,14 +711,6 @@ typedef struct { #define mtp_auint32_t(_count) mtp_array_t(uint32_t, _count) #define mtp_auint64_t(_count) mtp_array_t(uint64_t, _count) -typedef union TU_ATTR_PACKED { - struct { - uint16_t physical; // physical location - uint16_t logical; // logical within physical - }; - uint32_t id; -} mtp_storage_id_t; - #define MTP_STORAGE_INFO_STRUCT(_storage_desc_chars, _volume_id_chars) \ struct TU_ATTR_PACKED { \ uint16_t storage_type; \ @@ -795,12 +785,25 @@ typedef struct TU_ATTR_PACKED { // return number of bytes added //--------------------------------------------------------------------+ +// return payload buffer for next write +TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_next(mtp_container_info_t* p_container) { + // only 1st packet include header + uint32_t pos = p_container->header->len - sizeof(mtp_container_header_t); + while (pos > CFG_TUD_MTP_EP_BUFSIZE) { + pos -= CFG_TUD_MTP_EP_BUFSIZE; + } + return p_container->payload + pos; +} + +// only add_raw does partial copy TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) { - TU_ASSERT(p_container->header->len + len < sizeof(mtp_generic_container_t), 0); - uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); - memcpy(buf, data, len); - p_container->header->len += len; - return len; + uint8_t* buf = mtp_container_payload_next(p_container); + const uint32_t added_len = tu_min32(len, sizeof(mtp_generic_container_t) - p_container->header->len); + if (added_len > 0) { + memcpy(buf, data, added_len); + } + p_container->header->len += len; // always increase len, even partial copy + return added_len; } TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_container_info_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 71ead732d..92801a8ad 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -60,15 +60,12 @@ typedef struct { // Bulk Only Transfer (BOT) Protocol uint8_t phase; - uint32_t queued_len; // number of bytes queued from the DataIN Stage - uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw - uint32_t xferred_len; // number of bytes transferred so far in the Data Stage - uint32_t handled_len; // number of bytes already handled in the Data Stage - bool xfer_completed; // true when DATA-IN/DATA-OUT transfer is completed + uint32_t total_len; + uint32_t xferred_len; uint32_t session_id; mtp_container_command_t command; - // mtp_container_header_t reply_header; + mtp_container_header_t reply_header; } mtpd_interface_t; typedef struct { @@ -268,14 +265,23 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t bool tud_mtp_data_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; - p_mtp->phase = MTP_PHASE_DATA; - p_mtp->total_len = p_container->header->len; - p_mtp->xferred_len = 0; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; + if (p_mtp->phase == MTP_PHASE_COMMAND) { + // 1st data block: header + payload + p_mtp->phase = MTP_PHASE_DATA; + p_mtp->total_len = p_container->header->len; + p_mtp->xferred_len = 0; + + p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->header->transaction_id = p_mtp->command.header.transaction_id; + p_mtp->reply_header = *p_container->header; // save header for subsequent data + } else { + // subsequent data block: payload only + TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); + } + + const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), xact_len)); - p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), (uint16_t)p_container->header->len)); return true; } @@ -283,6 +289,7 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->header->transaction_id = p_mtp->command.header.transaction_id; TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), (uint16_t)p_container->header->len)); return true; } @@ -304,7 +311,9 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t cb_data.command = &p_mtp->command; cb_data.reply.header = (mtp_container_header_t*) p_container; cb_data.reply.payload32 = p_container->data; - cb_data.offset = 0; + cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); + cb_data.xferred_bytes = 0; + cb_data.xfer_result = event; switch (p_mtp->phase) { case MTP_PHASE_IDLE: @@ -322,6 +331,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t case MTP_PHASE_DATA: { const uint16_t bulk_mps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; p_mtp->xferred_len += xferred_bytes; + cb_data.xferred_bytes = p_mtp->xferred_len; // transfer complete if ZLP or short packet or overflow if (xferred_bytes == 0 || // ZLP @@ -329,7 +339,11 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_mtp->xferred_len > p_mtp->total_len) { tud_mtp_data_complete_cb(&cb_data); } else { - TU_ASSERT(false); + // payload only packet + cb_data.reply.header = &p_mtp->reply_header; + cb_data.reply.payload = (uint8_t*) p_container; + cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE; + tud_mtp_data_more_cb(&cb_data); } break; } diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 0524edcf6..88960b1aa 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -41,15 +41,6 @@ typedef struct { const mtp_container_command_t* command; mtp_container_info_t reply; - union { - uint8_t* buffer; - uint16_t* buffer16; - uint32_t* buffer32; - }; - uint32_t bufsize; - uint32_t offset; // offset from start of header, since data can span multiple xfers - - tusb_xfer_result_t xfer_result; uint32_t xferred_bytes; } tud_mtp_cb_data_t; @@ -117,9 +108,13 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container); //--------------------------------------------------------------------+ // Invoked when new command is received. Application fill the out_block with either DATA or RESPONSE container +// and call tud_mtp_data_send() or tud_mtp_response_send(). // return MTP response code int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); +// Invoked when a data packet is received/sent, and more data is expected +int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data); + // Invoked when data phase is complete int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); diff --git a/tools/file2carray.py b/tools/file2carray.py new file mode 100644 index 000000000..abfb4e21b --- /dev/null +++ b/tools/file2carray.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import argparse +import random +import os +import sys +import time +import subprocess +from pathlib import Path +from multiprocessing import Pool +from weakref import finalize + + +def print_carray(f, payload): + while len(payload) > 0: + f.write('\n ') + f.write(', '.join('0x{:02x}'.format(x) for x in payload[0:16])) + f.write(',') + payload = payload[16:] + f.write('\n') + + +def main(): + parser = argparse.ArgumentParser(description='Convert binary files to C array format') + parser.add_argument('files', nargs='+', help='Binary files to convert') + args = parser.parse_args() + + files = args.files + for fin_name in files: + if not os.path.isfile(fin_name): + print(f"File {fin_name} does not exist") + continue + + with open(fin_name, 'rb') as fin: + contents = fin.read() + fout_name = fin_name + '.h' + with open(fout_name, 'w') as fout: + print(f"Converting {fin_name} to {fout_name}") + fout.write(f'const size_t bindata_len = {len(contents)};\n') + fout.write(f'const uint8_t bindata[] __attribute__((aligned(16))) = {{') + print_carray(fout, contents) + fout.write('};\n') + + +if __name__ == '__main__': + sys.exit(main()) -- cgit v1.3.1 From b8126d9c4e9f1fe55988e1789d1bc286a3c0cbb2 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 24 Sep 2025 14:36:25 +0700 Subject: clean up --- examples/device/mtp/src/mtp_fs_example.c | 196 ++++++++++++++----------------- src/class/mtp/mtp.h | 5 +- src/class/mtp/mtp_device.c | 190 +++++------------------------- src/class/mtp/mtp_device.h | 26 +--- 4 files changed, 124 insertions(+), 293 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 19ca71d62..7e19fa75c 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -27,9 +27,6 @@ #include "tusb.h" #include "tinyusb_logo_png.h" -#define MTPD_STORAGE_DESCRIPTION "storage" -#define MTPD_VOLUME_IDENTIFIER "volume" - //--------------------------------------------------------------------+ // Dataset //--------------------------------------------------------------------+ @@ -118,29 +115,6 @@ enum { static bool is_session_opened = false; -//--------------------------------------------------------------------+ -// OPERATING STATUS -//--------------------------------------------------------------------+ -typedef struct { - // Session - uint32_t session_id; - // Association traversal - uint32_t traversal_parent; - uint32_t traversal_index; - // Object open for reading - uint32_t read_handle; - uint32_t read_pos; - // Object open for writing - uint32_t write_handle; - uint32_t write_pos; - // Unique identifier - uint32_t last_handle; -} fs_operation_t; - -static fs_operation_t _fs_operation = { - .last_handle = 1 -}; - //--------------------------------------------------------------------+ // INTERNAL FUNCTIONS //--------------------------------------------------------------------+ @@ -166,7 +140,6 @@ uint32_t fs_get_object_count(void) { int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { mtp_container_info_t* reply = &cb_data->reply; - reply->header->len = sizeof(mtp_container_header_t); reply->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; tud_mtp_response_send(reply); return 0; @@ -181,30 +154,42 @@ int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { // only a few command that need more data e.g GetObject and SendObject const mtp_container_command_t* command = cb_data->command; mtp_container_info_t* reply = &cb_data->reply; - switch (command->header.code) { + uint32_t resp_code = 0; + switch (command->code) { case MTP_OP_GET_OBJECT: { const uint32_t obj_handle = command->params[0]; fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { - return MTP_RESP_INVALID_OBJECT_HANDLE; + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + // file contents offset is xferred byte minus header size + const uint32_t offset = cb_data->xferred_bytes - sizeof(mtp_container_header_t); + const uint32_t xact_len = tu_min32(obj->size - offset, reply->payload_size); + memcpy(reply->payload, obj->data + offset, xact_len); + tud_mtp_data_send(&cb_data->reply); } - // file contents offset is xferred byte minus header size - const uint32_t offset = cb_data->xferred_bytes - sizeof(mtp_container_header_t); - const uint32_t xact_len = tu_min32(obj->size - offset, reply->payload_size); - memcpy(reply->payload, obj->data + offset, xact_len); - tud_mtp_data_send(&cb_data->reply); + break; } - default: return MTP_RESP_OPERATION_NOT_SUPPORTED; + default: + resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; + break; } - return MTP_RESP_OK; + // send response if needed + if (resp_code != 0) { + reply->header->code = resp_code; + tud_mtp_response_send(reply); + } + + return 0; // 0 mean data/response is sent already } int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command; mtp_container_info_t* reply = &cb_data->reply; - switch (command->header.code) { + uint32_t resp_code = 0; + switch (command->code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MANUFACTURER); @@ -218,24 +203,20 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { case MTP_OP_OPEN_SESSION: if (is_session_opened) { - //return MTP_RESP_SESSION_ALREADY_OPEN; - reply->header->code = MTP_RESP_SESSION_ALREADY_OPEN; + resp_code = MTP_RESP_SESSION_ALREADY_OPEN; }else { - reply->header->code = MTP_RESP_OK; + resp_code = MTP_RESP_OK; } is_session_opened = true; - tud_mtp_response_send(&cb_data->reply); break; case MTP_OP_CLOSE_SESSION: if (!is_session_opened) { - // return MTP_RESP_SESSION_NOT_OPEN; - reply->header->code = MTP_RESP_SESSION_NOT_OPEN; + resp_code = MTP_RESP_SESSION_NOT_OPEN; } else { - reply->header->code = MTP_RESP_OK; + resp_code = MTP_RESP_OK; } is_session_opened = false; - tud_mtp_response_send(&cb_data->reply); break; case MTP_OP_GET_STORAGE_IDS: { @@ -246,7 +227,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { } case MTP_OP_GET_STORAGE_INFO: { - uint32_t storage_id = command->params[0]; + const uint32_t storage_id = command->params[0]; TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); // update storage info with current free space storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_object_count(); @@ -271,7 +252,9 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { tud_mtp_data_send(&cb_data->reply); break; - default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; + default: + resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED; + break; } break; } @@ -284,7 +267,9 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { tud_mtp_data_send(&cb_data->reply); break; - default: return MTP_RESP_PARAMETER_NOT_SUPPORTED; + default: + resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED; + break; } break; } @@ -295,20 +280,20 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { (void) obj_format; const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { - return MTP_RESP_INVALID_STORAGE_ID; - } - - uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; - uint32_t count = 0; - for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { - fs_object_info_t* obj = &fs_objects[i]; - if (obj->name[0] != 0 && - (parent_handle == obj->parent || (parent_handle == 0xFFFFFFFF && obj->parent == 0))) { - handles[count++] = i + 1; // handle is index + 1 + resp_code = MTP_RESP_INVALID_STORAGE_ID; + } else { + uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; + uint32_t count = 0; + for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + fs_object_info_t* obj = &fs_objects[i]; + if (obj->name[0] != 0 && + (parent_handle == obj->parent || (parent_handle == 0xFFFFFFFF && obj->parent == 0))) { + handles[count++] = i + 1; // handle is index + 1 + } } + mtp_container_add_auint32(&cb_data->reply, count, handles); + tud_mtp_data_send(&cb_data->reply); } - mtp_container_add_auint32(&cb_data->reply, count, handles); - tud_mtp_data_send(&cb_data->reply); break; } @@ -316,32 +301,33 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const uint32_t obj_handle = command->params[0]; fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { - return MTP_RESP_INVALID_OBJECT_HANDLE; + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + mtp_object_info_header_t obj_info_header = { + .storage_id = SUPPORTED_STORAGE_ID, + .object_format = obj->format, + .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, + .object_compressed_size = obj->size, + .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, + .thumb_compressed_size = 0, + .thumb_pix_width = 0, + .thumb_pix_height = 0, + .image_pix_width = 128, + .image_pix_height = 64, + .image_bit_depth = 32, + .parent_object = obj->parent, + .association_type = MTP_ASSOCIATION_UNDEFINED, + .association_desc = 0, + .sequence_number = 0 + }; + mtp_container_add_raw(&cb_data->reply, &obj_info_header, sizeof(obj_info_header)); + mtp_container_add_cstring(&cb_data->reply, obj->name); + mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); + mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); + mtp_container_add_cstring(&cb_data->reply, ""); // keywords, not used + + tud_mtp_data_send(&cb_data->reply); } - mtp_object_info_header_t obj_info_header = { - .storage_id = SUPPORTED_STORAGE_ID, - .object_format = obj->format, - .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, - .object_compressed_size = obj->size, - .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, - .thumb_compressed_size = 0, - .thumb_pix_width = 0, - .thumb_pix_height = 0, - .image_pix_width = 128, - .image_pix_height = 64, - .image_bit_depth = 32, - .parent_object = obj->parent, - .association_type = MTP_ASSOCIATION_UNDEFINED, - .association_desc = 0, - .sequence_number = 0 - }; - mtp_container_add_raw(&cb_data->reply, &obj_info_header, sizeof(obj_info_header)); - mtp_container_add_cstring(&cb_data->reply, obj->name); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, ""); // keywords, not used - - tud_mtp_data_send(&cb_data->reply); break; } @@ -349,20 +335,28 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const uint32_t obj_handle = command->params[0]; fs_object_info_t* obj = fs_get_object(obj_handle); if (obj == NULL) { - return MTP_RESP_INVALID_OBJECT_HANDLE; + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here + // the rest will be sent in tud_mtp_data_more_cb + mtp_container_add_raw(&cb_data->reply, obj->data, obj->size); + tud_mtp_data_send(&cb_data->reply); } - - // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here - // the rest will be sent in tud_mtp_data_more_cb - mtp_container_add_raw(&cb_data->reply, obj->data, obj->size); - tud_mtp_data_send(&cb_data->reply); break; } - default: return MTP_RESP_OPERATION_NOT_SUPPORTED; + default: + resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; + break; } - return MTP_RESP_OK; + // send response if needed + if (resp_code != 0) { + reply->header->code = resp_code; + tud_mtp_response_send(reply); + } + + return 0; } //--------------------------------------------------------------------+ @@ -556,23 +550,7 @@ void tud_mtp_storage_object_done(void) { #endif void tud_mtp_storage_cancel(void) { - fs_object_info_t* obj; - - _fs_operation.traversal_parent = 0; - _fs_operation.traversal_index = 0; - _fs_operation.read_handle = 0; - _fs_operation.read_pos = 0; - // If write operation is canceled, discard object - if (_fs_operation.write_handle) { - obj = fs_get_object(_fs_operation.write_handle); - // if (obj) - // obj->allocated = false; - } - _fs_operation.write_handle = 0; - _fs_operation.write_pos = 0; } void tud_mtp_storage_reset(void) { - tud_mtp_storage_cancel(); - _fs_operation.session_id = 0; } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index d4d81492e..f1b1eaf23 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -666,7 +666,10 @@ typedef struct TU_ATTR_PACKED { TU_VERIFY_STATIC(sizeof(mtp_container_header_t) == 12, "size is not correct"); typedef struct TU_ATTR_PACKED { - mtp_container_header_t header; + uint32_t len; + uint16_t type; + uint16_t code; + uint32_t transaction_id; uint32_t params[5]; } mtp_container_command_t; TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 92801a8ad..358114655 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -69,17 +69,13 @@ typedef struct { } mtpd_interface_t; typedef struct { - TUD_EPBUF_TYPE_DEF(mtp_generic_container_t, container); + TUD_EPBUF_DEF(buf, CFG_TUD_MTP_EP_BUFSIZE); } mtpd_epbuf_t; //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ -// Checker -static mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message); - -// MTP commands -static mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); +static int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); @@ -95,10 +91,7 @@ static mtpd_interface_t _mtpd_itf; CFG_TUD_MEM_SECTION static mtpd_epbuf_t _mtpd_epbuf; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_device_status_res_t _mtpd_device_status_res; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint32_t _mtpd_get_object_handle; CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static char _mtp_datestr[20]; - //--------------------------------------------------------------------+ // Debug @@ -165,17 +158,15 @@ TU_ATTR_UNUSED static tu_lookup_table_t const _mtp_op_table = { static bool prepare_new_command(mtpd_interface_t* p_mtp) { p_mtp->phase = MTP_PHASE_IDLE; - return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t)); + return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE); } - //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void mtpd_init(void) { tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); - _mtpd_get_object_handle = 0; } bool mtpd_deinit(void) { @@ -187,7 +178,6 @@ void mtpd_reset(uint8_t rhport) { tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); tu_memclr(&_mtpd_epbuf, sizeof(mtpd_epbuf_t)); tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); - _mtpd_get_object_handle = 0; } uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { @@ -242,7 +232,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); tud_mtp_storage_reset(); // Prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, (uint8_t *)(&_mtpd_epbuf.container), sizeof(mtp_generic_container_t))); + TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE)); break; case MTP_REQ_GET_DEVICE_STATUS: { @@ -272,7 +262,7 @@ bool tud_mtp_data_send(mtp_container_info_t* p_container) { p_mtp->xferred_len = 0; p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->header->transaction_id = p_mtp->command.header.transaction_id; + p_container->header->transaction_id = p_mtp->command.transaction_id; p_mtp->reply_header = *p_container->header; // save header for subsequent data } else { // subsequent data block: payload only @@ -280,7 +270,7 @@ bool tud_mtp_data_send(mtp_container_info_t* p_container) { } const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), xact_len)); + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, xact_len)); return true; } @@ -289,22 +279,25 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->header->transaction_id = p_mtp->command.header.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, (uint8_t *)(&_mtpd_epbuf.container), (uint16_t)p_container->header->len)); + p_container->header->transaction_id = p_mtp->command.transaction_id; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); return true; } // Transfer on bulk endpoints bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { - TU_ASSERT(event == XFER_RESULT_SUCCESS); - if (ep_addr == _mtpd_itf.ep_event) { // nothing to do return true; } mtpd_interface_t* p_mtp = &_mtpd_itf; - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = (mtp_generic_container_t*) _mtpd_epbuf.buf; + +#if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL + tu_lookup_find(&_mtp_op_table, p_mtp->command.code); + TU_LOG_DRV(" MTP %s phase = %u\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), p_mtp->phase); +#endif tud_mtp_cb_data_t cb_data; cb_data.idx = 0; @@ -323,8 +316,10 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ATTR_FALLTHROUGH; // handle in the next case case MTP_PHASE_COMMAND: { - memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // copy new command - mtpd_handle_cmd(p_mtp, &cb_data); + memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command + if (mtpd_handle_cmd(p_mtp, &cb_data) < 0) { + p_mtp->phase = MTP_PHASE_ERROR; + } break; } @@ -337,6 +332,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (xferred_bytes == 0 || // ZLP (xferred_bytes & (bulk_mps - 1)) || // short packet p_mtp->xferred_len > p_mtp->total_len) { + cb_data.reply.header->len = sizeof(mtp_container_header_t); tud_mtp_data_complete_cb(&cb_data); } else { // payload only packet @@ -450,15 +446,11 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // Decode command and prepare response -mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - p_container->len = sizeof(mtp_container_header_t); // default data/response length - - tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code); - TU_LOG_DRV(" MTP command: %s\r\n", (char const*) tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code)); +int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { + cb_data->reply.header->len = sizeof(mtp_container_header_t); // pre-processed commands - switch (p_mtp->command.header.code) { + switch (p_mtp->command.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { .standard_version = 100, @@ -500,7 +492,6 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_ } #endif mtp_container_add_raw(&cb_data->reply, &dev_info, sizeof(tud_mtp_device_info_t)); - p_container->type = MTP_CONTAINER_TYPE_DATA_BLOCK; break; } @@ -508,14 +499,13 @@ mtp_phase_type_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_ break; } - tud_mtp_command_received_cb(cb_data); - return MTP_PHASE_RESPONSE; + return tud_mtp_command_received_cb(cb_data); } #if 0 mtp_phase_type_t mtpd_handle_data(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); switch(p_container->code) @@ -535,7 +525,7 @@ mtp_phase_type_t mtpd_handle_data(void) mtp_phase_type_t mtpd_handle_cmd_delete_object(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; uint32_t object_handle = p_container->data[0]; uint32_t object_code_format = p_container->data[1]; // not used (void) object_code_format; @@ -552,7 +542,7 @@ mtp_phase_type_t mtpd_handle_cmd_delete_object(void) mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; _mtpd_soi.storage_id = p_container->data[0]; _mtpd_soi.parent_object_handle = (p_container->data[1] == 0xFFFFFFFF ? 0 : p_container->data[1]); @@ -562,7 +552,7 @@ mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) mtp_phase_type_t mtpd_handle_dto_send_object_info(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; uint32_t new_object_handle = 0; mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_header_t *)p_container->data); mtp_phase_type_t phase; @@ -589,7 +579,7 @@ mtp_phase_type_t mtpd_handle_cmd_send_object(void) mtp_phase_type_t mtpd_handle_dto_send_object(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; uint8_t *buffer = (uint8_t *)&p_container->data; uint32_t buffer_size = _mtpd_itf.xferred_len - _mtpd_itf.handled_len; // First block of DATA @@ -622,7 +612,7 @@ mtp_phase_type_t mtpd_handle_dto_send_object(void) mtp_phase_type_t mtpd_handle_cmd_format_store(void) { - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; + mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; uint32_t storage_id = p_container->data[0]; uint32_t file_system_format = p_container->data[1]; // not used (void) file_system_format; @@ -636,124 +626,4 @@ mtp_phase_type_t mtpd_handle_cmd_format_store(void) } #endif -//--------------------------------------------------------------------+ -// Checker -//--------------------------------------------------------------------+ -mtp_phase_type_t mtpd_chk_generic(const char *func_name, const bool err_cd, const uint16_t ret_code, const char *message) -{ - (void)func_name; - (void)message; - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - if (err_cd) - { - TU_LOG_DRV(" MTP error in %s: (%x) %s\n", func_name, ret_code, message); - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = ret_code; - p_container->len = sizeof(mtp_container_header_t); - return MTP_PHASE_RESPONSE; - } - return MTP_PHASE_NONE; -} - -//--------------------------------------------------------------------+ -// Generic container data -//--------------------------------------------------------------------+ -void mtpd_wc16cpy(uint8_t *dest, const char *src) -{ - wchar16_t s; - while(true) - { - s = *src; - memcpy(dest, &s, sizeof(wchar16_t)); - if (*src == 0) break; - ++src; - dest += sizeof(wchar16_t); - } -} - -//--------------------------------------------------------------------+ -// Generic container function -//--------------------------------------------------------------------+ -bool mtpd_gct_append_uint8(const uint8_t value) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint8_t *p_value = ((uint8_t *)p_container) + p_container->len; - p_container->len += sizeof(uint8_t); - // Verify space requirement (8 bit string length, number of wide characters including terminator) - TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); - *p_value = value; - return true; -} - -bool mtpd_gct_append_object_handle(const uint32_t object_handle) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - p_container->len += sizeof(uint32_t); - TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); - p_container->data[0]++; - p_container->data[p_container->data[0]] = object_handle; - return true; -} - -bool mtpd_gct_append_wstring(const char *s) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - size_t len = strlen(s) + 1; - TU_ASSERT(len <= UINT8_MAX); - uint8_t *p_len = ((uint8_t *)p_container)+p_container->len; - p_container->len += sizeof(uint8_t) + sizeof(wchar16_t) * len; - // Verify space requirement (8 bit string length, number of wide characters including terminator) - TU_ASSERT(p_container->len < sizeof(mtp_generic_container_t)); - *p_len = (uint8_t)len; - uint8_t *p_str = p_len + sizeof(uint8_t); - mtpd_wc16cpy(p_str, s); - return true; -} - -bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max_size) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - uint16_t size = *(((uint8_t *)&p_container->data) + *offset_data); - if (size > max_size) - size = max_size; - TU_ASSERT(*offset_data + size < sizeof(p_container->data)); - - uint8_t *s = ((uint8_t *)&p_container->data) + *offset_data + sizeof(uint8_t); - for(uint16_t i = 0; i < size; i++) - { - string[i] = *s; - s += sizeof(wchar16_t); - } - *offset_data += (uint16_t)(sizeof(uint8_t) + size * sizeof(wchar16_t)); - return true; -} - -bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_size) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - TU_ASSERT(p_container->len + sizeof(uint32_t) + array_size * type_size < sizeof(p_container->data)); - uint8_t *p = ((uint8_t *)p_container) + p_container->len; - memcpy(p, &array_size, sizeof(uint32_t)); - p += sizeof(uint32_t); - memcpy(p, data, array_size * type_size); - p_container->len += sizeof(uint32_t) + array_size * type_size; - return true; -} - -bool mtpd_gct_append_date(struct tm *timeinfo) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.container; - // strftime is not supported by all platform, this implementation is just for reference - int len = snprintf(_mtp_datestr, sizeof(p_container->data) - p_container->len, "%04d%02d%02dT%02d%02d%02dZ", - timeinfo->tm_year + 1900, - timeinfo->tm_mon + 1, - timeinfo->tm_mday, - timeinfo->tm_hour, - timeinfo->tm_min, - timeinfo->tm_sec); - if (len == 0) - return false; - return mtpd_gct_append_wstring(_mtp_datestr); -} - -#endif // (CFG_TUD_ENABLED && CFG_TUD_MTP) +#endif diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 88960b1aa..28c01ddc6 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -107,9 +107,9 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container); // Bulk only protocol Callbacks //--------------------------------------------------------------------+ -// Invoked when new command is received. Application fill the out_block with either DATA or RESPONSE container -// and call tud_mtp_data_send() or tud_mtp_response_send(). -// return MTP response code +/* Invoked when new command is received. Application fill the cb_data->reply with either DATA or RESPONSE and call + * tud_mtp_data_send() or tud_mtp_response_send(). Return negative to stall the endpoints + */ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); // Invoked when a data packet is received/sent, and more data is expected @@ -121,26 +121,6 @@ int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); // Invoked when response phase is complete int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data); -//--------------------------------------------------------------------+ -// Helper functions -//--------------------------------------------------------------------+ -// Generic container function -void mtpd_wc16cpy(uint8_t *dest, const char *src); -bool mtpd_gct_append_uint8(const uint8_t value); -bool mtpd_gct_append_object_handle(const uint32_t object_handle); -bool mtpd_gct_append_wstring(const char *s); -bool mtpd_gct_get_string(uint16_t *offset_data, char *string, const uint16_t max_size); - -// Append the given array to the global context buffer -// The function returns true if the data fits in the available buffer space. -bool mtpd_gct_append_array(uint32_t array_size, const void *data, size_t type_size); - -// Append an UTC date string to the global context buffer -// Required format is 'YYYYMMDDThhmmss.s' optionally added 'Z' for UTC or +/-hhmm for time zone -// This function is provided for reference and only supports UTC format without partial seconds -// The function returns true if the data fits in the available buffer space. -bool mtpd_gct_append_date(struct tm *timeinfo); - //--------------------------------------------------------------------+ // Internal Class Driver API //--------------------------------------------------------------------+ -- cgit v1.3.1 From f36b63ad6349cb51731989eaa4afc92b9574942c Mon Sep 17 00:00:00 2001 From: Lu Chang Date: Wed, 24 Sep 2025 20:54:48 +0800 Subject: Add HID Usage Page and Table for Power Devices (0x84 - 0x85) --- src/class/hid/hid.h | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index bbc58af80..007a701ae 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -807,7 +807,9 @@ enum { HID_USAGE_PAGE_MEDICAL_INSTRUMENT = 0x40, HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59, HID_USAGE_PAGE_MONITOR = 0x80, // 0x80 - 0x83 - HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 + HID_USAGE_PAGE_POWER = 0x84, + HID_USAGE_PAGE_BATTERY = 0x85, + // 0x86 - 0x87 is reserved for Power Device HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, HID_USAGE_PAGE_SCALE = 0x8d, HID_USAGE_PAGE_MSR = 0x8e, @@ -1595,6 +1597,200 @@ enum { HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71, }; +/// HID Usage Table: Power Device Page (0x84) +enum { + HID_USAGE_POWER_UNDEFINED = 0x00, + HID_USAGE_POWER_I_NAME = 0x01, + HID_USAGE_POWER_PRESENT_STATUS = 0x02, + HID_USAGE_POWER_CHANGED_STATUS = 0x03, + HID_USAGE_POWER_UPS = 0x04, + HID_USAGE_POWER_POWER_SUPPLY = 0x05, + // 06-0F Reserved + HID_USAGE_POWER_BATTERY_SYSTEM = 0x10, + HID_USAGE_POWER_BATTERY_SYSTEM_ID = 0x11, + HID_USAGE_POWER_BATTERY = 0x12, + HID_USAGE_POWER_BATTERY_ID = 0x13, + HID_USAGE_POWER_CHARGER = 0x14, + HID_USAGE_POWER_CHARGER_ID = 0x15, + HID_USAGE_POWER_POWER_CONVERTER = 0x16, + HID_USAGE_POWER_POWER_CONVERTER_ID = 0x17, + HID_USAGE_POWER_OUTLET_SYSTEM = 0x18, + HID_USAGE_POWER_OUTLET_SYSTEM_ID = 0x19, + HID_USAGE_POWER_INPUT = 0x1A, + HID_USAGE_POWER_INPUT_ID = 0x1B, + HID_USAGE_POWER_OUTPUT = 0x1C, + HID_USAGE_POWER_OUTPUT_ID = 0x1D, + HID_USAGE_POWER_FLOW = 0x1E, + HID_USAGE_POWER_FLOW_ID = 0x1F, + HID_USAGE_POWER_OUTLET = 0x20, + HID_USAGE_POWER_OUTLET_ID = 0x21, + HID_USAGE_POWER_GANG = 0x22, + HID_USAGE_POWER_GANG_ID = 0x23, + HID_USAGE_POWER_POWER_SUMMARY = 0x24, + HID_USAGE_POWER_POWER_SUMMARY_ID = 0x25, + // 26-2F Reserved + HID_USAGE_POWER_VOLTAGE = 0x30, + HID_USAGE_POWER_CURRENT = 0x31, + HID_USAGE_POWER_FREQUENCY = 0x32, + HID_USAGE_POWER_APPARENT_POWER = 0x33, + HID_USAGE_POWER_ACTIVE_POWER = 0x34, + HID_USAGE_POWER_PERCENT_LOAD = 0x35, + HID_USAGE_POWER_TEMPERATURE = 0x36, + HID_USAGE_POWER_HUMIDITY = 0x37, + HID_USAGE_POWER_BAD_COUNT = 0x38, + // 39-3F Reserved + HID_USAGE_POWER_CONFIG_VOLTAGE = 0x40, + HID_USAGE_POWER_CONFIG_CURRENT = 0x41, + HID_USAGE_POWER_CONFIG_FREQUENCY = 0x42, + HID_USAGE_POWER_CONFIG_APPARENT_POWER = 0x43, + HID_USAGE_POWER_CONFIG_ACTIVE_POWER = 0x44, + HID_USAGE_POWER_CONFIG_PERCENT_LOAD = 0x45, + HID_USAGE_POWER_CONFIG_TEMPERATURE = 0x46, + HID_USAGE_POWER_CONFIG_HUMIDITY = 0x47, + // 48-4F Reserved + HID_USAGE_POWER_SWITCH_ON_CONTROL = 0x50, + HID_USAGE_POWER_SWITCH_OFF_CONTROL = 0x51, + HID_USAGE_POWER_TOGGLE_CONTROL = 0x52, + HID_USAGE_POWER_LOW_VOLTAGE_TRANSFER = 0x53, + HID_USAGE_POWER_HIGH_VOLTAGE_TRANSFER = 0x54, + HID_USAGE_POWER_DELAY_BEFORE_REBOOT = 0x55, + HID_USAGE_POWER_DELAY_BEFORE_STARTUP = 0x56, + HID_USAGE_POWER_DELAY_BEFORE_SHUTDOWN = 0x57, + HID_USAGE_POWER_TEST = 0x58, + HID_USAGE_POWER_MODULE_RESET = 0x59, + HID_USAGE_POWER_AUDIBLE_ALARM_CONTROL = 0x5A, + // 5B-5F Reserved + HID_USAGE_POWER_PRESENT = 0x60, + HID_USAGE_POWER_GOOD = 0x61, + HID_USAGE_POWER_INTERNAL_FAILURE = 0x62, + HID_USAGE_POWER_VOLTAGE_OUT_OF_RANGE = 0x63, + HID_USAGE_POWER_FREQUENCY_OUT_OF_RANGE = 0x64, + HID_USAGE_POWER_OVERLOAD = 0x65, + HID_USAGE_POWER_OVER_CHARGED = 0x66, + HID_USAGE_POWER_OVER_TEMPERATURE = 0x67, + HID_USAGE_POWER_SHUTDOWN_REQUESTED = 0x68, + HID_USAGE_POWER_SHUTDOWN_IMMINENT = 0x69, + // 6A Reserved + HID_USAGE_POWER_SWITCH_ON_OFF = 0x6B, + HID_USAGE_POWER_SWITCHABLE = 0x6C, + HID_USAGE_POWER_USED = 0x6D, + HID_USAGE_POWER_BOOST = 0x6E, + HID_USAGE_POWER_BUCK = 0x6F, + HID_USAGE_POWER_INITIALIZED = 0x70, + HID_USAGE_POWER_TESTED = 0x71, + HID_USAGE_POWER_AWAITING_POWER = 0x72, + HID_USAGE_POWER_COMMUNICATION_LOST = 0x73, + // 74-FC Reserved + HID_USAGE_POWER_I_MANUFACTURER = 0xFD, + HID_USAGE_POWER_I_PRODUCT = 0xFE, + HID_USAGE_POWER_I_SERIAL_NUMBER = 0xFF +}; + +/// HID Usage Table: Battery System Page (0x85) +enum { + HID_USAGE_BATTERY_UNDEFINED = 0x00, + HID_USAGE_BATTERY_SMB_BATTERY_MODE = 0x01, + HID_USAGE_BATTERY_SMB_BATTERY_STATUS = 0x02, + HID_USAGE_BATTERY_SMB_ALARM_WARNING = 0x03, + HID_USAGE_BATTERY_SMB_CHARGER_MODE = 0x04, + HID_USAGE_BATTERY_SMB_CHARGER_STATUS = 0x05, + HID_USAGE_BATTERY_SMB_CHARGER_SPEC_INFO = 0x06, + HID_USAGE_BATTERY_SMB_SELECTOR_STATE = 0x07, + HID_USAGE_BATTERY_SMB_SELECTOR_PRESETS = 0x08, + HID_USAGE_BATTERY_SMB_SELECTOR_INFO = 0x09, + // 0A-0F Reserved + HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_1 = 0x10, + HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_2 = 0x11, + HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_3 = 0x12, + HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_4 = 0x13, + HID_USAGE_BATTERY_OPTIONAL_MFG_FUNCTION_5 = 0x14, + HID_USAGE_BATTERY_CONNECTION_TO_SMBUS = 0x15, + HID_USAGE_BATTERY_OUTPUT_CONNECTION = 0x16, + HID_USAGE_BATTERY_CHARGER_CONNECTION = 0x17, + HID_USAGE_BATTERY_BATTERY_INSERTION = 0x18, + HID_USAGE_BATTERY_USE_NEXT = 0x19, + HID_USAGE_BATTERY_OK_TO_USE = 0x1A, + HID_USAGE_BATTERY_BATTERY_SUPPORTED = 0x1B, + HID_USAGE_BATTERY_SELECTOR_REVISION = 0x1C, + HID_USAGE_BATTERY_CHARGING_INDICATOR = 0x1D, + // 1E-27 Reserved + HID_USAGE_BATTERY_MANUFACTURER_ACCESS = 0x28, + HID_USAGE_BATTERY_REMAINING_CAPACITY_LIMIT = 0x29, + HID_USAGE_BATTERY_REMAINING_TIME_LIMIT = 0x2A, + HID_USAGE_BATTERY_AT_RATE = 0x2B, + HID_USAGE_BATTERY_CAPACITY_MODE = 0x2C, + HID_USAGE_BATTERY_BROADCAST_TO_CHARGER = 0x2D, + HID_USAGE_BATTERY_PRIMARY_BATTERY = 0x2E, + HID_USAGE_BATTERY_CHARGE_CONTROLLER = 0x2F, + // 30-3F Reserved + HID_USAGE_BATTERY_TERMINATE_CHARGE = 0x40, + HID_USAGE_BATTERY_TERMINATE_DISCHARGE = 0x41, + HID_USAGE_BATTERY_BELOW_REMAINING_CAPACITY_LIMIT = 0x42, + HID_USAGE_BATTERY_REMAINING_TIME_LIMIT_EXPIRED = 0x43, + HID_USAGE_BATTERY_CHARGING = 0x44, + HID_USAGE_BATTERY_DISCHARGING = 0x45, + HID_USAGE_BATTERY_FULLY_CHARGED = 0x46, + HID_USAGE_BATTERY_FULLY_DISCHARGED = 0x47, + HID_USAGE_BATTERY_CONDITIONING_FLAG = 0x48, + HID_USAGE_BATTERY_AT_RATE_OK = 0x49, + HID_USAGE_BATTERY_SMB_ERROR_CODE = 0x4A, + HID_USAGE_BATTERY_NEED_REPLACEMENT = 0x4B, + // 4C-5F Reserved + HID_USAGE_BATTERY_AT_RATE_TIME_TO_FULL = 0x60, + HID_USAGE_BATTERY_AT_RATE_TIME_TO_EMPTY = 0x61, + HID_USAGE_BATTERY_AVERAGE_CURRENT = 0x62, + HID_USAGE_BATTERY_MAX_ERROR = 0x63, + HID_USAGE_BATTERY_RELATIVE_STATE_OF_CHARGE = 0x64, + HID_USAGE_BATTERY_ABSOLUTE_STATE_OF_CHARGE = 0x65, + HID_USAGE_BATTERY_REMAINING_CAPACITY = 0x66, + HID_USAGE_BATTERY_FULL_CHARGE_CAPACITY = 0x67, + HID_USAGE_BATTERY_RUN_TIME_TO_EMPTY = 0x68, + HID_USAGE_BATTERY_AVERAGE_TIME_TO_EMPTY = 0x69, + HID_USAGE_BATTERY_AVERAGE_TIME_TO_FULL = 0x6A, + HID_USAGE_BATTERY_CYCLE_COUNT = 0x6B, + // 6C-7F Reserved + HID_USAGE_BATTERY_BATT_PACK_MODEL_LEVEL = 0x80, + HID_USAGE_BATTERY_INTERNAL_CHARGE_CONTROLLER = 0x81, + HID_USAGE_BATTERY_PRIMARY_BATTERY_SUPPORT = 0x82, + HID_USAGE_BATTERY_DESIGN_CAPACITY = 0x83, + HID_USAGE_BATTERY_SPECIFICATION_INFO = 0x84, + HID_USAGE_BATTERY_MANUFACTURER_DATE = 0x85, + HID_USAGE_BATTERY_SERIAL_NUMBER = 0x86, + HID_USAGE_BATTERY_I_MANUFACTURER_NAME = 0x87, + HID_USAGE_BATTERY_I_DEVICE_NAME = 0x88, + HID_USAGE_BATTERY_I_DEVICE_CHEMISTRY = 0x89, + HID_USAGE_BATTERY_MANUFACTURER_DATA = 0x8A, + HID_USAGE_BATTERY_RECHARGEABLE = 0x8B, + HID_USAGE_BATTERY_WARNING_CAPACITY_LIMIT = 0x8C, + HID_USAGE_BATTERY_CAPACITY_GRANULARITY_1 = 0x8D, + HID_USAGE_BATTERY_CAPACITY_GRANULARITY_2 = 0x8E, + HID_USAGE_BATTERY_I_OEMINFORMATION = 0x8F, + // 90-BF Reserved + HID_USAGE_BATTERY_INHIBIT_CHARGE = 0xC0, + HID_USAGE_BATTERY_ENABLE_POLLING = 0xC1, + HID_USAGE_BATTERY_RESET_TO_ZERO = 0xC2, + // C3-CF Reserved + HID_USAGE_BATTERY_AC_PRESENT = 0xD0, + HID_USAGE_BATTERY_BATTERY_PRESENT = 0xD1, + HID_USAGE_BATTERY_POWER_FAIL = 0xD2, + HID_USAGE_BATTERY_ALARM_INHIBITED = 0xD3, + HID_USAGE_BATTERY_THERMISTOR_UNDER_RANGE = 0xD4, + HID_USAGE_BATTERY_THERMISTOR_HOT = 0xD5, + HID_USAGE_BATTERY_THERMISTOR_COLD = 0xD6, + HID_USAGE_BATTERY_THERMISTOR_OVER_RANGE = 0xD7, + HID_USAGE_BATTERY_VOLTAGE_OUT_OF_RANGE = 0xD8, + HID_USAGE_BATTERY_CURRENT_OUT_OF_RANGE = 0xD9, + HID_USAGE_BATTERY_CURRENT_NOT_REGULATED = 0xDA, + HID_USAGE_BATTERY_VOLTAGE_NOT_REGULATED = 0xDB, + HID_USAGE_BATTERY_MASTER_MODE = 0xDC, + // DD-EF Reserved + HID_USAGE_BATTERY_CHARGER_SELECTOR_SUPPORT = 0xF0, + HID_USAGE_BATTERY_CHARGER_SPEC = 0xF1, + HID_USAGE_BATTERY_LEVEL_2 = 0xF2, + HID_USAGE_BATTERY_LEVEL_3 = 0xF3 + // F2-CF Reserved +}; + /// HID Usage Table: FIDO Alliance Page (0xF1D0) enum { HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection -- cgit v1.3.1 From f4a3c1a1ba177abc97b0dfad6df8ea622cd97e64 Mon Sep 17 00:00:00 2001 From: Lu Chang Date: Thu, 25 Sep 2025 08:34:18 +0800 Subject: Fix incorrect comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/class/hid/hid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 007a701ae..b69f623a0 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -1788,7 +1788,7 @@ enum { HID_USAGE_BATTERY_CHARGER_SPEC = 0xF1, HID_USAGE_BATTERY_LEVEL_2 = 0xF2, HID_USAGE_BATTERY_LEVEL_3 = 0xF3 - // F2-CF Reserved + // F4-FF Reserved }; /// HID Usage Table: FIDO Alliance Page (0xF1D0) -- cgit v1.3.1 From f8397717ea3f476c810ae3ce27ec4c866b305506 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 25 Sep 2025 15:30:37 +0700 Subject: implement MTP_OP_SEND_OBJECT_INFO, refactor fs example --- examples/device/mtp/src/mtp_fs_example.c | 300 +++++++++++++++++++++---------- src/class/mtp/mtp.h | 29 +-- src/class/mtp/mtp_device.c | 96 ++++++---- src/class/mtp/mtp_device.h | 20 ++- 4 files changed, 301 insertions(+), 144 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 7e19fa75c..d6d7f1907 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -51,41 +51,55 @@ typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION) // RAM FILESYSTEM //--------------------------------------------------------------------+ #define FS_MAX_FILE_COUNT 5UL -#define FS_MAX_CAPACITY_BYTES (2 * 1024UL) -#define FS_MAX_FILENAME_LEN 16UL +#define FS_MAX_CAPACITY_BYTES (4 * 1024UL) +#define FS_MAX_FILENAME_LEN 16 #define FS_FIXED_DATETIME "20250808T173500.0" // "YYYYMMDDTHHMMSS.s" #define README_TXT_CONTENT "TinyUSB MTP on RAM Filesystem example" typedef struct { - // uint32_t handle; - char name[FS_MAX_FILENAME_LEN]; - mtp_object_formats_t format; + uint16_t name[FS_MAX_FILENAME_LEN]; + mtp_object_formats_t object_format; + uint16_t protection_status; + uint32_t image_pix_width; + uint32_t image_pix_height; + uint32_t image_bit_depth; + uint32_t parent; - bool association; + uint8_t association_type; + uint32_t size; uint8_t* data; -} fs_object_info_t; + +} fs_file_t; // object data buffer (excluding 2 predefined files) uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; uint8_t fs_buf_head = 0; // simple allocation pointer // Files system, handle is index + 1 -static fs_object_info_t fs_objects[FS_MAX_FILE_COUNT] = { +static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { { - .name = "readme.txt", - .format = MTP_OBJ_FORMAT_TEXT, + .name = { 'r', 'e', 'a', 'd', 'm', 'e', '.', 't', 'x', 't', 0 }, // readme.txt + .object_format = MTP_OBJ_FORMAT_TEXT, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, + .image_pix_width = 0, + .image_pix_height = 0, + .image_bit_depth = 0, .parent = 0, - .association = false, + .association_type = MTP_ASSOCIATION_UNDEFINED, .data = (uint8_t*) README_TXT_CONTENT, .size = sizeof(README_TXT_CONTENT) }, { - .name = "tinyusb.png", - .format = MTP_OBJ_FORMAT_PNG, + .name = { 't', 'i', 'n', 'y', 'u', 's', 'b', '.', 'p', 'n', 'g', 0 }, // "tinyusb.png" + .object_format = MTP_OBJ_FORMAT_PNG, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, + .image_pix_width = 128, + .image_pix_height = 64, + .image_bit_depth = 32, .parent = 0, - .association = false, + .association_type = MTP_ASSOCIATION_UNDEFINED, .data = logo_bin, .size = logo_len, } @@ -114,34 +128,77 @@ enum { }; static bool is_session_opened = false; - -//--------------------------------------------------------------------+ -// INTERNAL FUNCTIONS -//--------------------------------------------------------------------+ +static uint32_t send_obj_handle = 0; // Get pointer to object info from handle -static inline fs_object_info_t* fs_get_object(uint32_t handle) { +static inline fs_file_t* fs_get_file(uint32_t handle) { if (handle == 0 || handle > FS_MAX_FILE_COUNT) { return NULL; } return &fs_objects[handle-1]; } +static inline bool fs_file_exist(fs_file_t* f) { + return f->name[0] != 0; +} + // Get the number of allocated nodes in filesystem -uint32_t fs_get_object_count(void) { +static uint32_t fs_get_file_count(void) { uint32_t count = 0; - for (unsigned int i = 0; i < FS_MAX_FILE_COUNT; i++) { - if (fs_objects[i].name[0] != 0) { + for (size_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + if (fs_file_exist(&fs_objects[i])) { count++; } } return count; } +static inline fs_file_t* fs_create_file(void) { + for (size_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + fs_file_t* f = &fs_objects[i]; + if (!fs_file_exist(f)) { + send_obj_handle = i + 1; + return f; + } + } +} + +// simple malloc +static inline uint8_t* fs_malloc(size_t size) { + if (fs_buf_head + size > FS_MAX_CAPACITY_BYTES) { + return NULL; + } + uint8_t* ptr = &fs_buf[fs_buf_head]; + fs_buf_head += size; + return ptr; +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { - mtp_container_info_t* reply = &cb_data->reply; - reply->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - tud_mtp_response_send(reply); + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* resp = &cb_data->io_container; + switch (command->code) { + case MTP_OP_SEND_OBJECT_INFO: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp->header->code = MTP_RESP_GENERAL_ERROR; + break; + } + // parameter is: storage id, parent handle, new handle + mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); + mtp_container_add_uint32(resp, f->parent); + mtp_container_add_uint32(resp, send_obj_handle); + resp->header->code = MTP_RESP_OK; + break; + } + + default: + resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; + break; + } + tud_mtp_response_send(resp); return 0; } @@ -150,24 +207,59 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { return 0; // nothing to do } -int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { - // only a few command that need more data e.g GetObject and SendObject - const mtp_container_command_t* command = cb_data->command; - mtp_container_info_t* reply = &cb_data->reply; +int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; uint32_t resp_code = 0; switch (command->code) { case MTP_OP_GET_OBJECT: { + // File contents span over multiple xfers const uint32_t obj_handle = command->params[0]; - fs_object_info_t* obj = fs_get_object(obj_handle); - if (obj == NULL) { + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; } else { // file contents offset is xferred byte minus header size const uint32_t offset = cb_data->xferred_bytes - sizeof(mtp_container_header_t); - const uint32_t xact_len = tu_min32(obj->size - offset, reply->payload_size); - memcpy(reply->payload, obj->data + offset, xact_len); - tud_mtp_data_send(&cb_data->reply); + const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_size); + memcpy(io_container->payload, f->data + offset, xact_len); + tud_mtp_data_send(&cb_data->io_container); + } + break; + } + + case MTP_OP_SEND_OBJECT_INFO: { + mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload; + if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) { + resp_code = MTP_RESP_INVALID_STORAGE_ID; + break; + } + + if (obj_info->parent_object) { + fs_file_t* parent = fs_get_file(obj_info->parent_object); + if (parent == NULL || !parent->association_type) { + resp_code = MTP_RESP_INVALID_PARENT_OBJECT; + break; + } + } + + fs_file_t* f = fs_create_file(); + f->object_format = obj_info->object_format; + f->protection_status = obj_info->protection_status; + f->image_pix_width = obj_info->image_pix_width; + f->image_pix_height = obj_info->image_pix_height; + f->image_bit_depth = obj_info->image_bit_depth; + f->parent = obj_info->parent_object; + f->association_type = obj_info->association_type; + f->size = obj_info->object_compressed_size; + f->data = fs_malloc(f->size); + if (f->data == NULL) { + resp_code = MTP_RESP_STORE_FULL; + break; } + uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); + mtp_container_get_string(buf,f->name); + // ignore date created/modified/keywords break; } @@ -178,26 +270,26 @@ int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data) { // send response if needed if (resp_code != 0) { - reply->header->code = resp_code; - tud_mtp_response_send(reply); + io_container->header->code = resp_code; + tud_mtp_response_send(io_container); } return 0; // 0 mean data/response is sent already } int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { - const mtp_container_command_t* command = cb_data->command; - mtp_container_info_t* reply = &cb_data->reply; + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; uint32_t resp_code = 0; switch (command->code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MANUFACTURER); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_MODEL); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_VERSION); - mtp_container_add_cstring(&cb_data->reply, DEV_INFO_SERIAL); + mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER); + mtp_container_add_cstring(io_container, DEV_INFO_MODEL); + mtp_container_add_cstring(io_container, DEV_INFO_VERSION); + mtp_container_add_cstring(io_container, DEV_INFO_SERIAL); - tud_mtp_data_send(&cb_data->reply); + tud_mtp_data_send(io_container); break; } @@ -220,9 +312,9 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { break; case MTP_OP_GET_STORAGE_IDS: { - uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; // physical = 1, logical = 1 - mtp_container_add_auint32(&cb_data->reply, 1, storage_ids); - tud_mtp_data_send(&cb_data->reply); + uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; + mtp_container_add_auint32(io_container, 1, storage_ids); + tud_mtp_data_send(io_container); break; } @@ -230,10 +322,10 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const uint32_t storage_id = command->params[0]; TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); // update storage info with current free space - storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_object_count(); + storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count(); storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; - mtp_container_add_raw(&cb_data->reply, &storage_info, sizeof(storage_info)); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info)); + tud_mtp_data_send(io_container); break; } @@ -245,11 +337,11 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: device_prop_header.datatype = MTP_DATA_TYPE_STR; device_prop_header.get_set = MTP_MODE_GET; - mtp_container_add_raw(&cb_data->reply, &device_prop_header, sizeof(device_prop_header)); - mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // factory - mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); // current - mtp_container_add_uint8(&cb_data->reply, 0); // no form - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header)); + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current + mtp_container_add_uint8(io_container, 0); // no form + tud_mtp_data_send(io_container); break; default: @@ -263,8 +355,8 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const uint16_t dev_prop_code = (uint16_t) command->params[0]; switch (dev_prop_code) { case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - mtp_container_add_cstring(&cb_data->reply, DEV_PROP_FRIENDLY_NAME); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); + tud_mtp_data_send(io_container); break; default: @@ -285,66 +377,82 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; uint32_t count = 0; for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { - fs_object_info_t* obj = &fs_objects[i]; - if (obj->name[0] != 0 && - (parent_handle == obj->parent || (parent_handle == 0xFFFFFFFF && obj->parent == 0))) { - handles[count++] = i + 1; // handle is index + 1 + fs_file_t* f = &fs_objects[i]; + if (fs_file_exist(f) && + (parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) { + handles[count++] = i + 1; // handle is index + 1 } } - mtp_container_add_auint32(&cb_data->reply, count, handles); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_auint32(io_container, count, handles); + tud_mtp_data_send(io_container); } break; } case MTP_OP_GET_OBJECT_INFO: { const uint32_t obj_handle = command->params[0]; - fs_object_info_t* obj = fs_get_object(obj_handle); - if (obj == NULL) { + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; } else { mtp_object_info_header_t obj_info_header = { .storage_id = SUPPORTED_STORAGE_ID, - .object_format = obj->format, + .object_format = f->object_format, .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, - .object_compressed_size = obj->size, + .object_compressed_size = f->size, .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, .thumb_compressed_size = 0, .thumb_pix_width = 0, .thumb_pix_height = 0, - .image_pix_width = 128, - .image_pix_height = 64, - .image_bit_depth = 32, - .parent_object = obj->parent, - .association_type = MTP_ASSOCIATION_UNDEFINED, + .image_pix_width = f->image_pix_width, + .image_pix_height = f->image_pix_height, + .image_bit_depth = f->image_bit_depth, + .parent_object = f->parent, + .association_type = f->association_type, .association_desc = 0, .sequence_number = 0 }; - mtp_container_add_raw(&cb_data->reply, &obj_info_header, sizeof(obj_info_header)); - mtp_container_add_cstring(&cb_data->reply, obj->name); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, FS_FIXED_DATETIME); - mtp_container_add_cstring(&cb_data->reply, ""); // keywords, not used + mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header)); + mtp_container_add_string(io_container, f->name); + mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); + mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); + mtp_container_add_cstring(io_container, ""); // keywords, not used - tud_mtp_data_send(&cb_data->reply); + tud_mtp_data_send(io_container); } break; } case MTP_OP_GET_OBJECT: { const uint32_t obj_handle = command->params[0]; - fs_object_info_t* obj = fs_get_object(obj_handle); + fs_file_t* obj = fs_get_file(obj_handle); if (obj == NULL) { resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; } else { // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here // the rest will be sent in tud_mtp_data_more_cb - mtp_container_add_raw(&cb_data->reply, obj->data, obj->size); - tud_mtp_data_send(&cb_data->reply); + mtp_container_add_raw(io_container, obj->data, obj->size); + tud_mtp_data_send(io_container); + } + break; + } + + case MTP_OP_SEND_OBJECT_INFO: { + const uint32_t storage_id = command->params[0]; + const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root + if (!is_session_opened) { + resp_code = MTP_RESP_SESSION_NOT_OPEN; + } else if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { + resp_code = MTP_RESP_INVALID_STORAGE_ID; + } else { + tud_mtp_data_receive(io_container); } break; } + case MTP_OP_SEND_OBJECT: + break; + default: resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; break; @@ -352,8 +460,8 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { // send response if needed if (resp_code != 0) { - reply->header->code = resp_code; - tud_mtp_response_send(reply); + io_container->header->code = resp_code; + tud_mtp_response_send(io_container); } return 0; @@ -382,7 +490,7 @@ mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t* new_object_handle, const mtp_object_info_header_t* info) { - fs_object_info_t* obj = NULL; + fs_file_t* obj = NULL; if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); @@ -405,12 +513,12 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p // Ensure we are not creating an orphaned object outside root if (parent_object != 0) { - obj = fs_get_object(parent_object); + obj = fs_get_file(parent_object); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", parent_object); return MTP_RESP_INVALID_PARENT_OBJECT; } - if (!obj->association) { + if (!obj->association_type) { TU_LOG1("Parent %ld is not an association\r\n", parent_object); return MTP_RESP_INVALID_PARENT_OBJECT; } @@ -434,7 +542,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p obj->handle = ++_fs_operation.last_handle; obj->parent = parent_object; obj->size = info->object_compressed_size; - obj->association = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; + obj->association_type = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; // Extract variable data uint16_t offset_data = sizeof(mtp_object_info_header_t); @@ -443,7 +551,7 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", - obj->association ? "association" : "object", + obj->association_type ? "association" : "object", obj->name, obj->handle, obj->parent, obj->size); *new_object_handle = obj->handle; // Initialize operation @@ -453,9 +561,9 @@ mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t p } mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { - fs_object_info_t* obj; + fs_file_t* obj; - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); return MTP_RESP_INVALID_OBJECT_HANDLE; @@ -481,25 +589,25 @@ mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_ } mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) { - fs_object_info_t* obj; + fs_file_t* obj; if (new_parent_object_handle == 0xFFFFFFFF) new_parent_object_handle = 0; // Ensure we are not moving to an nonexisting parent if (new_parent_object_handle != 0) { - obj = fs_get_object(new_parent_object_handle); + obj = fs_get_file(new_parent_object_handle); if (obj == NULL) { TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); return MTP_RESP_INVALID_PARENT_OBJECT; } - if (!obj->association) { + if (!obj->association_type) { TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); return MTP_RESP_INVALID_PARENT_OBJECT; } } - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -511,7 +619,7 @@ mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_ } mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { - fs_object_info_t* obj; + fs_file_t* obj; if (_fs_operation.session_id == 0) { TU_LOG1("ERR: Session not open\r\n"); @@ -522,7 +630,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { object_handle = 0; if (object_handle != 0) { - obj = fs_get_object(object_handle); + obj = fs_get_file(object_handle); if (obj == NULL) { TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); @@ -532,7 +640,7 @@ mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { TU_LOG1("Delete object with handle %ld\r\n", object_handle); } - if (object_handle == 0 || obj->association) { + if (object_handle == 0 || obj->association_type) { // Delete also children for (unsigned int i = 0; i < FS_MAX_NODES; i++) { obj = &fs_objects[i]; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index f1b1eaf23..e713d5c23 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -676,16 +676,12 @@ TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { - uint32_t len; - uint16_t type; - uint16_t code; - uint32_t transaction_id; + mtp_container_header_t header; // union { uint32_t data[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)) / sizeof(uint32_t)]; // uint8_t data[CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)]; // }; } mtp_generic_container_t; -TU_VERIFY_STATIC(sizeof(mtp_generic_container_t) == CFG_TUD_MTP_EP_BUFSIZE, "size is not correct"); typedef struct { mtp_container_header_t* header; @@ -801,7 +797,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_next(mtp_cont // only add_raw does partial copy TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) { uint8_t* buf = mtp_container_payload_next(p_container); - const uint32_t added_len = tu_min32(len, sizeof(mtp_generic_container_t) - p_container->header->len); + const uint32_t added_len = tu_min32(len, CFG_TUD_MTP_EP_BUFSIZE - p_container->header->len); if (added_len > 0) { memcpy(buf, data, added_len); } @@ -811,7 +807,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_container_info_t* p_container, uint8_t scalar_size, uint32_t count, const void* data) { const uint32_t added_len = 4 + count * scalar_size; - TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); tu_unaligned_write32(buf, count); @@ -824,9 +820,13 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_array(mtp_contain return added_len; } -TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint8_t count, uint16_t* utf16) { +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_container_info_t* p_container, uint16_t* utf16) { + uint8_t count = 0; + while (utf16[count]) { + count++; + } const uint32_t added_len = 1 + 2 * count; - TU_ASSERT(p_container->header->len + added_len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); *buf++ = count; @@ -840,7 +840,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_contai TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_container_info_t* p_container, const char* str) { const uint8_t len = (uint8_t) (strlen(str) + 1); // include null - TU_ASSERT(p_container->header->len + 1 + 2 * len < sizeof(mtp_generic_container_t), 0); + TU_ASSERT(p_container->header->len + 1 + 2 * len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); if (len == 1) { @@ -894,6 +894,15 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_conta return mtp_container_add_array(p_container, sizeof(uint32_t), count, data); } +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) { + uint8_t nchars = *buf++; + memcpy(buf, utf16, nchars * 2); + return 1 + nchars * 2; +} + #ifdef __cplusplus } #endif diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 358114655..db562d0c7 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -65,7 +65,7 @@ typedef struct { uint32_t session_id; mtp_container_command_t command; - mtp_container_header_t reply_header; + mtp_container_header_t io_header; } mtpd_interface_t; typedef struct { @@ -75,7 +75,7 @@ typedef struct { //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ -static int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); +static void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); static mtp_phase_type_t mtpd_handle_data(void); static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); @@ -253,28 +253,39 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -bool tud_mtp_data_send(mtp_container_info_t* p_container) { +static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { mtpd_interface_t* p_mtp = &_mtpd_itf; if (p_mtp->phase == MTP_PHASE_COMMAND) { // 1st data block: header + payload p_mtp->phase = MTP_PHASE_DATA; - p_mtp->total_len = p_container->header->len; p_mtp->xferred_len = 0; - p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; - p_mtp->reply_header = *p_container->header; // save header for subsequent data + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + p_mtp->total_len = p_container->header->len; + p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->header->transaction_id = p_mtp->command.transaction_id; + p_mtp->io_header = *p_container->header; // save header for subsequent data + } else { + p_mtp->total_len = CFG_TUD_MTP_EP_BUFSIZE; + } } else { // subsequent data block: payload only TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); } const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, xact_len)); - + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); return true; } +bool tud_mtp_data_send(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_in); +} + +bool tud_mtp_data_receive(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_out); +} + bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; @@ -301,23 +312,25 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t tud_mtp_cb_data_t cb_data; cb_data.idx = 0; - cb_data.command = &p_mtp->command; - cb_data.reply.header = (mtp_container_header_t*) p_container; - cb_data.reply.payload32 = p_container->data; - cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); + cb_data.command_container = &p_mtp->command; + cb_data.io_container.header = &p_container->header; + cb_data.io_container.payload32 = p_container->data; + cb_data.io_container.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); cb_data.xferred_bytes = 0; cb_data.xfer_result = event; switch (p_mtp->phase) { case MTP_PHASE_IDLE: // received new command - TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); + TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); p_mtp->phase = MTP_PHASE_COMMAND; TU_ATTR_FALLTHROUGH; // handle in the next case case MTP_PHASE_COMMAND: { memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command - if (mtpd_handle_cmd(p_mtp, &cb_data) < 0) { + p_container->header.len = sizeof(mtp_container_header_t); // default container to header only + process_cmd(p_mtp, &cb_data); + if (tud_mtp_command_received_cb(&cb_data) < 0) { p_mtp->phase = MTP_PHASE_ERROR; } break; @@ -328,18 +341,44 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t p_mtp->xferred_len += xferred_bytes; cb_data.xferred_bytes = p_mtp->xferred_len; - // transfer complete if ZLP or short packet or overflow + bool is_complete = false; + // complete if ZLP or short packet or overflow if (xferred_bytes == 0 || // ZLP (xferred_bytes & (bulk_mps - 1)) || // short packet p_mtp->xferred_len > p_mtp->total_len) { - cb_data.reply.header->len = sizeof(mtp_container_header_t); - tud_mtp_data_complete_cb(&cb_data); + is_complete = true; + } + + const mtp_container_info_t headerless_packet = { + .header = &p_mtp->io_header, + .payload = _mtpd_epbuf.buf, + .payload_size = CFG_TUD_MTP_EP_BUFSIZE + }; + + if (ep_addr == p_mtp->ep_in) { + // Data In + if (is_complete) { + cb_data.io_container.header->len = sizeof(mtp_container_header_t); + tud_mtp_data_complete_cb(&cb_data); + } else { + // 2nd+ packet: payload only + cb_data.io_container = headerless_packet; + tud_mtp_data_xfer_cb(&cb_data); + } } else { - // payload only packet - cb_data.reply.header = &p_mtp->reply_header; - cb_data.reply.payload = (uint8_t*) p_container; - cb_data.reply.payload_size = CFG_TUD_MTP_EP_BUFSIZE; - tud_mtp_data_more_cb(&cb_data); + // Data Out + if (p_mtp->xferred_len == xferred_bytes) { + // 1st OUT packet: header + payload + p_mtp->io_header = p_container->header; // save header for subsequent transaction + } else { + // 2nd+ packet: payload only + cb_data.io_container = headerless_packet; + } + tud_mtp_data_xfer_cb(&cb_data); + if (is_complete) { + cb_data.io_container.header->len = sizeof(mtp_container_header_t); + tud_mtp_data_complete_cb(&cb_data); + } } break; } @@ -445,11 +484,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // MTPD Internal functionality //--------------------------------------------------------------------+ -// Decode command and prepare response -int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { - cb_data->reply.header->len = sizeof(mtp_container_header_t); - - // pre-processed commands +// pre-processed commands +void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { switch (p_mtp->command.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { @@ -491,15 +527,13 @@ int32_t mtpd_handle_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; } #endif - mtp_container_add_raw(&cb_data->reply, &dev_info, sizeof(tud_mtp_device_info_t)); + mtp_container_add_raw(&cb_data->io_container, &dev_info, sizeof(tud_mtp_device_info_t)); break; } default: break; } - - return tud_mtp_command_received_cb(cb_data); } #if 0 diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 28c01ddc6..7b6ae9e0f 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -38,11 +38,11 @@ typedef struct { uint8_t idx; // mtp instance - const mtp_container_command_t* command; - mtp_container_info_t reply; + const mtp_container_command_t* command_container; + mtp_container_info_t io_container; tusb_xfer_result_t xfer_result; - uint32_t xferred_bytes; + uint32_t xferred_bytes; // number of bytes transferred so far in this phase } tud_mtp_cb_data_t; // Number of supported operations, events, device properties, capture formats, playback formats @@ -94,8 +94,14 @@ typedef struct { //--------------------------------------------------------------------+ // Application API //--------------------------------------------------------------------+ + +// send data phase bool tud_mtp_data_send(mtp_container_info_t* p_container); -// bool tud_mtp_block_data_receive(); + +// receive data phase +bool tud_mtp_data_receive(mtp_container_info_t* p_container); + +// send response bool tud_mtp_response_send(mtp_container_info_t* p_container); //--------------------------------------------------------------------+ @@ -112,10 +118,10 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container); */ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); -// Invoked when a data packet is received/sent, and more data is expected -int32_t tud_mtp_data_more_cb(tud_mtp_cb_data_t* cb_data); +// Invoked when a data packet is transferred, and more data is expected +int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data); -// Invoked when data phase is complete +// Invoked when all bytes in DATA phase is complete. A response packet is expected int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); // Invoked when response phase is complete -- cgit v1.3.1 From c9a8330081b59c05b901889d405ed7b9648c0947 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 25 Sep 2025 17:43:25 +0700 Subject: implement send object command, able to create new file --- examples/device/mtp/src/mtp_fs_example.c | 332 ++++++++++++------------------- src/class/mtp/mtp.h | 6 +- src/class/mtp/mtp_device.c | 183 +++-------------- src/class/mtp/mtp_device.h | 2 +- 4 files changed, 158 insertions(+), 365 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index d6d7f1907..468fc9146 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -176,107 +176,6 @@ static inline uint8_t* fs_malloc(size_t size) { //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ -int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { - const mtp_container_command_t* command = cb_data->command_container; - mtp_container_info_t* resp = &cb_data->io_container; - switch (command->code) { - case MTP_OP_SEND_OBJECT_INFO: { - fs_file_t* f = fs_get_file(send_obj_handle); - if (f == NULL) { - resp->header->code = MTP_RESP_GENERAL_ERROR; - break; - } - // parameter is: storage id, parent handle, new handle - mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); - mtp_container_add_uint32(resp, f->parent); - mtp_container_add_uint32(resp, send_obj_handle); - resp->header->code = MTP_RESP_OK; - break; - } - - default: - resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - break; - } - tud_mtp_response_send(resp); - return 0; -} - -int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { - (void) cb_data; - return 0; // nothing to do -} - -int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { - const mtp_container_command_t* command = cb_data->command_container; - mtp_container_info_t* io_container = &cb_data->io_container; - uint32_t resp_code = 0; - switch (command->code) { - case MTP_OP_GET_OBJECT: { - // File contents span over multiple xfers - const uint32_t obj_handle = command->params[0]; - fs_file_t* f = fs_get_file(obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - // file contents offset is xferred byte minus header size - const uint32_t offset = cb_data->xferred_bytes - sizeof(mtp_container_header_t); - const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_size); - memcpy(io_container->payload, f->data + offset, xact_len); - tud_mtp_data_send(&cb_data->io_container); - } - break; - } - - case MTP_OP_SEND_OBJECT_INFO: { - mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload; - if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) { - resp_code = MTP_RESP_INVALID_STORAGE_ID; - break; - } - - if (obj_info->parent_object) { - fs_file_t* parent = fs_get_file(obj_info->parent_object); - if (parent == NULL || !parent->association_type) { - resp_code = MTP_RESP_INVALID_PARENT_OBJECT; - break; - } - } - - fs_file_t* f = fs_create_file(); - f->object_format = obj_info->object_format; - f->protection_status = obj_info->protection_status; - f->image_pix_width = obj_info->image_pix_width; - f->image_pix_height = obj_info->image_pix_height; - f->image_bit_depth = obj_info->image_bit_depth; - f->parent = obj_info->parent_object; - f->association_type = obj_info->association_type; - f->size = obj_info->object_compressed_size; - f->data = fs_malloc(f->size); - if (f->data == NULL) { - resp_code = MTP_RESP_STORE_FULL; - break; - } - uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); - mtp_container_get_string(buf,f->name); - // ignore date created/modified/keywords - break; - } - - default: - resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; - break; - } - - // send response if needed - if (resp_code != 0) { - io_container->header->code = resp_code; - tud_mtp_response_send(io_container); - } - - return 0; // 0 mean data/response is sent already -} - int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; @@ -425,13 +324,13 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { case MTP_OP_GET_OBJECT: { const uint32_t obj_handle = command->params[0]; - fs_file_t* obj = fs_get_file(obj_handle); - if (obj == NULL) { + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; } else { // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here // the rest will be sent in tud_mtp_data_more_cb - mtp_container_add_raw(io_container, obj->data, obj->size); + mtp_container_add_raw(io_container, f->data, f->size); tud_mtp_data_send(io_container); } break; @@ -450,8 +349,16 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { break; } - case MTP_OP_SEND_OBJECT: + case MTP_OP_SEND_OBJECT: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + io_container->header->len += f->size; + tud_mtp_data_receive(io_container); + } break; + } default: resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; @@ -467,124 +374,143 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { return 0; } -//--------------------------------------------------------------------+ -// API -//--------------------------------------------------------------------+ -#if 0 -mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - if (storage_id != SUPPORTED_STORAGE_ID) { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } +int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; + uint32_t resp_code = 0; + switch (command->code) { + case MTP_OP_GET_OBJECT: { + // File contents span over multiple xfers + const uint32_t obj_handle = command->params[0]; + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + // file contents offset is xferred byte minus header size + const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t); + const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_bytes); + memcpy(io_container->payload, f->data + offset, xact_len); + tud_mtp_data_send(io_container); + } + break; + } - // Simply deallocate all entries - for (unsigned int i = 0; i < FS_MAX_NODES; i++) - fs_objects[i].allocated = false; - TU_LOG1("Format completed\r\n"); - return MTP_RESP_OK; -} + case MTP_OP_SEND_OBJECT_INFO: { + mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload; + if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) { + resp_code = MTP_RESP_INVALID_STORAGE_ID; + break; + } -mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, - uint32_t* new_object_handle, const mtp_object_info_header_t* info) { - fs_file_t* obj = NULL; + if (obj_info->parent_object) { + fs_file_t* parent = fs_get_file(obj_info->parent_object); + if (parent == NULL || !parent->association_type) { + resp_code = MTP_RESP_INVALID_PARENT_OBJECT; + break; + } + } - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - // Accept command on default storage - if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; + fs_file_t* f = fs_create_file(); + f->object_format = obj_info->object_format; + f->protection_status = obj_info->protection_status; + f->image_pix_width = obj_info->image_pix_width; + f->image_pix_height = obj_info->image_pix_height; + f->image_bit_depth = obj_info->image_bit_depth; + f->parent = obj_info->parent_object; + f->association_type = obj_info->association_type; + f->size = obj_info->object_compressed_size; + f->data = fs_malloc(f->size); + if (f->data == NULL) { + resp_code = MTP_RESP_STORE_FULL; + break; + } + uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); + mtp_container_get_string(buf, f->name); + // ignore date created/modified/keywords + break; + } + + case MTP_OP_SEND_OBJECT: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; + } else { + // file contents offset is total xferred minus header size minus last received chunk + const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) - io_container->payload_bytes; + memcpy(f->data + offset, io_container->payload, io_container->payload_bytes); + tud_mtp_data_receive(io_container); // receive more data if needed + } + break; + } + + default: + resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; + break; } - if (info->object_compressed_size > FS_MAX_NODE_BYTES) { - TU_LOG1("Object size %ld is more than maximum %ld\r\n", info->object_compressed_size, FS_MAX_NODE_BYTES); - return MTP_RESP_STORE_FULL; + // send response if needed + if (resp_code != 0) { + io_container->header->code = resp_code; + tud_mtp_response_send(io_container); } - // Request for objects with no parent (0xFFFFFFFF) are considered root objects - if (parent_object == 0xFFFFFFFF) - parent_object = 0; + return 0; // 0 mean data/response is sent already +} - // Ensure we are not creating an orphaned object outside root - if (parent_object != 0) { - obj = fs_get_file(parent_object); - if (obj == NULL) { - TU_LOG1("Parent %ld does not exist\r\n", parent_object); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - if (!obj->association_type) { - TU_LOG1("Parent %ld is not an association\r\n", parent_object); - return MTP_RESP_INVALID_PARENT_OBJECT; +int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* resp = &cb_data->io_container; + switch (command->code) { + case MTP_OP_SEND_OBJECT_INFO: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp->header->code = MTP_RESP_GENERAL_ERROR; + break; + } + // parameter is: storage id, parent handle, new handle + mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); + mtp_container_add_uint32(resp, f->parent); + mtp_container_add_uint32(resp, send_obj_handle); + resp->header->code = MTP_RESP_OK; + break; } - } - // Search for first free object - for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - if (!fs_objects[i].allocated) { - obj = &fs_objects[i]; + case MTP_OP_SEND_OBJECT: + resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; break; - } - } - if (obj == NULL) { - TU_LOG1("No space left on device\r\n"); - return MTP_RESP_STORE_FULL; + default: + resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; + break; } - // Fill-in structure - obj->allocated = true; - obj->handle = ++_fs_operation.last_handle; - obj->parent = parent_object; - obj->size = info->object_compressed_size; - obj->association_type = info->object_format == MTP_OBJ_FORMAT_ASSOCIATION; - - // Extract variable data - uint16_t offset_data = sizeof(mtp_object_info_header_t); - mtpd_gct_get_string(&offset_data, obj->name, FS_MAX_NODE_NAME_LEN); - mtpd_gct_get_string(&offset_data, obj->created, FS_ISODATETIME_LEN); - mtpd_gct_get_string(&offset_data, obj->modified, FS_ISODATETIME_LEN); - - TU_LOG1("Create %s %s with handle %ld, parent %ld and size %ld\r\n", - obj->association_type ? "association" : "object", - obj->name, obj->handle, obj->parent, obj->size); - *new_object_handle = obj->handle; - // Initialize operation - _fs_operation.write_handle = obj->handle; - _fs_operation.write_pos = 0; - return MTP_RESP_OK; + tud_mtp_response_send(resp); + return 0; } -mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t* buffer, uint32_t size) { - fs_file_t* obj; +int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; + return 0; // nothing to do +} - obj = fs_get_file(object_handle); - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; +//--------------------------------------------------------------------+ +// API +//--------------------------------------------------------------------+ +#if 0 +mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { + if (_fs_operation.session_id == 0) { + TU_LOG1("ERR: Session not open\r\n"); + return MTP_RESP_SESSION_NOT_OPEN; } - // It's a requirement that this command is preceded by a write info - if (object_handle != _fs_operation.write_handle) { - TU_LOG1("ERR: Object %ld not open for write\r\n", object_handle); - return MTP_RESP_NO_VALID_OBJECTINFO; + if (storage_id != SUPPORTED_STORAGE_ID) { + TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); + return MTP_RESP_INVALID_STORAGE_ID; } - TU_LOG1("Write object %ld: data chunk at %ld/%ld bytes at offset %ld\r\n", object_handle, _fs_operation.write_pos, - obj->size, size); - TU_ASSERT(obj->size >= _fs_operation.write_pos + size, MTP_RESP_INCOMPLETE_TRANSFER); - if (_fs_operation.write_pos + size < FS_MAX_NODE_BYTES) - memcpy(&obj->data[_fs_operation.write_pos], buffer, size); - _fs_operation.write_pos += size; - // Write operation completed - if (_fs_operation.write_pos == obj->size) { - _fs_operation.write_handle = 0; - _fs_operation.write_pos = 0; - } + // Simply deallocate all entries + for (unsigned int i = 0; i < FS_MAX_NODES; i++) + fs_objects[i].allocated = false; + TU_LOG1("Format completed\r\n"); return MTP_RESP_OK; } diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index e713d5c23..15cf1bfc6 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -59,8 +59,6 @@ typedef enum { MTP_PHASE_IDLE = 0, MTP_PHASE_COMMAND, MTP_PHASE_DATA, - MTP_PHASE_DATA_IN, - MTP_PHASE_DATA_OUT, MTP_PHASE_RESPONSE, MTP_PHASE_RESPONSE_QUEUED, MTP_PHASE_ERROR, @@ -690,7 +688,7 @@ typedef struct { uint16_t* payload16; uint32_t* payload32; }; - uint32_t payload_size; + uint32_t payload_bytes; // available bytes for read/write } mtp_container_info_t; #define mtp_string_t(_nchars) \ @@ -899,7 +897,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_conta //--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) { uint8_t nchars = *buf++; - memcpy(buf, utf16, nchars * 2); + memcpy(utf16, buf, 2 * nchars); return 1 + nchars * 2; } diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index db562d0c7..6d1c7b29b 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -266,7 +266,8 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { p_container->header->transaction_id = p_mtp->command.transaction_id; p_mtp->io_header = *p_container->header; // save header for subsequent data } else { - p_mtp->total_len = CFG_TUD_MTP_EP_BUFSIZE; + // OUT transfer: total length is at least max packet size + p_mtp->total_len = tu_max32(p_container->header->len, CFG_TUD_MTP_EP_BUFSIZE); } } else { // subsequent data block: payload only @@ -274,7 +275,10 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { } const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); + if (xact_len) { + // already transferred all bytes in header's length. Application make an unnecessary extra call + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); + } return true; } @@ -310,13 +314,23 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_LOG_DRV(" MTP %s phase = %u\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), p_mtp->phase); #endif + const mtp_container_info_t headered_packet = { + .header = &p_container->header, + .payload32 = p_container->data, + .payload_bytes = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t) + }; + + const mtp_container_info_t headerless_packet = { + .header = &p_mtp->io_header, + .payload = _mtpd_epbuf.buf, + .payload_bytes = CFG_TUD_MTP_EP_BUFSIZE + }; + tud_mtp_cb_data_t cb_data; cb_data.idx = 0; cb_data.command_container = &p_mtp->command; - cb_data.io_container.header = &p_container->header; - cb_data.io_container.payload32 = p_container->data; - cb_data.io_container.payload_size = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t); - cb_data.xferred_bytes = 0; + cb_data.io_container = headered_packet; + cb_data.total_xferred_bytes = 0; cb_data.xfer_result = event; switch (p_mtp->phase) { @@ -339,7 +353,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t case MTP_PHASE_DATA: { const uint16_t bulk_mps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 512 : 64; p_mtp->xferred_len += xferred_bytes; - cb_data.xferred_bytes = p_mtp->xferred_len; + cb_data.total_xferred_bytes = p_mtp->xferred_len; bool is_complete = false; // complete if ZLP or short packet or overflow @@ -349,12 +363,6 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t is_complete = true; } - const mtp_container_info_t headerless_packet = { - .header = &p_mtp->io_header, - .payload = _mtpd_epbuf.buf, - .payload_size = CFG_TUD_MTP_EP_BUFSIZE - }; - if (ep_addr == p_mtp->ep_in) { // Data In if (is_complete) { @@ -370,12 +378,17 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (p_mtp->xferred_len == xferred_bytes) { // 1st OUT packet: header + payload p_mtp->io_header = p_container->header; // save header for subsequent transaction + cb_data.io_container.payload_bytes = xferred_bytes - sizeof(mtp_container_header_t); } else { // 2nd+ packet: payload only cb_data.io_container = headerless_packet; + cb_data.io_container.payload_bytes = xferred_bytes; } tud_mtp_data_xfer_cb(&cb_data); + if (is_complete) { + // back to header + payload for response + cb_data.io_container = headered_packet; cb_data.io_container.header->len = sizeof(mtp_container_header_t); tud_mtp_data_complete_cb(&cb_data); } @@ -383,80 +396,6 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } -#if 0 - case MTP_PHASE_DATA_IN: - p_mtp->xferred_len += xferred_bytes; - p_mtp->handled_len = p_mtp->xferred_len; - - // Check if transfer completed TODO check ZLP with FS/HS bulk size - if (p_mtp->xferred_len >= p_mtp->total_len && (xferred_bytes == 0 || (xferred_bytes % CFG_MTP_EP_SIZE) != 0)) { - p_mtp->phase = MTP_PHASE_RESPONSE; - p_container->code = MTP_RESP_OK; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - if (p_mtp->session_id != 0) { // is this needed ? - p_container->data[0] = p_mtp->session_id; - p_container->len += sizeof(uint32_t); - } - } else { - // Send next block of DATA - if (p_mtp->xferred_len == p_mtp->total_len) { - // send Zero-Length Packet - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, NULL, 0 )); - } else { - p_mtp->phase = mtpd_handle_data(); - if (p_mtp->phase == MTP_PHASE_DATA_IN) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_in, ((uint8_t *)(&p_container->data)), (uint16_t)p_mtp->queued_len)); - } - } - } - break; - - case MTP_PHASE_DATA_OUT: - // First block of data - if (p_mtp->xferred_len == 0) { - p_mtp->total_len = p_container->len; - p_mtp->handled_len = 0; - p_mtp->xfer_completed = false; - TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); - } - p_mtp->xferred_len += xferred_bytes; - - // A zero-length or a short packet termination - if (xferred_bytes < CFG_MTP_EP_SIZE) { - p_mtp->xfer_completed = true; - // Handle data block - p_mtp->phase = mtpd_handle_data(); - if (p_mtp->phase == MTP_PHASE_DATA_OUT) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, (uint8_t*) p_container, sizeof(mtp_generic_container_t)), 0); - p_mtp->xferred_len = 0; - p_mtp->xfer_completed = false; - } - } else { - // Handle data block when container is full - if (p_mtp->xferred_len - p_mtp->handled_len >= MTP_MAX_PACKET_SIZE - CFG_MTP_EP_SIZE) { - p_mtp->phase = mtpd_handle_data(); - p_mtp->handled_len = p_mtp->xferred_len; - } - // Transfer completed: wait for zero-length packet - // Some platforms may not respect EP size and xferred_bytes may be more than CFG_MTP_EP_SIZE if - // the OUT EP is waiting for more data. Ensure we are not waiting for more than CFG_MTP_EP_SIZE. - if (p_mtp->total_len == p_mtp->xferred_len) { - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, ((uint8_t *)(&p_container->data)), CFG_MTP_EP_SIZE), 0); - } else if (p_mtp->handled_len == 0) { - // First data block includes container header + container data - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, - (uint8_t*) p_container + p_mtp->xferred_len, - (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); - } else { - // Successive data block includes only container data - TU_ASSERT(usbd_edpt_xfer(rhport, p_mtp->ep_out, - ((uint8_t *)(&p_container->data)) + p_mtp->xferred_len - p_mtp->handled_len, - (uint16_t)TU_MIN(p_mtp->total_len - p_mtp->xferred_len, CFG_MTP_EP_SIZE))); - } - } - break; -#endif - case MTP_PHASE_RESPONSE_QUEUED: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); @@ -574,76 +513,6 @@ mtp_phase_type_t mtpd_handle_cmd_delete_object(void) return MTP_PHASE_RESPONSE; } -mtp_phase_type_t mtpd_handle_cmd_send_object_info(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - _mtpd_soi.storage_id = p_container->data[0]; - _mtpd_soi.parent_object_handle = (p_container->data[1] == 0xFFFFFFFF ? 0 : p_container->data[1]); - - // Enter OUT phase and wait for DATA BLOCK - return MTP_PHASE_DATA_OUT; -} - -mtp_phase_type_t mtpd_handle_dto_send_object_info(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - uint32_t new_object_handle = 0; - mtp_response_t res = tud_mtp_storage_object_write_info(_mtpd_soi.storage_id, _mtpd_soi.parent_object_handle, &new_object_handle, (mtp_object_info_header_t *)p_container->data); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - - // Save send_object_info - _mtpd_soi.object_handle = new_object_handle; - - // Response - p_container->len = MTP_CONTAINER_HEADER_LENGTH + 3 * sizeof(uint32_t); - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_OK; - p_container->data[0] = _mtpd_soi.storage_id; - p_container->data[1] = _mtpd_soi.parent_object_handle; - p_container->data[2] = _mtpd_soi.object_handle; - return MTP_PHASE_RESPONSE; -} - -mtp_phase_type_t mtpd_handle_cmd_send_object(void) -{ - // Enter OUT phase and wait for DATA BLOCK - return MTP_PHASE_DATA_OUT; -} - -mtp_phase_type_t mtpd_handle_dto_send_object(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - uint8_t *buffer = (uint8_t *)&p_container->data; - uint32_t buffer_size = _mtpd_itf.xferred_len - _mtpd_itf.handled_len; - // First block of DATA - if (_mtpd_itf.handled_len == 0) - { - buffer_size -= MTP_CONTAINER_HEADER_LENGTH; - } - - if (buffer_size > 0) - { - mtp_response_t res = tud_mtp_storage_object_write(_mtpd_soi.object_handle, buffer, buffer_size); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - } - - if (!_mtpd_itf.xfer_completed) - { - // Continue with next DATA BLOCK - return MTP_PHASE_DATA_OUT; - } - - // Send completed - tud_mtp_storage_object_done(); - - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_OK; - return MTP_PHASE_RESPONSE; -} - mtp_phase_type_t mtpd_handle_cmd_format_store(void) { mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 7b6ae9e0f..d0c894742 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -42,7 +42,7 @@ typedef struct { mtp_container_info_t io_container; tusb_xfer_result_t xfer_result; - uint32_t xferred_bytes; // number of bytes transferred so far in this phase + uint32_t total_xferred_bytes; // number of bytes transferred so far in this phase } tud_mtp_cb_data_t; // Number of supported operations, events, device properties, capture formats, playback formats -- cgit v1.3.1 From 34be38db190c9fb8ca5f6c1db8bab33566e397cb Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 25 Sep 2025 18:58:25 +0700 Subject: clean up, wrap up bulk command supported --- examples/device/mtp/src/mtp_fs_example.c | 90 -------------------------------- examples/device/mtp/src/tusb_config.h | 2 - src/class/mtp/mtp_device.c | 61 ---------------------- 3 files changed, 153 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 4ccc122c7..a06fcb8db 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -513,96 +513,6 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { return 0; // nothing to do } -//--------------------------------------------------------------------+ -// API -//--------------------------------------------------------------------+ -#if 0 -mtp_response_t tud_mtp_storage_format(uint32_t storage_id) { - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - if (storage_id != SUPPORTED_STORAGE_ID) { - TU_LOG1("ERR: Unexpected storage id %ld\r\n", storage_id); - return MTP_RESP_INVALID_STORAGE_ID; - } - - // Simply deallocate all entries - for (unsigned int i = 0; i < FS_MAX_NODES; i++) - fs_objects[i].allocated = false; - TU_LOG1("Format completed\r\n"); - return MTP_RESP_OK; -} - -mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle) { - fs_file_t* obj; - - if (new_parent_object_handle == 0xFFFFFFFF) - new_parent_object_handle = 0; - - // Ensure we are not moving to an nonexisting parent - if (new_parent_object_handle != 0) { - obj = fs_get_file(new_parent_object_handle); - if (obj == NULL) { - TU_LOG1("Parent %ld does not exist\r\n", new_parent_object_handle); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - if (!obj->association_type) { - TU_LOG1("Parent %ld is not an association\r\n", new_parent_object_handle); - return MTP_RESP_INVALID_PARENT_OBJECT; - } - } - - obj = fs_get_file(object_handle); - - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - TU_LOG1("Move object %ld to new parent %ld\r\n", object_handle, new_parent_object_handle); - obj->parent = new_parent_object_handle; - return MTP_RESP_OK; -} - -mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle) { - fs_file_t* obj; - - if (_fs_operation.session_id == 0) { - TU_LOG1("ERR: Session not open\r\n"); - return MTP_RESP_SESSION_NOT_OPEN; - } - - if (object_handle == 0xFFFFFFFF) - object_handle = 0; - - if (object_handle != 0) { - obj = fs_get_file(object_handle); - - if (obj == NULL) { - TU_LOG1("ERR: Object with handle %ld does not exist\r\n", object_handle); - return MTP_RESP_INVALID_OBJECT_HANDLE; - } - obj->allocated = false; - TU_LOG1("Delete object with handle %ld\r\n", object_handle); - } - - if (object_handle == 0 || obj->association_type) { - // Delete also children - for (unsigned int i = 0; i < FS_MAX_NODES; i++) { - obj = &fs_objects[i]; - if (obj->allocated && obj->parent == object_handle) { - tud_mtp_storage_object_delete(obj->handle); - } - } - } - - return MTP_RESP_OK; -} - -void tud_mtp_storage_object_done(void) { -} -#endif - void tud_mtp_storage_cancel(void) { } diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index 7db5235d0..62a5729bf 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -102,14 +102,12 @@ MTP_OP_CLOSE_SESSION, \ MTP_OP_GET_STORAGE_IDS, \ MTP_OP_GET_STORAGE_INFO, \ - MTP_OP_GET_NUM_OBJECTS, \ MTP_OP_GET_OBJECT_HANDLES, \ MTP_OP_GET_OBJECT_INFO, \ MTP_OP_GET_OBJECT, \ MTP_OP_DELETE_OBJECT, \ MTP_OP_SEND_OBJECT_INFO, \ MTP_OP_SEND_OBJECT, \ - MTP_OP_FORMAT_STORE, \ MTP_OP_RESET_DEVICE, \ MTP_OP_GET_DEVICE_PROP_DESC, \ MTP_OP_GET_DEVICE_PROP_VALUE, \ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 6d1c7b29b..6438fd2d6 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -76,13 +76,6 @@ typedef struct { // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ static void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); -static mtp_phase_type_t mtpd_handle_data(void); -static mtp_phase_type_t mtpd_handle_cmd_delete_object(void); -static mtp_phase_type_t mtpd_handle_cmd_send_object_info(void); -static mtp_phase_type_t mtpd_handle_dto_send_object_info(void); -static mtp_phase_type_t mtpd_handle_cmd_send_object(void); -static mtp_phase_type_t mtpd_handle_dto_send_object(void); -static mtp_phase_type_t mtpd_handle_cmd_format_store(void); //--------------------------------------------------------------------+ // MTP variable declaration @@ -474,59 +467,5 @@ void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { break; } } -#if 0 - -mtp_phase_type_t mtpd_handle_data(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - TU_ASSERT(p_container->type == MTP_CONTAINER_TYPE_DATA_BLOCK); - - switch(p_container->code) - { - case MTP_OP_SEND_OBJECT_INFO: - TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT_INFO-DATA_OUT\n"); - return mtpd_handle_dto_send_object_info(); - case MTP_OP_SEND_OBJECT: - TU_LOG_DRV(" MTP command: MTP_OP_SEND_OBJECT-DATA_OUT\n"); - return mtpd_handle_dto_send_object(); - default: - TU_LOG_DRV(" MTP command: MTP_OP_UNKNOWN_COMMAND %x!!!!\n", p_container->code); - return false; - } - return true; -} - -mtp_phase_type_t mtpd_handle_cmd_delete_object(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - uint32_t object_handle = p_container->data[0]; - uint32_t object_code_format = p_container->data[1]; // not used - (void) object_code_format; - - mtp_response_t res = tud_mtp_storage_object_delete(object_handle); - mtp_phase_type_t phase; - if ((phase = mtpd_chk_generic(__func__, (res != MTP_RESP_OK), res, "")) != MTP_PHASE_NONE) return phase; - - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = MTP_RESP_OK; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - return MTP_PHASE_RESPONSE; -} - -mtp_phase_type_t mtpd_handle_cmd_format_store(void) -{ - mtp_generic_container_t* p_container = &_mtpd_epbuf.buf; - uint32_t storage_id = p_container->data[0]; - uint32_t file_system_format = p_container->data[1]; // not used - (void) file_system_format; - - mtp_response_t res = tud_mtp_storage_format(storage_id); - - p_container->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->code = res; - p_container->len = MTP_CONTAINER_HEADER_LENGTH; - return MTP_PHASE_RESPONSE; -} -#endif #endif -- cgit v1.3.1 From e4f7fcf7ec4239aff6193a8d71cd0e8ce94024dd Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Sep 2025 10:06:32 +0700 Subject: fix compile warnings --- examples/device/mtp/src/mtp_fs_example.c | 15 +++++++++------ src/class/mtp/mtp.h | 11 ++++------- src/class/mtp/mtp_device.c | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index a06fcb8db..2feb69758 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -66,7 +66,7 @@ typedef struct { uint32_t image_bit_depth; uint32_t parent; - uint8_t association_type; + uint16_t association_type; uint32_t size; uint8_t* data; @@ -74,8 +74,9 @@ typedef struct { } fs_file_t; // object data buffer (excluding 2 predefined files) +// with simple allocation pointer uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; -uint8_t fs_buf_head = 0; // simple allocation pointer +size_t fs_buf_head = 0; // Files system, handle is index + 1 static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { @@ -88,7 +89,7 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { .image_bit_depth = 0, .parent = 0, .association_type = MTP_ASSOCIATION_UNDEFINED, - .data = (uint8_t*) README_TXT_CONTENT, + .data = (uint8_t*) (uintptr_t) README_TXT_CONTENT, .size = sizeof(README_TXT_CONTENT) }, { @@ -100,7 +101,7 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { .image_bit_depth = 32, .parent = 0, .association_type = MTP_ASSOCIATION_UNDEFINED, - .data = logo_bin, + .data = (uint8_t*) (uintptr_t) logo_bin, .size = logo_len, } }; @@ -161,6 +162,7 @@ static inline fs_file_t* fs_create_file(void) { return f; } } + return NULL; } // simple malloc @@ -179,7 +181,7 @@ static inline uint8_t* fs_malloc(size_t size) { int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; - uint32_t resp_code = 0; + uint16_t resp_code = 0; switch (command->code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields @@ -339,6 +341,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { case MTP_OP_SEND_OBJECT_INFO: { const uint32_t storage_id = command->params[0]; const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root + (void) parent_handle; if (!is_session_opened) { resp_code = MTP_RESP_SESSION_NOT_OPEN; } else if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { @@ -397,7 +400,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; - uint32_t resp_code = 0; + uint16_t resp_code = 0; switch (command->code) { case MTP_OP_GET_OBJECT: { // File contents span over multiple xfers diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 15cf1bfc6..073e6c470 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -675,10 +675,7 @@ TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); // PTP/MTP Generic container typedef struct TU_ATTR_PACKED { mtp_container_header_t header; - // union { - uint32_t data[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)) / sizeof(uint32_t)]; - // uint8_t data[CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t)]; - // }; + uint8_t payload[(CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t))]; } mtp_generic_container_t; typedef struct { @@ -823,7 +820,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_string(mtp_contai while (utf16[count]) { count++; } - const uint32_t added_len = 1 + 2 * count; + const uint32_t added_len = 1u + 2u * count; TU_ASSERT(p_container->header->len + added_len < CFG_TUD_MTP_EP_BUFSIZE, 0); uint8_t* buf = p_container->payload + p_container->header->len - sizeof(mtp_container_header_t); @@ -856,7 +853,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_cstring(mtp_conta buf += 2; p_container->header->len += 2; } - return 1 + 2 * len; + return 1u + 2u * len; } } @@ -898,7 +895,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_auint32(mtp_conta TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* buf, uint16_t utf16[]) { uint8_t nchars = *buf++; memcpy(utf16, buf, 2 * nchars); - return 1 + nchars * 2; + return 1u + 2u * nchars; } #ifdef __cplusplus diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 6438fd2d6..61b1c9613 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -267,7 +267,7 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); } - const uint16_t xact_len = tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); + const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); if (xact_len) { // already transferred all bytes in header's length. Application make an unnecessary extra call TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); @@ -309,7 +309,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t const mtp_container_info_t headered_packet = { .header = &p_container->header, - .payload32 = p_container->data, + .payload = p_container->payload, .payload_bytes = CFG_TUD_MTP_EP_BUFSIZE - sizeof(mtp_container_header_t) }; -- cgit v1.3.1 From e92262eb5cdc35dfc6019c97d1b9166be0f5e62f Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Sep 2025 11:51:09 +0700 Subject: fix more ci build --- examples/device/mtp/src/mtp_fs_example.c | 100 ++++++++++++++++++------------- hw/bsp/samd11/family.cmake | 1 + src/class/mtp/mtp_device.c | 8 +-- src/class/mtp/mtp_device.h | 33 ++-------- 4 files changed, 66 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 2feb69758..a13acfcdd 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -47,15 +47,48 @@ typedef MTP_STORAGE_INFO_STRUCT(TU_ARRAY_SIZE((uint16_t[]) STORAGE_DESCRIPTRION) TU_ARRAY_SIZE(((uint16_t[])VOLUME_IDENTIFIER)) ) storage_info_t; +storage_info_t storage_info = { + #ifdef CFG_EXAMPLE_MTP_READONLY + .storage_type = MTP_STORAGE_TYPE_FIXED_ROM, + #else + .storage_type = MTP_STORAGE_TYPE_FIXED_RAM, + #endif + + .filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL, + .access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE, + .max_capacity_in_bytes = 0, // calculated at runtime + .free_space_in_bytes = 0, // calculated at runtime + .free_space_in_objects = 0, // calculated at runtime + .storage_description = { + .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t), + .utf16 = STORAGE_DESCRIPTRION + }, + .volume_identifier = { + .count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t), + .utf16 = VOLUME_IDENTIFIER + } +}; + + //--------------------------------------------------------------------+ -// RAM FILESYSTEM +// MTP FILESYSTEM //--------------------------------------------------------------------+ #define FS_MAX_FILE_COUNT 5UL -#define FS_MAX_CAPACITY_BYTES (4 * 1024UL) #define FS_MAX_FILENAME_LEN 16 -#define FS_FIXED_DATETIME "20250808T173500.0" // "YYYYMMDDTHHMMSS.s" -#define README_TXT_CONTENT "TinyUSB MTP on RAM Filesystem example" +#ifdef CFG_EXAMPLE_MTP_READONLY + #define FS_MAX_CAPACITY_BYTES 0 +#else + #define FS_MAX_CAPACITY_BYTES (4 * 1024UL) + + // object data buffer (excluding 2 predefined files) + // with simple allocation pointer + uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; +#endif +size_t fs_buf_head = 0; + +#define FS_FIXED_DATETIME "20250808T173500.0" // "YYYYMMDDTHHMMSS.s" +#define README_TXT_CONTENT "TinyUSB MTP Filesystem example" typedef struct { uint16_t name[FS_MAX_FILENAME_LEN]; @@ -64,26 +97,18 @@ typedef struct { uint32_t image_pix_width; uint32_t image_pix_height; uint32_t image_bit_depth; - uint32_t parent; uint16_t association_type; - uint32_t size; uint8_t* data; - } fs_file_t; -// object data buffer (excluding 2 predefined files) -// with simple allocation pointer -uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; -size_t fs_buf_head = 0; - // Files system, handle is index + 1 static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { { .name = { 'r', 'e', 'a', 'd', 'm', 'e', '.', 't', 'x', 't', 0 }, // readme.txt .object_format = MTP_OBJ_FORMAT_TEXT, - .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, .image_pix_width = 0, .image_pix_height = 0, .image_bit_depth = 0, @@ -95,7 +120,7 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { { .name = { 't', 'i', 'n', 'y', 'u', 's', 'b', '.', 'p', 'n', 'g', 0 }, // "tinyusb.png" .object_format = MTP_OBJ_FORMAT_PNG, - .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, + .protection_status = MTP_PROTECTION_STATUS_READ_ONLY, .image_pix_width = 128, .image_pix_height = 64, .image_bit_depth = 32, @@ -106,23 +131,6 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { } }; -//------------- Storage Info -------------// -storage_info_t storage_info = { - .storage_type = MTP_STORAGE_TYPE_FIXED_RAM, - .filesystem_type = MTP_FILESYSTEM_TYPE_GENERIC_HIERARCHICAL, - .access_capability = MTP_ACCESS_CAPABILITY_READ_WRITE, - .max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES, - .free_space_in_bytes = 0, // calculated at runtime - .free_space_in_objects = 0, // calculated at runtime - .storage_description = { - .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t), - .utf16 = STORAGE_DESCRIPTRION - }, - .volume_identifier = { - .count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t), - .utf16 = VOLUME_IDENTIFIER - } -}; enum { SUPPORTED_STORAGE_ID = 0x00010001u // physical = 1, logical = 1 @@ -167,16 +175,21 @@ static inline fs_file_t* fs_create_file(void) { // simple malloc static inline uint8_t* fs_malloc(size_t size) { +#ifdef CFG_EXAMPLE_MTP_READONLY + (void) size; + return NULL; +#else if (fs_buf_head + size > FS_MAX_CAPACITY_BYTES) { return NULL; } uint8_t* ptr = &fs_buf[fs_buf_head]; fs_buf_head += size; return ptr; +#endif } //--------------------------------------------------------------------+ -// +// Bulk Only Protocol //--------------------------------------------------------------------+ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; @@ -223,6 +236,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const uint32_t storage_id = command->params[0]; TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); // update storage info with current free space + storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES; storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count(); storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info)); @@ -299,7 +313,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { mtp_object_info_header_t obj_info_header = { .storage_id = SUPPORTED_STORAGE_ID, .object_format = f->object_format, - .protection_status = MTP_PROTECTION_STATUS_NO_PROTECTION, + .protection_status = f->protection_status, .object_compressed_size = f->size, .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, .thumb_compressed_size = 0, @@ -433,7 +447,17 @@ int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { } } + uint8_t* f_buf = fs_malloc(obj_info->object_compressed_size); + if (f_buf == NULL) { + resp_code = MTP_RESP_STORE_FULL; + break; + } fs_file_t* f = fs_create_file(); + if (f == NULL) { + resp_code = MTP_RESP_STORE_FULL; + break; + } + f->object_format = obj_info->object_format; f->protection_status = obj_info->protection_status; f->image_pix_width = obj_info->image_pix_width; @@ -442,11 +466,7 @@ int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { f->parent = obj_info->parent_object; f->association_type = obj_info->association_type; f->size = obj_info->object_compressed_size; - f->data = fs_malloc(f->size); - if (f->data == NULL) { - resp_code = MTP_RESP_STORE_FULL; - break; - } + f->data = f_buf; uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); mtp_container_get_string(buf, f->name); // ignore date created/modified/keywords @@ -498,10 +518,6 @@ int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { break; } - case MTP_OP_SEND_OBJECT: - resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - break; - default: resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; break; diff --git a/hw/bsp/samd11/family.cmake b/hw/bsp/samd11/family.cmake index 965b1cfb5..e3dc23c35 100644 --- a/hw/bsp/samd11/family.cmake +++ b/hw/bsp/samd11/family.cmake @@ -54,6 +54,7 @@ function(add_board_target BOARD_TARGET) OSC32K_OVERWRITE_CALIBRATION=0 CFG_EXAMPLE_MSC_READONLY CFG_EXAMPLE_VIDEO_READONLY + CFG_EXAMPLE_MTP_READONLY ) update_board(${BOARD_TARGET}) diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 61b1c9613..b3d982323 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -424,14 +424,10 @@ void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { .standard_version = 100, .mtp_vendor_extension_id = 6, // MTP specs say 0xFFFFFFFF but libMTP check for value 6 .mtp_version = 100, -#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS .mtp_extensions = { .count = sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), .utf16 = { 0 } }, -#else - .mtp_extensions = 0, -#endif .functional_mode = 0x0000, .supported_operations = { .count = TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), @@ -454,11 +450,11 @@ void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { .arr = { CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS } } }; -#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS + for (uint8_t i=0; i < dev_info.mtp_extensions.count; i++) { dev_info.mtp_extensions.utf16[i] = (uint16_t)CFG_TUD_MTP_DEVICEINFO_EXTENSIONS[i]; } -#endif + mtp_container_add_raw(&cb_data->io_container, &dev_info, sizeof(tud_mtp_device_info_t)); break; } diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index d0c894742..b10c3abda 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -62,34 +62,11 @@ typedef struct { /* string fields will be added using append function */ \ } -#define MTP_DEVICE_INFO_NO_EXTENSION_STRUCT(_op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ - struct TU_ATTR_PACKED { \ - uint16_t standard_version; \ - uint32_t mtp_vendor_extension_id; \ - uint16_t mtp_version; \ - uint8_t mtp_extensions; \ - uint16_t functional_mode; \ - mtp_auint16_t(_op_count) supported_operations; \ - mtp_auint16_t(_event_count) supported_events; \ - mtp_auint16_t(_devprop_count) supported_device_properties; \ - mtp_auint16_t(_capture_count) capture_formats; \ - mtp_auint16_t(_playback_count) playback_formats; \ - /* string fields will be added using append function */ \ - } - -#ifdef CFG_TUD_MTP_DEVICEINFO_EXTENSIONS - typedef MTP_DEVICE_INFO_STRUCT( - sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), - TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), - TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) - ) tud_mtp_device_info_t; -#else - typedef MTP_DEVICE_INFO_NO_EXTENSION_STRUCT( - TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), - TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), - TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) - ) tud_mtp_device_info_t; -#endif +typedef MTP_DEVICE_INFO_STRUCT( + sizeof(CFG_TUD_MTP_DEVICEINFO_EXTENSIONS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_OPERATIONS), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_EVENTS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_SUPPORTED_DEVICE_PROPERTIES), + TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_CAPTURE_FORMATS), TU_ARGS_NUM(CFG_TUD_MTP_DEVICEINFO_PLAYBACK_FORMATS) +) tud_mtp_device_info_t; //--------------------------------------------------------------------+ // Application API -- cgit v1.3.1 From f9d4bc798177e63532cd5a2ef058400d6f0ef5e4 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Sep 2025 15:52:37 +0700 Subject: implement tud_mtp_mounted() and tud_mtp_event_send() refactor phase state --- src/class/mtp/mtp.h | 33 +++++------- src/class/mtp/mtp_device.c | 132 +++++++++++++++++++++++++-------------------- src/class/mtp/mtp_device.h | 5 ++ 3 files changed, 94 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 073e6c470..b73bd200a 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -56,18 +56,14 @@ typedef enum { // PTP/MTP protocol phases typedef enum { - MTP_PHASE_IDLE = 0, - MTP_PHASE_COMMAND, + MTP_PHASE_COMMAND = 0, MTP_PHASE_DATA, MTP_PHASE_RESPONSE, - MTP_PHASE_RESPONSE_QUEUED, - MTP_PHASE_ERROR, - MTP_PHASE_NONE, + MTP_PHASE_ERROR } mtp_phase_type_t; // PTP/MTP Class requests, PIMA 15740-2000: D.5.2 -typedef enum -{ +typedef enum { MTP_REQ_CANCEL = 0x64, MTP_REQ_GET_EXT_EVENT_DATA = 0x65, MTP_REQ_RESET = 0x66, @@ -75,8 +71,7 @@ typedef enum } mtp_class_request_t; // PTP/MTP Container type -typedef enum -{ +typedef enum { MTP_CONTAINER_TYPE_UNDEFINED = 0, MTP_CONTAINER_TYPE_COMMAND_BLOCK = 1, MTP_CONTAINER_TYPE_DATA_BLOCK = 2, @@ -85,8 +80,7 @@ typedef enum } mtp_container_type_t; // MTP 1.1 Appendix A: Object formats -typedef enum -{ +typedef enum { // ---- Base formats ---- MTP_OBJ_FORMAT_UNDEFINED = 0x3000u, // Undefined object MTP_OBJ_FORMAT_ASSOCIATION = 0x3001u, // Association (for example, a folder) @@ -572,12 +566,6 @@ typedef enum { MTP_EVENT_OBJECT_REFERENCES_CHANGED = 0xC803u, } mtp_event_code_t; -// Predefined Object handles -typedef enum -{ - MTP_OBJH_ROOT = 0x0000, -} mtp_object_handles_t; - // Datatypes typedef enum { MTP_DATA_TYPE_UNDEFINED = 0x0000u, @@ -607,8 +595,7 @@ typedef enum { } mtp_data_type_t; // Get/Set -typedef enum -{ +typedef enum { MTP_MODE_GET = 0x00u, MTP_MODE_GET_SET = 0x01u, } mtp_mode_get_set_t; @@ -688,6 +675,14 @@ typedef struct { uint32_t payload_bytes; // available bytes for read/write } mtp_container_info_t; +typedef struct TU_ATTR_PACKED { + uint16_t code; + uint32_t session_id; + uint32_t transaction_id; + uint32_t params[3]; +} mtp_event_t; +TU_VERIFY_STATIC(sizeof(mtp_event_t) == 22, "size is not correct"); + #define mtp_string_t(_nchars) \ struct TU_ATTR_PACKED { \ uint8_t count; /* in characters including null */ \ diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index b3d982323..4e468458d 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -70,6 +70,7 @@ typedef struct { typedef struct { TUD_EPBUF_DEF(buf, CFG_TUD_MTP_EP_BUFSIZE); + TUD_EPBUF_TYPE_DEF(mtp_event_t, buf_event); } mtpd_epbuf_t; //--------------------------------------------------------------------+ @@ -142,18 +143,84 @@ TU_ATTR_UNUSED static tu_lookup_table_t const _mtp_op_table = { .items = _mpt_op_lookup }; +TU_ATTR_UNUSED static const char* _mtp_phase_str[] = { + "Command", + "Data", + "Response", + "Error" +}; + #endif //--------------------------------------------------------------------+ // Helper //--------------------------------------------------------------------+ - static bool prepare_new_command(mtpd_interface_t* p_mtp) { - p_mtp->phase = MTP_PHASE_IDLE; + p_mtp->phase = MTP_PHASE_COMMAND; return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE); } +static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + if (p_mtp->phase == MTP_PHASE_COMMAND) { + // 1st data block: header + payload + p_mtp->phase = MTP_PHASE_DATA; + p_mtp->xferred_len = 0; + + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + p_mtp->total_len = p_container->header->len; + p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; + p_container->header->transaction_id = p_mtp->command.transaction_id; + p_mtp->io_header = *p_container->header; // save header for subsequent data + } else { + // OUT transfer: total length is at least max packet size + p_mtp->total_len = tu_max32(p_container->header->len, CFG_TUD_MTP_EP_BUFSIZE); + } + } else { + // subsequent data block: payload only + TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); + } + + const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); + if (xact_len) { + // already transferred all bytes in header's length. Application make an unnecessary extra call + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); + } + return true; +} + +bool tud_mtp_data_send(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_in); +} + +bool tud_mtp_data_receive(mtp_container_info_t* p_container) { + return mtpd_data_xfer(p_container, _mtpd_itf.ep_out); +} + +bool tud_mtp_response_send(mtp_container_info_t* p_container) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + p_mtp->phase = MTP_PHASE_RESPONSE; + p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; + p_container->header->transaction_id = p_mtp->command.transaction_id; + TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); + return true; +} + +bool tud_mtp_mounted(void) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + return p_mtp->ep_out != 0 && p_mtp->ep_in != 0; +} + +bool tud_mtp_event_send(mtp_event_t* event) { + mtpd_interface_t* p_mtp = &_mtpd_itf; + TU_VERIFY(p_mtp->ep_event != 0); + _mtpd_epbuf.buf_event = *event; + TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_event)); // Claim the endpoint + + return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t)); +} + //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -246,52 +313,6 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t return true; } -static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { - mtpd_interface_t* p_mtp = &_mtpd_itf; - if (p_mtp->phase == MTP_PHASE_COMMAND) { - // 1st data block: header + payload - p_mtp->phase = MTP_PHASE_DATA; - p_mtp->xferred_len = 0; - - if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { - p_mtp->total_len = p_container->header->len; - p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; - p_mtp->io_header = *p_container->header; // save header for subsequent data - } else { - // OUT transfer: total length is at least max packet size - p_mtp->total_len = tu_max32(p_container->header->len, CFG_TUD_MTP_EP_BUFSIZE); - } - } else { - // subsequent data block: payload only - TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); - } - - const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); - if (xact_len) { - // already transferred all bytes in header's length. Application make an unnecessary extra call - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); - } - return true; -} - -bool tud_mtp_data_send(mtp_container_info_t* p_container) { - return mtpd_data_xfer(p_container, _mtpd_itf.ep_in); -} - -bool tud_mtp_data_receive(mtp_container_info_t* p_container) { - return mtpd_data_xfer(p_container, _mtpd_itf.ep_out); -} - -bool tud_mtp_response_send(mtp_container_info_t* p_container) { - mtpd_interface_t* p_mtp = &_mtpd_itf; - p_mtp->phase = MTP_PHASE_RESPONSE_QUEUED; - p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); - return true; -} - // Transfer on bulk endpoints bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { if (ep_addr == _mtpd_itf.ep_event) { @@ -304,7 +325,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t #if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL tu_lookup_find(&_mtp_op_table, p_mtp->command.code); - TU_LOG_DRV(" MTP %s phase = %u\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), p_mtp->phase); + TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), + _mtp_phase_str[p_mtp->phase]); #endif const mtp_container_info_t headered_packet = { @@ -327,13 +349,9 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t cb_data.xfer_result = event; switch (p_mtp->phase) { - case MTP_PHASE_IDLE: + case MTP_PHASE_COMMAND: { // received new command TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); - p_mtp->phase = MTP_PHASE_COMMAND; - TU_ATTR_FALLTHROUGH; // handle in the next case - - case MTP_PHASE_COMMAND: { memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command p_container->header.len = sizeof(mtp_container_header_t); // default container to header only process_cmd(p_mtp, &cb_data); @@ -389,16 +407,15 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } - case MTP_PHASE_RESPONSE_QUEUED: + case MTP_PHASE_RESPONSE: // response phase is complete -> prepare for new command TU_ASSERT(ep_addr == p_mtp->ep_in); tud_mtp_response_complete_cb(&cb_data); prepare_new_command(p_mtp); break; - case MTP_PHASE_RESPONSE: case MTP_PHASE_ERROR: - // supposedly to be empty + // handled after switch, supposedly to be empty break; default: return false; } @@ -412,6 +429,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t return true; } + //--------------------------------------------------------------------+ // MTPD Internal functionality //--------------------------------------------------------------------+ diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index b10c3abda..4956628d3 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -72,6 +72,9 @@ typedef MTP_DEVICE_INFO_STRUCT( // Application API //--------------------------------------------------------------------+ +// check if mtp interface is mounted +bool tud_mtp_mounted(void); + // send data phase bool tud_mtp_data_send(mtp_container_info_t* p_container); @@ -81,6 +84,8 @@ bool tud_mtp_data_receive(mtp_container_info_t* p_container); // send response bool tud_mtp_response_send(mtp_container_info_t* p_container); +bool tud_mtp_event_send(mtp_event_t* event); + //--------------------------------------------------------------------+ // Control request Callbacks //--------------------------------------------------------------------+ -- cgit v1.3.1 From cb21ca1b0cb3221341dca2559109c0bb4dc18e6f Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 27 Sep 2025 16:15:04 +0700 Subject: implement control request --- examples/device/mtp/src/mtp_fs_example.c | 41 ++++++++-- examples/device/mtp/src/tusb_config.h | 5 +- examples/device/mtp/src/usb_descriptors.c | 10 ++- src/class/mtp/mtp.h | 13 +--- src/class/mtp/mtp_device.c | 120 +++++++++++++++++++++--------- src/class/mtp/mtp_device.h | 44 +++++++++-- src/class/mtp/mtp_device_storage.h | 93 ----------------------- 7 files changed, 169 insertions(+), 157 deletions(-) delete mode 100644 src/class/mtp/mtp_device_storage.h (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index a13acfcdd..9d90f7663 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -23,7 +23,6 @@ * */ -#include "class/mtp/mtp_device_storage.h" #include "tusb.h" #include "tinyusb_logo_png.h" @@ -188,6 +187,40 @@ static inline uint8_t* fs_malloc(size_t size) { #endif } +//--------------------------------------------------------------------+ +// Control Request callback +//--------------------------------------------------------------------+ +bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data) { + mtp_request_reset_cancel_data_t cancel_data; + memcpy(&cancel_data, cb_data->buf, sizeof(cancel_data)); + (void) cancel_data.code; + (void ) cancel_data.transaction_id; + return true; +} + +// Invoked when received Device Reset request +// return false to stall the request +bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return true; +} + +// Invoked when received Get Extended Event request. Application fill callback data's buffer for response +// return negative to stall the request +int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return false; // not implemented yet +} + +// Invoked when received Get DeviceStatus request. Application fill callback data's buffer for response +// return negative to stall the request +int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data) { + uint16_t* buf16 = (uint16_t*)(uintptr_t) cb_data->buf; + buf16[0] = 4; // length + buf16[1] = MTP_RESP_OK; // status + return 4; +} + //--------------------------------------------------------------------+ // Bulk Only Protocol //--------------------------------------------------------------------+ @@ -531,9 +564,3 @@ int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { (void) cb_data; return 0; // nothing to do } - -void tud_mtp_storage_cancel(void) { -} - -void tud_mtp_storage_reset(void) { -} diff --git a/examples/device/mtp/src/tusb_config.h b/examples/device/mtp/src/tusb_config.h index 62a5729bf..4d166aa63 100644 --- a/examples/device/mtp/src/tusb_config.h +++ b/examples/device/mtp/src/tusb_config.h @@ -93,6 +93,7 @@ //------------- CLASS -------------// #define CFG_TUD_MTP 1 #define CFG_TUD_MTP_EP_BUFSIZE 512 +#define CFG_TUD_MTP_EP_CONTROL_BUFSIZE 16 // should be enough to hold data in MTP control request //------------- MTP device info -------------// #define CFG_TUD_MTP_DEVICEINFO_EXTENSIONS "microsoft.com: 1.0; " @@ -131,10 +132,6 @@ MTP_OBJ_FORMAT_TEXT, \ MTP_OBJ_FORMAT_PNG -#define CFG_TUD_MANUFACTURER "TinyUsb Manufacturer" -#define CFG_TUD_MODEL "TinyUsb Device" -#define CFG_MTP_INTERFACE (CFG_TUD_MODEL " MTP") - #ifdef __cplusplus } #endif diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c index 4a43a0dcc..810137c46 100644 --- a/examples/device/mtp/src/usb_descriptors.c +++ b/examples/device/mtp/src/usb_descriptors.c @@ -140,10 +140,10 @@ enum { char const *string_desc_arr[] = { (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - CFG_TUD_MANUFACTURER, // 1: Manufacturer - CFG_TUD_MODEL, // 2: Product + "TinyUsb", // 1: Manufacturer + "TinyUsb Device", // 2: Product NULL, // 3: Serials will use unique ID if possible - CFG_MTP_INTERFACE, // 4: MTP Interface + "TinyUSBB MTP", // 4: MTP Interface }; static uint16_t _desc_str[32 + 1]; @@ -168,7 +168,9 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { // 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; + if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) { + return NULL; + } const char *str = string_desc_arr[index]; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index b73bd200a..0450c7e36 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -38,8 +38,6 @@ extern "C" { #endif -typedef uint16_t wchar16_t; - //--------------------------------------------------------------------+ // Media Transfer Protocol Class Constant //--------------------------------------------------------------------+ @@ -759,15 +757,10 @@ typedef struct TU_ATTR_PACKED { }; typedef struct TU_ATTR_PACKED { - uint16_t wLength; uint16_t code; -} mtp_device_status_res_t; - -typedef struct TU_ATTR_PACKED { - uint32_t object_handle; - uint32_t storage_id; - uint32_t parent_object_handle; -} mtp_basic_object_info_t; + uint32_t transaction_id; +} mtp_request_reset_cancel_data_t; +TU_VERIFY_STATIC(sizeof(mtp_request_reset_cancel_data_t) == 6, "size is not correct"); //--------------------------------------------------------------------+ // Container helper function diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 4e468458d..a72712795 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -31,12 +31,11 @@ //--------------------------------------------------------------------+ // INCLUDE //--------------------------------------------------------------------+ -#include "device/dcd.h" // for faking dcd_event_xfer_complete +#include "device/dcd.h" #include "device/usbd.h" #include "device/usbd_pvt.h" #include "mtp_device.h" -#include "mtp_device_storage.h" // Level where CFG_TUSB_DEBUG must be at least for this driver is logged #ifndef CFG_TUD_MTP_LOG_LEVEL @@ -45,7 +44,45 @@ #define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MTP_LOG_LEVEL, __VA_ARGS__) -#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +//--------------------------------------------------------------------+ +// Weak stubs: invoked if no strong implementation is available +//--------------------------------------------------------------------+ +TU_ATTR_WEAK bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return false; +} +TU_ATTR_WEAK bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return false; +} +TU_ATTR_WEAK int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return -1; +} +TU_ATTR_WEAK int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return -1; +} +TU_ATTR_WEAK bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data) { + (void) cb_data; + return false; +} +TU_ATTR_WEAK int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data) { + (void) cb_data; + return -1; +} +TU_ATTR_WEAK int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; + return -1; +} +TU_ATTR_WEAK int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; + return -1; +} +TU_ATTR_WEAK int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; + return -1; +} //--------------------------------------------------------------------+ // STRUCT @@ -66,6 +103,8 @@ typedef struct { uint32_t session_id; mtp_container_command_t command; mtp_container_header_t io_header; + + TU_ATTR_ALIGNED(4) uint8_t control_buf[CFG_TUD_MTP_EP_CONTROL_BUFSIZE]; } mtpd_interface_t; typedef struct { @@ -76,16 +115,10 @@ typedef struct { //--------------------------------------------------------------------+ // INTERNAL FUNCTION DECLARATION //--------------------------------------------------------------------+ -static void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); - -//--------------------------------------------------------------------+ -// MTP variable declaration -//--------------------------------------------------------------------+ static mtpd_interface_t _mtpd_itf; CFG_TUD_MEM_SECTION static mtpd_epbuf_t _mtpd_epbuf; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_device_status_res_t _mtpd_device_status_res; -CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN static mtp_basic_object_info_t _mtpd_soi; +static void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data); //--------------------------------------------------------------------+ // Debug @@ -226,7 +259,6 @@ bool tud_mtp_event_send(mtp_event_t* event) { //--------------------------------------------------------------------+ void mtpd_init(void) { tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); - tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); } bool mtpd_deinit(void) { @@ -236,8 +268,6 @@ bool mtpd_deinit(void) { void mtpd_reset(uint8_t rhport) { (void) rhport; tu_memclr(&_mtpd_itf, sizeof(mtpd_interface_t)); - tu_memclr(&_mtpd_epbuf, sizeof(mtpd_epbuf_t)); - tu_memclr(&_mtpd_soi, sizeof(mtp_basic_object_info_t)); } uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { @@ -264,7 +294,6 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 // Open endpoint pair TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(ep_desc), 2, TUSB_XFER_BULK, &p_mtp->ep_out, &p_mtp->ep_in), 0); - TU_ASSERT(prepare_new_command(p_mtp), 0); return mtpd_itf_size; @@ -274,40 +303,63 @@ uint16_t mtpd_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, uint16 // Driver response accordingly to the request and the transfer stage (setup/data/ack) // return false to stall control endpoint (e.g unsupported request) bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) { - if (stage != CONTROL_STAGE_SETUP) { - return true; // nothing to do with DATA & ACK stage - } + mtpd_interface_t* p_mtp = &_mtpd_itf; + tud_mtp_request_cb_data_t cb_data = { + .idx = 0, + .stage = stage, + .request = request, + .buf = p_mtp->control_buf, + .bufsize = tu_le16toh(request->wLength), + }; switch (request->bRequest) { case MTP_REQ_CANCEL: - TU_LOG_DRV(" MTP request: MTP_REQ_CANCEL\n"); - tud_mtp_storage_cancel(); + TU_LOG_DRV(" MTP request: Cancel\n"); + if (stage == CONTROL_STAGE_SETUP) { + return tud_control_xfer(rhport, request, p_mtp->control_buf, CFG_TUD_MTP_EP_CONTROL_BUFSIZE); + } else if (stage == CONTROL_STAGE_ACK) { + return tud_mtp_request_cancel_cb(&cb_data); + } break; case MTP_REQ_GET_EXT_EVENT_DATA: - TU_LOG_DRV(" MTP request: MTP_REQ_GET_EXT_EVENT_DATA\n"); + TU_LOG_DRV(" MTP request: Get Extended Event Data\n"); + if (stage == CONTROL_STAGE_SETUP) { + const int32_t len = tud_mtp_request_get_extended_event_cb(&cb_data); + TU_VERIFY(len > 0); + return tud_control_xfer(rhport,request, p_mtp->control_buf, (uint16_t) len); + } break; case MTP_REQ_RESET: - TU_LOG_DRV(" MTP request: MTP_REQ_RESET\n"); - tud_mtp_storage_reset(); - // Prepare for a new command - TU_ASSERT(usbd_edpt_xfer(rhport, _mtpd_itf.ep_out, _mtpd_epbuf.buf, CFG_TUD_MTP_EP_BUFSIZE)); + TU_LOG_DRV(" MTP request: Device Reset\n"); + // used by the host to return the Still Image Capture Device to the Idle state after the Bulk-pipe has stalled + if (stage == CONTROL_STAGE_SETUP) { + // clear stalled + if (usbd_edpt_stalled(rhport, p_mtp->ep_out)) { + usbd_edpt_clear_stall(rhport, p_mtp->ep_out); + } + if (usbd_edpt_stalled(rhport, p_mtp->ep_in)) { + usbd_edpt_clear_stall(rhport, p_mtp->ep_in); + } + } else if (stage == CONTROL_STAGE_ACK) { + prepare_new_command(p_mtp); + return tud_mtp_request_device_reset_cb(&cb_data); + } break; case MTP_REQ_GET_DEVICE_STATUS: { - TU_LOG_DRV(" MTP request: MTP_REQ_GET_DEVICE_STATUS\n"); - uint16_t len = 4; - _mtpd_device_status_res.wLength = len; - // Cancel is synchronous, always answer OK - _mtpd_device_status_res.code = MTP_RESP_OK; - TU_ASSERT(tud_control_xfer(rhport, request, (uint8_t *)&_mtpd_device_status_res , len)); + TU_LOG_DRV(" MTP request: Get Device Status\n"); + if (stage == CONTROL_STAGE_SETUP) { + const int32_t len = tud_mtp_request_get_device_status_cb(&cb_data); + TU_VERIFY(len > 0); + return tud_control_xfer(rhport, request, p_mtp->control_buf, (uint16_t) len); + } break; } default: - TU_LOG_DRV(" MTP request: invalid request\r\n"); - return false; // stall unsupported request + return tud_mtp_request_vendor_cb(&cb_data); } return true; @@ -354,7 +406,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_VERIFY(ep_addr == p_mtp->ep_out && p_container->header.type == MTP_CONTAINER_TYPE_COMMAND_BLOCK); memcpy(&p_mtp->command, p_container, sizeof(mtp_container_command_t)); // save new command p_container->header.len = sizeof(mtp_container_header_t); // default container to header only - process_cmd(p_mtp, &cb_data); + preprocess_cmd(p_mtp, &cb_data); if (tud_mtp_command_received_cb(&cb_data) < 0) { p_mtp->phase = MTP_PHASE_ERROR; } @@ -435,7 +487,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // pre-processed commands -void process_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { +void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { switch (p_mtp->command.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index 4956628d3..c69f6f12f 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -36,6 +36,7 @@ extern "C" { #endif +// callback data for Bulk Only Transfer (BOT) protocol typedef struct { uint8_t idx; // mtp instance const mtp_container_command_t* command_container; @@ -45,6 +46,16 @@ typedef struct { uint32_t total_xferred_bytes; // number of bytes transferred so far in this phase } tud_mtp_cb_data_t; +// callback data for Control requests +typedef struct { + uint8_t idx; + uint8_t stage; // control stage + const tusb_control_request_t* request; + // buffer for data stage + uint8_t* buf; + uint16_t bufsize; +} tud_mtp_request_cb_data_t; + // Number of supported operations, events, device properties, capture formats, playback formats // and max number of characters for strings manufacturer, model, device_version, serial_number #define MTP_DEVICE_INFO_STRUCT(_extension_nchars, _op_count, _event_count, _devprop_count, _capture_count, _playback_count) \ @@ -89,24 +100,47 @@ bool tud_mtp_event_send(mtp_event_t* event); //--------------------------------------------------------------------+ // Control request Callbacks //--------------------------------------------------------------------+ -// bool tud_mtp_control_xfer_cb(uint8_t idx, uint8_t stage, tusb_control_request_t const *p_request); + +// Invoked when received Cancel request. Data is available in callback data's buffer +// return false to stall the request +bool tud_mtp_request_cancel_cb(tud_mtp_request_cb_data_t* cb_data); + +// Invoked when received Device Reset request +// return false to stall the request +bool tud_mtp_request_device_reset_cb(tud_mtp_request_cb_data_t* cb_data); + +// Invoked when received Get Extended Event request. Application fill callback data's buffer for response +// return negative to stall the request +int32_t tud_mtp_request_get_extended_event_cb(tud_mtp_request_cb_data_t* cb_data); + +// Invoked when received Get DeviceStatus request. Application fill callback data's buffer for response +// return negative to stall the request +int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data); + +// Invoked when received vendor-specific request not in the above standard MTP requests +// return false to stall the request +bool tud_mtp_request_vendor_cb(tud_mtp_request_cb_data_t* cb_data); //--------------------------------------------------------------------+ // Bulk only protocol Callbacks //--------------------------------------------------------------------+ -/* Invoked when new command is received. Application fill the cb_data->reply with either DATA or RESPONSE and call - * tud_mtp_data_send() or tud_mtp_response_send(). Return negative to stall the endpoints - */ +// Invoked when new command is received. Application fill the cb_data->io_container and call tud_mtp_data_send() or +// tud_mtp_response_send() for Data or Response phase. +// Return negative to stall the endpoints int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t * cb_data); -// Invoked when a data packet is transferred, and more data is expected +// Invoked when a data packet is transferred. If data spans over multiple packets, application can use +// total_xferred_bytes and io_container's payload_bytes to determine the offset and remaining bytes to be transferred. +// Return negative to stall the endpoints int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data); // Invoked when all bytes in DATA phase is complete. A response packet is expected +// Return negative to stall the endpoints int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data); // Invoked when response phase is complete +// Return negative to stall the endpoints int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data); //--------------------------------------------------------------------+ diff --git a/src/class/mtp/mtp_device_storage.h b/src/class/mtp/mtp_device_storage.h deleted file mode 100644 index 30038978f..000000000 --- a/src/class/mtp/mtp_device_storage.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2025 Ennebi Elettronica (https://ennebielettronica.com) - * - * 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_MTP_DEVICE_STORAGE_H_ -#define _TUSB_MTP_DEVICE_STORAGE_H_ - -#include "common/tusb_common.h" -#include "mtp.h" - -#if (CFG_TUD_ENABLED && CFG_TUD_MTP) - -#ifdef __cplusplus - extern "C" { -#endif - -//--------------------------------------------------------------------+ -// Storage Application Callbacks -//--------------------------------------------------------------------+ - -/* - * The entire MTP functionality is based on object handles, as described in MTP Specs v. 1.1 under 3.4.1. - * The major weakness of the protocol is that those handles are supposed to be unique within a session - * and for every enumerated object. There is no specified lifetime limit or way to control the expiration: - * once given, they have to persist for an indefinite time and number of iterations. - * If the filesystem does not provide unique persistent object handle for every entry, the best approach - * would be to keep a full association between generated handles and full file paths. The suggested - * approach with memory constrained devices is to keep a hard ID associated with each file or a volatile - * ID generated on the fly and invalidated on each operation that may rearrange the order. - * In order to invalidate existing IDS, it might be necessary to invalidate the whole session from - * the device side. - * Depending on the application, the handle could be also be the file name or a tag (i.e. host-only file access) - */ - -// Format the specified storage -mtp_response_t tud_mtp_storage_format(uint32_t storage_id); - -// Called with the creation of a new object is requested. -// The handle of the new object shall be returned in new_object_handle. -// The structure info contains the information to be used for file creation, as passted by the host. -// Note that the variable information (e.g. wstring file name, dates and tags shall be retrieved by using the library functions) -mtp_response_t tud_mtp_storage_object_write_info(uint32_t storage_id, uint32_t parent_object, uint32_t *new_object_handle, const mtp_object_info_header_t *info); - -// Write object data -// -// The function shall open the object for writing if not already open. -// The binary data shall be written to the file in full before this function is returned. -mtp_response_t tud_mtp_storage_object_write(uint32_t object_handle, const uint8_t *buffer, uint32_t buffer_size); - -// Move an object to a new parent -mtp_response_t tud_mtp_storage_object_move(uint32_t object_handle, uint32_t new_parent_object_handle); - -// Delete the specified object -mtp_response_t tud_mtp_storage_object_delete(uint32_t object_handle); - -// Issued when IO operation has been terminated (e.g. read, traverse), close open file handles -void tud_mtp_storage_object_done(void); - -// Cancel any pending operation. Current operation shall be discarded. -void tud_mtp_storage_cancel(void); - -// Restore the operation out of reset. Cancel any pending operation and close the session. -void tud_mtp_storage_reset(void); - -#ifdef __cplusplus - } -#endif - -#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ - -#endif /* _TUSB_MTP_DEVICE_STORAGE_H_ */ -- cgit v1.3.1 From adce3bbac02f81c0e7e17f4fcbd1deb83bef7cb4 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 27 Sep 2025 17:30:27 +0700 Subject: implement serial for MTP --- README.rst | 1 + examples/device/mtp/src/mtp_fs_example.c | 12 ++++++++---- src/class/mtp/mtp.h | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/README.rst b/README.rst index d6830e707..2c127bfdc 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,7 @@ Supports multiple device configurations by dynamically changing USB descriptors, - Human Interface Device (HID): Generic (In & Out), Keyboard, Mouse, Gamepad etc ... - Mass Storage Class (MSC): with multiple LUNs - Musical Instrument Digital Interface (MIDI) +- Media Transfer Protocol (MTP/PTP) - Network with RNDIS, Ethernet Control Model (ECM), Network Control Model (NCM) - Test and Measurement Class (USBTMC) - Video class 1.5 (UVC): work in progress diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 9d90f7663..72fff5402 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -23,7 +23,9 @@ * */ +#include "bsp/board_api.h" #include "tusb.h" + #include "tinyusb_logo_png.h" //--------------------------------------------------------------------+ @@ -68,7 +70,6 @@ storage_info_t storage_info = { } }; - //--------------------------------------------------------------------+ // MTP FILESYSTEM //--------------------------------------------------------------------+ @@ -80,8 +81,7 @@ storage_info_t storage_info = { #else #define FS_MAX_CAPACITY_BYTES (4 * 1024UL) - // object data buffer (excluding 2 predefined files) - // with simple allocation pointer + // object data buffer (excluding 2 predefined files) with simple allocation pointer uint8_t fs_buf[FS_MAX_CAPACITY_BYTES]; #endif size_t fs_buf_head = 0; @@ -234,7 +234,11 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER); mtp_container_add_cstring(io_container, DEV_INFO_MODEL); mtp_container_add_cstring(io_container, DEV_INFO_VERSION); - mtp_container_add_cstring(io_container, DEV_INFO_SERIAL); + + uint16_t serial_utf16[32]; + board_usb_get_serial(serial_utf16, 32); + serial_utf16[31] = 0; // ensure null termination + mtp_container_add_string(io_container, serial_utf16); tud_mtp_data_send(io_container); break; diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 0450c7e36..a8123b380 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -768,7 +768,7 @@ TU_VERIFY_STATIC(sizeof(mtp_request_reset_cancel_data_t) == 6, "size is not corr //--------------------------------------------------------------------+ // return payload buffer for next write -TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_next(mtp_container_info_t* p_container) { +TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_ptr(mtp_container_info_t* p_container) { // only 1st packet include header uint32_t pos = p_container->header->len - sizeof(mtp_container_header_t); while (pos > CFG_TUD_MTP_EP_BUFSIZE) { @@ -779,7 +779,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t* mtp_container_payload_next(mtp_cont // only add_raw does partial copy TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_add_raw(mtp_container_info_t* p_container, const void* data, uint32_t len) { - uint8_t* buf = mtp_container_payload_next(p_container); + uint8_t* buf = mtp_container_payload_ptr(p_container); const uint32_t added_len = tu_min32(len, CFG_TUD_MTP_EP_BUFSIZE - p_container->header->len); if (added_len > 0) { memcpy(buf, data, added_len); -- cgit v1.3.1 From bc688ccbad78053ee47306307a91e8f2d9d51e38 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 27 Sep 2025 18:28:11 +0700 Subject: add edpt claim for public API --- src/class/mtp/mtp_device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index a72712795..86d18a942 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -218,6 +218,7 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); if (xact_len) { // already transferred all bytes in header's length. Application make an unnecessary extra call + TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, ep_addr)); TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, ep_addr, _mtpd_epbuf.buf, xact_len)); } return true; @@ -236,8 +237,8 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container) { p_mtp->phase = MTP_PHASE_RESPONSE; p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; p_container->header->transaction_id = p_mtp->command.transaction_id; - TU_ASSERT(usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len)); - return true; + TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_in)); + return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len); } bool tud_mtp_mounted(void) { @@ -250,7 +251,6 @@ bool tud_mtp_event_send(mtp_event_t* event) { TU_VERIFY(p_mtp->ep_event != 0); _mtpd_epbuf.buf_event = *event; TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_event)); // Claim the endpoint - return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_event, (uint8_t*) &_mtpd_epbuf.buf_event, sizeof(mtp_event_t)); } -- cgit v1.3.1 From a09c65c4e4860d215648a3fc2d5f768f292e0e01 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 27 Sep 2025 18:43:46 +0700 Subject: make command container more consistent --- examples/device/mtp/src/mtp_fs_example.c | 6 +++--- src/class/mtp/mtp.h | 5 +---- src/class/mtp/mtp_device.c | 10 +++++----- tools/iar_template.ipcf | 1 - 4 files changed, 9 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 72fff5402..5b582e7db 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -228,7 +228,7 @@ int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; uint16_t resp_code = 0; - switch (command->code) { + switch (command->header.code) { case MTP_OP_GET_DEVICE_INFO: { // Device info is already prepared up to playback formats. Application only need to add string fields mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER); @@ -452,7 +452,7 @@ int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; uint16_t resp_code = 0; - switch (command->code) { + switch (command->header.code) { case MTP_OP_GET_OBJECT: { // File contents span over multiple xfers const uint32_t obj_handle = command->params[0]; @@ -540,7 +540,7 @@ int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* resp = &cb_data->io_container; - switch (command->code) { + switch (command->header.code) { case MTP_OP_SEND_OBJECT_INFO: { fs_file_t* f = fs_get_file(send_obj_handle); if (f == NULL) { diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index a8123b380..84fd1b429 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -649,10 +649,7 @@ typedef struct TU_ATTR_PACKED { TU_VERIFY_STATIC(sizeof(mtp_container_header_t) == 12, "size is not correct"); typedef struct TU_ATTR_PACKED { - uint32_t len; - uint16_t type; - uint16_t code; - uint32_t transaction_id; + mtp_container_header_t header; uint32_t params[5]; } mtp_container_command_t; TU_VERIFY_STATIC(sizeof(mtp_container_command_t) == 32, "size is not correct"); diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 86d18a942..f3f594dcf 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -204,7 +204,7 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { p_mtp->total_len = p_container->header->len; p_container->header->type = MTP_CONTAINER_TYPE_DATA_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; + p_container->header->transaction_id = p_mtp->command.header.transaction_id; p_mtp->io_header = *p_container->header; // save header for subsequent data } else { // OUT transfer: total length is at least max packet size @@ -236,7 +236,7 @@ bool tud_mtp_response_send(mtp_container_info_t* p_container) { mtpd_interface_t* p_mtp = &_mtpd_itf; p_mtp->phase = MTP_PHASE_RESPONSE; p_container->header->type = MTP_CONTAINER_TYPE_RESPONSE_BLOCK; - p_container->header->transaction_id = p_mtp->command.transaction_id; + p_container->header->transaction_id = p_mtp->command.header.transaction_id; TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, p_mtp->ep_in)); return usbd_edpt_xfer(p_mtp->rhport, p_mtp->ep_in, _mtpd_epbuf.buf, (uint16_t)p_container->header->len); } @@ -376,8 +376,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t mtp_generic_container_t* p_container = (mtp_generic_container_t*) _mtpd_epbuf.buf; #if CFG_TUSB_DEBUG >= CFG_TUD_MTP_LOG_LEVEL - tu_lookup_find(&_mtp_op_table, p_mtp->command.code); - TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.code), + tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code); + TU_LOG_DRV(" MTP %s: %s phase\r\n", (const char *) tu_lookup_find(&_mtp_op_table, p_mtp->command.header.code), _mtp_phase_str[p_mtp->phase]); #endif @@ -488,7 +488,7 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // pre-processed commands void preprocess_cmd(mtpd_interface_t* p_mtp, tud_mtp_cb_data_t* cb_data) { - switch (p_mtp->command.code) { + switch (p_mtp->command.header.code) { case MTP_OP_GET_DEVICE_INFO: { tud_mtp_device_info_t dev_info = { .standard_version = 100, diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf index c93795b9c..2581a4702 100644 --- a/tools/iar_template.ipcf +++ b/tools/iar_template.ipcf @@ -62,7 +62,6 @@ $TUSB_DIR$/src/class/mtp/mtp_device.c $TUSB_DIR$/src/class/mtp/mtp.h $TUSB_DIR$/src/class/mtp/mtp_device.h - $TUSB_DIR$/src/class/mtp/mtp_device_storage.h $TUSB_DIR$/src/class/net/ecm_rndis_device.c -- cgit v1.3.1 From 8952838a265ef3f5956d41670b26850ee9641ec8 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sat, 27 Sep 2025 22:17:10 +0200 Subject: audio: simplify alt settings management Signed-off-by: HiFiPhile --- .../device/audio_4_channel_mic/src/tusb_config.h | 1 - .../audio_4_channel_mic_freertos/src/tusb_config.h | 1 - examples/device/audio_test/src/tusb_config.h | 1 - .../device/audio_test_freertos/src/tusb_config.h | 1 - .../device/audio_test_multi_rate/src/tusb_config.h | 1 - examples/device/cdc_uac2/src/tusb_config.h | 3 - examples/device/uac2_headset/src/tusb_config.h | 3 - examples/device/uac2_speaker_fb/src/tusb_config.h | 5 +- src/class/audio/audio_device.c | 150 +++++---------------- src/class/audio/audio_device.h | 15 --- 10 files changed, 38 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/examples/device/audio_4_channel_mic/src/tusb_config.h b/examples/device/audio_4_channel_mic/src/tusb_config.h index 446a7a32a..0ee3ba2d0 100644 --- a/examples/device/audio_4_channel_mic/src/tusb_config.h +++ b/examples/device/audio_4_channel_mic/src/tusb_config.h @@ -107,7 +107,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_FOUR_CH_DESC_LEN -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 diff --git a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h index f7c0efe08..d973be2af 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h +++ b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h @@ -113,7 +113,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_FOUR_CH_DESC_LEN -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 diff --git a/examples/device/audio_test/src/tusb_config.h b/examples/device/audio_test/src/tusb_config.h index b38958d7c..10bf53809 100644 --- a/examples/device/audio_test/src/tusb_config.h +++ b/examples/device/audio_test/src/tusb_config.h @@ -109,7 +109,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000 #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 diff --git a/examples/device/audio_test_freertos/src/tusb_config.h b/examples/device/audio_test_freertos/src/tusb_config.h index b708e2541..c9dc50082 100644 --- a/examples/device/audio_test_freertos/src/tusb_config.h +++ b/examples/device/audio_test_freertos/src/tusb_config.h @@ -115,7 +115,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 48000 #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 diff --git a/examples/device/audio_test_multi_rate/src/tusb_config.h b/examples/device/audio_test_multi_rate/src/tusb_config.h index d9b925dcf..b48c0a0be 100644 --- a/examples/device/audio_test_multi_rate/src/tusb_config.h +++ b/examples/device/audio_test_multi_rate/src/tusb_config.h @@ -123,7 +123,6 @@ extern "C" { // Have a look into audio_device.h for all configurations #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_2_FORMAT_DESC_LEN -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 diff --git a/examples/device/cdc_uac2/src/tusb_config.h b/examples/device/cdc_uac2/src/tusb_config.h index 2318ccf73..2e744f8d2 100644 --- a/examples/device/cdc_uac2/src/tusb_config.h +++ b/examples/device/cdc_uac2/src/tusb_config.h @@ -157,9 +157,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device -// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2 - // Size of control request buffer #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h index e738a09af..e9165163b 100644 --- a/examples/device/uac2_headset/src/tusb_config.h +++ b/examples/device/uac2_headset/src/tusb_config.h @@ -158,9 +158,6 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device -// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2 - // Size of control request buffer #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 diff --git a/examples/device/uac2_speaker_fb/src/tusb_config.h b/examples/device/uac2_speaker_fb/src/tusb_config.h index fd4925c7f..18ab2ff96 100644 --- a/examples/device/uac2_speaker_fb/src/tusb_config.h +++ b/examples/device/uac2_speaker_fb/src/tusb_config.h @@ -130,7 +130,7 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN -// Enable if Full-Speed on OSX, also set feedback EP size to 3 +// Can be enabled with Full-Speed device on OSX, which forces feedback EP size to 3, in this case CFG_QUIRK_OS_GUESSING can be disabled #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // Audio format type I specifications @@ -155,9 +155,6 @@ extern "C" { // Enable feedback EP #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1 -// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 - // Size of control request buffer #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index bb906c7cf..701411401 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -192,17 +192,6 @@ tu_static CFG_TUD_MEM_SECTION struct { #endif } ctrl_buf; -// Active alternate setting of interfaces -tu_static uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT]; - -#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0 -tu_static uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT]; -#endif - -#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0 -tu_static uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT]; -#endif - // Aligned buffer for feedback EP #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP tu_static CFG_TUD_MEM_SECTION struct { @@ -234,13 +223,14 @@ typedef struct uint8_t ep_in; // TX audio data EP. uint16_t ep_in_sz; // Current size of TX EP uint8_t ep_in_as_intf_num;// Corresponding Standard AS Interface Descriptor (4.9.1) belonging to output terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero) -#endif + uint8_t ep_in_alt; // Current alternate setting of TX EP + #endif #if CFG_TUD_AUDIO_ENABLE_EP_OUT uint8_t ep_out; // Incoming (into uC) audio data EP. uint16_t ep_out_sz; // Current size of RX EP uint8_t ep_out_as_intf_num;// Corresponding Standard AS Interface Descriptor (4.9.1) belonging to input terminal to which this EP belongs - 0 is invalid (this fits to UAC2 specification since AS interfaces can not have interface number equal to zero) - + uint8_t ep_out_alt; // Current alternate setting of RX EP #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP uint8_t ep_fb;// Feedback EP. #endif @@ -304,9 +294,6 @@ typedef struct uint8_t *ctrl_buf; uint8_t ctrl_buf_sz; - // Current active alternate settings - uint8_t *alt_setting;// We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP! - // EP Transfer buffers and FIFOs #if CFG_TUD_AUDIO_ENABLE_EP_OUT tu_fifo_t ep_out_ff; @@ -475,8 +462,6 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p_request); static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p_request); -static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int); -static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int); static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id); static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id); static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id); @@ -529,11 +514,7 @@ tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) { } static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) { - uint8_t idxItf; - uint8_t const *dummy2; - uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio); - TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2)); #if USE_LINEAR_BUFFER_RX // Data currently is in linear buffer, copy into EP OUT FIFO @@ -553,7 +534,7 @@ static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_ #endif // Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO - TU_VERIFY(tud_audio_rx_done_isr(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf])); + TU_VERIFY(tud_audio_rx_done_isr(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->ep_out_alt)); return true; } @@ -584,14 +565,10 @@ tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) { } static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16_t n_bytes_sent) { - uint8_t idxItf; - uint8_t const *dummy2; - uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio); - TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, &idxItf, &dummy2)); // Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications - if (audio->alt_setting[idxItf] == 0) { return false; } + if (audio->ep_in_alt == 0) { return false; } // Send everything in ISO EP FIFO uint16_t n_bytes_tx; @@ -611,7 +588,7 @@ static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16 #endif // Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer - TU_VERIFY(tud_audio_tx_done_isr(rhport, n_bytes_sent, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf])); + TU_VERIFY(tud_audio_tx_done_isr(rhport, n_bytes_sent, idx_audio_fct, audio->ep_in, audio->ep_in_alt)); return true; } @@ -704,25 +681,6 @@ void audiod_init(void) { #endif } - // Initialize active alternate interface buffers - switch (i) { -#if CFG_TUD_AUDIO_FUNC_1_N_AS_INT > 0 - case 0: - audio->alt_setting = alt_setting_1; - break; -#endif -#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_N_AS_INT > 0 - case 1: - audio->alt_setting = alt_setting_2; - break; -#endif -#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_N_AS_INT > 0 - case 2: - audio->alt_setting = alt_setting_3; - break; -#endif - } - // Initialize IN EP FIFO if required #if CFG_TUD_AUDIO_ENABLE_EP_IN @@ -1029,13 +987,25 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p uint8_t const itf = tu_u16_low(p_request->wIndex); // Find index of audio streaming interface - uint8_t func_id, idxItf; - uint8_t const *dummy; + uint8_t func_id; + TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); + + // Default to 0 if interface not yet activated + uint8_t alt = 0; +#if CFG_TUD_AUDIO_ENABLE_EP_IN + if (_audiod_fct[func_id].ep_in_as_intf_num == itf) { + alt = _audiod_fct[func_id].ep_in_alt; + } +#endif +#if CFG_TUD_AUDIO_ENABLE_EP_OUT + if (_audiod_fct[func_id].ep_out_as_intf_num == itf) { + alt = _audiod_fct[func_id].ep_out_alt; + } +#endif - TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy)); - TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1)); + TU_VERIFY(tud_control_xfer(rhport, p_request, &alt, 1)); - TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]); + TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, alt); return true; } @@ -1060,9 +1030,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p TU_LOG2(" Set itf: %u - alt: %u\r\n", itf, alt); // Find index of audio streaming interface and index of interface - uint8_t func_id, idxItf; - uint8_t const *p_desc; - TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc)); + uint8_t func_id; + TU_VERIFY(audiod_verify_itf_exists(itf, &func_id)); audiod_function_t *audio = &_audiod_fct[func_id]; @@ -1070,6 +1039,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p #if CFG_TUD_AUDIO_ENABLE_EP_IN if (audio->ep_in_as_intf_num == itf) { audio->ep_in_as_intf_num = 0; + audio->ep_in_alt = 0; #ifndef TUP_DCD_EDPT_ISO_ALLOC usbd_edpt_close(rhport, audio->ep_in); #endif @@ -1093,6 +1063,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p #if CFG_TUD_AUDIO_ENABLE_EP_OUT if (audio->ep_out_as_intf_num == itf) { audio->ep_out_as_intf_num = 0; + audio->ep_out_alt = 0; #ifndef TUP_DCD_EDPT_ISO_ALLOC usbd_edpt_close(rhport, audio->ep_out); #endif @@ -1116,10 +1087,10 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p } #endif// CFG_TUD_AUDIO_ENABLE_EP_OUT - // Save current alternative interface setting - audio->alt_setting[idxItf] = alt; - // Open new EP if necessary - EPs are only to be closed or opened for AS interfaces - Look for AS interface with correct alternate interface + uint8_t const *p_desc = tu_desc_next(audio->p_desc); + // Skip entire AC descriptor block + p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength; // Get pointer at end uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; @@ -1154,6 +1125,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p // Save address audio->ep_in = ep_addr; audio->ep_in_as_intf_num = itf; + audio->ep_in_alt = alt; audio->ep_in_sz = tu_edpt_packet_size(desc_ep); // If flow control is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters @@ -1176,6 +1148,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p // Save address audio->ep_out = ep_addr; audio->ep_out_as_intf_num = itf; + audio->ep_out_alt = alt; audio->ep_out_sz = tu_edpt_packet_size(desc_ep); // Prepare for incoming data @@ -1474,8 +1447,7 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint #if CFG_TUD_AUDIO_ENABLE_EP_IN // Data transmission of audio packet finished - if (audio->ep_in == ep_addr && audio->alt_setting != 0) - { + if (audio->ep_in == ep_addr) { // USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified." // That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available." // This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP. @@ -1492,8 +1464,7 @@ bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint #if CFG_TUD_AUDIO_ENABLE_EP_OUT // New audio packet received - if (audio->ep_out == ep_addr) - { + if (audio->ep_out == ep_addr) { audiod_rx_xfer_isr(rhport, audio, (uint16_t) xferred_bytes); return true; } @@ -1694,53 +1665,6 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req return tud_control_xfer(rhport, p_request, (void *) _audiod_fct[func_id].ctrl_buf, len); } -// This helper function finds for a given audio function and AS interface number the index of the attached driver structure, the index of the interface in the audio function -// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and -// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero. -static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int) { - if (audio->p_desc) { - // Get pointer at end - uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN; - - // Advance past AC descriptors - uint8_t const *p_desc = tu_desc_next(audio->p_desc); - p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength; - - uint8_t tmp = 0; - // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning - while (p_desc_end - p_desc > 0) { - // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero! - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bAlternateSetting == 0) { - if (((tusb_desc_interface_t const *) p_desc)->bInterfaceNumber == itf) { - *idxItf = tmp; - *pp_desc_int = p_desc; - return true; - } - // Increase index, bytes read, and pointer - tmp++; - } - p_desc = tu_desc_next(p_desc); - } - } - return false; -} - -// This helper function finds for a given AS interface number the index of the attached driver structure, the index of the interface in the audio function -// (e.g. the std. AS interface with interface number 15 is the first AS interface for the given audio function and thus gets index zero), and -// finally a pointer to the std. AS interface, where the pointer always points to the first alternate setting i.e. alternate interface zero. -static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int) { - // Loop over audio driver interfaces - uint8_t i; - for (i = 0; i < CFG_TUD_AUDIO; i++) { - if (audiod_get_AS_interface_index(itf, &_audiod_fct[i], idxItf, pp_desc_int)) { - *func_id = i; - return true; - } - } - - return false; -} - // Verify an entity with the given ID exists and returns also the corresponding driver index static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id) { uint8_t i; @@ -1754,8 +1678,8 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t * // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning while (p_desc_end - p_desc > 0) { - if (p_desc[3] == entityID)// Entity IDs are always at offset 3 - { + // Entity IDs are always at offset 3 + if (p_desc[3] == entityID) { *func_id = i; return true; } @@ -1775,7 +1699,7 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) { uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning while (p_desc_end - p_desc > 0) { - if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) { + if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)p_desc)->bInterfaceNumber == itf) { *func_id = i; return true; } diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h index 6bcedc88c..fd47c649d 100644 --- a/src/class/audio/audio_device.h +++ b/src/class/audio/audio_device.h @@ -51,21 +51,6 @@ #endif #endif -// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces -#ifndef CFG_TUD_AUDIO_FUNC_1_N_AS_INT -#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor! -#endif -#if CFG_TUD_AUDIO > 1 -#ifndef CFG_TUD_AUDIO_FUNC_2_N_AS_INT -#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor! -#endif -#endif -#if CFG_TUD_AUDIO > 2 -#ifndef CFG_TUD_AUDIO_FUNC_3_N_AS_INT -#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor! -#endif -#endif - // Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors #ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ #error You must define an audio class control request buffer size! -- cgit v1.3.1 From 4f3a5f92fa21307b1f1c9700cd041e945a627f63 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 29 Sep 2025 15:02:51 +0700 Subject: refactor mtp examples, add phase and session id to callback data --- examples/device/mtp/src/mtp_fs_example.c | 625 +++++++++++++++++-------------- src/class/mtp/mtp_device.c | 3 + src/class/mtp/mtp_device.h | 6 + 3 files changed, 350 insertions(+), 284 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 5b582e7db..09a5f4ffc 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -130,14 +130,50 @@ static fs_file_t fs_objects[FS_MAX_FILE_COUNT] = { } }; - enum { SUPPORTED_STORAGE_ID = 0x00010001u // physical = 1, logical = 1 }; +static int32_t fs_get_device_info(tud_mtp_cb_data_t* cb_data); +static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_storage_ids(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_storage_info(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data); +static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data); +static int32_t fs_delete_object(tud_mtp_cb_data_t* cb_data); +static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data); +static int32_t fs_send_object(tud_mtp_cb_data_t* cb_data); + +typedef int32_t (*fs_op_handler_t)(tud_mtp_cb_data_t* cb_data); +typedef struct { + uint32_t op_code; + fs_op_handler_t handler; +}fs_op_handler_dict_t; + +fs_op_handler_dict_t fs_op_handler_dict[] = { + { MTP_OP_GET_DEVICE_INFO, fs_get_device_info }, + { MTP_OP_OPEN_SESSION, fs_open_close_session }, + { MTP_OP_CLOSE_SESSION, fs_open_close_session }, + { MTP_OP_GET_STORAGE_IDS, fs_get_storage_ids }, + { MTP_OP_GET_STORAGE_INFO, fs_get_storage_info }, + { MTP_OP_GET_DEVICE_PROP_DESC, fs_get_device_properties }, + { MTP_OP_GET_DEVICE_PROP_VALUE, fs_get_device_properties }, + { MTP_OP_GET_OBJECT_HANDLES, fs_get_object_handles }, + { MTP_OP_GET_OBJECT_INFO, fs_get_object_info }, + { MTP_OP_GET_OBJECT, fs_get_object }, + { MTP_OP_DELETE_OBJECT, fs_delete_object }, + { MTP_OP_SEND_OBJECT_INFO, fs_send_object_info }, + { MTP_OP_SEND_OBJECT, fs_send_object }, +}; + static bool is_session_opened = false; static uint32_t send_obj_handle = 0; +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ // Get pointer to object info from handle static inline fs_file_t* fs_get_file(uint32_t handle) { if (handle == 0 || handle > FS_MAX_FILE_COUNT) { @@ -227,344 +263,365 @@ int32_t tud_mtp_request_get_device_status_cb(tud_mtp_request_cb_data_t* cb_data) int32_t tud_mtp_command_received_cb(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; - uint16_t resp_code = 0; - switch (command->header.code) { - case MTP_OP_GET_DEVICE_INFO: { - // Device info is already prepared up to playback formats. Application only need to add string fields - mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER); - mtp_container_add_cstring(io_container, DEV_INFO_MODEL); - mtp_container_add_cstring(io_container, DEV_INFO_VERSION); - - uint16_t serial_utf16[32]; - board_usb_get_serial(serial_utf16, 32); - serial_utf16[31] = 0; // ensure null termination - mtp_container_add_string(io_container, serial_utf16); - - tud_mtp_data_send(io_container); + fs_op_handler_t handler = NULL; + for (size_t i = 0; i < TU_ARRAY_SIZE(fs_op_handler_dict); i++) { + if (fs_op_handler_dict[i].op_code == command->header.code) { + handler = fs_op_handler_dict[i].handler; break; } + } - case MTP_OP_OPEN_SESSION: - if (is_session_opened) { - resp_code = MTP_RESP_SESSION_ALREADY_OPEN; - }else { - resp_code = MTP_RESP_OK; - } - is_session_opened = true; - break; + int32_t resp_code; + if (handler == NULL) { + resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; + } else { + resp_code = handler(cb_data); + if (resp_code > MTP_RESP_UNDEFINED) { + // send response if needed + io_container->header->code = (uint16_t)resp_code; + tud_mtp_response_send(io_container); + } + } - case MTP_OP_CLOSE_SESSION: - if (!is_session_opened) { - resp_code = MTP_RESP_SESSION_NOT_OPEN; - } else { - resp_code = MTP_RESP_OK; - } - is_session_opened = false; - break; + return resp_code; +} - case MTP_OP_GET_STORAGE_IDS: { - uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; - mtp_container_add_auint32(io_container, 1, storage_ids); - tud_mtp_data_send(io_container); - break; - } +int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; - case MTP_OP_GET_STORAGE_INFO: { - const uint32_t storage_id = command->params[0]; - TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); - // update storage info with current free space - storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES; - storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count(); - storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; - mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info)); - tud_mtp_data_send(io_container); + fs_op_handler_t handler = NULL; + for (size_t i = 0; i < TU_ARRAY_SIZE(fs_op_handler_dict); i++) { + if (fs_op_handler_dict[i].op_code == command->header.code) { + handler = fs_op_handler_dict[i].handler; break; } + } - case MTP_OP_GET_DEVICE_PROP_DESC: { - const uint16_t dev_prop_code = (uint16_t) command->params[0]; - mtp_device_prop_desc_header_t device_prop_header; - device_prop_header.device_property_code = dev_prop_code; - switch (dev_prop_code) { - case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - device_prop_header.datatype = MTP_DATA_TYPE_STR; - device_prop_header.get_set = MTP_MODE_GET; - mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header)); - mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory - mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current - mtp_container_add_uint8(io_container, 0); // no form - tud_mtp_data_send(io_container); - break; - - default: - resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - break; - } - break; + int32_t resp_code; + if (handler == NULL) { + resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; + } else { + resp_code = handler(cb_data); + if (resp_code > MTP_RESP_UNDEFINED) { + // send response if needed + io_container->header->code = (uint16_t)resp_code; + tud_mtp_response_send(io_container); } + } + + return 0; +} - case MTP_OP_GET_DEVICE_PROP_VALUE: { - const uint16_t dev_prop_code = (uint16_t) command->params[0]; - switch (dev_prop_code) { - case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: - mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); - tud_mtp_data_send(io_container); - break; - - default: - resp_code = MTP_RESP_PARAMETER_NOT_SUPPORTED; - break; +int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* resp = &cb_data->io_container; + switch (command->header.code) { + case MTP_OP_SEND_OBJECT_INFO: { + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + resp->header->code = MTP_RESP_GENERAL_ERROR; + break; } + // parameter is: storage id, parent handle, new handle + mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); + mtp_container_add_uint32(resp, f->parent); + mtp_container_add_uint32(resp, send_obj_handle); + resp->header->code = MTP_RESP_OK; break; } - case MTP_OP_GET_OBJECT_HANDLES: { - const uint32_t storage_id = command->params[0]; - const uint32_t obj_format = command->params[1]; // optional - (void) obj_format; - const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root - if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { - resp_code = MTP_RESP_INVALID_STORAGE_ID; - } else { - uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; - uint32_t count = 0; - for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { - fs_file_t* f = &fs_objects[i]; - if (fs_file_exist(f) && - (parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) { - handles[count++] = i + 1; // handle is index + 1 - } - } - mtp_container_add_auint32(io_container, count, handles); - tud_mtp_data_send(io_container); - } + default: + resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; break; - } + } - case MTP_OP_GET_OBJECT_INFO: { - const uint32_t obj_handle = command->params[0]; - fs_file_t* f = fs_get_file(obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - mtp_object_info_header_t obj_info_header = { - .storage_id = SUPPORTED_STORAGE_ID, - .object_format = f->object_format, - .protection_status = f->protection_status, - .object_compressed_size = f->size, - .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, - .thumb_compressed_size = 0, - .thumb_pix_width = 0, - .thumb_pix_height = 0, - .image_pix_width = f->image_pix_width, - .image_pix_height = f->image_pix_height, - .image_bit_depth = f->image_bit_depth, - .parent_object = f->parent, - .association_type = f->association_type, - .association_desc = 0, - .sequence_number = 0 - }; - mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header)); - mtp_container_add_string(io_container, f->name); - mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); - mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); - mtp_container_add_cstring(io_container, ""); // keywords, not used + tud_mtp_response_send(resp); + return 0; +} - tud_mtp_data_send(io_container); - } - break; +int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { + (void) cb_data; + return 0; // nothing to do +} + +//--------------------------------------------------------------------+ +// File System Handlers +//--------------------------------------------------------------------+ +static int32_t fs_get_device_info(tud_mtp_cb_data_t* cb_data) { + // Device info is already prepared up to playback formats. Application only need to add string fields + mtp_container_info_t* io_container = &cb_data->io_container; + mtp_container_add_cstring(io_container, DEV_INFO_MANUFACTURER); + mtp_container_add_cstring(io_container, DEV_INFO_MODEL); + mtp_container_add_cstring(io_container, DEV_INFO_VERSION); + + uint16_t serial_utf16[32]; + board_usb_get_serial(serial_utf16, 32); + serial_utf16[31] = 0; // ensure null termination + mtp_container_add_string(io_container, serial_utf16); + + tud_mtp_data_send(io_container); + return 0; +} + +static int32_t fs_open_close_session(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + if (command->header.code == MTP_OP_OPEN_SESSION) { + if (is_session_opened) { + return MTP_RESP_SESSION_ALREADY_OPEN; + } + is_session_opened = true; + } else { // close session + if (!is_session_opened) { + return MTP_RESP_SESSION_NOT_OPEN; } + is_session_opened = false; + } + return MTP_RESP_OK; +} - case MTP_OP_GET_OBJECT: { - const uint32_t obj_handle = command->params[0]; - fs_file_t* f = fs_get_file(obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, only partial data is added here - // the rest will be sent in tud_mtp_data_more_cb - mtp_container_add_raw(io_container, f->data, f->size); +static int32_t fs_get_storage_ids(tud_mtp_cb_data_t* cb_data) { + mtp_container_info_t* io_container = &cb_data->io_container; + uint32_t storage_ids [] = { SUPPORTED_STORAGE_ID }; + mtp_container_add_auint32(io_container, 1, storage_ids); + tud_mtp_data_send(io_container); + return 0; +} + +static int32_t fs_get_storage_info(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; + const uint32_t storage_id = command->params[0]; + TU_VERIFY(SUPPORTED_STORAGE_ID == storage_id, -1); + // update storage info with current free space + storage_info.max_capacity_in_bytes = sizeof(README_TXT_CONTENT) + logo_len + FS_MAX_CAPACITY_BYTES; + storage_info.free_space_in_objects = FS_MAX_FILE_COUNT - fs_get_file_count(); + storage_info.free_space_in_bytes = FS_MAX_CAPACITY_BYTES-fs_buf_head; + mtp_container_add_raw(io_container, &storage_info, sizeof(storage_info)); + tud_mtp_data_send(io_container); + return 0; +} + +static int32_t fs_get_device_properties(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; + const uint16_t dev_prop_code = (uint16_t) command->params[0]; + + if (command->header.code == MTP_OP_GET_DEVICE_PROP_DESC) { + // get describing dataset + mtp_device_prop_desc_header_t device_prop_header; + device_prop_header.device_property_code = dev_prop_code; + switch (dev_prop_code) { + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: + device_prop_header.datatype = MTP_DATA_TYPE_STR; + device_prop_header.get_set = MTP_MODE_GET; + mtp_container_add_raw(io_container, &device_prop_header, sizeof(device_prop_header)); + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // factory + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); // current + mtp_container_add_uint8(io_container, 0); // no form tud_mtp_data_send(io_container); - } - break; - } + break; - case MTP_OP_SEND_OBJECT_INFO: { - const uint32_t storage_id = command->params[0]; - const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root - (void) parent_handle; - if (!is_session_opened) { - resp_code = MTP_RESP_SESSION_NOT_OPEN; - } else if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { - resp_code = MTP_RESP_INVALID_STORAGE_ID; - } else { - tud_mtp_data_receive(io_container); - } - break; + default: + return MTP_RESP_PARAMETER_NOT_SUPPORTED; } + } else { + // get value + switch (dev_prop_code) { + case MTP_DEV_PROP_DEVICE_FRIENDLY_NAME: + mtp_container_add_cstring(io_container, DEV_PROP_FRIENDLY_NAME); + tud_mtp_data_send(io_container); + break; - case MTP_OP_SEND_OBJECT: { - fs_file_t* f = fs_get_file(send_obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - io_container->header->len += f->size; - tud_mtp_data_receive(io_container); - } - break; + default: + return MTP_RESP_PARAMETER_NOT_SUPPORTED; } + } + return 0; +} - case MTP_OP_DELETE_OBJECT: { - if (!is_session_opened) { - resp_code = MTP_RESP_SESSION_NOT_OPEN; - } else { - const uint32_t obj_handle = command->params[0]; - const uint32_t obj_format = command->params[1]; // optional - (void) obj_format; - fs_file_t* f = fs_get_file(obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - break; - } - - // delete object by clear the name - f->name[0] = 0; - resp_code = MTP_RESP_OK; - } - break; - } +static int32_t fs_get_object_handles(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; - default: - resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; - break; + const uint32_t storage_id = command->params[0]; + const uint32_t obj_format = command->params[1]; // optional + const uint32_t parent_handle = command->params[2]; // folder handle, 0xFFFFFFFF is root + (void)obj_format; + + if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { + return MTP_RESP_INVALID_STORAGE_ID; } - // send response if needed - if (resp_code != 0) { - io_container->header->code = resp_code; - tud_mtp_response_send(io_container); + uint32_t handles[FS_MAX_FILE_COUNT] = { 0 }; + uint32_t count = 0; + for (uint8_t i = 0; i < FS_MAX_FILE_COUNT; i++) { + fs_file_t* f = &fs_objects[i]; + if (fs_file_exist(f) && + (parent_handle == f->parent || (parent_handle == 0xFFFFFFFF && f->parent == 0))) { + handles[count++] = i + 1; // handle is index + 1 + } } + mtp_container_add_auint32(io_container, count, handles); + tud_mtp_data_send(io_container); return 0; } -int32_t tud_mtp_data_xfer_cb(tud_mtp_cb_data_t* cb_data) { +static int32_t fs_get_object_info(tud_mtp_cb_data_t* cb_data) { const mtp_container_command_t* command = cb_data->command_container; mtp_container_info_t* io_container = &cb_data->io_container; - uint16_t resp_code = 0; - switch (command->header.code) { - case MTP_OP_GET_OBJECT: { - // File contents span over multiple xfers - const uint32_t obj_handle = command->params[0]; - fs_file_t* f = fs_get_file(obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - // file contents offset is xferred byte minus header size - const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t); - const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_bytes); - memcpy(io_container->payload, f->data + offset, xact_len); - tud_mtp_data_send(io_container); - } - break; + const uint32_t obj_handle = command->params[0]; + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + mtp_object_info_header_t obj_info_header = { + .storage_id = SUPPORTED_STORAGE_ID, + .object_format = f->object_format, + .protection_status = f->protection_status, + .object_compressed_size = f->size, + .thumb_format = MTP_OBJ_FORMAT_UNDEFINED, + .thumb_compressed_size = 0, + .thumb_pix_width = 0, + .thumb_pix_height = 0, + .image_pix_width = f->image_pix_width, + .image_pix_height = f->image_pix_height, + .image_bit_depth = f->image_bit_depth, + .parent_object = f->parent, + .association_type = f->association_type, + .association_desc = 0, + .sequence_number = 0 + }; + mtp_container_add_raw(io_container, &obj_info_header, sizeof(obj_info_header)); + mtp_container_add_string(io_container, f->name); + mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); + mtp_container_add_cstring(io_container, FS_FIXED_DATETIME); + mtp_container_add_cstring(io_container, ""); // keywords, not used + tud_mtp_data_send(io_container); + + return 0; +} + +static int32_t fs_get_object(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; + const uint32_t obj_handle = command->params[0]; + const fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + + if (cb_data->phase == MTP_PHASE_COMMAND) { + // If file contents is larger than CFG_TUD_MTP_EP_BUFSIZE, data may only partially is added here + // the rest will be sent in tud_mtp_data_more_cb + mtp_container_add_raw(io_container, f->data, f->size); + tud_mtp_data_send(io_container); + } else if (cb_data->phase == MTP_PHASE_DATA) { + // continue sending remaining data: file contents offset is xferred byte minus header size + const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t); + const uint32_t xact_len = tu_min32(f->size - offset, io_container->payload_bytes); + if (xact_len > 0) { + memcpy(io_container->payload, f->data + offset, xact_len); + tud_mtp_data_send(io_container); } + } - case MTP_OP_SEND_OBJECT_INFO: { - mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload; - if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) { - resp_code = MTP_RESP_INVALID_STORAGE_ID; - break; - } + return 0; +} - if (obj_info->parent_object) { - fs_file_t* parent = fs_get_file(obj_info->parent_object); - if (parent == NULL || !parent->association_type) { - resp_code = MTP_RESP_INVALID_PARENT_OBJECT; - break; - } - } +static int32_t fs_send_object_info(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + mtp_container_info_t* io_container = &cb_data->io_container; + const uint32_t storage_id = command->params[0]; + const uint32_t parent_handle = command->params[1]; // folder handle, 0xFFFFFFFF is root + (void) parent_handle; - uint8_t* f_buf = fs_malloc(obj_info->object_compressed_size); - if (f_buf == NULL) { - resp_code = MTP_RESP_STORE_FULL; - break; - } - fs_file_t* f = fs_create_file(); - if (f == NULL) { - resp_code = MTP_RESP_STORE_FULL; - break; - } + if (!is_session_opened) { + return MTP_RESP_SESSION_NOT_OPEN; + } + if (storage_id != 0xFFFFFFFF && storage_id != SUPPORTED_STORAGE_ID) { + return MTP_RESP_INVALID_STORAGE_ID; + } - f->object_format = obj_info->object_format; - f->protection_status = obj_info->protection_status; - f->image_pix_width = obj_info->image_pix_width; - f->image_pix_height = obj_info->image_pix_height; - f->image_bit_depth = obj_info->image_bit_depth; - f->parent = obj_info->parent_object; - f->association_type = obj_info->association_type; - f->size = obj_info->object_compressed_size; - f->data = f_buf; - uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); - mtp_container_get_string(buf, f->name); - // ignore date created/modified/keywords - break; + if (cb_data->phase == MTP_PHASE_COMMAND) { + tud_mtp_data_receive(io_container); + } else if (cb_data->phase == MTP_PHASE_DATA) { + mtp_object_info_header_t* obj_info = (mtp_object_info_header_t*) io_container->payload; + if (obj_info->storage_id != 0 && obj_info->storage_id != SUPPORTED_STORAGE_ID) { + return MTP_RESP_INVALID_STORAGE_ID; } - case MTP_OP_SEND_OBJECT: { - fs_file_t* f = fs_get_file(send_obj_handle); - if (f == NULL) { - resp_code = MTP_RESP_INVALID_OBJECT_HANDLE; - } else { - // file contents offset is total xferred minus header size minus last received chunk - const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) - io_container->payload_bytes; - memcpy(f->data + offset, io_container->payload, io_container->payload_bytes); - tud_mtp_data_receive(io_container); // receive more data if needed + if (obj_info->parent_object) { + fs_file_t* parent = fs_get_file(obj_info->parent_object); + if (parent == NULL || !parent->association_type) { + return MTP_RESP_INVALID_PARENT_OBJECT; } - break; } - default: - resp_code = MTP_RESP_OPERATION_NOT_SUPPORTED; - break; - } + uint8_t* f_buf = fs_malloc(obj_info->object_compressed_size); + if (f_buf == NULL) { + return MTP_RESP_STORE_FULL; + } + fs_file_t* f = fs_create_file(); + if (f == NULL) { + return MTP_RESP_STORE_FULL; + } - // send response if needed - if (resp_code != 0) { - io_container->header->code = resp_code; - tud_mtp_response_send(io_container); + f->object_format = obj_info->object_format; + f->protection_status = obj_info->protection_status; + f->image_pix_width = obj_info->image_pix_width; + f->image_pix_height = obj_info->image_pix_height; + f->image_bit_depth = obj_info->image_bit_depth; + f->parent = obj_info->parent_object; + f->association_type = obj_info->association_type; + f->size = obj_info->object_compressed_size; + f->data = f_buf; + uint8_t* buf = io_container->payload + sizeof(mtp_object_info_header_t); + mtp_container_get_string(buf, f->name); + // ignore date created/modified/keywords } - return 0; // 0 mean data/response is sent already + return 0; } -int32_t tud_mtp_data_complete_cb(tud_mtp_cb_data_t* cb_data) { - const mtp_container_command_t* command = cb_data->command_container; - mtp_container_info_t* resp = &cb_data->io_container; - switch (command->header.code) { - case MTP_OP_SEND_OBJECT_INFO: { - fs_file_t* f = fs_get_file(send_obj_handle); - if (f == NULL) { - resp->header->code = MTP_RESP_GENERAL_ERROR; - break; - } - // parameter is: storage id, parent handle, new handle - mtp_container_add_uint32(resp, SUPPORTED_STORAGE_ID); - mtp_container_add_uint32(resp, f->parent); - mtp_container_add_uint32(resp, send_obj_handle); - resp->header->code = MTP_RESP_OK; - break; - } +static int32_t fs_send_object(tud_mtp_cb_data_t* cb_data) { + mtp_container_info_t* io_container = &cb_data->io_container; + fs_file_t* f = fs_get_file(send_obj_handle); + if (f == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } - default: - resp->header->code = (cb_data->xfer_result == XFER_RESULT_SUCCESS) ? MTP_RESP_OK : MTP_RESP_GENERAL_ERROR; - break; + if (cb_data->phase == MTP_PHASE_COMMAND) { + io_container->header->len += f->size; + tud_mtp_data_receive(io_container); + } else { + // file contents offset is total xferred minus header size minus last received chunk + const uint32_t offset = cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) - io_container->payload_bytes; + memcpy(f->data + offset, io_container->payload, io_container->payload_bytes); + if (cb_data->total_xferred_bytes - sizeof(mtp_container_header_t) < f->size) { + tud_mtp_data_receive(io_container); + } } - tud_mtp_response_send(resp); return 0; } -int32_t tud_mtp_response_complete_cb(tud_mtp_cb_data_t* cb_data) { - (void) cb_data; - return 0; // nothing to do +static int32_t fs_delete_object(tud_mtp_cb_data_t* cb_data) { + const mtp_container_command_t* command = cb_data->command_container; + const uint32_t obj_handle = command->params[0]; + const uint32_t obj_format = command->params[1]; // optional + (void) obj_format; + + if (!is_session_opened) { + return MTP_RESP_SESSION_NOT_OPEN; + } + fs_file_t* f = fs_get_file(obj_handle); + if (f == NULL) { + return MTP_RESP_INVALID_OBJECT_HANDLE; + } + + // delete object by clear the name + f->name[0] = 0; + return MTP_RESP_OK; } diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index f3f594dcf..798a965eb 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -307,6 +307,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t tud_mtp_request_cb_data_t cb_data = { .idx = 0, .stage = stage, + .session_id = p_mtp->session_id, .request = request, .buf = p_mtp->control_buf, .bufsize = tu_le16toh(request->wLength), @@ -395,6 +396,8 @@ bool mtpd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t tud_mtp_cb_data_t cb_data; cb_data.idx = 0; + cb_data.phase = p_mtp->phase; + cb_data.session_id = p_mtp->session_id; cb_data.command_container = &p_mtp->command; cb_data.io_container = headered_packet; cb_data.total_xferred_bytes = 0; diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h index c69f6f12f..397fbbbce 100644 --- a/src/class/mtp/mtp_device.h +++ b/src/class/mtp/mtp_device.h @@ -39,6 +39,9 @@ // callback data for Bulk Only Transfer (BOT) protocol typedef struct { uint8_t idx; // mtp instance + uint8_t phase; // current phase + uint32_t session_id; + const mtp_container_command_t* command_container; mtp_container_info_t io_container; @@ -50,6 +53,8 @@ typedef struct { typedef struct { uint8_t idx; uint8_t stage; // control stage + uint32_t session_id; + const tusb_control_request_t* request; // buffer for data stage uint8_t* buf; @@ -95,6 +100,7 @@ bool tud_mtp_data_receive(mtp_container_info_t* p_container); // send response bool tud_mtp_response_send(mtp_container_info_t* p_container); +// send event notification on event endpoint bool tud_mtp_event_send(mtp_event_t* event); //--------------------------------------------------------------------+ -- cgit v1.3.1 From bd35dd17a8970c53a916ede6784137eafda8531b Mon Sep 17 00:00:00 2001 From: Mengsk Date: Mon, 29 Sep 2025 16:00:07 +0200 Subject: Fix PMA size for U0 Signed-off-by: Mengsk --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 95f40269a..0f2b9e068 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -200,7 +200,8 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 #include "stm32u0xx.h" - #define FSDEV_PMA_SIZE (2048u) + #define FSDEV_PMA_SIZE (1024u) + #define FSDEV_BUS_32BIT #define FSDEV_HAS_SBUF_ISO 1 #define USB USB_DRD_FS -- cgit v1.3.1 From 30f8a9dfae7d93130c2d1aebd9f2a5dc6a96785c Mon Sep 17 00:00:00 2001 From: Mengsk Date: Mon, 29 Sep 2025 16:07:35 +0200 Subject: Disable SBUF_ISO for U0 Signed-off-by: Mengsk --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 0f2b9e068..63b50f13d 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -202,7 +202,8 @@ #include "stm32u0xx.h" #define FSDEV_PMA_SIZE (1024u) #define FSDEV_BUS_32BIT - #define FSDEV_HAS_SBUF_ISO 1 + // Disable SBUF_ISO on U0 for now due to bad performance (audio glitching) + #define FSDEV_HAS_SBUF_ISO 0 #define USB USB_DRD_FS #define USB_EP_CTR_RX USB_EP_VTRX -- cgit v1.3.1 From 7281bfbfc4c19feb63c7691ce0e4f98ccaa0a143 Mon Sep 17 00:00:00 2001 From: Mengsk Date: Mon, 29 Sep 2025 16:07:46 +0200 Subject: Fix warning Signed-off-by: Mengsk --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 3b9825e25..ed823a832 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -678,6 +678,7 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet btable_set_addr(ep_idx, 1, pma_addr2); #else btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); + (void) pma_addr2; #endif xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); -- cgit v1.3.1 From 13c240134ac0ce0d830e0257ca97d955e032ab93 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 1 Oct 2025 16:53:44 +0700 Subject: minor update --- src/class/mtp/mtp.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/class/mtp/mtp.h b/src/class/mtp/mtp.h index 84fd1b429..40b6dd8b0 100644 --- a/src/class/mtp/mtp.h +++ b/src/class/mtp/mtp.h @@ -24,13 +24,10 @@ * This file is part of the TinyUSB stack. */ -#ifndef _TUSB_MTP_H_ -#define _TUSB_MTP_H_ +#ifndef TUSB_MTP_H_ +#define TUSB_MTP_H_ -#include -#include -#include -#include "tusb_option.h" +#include "common/tusb_common.h" #if (CFG_TUD_ENABLED && CFG_TUD_MTP) @@ -887,6 +884,5 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t mtp_container_get_string(uint8_t* b } #endif -#endif /* CFG_TUD_ENABLED && CFG_TUD_MTP */ - -#endif /* _TUSB_MTP_H_ */ +#endif +#endif -- cgit v1.3.1 From 981dc982ce2e8b37041e47d0c17f479d08f11bd6 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 2 Oct 2025 15:57:59 +0700 Subject: mtp example work with highspeed device --- examples/device/mtp/src/usb_descriptors.c | 72 ++++++++++++++++++++++++++++--- src/device/usbd.h | 2 +- 2 files changed, 68 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c index ddda4d686..cabb96439 100644 --- a/examples/device/mtp/src/usb_descriptors.c +++ b/examples/device/mtp/src/usb_descriptors.c @@ -109,18 +109,79 @@ enum #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MTP_DESC_LEN) -uint8_t const desc_fs_configuration[] = { +// full speed configuration +const uint8_t desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + // Interface number, string index, EP event, EP event size, EP event polling, EP Out & EP In address, EP size TUD_MTP_DESCRIPTOR(ITF_NUM_MTP, 4, EPNUM_MTP_EVT, 64, 1, EPNUM_MTP_OUT, EPNUM_MTP_IN, 64), }; +#if TUD_OPT_HIGH_SPEED +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +// high speed configuration +uint8_t const desc_hs_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + // Interface number, string index, EP event, EP event size, EP event polling, EP Out & EP In address, EP size + TUD_MTP_DESCRIPTOR(ITF_NUM_MTP, 4, EPNUM_MTP_EVT, 64, 1, EPNUM_MTP_OUT, EPNUM_MTP_IN, 512), +}; + +// other speed configuration +uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN]; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = USB_BCD, + + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const *tud_descriptor_device_qualifier_cb(void) { + return (uint8_t const *) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) { + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG + memcpy(desc_other_speed_config, + (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration, + CONFIG_TOTAL_LEN); + desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG; + return desc_other_speed_config; +} + +#endif // highspeed + // 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) { +const uint8_t*tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else return desc_fs_configuration; +#endif } //--------------------------------------------------------------------+ @@ -173,11 +234,12 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { } const char *str = string_desc_arr[index]; - // Cap at max char chr_count = strlen(str); - size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type - if ( chr_count > max_count ) chr_count = max_count; + const size_t max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type + if ( chr_count > max_count ) { + chr_count = max_count; + } // Convert ASCII string into UTF-16 for ( size_t i = 0; i < chr_count; i++ ) { diff --git a/src/device/usbd.h b/src/device/usbd.h index 1dc3761d9..a4104e47d 100644 --- a/src/device/usbd.h +++ b/src/device/usbd.h @@ -276,7 +276,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ // Length of template descriptor: 30 bytes #define TUD_MTP_DESC_LEN (9 + 7 + 7 + 7) -// Interface number, string index, EP Out & EP In address, EP size +// Interface number, string index, EP event, EP event size, EP event polling, EP Out & EP In address, EP size #define TUD_MTP_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_polling_interval, _epout, _epin, _epsize) \ /* Interface */\ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 3, TUSB_CLASS_IMAGE, MTP_SUBCLASS_STILL_IMAGE, MTP_PROTOCOL_PIMA_15470, _stridx,\ -- cgit v1.3.1 From b18a8fbcd5eacf948b430a996fe54b62da285ccd Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 2 Oct 2025 15:48:36 +0700 Subject: update for release 0.19.0 --- CLAUDE.md | 9 ++ docs/info/changelog.rst | 213 ++++++++++++++++++++++++++++++++++++++++ docs/reference/dependencies.rst | 10 +- library.json | 2 +- repository.yml | 3 +- src/tusb_option.h | 2 +- tools/make_release.py | 2 +- 7 files changed, 233 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/CLAUDE.md b/CLAUDE.md index 9cfe29aae..6c6baa246 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,3 +67,12 @@ Before building, it's recommended to run pre-commit to ensure code quality: - hw/: Board support packages and MCU drivers - examples/: Reference examples for device/host/dual - test/: Unit tests and hardware integration tests + +## Release Process +To prepare a new release: +1. Update the `version` variable in `tools/make_release.py` to the new version number +2. Run the release script: `python tools/make_release.py` + - This will update version numbers in `src/tusb_option.h`, `repository.yml`, and `library.json` + - It will also regenerate documentation +3. Update `docs/info/changelog.rst` with release notes +4. Commit changes and create release tag diff --git a/docs/info/changelog.rst b/docs/info/changelog.rst index 6024bb9e3..16d4dffae 100644 --- a/docs/info/changelog.rst +++ b/docs/info/changelog.rst @@ -2,6 +2,219 @@ Changelog ********* +0.19.0 +====== + +General +------- + +- New MCUs and Boards: + + - Add ESP32-H4, ESP32-C5, ESP32-C61 support + - Add STM32U083C-DK, STM32WBA, STM32N6570-DK, STM32N657 Nucleo + - Add AT32F405, AT32F403A, AT32F415, AT32F423 support + - Add CH32V305 support and CH32V20x USB host support + - Add MCXA156 SDK 2.16 support and FRDM-MCXA156 board + - Update all STM32 HAL and CMSIS dependencies to latest versions + +- Build System and CI Improvements + - Improve build system with GCC 14 support + - Add ARM IAR toolchain build support via CircleCI and GitHub Actions + - Add comprehensive CMake build documentation + - Improve hardware-in-the-loop (HIL) testing infrastructure + - Add Claude Code AI assistant workflows and documentation + +- Add ``tusb_deinit()`` function for stack cleanup + +API Changes +----------- + +- Core APIs + - Add weak callbacks with new syntax for better compiler compatibility + - Add ``tusb_deinit()`` to cleanup stack + - Add time functions: ``tusb_time_millis_api()`` and ``tusb_time_delay_ms_api()`` + - Add ``osal_critical`` APIs for critical section handling + - Introduce ``xfer_isr()`` callback for ISO transfer optimization in device classes + +- Device APIs + - CDC: Add ``tud_cdc_configure()``, ``tud_cdc_n_notify_uart_state()``, + ``tud_cdc_n_notify_conn_speed_change()``, ``tud_cdc_notify_complete_cb()`` + - MSC: Add ``tud_msc_inquiry2_cb()`` with bufsize parameter, update ``tud_msc_async_io_done()`` + with ``in_isr`` parameter + - Audio: Add ``tud_audio_n_mounted()`` and various FIFO access functions + - MTP: Add ``tud_mtp_mounted()``, ``tud_mtp_data_send()``, ``tud_mtp_data_receive()``, + ``tud_mtp_response_send()``, ``tud_mtp_event_send()`` + +- Host APIs + - Core: Add ``tuh_edpt_close()``, ``tuh_address_set()``, ``tuh_descriptor_get_device_local()``, + ``tuh_descriptor_get_string_langid()``, ``tuh_connected()``, ``tuh_bus_info_get()`` + - Add enumeration callbacks: ``tuh_enum_descriptor_device_cb()``, + ``tuh_enum_descriptor_configuration_cb()`` + - CDC: Add ``tuh_cdc_get_control_line_state_local()``, ``tuh_cdc_get/set_dtr/rts()``, + ``tuh_cdc_connect/disconnect()`` and sync versions of all control APIs + - MIDI: Add ``tuh_midi_itf_get_info()``, ``tuh_midi_packet_read_n()``, + ``tuh_midi_packet_write_n()``, ``tuh_midi_read_available()``, ``tuh_midi_write_flush()``, + ``tuh_midi_descriptor_cb()`` + +Controller Driver (DCD & HCD) +----------------------------- + +- DWC2 + - Support DWC2 v4.30a with improved reset procedure + - Fix core reset: wait for AHB idle before reset + - Add STM32 DWC2 data cache support with proper alignment + - Host improvements: + - Fix disconnect detection and SOF flag handling + - Fix HFIR timing off-by-one error + - Retry IN token immediately for bInterval=1 + - Proper attach debouncing (200ms) + - Fix all retry intervals + - Resume OUT transfer when PING ACKed + - Fix enumeration racing conditions + - Refactor bitfields for better code generation + +- FSDEV (STM32) + - Fix AT32 compile issues after single-buffered endpoint changes + - Add configurable single-buffered isochronous endpoints + - Fix STM32H7 recurrent suspend ISR + - Fix STM32L4 GPIOD clock enable for variants without GPIOD + - Fix STM32 PHYC PLL stability wait + - Improve PMA size handling for STM32U0 + +- EHCI + - Fix removed QHD getting reused + - Fix NXP USBPHY disconnection detection + +- Chipidea/NXP + - Fix race condition with spinlock + - Add async I/O support for MSC + - Improve iMXRT support: fix build, disable BOARD_ConfigMPU, fix attach debouncing on port1 highspeed + - Fix iMXRT1064 and add to HIL test pool + +- MAX3421E + - Use spinlock for thread safety instead of atomic flag + - Implement ``hcd_edpt_close()`` + +- RP2040 + - Fix audio ISO transfer: reset state before notifying stack + - Fix CMake RTOS cache variable + - Abort transfer if active in ``iso_activate()`` + +- SAMD + - Add host controller driver support + +Device Stack +------------ + +- USBD Core + - Introduce ``xfer_isr()`` callback for interrupt-time transfer handling + - Add ``usbd_edpt_xfer_fifo()`` stub + - Revert endpoint busy/claim status if ``xfer_isr()`` defers to ``xfer_cb()`` + +- Audio + - Major simplification of UAC driver and alt settings management + - Move ISO transfers into ``xfer_isr()`` for better performance + - Remove FIFO mutex (single producer/consumer optimization) + - Add implicit feedback support for data IN endpoints + - Fix alignment issues + - Update buffer macros with cache line size alignment + +- CDC + - Add notification support: ``CFG_TUD_CDC_NOTIFY``, ``tud_cdc_n_notify_conn_speed_change()``, ``tud_cdc_notify_complete_cb()`` + - Reduce default bInterval from 16ms to 1ms for better responsiveness + - Rename ``tud_cdc_configure_fifo()`` to ``tud_cdc_configure()`` and add ``tx_overwritable_if_not_connected`` option + - Fix web serial robustness with major overhaul and logic cleanup + +- HID + - Add Usage Page and Table for Power Devices (0x84 - 0x85) + - Fix HID descriptor parser variable size and 4-byte item handling + - Add consumer page configurations + +- MIDI + - Fix MIDI interface descriptor handling after audio streaming interface + - Skip RX data with all zeroes + +- MSC + - Add ``tud_msc_inquiry2_cb()`` with bufsize for full inquiry response + - Refactor async I/O: add ``in_isr`` argument to ``tud_msc_async_io_done()`` + +- MTP + - Add new Media Transfer Protocol (MTP) device class driver + - Support MTP operations: GetDeviceInfo, SendObjectInfo, SendObject + - Add MTP event support with ``tud_mtp_event_send()`` + - Implement filesystem example with callbacks + - Add hardware-in-the-loop testing support + +- NCM + - Add USB NCM link state control support + - Fix DHCP offer/ACK destination + +- USBTMC + - Add vendor-specific message support + +- Vendor + - Fix vendor device reset and open issues + - Fix descriptor parsing for ``CFG_TUD_VENDOR > 1`` + - Fix vendor FIFO argument calculation + +Host Stack +---------- + +- USBH Core + - Major enumeration improvements: + - Fix enumeration racing conditions + - Add proper attach debouncing with hub/rootport handling (200ms delay) + - Reduce ``ENUM_DEBOUNCING_DELAY_MS`` to 200ms + - Always get language ID, manufacturer, product, and serial strings during enumeration + - Always get first 2 bytes of string descriptor to determine length (prevents buffer overflow) + - Support devices with multiple configurations + - Add ``tuh_enum_descriptor_device_cb()`` and ``tuh_enum_descriptor_configuration_cb()`` callbacks + - Add ``tuh_descriptor_get_string_langid()`` API + - Hub improvements: + - Check status before getting first device descriptor + - Properly handle port status and change detection + - Queue status endpoint for detach/remove events + - Fix hub status change endpoint handling + - Fix endpoint management: + - ``hcd_edpt_open()`` returns false if endpoint already opened + - Add ``hcd_edpt_close()`` implementation + - Abort pending transfers on close + - Add roothub debouncing flag to ignore attach/remove during debouncing + - Move address setting and bus info management to separate structures + - Force removed devices in same bus info before setting address + +- CDC Serial Host + - Major refactor to generalize CDC serial drivers (FTDI, CP210x, CH34x, PL2303, ACM) + - Add common 2-stage set line coding for drivers without direct support + - Add ``cdch_process_line_state_on_enum()`` for line state configuration during enumeration + - Refactor control transfer handling with ``cdch_internal_control_complete()`` + - Add explicit ``sync()`` API with ``TU_API_SYNC()`` returning ``tusb_xfer_result_t`` + - Rename ``tuh_cdc_get_local_line_coding()`` to ``tuh_cdc_get_line_coding_local()`` + - Add ``tuh_cdc_get_control_line_state_local()`` + - Implement ``tuh_cdc_get/set_dtr/rts()`` as inline functions + - Add ``get_itf_by_xfer()`` for better CDC interface determination + - Union FTDI/PL2303/ACM data structures to save memory + - Remove local device descriptor storage + +- MIDI Host + - Major API changes: + - Rename ``tuh_midi_stream_flush()`` to ``tuh_midi_write_flush()`` + - Add ``tuh_midi_packet_read_n()`` and ``tuh_midi_packet_write_n()`` + - Add ``CFG_TUH_MIDI_STREAM_API`` to opt out of stream API + - Change API to use index instead of device address (supports multiple MIDI per device) + - Add ``tuh_midi_mount_cb_t`` struct for mount callback + - Change ``tuh_midi_rx/tx_cb()`` to include ``xferred_bytes`` + - Rename ``tuh_midi_get_num_rx/tx_cables()`` to ``tuh_midi_get_rx/tx_cable_count()`` + - Add ``tuh_midi_descriptor_cb()`` and ``tuh_midi_itf_get_info()`` + - Fix ``iInterface`` value in ``tuh_midi_itf_get_info()`` + - Remove ``CFG_MIDI_HOST_DEVSTRINGS`` support + +- MSC Host + - Continue async I/O improvements + +- HID Host + - Fix version string to actually show version + 0.18.0 ====== diff --git a/docs/reference/dependencies.rst b/docs/reference/dependencies.rst index ca5c84151..1a088c989 100644 --- a/docs/reference/dependencies.rst +++ b/docs/reference/dependencies.rst @@ -4,9 +4,9 @@ Dependencies MCU low-level peripheral driver and external libraries for building TinyUSB examples -======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================== +======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================================== Local Path Repo Commit Required by -======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================== +======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================================== hw/mcu/allwinner https://github.com/hathach/allwinner_driver.git 8e5e89e8e132c0fd90e72d5422e5d3d68232b756 fc100s hw/mcu/analog/msdk https://github.com/analogdevicesinc/msdk.git b20b398d3e5e2007594e54a74ba3d2a2e50ddd75 maxim hw/mcu/artery/at32f402_405 https://github.com/ArteryTek/AT32F402_405_Firmware_Library.git 4424515c2663e82438654e0947695295df2abdfe at32f402_405 @@ -31,6 +31,7 @@ hw/mcu/renesas/fsp https://github.com/renesas/fsp.git hw/mcu/renesas/rx https://github.com/kkitayam/rx_device.git 706b4e0cf485605c32351e2f90f5698267996023 rx hw/mcu/silabs/cmsis-dfp-efm32gg12b https://github.com/cmsis-packs/cmsis-dfp-efm32gg12b.git f1c31b7887669cb230b3ea63f9b56769078960bc efm32 hw/mcu/sony/cxd56/spresense-exported-sdk https://github.com/sonydevworld/spresense-exported-sdk.git 2ec2a1538362696118dc3fdf56f33dacaf8f4067 spresense +hw/mcu/st/cmsis-device-u0 https://github.com/STMicroelectronics/cmsis-device-u0.git e3a627c6a5bc4eb2388e1885a95cc155e1672253 stm32u0 hw/mcu/st/cmsis-device-wba https://github.com/STMicroelectronics/cmsis-device-wba.git 647d8522e5fd15049e9a1cc30ed19d85e5911eaf stm32wba hw/mcu/st/cmsis_device_c0 https://github.com/STMicroelectronics/cmsis_device_c0.git 517611273f835ffe95318947647bc1408f69120d stm32c0 hw/mcu/st/cmsis_device_f0 https://github.com/STMicroelectronics/cmsis_device_f0.git cbb5da5d48b4b5f2efacdc2f033be30f9d29889f stm32f0 @@ -70,6 +71,7 @@ hw/mcu/st/stm32l1xx_hal_driver https://github.com/STMicroelectronics/ hw/mcu/st/stm32l4xx_hal_driver https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git 3e039bbf62f54bbd834d578185521cff80596efe stm32l4 hw/mcu/st/stm32l5xx_hal_driver https://github.com/STMicroelectronics/stm32l5xx_hal_driver.git 3340b9a597bcf75cc173345a90a74aa2a4a37510 stm32l5 hw/mcu/st/stm32n6xx_hal_driver https://github.com/STMicroelectronics/stm32n6xx-hal-driver.git bc6c41f8f67d61b47af26695d0bf67762a000666 stm32n6 +hw/mcu/st/stm32u0xx_hal_driver https://github.com/STMicroelectronics/stm32u0xx-hal-driver.git cbfb5ac654256445237fd32b3587ac6a238d24f1 stm32u0 hw/mcu/st/stm32u5xx_hal_driver https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git 2c5e2568fbdb1900a13ca3b2901fdd302cac3444 stm32u5 hw/mcu/st/stm32wbaxx_hal_driver https://github.com/STMicroelectronics/stm32wbaxx_hal_driver.git 9442fbb71f855ff2e64fbf662b7726beba511a24 stm32wba hw/mcu/st/stm32wbxx_hal_driver https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git d60dd46996876506f1d2e9abd6b1cc110c8004cd stm32wb @@ -78,10 +80,10 @@ hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.gi hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x hw/mcu/wch/ch32v20x https://github.com/openwch/ch32v20x.git c4c38f507e258a4e69b059ccc2dc27dde33cea1b ch32v20x hw/mcu/wch/ch32v307 https://github.com/openwch/ch32v307.git 184f21b852cb95eed58e86e901837bc9fff68775 ch32v30x -lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32n6 stm32u5 stm32wb sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c +lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32n6 stm32u0 stm32u5 stm32wb stm32wbasam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c lib/CMSIS_6 https://github.com/ARM-software/CMSIS_6.git b0bbb0423b278ca632cfe1474eb227961d835fd2 ra lib/FreeRTOS-Kernel https://github.com/FreeRTOS/FreeRTOS-Kernel.git cc0e0707c0c748713485b870bb980852b210877f all lib/lwip https://github.com/lwip-tcpip/lwip.git 159e31b689577dbf69cf0683bbaffbd71fa5ee10 all lib/sct_neopixel https://github.com/gsteiert/sct_neopixel.git e73e04ca63495672d955f9268e003cffe168fcd8 lpc55 tools/uf2 https://github.com/microsoft/uf2.git c594542b2faa01cc33a2b97c9fbebc38549df80a all -======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================== +======================================== ================================================================ ======================================== ====================================================================================================================================================================================================================================================================================================================================================================== diff --git a/library.json b/library.json index f1bfd6387..718fd84d3 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TinyUSB", - "version": "0.18.0", + "version": "0.19.0", "description": "TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function.", "keywords": "usb, host, device", "repository": diff --git a/repository.yml b/repository.yml index 31c9eddc5..5c2aaa6fa 100644 --- a/repository.yml +++ b/repository.yml @@ -16,5 +16,6 @@ repo.versions: "0.16.0": "0.16.0" "0.17.0": "0.17.0" "0.18.0": "0.18.0" - "0-latest": "0.18.0" + "0.19.0": "0.19.0" + "0-latest": "0.19.0" "0-dev": "0.0.0" diff --git a/src/tusb_option.h b/src/tusb_option.h index 80060914b..378b5607e 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -31,7 +31,7 @@ // Version is release as major.minor.revision eg 1.0.0 #define TUSB_VERSION_MAJOR 0 -#define TUSB_VERSION_MINOR 18 +#define TUSB_VERSION_MINOR 19 #define TUSB_VERSION_REVISION 0 #define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION) diff --git a/tools/make_release.py b/tools/make_release.py index c1caf3300..488ad4901 100755 --- a/tools/make_release.py +++ b/tools/make_release.py @@ -2,7 +2,7 @@ import re import gen_doc -version = '0.18.0' +version = '0.19.0' print('version {}'.format(version)) ver_id = version.split('.') -- cgit v1.3.1 From afdfb0895f33fd74e81f298e40205b3c521e900b Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sun, 5 Oct 2025 18:58:16 +0200 Subject: Fix HIS stylus descriptor and hid_composite example Signed-off-by: HiFiPhile --- examples/device/hid_composite/src/main.c | 61 +++++++++++--------------------- src/class/hid/hid_device.h | 10 +++--- 2 files changed, 26 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/examples/device/hid_composite/src/main.c b/examples/device/hid_composite/src/main.c index fa02a1abe..89dab0bdc 100644 --- a/examples/device/hid_composite/src/main.c +++ b/examples/device/hid_composite/src/main.c @@ -195,41 +195,30 @@ static void send_hid_report(uint8_t report_id, uint32_t btn) } } break; - default: break; - } -} - -/* use this to send stylus touch signal through USB. */ -static void send_stylus_touch(uint16_t x, uint16_t y, bool state) -{ - // skip if hid is not ready yet - if ( !tud_hid_ready() ) return; - - static bool has_stylus_pen = false; - - hid_stylus_report_t report = - { - .attr = 0, - .x = 0, - .y = 0 - }; - - report.x = x; - report.y = y; - if (state) - { - report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE; - tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); + case REPORT_ID_STYLUS_PEN: { + static bool touch_state = false; + hid_stylus_report_t report = { + .attr = 0, + .x = 0, + .y = 0 + }; - has_stylus_pen = true; - }else - { - report.attr = 0; - if (has_stylus_pen) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); - has_stylus_pen = false; + if (btn) { + report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE; + report.x = 100; + report.y = 100; + tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); + touch_state = true; + } else { + report.attr = 0; + if (touch_state) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report)); + touch_state = false; + } + } + break; + default: break; } - } // Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..) @@ -239,14 +228,6 @@ void hid_task(void) // Poll every 10ms const uint32_t interval_ms = 10; static uint32_t start_ms = 0; - static uint32_t touch_ms = 0; - static bool touch_state = false; - - if (board_millis() - touch_ms < 100) { - touch_ms = board_millis(); - send_stylus_touch(0, 0, touch_state = !touch_state); - return; - } if ( board_millis() - start_ms < interval_ms) return; // not enough time start_ms += interval_ms; diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h index fc1dbcbd8..32b572973 100644 --- a/src/class/hid/hid_device.h +++ b/src/class/hid/hid_device.h @@ -267,14 +267,14 @@ void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, u // Stylus Pen Report Descriptor Template #define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \ HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \ - HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \ + HID_USAGE ( HID_USAGE_DIGITIZER_PEN ) , \ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \ /* Report ID if any */\ __VA_ARGS__ \ - HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \ - HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \ - HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \ - HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \ + HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ), \ + HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), \ + HID_USAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ), \ + HID_USAGE ( HID_USAGE_DIGITIZER_IN_RANGE ), \ HID_LOGICAL_MIN ( 0 ), \ HID_LOGICAL_MAX ( 1 ), \ HID_REPORT_SIZE ( 1 ), \ -- cgit v1.3.1 From f272d87a3f3cce71aa2d13813a1fee94aecb9fc5 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 6 Oct 2025 10:58:58 +0700 Subject: remove dcd_esp32sx which is replaced by dwc2 --- README.rst | 2 +- src/class/audio/audio_device.c | 5 +- src/portable/espressif/esp32sx/dcd_esp32sx.c | 889 --------------------------- tools/iar_gen.py | 2 +- 4 files changed, 4 insertions(+), 894 deletions(-) delete mode 100644 src/portable/espressif/esp32sx/dcd_esp32sx.c (limited to 'src') diff --git a/README.rst b/README.rst index 16de684a6..03ad3744c 100644 --- a/README.rst +++ b/README.rst @@ -122,7 +122,7 @@ Supported CPUs +--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+ | Dialog | DA1469x | ✔ | ✖ | ✖ | da146xx | | +--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+ -| Espressif | S2, S3 | ✔ | ✔ | ✖ | dwc2 or esp32sx | | +| Espressif | S2, S3 | ✔ | ✔ | ✖ | dwc2 | | | ESP32 +-----------------------------+--------+------+-----------+------------------------+-------------------+ | | P4 | ✔ | ✔ | ✔ | dwc2 | | | +-----------------------------+--------+------+-----------+------------------------+-------------------+ diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 701411401..7df177773 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -79,10 +79,9 @@ // Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer // is available or driver is would need to be changed dramatically -// Only STM32 and dcd_transdimension use non-linear buffer for now -// dwc2 except esp32sx (since it may use dcd_esp32sx) +// Only STM32 and ChipIdea HS use non-linear buffer for now // Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line -#if (defined(TUP_USBIP_DWC2) && !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)) || \ +#if defined(TUP_USBIP_DWC2) || \ defined(TUP_USBIP_FSDEV) || \ CFG_TUSB_MCU == OPT_MCU_RX63X || \ CFG_TUSB_MCU == OPT_MCU_RX65X || \ diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c deleted file mode 100644 index 1b6aae026..000000000 --- a/src/portable/espressif/esp32sx/dcd_esp32sx.c +++ /dev/null @@ -1,889 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd. - * - * 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 "tusb_option.h" - -#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED) - -// Espressif -#include "xtensa/xtensa_api.h" - -#include "esp_intr_alloc.h" -#include "esp_log.h" -#include "soc/dport_reg.h" -#include "soc/gpio_sig_map.h" -#include "soc/usb_periph.h" -#include "soc/usb_reg.h" -#include "soc/usb_struct.h" -#include "soc/periph_defs.h" // for interrupt source - -#include "device/dcd.h" - -#ifndef USB_OUT_EP_NUM -#define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0]))) -#endif - -#ifndef USB_IN_EP_NUM -#define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0]))) -#endif - -// Max number of bi-directional endpoints including EP0 -// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0 -// We should probably prohibit enabling Endpoint IN > 4 (not done yet) -#define EP_MAX USB_OUT_EP_NUM - -// FIFO size in bytes -#define EP_FIFO_SIZE 1024 - -// Max number of IN EP FIFOs -#define EP_FIFO_NUM 5 - -typedef struct { - uint8_t *buffer; - // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API - uint16_t total_len; - uint16_t queued_len; - uint16_t max_size; - bool short_packet; - uint8_t interval; -} xfer_ctl_t; - -static const char *TAG = "TUSB:DCD"; -static intr_handle_t usb_ih; - - -static uint32_t _setup_packet[2]; - -#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] -static xfer_ctl_t xfer_status[EP_MAX][2]; - -// Keep count of how many FIFOs are in use -static uint8_t _allocated_fifos = 1; //FIFO0 is always in use - -// Will either return an unused FIFO number, or 0 if all are used. -static uint8_t get_free_fifo(void) -{ - if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++; - return 0; -} - -// Setup the control endpoint 0. -static void bus_reset(void) -{ - for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) { - USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - - // clear device address - USB0.dcfg &= ~USB_DEVADDR_M; - - USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; - USB0.doepmsk = USB_SETUPMSK_M | USB_XFERCOMPLMSK; - USB0.diepmsk = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/; - - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO MAX | - // --------------- - // | ... | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): - // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN - // - // - All EP OUT shared a unique OUT FIFO which uses - // * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets). - // * 2 locations for OUT endpoint control words. - // * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes) - // * 1 location for global NAK (not required/used here). - // * It is recommended to allocate 2 times the largest packet size, therefore - // Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52 - USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10, - USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo - USB0.grxfsiz = 52; - - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL); - - // Ready to receive SETUP packet - USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M; - - USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M; -} - -static void enum_done_processing(void) -{ - ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then"); - // On current silicon on the Full Speed core, speed is fixed to Full Speed. - // However, keep for debugging and in case Low Speed is ever supported. - uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V); - - // Maximum packet size for EP 0 is set for both directions by writing DIEPCTL - if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz) - USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN].max_size = 64; - } else { - USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes - USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall - xfer_status[0][TUSB_DIR_OUT].max_size = 8; - xfer_status[0][TUSB_DIR_IN].max_size = 8; - } -} - - -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ -bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rh_init; - ESP_LOGV(TAG, "DCD init - Start"); - - // A. Disconnect - ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up"); - USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect - - // B. Programming DCFG - /* If USB host misbehaves during status portion of control xfer - (non zero-length packet), send STALL back and discard. Full speed. */ - USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL - (3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476) - - USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON - USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode - USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides - - // C. Setting SNAKs, then connect - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK - } - - // D. Interruption masking - USB0.gintmsk = 0; //mask all - USB0.gotgint = ~0U; //clear OTG ints - USB0.gintsts = ~0U; //clear pending ints - USB0.gintmsk = USB_OTGINTMSK_M | - USB_MODEMISMSK_M | - USB_RXFLVIMSK_M | - USB_ERLYSUSPMSK_M | - USB_USBSUSPMSK_M | - USB_USBRSTMSK_M | - USB_ENUMDONEMSK_M | - USB_RESETDETMSK_M | - USB_WKUPINT_M | - USB_DISCONNINTMSK_M; // host most only - - dcd_connect(rhport); - return true; -} - -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void)rhport; - ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr); - USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S); - // Response with status after changing device address - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); -} - -void dcd_remote_wakeup(uint8_t rhport) -{ - (void)rhport; - - // set remote wakeup - USB0.dctl |= USB_RMTWKUPSIG_M; - - // enable SOF to detect bus resume - USB0.gintsts = USB_SOF_M; - USB0.gintmsk |= USB_SOFMSK_M; - - // Per specs: remote wakeup signal bit must be clear within 1-15ms - vTaskDelay(pdMS_TO_TICKS(1)); - - USB0.dctl &= ~USB_RMTWKUPSIG_M; -} - -// connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(uint8_t rhport) -{ - (void) rhport; - USB0.dctl &= ~USB_SFTDISCON_M; -} - -// disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(uint8_t rhport) -{ - (void) rhport; - USB0.dctl |= USB_SFTDISCON_M; -} - -void dcd_sof_enable(uint8_t rhport, bool en) -{ - (void) rhport; - (void) en; - - // TODO implement later -} - -/*------------------------------------------------------------------*/ -/* DCD Endpoint port - *------------------------------------------------------------------*/ - -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) -{ - ESP_LOGV(TAG, "DCD endpoint opened"); - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); - - TU_ASSERT(epnum < EP_MAX); - - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir); - xfer->max_size = tu_edpt_packet_size(desc_edpt); - xfer->interval = desc_edpt->bInterval; - - if (dir == TUSB_DIR_OUT) { - out_ep[epnum].doepctl |= USB_USBACTEP1_M | - desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) | - xfer->max_size << USB_MPS1_S; - USB0.daintmsk |= (1 << (16 + epnum)); - } else { - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO MAX | - // --------------- - // | ... | - // --------------- y + x + 16 + GRXFSIZ - // | IN FIFO 2 | - // --------------- x + 16 + GRXFSIZ - // | IN FIFO 1 | - // --------------- 16 + GRXFSIZ - // | IN FIFO 0 | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints - // - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1) - // - Offset: GRXFSIZ + 16 + Size*(epnum-1) - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". - - uint8_t fifo_num = get_free_fifo(); - TU_ASSERT(fifo_num != 0); - - in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M); - in_ep[epnum].diepctl |= USB_D_USBACTEP1_M | - fifo_num << USB_D_TXFNUM1_S | - desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S | - (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) | - xfer->max_size << 0; - - USB0.daintmsk |= (1 << (0 + epnum)); - - // Both TXFD and TXSA are in unit of 32-bit words. - // IN FIFO 0 was configured during enumeration, hence the "+ 16". - uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16; - uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1); - uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1); - - // DIEPTXF starts at FIFO #1. - USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset; - } - return true; -} - -void dcd_edpt_close_all(uint8_t rhport) -{ - (void) rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - // Disable non-control interrupt - USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M; - - for(uint8_t n = 1; n < EP_MAX; n++) - { - // disable OUT endpoint - out_ep[n].doepctl = 0; - xfer_status[n][TUSB_DIR_OUT].max_size = 0; - - // disable IN endpoint - in_ep[n].diepctl = 0; - xfer_status[n][TUSB_DIR_IN].max_size = 0; - } - - _allocated_fifos = 1; -} - -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - (void)rhport; - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); - xfer->buffer = buffer; - // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API - xfer->total_len = total_bytes; - xfer->queued_len = 0; - xfer->short_packet = false; - - uint16_t num_packets = (total_bytes / xfer->max_size); - uint8_t short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if (short_packet_size > 0 || (total_bytes == 0)) { - num_packets++; - } - - ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i", - epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"), - num_packets, total_bytes); - - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them - // here. - if (dir == TUSB_DIR_IN) { - // A full IN transfer (multiple packets, possibly) triggers XFRC. - USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes; - USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK - - // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame - if ((USB0.in_ep_reg[epnum].diepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S)); - USB0.in_ep_reg[epnum].diepctl |= (odd_frame_now ? USB_DI_SETD0PID1 : USB_DI_SETD1PID1); - } - - // Enable fifo empty interrupt only if there are something to put in the fifo. - if(total_bytes != 0) { - USB0.dtknqr4_fifoemptymsk |= (1 << epnum); - } - } else { - // Each complete packet for OUT xfers triggers XFRC. - USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - - // For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame - if ((USB0.out_ep_reg[epnum].doepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S)); - USB0.out_ep_reg[epnum].doepctl |= (odd_frame_now ? USB_DO_SETD0PID1 : USB_DO_SETD1PID1); - } - } - return true; -} - -#if 0 // TODO support dcd_edpt_xfer_fifo API -bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) -{ - (void)rhport; -} -#endif - -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) { - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M); - } else { - // Stop transmitting packets and NAK IN xfers. - in_ep[epnum].diepctl |= USB_DI_SNAK1_M; - while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ; - - // Disable the endpoint. Note that both SNAK and STALL are set here. - in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M); - while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ; - in_ep[epnum].diepint = USB_D_EPDISBLD0_M; - } - - // Flush the FIFO, and wait until we have confirmed it cleared. - uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V); - USB0.grstctl |= (fifo_num << USB_TXFNUM_S); - USB0.grstctl |= USB_TXFFLSH_M; - while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ; - } else { - // Only disable currently enabled non-control endpoint - if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) { - out_ep[epnum].doepctl |= USB_STALL0_M; - } else { - // Asserting GONAK is required to STALL an OUT endpoint. - // Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt - // anyway, and it can't be cleared by user code. If this while loop never - // finishes, we have bigger problems than just the stack. - USB0.dctl |= USB_SGOUTNAK_M; - while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ; - - // Ditto here- disable the endpoint. Note that only STALL and not SNAK - // is set here. - out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M); - while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ; - out_ep[epnum].doepint = USB_EPDISBLD0_M; - - // Allow other OUT endpoints to keep receiving. - USB0.dctl |= USB_CGOUTNAK_M; - } - } -} - -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - (void)rhport; - - usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]); - usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]); - - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - - if (dir == TUSB_DIR_IN) { - in_ep[epnum].diepctl &= ~USB_D_STALL1_M; - - uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M; - } - } else { - out_ep[epnum].doepctl &= ~USB_STALL1_M; - - uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S; - // Required by USB spec to reset DATA toggle bit to DATA0 on interrupt - // and bulk endpoints. - if (eptype == 2 || eptype == 3) { - out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M; - } - } -} - -/*------------------------------------------------------------------*/ - -static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size) -{ - ESP_EARLY_LOGV(TAG, "USB - receive_packet"); - volatile uint32_t *rx_fifo = USB0.fifo[0]; - - // See above TODO - // uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos; - // xfer->queued_len = xfer->total_len - remaining; - - uint16_t remaining = xfer->total_len - xfer->queued_len; - uint16_t to_recv_size; - - if (remaining <= xfer->max_size) { - // Avoid buffer overflow. - to_recv_size = (xfer_size > remaining) ? remaining : xfer_size; - } else { - // Room for full packet, choose recv_size based on what the microcontroller - // claims. - to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size; - } - - // Common buffer read -#if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size); - } - else -#endif - { - uint8_t to_recv_rem = to_recv_size % 4; - uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem; - - // Do not assume xfer buffer is aligned. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to collect. - if (to_recv_size >= 4) { - for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) { - uint32_t tmp = (*rx_fifo); - base[i] = tmp & 0x000000FF; - base[i + 1] = (tmp & 0x0000FF00) >> 8; - base[i + 2] = (tmp & 0x00FF0000) >> 16; - base[i + 3] = (tmp & 0xFF000000) >> 24; - } - } - - // Do not read invalid bytes from RX FIFO. - if (to_recv_rem != 0) { - uint32_t tmp = (*rx_fifo); - uint8_t *last_32b_bound = base + to_recv_size_aligned; - - last_32b_bound[0] = tmp & 0x000000FF; - if (to_recv_rem > 1) { - last_32b_bound[1] = (tmp & 0x0000FF00) >> 8; - } - if (to_recv_rem > 2) { - last_32b_bound[2] = (tmp & 0x00FF0000) >> 16; - } - } - } - - xfer->queued_len += xfer_size; - - // Per USB spec, a short OUT packet (including length 0) is always - // indicative of the end of a transfer (at least for ctl, bulk, int). - xfer->short_packet = (xfer_size < xfer->max_size); -} - -static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num) -{ - ESP_EARLY_LOGV(TAG, "USB - transmit_packet"); - volatile uint32_t *tx_fifo = USB0.fifo[fifo_num]; - - uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S; - xfer->queued_len = xfer->total_len - remaining; - - uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining; - -#if 0 // TODO support dcd_edpt_xfer_fifo API - if (xfer->ff) - { - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size); - } - else -#endif - { - uint8_t to_xfer_rem = to_xfer_size % 4; - uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem; - - // Buffer might not be aligned to 32b, so we need to force alignment - // by copying to a temp var. - uint8_t *base = (xfer->buffer + xfer->queued_len); - - // This for loop always runs at least once- skip if less than 4 bytes - // to send off. - if (to_xfer_size >= 4) { - for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) { - uint32_t tmp = base[i] | (base[i + 1] << 8) | - (base[i + 2] << 16) | (base[i + 3] << 24); - (*tx_fifo) = tmp; - } - } - - // Do not read beyond end of buffer if not divisible by 4. - if (to_xfer_rem != 0) { - uint32_t tmp = 0; - uint8_t *last_32b_bound = base + to_xfer_size_aligned; - - tmp |= last_32b_bound[0]; - if (to_xfer_rem > 1) { - tmp |= (last_32b_bound[1] << 8); - } - if (to_xfer_rem > 2) { - tmp |= (last_32b_bound[2] << 16); - } - - (*tx_fifo) = tmp; - } - } -} - -static void read_rx_fifo(void) -{ - // Pop control word off FIFO (completed xfers will have 2 control words, - // we only pop one ctl word each interrupt). - uint32_t const ctl_word = USB0.grxstsp; - uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S; - uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S; - uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S; - - switch (pktsts) { - case 0x01: // Global OUT NAK (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK"); - break; - - case 0x02: { // Out packet recvd - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet"); - xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - receive_packet(xfer, bcnt); - } - break; - - case 0x03: // Out packet done (Interrupt) - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done"); - break; - - case 0x04: // Step 2: Setup transaction completed (Interrupt) - // After this event, OEPINT interrupt will occur with SETUP bit set - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done"); - USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M; - break; - - case 0x06: { // Step1: Setup data packet received - volatile uint32_t *rx_fifo = USB0.fifo[0]; - - // We can receive up to three setup packets in succession, but - // only the last one is valid. Therefore we just overwrite it - _setup_packet[0] = (*rx_fifo); - _setup_packet[1] = (*rx_fifo); - - ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]); - } - break; - - default: // Invalid, do something here, like breakpoint? - TU_BREAKPOINT(); - break; - } -} - -static void handle_epout_ints(void) -{ - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DOEPINTx is cleared. - // DOEPINT will be cleared when DAINT's out bits are cleared. - for (int n = 0; n < USB_OUT_EP_NUM; n++) { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); - - if (USB0.daint & (1 << (16 + n))) { - // SETUP packet Setup Phase done. - if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) { - USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear - dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true); - } - - // OUT XFER complete (single packet).q - if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) { - - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)"); - USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M; - - // Transfer complete if short packet or total len is transferred - if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) { - xfer->short_packet = false; - dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true); - } else { - // Schedule another packet to be received. - USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S); - USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M; - } - } - } - } -} - -static void handle_epin_ints(void) -{ - // GINTSTS will be cleared with DAINT == 0 - // DAINT for a given EP clears when DIEPINTx is cleared. - // IEPINT will be cleared when DAINT's out bits are cleared. - for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) { - xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN]; - - if (USB0.daint & (1 << (0 + n))) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n); - // IN XFER complete (entire xfer). - if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!"); - USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M; - dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - - // XFER FIFO empty - if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) { - ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!"); - USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M; - transmit_packet(xfer, &USB0.in_ep_reg[n], n); - - // Turn off TXFE if all bytes are written. - if (xfer->queued_len == xfer->total_len) - { - USB0.dtknqr4_fifoemptymsk &= ~(1 << n); - } - } - - // XFER Timeout - if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) { - // Clear interrupt or endpoint will hang. - USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M; - // Maybe retry? - } - } - } -} - - -static void _dcd_int_handler(void* arg) -{ - (void) arg; - uint8_t const rhport = 0; - - const uint32_t int_msk = USB0.gintmsk; - const uint32_t int_status = USB0.gintsts & int_msk; - - if (int_status & USB_USBRST_M) { - // start of reset - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset"); - USB0.gintsts = USB_USBRST_M; - // FIFOs will be reassigned when the endpoints are reopen - _allocated_fifos = 1; - bus_reset(); - } - - if (int_status & USB_RESETDET_M) { - ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend"); - USB0.gintsts = USB_RESETDET_M; - bus_reset(); - } - - if (int_status & USB_ENUMDONE_M) { - // ENUMDNE detects speed of the link. For full-speed, we - // always expect the same value. This interrupt is considered - // the end of reset. - USB0.gintsts = USB_ENUMDONE_M; - enum_done_processing(); - dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true); - } - - if(int_status & USB_USBSUSP_M) - { - USB0.gintsts = USB_USBSUSP_M; - dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); - } - - if(int_status & USB_WKUPINT_M) - { - USB0.gintsts = USB_WKUPINT_M; - dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); - } - - if (int_status & USB_OTGINT_M) - { - // OTG INT bit is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected"); - - uint32_t const otg_int = USB0.gotgint; - - if (otg_int & USB_SESENDDET_M) - { - dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true); - } - - USB0.gotgint = otg_int; - } - - if (int_status & USB_SOF_M) { - USB0.gintsts = USB_SOF_M; - - // Disable SOF interrupt since currently only used for remote wakeup detection - USB0.gintmsk &= ~USB_SOFMSK_M; - - dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true); - } - - - if (int_status & USB_RXFLVI_M) { - // RXFLVL bit is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!"); - - // Mask out RXFLVL while reading data from FIFO - USB0.gintmsk &= ~USB_RXFLVIMSK_M; - read_rx_fifo(); - USB0.gintmsk |= USB_RXFLVIMSK_M; - } - - // OUT endpoint interrupt handling. - if (int_status & USB_OEPINT_M) { - // OEPINT is read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!"); - handle_epout_ints(); - } - - // IN endpoint interrupt handling. - if (int_status & USB_IEPINT_M) { - // IEPINT bit read-only - ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!"); - handle_epin_ints(); - } - - // Without handling - USB0.gintsts |= USB_CURMOD_INT_M | - USB_MODEMIS_M | - USB_OTGINT_M | - USB_NPTXFEMP_M | - USB_GINNAKEFF_M | - USB_GOUTNAKEFF | - USB_ERLYSUSP_M | - USB_USBSUSP_M | - USB_ISOOUTDROP_M | - USB_EOPF_M | - USB_EPMIS_M | - USB_INCOMPISOIN_M | - USB_INCOMPIP_M | - USB_FETSUSP_M | - USB_PTXFEMP_M; -} - -void dcd_int_enable (uint8_t rhport) -{ - (void) rhport; - esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) _dcd_int_handler, NULL, &usb_ih); -} - -void dcd_int_disable (uint8_t rhport) -{ - (void) rhport; - esp_intr_free(usb_ih); -} - -#endif // #if OPT_MCU_ESP32S2 || OPT_MCU_ESP32S3 diff --git a/tools/iar_gen.py b/tools/iar_gen.py index 8d45659db..571febb2c 100755 --- a/tools/iar_gen.py +++ b/tools/iar_gen.py @@ -74,7 +74,7 @@ def ListPath(path, blacklist=[]): print('') def List(): - ListPath('src', [ 'template.c', 'dcd_synopsys.c', 'dcd_esp32sx.c' ]) + ListPath('src', [ 'template.c' ]) ListPath('lib/SEGGER_RTT') if __name__ == "__main__": -- cgit v1.3.1 From 044512c4599339a57019def2d4587e9207a0649e Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 6 Oct 2025 13:34:05 +0700 Subject: fix typo --- examples/device/mtp/src/mtp_fs_example.c | 4 ++-- src/common/tusb_common.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/examples/device/mtp/src/mtp_fs_example.c b/examples/device/mtp/src/mtp_fs_example.c index 73722fc4f..b4772f146 100644 --- a/examples/device/mtp/src/mtp_fs_example.c +++ b/examples/device/mtp/src/mtp_fs_example.c @@ -59,11 +59,11 @@ storage_info_t storage_info = { .free_space_in_bytes = 0, // calculated at runtime .free_space_in_objects = 0, // calculated at runtime .storage_description = { - .count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t), + .count = (TU_FIELD_SIZE(storage_info_t, storage_description)-1) / sizeof(uint16_t), .utf16 = STORAGE_DESCRIPTRION }, .volume_identifier = { - .count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t), + .count = (TU_FIELD_SIZE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t), .utf16 = VOLUME_IDENTIFIER } }; diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index 12dea2183..2b095e238 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -35,7 +35,7 @@ // Macros Helper //--------------------------------------------------------------------+ #define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) ) -#define TU_FIELD_SZIE(_type, _field) (sizeof(((_type *)0)->_field)) +#define TU_FIELD_SIZE(_type, _field) (sizeof(((_type *)0)->_field)) #define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) ) #define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) ) #define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d)) -- cgit v1.3.1 From bf69c49c29a58055a166e7a3f172f36ee3abb8fb Mon Sep 17 00:00:00 2001 From: RigoLigo Date: Wed, 8 Oct 2025 07:22:34 +0800 Subject: fix incorrect MTP xact_len calculation --- src/class/mtp/mtp_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 798a965eb..04bde9415 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -215,7 +215,7 @@ static bool mtpd_data_xfer(mtp_container_info_t* p_container, uint8_t ep_addr) { TU_ASSERT(p_mtp->phase == MTP_PHASE_DATA); } - const uint16_t xact_len = tu_min16((uint16_t) (p_mtp->total_len - p_mtp->xferred_len), CFG_TUD_MTP_EP_BUFSIZE); + const uint16_t xact_len = (uint16_t) tu_min32(p_mtp->total_len - p_mtp->xferred_len, CFG_TUD_MTP_EP_BUFSIZE); if (xact_len) { // already transferred all bytes in header's length. Application make an unnecessary extra call TU_VERIFY(usbd_edpt_claim(p_mtp->rhport, ep_addr)); -- cgit v1.3.1