summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-02-12 11:28:16 +0700
committerhathach <[email protected]>2025-02-12 11:28:16 +0700
commit87adc63226a59e6c2602b1bc453ced77d44fedba (patch)
tree1e47a80bfab09dbac47312b4242939df03ec7796 /src
parentb41f5eadb3143d0bfaf211d9bc84c95e5d623c29 (diff)
parent5afcfb7522c4fd8b50512cfb277d8a3ccdbc5453 (diff)
Merge branch 'master' into fork/atoktoto/midihost
# Conflicts: # hw/bsp/rp2040/family.cmake # src/class/midi/midi.h # src/class/midi/midi_device.c # src/device/usbd_control.c # src/host/hcd.h # src/host/usbh.c # src/host/usbh.h
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt42
-rw-r--r--src/class/audio/audio.h37
-rw-r--r--src/class/audio/audio_device.c2519
-rw-r--r--src/class/audio/audio_device.h136
-rwxr-xr-xsrc/class/bth/bth_device.c82
-rwxr-xr-xsrc/class/bth/bth_device.h10
-rw-r--r--src/class/cdc/cdc.h55
-rw-r--r--src/class/cdc/cdc_device.c388
-rw-r--r--src/class/cdc/cdc_device.h200
-rw-r--r--src/class/cdc/cdc_host.c1692
-rw-r--r--src/class/cdc/cdc_host.h211
-rw-r--r--src/class/cdc/cdc_rndis.h2
-rw-r--r--src/class/cdc/cdc_rndis_host.c8
-rw-r--r--src/class/cdc/cdc_rndis_host.h2
-rw-r--r--src/class/cdc/serial/ch34x.h84
-rw-r--r--src/class/cdc/serial/cp210x.h62
-rw-r--r--src/class/cdc/serial/ftdi_sio.h246
-rw-r--r--src/class/dfu/dfu_device.c301
-rw-r--r--src/class/dfu/dfu_device.h1
-rw-r--r--src/class/dfu/dfu_rt_device.c27
-rw-r--r--src/class/dfu/dfu_rt_device.h1
-rw-r--r--src/class/hid/hid.h779
-rw-r--r--src/class/hid/hid_device.c391
-rw-r--r--src/class/hid/hid_device.h396
-rw-r--r--src/class/hid/hid_host.c808
-rw-r--r--src/class/hid/hid_host.h95
-rw-r--r--src/class/midi/midi.h4
-rw-r--r--src/class/midi/midi_device.c226
-rw-r--r--src/class/midi/midi_device.h3
-rw-r--r--src/class/msc/msc.h12
-rw-r--r--src/class/msc/msc_device.c669
-rw-r--r--src/class/msc/msc_device.h6
-rw-r--r--src/class/msc/msc_host.c409
-rw-r--r--src/class/msc/msc_host.h41
-rw-r--r--src/class/net/ecm_rndis_device.c312
-rw-r--r--src/class/net/ncm.h117
-rw-r--r--src/class/net/ncm_device.c1155
-rw-r--r--src/class/net/net_device.h31
-rw-r--r--src/class/usbtmc/usbtmc.h50
-rw-r--r--src/class/usbtmc/usbtmc_device.c191
-rw-r--r--src/class/usbtmc/usbtmc_device.h34
-rw-r--r--src/class/vendor/vendor_device.c290
-rw-r--r--src/class/vendor/vendor_device.h115
-rw-r--r--src/class/vendor/vendor_host.c2
-rw-r--r--src/class/vendor/vendor_host.h2
-rw-r--r--src/class/video/video.h322
-rw-r--r--src/class/video/video_device.c595
-rw-r--r--src/class/video/video_device.h3
-rw-r--r--src/common/tusb_common.h83
-rw-r--r--src/common/tusb_compiler.h62
-rw-r--r--src/common/tusb_debug.h45
-rw-r--r--src/common/tusb_fifo.c515
-rw-r--r--src/common/tusb_fifo.h128
-rw-r--r--src/common/tusb_mcu.h380
-rw-r--r--src/common/tusb_private.h119
-rw-r--r--src/common/tusb_types.h289
-rw-r--r--src/common/tusb_verify.h75
-rw-r--r--src/device/dcd.h136
-rw-r--r--src/device/usbd.c1204
-rw-r--r--src/device/usbd.h100
-rw-r--r--src/device/usbd_control.c152
-rw-r--r--src/device/usbd_pvt.h46
-rw-r--r--src/host/hcd.h81
-rw-r--r--src/host/hub.c78
-rw-r--r--src/host/hub.h13
-rw-r--r--src/host/usbh.c1640
-rw-r--r--src/host/usbh.h120
-rw-r--r--src/host/usbh_pvt.h (renamed from src/host/usbh_classdriver.h)32
-rw-r--r--src/osal/osal.h28
-rw-r--r--src/osal/osal_freertos.h174
-rw-r--r--src/osal/osal_mynewt.h61
-rw-r--r--src/osal/osal_none.h119
-rw-r--r--src/osal/osal_pico.h108
-rw-r--r--src/osal/osal_rtthread.h64
-rw-r--r--src/osal/osal_rtx4.h63
-rw-r--r--src/osal/osal_zephyr.h123
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c1076
-rw-r--r--src/portable/bridgetek/ft9xx/dcd_ft9xx.c335
-rw-r--r--src/portable/chipidea/ci_fs/ci_fs_kinetis.h (renamed from src/portable/mentor/musb/musb_msp432e.h)29
-rw-r--r--src/portable/chipidea/ci_fs/ci_fs_mcx.h (renamed from src/portable/mentor/musb/musb_tm4c.h)37
-rw-r--r--src/portable/chipidea/ci_fs/ci_fs_type.h118
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c568
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_imxrt.h74
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h15
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_mcx.h (renamed from src/common/tusb_timeout.h)64
-rw-r--r--src/portable/chipidea/ci_hs/ci_hs_type.h16
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c130
-rw-r--r--src/portable/chipidea/ci_hs/hcd_ci_hs.c54
-rw-r--r--src/portable/dialog/da146xx/dcd_da146xx.c14
-rw-r--r--src/portable/ehci/ehci.c961
-rw-r--r--src/portable/ehci/ehci.h187
-rw-r--r--src/portable/espressif/esp32sx/dcd_esp32sx.c22
-rw-r--r--src/portable/mentor/musb/dcd_musb.c780
-rw-r--r--src/portable/mentor/musb/hcd_musb.c46
-rw-r--r--src/portable/mentor/musb/musb_max32.h150
-rw-r--r--src/portable/mentor/musb/musb_ti.h91
-rw-r--r--src/portable/mentor/musb/musb_type.h2840
-rw-r--r--src/portable/microchip/pic/README.md51
-rw-r--r--src/portable/microchip/pic/dcd_pic.c128
-rw-r--r--src/portable/microchip/pic32mz/dcd_pic32mz.c5
-rw-r--r--src/portable/microchip/pic32mz/usbhs_registers.h44
-rw-r--r--src/portable/microchip/samd/dcd_samd.c19
-rw-r--r--src/portable/microchip/samg/dcd_samg.c54
-rw-r--r--src/portable/microchip/samx7x/common_usb_regs.h6
-rw-r--r--src/portable/microchip/samx7x/dcd_samx7x.c49
-rw-r--r--src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c18
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c684
-rw-r--r--src/portable/nuvoton/nuc120/dcd_nuc120.c12
-rw-r--r--src/portable/nuvoton/nuc121/dcd_nuc121.c12
-rw-r--r--src/portable/nuvoton/nuc505/dcd_nuc505.c14
-rw-r--r--src/portable/nxp/khci/dcd_khci.c42
-rw-r--r--src/portable/nxp/khci/hcd_khci.c64
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.c15
-rw-r--r--src/portable/nxp/lpc17_40/dcd_lpc17_40.h2
-rw-r--r--src/portable/nxp/lpc17_40/hcd_lpc17_40.c4
-rw-r--r--src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c318
-rw-r--r--src/portable/nxp/transdimension/common_transdimension.h136
-rw-r--r--src/portable/nxp/transdimension/dcd_transdimension.c672
-rw-r--r--src/portable/nxp/transdimension/hcd_transdimension.c119
-rw-r--r--src/portable/ohci/ohci.c214
-rw-r--r--src/portable/ohci/ohci.h59
-rw-r--r--src/portable/raspberrypi/pio_usb/dcd_pio_usb.c9
-rw-r--r--src/portable/raspberrypi/pio_usb/hcd_pio_usb.c99
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c540
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c616
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c277
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.h70
-rw-r--r--src/portable/renesas/rusb2/dcd_rusb2.c1032
-rw-r--r--src/portable/renesas/rusb2/hcd_rusb2.c844
-rw-r--r--src/portable/renesas/rusb2/rusb2_common.c62
-rw-r--r--src/portable/renesas/rusb2/rusb2_ra.h109
-rw-r--r--src/portable/renesas/rusb2/rusb2_rx.h94
-rw-r--r--src/portable/renesas/rusb2/rusb2_type.h1780
-rw-r--r--src/portable/renesas/usba/dcd_usba.c915
-rw-r--r--src/portable/renesas/usba/hcd_usba.c875
-rw-r--r--src/portable/sony/cxd56/dcd_cxd56.c36
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c1457
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h426
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_ch32.h206
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h372
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_type.h307
-rw-r--r--src/portable/st/synopsys/dcd_synopsys.c1240
-rw-r--r--src/portable/st/synopsys/synopsys_common.h1465
-rw-r--r--src/portable/st/typec/typec_stm32.c372
-rw-r--r--src/portable/sunxi/dcd_sunxi_musb.c61
-rw-r--r--src/portable/sunxi/musb_def.h2
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c1703
-rw-r--r--src/portable/synopsys/dwc2/dwc2_bcm.h2
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.c311
-rw-r--r--src/portable/synopsys/dwc2/dwc2_common.h101
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h136
-rw-r--r--src/portable/synopsys/dwc2/dwc2_info.md58
-rwxr-xr-xsrc/portable/synopsys/dwc2/dwc2_info.py210
-rw-r--r--src/portable/synopsys/dwc2/dwc2_stm32.h153
-rw-r--r--src/portable/synopsys/dwc2/dwc2_type.h2884
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c1360
-rw-r--r--src/portable/synopsys/dwc2/hwcfg_list.md777
-rw-r--r--src/portable/template/dcd_template.c67
-rw-r--r--src/portable/template/hcd_template.c163
-rw-r--r--src/portable/ti/msp430x5xx/dcd_msp430x5xx.c147
-rw-r--r--src/portable/valentyusb/eptri/dcd_eptri.c15
-rw-r--r--src/portable/valentyusb/eptri/dcd_eptri.h2
-rw-r--r--src/portable/wch/ch32_usbfs_reg.h175
-rw-r--r--src/portable/wch/ch32_usbhs_reg.h395
-rw-r--r--src/portable/wch/dcd_ch32_usbfs.c348
-rw-r--r--src/portable/wch/dcd_ch32_usbhs.c423
-rw-r--r--src/tinyusb.mk26
-rw-r--r--src/tusb.c517
-rw-r--r--src/tusb.h56
-rw-r--r--src/tusb_option.h343
-rw-r--r--src/typec/pd_types.h237
-rw-r--r--src/typec/tcd.h143
-rw-r--r--src/typec/usbc.c219
-rw-r--r--src/typec/usbc.h91
174 files changed, 31736 insertions, 22142 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 000000000..7b3ab4d42
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,42 @@
+# TODO more docs and example on how to use this file
+# TINYUSB_TARGET_PREFIX and TINYUSB_TARGET_SUFFIX can be used to change the name of the target
+
+cmake_minimum_required(VERSION 3.20)
+
+# Add tinyusb to a existing target
+function(tinyusb_target_add TARGET)
+ target_sources(${TARGET} PRIVATE
+ # common
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tusb.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/common/tusb_fifo.c
+ # device
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_device.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_device.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_rt_device.c
+ ${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/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
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c
+ # host
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/usbh.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/hub.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_host.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_host.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_host.c
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c
+ # typec
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/typec/usbc.c
+ )
+ target_include_directories(${TARGET} PUBLIC
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+ # TODO for net driver, should be removed/changed
+ ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
+ )
+endfunction()
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index 6f9c1a6b5..2f97c0f23 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -489,7 +489,7 @@ typedef enum
AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
- AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000,
+ AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x80000000u,
} audio_data_format_type_I_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@@ -640,7 +640,7 @@ typedef enum
AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER = 0x01000000,
AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER = 0x02000000,
AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER = 0x04000000,
- AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000,
+ AUDIO_CHANNEL_CONFIG_RAW_DATA = 0x80000000u,
} audio_channel_config_t;
/// AUDIO Channel Cluster Descriptor (4.1)
@@ -721,11 +721,13 @@ typedef struct TU_ATTR_PACKED
uint8_t bLength ; ///< Size of this descriptor, in bytes: 17.
uint8_t bDescriptorType ; ///< Descriptor Type. Value: TUSB_DESC_CS_INTERFACE.
uint8_t bDescriptorSubType ; ///< Descriptor SubType. Value: AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL.
+ uint8_t bTerminalID ; ///< Constant uniquely identifying the Terminal within the audio function. This value is used in all requests to address this terminal.
uint16_t wTerminalType ; ///< Constant characterizing the type of Terminal. See: audio_terminal_type_t for USB streaming and audio_terminal_input_type_t for other input types.
uint8_t bAssocTerminal ; ///< ID of the Output Terminal to which this Input Terminal is associated.
uint8_t bCSourceID ; ///< ID of the Clock Entity to which this Input Terminal is connected.
uint8_t bNrChannels ; ///< Number of logical output channels in the Terminal’s output audio channel cluster.
uint32_t bmChannelConfig ; ///< Describes the spatial location of the logical channels. See:audio_channel_config_t.
+ uint8_t iChannelNames ; ///< Index of a string descriptor, describing the name of the first logical channel.
uint16_t bmControls ; ///< See: audio_terminal_input_control_pos_t.
uint8_t iTerminal ; ///< Index of a string descriptor, describing the Input Terminal.
} audio_desc_input_terminal_t;
@@ -822,10 +824,10 @@ typedef struct TU_ATTR_PACKED
uint8_t type : 2; ///< Request type tusb_request_type_t.
uint8_t direction : 1; ///< Direction type. tusb_dir_t
} bmRequestType_bit;
-
+
uint8_t bmRequestType;
};
-
+
uint8_t bRequest; ///< Request type audio_cs_req_t
uint8_t bChannelNumber;
uint8_t bControlSelector;
@@ -922,6 +924,31 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
+// 6.1 Interrupt Data Message Format
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bInfo;
+ uint8_t bAttribute;
+ union
+ {
+ uint16_t wValue;
+ struct
+ {
+ uint8_t wValue_cn_or_mcn;
+ uint8_t wValue_cs;
+ };
+ };
+ union
+ {
+ uint16_t wIndex;
+ struct
+ {
+ uint8_t wIndex_ep_or_int;
+ uint8_t wIndex_entity_id;
+ };
+ };
+} audio_interrupt_data_t;
+
/** @} */
#ifdef __cplusplus
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 698fba566..8a130b7e2 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -1,7 +1,8 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Reinhard Panhuber, Jerzy Kasenberg
+ * Copyright (c) 2023 HiFiPhile
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -65,414 +66,589 @@
//--------------------------------------------------------------------+
// Use ring buffer if it's available, some MCUs need extra RAM requirements
+// For DWC2 enable ring buffer will disable DMA (if available)
#ifndef TUD_AUDIO_PREFER_RING_BUFFER
-#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT
-#define TUD_AUDIO_PREFER_RING_BUFFER 0
-#else
-#define TUD_AUDIO_PREFER_RING_BUFFER 1
-#endif
+ #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
+ defined(TUP_USBIP_DWC2)
+ #define TUD_AUDIO_PREFER_RING_BUFFER 0
+ #else
+ #define TUD_AUDIO_PREFER_RING_BUFFER 1
+ #endif
#endif
// 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 synopsys and dcd_transdimension use non-linear buffer for now
-// Synopsys detection copied from dcd_synopsys.c (refactor later on)
-#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
- defined (STM32F107xB) || defined (STM32F107xC)
-#define STM32F1_SYNOPSYS
-#endif
-
-#if defined (STM32L475xx) || defined (STM32L476xx) || \
- defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
- defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
- defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
-#define STM32L4_SYNOPSYS
-#endif
-
-#if (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
- CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
- CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
- (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) || \
- CFG_TUSB_MCU == OPT_MCU_RX63X || \
- CFG_TUSB_MCU == OPT_MCU_RX65X || \
- CFG_TUSB_MCU == OPT_MCU_RX72N || \
- CFG_TUSB_MCU == OPT_MCU_GD32VF103 || \
- CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
- CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
- CFG_TUSB_MCU == OPT_MCU_MIMXRT || \
+// Only STM32 and dcd_transdimension use non-linear buffer for now
+// dwc2 except esp32sx (since it may use dcd_esp32sx)
+// 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)) || \
+ defined(TUP_USBIP_FSDEV) || \
+ CFG_TUSB_MCU == OPT_MCU_RX63X || \
+ CFG_TUSB_MCU == OPT_MCU_RX65X || \
+ CFG_TUSB_MCU == OPT_MCU_RX72N || \
+ CFG_TUSB_MCU == OPT_MCU_LPC18XX || \
+ CFG_TUSB_MCU == OPT_MCU_LPC43XX || \
+ CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX || \
CFG_TUSB_MCU == OPT_MCU_MSP432E4
-#if TUD_AUDIO_PREFER_RING_BUFFER
-#define USE_LINEAR_BUFFER 0
+ #if TUD_AUDIO_PREFER_RING_BUFFER && !CFG_TUD_MEM_DCACHE_ENABLE
+ #define USE_LINEAR_BUFFER 0
+ #else
+ #define USE_LINEAR_BUFFER 1
+ #endif
#else
-#define USE_LINEAR_BUFFER 1
-#endif
-#else
-#define USE_LINEAR_BUFFER 1
+ #define USE_LINEAR_BUFFER 1
#endif
// Declaration of buffers
// Check for maximum supported numbers
#if CFG_TUD_AUDIO > 3
-#error Maximum number of audio functions restricted to three!
+ #error Maximum number of audio functions restricted to three!
#endif
-// EP IN software buffers and mutexes
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
-#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_in_ff_mutex_wr_1; // No need for read mutex as only USB driver reads from FIFO
-#endif
-#endif // CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_in_ff_mutex_wr_2; // No need for read mutex as only USB driver reads from FIFO
+// Put swap buffer in USB section only if necessary
+#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING
+ #define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
+#else
+ #define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_in_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_in_ff_mutex_wr_3; // No need for read mutex as only USB driver reads from FIFO
+#if USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING
+ #define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
+#else
+ #define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
-#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+
+// EP IN software buffers and mutexes
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+tu_static IN_SW_BUF_MEM_ATTR struct {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ);
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ);
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ);
+ #endif
+} ep_in_sw_buf;
+
+ #if CFG_FIFO_MUTEX
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_in_ff_mutex_wr_1;
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_in_ff_mutex_wr_2;
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_in_ff_mutex_wr_3;
+ #endif
+ #endif
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
// Linear buffer TX 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 OR
// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
-#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_2[CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX];
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_in_3[CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX];
-#endif
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+tu_static CFG_TUD_MEM_SECTION struct {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX);
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX);
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX);
+ #endif
+} lin_buf_in;
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
// EP OUT software buffers and mutexes
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
-#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_out_ff_mutex_rd_1; // No need for write mutex as only USB driver writes into FIFO
-#endif
-#endif // CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_out_ff_mutex_rd_2; // No need for write mutex as only USB driver writes into FIFO
-#endif
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t audio_ep_out_sw_buf_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t ep_out_ff_mutex_rd_3; // No need for write mutex as only USB driver writes into FIFO
-#endif
-#endif // CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+tu_static OUT_SW_BUF_MEM_ATTR struct {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ);
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ);
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+ TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ);
+ #endif
+} ep_out_sw_buf;
+
+ #if CFG_FIFO_MUTEX
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_out_ff_mutex_rd_1;
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_out_ff_mutex_rd_2;
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+ tu_static osal_mutex_def_t ep_out_ff_mutex_rd_3;
+ #endif
+ #endif
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
// Linear buffer RX 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 OR
// - the software encoding is used - in this case the linear buffers serve as a target memory where logical channels are encoded into
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
-#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_1[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX];
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_2[CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX];
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t lin_buf_out_3[CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX];
-#endif
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
+tu_static CFG_TUD_MEM_SECTION struct {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX);
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX);
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX);
+ #endif
+} lin_buf_out;
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
// Control buffers
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
+tu_static uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
+
#if CFG_TUD_AUDIO > 1
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
+tu_static uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
#endif
+
#if CFG_TUD_AUDIO > 2
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
+tu_static uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
#endif
// Active alternate setting of interfaces
-uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
+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
-uint8_t alt_setting_2[CFG_TUD_AUDIO_FUNC_2_N_AS_INT];
+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
-uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
+tu_static uint8_t alt_setting_3[CFG_TUD_AUDIO_FUNC_3_N_AS_INT];
#endif
// Software encoding/decoding support FIFOs
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
-#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
-tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
-tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
-tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO]; // No need for read mutex as only USB driver reads from FIFO
-#endif
-#endif
+ #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t tx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_1[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
+ #endif
+ #endif
+
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t tx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_2[CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
+ #endif
+ #endif
+
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t tx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t tx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t tx_supp_ff_mutex_wr_3[CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO];// No need for read mutex as only USB driver reads from FIFO
+ #endif
+ #endif
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
-#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
-tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
-tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
-#endif
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
-tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
-#if CFG_FIFO_MUTEX
-osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO]; // No need for write mutex as only USB driver writes into FIFO
+ #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t rx_supp_ff_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_1[CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
+ #endif
+ #endif
+
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t rx_supp_ff_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_2[CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
+ #endif
+ #endif
+
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ tu_static TU_ATTR_ALIGNED(4) uint8_t rx_supp_ff_buf_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ];
+ tu_static tu_fifo_t rx_supp_ff_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];
+ #if CFG_FIFO_MUTEX
+ tu_static osal_mutex_def_t rx_supp_ff_mutex_rd_3[CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO];// No need for write mutex as only USB driver writes into FIFO
+ #endif
+ #endif
#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 {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_TYPE_DEF(uint32_t, buf_1);
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_TYPE_DEF(uint32_t, buf_2);
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ TUD_EPBUF_TYPE_DEF(uint32_t, buf_3);
+ #endif
+} fb_ep_buf;
#endif
+
+// Aligned buffer for interrupt EP
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+tu_static CFG_TUD_MEM_SECTION struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_AUDIO_INTERRUPT_EP_SZ);
+} int_ep_buf[CFG_TUD_AUDIO];
#endif
typedef struct
{
uint8_t rhport;
- uint8_t const * p_desc; // Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
+ uint8_t const *p_desc;// Pointer pointing to Standard AC Interface Descriptor(4.7.1) - Audio Control descriptor defining audio function
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- 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)
+ 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
#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; // 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)
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- uint8_t ep_fb; // Feedback EP.
-#endif
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb;// Feedback EP.
+ #endif
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
- uint8_t ep_int_ctr; // Audio control interrupt EP.
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+ uint8_t ep_int;// Audio control interrupt EP.
#endif
- /*------------- From this point, data is not cleared by bus reset -------------*/
-
- uint16_t desc_length; // Length of audio function descriptor
-
- // Buffer for control requests
- 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!
+ bool mounted;// Device opened
- // EP Transfer buffers and FIFOs
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-#if !CFG_TUD_AUDIO_ENABLE_DECODING
- tu_fifo_t ep_out_ff;
-#endif
+ uint16_t desc_length;// Length of audio function descriptor
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
struct {
- uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
- uint32_t min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1.
- uint32_t max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1.
+ uint32_t value; // Feedback value for asynchronous mode (in 16.16 format).
+ uint32_t min_value;// min value according to UAC2 FMT-2.0 section 2.3.1.1.
+ uint32_t max_value;// max value according to UAC2 FMT-2.0 section 2.3.1.1.
- uint8_t frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS)
+ uint8_t frame_shift;// bInterval-1 in unit of frame (FS), micro-frame (HS)
uint8_t compute_method;
-
+ bool format_correction;
union {
- uint8_t power_of_2; // pre-computed power of 2 shift
- float float_const; // pre-computed float constant
+ uint8_t power_of_2;// pre-computed power of 2 shift
+ float float_const; // pre-computed float constant
struct {
uint32_t sample_freq;
uint32_t mclk_freq;
- }fixed;
+ } fixed;
-#if 0 // implement later
struct {
- uint32_t nominal_value;
- uint32_t threshold_bytes;
- }fifo_count;
-#endif
- }compute;
+ uint32_t nom_value; // In 16.16 format
+ uint32_t fifo_lvl_avg; // In 16.16 format
+ uint16_t fifo_lvl_thr; // fifo level threshold
+ uint16_t rate_const[2];// pre-computed feedback/fifo_depth rate
+ } fifo_count;
+ } compute;
} feedback;
-#endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
- tu_fifo_t ep_in_ff;
-#endif
+#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
- CFG_TUSB_MEM_ALIGN uint8_t ep_int_ctr_buf[CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE];
-#endif
-
- // Decoding parameters - parameters are set when alternate AS interface is set by host
- // Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
+// Decoding parameters - parameters are set when alternate AS interface is set by host
+// Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
audio_format_type_t format_type_rx;
uint8_t n_channels_rx;
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
audio_data_format_type_I_t format_type_I_rx;
- uint8_t n_bytes_per_sampe_rx;
- uint8_t n_channels_per_ff_rx;
+ uint8_t n_bytes_per_sample_rx;
uint8_t n_ff_used_rx;
+ #endif
#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ uint32_t sample_rate_tx;
+ uint16_t packet_sz_tx[3];
+ uint8_t bclock_id_tx;
+ uint8_t interval_tx;
#endif
- // Encoding parameters - parameters are set when alternate AS interface is set by host
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+// Encoding parameters - parameters are set when alternate AS interface is set by host
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL)
audio_format_type_t format_type_tx;
uint8_t n_channels_tx;
+ uint8_t n_bytes_per_sample_tx;
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
audio_data_format_type_I_t format_type_I_tx;
- uint8_t n_bytes_per_sampe_tx;
- uint8_t n_channels_per_ff_tx;
uint8_t n_ff_used_tx;
+ #endif
#endif
+
+ /*------------- From this point, data is not cleared by bus reset -------------*/
+
+ // Buffer for control requests
+ 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 && !CFG_TUD_AUDIO_ENABLE_DECODING
+ tu_fifo_t ep_out_ff;
#endif
- // Support FIFOs for software encoding and decoding
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+ tu_fifo_t ep_in_ff;
+#endif
+
+// Support FIFOs for software encoding and decoding
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- tu_fifo_t * rx_supp_ff;
+ tu_fifo_t *rx_supp_ff;
uint8_t n_rx_supp_ff;
uint16_t rx_supp_ff_sz_max;
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ uint8_t n_channels_per_ff_rx;
+ #endif
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- tu_fifo_t * tx_supp_ff;
+ tu_fifo_t *tx_supp_ff;
uint8_t n_tx_supp_ff;
uint16_t tx_supp_ff_sz_max;
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ uint8_t n_channels_per_ff_tx;
+ #endif
#endif
- // 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 OR the support FIFOs are used
+// 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 OR the support FIFOs are used
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
- uint8_t * lin_buf_out;
-#define USE_LINEAR_BUFFER_RX 1
+ uint8_t *lin_buf_out;
+ #define USE_LINEAR_BUFFER_RX 1
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_ENCODING)
- uint8_t * lin_buf_in;
-#define USE_LINEAR_BUFFER_TX 1
+ uint8_t *lin_buf_in;
+ #define USE_LINEAR_BUFFER_TX 1
#endif
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint32_t *fb_buf;
+#endif
} audiod_function_t;
#ifndef USE_LINEAR_BUFFER_TX
-#define USE_LINEAR_BUFFER_TX 0
+ #define USE_LINEAR_BUFFER_TX 0
#endif
#ifndef USE_LINEAR_BUFFER_RX
-#define USE_LINEAR_BUFFER_RX 0
+ #define USE_LINEAR_BUFFER_RX 0
#endif
-#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
+#define ITF_MEM_RESET_SIZE offsetof(audiod_function_t, ctrl_buf)
+
+//--------------------------------------------------------------------+
+// WEAK FUNCTION STUBS
+//--------------------------------------------------------------------+
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN
+TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
+ (void) rhport;
+ (void) func_id;
+ (void) ep_in;
+ (void) cur_alt_setting;
+ return true;
+}
+
+TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
+ (void) rhport;
+ (void) n_bytes_copied;
+ (void) func_id;
+ (void) ep_in;
+ (void) cur_alt_setting;
+ return true;
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
+ (void) rhport;
+ (void) n_bytes_received;
+ (void) func_id;
+ (void) ep_out;
+ (void) cur_alt_setting;
+ return true;
+}
+
+TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
+ (void) rhport;
+ (void) n_bytes_received;
+ (void) func_id;
+ (void) ep_out;
+ (void) cur_alt_setting;
+ return true;
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id) {
+ (void) func_id;
+}
+
+TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
+ (void) func_id;
+ (void) alt_itf;
+ feedback_param->method = AUDIO_FEEDBACK_METHOD_DISABLED;
+}
+
+TU_ATTR_WEAK bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
+ (void) func_id;
+ return CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION;
+}
+
+TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift) {
+ (void) func_id;
+ (void) frame_number;
+ (void) interval_shift;
+}
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) {
+ (void) rhport;
+}
+#endif
+
+// Invoked when audio set interface request received
+TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ (void) p_request;
+ return true;
+}
+
+// Invoked when audio set interface request received which closes an EP
+TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ (void) p_request;
+ return true;
+}
+
+// Invoked when audio class specific set request received for an EP
+TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) p_request;
+ (void) pBuff;
+ TU_LOG2(" No EP set request callback available!\r\n");
+ return false;// In case no callback function is present or request can not be conducted we stall it
+}
+
+// Invoked when audio class specific set request received for an interface
+TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) p_request;
+ (void) pBuff;
+ TU_LOG2(" No interface set request callback available!\r\n");
+ return false;// In case no callback function is present or request can not be conducted we stall it
+}
+
+// Invoked when audio class specific set request received for an entity
+TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) p_request;
+ (void) pBuff;
+ TU_LOG2(" No entity set request callback available!\r\n");
+ return false;// In case no callback function is present or request can not be conducted we stall it
+}
+
+// Invoked when audio class specific get request received for an EP
+TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ (void) p_request;
+ TU_LOG2(" No EP get request callback available!\r\n");
+ return false;// Stall
+}
+
+// Invoked when audio class specific get request received for an interface
+TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ (void) p_request;
+ TU_LOG2(" No interface get request callback available!\r\n");
+ return false;// Stall
+}
+
+// Invoked when audio class specific get request received for an entity
+TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+ (void) p_request;
+ TU_LOG2(" No entity get request callback available!\r\n");
+ return false;// Stall
+}
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
+tu_static CFG_TUD_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received);
#endif
#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t* audio);
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio);
#endif
#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
-static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio);
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t *audio);
#endif
-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_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_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);
-static uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio);
+static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
-static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf);
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
+static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf);
-static inline uint8_t tu_desc_subtype(void const* desc)
-{
- return ((uint8_t const*) desc)[2];
+static inline uint8_t tu_desc_subtype(void const *desc) {
+ return ((uint8_t const *) desc)[2];
}
#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq);
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+static bool audiod_calc_tx_packet_sz(audiod_function_t *audio);
+static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_size);
#endif
-bool tud_audio_n_mounted(uint8_t func_id)
-{
- TU_VERIFY(func_id < CFG_TUD_AUDIO);
- audiod_function_t* audio = &_audiod_fct[func_id];
-
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (audio->ep_out == 0) return false;
-#endif
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (audio->ep_in == 0) return false;
-#endif
-
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
- if (audio->ep_int_ctr == 0) return false;
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq);
+static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_new);
#endif
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (audio->ep_fb == 0) return false;
-#endif
+bool tud_audio_n_mounted(uint8_t func_id) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO);
+ audiod_function_t *audio = &_audiod_fct[func_id];
- return true;
+ return audio->mounted;
}
//--------------------------------------------------------------------+
@@ -481,27 +657,23 @@ bool tud_audio_n_mounted(uint8_t func_id)
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
-uint16_t tud_audio_n_available(uint8_t func_id)
-{
+uint16_t tud_audio_n_available(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_count(&_audiod_fct[func_id].ep_out_ff);
}
-uint16_t tud_audio_n_read(uint8_t func_id, void* buffer, uint16_t bufsize)
-{
+uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_read_n(&_audiod_fct[func_id].ep_out_ff, buffer, bufsize);
}
-bool tud_audio_n_clear_ep_out_ff(uint8_t func_id)
-{
+bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
}
-tu_fifo_t* tud_audio_n_get_ep_out_ff(uint8_t func_id)
-{
- if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
+tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
return NULL;
}
@@ -509,27 +681,23 @@ tu_fifo_t* tud_audio_n_get_ep_out_ff(uint8_t func_id)
#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
// Delete all content in the support RX FIFOs
-bool tud_audio_n_clear_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
-{
+bool tud_audio_n_clear_rx_support_ff(uint8_t func_id, uint8_t ff_idx) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
return tu_fifo_clear(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
}
-uint16_t tud_audio_n_available_support_ff(uint8_t func_id, uint8_t ff_idx)
-{
+uint16_t tud_audio_n_available_support_ff(uint8_t func_id, uint8_t ff_idx) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
return tu_fifo_count(&_audiod_fct[func_id].rx_supp_ff[ff_idx]);
}
-uint16_t tud_audio_n_read_support_ff(uint8_t func_id, uint8_t ff_idx, void* buffer, uint16_t bufsize)
-{
+uint16_t tud_audio_n_read_support_ff(uint8_t func_id, uint8_t ff_idx, void *buffer, uint16_t bufsize) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff);
return tu_fifo_read_n(&_audiod_fct[func_id].rx_supp_ff[ff_idx], buffer, bufsize);
}
-tu_fifo_t* tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
-{
- if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff) return &_audiod_fct[func_id].rx_supp_ff[ff_idx];
+tu_fifo_t *tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx) {
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_rx_supp_ff) return &_audiod_fct[func_id].rx_supp_ff[ff_idx];
return NULL;
}
#endif
@@ -539,28 +707,20 @@ tu_fifo_t* tud_audio_n_get_rx_support_ff(uint8_t func_id, uint8_t ff_idx)
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
-{
+static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received) {
uint8_t idxItf = 0;
uint8_t const *dummy2;
uint8_t idx_audio_fct = 0;
- if (tud_audio_rx_done_pre_read_cb || tud_audio_rx_done_post_read_cb)
- {
- 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));
- }
+ 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));
// 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 (or decoded into support RX software FIFO)
- if (tud_audio_rx_done_pre_read_cb)
- {
- TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
- }
+ TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
-#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ #if CFG_TUD_AUDIO_ENABLE_DECODING
- switch (audio->format_type_rx)
- {
+ switch (audio->format_type_rx) {
case AUDIO_FORMAT_TYPE_UNDEFINED:
// INDIVIDUAL DECODING PROCEDURE REQUIRED HERE!
TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
@@ -569,8 +729,7 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
case AUDIO_FORMAT_TYPE_I:
- switch (audio->format_type_I_rx)
- {
+ switch (audio->format_type_I_rx) {
case AUDIO_DATA_FORMAT_TYPE_I_PCM:
TU_VERIFY(audiod_decode_type_I_pcm(rhport, audio, n_bytes_received));
break;
@@ -583,41 +742,44 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
}
break;
- default:
- // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
- TU_BREAKPOINT();
- break;
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_RX not implemented!\r\n");
+ TU_BREAKPOINT();
+ break;
}
// Prepare for next transmission
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
-#else
+ #else
-#if USE_LINEAR_BUFFER_RX
+ #if USE_LINEAR_BUFFER_RX
// Data currently is in linear buffer, copy into EP OUT FIFO
TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
// Schedule for next receive
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
-#else
+ #else
// Data is already placed in EP FIFO, schedule for next receive
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
-#endif
+ #endif
-#endif
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) {
+ audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->ep_out_ff));
+ }
+ #endif
+
+ #endif
// Call a weak callback here - a possibility for user to get informed decoding was completed
- if (tud_audio_rx_done_post_read_cb)
- {
- TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
- }
+ TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
return true;
}
-#endif //CFG_TUD_AUDIO_ENABLE_EP_OUT
+#endif//CFG_TUD_AUDIO_ENABLE_EP_OUT
// The following functions are used in case CFG_TUD_AUDIO_ENABLE_DECODING != 0
#if CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -625,107 +787,74 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio, uint16_t
// Decoding according to 2.3.1.5 Audio Streams
// Helper function
-static inline uint8_t * audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesToCopy, void * dst, uint8_t * dst_end, uint8_t * src, uint8_t const n_ff_used)
-{
-
- // This function is an optimized version of
- // while((uint8_t *)dst < dst_end)
- // {
- // memcpy(dst, src, nBytesToCopy);
- // dst = (uint8_t *)dst + nBytesToCopy;
- // src += nBytesToCopy * n_ff_used;
- // }
-
- // Optimize for fast half word copies
- typedef struct{
- uint16_t val;
- } __attribute((__packed__)) unaligned_uint16_t;
-
- // Optimize for fast word copies
- typedef struct{
- uint32_t val;
- } __attribute((__packed__)) unaligned_uint32_t;
+static inline void *audiod_interleaved_copy_bytes_fast_decode(uint16_t const nBytesPerSample, void *dst, const void *dst_end, void *src, uint8_t const n_ff_used) {
+ // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
+ uint16_t *dst16 = dst;
+ uint16_t *src16 = src;
+ const uint16_t *dst_end16 = dst_end;
+ uint32_t *dst32 = dst;
+ uint32_t *src32 = src;
+ const uint32_t *dst_end32 = dst_end;
- switch (nBytesToCopy)
+ if (nBytesPerSample == 1) {
+ while (dst16 < dst_end16) {
+ *dst16++ = *src16++;
+ src16 += n_ff_used - 1;
+ }
+ return src16;
+ } else if (nBytesPerSample == 2) {
+ while (dst32 < dst_end32) {
+ *dst32++ = *src32++;
+ src32 += n_ff_used - 1;
+ }
+ return src32;
+ } else if (nBytesPerSample == 3) {
+ while (dst16 < dst_end16) {
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ src16 += 3 * (n_ff_used - 1);
+ }
+ return src16;
+ } else// nBytesPerSample == 4
{
- case 1:
- while((uint8_t *)dst < dst_end)
- {
- *(uint8_t *)dst++ = *src;
- src += n_ff_used;
- }
- break;
-
- case 2:
- while((uint8_t *)dst < dst_end)
- {
- *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
- dst += 2;
- src += 2 * n_ff_used;
- }
- break;
-
- case 3:
- while((uint8_t *)dst < dst_end)
- {
- // memcpy(dst, src, 3);
- // dst = (uint8_t *)dst + 3;
- // src += 3 * n_ff_used;
-
- // TODO: Is there a faster way to copy 3 bytes?
- *(uint8_t *)dst++ = *src++;
- *(uint8_t *)dst++ = *src++;
- *(uint8_t *)dst++ = *src++;
-
- src += 3 * (n_ff_used - 1);
- }
- break;
-
- case 4:
- while((uint8_t *)dst < dst_end)
- {
- *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
- dst += 4;
- src += 4 * n_ff_used;
- }
- break;
+ while (dst32 < dst_end32) {
+ *dst32++ = *src32++;
+ *dst32++ = *src32++;
+ src32 += 2 * (n_ff_used - 1);
+ }
+ return src32;
}
-
- return src;
}
-static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received)
-{
+static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received) {
(void) rhport;
// Determine amount of samples
- uint8_t const n_ff_used = audio->n_ff_used_rx;
- uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
+ uint8_t const n_ff_used = audio->n_ff_used_rx;
+ uint16_t const nBytesPerFFToRead = n_bytes_received / n_ff_used;
uint8_t cnt_ff;
// Decode
- uint8_t * src;
- uint8_t * dst_end;
+ uint8_t *src;
+ uint8_t *dst_end;
tu_fifo_buffer_info_t info;
- for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
- {
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++) {
tu_fifo_get_write_info(&audio->rx_supp_ff[cnt_ff], &info);
- if (info.len_lin != 0)
- {
+ if (info.len_lin != 0) {
info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
- src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+ src = &audio->lin_buf_out[cnt_ff * audio->n_channels_per_ff_rx * audio->n_bytes_per_sample_rx];
dst_end = info.ptr_lin + info.len_lin;
- src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
+ src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_lin, dst_end, src, n_ff_used);
// Handle wrapped part of FIFO
info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
- if (info.len_wrap != 0)
- {
+ if (info.len_wrap != 0) {
dst_end = info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
+ audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_wrap, dst_end, src, n_ff_used);
}
tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
}
@@ -734,9 +863,15 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
// Number of bytes should be a multiple of CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_N_CHANNELS_RX but checking makes no sense - no way to correct it
// TU_VERIFY(cnt != n_bytes);
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) {
+ audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->rx_supp_ff[0]));
+ }
+ #endif
+
return true;
}
-#endif //CFG_TUD_AUDIO_ENABLE_DECODING
+#endif//CFG_TUD_AUDIO_ENABLE_DECODING
//--------------------------------------------------------------------+
// WRITE API
@@ -755,21 +890,19 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
* \param[in] len: # of array elements to copy
* \return Number of bytes actually written
*/
-uint16_t tud_audio_n_write(uint8_t func_id, const void * data, uint16_t len)
-{
+uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
}
-bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) // Delete all content in the EP IN FIFO
+bool tud_audio_n_clear_ep_in_ff(uint8_t func_id)// Delete all content in the EP IN FIFO
{
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
}
-tu_fifo_t* tud_audio_n_get_ep_in_ff(uint8_t func_id)
-{
- if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
+tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
return NULL;
}
@@ -777,73 +910,68 @@ tu_fifo_t* tud_audio_n_get_ep_in_ff(uint8_t func_id)
#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
-uint16_t tud_audio_n_flush_tx_support_ff(uint8_t func_id) // Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
+uint16_t tud_audio_n_flush_tx_support_ff(uint8_t func_id)// Force all content in the support TX FIFOs to be written into linear buffer and schedule a transmit
{
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
- audiod_function_t* audio = &_audiod_fct[func_id];
+ audiod_function_t *audio = &_audiod_fct[func_id];
uint16_t n_bytes_copied = tu_fifo_count(&audio->tx_supp_ff[0]);
TU_VERIFY(audiod_tx_done_cb(audio->rhport, audio));
n_bytes_copied -= tu_fifo_count(&audio->tx_supp_ff[0]);
- n_bytes_copied = n_bytes_copied*audio->tx_supp_ff[0].item_size;
+ n_bytes_copied = n_bytes_copied * audio->tx_supp_ff[0].item_size;
return n_bytes_copied;
}
-bool tud_audio_n_clear_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
-{
+bool tud_audio_n_clear_tx_support_ff(uint8_t func_id, uint8_t ff_idx) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
return tu_fifo_clear(&_audiod_fct[func_id].tx_supp_ff[ff_idx]);
}
-uint16_t tud_audio_n_write_support_ff(uint8_t func_id, uint8_t ff_idx, const void * data, uint16_t len)
-{
+uint16_t tud_audio_n_write_support_ff(uint8_t func_id, uint8_t ff_idx, const void *data, uint16_t len) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff);
return tu_fifo_write_n(&_audiod_fct[func_id].tx_supp_ff[ff_idx], data, len);
}
-tu_fifo_t* tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx)
-{
- if(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff) return &_audiod_fct[func_id].tx_supp_ff[ff_idx];
+tu_fifo_t *tud_audio_n_get_tx_support_ff(uint8_t func_id, uint8_t ff_idx) {
+ if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL && ff_idx < _audiod_fct[func_id].n_tx_supp_ff) return &_audiod_fct[func_id].tx_supp_ff[ff_idx];
return NULL;
}
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-
-// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_ctr_done_cb() is called in inform user
-uint16_t tud_audio_int_ctr_n_write(uint8_t func_id, uint8_t const* buffer, uint16_t len)
-{
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+// If no interrupt transmit is pending bytes get written into buffer and a transmit is scheduled - once transmit completed tud_audio_int_done_cb() is called in inform user
+bool tud_audio_int_n_write(uint8_t func_id, const audio_interrupt_data_t *data) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+ TU_VERIFY(_audiod_fct[func_id].ep_int != 0);
+
// We write directly into the EP's buffer - abort if previous transfer not complete
- TU_VERIFY(!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr));
+ TU_VERIFY(usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int));
// Check length
- TU_VERIFY(len <= CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE);
-
- memcpy(_audiod_fct[func_id].ep_int_ctr_buf, buffer, len);
-
- // Schedule transmit
- TU_VERIFY(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int_ctr, _audiod_fct[func_id].ep_int_ctr_buf, len));
+ if (tu_memcpy_s(int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf), data, sizeof(audio_interrupt_data_t)) == 0) {
+ // Schedule transmit
+ TU_ASSERT(usbd_edpt_xfer(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int, int_ep_buf[func_id].buf, sizeof(int_ep_buf[func_id].buf)), 0);
+ } else {
+ // Release endpoint since we don't make any transfer
+ usbd_edpt_release(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_int);
+ }
return true;
}
-
#endif
-
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
// If you prefer your own (more efficient) implementation suiting your purpose set CFG_TUD_AUDIO_ENABLE_ENCODING = 0 and use tud_audio_n_write.
// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
-{
+static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) {
uint8_t idxItf;
uint8_t const *dummy2;
@@ -855,15 +983,14 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
// 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 (in case FIFOs are used) or
// if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
- if (tud_audio_tx_done_pre_load_cb) TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+ TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
// Send everything in ISO EP FIFO
uint16_t n_bytes_tx;
// If support FIFOs are used, encode and schedule transmit
-#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
- switch (audio->format_type_tx)
- {
+ #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
+ switch (audio->format_type_tx) {
case AUDIO_FORMAT_TYPE_UNDEFINED:
// INDIVIDUAL ENCODING PROCEDURE REQUIRED HERE!
TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT encoding not implemented!\r\n");
@@ -873,8 +1000,7 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
case AUDIO_FORMAT_TYPE_I:
- switch (audio->format_type_I_tx)
- {
+ switch (audio->format_type_I_tx) {
case AUDIO_DATA_FORMAT_TYPE_I_PCM:
n_bytes_tx = audiod_encode_type_I_pcm(rhport, audio);
@@ -889,38 +1015,41 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t * audio)
}
break;
- default:
- // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
- TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
- TU_BREAKPOINT();
- n_bytes_tx = 0;
- break;
+ default:
+ // Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!
+ TU_LOG2(" Desired CFG_TUD_AUDIO_FORMAT_TYPE_TX not implemented!\r\n");
+ TU_BREAKPOINT();
+ n_bytes_tx = 0;
+ break;
}
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
-#else
- // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
-
- n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz); // Limit up to max packet size, more can not be done for ISO
-
-#if USE_LINEAR_BUFFER_TX
+ #else
+ // No support FIFOs, if no linear buffer required schedule transmit, else put data into linear buffer and schedule
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ // packet_sz_tx is based on total packet size, here we want size for each support buffer.
+ n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
+ #else
+ n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
+ #endif
+ #if USE_LINEAR_BUFFER_TX
tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
-#else
+ #else
// Send everything in ISO EP FIFO
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
-#endif
+ #endif
-#endif
+ #endif
// Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
- if (tud_audio_tx_done_post_load_cb) TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
+ TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
return true;
}
-#endif //CFG_TUD_AUDIO_ENABLE_EP_IN
+#endif//CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_EP_IN
// Take samples from the support buffer and encode them into the IN EP software FIFO
@@ -941,68 +1070,47 @@ range [-1, +1)
* */
// Helper function
-static inline uint8_t * audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesToCopy, uint8_t * src, uint8_t * src_end, uint8_t * dst, uint8_t const n_ff_used)
-{
- // Optimize for fast half word copies
- typedef struct{
- uint16_t val;
- } __attribute((__packed__)) unaligned_uint16_t;
-
- // Optimize for fast word copies
- typedef struct{
- uint32_t val;
- } __attribute((__packed__)) unaligned_uint32_t;
+static inline void *audiod_interleaved_copy_bytes_fast_encode(uint16_t const nBytesPerSample, void *src, const void *src_end, void *dst, uint8_t const n_ff_used) {
+ // Due to one FIFO contains 2 channels, data always aligned to (nBytesPerSample * 2)
+ uint16_t *dst16 = dst;
+ uint16_t *src16 = src;
+ const uint16_t *src_end16 = src_end;
+ uint32_t *dst32 = dst;
+ uint32_t *src32 = src;
+ const uint32_t *src_end32 = src_end;
- switch (nBytesToCopy)
+ if (nBytesPerSample == 1) {
+ while (src16 < src_end16) {
+ *dst16++ = *src16++;
+ dst16 += n_ff_used - 1;
+ }
+ return dst16;
+ } else if (nBytesPerSample == 2) {
+ while (src32 < src_end32) {
+ *dst32++ = *src32++;
+ dst32 += n_ff_used - 1;
+ }
+ return dst32;
+ } else if (nBytesPerSample == 3) {
+ while (src16 < src_end16) {
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ *dst16++ = *src16++;
+ dst16 += 3 * (n_ff_used - 1);
+ }
+ return dst16;
+ } else// nBytesPerSample == 4
{
- case 1:
- while(src < src_end)
- {
- *dst = *src++;
- dst += n_ff_used;
- }
- break;
-
- case 2:
- while(src < src_end)
- {
- *(unaligned_uint16_t*)dst = *(unaligned_uint16_t*)src;
- src += 2;
- dst += 2 * n_ff_used;
- }
- break;
-
- case 3:
- while(src < src_end)
- {
- // memcpy(dst, src, 3);
- // src = (uint8_t *)src + 3;
- // dst += 3 * n_ff_used;
-
- // TODO: Is there a faster way to copy 3 bytes?
- *dst++ = *src++;
- *dst++ = *src++;
- *dst++ = *src++;
-
- dst += 3 * (n_ff_used - 1);
- }
- break;
-
- case 4:
- while(src < src_end)
- {
- *(unaligned_uint32_t*)dst = *(unaligned_uint32_t*)src;
- src += 4;
- dst += 4 * n_ff_used;
- }
- break;
+ while (src32 < src_end32) {
+ *dst32++ = *src32++;
+ *dst32++ = *src32++;
+ dst32 += 2 * (n_ff_used - 1);
+ }
+ return dst32;
}
-
- return dst;
}
-static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audio)
-{
+static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t *audio) {
// This function relies on the fact that the length of the support FIFOs was configured to be a multiple of the active sample size in bytes s.t. no sample is split within a wrap
// This is ensured within set_interface, where the FIFOs are reconfigured according to this size
@@ -1010,56 +1118,58 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
TU_VERIFY(!usbd_edpt_busy(rhport, audio->ep_in));
// Determine amount of samples
- uint8_t const n_ff_used = audio->n_ff_used_tx;
- uint16_t const nBytesToCopy = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
- uint16_t const capPerFF = audio->ep_in_sz / n_ff_used; // Sample capacity per FIFO in bytes
- uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
+ uint8_t const n_ff_used = audio->n_ff_used_tx;
+ uint16_t nBytesPerFFToSend = tu_fifo_count(&audio->tx_supp_ff[0]);
uint8_t cnt_ff;
- for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++)
- {
+ for (cnt_ff = 1; cnt_ff < n_ff_used; cnt_ff++) {
uint16_t const count = tu_fifo_count(&audio->tx_supp_ff[cnt_ff]);
- if (count < nBytesPerFFToSend)
- {
+ if (count < nBytesPerFFToSend) {
nBytesPerFFToSend = count;
}
}
- // Check if there is enough
- if (nBytesPerFFToSend == 0) return 0;
-
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ const uint16_t norm_packet_sz_tx[3] = {audio->packet_sz_tx[0] / n_ff_used,
+ audio->packet_sz_tx[1] / n_ff_used,
+ audio->packet_sz_tx[2] / n_ff_used};
+ // packet_sz_tx is based on total packet size, here we want size for each support buffer.
+ nBytesPerFFToSend = audiod_tx_packet_size(norm_packet_sz_tx, nBytesPerFFToSend, audio->tx_supp_ff[0].depth, audio->ep_in_sz / n_ff_used);
+ // Check if there is enough data
+ if (nBytesPerFFToSend == 0) return 0;
+ #else
+ // Check if there is enough data
+ if (nBytesPerFFToSend == 0) return 0;
// Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
- nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, capPerFF);
-
+ nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used);
// Round to full number of samples (flooring)
- nBytesPerFFToSend = (nBytesPerFFToSend / nBytesToCopy) * nBytesToCopy;
+ uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx;
+ nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
+ #endif
// Encode
- uint8_t * dst;
- uint8_t * src_end;
+ uint8_t *dst;
+ uint8_t *src_end;
tu_fifo_buffer_info_t info;
- for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
- {
- dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
+ for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++) {
+ dst = &audio->lin_buf_in[cnt_ff * audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx];
tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
- if (info.len_lin != 0)
- {
- info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin); // Limit up to desired length
- src_end = (uint8_t *)info.ptr_lin + info.len_lin;
- dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
+ if (info.len_lin != 0) {
+ info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin);// Limit up to desired length
+ src_end = (uint8_t *) info.ptr_lin + info.len_lin;
+ dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_lin, src_end, dst, n_ff_used);
// Limit up to desired length
info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
// Handle wrapped part of FIFO
- if (info.len_wrap != 0)
- {
- src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
- audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
+ if (info.len_wrap != 0) {
+ src_end = (uint8_t *) info.ptr_wrap + info.len_wrap;
+ audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_wrap, src_end, dst, n_ff_used);
}
tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
@@ -1068,31 +1178,54 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
return nBytesPerFFToSend * n_ff_used;
}
-#endif //CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif//CFG_TUD_AUDIO_ENABLE_ENCODING
// This function is called once a transmit of a feedback packet was successfully completed. Here, we get the next feedback value to be sent
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static inline bool audiod_fb_send(uint8_t rhport, audiod_function_t *audio)
-{
- return usbd_edpt_xfer(rhport, audio->ep_fb, (uint8_t *) &audio->feedback.value, 4);
+static inline bool audiod_fb_send(audiod_function_t *audio) {
+ bool apply_correction = (TUSB_SPEED_FULL == tud_speed_get()) && audio->feedback.format_correction;
+ // Format the feedback value
+ if (apply_correction) {
+ uint8_t *fb = (uint8_t *) audio->fb_buf;
+
+ // For FS format is 10.14
+ *(fb++) = (audio->feedback.value >> 2) & 0xFF;
+ *(fb++) = (audio->feedback.value >> 10) & 0xFF;
+ *(fb++) = (audio->feedback.value >> 18) & 0xFF;
+ *fb = 0;
+ } else {
+ *audio->fb_buf = audio->feedback.value;
+ }
+
+ // About feedback format on FS
+ //
+ // 3 variables: Format | packetSize | sendSize | Working OS:
+ // 16.16 4 4 Linux, Windows
+ // 16.16 4 3 Linux
+ // 16.16 3 4 Linux
+ // 16.16 3 3 Linux
+ // 10.14 4 4 Linux
+ // 10.14 4 3 Linux
+ // 10.14 3 4 Linux, OSX
+ // 10.14 3 3 Linux, OSX
+ //
+ // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty ugly
+ return usbd_edpt_xfer(audio->rhport, audio->ep_fb, (uint8_t *) audio->fb_buf, apply_correction ? 3 : 4);
}
#endif
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void audiod_init(void)
-{
+void audiod_init(void) {
tu_memclr(_audiod_fct, sizeof(_audiod_fct));
- for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
- {
- audiod_function_t* audio = &_audiod_fct[i];
+ for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
+ audiod_function_t *audio = &_audiod_fct[i];
// Initialize control buffers
- switch (i)
- {
+ switch (i) {
case 0:
audio->ctrl_buf = ctrl_buf_1;
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
@@ -1112,8 +1245,7 @@ void audiod_init(void)
}
// Initialize active alternate interface buffers
- switch (i)
- {
+ switch (i) {
#if CFG_TUD_AUDIO_FUNC_1_N_AS_INT > 0
case 0:
audio->alt_setting = alt_setting_1;
@@ -1131,277 +1263,285 @@ void audiod_init(void)
#endif
}
- // Initialize IN EP FIFO if required
+ // Initialize IN EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL);
-#endif
+ #endif
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_2), NULL);
-#endif
+ #endif
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_in_ff, audio_ep_in_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_3), NULL);
-#endif
+ #endif
break;
-#endif
+ #endif
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
- // Initialize linear buffers
+ // Initialize linear buffers
#if USE_LINEAR_BUFFER_TX
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX > 0
case 0:
- audio->lin_buf_in = lin_buf_in_1;
+ audio->lin_buf_in = lin_buf_in.buf_1;
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX > 0
case 1:
- audio->lin_buf_in = lin_buf_in_2;
+ audio->lin_buf_in = lin_buf_in.buf_2;
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX > 0
case 2:
- audio->lin_buf_in = lin_buf_in_3;
+ audio->lin_buf_in = lin_buf_in.buf_3;
break;
-#endif
+ #endif
}
-#endif // USE_LINEAR_BUFFER_TX
+#endif// USE_LINEAR_BUFFER_TX
- // Initialize OUT EP FIFO if required
+ // Initialize OUT EP FIFO if required
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
- tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_1));
-#endif
+ #endif
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
case 1:
- tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_2));
-#endif
+ #endif
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
case 2:
- tu_fifo_config(&audio->ep_out_ff, audio_ep_out_sw_buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_3));
-#endif
+ #endif
break;
-#endif
+ #endif
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
- // Initialize linear buffers
+ // Initialize linear buffers
#if USE_LINEAR_BUFFER_RX
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
case 0:
- audio->lin_buf_out = lin_buf_out_1;
+ audio->lin_buf_out = lin_buf_out.buf_1;
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
case 1:
- audio->lin_buf_out = lin_buf_out_2;
+ audio->lin_buf_out = lin_buf_out.buf_2;
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
case 2:
- audio->lin_buf_out = lin_buf_out_3;
+ audio->lin_buf_out = lin_buf_out.buf_3;
break;
-#endif
+ #endif
}
-#endif // USE_LINEAR_BUFFER_TX
+#endif// USE_LINEAR_BUFFER_RX
- // Initialize TX support FIFOs if required
+#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX > 0
+ case 0:
+ audio->fb_buf = &fb_ep_buf.buf_1;
+ break;
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX > 0
+ case 1:
+ audio->fb_buf = &fb_ep_buf.buf_2;
+ break;
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX > 0
+ case 2:
+ audio->fb_buf = &fb_ep_buf.buf_3;
+ break;
+ #endif
+ }
+#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+
+ // Initialize TX support FIFOs if required
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
case 0:
audio->tx_supp_ff = tx_supp_ff_1;
audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO;
audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&tx_supp_ff_1[cnt], tx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&tx_supp_ff_1[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_1[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
case 1:
audio->tx_supp_ff = tx_supp_ff_2;
audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO;
audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&tx_supp_ff_2[cnt], tx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&tx_supp_ff_2[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_2[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
case 2:
audio->tx_supp_ff = tx_supp_ff_3;
audio->n_tx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO;
audio->tx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&tx_supp_ff_3[cnt], tx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&tx_supp_ff_3[cnt], osal_mutex_create(&tx_supp_ff_mutex_wr_3[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- // Set encoding parameters for Type_I formats
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
+ // Set encoding parameters for Type_I formats
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ > 0
case 0:
audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX;
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ > 0
case 1:
audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX;
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ > 0
case 2:
audio->n_channels_per_ff_tx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX;
break;
-#endif
+ #endif
}
-#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- // Initialize RX support FIFOs if required
+ // Initialize RX support FIFOs if required
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
case 0:
audio->rx_supp_ff = rx_supp_ff_1;
audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO;
audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&rx_supp_ff_1[cnt], rx_supp_ff_buf_1[cnt], CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&rx_supp_ff_1[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_1[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
case 1:
audio->rx_supp_ff = rx_supp_ff_2;
audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO;
audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&rx_supp_ff_2[cnt], rx_supp_ff_buf_2[cnt], CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&rx_supp_ff_2[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_2[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
case 2:
audio->rx_supp_ff = rx_supp_ff_3;
audio->n_rx_supp_ff = CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO;
audio->rx_supp_ff_sz_max = CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ;
- for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO; cnt++) {
tu_fifo_config(&rx_supp_ff_3[cnt], rx_supp_ff_buf_3[cnt], CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ, 1, true);
-#if CFG_FIFO_MUTEX
+ #if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&rx_supp_ff_3[cnt], osal_mutex_create(&rx_supp_ff_mutex_rd_3[cnt]), NULL);
-#endif
+ #endif
}
break;
-#endif // CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ #endif// CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- // Set encoding parameters for Type_I formats
+ // Set encoding parameters for Type_I formats
#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- switch (i)
- {
-#if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
+ switch (i) {
+ #if CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ > 0
case 0:
audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX;
break;
-#endif
-#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ > 0
case 1:
audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX;
break;
-#endif
-#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
+ #endif
+ #if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ > 0
case 2:
audio->n_channels_per_ff_rx = CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX;
break;
-#endif
+ #endif
}
-#endif // CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+#endif// CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
}
}
-void audiod_reset(uint8_t rhport)
-{
+bool audiod_deinit(void) {
+ return false;// TODO not implemented yet
+}
+
+void audiod_reset(uint8_t rhport) {
(void) rhport;
- for(uint8_t i=0; i<CFG_TUD_AUDIO; i++)
- {
- audiod_function_t* audio = &_audiod_fct[i];
+ for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
+ audiod_function_t *audio = &_audiod_fct[i];
tu_memclr(audio, ITF_MEM_RESET_SIZE);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
@@ -1413,35 +1553,32 @@ void audiod_reset(uint8_t rhport)
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
tu_fifo_clear(&audio->tx_supp_ff[cnt]);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
- {
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
tu_fifo_clear(&audio->rx_supp_ff[cnt]);
}
#endif
}
}
-uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
(void) max_len;
- TU_VERIFY ( TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
- AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
+ TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
+ AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
// Verify version is correct - this check can be omitted
TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
- // Verify interrupt control EP is enabled if demanded by descriptor - this should be best some static check however - this check can be omitted
- if (itf_desc->bNumEndpoints == 1) // 0 or 1 EPs are allowed
- {
- TU_VERIFY(CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0);
+ // Verify interrupt control EP is enabled if demanded by descriptor
+ TU_ASSERT(itf_desc->bNumEndpoints <= 1);// 0 or 1 EPs are allowed
+ if (itf_desc->bNumEndpoints == 1) {
+ TU_ASSERT(CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP);
}
// Alternate setting MUST be zero - this check can be omitted
@@ -1449,16 +1586,13 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
// Find available audio driver interface
uint8_t i;
- for (i = 0; i < CFG_TUD_AUDIO; i++)
- {
- if (!_audiod_fct[i].p_desc)
- {
- _audiod_fct[i].p_desc = (uint8_t const *)itf_desc; // Save pointer to AC descriptor which is by specification always the first one
+ for (i = 0; i < CFG_TUD_AUDIO; i++) {
+ if (!_audiod_fct[i].p_desc) {
+ _audiod_fct[i].p_desc = (uint8_t const *) itf_desc;// Save pointer to AC descriptor which is by specification always the first one
_audiod_fct[i].rhport = rhport;
// Setup descriptor lengths
- switch (i)
- {
+ switch (i) {
case 0:
_audiod_fct[i].desc_length = CFG_TUD_AUDIO_FUNC_1_DESC_LEN;
break;
@@ -1474,21 +1608,136 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
#endif
}
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ {
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ uint8_t ep_in = 0;
+ uint16_t ep_in_size = 0;
+ #endif
+
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ uint8_t ep_out = 0;
+ uint16_t ep_out_size = 0;
+ #endif
+
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ uint8_t ep_fb = 0;
+ #endif
+ uint8_t const *p_desc = _audiod_fct[i].p_desc;
+ uint8_t const *p_desc_end = 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_ENDPOINT) {
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ // Explicit feedback EP
+ if (desc_ep->bmAttributes.usage == 1) {
+ ep_fb = desc_ep->bEndpointAddress;
+ }
+ #endif
+ // Data EP
+ if (desc_ep->bmAttributes.usage == 0) {
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ ep_in = desc_ep->bEndpointAddress;
+ ep_in_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_in_size);
+ #endif
+ } else {
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ ep_out = desc_ep->bEndpointAddress;
+ ep_out_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_out_size);
+ #endif
+ }
+ }
+ }
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (ep_in) {
+ usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
+ }
+ #endif
+
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (ep_out) {
+ usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
+ }
+ #endif
+
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (ep_fb) {
+ usbd_edpt_iso_alloc(rhport, ep_fb, 4);
+ }
+ #endif
+ }
+#endif// TUP_DCD_EDPT_ISO_ALLOC
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ {
+ uint8_t const *p_desc = _audiod_fct[i].p_desc;
+ uint8_t const *p_desc_end = 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_ENDPOINT) {
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ if (desc_ep->bmAttributes.usage == 0) {
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ _audiod_fct[i].interval_tx = desc_ep->bInterval;
+ }
+ }
+ }
+ } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL) {
+ if (tu_unaligned_read16(p_desc + 4) == AUDIO_TERM_TYPE_USB_STREAMING) {
+ _audiod_fct[i].bclock_id_tx = p_desc[8];
+ }
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+#endif// CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+ {
+ uint8_t const *p_desc = _audiod_fct[i].p_desc;
+ uint8_t const *p_desc_end = 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) {
+ // For each endpoint
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ // If endpoint is input-direction and interrupt-type
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
+ // Store endpoint number and open endpoint
+ _audiod_fct[i].ep_int = ep_addr;
+ TU_ASSERT(usbd_edpt_open(_audiod_fct[i].rhport, desc_ep));
+ }
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ }
+#endif
+
+ _audiod_fct[i].mounted = true;
break;
}
}
// Verify we found a free one
- TU_ASSERT( i < CFG_TUD_AUDIO );
+ TU_ASSERT(i < CFG_TUD_AUDIO);
// This is all we need so far - the EPs are setup by a later set_interface request (as per UAC2 specification)
- uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN; // - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
+ uint16_t drv_len = _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;// - TUD_AUDIO_DESC_IAD_LEN since tinyUSB already handles the IAD descriptor
return drv_len;
}
-static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * p_request)
-{
+static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p_request) {
uint8_t const itf = tu_u16_low(p_request->wIndex);
// Find index of audio streaming interface
@@ -1503,8 +1752,7 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *
return true;
}
-static bool audiod_set_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) {
(void) rhport;
// Here we need to do the following:
@@ -1528,62 +1776,69 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *p_desc;
TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &p_desc));
- audiod_function_t* audio = &_audiod_fct[func_id];
+ audiod_function_t *audio = &_audiod_fct[func_id];
- // Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
+// Look if there is an EP to be closed - for this driver, there are only 3 possible EPs which may be closed (only AS related EPs can be closed, AC EP (if present) is always open)
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (audio->ep_in_as_intf_num == itf)
- {
+ if (audio->ep_in_as_intf_num == itf) {
audio->ep_in_as_intf_num = 0;
+ #ifndef TUP_DCD_EDPT_ISO_ALLOC
usbd_edpt_close(rhport, audio->ep_in);
+ #endif
// Clear FIFOs, since data is no longer valid
-#if !CFG_TUD_AUDIO_ENABLE_ENCODING
+ #if !CFG_TUD_AUDIO_ENABLE_ENCODING
tu_fifo_clear(&audio->ep_in_ff);
-#else
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
- {
+ #else
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
tu_fifo_clear(&audio->tx_supp_ff[cnt]);
}
-#endif
+ #endif
// Invoke callback - can be used to stop data sampling
- if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+ TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
- audio->ep_in = 0; // Necessary?
+ audio->ep_in = 0;// Necessary?
+ #if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ audio->packet_sz_tx[0] = 0;
+ audio->packet_sz_tx[1] = 0;
+ audio->packet_sz_tx[2] = 0;
+ #endif
}
-#endif
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (audio->ep_out_as_intf_num == itf)
- {
+ if (audio->ep_out_as_intf_num == itf) {
audio->ep_out_as_intf_num = 0;
+ #ifndef TUP_DCD_EDPT_ISO_ALLOC
usbd_edpt_close(rhport, audio->ep_out);
+ #endif
// Clear FIFOs, since data is no longer valid
-#if !CFG_TUD_AUDIO_ENABLE_DECODING
+ #if !CFG_TUD_AUDIO_ENABLE_DECODING
tu_fifo_clear(&audio->ep_out_ff);
-#else
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
- {
+ #else
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
tu_fifo_clear(&audio->rx_supp_ff[cnt]);
}
-#endif
+ #endif
// Invoke callback - can be used to stop data sampling
- if (tud_audio_set_itf_close_EP_cb) TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
+ TU_VERIFY(tud_audio_set_itf_close_EP_cb(rhport, p_request));
- audio->ep_out = 0; // Necessary?
+ audio->ep_out = 0;// Necessary?
// Close corresponding feedback EP
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ #ifndef TUP_DCD_EDPT_ISO_ALLOC
usbd_edpt_close(rhport, audio->ep_fb);
+ #endif
audio->ep_fb = 0;
tu_memclr(&audio->feedback, sizeof(audio->feedback));
-#endif
+ #endif
}
-#endif
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Save current alternative interface setting
audio->alt_setting[idxItf] = alt;
@@ -1593,30 +1848,31 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
// p_desc starts at required interface with alternate setting zero
- while (p_desc < p_desc_end)
- {
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0) {
// Find correct interface
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
- {
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
- uint8_t const * p_desc_parse_for_params = p_desc;
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *) p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const *) p_desc)->bAlternateSetting == alt) {
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
+ uint8_t const *p_desc_parse_for_params = p_desc;
#endif
// From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
- uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
- while (foundEPs < nEps && p_desc < p_desc_end)
- {
- if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
- {
- tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const *) p_desc)->bNumEndpoints;
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (foundEPs < nEps && (p_desc_end - p_desc > 0)) {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ TU_ASSERT(usbd_edpt_iso_activate(rhport, desc_ep));
+#else
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
-
+#endif
uint8_t const ep_addr = desc_ep->bEndpointAddress;
//TODO: We need to set EP non busy since this is not taken care of right now in ep_close() - THIS IS A WORKAROUND!
usbd_edpt_clear_stall(rhport, ep_addr);
#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 0x00) // Check if usage is data EP
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 0x00)// Check if usage is data EP
{
// Save address
audio->ep_in = ep_addr;
@@ -1624,71 +1880,65 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
// If software encoding is enabled, parse for the corresponding parameters - doing this here means only AS interfaces with EPs get scanned for parameters
-#if CFG_TUD_AUDIO_ENABLE_ENCODING
+ #if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
- // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / audio->n_bytes_per_sampe_tx) * audio->n_bytes_per_sampe_tx);
- for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
- {
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+ #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx)) * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx));
+ for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++) {
tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
}
audio->n_ff_used_tx = audio->n_channels_tx / audio->n_channels_per_ff_tx;
- TU_ASSERT( audio->n_ff_used_tx <= audio->n_tx_supp_ff );
-#endif
-
-#endif
+ TU_ASSERT(audio->n_ff_used_tx <= audio->n_tx_supp_ff);
+ #endif
+ #endif
// Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
// It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
+#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) // Checking usage not necessary
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT)// Checking usage not necessary
{
// Save address
audio->ep_out = ep_addr;
audio->ep_out_as_intf_num = itf;
audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
-#if CFG_TUD_AUDIO_ENABLE_DECODING
+ #if CFG_TUD_AUDIO_ENABLE_DECODING
audiod_parse_for_AS_params(audio, p_desc_parse_for_params, p_desc_end, itf);
- // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx;
- for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
- {
+ // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sample_rx) * audio->n_bytes_per_sample_rx;
+ for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++) {
tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
}
audio->n_ff_used_rx = audio->n_channels_rx / audio->n_channels_per_ff_rx;
- TU_ASSERT( audio->n_ff_used_rx <= audio->n_rx_supp_ff );
-#endif
-#endif
+ TU_ASSERT(audio->n_ff_used_rx <= audio->n_rx_supp_ff);
+ #endif
+ #endif
// Prepare for incoming data
-#if USE_LINEAR_BUFFER_RX
+ #if USE_LINEAR_BUFFER_RX
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out, audio->ep_out_sz), false);
-#else
+ #else
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_out, &audio->ep_out_ff, audio->ep_out_sz), false);
-#endif
+ #endif
}
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) // Check if usage is explicit data feedback
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1)// Check if usage is explicit data feedback
{
audio->ep_fb = ep_addr;
- audio->feedback.frame_shift = desc_ep->bInterval -1;
-
- // Enable SOF interrupt if callback is implemented
- if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true);
+ audio->feedback.frame_shift = desc_ep->bInterval - 1;
}
-#endif
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+ #endif
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
foundEPs += 1;
}
@@ -1698,47 +1948,59 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(foundEPs == nEps);
// Invoke one callback for a final set interface
- if (tud_audio_set_itf_cb) TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
+ TU_VERIFY(tud_audio_set_itf_cb(rhport, p_request));
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
- // Prepare feedback computation if callback is available
- if (tud_audio_feedback_params_cb)
- {
+ // Prepare feedback computation if endpoint is available
+ if (audio->ep_fb != 0) {
audio_feedback_params_t fb_param;
tud_audio_feedback_params_cb(func_id, alt, &fb_param);
audio->feedback.compute_method = fb_param.method;
+ if (TUSB_SPEED_FULL == tud_speed_get())
+ audio->feedback.format_correction = tud_audio_feedback_format_correction_cb(func_id);
+
// Minimal/Maximum value in 16.16 format for full speed (1ms per frame) or high speed (125 us per frame)
- uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
- audio->feedback.min_value = (fb_param.sample_freq/frame_div - 1) << 16;
- audio->feedback.max_value = (fb_param.sample_freq/frame_div + 1) << 16;
+ uint32_t const frame_div = (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
+ audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
+ audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
- switch(fb_param.method)
- {
+ switch (fb_param.method) {
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
- set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
- break;
+ audiod_set_fb_params_freq(audio, fb_param.sample_freq, fb_param.frequency.mclk_freq);
+ break;
- #if 0 // implement later
- case AUDIO_FEEDBACK_METHOD_FIFO_COUNT:
- {
- uint64_t fb64 = ((uint64_t) fb_param.sample_freq) << 16;
- audio->feedback.compute.fifo_count.nominal_value = (uint32_t) (fb64 / frame_div);
- audio->feedback.compute.fifo_count.threshold_bytes = fb_param.fifo_count.threshold_bytes;
-
- tud_audio_fb_set(audio->feedback.compute.fifo_count.nominal_value);
- }
- break;
- #endif
+ case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
+ // Initialize the threshold level to half filled
+ uint16_t fifo_lvl_thr;
+ #if CFG_TUD_AUDIO_ENABLE_DECODING
+ fifo_lvl_thr = tu_fifo_depth(&audio->rx_supp_ff[0]) / 2;
+ #else
+ fifo_lvl_thr = tu_fifo_depth(&audio->ep_out_ff) / 2;
+ #endif
+ audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
+ audio->feedback.compute.fifo_count.fifo_lvl_avg = ((uint32_t) fifo_lvl_thr) << 16;
+ // Avoid 64bit division
+ uint32_t nominal = ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
+ audio->feedback.compute.fifo_count.nom_value = nominal;
+ audio->feedback.compute.fifo_count.rate_const[0] = (uint16_t) ((audio->feedback.max_value - nominal) / fifo_lvl_thr);
+ audio->feedback.compute.fifo_count.rate_const[1] = (uint16_t) ((nominal - audio->feedback.min_value) / fifo_lvl_thr);
+ // On HS feedback is more sensitive since packet size can vary every MSOF, could cause instability
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ audio->feedback.compute.fifo_count.rate_const[0] /= 8;
+ audio->feedback.compute.fifo_count.rate_const[1] /= 8;
+ }
+ } break;
// nothing to do
- default: break;
+ default:
+ break;
}
}
-#endif
+#endif// CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// We are done - abort loop
break;
@@ -1750,16 +2012,21 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Disable SOF interrupt if no driver has any enabled feedback EP
- bool disable = true;
- for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
- {
- if (_audiod_fct[i].ep_fb != 0)
- {
- disable = false;
+ bool enable_sof = false;
+ for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
+ if (_audiod_fct[i].ep_fb != 0 &&
+ (_audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED ||
+ _audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT ||
+ _audiod_fct[i].feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2)) {
+ enable_sof = true;
break;
}
}
- if (disable) usbd_sof_enable(rhport, false);
+ usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, enable_sof);
+#endif
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ audiod_calc_tx_packet_sz(audio);
#endif
tud_control_status(rhport, p_request);
@@ -1769,76 +2036,51 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
// Invoked when class request DATA stage is finished.
// return false to stall control EP (e.g Host send non-sense DATA)
-static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
-{
+static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const *p_request) {
// Handle audio class specific set requests
- if(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT)
- {
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) {
uint8_t func_id;
- switch (p_request->bmRequestType_bit.recipient)
- {
- case TUSB_REQ_RCPT_INTERFACE:
- {
+ switch (p_request->bmRequestType_bit.recipient) {
+ case TUSB_REQ_RCPT_INTERFACE: {
uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
- if (entityID != 0)
- {
- if (tud_audio_set_req_entity_cb)
- {
- // Check if entity is present and get corresponding driver index
- TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
+ if (entityID != 0) {
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
- // Invoke callback
- return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
- }
- else
- {
- TU_LOG2(" No entity set request callback available!\r\n");
- return false; // In case no callback function is present or request can not be conducted we stall it
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
}
- }
- else
- {
- if (tud_audio_set_req_itf_cb)
- {
- // Find index of audio driver structure and verify interface really exists
- TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+#endif
- // Invoke callback
- return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
- }
- else
- {
- TU_LOG2(" No interface set request callback available!\r\n");
- return false; // In case no callback function is present or request can not be conducted we stall it
- }
+ // Invoke callback
+ return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ } else {
+ // Find index of audio driver structure and verify interface really exists
+ TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
+
+ // Invoke callback
+ return tud_audio_set_req_itf_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
}
- }
- break;
+ } break;
- case TUSB_REQ_RCPT_ENDPOINT:
- {
+ case TUSB_REQ_RCPT_ENDPOINT: {
uint8_t ep = TU_U16_LOW(p_request->wIndex);
- if (tud_audio_set_req_ep_cb)
- {
- // Check if entity is present and get corresponding driver index
- TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
+ // Check if entity is present and get corresponding driver index
+ TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
- // Invoke callback
- return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
- }
- else
- {
- TU_LOG2(" No EP set request callback available!\r\n");
- return false; // In case no callback function is present or request can not be conducted we stall it
- }
- }
- break;
+ // Invoke callback
+ return tud_audio_set_req_ep_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf);
+ } break;
// Unknown/Unsupported recipient
- default: TU_BREAKPOINT(); return false;
+ default:
+ TU_BREAKPOINT();
+ return false;
}
}
return true;
@@ -1846,106 +2088,75 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
-static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
-{
+static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Handle standard requests - standard set requests usually have no data stage so we also handle set requests here
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
- {
- switch (p_request->bRequest)
- {
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
+ switch (p_request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
return audiod_get_interface(rhport, p_request);
case TUSB_REQ_SET_INTERFACE:
return audiod_set_interface(rhport, p_request);
- // Unknown/Unsupported request
- default: TU_BREAKPOINT(); return false;
+ case TUSB_REQ_CLEAR_FEATURE:
+ return true;
+
+ // Unknown/Unsupported request
+ default:
+ TU_BREAKPOINT();
+ return false;
}
}
// Handle class requests
- if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
- {
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t func_id;
// Conduct checks which depend on the recipient
- switch (p_request->bmRequestType_bit.recipient)
- {
- case TUSB_REQ_RCPT_INTERFACE:
- {
+ switch (p_request->bmRequestType_bit.recipient) {
+ case TUSB_REQ_RCPT_INTERFACE: {
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Verify if entity is present
- if (entityID != 0)
- {
+ if (entityID != 0) {
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
- if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
- {
- if (tud_audio_get_req_entity_cb)
- {
- return tud_audio_get_req_entity_cb(rhport, p_request);
- }
- else
- {
- TU_LOG2(" No entity get request callback available!\r\n");
- return false; // Stall
- }
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+ return tud_audio_get_req_entity_cb(rhport, p_request);
}
- }
- else
- {
+ } else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
- if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
- {
- if (tud_audio_get_req_itf_cb)
- {
- return tud_audio_get_req_itf_cb(rhport, p_request);
- }
- else
- {
- TU_LOG2(" No interface get request callback available!\r\n");
- return false; // Stall
- }
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+ return tud_audio_get_req_itf_cb(rhport, p_request);
}
}
- }
- break;
+ } break;
- case TUSB_REQ_RCPT_ENDPOINT:
- {
+ case TUSB_REQ_RCPT_ENDPOINT: {
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
// In case we got a get request invoke callback - callback needs to answer as defined in UAC2 specification page 89 - 5. Requests
- if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN)
- {
- if (tud_audio_get_req_ep_cb)
- {
- return tud_audio_get_req_ep_cb(rhport, p_request);
- }
- else
- {
- TU_LOG2(" No EP get request callback available!\r\n");
- return false; // Stall
- }
+ if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+ return tud_audio_get_req_ep_cb(rhport, p_request);
}
- }
- break;
+ } break;
// Unknown/Unsupported recipient
- default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ default:
+ TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient);
+ TU_BREAKPOINT();
+ return false;
}
// If we end here, the received request is a set request - we schedule a receive for the data stage and return true here. We handle the rest later in audiod_control_complete() once the data stage was finished
@@ -1958,35 +2169,28 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
return false;
}
-bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
+bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+ if (stage == CONTROL_STAGE_SETUP) {
return audiod_control_request(rhport, request);
- }
- else if ( stage == CONTROL_STAGE_DATA )
- {
+ } else if (stage == CONTROL_STAGE_DATA) {
return audiod_control_complete(rhport, request);
}
return true;
}
-bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
(void) xferred_bytes;
// Search for interface belonging to given end point address and proceed as required
- for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
- {
- audiod_function_t* audio = &_audiod_fct[func_id];
+ for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// Data transmission of control interrupt finished
- if (audio->ep_int_ctr == ep_addr)
- {
+ if (audio->ep_int == ep_addr) {
// According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
// In case there is nothing to send we have to return a NAK - this is taken care of by PHY ???
// In case of an erroneous transmission a retransmission is conducted - this is taken care of by PHY ???
@@ -1994,7 +2198,8 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// I assume here, that things above are handled by PHY
// All transmission is done - what remains to do is to inform job was completed
- if (tud_audio_int_ctr_done_cb) TU_VERIFY(tud_audio_int_ctr_done_cb(rhport, (uint16_t) xferred_bytes));
+ tud_audio_int_done_cb(rhport);
+ return true;
}
#endif
@@ -2002,8 +2207,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
#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 && audio->alt_setting != 0) {
// 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.
@@ -2023,27 +2227,24 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// New audio packet received
- if (audio->ep_out == ep_addr)
- {
+ if (audio->ep_out == ep_addr) {
TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes));
return true;
}
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+ #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Transmission of feedback EP finished
- if (audio->ep_fb == ep_addr)
- {
- if (tud_audio_fb_done_cb) tud_audio_fb_done_cb(func_id);
+ if (audio->ep_fb == ep_addr) {
+ tud_audio_fb_done_cb(func_id);
- // Schedule a transmit with the new value if EP is not busy
- if (!usbd_edpt_busy(rhport, audio->ep_fb))
- {
+ // Schedule a transmit with the new value if EP is not busy
+ if (usbd_edpt_claim(rhport, audio->ep_fb)) {
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
- return audiod_fb_send(rhport, audio);
+ return audiod_fb_send(audio);
}
}
-#endif
+ #endif
#endif
}
@@ -2052,8 +2253,7 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, uint32_t mclk_freq)
-{
+static bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq) {
// Check if frame interval is within sane limits
// The interval value n_frames was taken from the descriptors within audiod_set_interface()
@@ -2062,23 +2262,19 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u
uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
- if ( (((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame )
- {
- TU_LOG1(" UAC2 feedback interval too small\r\n"); TU_BREAKPOINT(); return false;
+ if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
+ TU_LOG1(" UAC2 feedback interval too small\r\n");
+ TU_BREAKPOINT();
+ return false;
}
// Check if parameters really allow for a power of two division
- if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq))
- {
- audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
- audio->feedback.compute.power_of_2 = 16 - audio->feedback.frame_shift - tu_log2(mclk_freq / sample_freq);
- }
- else if ( audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT)
- {
- audio->feedback.compute.float_const = (float)sample_freq / mclk_freq * (1UL << (16 - audio->feedback.frame_shift));
- }
- else
- {
+ if ((mclk_freq % sample_freq) == 0 && tu_is_power_of_two(mclk_freq / sample_freq)) {
+ audio->feedback.compute_method = AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
+ audio->feedback.compute.power_of_2 = (uint8_t) (16 - (audio->feedback.frame_shift - 1) - tu_log2(mclk_freq / sample_freq));
+ } else if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
+ audio->feedback.compute.float_const = (float) sample_freq / (float) mclk_freq * (1UL << (16 - (audio->feedback.frame_shift - 1)));
+ } else {
audio->feedback.compute.fixed.sample_freq = sample_freq;
audio->feedback.compute.fixed.mclk_freq = mclk_freq;
}
@@ -2086,46 +2282,83 @@ static bool set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq, u
return true;
}
-uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles)
-{
- audiod_function_t* audio = &_audiod_fct[func_id];
+static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_new) {
+ /* Low-pass (averaging) filter */
+ uint32_t lvl = audio->feedback.compute.fifo_count.fifo_lvl_avg;
+ lvl = (uint32_t) (((uint64_t) lvl * 63 + ((uint32_t) lvl_new << 16)) >> 6);
+ audio->feedback.compute.fifo_count.fifo_lvl_avg = lvl;
+
+ uint32_t const ff_lvl = lvl >> 16;
+ uint16_t const ff_thr = audio->feedback.compute.fifo_count.fifo_lvl_thr;
+ uint16_t const *rate = audio->feedback.compute.fifo_count.rate_const;
+
uint32_t feedback;
- switch (audio->feedback.compute_method)
- {
+ if (ff_lvl < ff_thr) {
+ feedback = audio->feedback.compute.fifo_count.nom_value + (ff_thr - ff_lvl) * rate[0];
+ } else {
+ feedback = audio->feedback.compute.fifo_count.nom_value - (ff_lvl - ff_thr) * rate[1];
+ }
+
+ if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
+ if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
+ audio->feedback.value = feedback;
+
+ // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
+ if (usbd_edpt_claim(audio->rhport, audio->ep_fb)) {
+ audiod_fb_send(audio);
+ }
+}
+
+uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
+ audiod_function_t *audio = &_audiod_fct[func_id];
+ uint32_t feedback;
+
+ switch (audio->feedback.compute_method) {
case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
feedback = (cycles << audio->feedback.compute.power_of_2);
- break;
+ break;
case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
feedback = (uint32_t) ((float) cycles * audio->feedback.compute.float_const);
- break;
+ break;
- case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
- {
- uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - audio->feedback.frame_shift);
+ case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
+ uint64_t fb64 = (((uint64_t) cycles) * audio->feedback.compute.fixed.sample_freq) << (16 - (audio->feedback.frame_shift - 1));
feedback = (uint32_t) (fb64 / audio->feedback.compute.fixed.mclk_freq);
- }
- break;
+ } break;
- default: return 0;
+ default:
+ return 0;
}
// For Windows: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
// The size of isochronous packets created by the device must be within the limits specified in FMT-2.0 section 2.3.1.1.
// This means that the deviation of actual packet size from nominal size must not exceed +/- one audio slot
// (audio slot = channel count samples).
- if ( feedback > audio->feedback.max_value ) feedback = audio->feedback.max_value;
- if ( feedback < audio->feedback.min_value ) feedback = audio->feedback.min_value;
+ if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
+ if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
tud_audio_n_fb_set(func_id, feedback);
return feedback;
}
+
+bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
+ TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+
+ _audiod_fct[func_id].feedback.value = feedback;
+
+ // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
+ if (usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb)) {
+ return audiod_fb_send(&_audiod_fct[func_id]);
+ }
+
+ return true;
+}
#endif
-TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count)
-{
+TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
(void) rhport;
(void) frame_count;
@@ -2137,26 +2370,22 @@ TU_ATTR_FAST_FUNC void audiod_sof_isr (uint8_t rhport, uint32_t frame_count)
// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
// Iterate over audio functions and set feedback value
- for(uint8_t i=0; i < CFG_TUD_AUDIO; i++)
- {
- audiod_function_t* audio = &_audiod_fct[i];
+ for (uint8_t i = 0; i < CFG_TUD_AUDIO; i++) {
+ audiod_function_t *audio = &_audiod_fct[i];
- if (audio->ep_fb != 0)
- {
+ if (audio->ep_fb != 0) {
// HS shift need to be adjusted since SOF event is generated for frame only
uint8_t const hs_adjust = (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0;
uint32_t const interval = 1UL << (audio->feedback.frame_shift - hs_adjust);
- if ( 0 == (frame_count & (interval-1)) )
- {
- if(tud_audio_feedback_interval_isr) tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift);
+ if (0 == (frame_count & (interval - 1))) {
+ tud_audio_feedback_interval_isr(i, frame_count, audio->feedback.frame_shift);
}
}
}
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
}
-bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len)
-{
+bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const *p_request, void *data, uint16_t len) {
// Handles only sending of data not receiving
if (p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) return false;
@@ -2165,71 +2394,73 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
uint8_t itf = TU_U16_LOW(p_request->wIndex);
// Conduct checks which depend on the recipient
- switch (p_request->bmRequestType_bit.recipient)
- {
- case TUSB_REQ_RCPT_INTERFACE:
- {
+ switch (p_request->bmRequestType_bit.recipient) {
+ case TUSB_REQ_RCPT_INTERFACE: {
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Verify if entity is present
- if (entityID != 0)
- {
+ if (entityID != 0) {
// Find index of audio driver structure and verify entity really exists
TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id));
- }
- else
- {
+ } else {
// Find index of audio driver structure and verify interface really exists
TU_VERIFY(audiod_verify_itf_exists(itf, &func_id));
}
- }
- break;
+ } break;
- case TUSB_REQ_RCPT_ENDPOINT:
- {
+ case TUSB_REQ_RCPT_ENDPOINT: {
uint8_t ep = TU_U16_LOW(p_request->wIndex);
// Find index of audio driver structure and verify EP really exists
TU_VERIFY(audiod_verify_ep_exists(ep, &func_id));
- }
- break;
+ } break;
// Unknown/Unsupported recipient
- default: TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient); TU_BREAKPOINT(); return false;
+ default:
+ TU_LOG2(" Unsupported recipient: %d\r\n", p_request->bmRequestType_bit.recipient);
+ TU_BREAKPOINT();
+ return false;
}
// Crop length
if (len > _audiod_fct[func_id].ctrl_buf_sz) len = _audiod_fct[func_id].ctrl_buf_sz;
// Copy into buffer
- memcpy((void *)_audiod_fct[func_id].ctrl_buf, data, (size_t)len);
+ TU_VERIFY(0 == tu_memcpy_s(_audiod_fct[func_id].ctrl_buf, _audiod_fct[func_id].ctrl_buf_sz, data, (size_t) len));
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+ // Find data for sampling_frequency_control
+ if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) {
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) {
+ _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf);
+ }
+ }
+#endif
// Schedule transmit
- return tud_control_xfer(rhport, p_request, (void*)_audiod_fct[func_id].ctrl_buf, len);
+ 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)
- {
+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;
+ p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
uint8_t tmp = 0;
- while (p_desc < p_desc_end)
- {
+ // 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)
- {
+ 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;
@@ -2246,14 +2477,11 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio
// 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)
-{
+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))
- {
+ 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;
}
@@ -2263,22 +2491,19 @@ static bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id,
}
// 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)
-{
+static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id) {
uint8_t i;
- for (i = 0; i < CFG_TUD_AUDIO; i++)
- {
+ for (i = 0; i < CFG_TUD_AUDIO; i++) {
// Look for the correct driver by checking if the unique standard AC interface number fits
- if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
- {
+ if (_audiod_fct[i].p_desc && ((tusb_desc_interface_t const *) _audiod_fct[i].p_desc)->bInterfaceNumber == itf) {
// Get pointers after class specific AC descriptors and end of AC descriptors - entities are defined in between
- uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc); // Points to CS AC descriptor
- uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
- p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
+ uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);// Points to CS AC descriptor
+ uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength + p_desc;
+ p_desc = tu_desc_next(p_desc);// Get past CS AC descriptor
- while (p_desc < p_desc_end)
- {
- if (p_desc[3] == entityID) // Entity IDs are always at offset 3
+ // 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
{
*func_id = i;
return true;
@@ -2290,21 +2515,16 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
return false;
}
-static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
-{
+static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id) {
uint8_t i;
- for (i = 0; i < CFG_TUD_AUDIO; i++)
- {
- if (_audiod_fct[i].p_desc)
- {
+ for (i = 0; i < CFG_TUD_AUDIO; i++) {
+ if (_audiod_fct[i].p_desc) {
// Get pointer at beginning and end
uint8_t const *p_desc = _audiod_fct[i].p_desc;
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
-
- while (p_desc < p_desc_end)
- {
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
- {
+ // 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) {
*func_id = i;
return true;
}
@@ -2315,24 +2535,20 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
return false;
}
-static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
-{
+static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id) {
uint8_t i;
- for (i = 0; i < CFG_TUD_AUDIO; i++)
- {
- if (_audiod_fct[i].p_desc)
- {
+ for (i = 0; i < CFG_TUD_AUDIO; i++) {
+ if (_audiod_fct[i].p_desc) {
// Get pointer at end
uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length;
// Advance past AC descriptors - EP we look for are streaming EPs
uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
- p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
+ p_desc += ((audio_desc_cs_ac_interface_t const *) p_desc)->wTotalLength;
- while (p_desc < p_desc_end)
- {
- if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
- {
+ // 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_ENDPOINT && ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress == ep) {
*func_id = i;
return true;
}
@@ -2343,85 +2559,77 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
return false;
}
-#if CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_ENABLE_DECODING
+#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
// p_desc points to the AS interface of alternate setting zero
// itf is the interface number of the corresponding interface - we check if the interface belongs to EP in or EP out to see if it is a TX or RX parameter
// Currently, only AS interfaces with an EP (in or out) are supposed to be parsed for!
-static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const * p_desc, uint8_t const * p_desc_end, uint8_t const as_itf)
-{
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) return; // Abort, this interface has no EP, this driver does not support this currently
-#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf) {
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) return;// Abort, this interface has no EP, this driver does not support this currently
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
if (as_itf != audio->ep_in_as_intf_num) return;
-#endif
-#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ #endif
+ #if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
if (as_itf != audio->ep_out_as_intf_num) return;
-#endif
-
- p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor of current alternate interface descriptor
+ #endif
- while (p_desc < p_desc_end)
- {
+ p_desc = tu_desc_next(p_desc);// Exclude standard AS interface descriptor of current alternate interface descriptor
+ // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+ while (p_desc_end - p_desc > 0) {
// Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
// Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format type and format and also to get number of physical channels
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL)
- {
-#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (as_itf == audio->ep_in_as_intf_num)
- {
- audio->n_channels_tx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
- audio->format_type_tx = (audio_format_type_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType);
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_AS_GENERAL) {
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num) {
+ audio->n_channels_tx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
+ audio->format_type_tx = (audio_format_type_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType);
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
- audio->format_type_I_tx = (audio_data_format_type_I_t)(((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats);
-#endif
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+ audio->format_type_I_tx = (audio_data_format_type_I_t) (((audio_desc_cs_as_interface_t const *) p_desc)->bmFormats);
+ #endif
}
-#endif
+ #endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf == audio->ep_out_as_intf_num)
- {
- audio->n_channels_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bNrChannels;
- audio->format_type_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bFormatType;
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- audio->format_type_I_rx = ((audio_desc_cs_as_interface_t const * )p_desc)->bmFormats;
-#endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ if (as_itf == audio->ep_out_as_intf_num) {
+ audio->n_channels_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bNrChannels;
+ audio->format_type_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bFormatType;
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ audio->format_type_I_rx = ((audio_desc_cs_as_interface_t const *) p_desc)->bmFormats;
+ #endif
}
-#endif
+ #endif
}
// Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
-#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const * )p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I)
- {
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break; // Abort loop, this interface has no EP, this driver does not support this currently
-#endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
+ #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+ if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && tu_desc_subtype(p_desc) == AUDIO_CS_AS_INTERFACE_FORMAT_TYPE && ((audio_desc_type_I_format_t const *) p_desc)->bFormatType == AUDIO_FORMAT_TYPE_I) {
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ if (as_itf != audio->ep_in_as_intf_num && as_itf != audio->ep_out_as_intf_num) break;// Abort loop, this interface has no EP, this driver does not support this currently
+ #endif
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_EP_OUT
if (as_itf != audio->ep_in_as_intf_num) break;
-#endif
-#if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
+ #endif
+ #if !CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_EP_OUT
if (as_itf != audio->ep_out_as_intf_num) break;
-#endif
+ #endif
-#if CFG_TUD_AUDIO_ENABLE_EP_IN
- if (as_itf == audio->ep_in_as_intf_num)
- {
- audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ #if CFG_TUD_AUDIO_ENABLE_EP_IN
+ if (as_itf == audio->ep_in_as_intf_num) {
+ audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
}
-#endif
+ #endif
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
- if (as_itf == audio->ep_out_as_intf_num)
- {
- audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+ #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
+ if (as_itf == audio->ep_out_as_intf_num) {
+ audio->n_bytes_per_sample_rx = ((audio_desc_type_I_format_t const *) p_desc)->bSubslotSize;
}
-#endif
+ #endif
}
-#endif
+ #endif
// Other format types are not supported yet
@@ -2430,50 +2638,87 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
}
#endif
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
-bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback)
-{
- TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
+static bool audiod_calc_tx_packet_sz(audiod_function_t *audio) {
+ TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
+ TU_VERIFY(audio->n_channels_tx);
+ TU_VERIFY(audio->n_bytes_per_sample_tx);
+ TU_VERIFY(audio->interval_tx);
+ TU_VERIFY(audio->sample_rate_tx);
- // Format the feedback value
-#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
- if ( TUSB_SPEED_FULL == tud_speed_get() )
- {
- uint8_t * fb = (uint8_t *) &_audiod_fct[func_id].feedback.value;
+ const uint8_t interval = (tud_speed_get() == TUSB_SPEED_FULL) ? audio->interval_tx : 1 << (audio->interval_tx - 1);
- // For FS format is 10.14
- *(fb++) = (feedback >> 2) & 0xFF;
- *(fb++) = (feedback >> 10) & 0xFF;
- *(fb++) = (feedback >> 18) & 0xFF;
- // 4th byte is needed to work correctly with MS Windows
- *fb = 0;
- }else
-#else
- {
- // Send value as-is, caller will choose the appropriate format
- _audiod_fct[func_id].feedback.value = feedback;
- }
-#endif
+ const uint16_t sample_normimal = (uint16_t) (audio->sample_rate_tx * interval / ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
+ const uint16_t sample_reminder = (uint16_t) (audio->sample_rate_tx * interval % ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
- // Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
- if (!usbd_edpt_busy(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb))
- {
- return audiod_fb_send(_audiod_fct[func_id].rhport, &_audiod_fct[func_id]);
+ const uint16_t packet_sz_tx_min = (uint16_t) ((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+ const uint16_t packet_sz_tx_norm = (uint16_t) (sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+ const uint16_t packet_sz_tx_max = (uint16_t) ((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+
+ // Endpoint size must larger than packet size
+ TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
+
+ // Frmt20.pdf 2.3.1.1 USB Packets
+ if (sample_reminder) {
+ // All virtual frame packets must either contain INT(nav) audio slots (small VFP) or INT(nav)+1 (large VFP) audio slots
+ audio->packet_sz_tx[0] = packet_sz_tx_norm;
+ audio->packet_sz_tx[1] = packet_sz_tx_norm;
+ audio->packet_sz_tx[2] = packet_sz_tx_max;
+ } else {
+ // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small VFP), INT(nav)
+ // (medium VFP) and INT(nav)+1 (large VFP).
+ audio->packet_sz_tx[0] = packet_sz_tx_min;
+ audio->packet_sz_tx[1] = packet_sz_tx_norm;
+ audio->packet_sz_tx[2] = packet_sz_tx_max;
}
return true;
}
+
+static uint16_t audiod_tx_packet_size(const uint16_t *norminal_size, uint16_t data_count, uint16_t fifo_depth, uint16_t max_depth) {
+ // Flow control need a FIFO size of at least 4*Navg
+ if (norminal_size[1] && norminal_size[1] <= fifo_depth * 4) {
+ // Use blackout to prioritize normal size packet
+ static int ctrl_blackout = 0;
+ uint16_t packet_size;
+ uint16_t slot_size = norminal_size[2] - norminal_size[1];
+ if (data_count < norminal_size[0]) {
+ // If you get here frequently, then your I2S clock deviation is too big !
+ packet_size = 0;
+ } else if (data_count < fifo_depth / 2 - slot_size && !ctrl_blackout) {
+ packet_size = norminal_size[0];
+ ctrl_blackout = 10;
+ } else if (data_count > fifo_depth / 2 + slot_size && !ctrl_blackout) {
+ packet_size = norminal_size[2];
+ if (norminal_size[0] == norminal_size[1]) {
+ // nav > INT(nav), eg. 44.1k, 88.2k
+ ctrl_blackout = 0;
+ } else {
+ // nav = INT(nav), eg. 48k, 96k
+ ctrl_blackout = 10;
+ }
+ } else {
+ packet_size = norminal_size[1];
+ if (ctrl_blackout) {
+ ctrl_blackout--;
+ }
+ }
+ // Normally this cap is not necessary
+ return tu_min16(packet_size, max_depth);
+ } else {
+ return tu_min16(data_count, max_depth);
+ }
+}
+
#endif
// No security checks here - internal function only which should always succeed
-uint8_t audiod_get_audio_fct_idx(audiod_function_t * audio)
-{
- for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO; cnt++)
- {
+static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio) {
+ for (uint8_t cnt = 0; cnt < CFG_TUD_AUDIO; cnt++) {
if (&_audiod_fct[cnt] == audio) return cnt;
}
return 0;
}
-#endif //CFG_TUD_ENABLED && CFG_TUD_AUDIO
+#endif // (CFG_TUD_ENABLED && CFG_TUD_AUDIO)
diff --git a/src/class/audio/audio_device.h b/src/class/audio/audio_device.h
index 0ef100fa4..0a7bff212 100644
--- a/src/class/audio/audio_device.h
+++ b/src/class/audio/audio_device.h
@@ -1,8 +1,9 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ha Thach (tinyusb.org)
* Copyright (c) 2020 Reinhard Panhuber
+ * Copyright (c) 2023 HiFiPhile
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -181,24 +182,29 @@
#endif
#endif
+// (For TYPE-I format only) Flow control is necessary to allow IN ep send correct amount of data, unless it's a virtual device where data is perfectly synchronized to USB clock.
+#ifndef CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
+#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL 1
+#endif
+
// Enable/disable feedback EP (required for asynchronous RX applications)
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
#endif
// Enable/disable conversion from 16.16 to 10.14 format on full-speed devices. See tud_audio_n_fb_set().
+// Can be override by tud_audio_feedback_format_correction_cb()
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION 0 // 0 or 1
#endif
-// Audio interrupt control EP size - disabled if 0
-#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
+// Enable/disable interrupt EP (required for notifying host of control changes)
+#ifndef CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+#define CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP 0 // Feedback - 0 or 1
#endif
-#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
-#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
-#endif
+// Audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
+#define CFG_TUD_AUDIO_INTERRUPT_EP_SZ 6
// Use software encoding/decoding
@@ -241,7 +247,8 @@
// Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size
// The actual coding parameters of active AS alternate interface is parsed from the descriptors
-// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sampe_sz) * sampe_sz)!
+// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple
+// of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sample_sz) * sample_sz)!
// This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!!
// For PCM encoding/decoding
@@ -388,10 +395,11 @@ uint16_t tud_audio_n_write_support_ff (uint8_t func_id, uint8_t ff_i
tu_fifo_t* tud_audio_n_get_tx_support_ff (uint8_t func_id, uint8_t ff_idx);
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-uint16_t tud_audio_int_ctr_n_write (uint8_t func_id, uint8_t const* buffer, uint16_t len);
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+bool tud_audio_int_n_write (uint8_t func_id, const audio_interrupt_data_t * data);
#endif
+
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
@@ -431,8 +439,8 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff (uint8_t ff_idx);
// INT CTR API
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+static inline bool tud_audio_int_write (const audio_interrupt_data_t * data);
#endif
// Buffer control EP data and schedule a transmit
@@ -444,63 +452,80 @@ static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buff
bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_request_t const * p_request, void* data, uint16_t len);
//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
+// Application Callback API
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_EP_IN
-TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
-TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
+bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
-TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
+bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id);
+void tud_audio_fb_done_cb(uint8_t func_id);
-// determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer. Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 8 frames), i.e. a larger delay is introduced.
-
-// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter) - see tud_audio_set_fb_params(). Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller (e.g. 2 frames) and thus a smaller delay is possible, disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. This option is a great starting point to try the SOF ISR option but depending on your hardware setup (performance of the CPU) it might not work. If so, figure out why and use the next option. (The most critical point is the reading of the cycle counter value of f_m. It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
+// Note about feedback calculation
+//
+// Option 1 - AUDIO_FEEDBACK_METHOD_FIFO_COUNT
+// Feedback value is calculated within the audio driver by regulating the FIFO level to half fill.
+// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load, well tested
+// (Windows, Linux, OSX) with a reliable result so far.
+// Disadvantage: A FIFO of minimal 4 frames is needed to compensate for jitter, an average delay of 2 frames is introduced.
+//
+// Option 2 - AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED / AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
+// Feedback value is calculated within the audio driver by use of SOF interrupt. The driver needs information about the master clock f_m from
+// which the audio sample frequency f_s is derived, f_s itself, and the cycle count of f_m at time of the SOF interrupt (e.g. by use of a hardware counter).
+// See tud_audio_set_fb_params() and tud_audio_feedback_update()
+// Advantage: Reduced jitter in the feedback value computation, hence, the receive FIFO can be smaller and thus a smaller delay is possible.
+// Disadvantage: higher CPU load due to SOF ISR handling every frame i.e. 1ms or 125us. (The most critical point is the reading of the cycle counter value of f_m.
+// It is read from within the SOF ISR - see: audiod_sof() -, hence, the ISR must has a high priority such that no software dependent "random" delay i.e. jitter is introduced).
+// Long-term drift could occur since error is accumulated.
+//
+// Option 3 - manual
+// Determined by the user itself and set by use of tud_audio_n_fb_set(). The feedback value may be determined e.g. from some fill status of some FIFO buffer.
+// Advantage: No ISR interrupt is enabled, hence the CPU need not to handle an ISR every 1ms or 125us and thus less CPU load.
+// Disadvantage: typically a larger FIFO is needed to compensate for jitter (e.g. 6 frames), i.e. a larger delay is introduced.
-// Feedback value is determined by the user by use of SOF interrupt. The user may use tud_audio_sof_isr() which is called every SOF (of course only invoked when an alternate interface other than zero was set). The number of frames used to determine the feedback value for the currently active alternate setting can be get by tud_audio_get_fb_n_frames(). The feedback value must be set by use of tud_audio_n_fb_set().
// This function is used to provide data rate feedback from an asynchronous sink. Feedback value will be sent at FB endpoint interval till it's changed.
//
// The feedback format is specified to be 16.16 for HS and 10.14 for FS devices (see Universal Serial Bus Specification Revision 2.0 5.12.4.2). By default,
-// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set, then tinyusb
-// expects 16.16 format and handles the conversion to 10.14 on FS.
+// the choice of format is left to the caller and feedback argument is sent as-is. If CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION is set or tud_audio_feedback_format_correction_cb()
+// return true, then tinyusb expects 16.16 format and handles the conversion to 10.14 on FS.
+//
+// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and it seems the
+// driver can work with either format.
//
-// Note that due to a bug in its USB Audio 2.0 driver, Windows currently requires 16.16 format for _all_ USB 2.0 devices. On Linux and macOS it seems the
-// driver can work with either format. So a good compromise is to keep format correction disabled and stick to 16.16 format.
-
// Feedback value can be determined from within the SOF ISR of the audio driver. This should reduce jitter. If the feature is used, the user can not set the feedback value.
-
+//
// Determine feedback value - The feedback method is described in 5.12.4.2 of the USB 2.0 spec
// Boiled down, the feedback value Ff = n_samples / (micro)frame.
-// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13 for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
+// Since an accuracy of less than 1 Sample / second is desired, at least n_frames = ceil(2^K * f_s / f_m) frames need to be measured, where K = 10 for full speed and K = 13
+// for high speed, f_s is the sampling frequency e.g. 48 kHz and f_m is the cpu clock frequency e.g. 100 MHz (or any other master clock whose clock count is available and locked to f_s)
// The update interval in the (4.10.2.1) Feedback Endpoint Descriptor must be less or equal to 2^(K - P), where P = min( ceil(log2(f_m / f_s)), K)
// feedback = n_cycles / n_frames * f_s / f_m in 16.16 format, where n_cycles are the number of main clock cycles within fb_n_frames
-
bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback);
-static inline bool tud_audio_fb_set(uint32_t feedback);
-// Update feedback value with passed cycles since last time this update function is called.
+// Update feedback value with passed MCLK cycles since last time this update function is called.
// Typically called within tud_audio_sof_isr(). Required tud_audio_feedback_params_cb() is implemented
// This function will also call tud_audio_feedback_set()
// return feedback value in 16.16 for reference (0 for error)
+// Example :
+// binterval=3 (4ms); FS = 48kHz; MCLK = 12.288MHz
+// In 4 SOF MCLK counted 49152 cycles
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles);
enum {
AUDIO_FEEDBACK_METHOD_DISABLED,
AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
- AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2,
-
- // impelemnt later
- // AUDIO_FEEDBACK_METHOD_FIFO_COUNT
+ AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only
+ AUDIO_FEEDBACK_METHOD_FIFO_COUNT
};
typedef struct {
@@ -512,52 +537,50 @@ typedef struct {
uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to which sample clock is based on
}frequency;
-#if 0 // implement later
- struct {
- uint32_t threshold_bytes; // minimum number of bytes received to be considered as filled/ready
- }fifo_count;
-#endif
};
}audio_feedback_params_t;
// Invoked when needed to set feedback parameters
-TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
+void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param);
// Callback in ISR context, invoked periodically according to feedback endpoint bInterval.
// Could be used to compute and update feedback value, should be placed in RAM if possible
// frame_number : current SOF count
// interval_shift: number of bit shift i.e log2(interval) from Feedback endpoint descriptor
-TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
+TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t frame_number, uint8_t interval_shift);
+// (Full-Speed only) Callback to set feedback format correction is applied or not,
+// default to CFG_TUD_AUDIO_ENABLE_FEEDBACK_FORMAT_CORRECTION if not implemented.
+bool tud_audio_feedback_format_correction_cb(uint8_t func_id);
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+void tud_audio_int_done_cb(uint8_t rhport);
#endif
// Invoked when audio set interface request received
-TU_ATTR_WEAK bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
// Invoked when audio set interface request received which closes an EP
-TU_ATTR_WEAK bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request);
// Invoked when audio class specific set request received for an EP
-TU_ATTR_WEAK bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
// Invoked when audio class specific set request received for an interface
-TU_ATTR_WEAK bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
// Invoked when audio class specific set request received for an entity
-TU_ATTR_WEAK bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
+bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff);
// Invoked when audio class specific get request received for an EP
-TU_ATTR_WEAK bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request);
// Invoked when audio class specific get request received for an interface
-TU_ATTR_WEAK bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request);
// Invoked when audio class specific get request received for an entity
-TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
+bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request);
//--------------------------------------------------------------------+
// Inline Functions
@@ -663,10 +686,10 @@ static inline tu_fifo_t* tud_audio_get_tx_support_ff(uint8_t ff_idx)
#endif
-#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
-static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+static inline bool tud_audio_int_write(const audio_interrupt_data_t * data)
{
- return tud_audio_int_ctr_n_write(0, buffer, len);
+ return tud_audio_int_n_write(0, data);
}
#endif
@@ -683,6 +706,7 @@ static inline bool tud_audio_fb_set(uint32_t feedback)
// Internal Class Driver API
//--------------------------------------------------------------------+
void audiod_init (void);
+bool audiod_deinit (void);
void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/bth/bth_device.c b/src/class/bth/bth_device.c
index f96bb3552..45cbf2d98 100755
--- a/src/class/bth/bth_device.c
+++ b/src/class/bth/bth_device.c
@@ -37,25 +37,29 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_ev;
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_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
-
+ // Previous amount of bytes sent when issuing ZLP
+ uint32_t prev_xferred_bytes;
} btd_interface_t;
+typedef struct {
+ TUD_EPBUF_DEF(epout_buf, CFG_TUD_BTH_DATA_EPSIZE);
+ TUD_EPBUF_TYPE_DEF(bt_hci_cmd_t, hci_cmd);
+} btd_epbuf_t;
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf;
+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)
{
@@ -91,11 +95,14 @@ bool tud_bt_acl_data_send(void *event, uint16_t event_len)
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void btd_init(void)
-{
+void btd_init(void) {
tu_memclr(&_btd_itf, sizeof(_btd_itf));
}
+bool btd_deinit(void) {
+ return true;
+}
+
void btd_reset(uint8_t rhport)
{
(void)rhport;
@@ -124,14 +131,28 @@ 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);
+
// Open endpoint pair
- TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
- &_btd_itf.ep_acl_in), 0);
+ 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);
- itf_desc = (tusb_desc_interface_t const *)tu_desc_next(tu_desc_next(tu_desc_next(desc_ep)));
+ // Save acl in endpoint max packet size
+ tusb_desc_endpoint_t const *desc_ep_acl_in = desc_ep;
+ for (size_t p = 0; p < 2; p++) {
+ if (tu_edpt_dir(desc_ep_acl_in->bEndpointAddress) == TUSB_DIR_IN) {
+ _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);
+ }
+
+ 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_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
+ TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_epbuf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
drv_len = hci_itf_size;
@@ -204,7 +225,9 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
{
// HCI command packet addressing for single function Primary Controllers
- TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
+ // 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)
{
@@ -220,30 +243,30 @@ bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t c
}
else return false;
- return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, sizeof(_btd_itf.hci_cmd));
+ return tud_control_xfer(rhport, request, &_btd_epbuf.hci_cmd, sizeof(bt_hci_cmd_t));
}
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_itf.hci_cmd, tu_min16(request->wLength, sizeof(_btd_itf.hci_cmd)));
+ if (tud_bt_hci_cmd_cb) {
+ tud_bt_hci_cmd_cb(&_btd_epbuf.hci_cmd, tu_min16(request->wLength, sizeof(bt_hci_cmd_t)));
+ }
}
return true;
}
-bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void)result;
-
+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_itf.epout_buf, xferred_bytes);
+ if (tud_bt_acl_data_received_cb) 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_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
+ 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)
{
@@ -251,7 +274,20 @@ bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t
}
else if (ep_addr == _btd_itf.ep_acl_in)
{
- if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes);
+ if ((result == XFER_RESULT_SUCCESS) && (xferred_bytes > 0) &&
+ ((xferred_bytes & (_btd_itf.ep_acl_in_pkt_sz - 1)) == 0)) {
+ // Save number of transferred bytes
+ _btd_itf.prev_xferred_bytes = xferred_bytes;
+
+ // Send zero-length packet
+ tud_bt_acl_data_send(NULL, 0);
+ } else if (tud_bt_acl_data_sent_cb) {
+ 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);
+ }
}
return true;
diff --git a/src/class/bth/bth_device.h b/src/class/bth/bth_device.h
index 1b90d0915..4f6350839 100755
--- a/src/class/bth/bth_device.h
+++ b/src/class/bth/bth_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jerzy Kasenberg
@@ -36,10 +36,17 @@
#ifndef CFG_TUD_BTH_EVENT_EPSIZE
#define CFG_TUD_BTH_EVENT_EPSIZE 16
#endif
+
#ifndef CFG_TUD_BTH_DATA_EPSIZE
#define CFG_TUD_BTH_DATA_EPSIZE 64
#endif
+// Allow BTH class to work in historically compatibility mode where the bRequest is always 0xe0.
+// See Bluetooth Core v5.3, Vol. 4, Part B, Section 2.2
+#ifndef CFG_TUD_BTH_HISTORICAL_COMPATIBLE
+#define CFG_TUD_BTH_HISTORICAL_COMPATIBLE 0
+#endif
+
typedef struct TU_ATTR_PACKED
{
uint16_t op_code;
@@ -97,6 +104,7 @@ bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
// Internal Class Driver API
//--------------------------------------------------------------------+
void btd_init (void);
+bool btd_deinit (void);
void btd_reset (uint8_t rhport);
uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool btd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index e345139ea..5cbd658fe 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -41,16 +41,6 @@
/** \defgroup ClassDriver_CDC_Common Common Definitions
* @{ */
-// TODO remove
-/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In)
-typedef enum
-{
- CDC_PIPE_NOTIFICATION , ///< Notification pipe
- CDC_PIPE_DATA_IN , ///< Data in pipe
- CDC_PIPE_DATA_OUT , ///< Data out pipe
- CDC_PIPE_ERROR , ///< Invalid Pipe ID
-}cdc_pipeid_t;
-
//--------------------------------------------------------------------+
// CDC Communication Interface Class
//--------------------------------------------------------------------+
@@ -146,8 +136,7 @@ typedef enum{
//--------------------------------------------------------------------+
/// Communication Interface Management Element Request Codes
-typedef enum
-{
+typedef enum {
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
@@ -190,15 +179,38 @@ typedef enum
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
-}cdc_management_request_t;
+} cdc_management_request_t;
+
+typedef enum {
+ CDC_CONTROL_LINE_STATE_DTR = 0x01,
+ CDC_CONTROL_LINE_STATE_RTS = 0x02,
+} cdc_control_line_state_t;
+
+typedef enum {
+ CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
+ CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
+ CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
+} cdc_line_coding_stopbits_t;
+
+// TODO Backward compatible for typos. Maybe removed in the future release
+#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
+#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
+#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
+
+typedef enum {
+ CDC_LINE_CODING_PARITY_NONE = 0,
+ CDC_LINE_CODING_PARITY_ODD = 1,
+ CDC_LINE_CODING_PARITY_EVEN = 2,
+ CDC_LINE_CODING_PARITY_MARK = 3,
+ CDC_LINE_CODING_PARITY_SPACE = 4,
+} cdc_line_coding_parity_t;
//--------------------------------------------------------------------+
-// Management Elemenent Notification (Notification Endpoint)
+// Management Element Notification (Notification Endpoint)
//--------------------------------------------------------------------+
/// 6.3 Notification Codes
-typedef enum
-{
+typedef enum {
CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
@@ -365,7 +377,9 @@ typedef struct TU_ATTR_PACKED
uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns.
uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line.
uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification
- uint32_t TU_RESERVED : 26;
+ uint32_t TU_RESERVED0 : 2;
+ uint32_t TU_RESERVED1 : 16;
+ uint32_t TU_RESERVED2 : 8;
} bmCapabilities;
}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
@@ -390,9 +404,10 @@ TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
typedef struct TU_ATTR_PACKED
{
- uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR.
- uint16_t half_duplex_carrier_control : 1;
- uint16_t : 14;
+ uint16_t dtr : 1;
+ uint16_t rts : 1;
+ uint16_t : 6;
+ uint16_t : 8;
} cdc_line_control_state_t;
TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index fab6f0035..efb672201 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -33,16 +33,19 @@
#include "cdc_device.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_CDC_LOG_LEVEL
+ #define CFG_TUD_CDC_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_CDC_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum
-{
- BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
-};
+#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_notif;
uint8_t ep_in;
@@ -52,8 +55,8 @@ typedef struct
uint8_t line_state;
/*------------- From this point, data is not cleared by bus reset -------------*/
- char wanted_char;
- cdc_line_coding_t line_coding;
+ char wanted_char;
+ TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
// FIFO
tu_fifo_t rx_ff;
@@ -62,34 +65,40 @@ typedef struct
uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
-#endif
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
-
-}cdcd_interface_t;
+ OSAL_MUTEX_DEF(rx_ff_mutex);
+ OSAL_MUTEX_DEF(tx_ff_mutex);
+} cdcd_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_CDC_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_CDC_EP_BUFSIZE);
+} cdcd_epbuf_t;
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
+
+static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
+
+static bool _prep_out_transaction(uint8_t itf) {
+ const uint8_t rhport = 0;
+ cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
+
+ // Skip if usb is not ready yet
+ TU_VERIFY(tud_ready() && p_cdc->ep_out);
-static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
-{
- uint8_t const rhport = 0;
uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
// TODO Actually we can still carry out the transfer, keeping count of received bytes
// and slowly move it to the FIFO when read().
// This pre-check reduces endpoint claiming
- TU_VERIFY(available >= sizeof(p_cdc->epout_buf));
+ TU_VERIFY(available >= CFG_TUD_CDC_EP_BUFSIZE);
// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out));
@@ -97,14 +106,11 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
// fifo can be changed before endpoint is claimed
available = tu_fifo_remaining(&p_cdc->rx_ff);
- if ( available >= sizeof(p_cdc->epout_buf) )
- {
- return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
- }else
- {
+ if (available >= CFG_TUD_CDC_EP_BUFSIZE) {
+ return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_epbuf->epout, CFG_TUD_CDC_EP_BUFSIZE);
+ } else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(rhport, p_cdc->ep_out);
-
return false;
}
}
@@ -112,97 +118,101 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_cdc_n_connected(uint8_t itf)
-{
+
+bool tud_cdc_configure_fifo(const tud_cdc_configure_fifo_t* cfg) {
+ TU_VERIFY(cfg);
+ _cdcd_fifo_cfg = (*cfg);
+ return true;
+}
+
+bool tud_cdc_n_ready(uint8_t itf) {
+ return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0;
+}
+
+bool tud_cdc_n_connected(uint8_t itf) {
// DTR (bit 0) active is considered as connected
return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
}
-uint8_t tud_cdc_n_get_line_state (uint8_t itf)
-{
+uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
return _cdcd_itf[itf].line_state;
}
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
-{
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
(*coding) = _cdcd_itf[itf].line_coding;
}
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
-{
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
_cdcd_itf[itf].wanted_char = wanted;
}
-
//--------------------------------------------------------------------+
// READ API
//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_available(uint8_t itf)
-{
+uint32_t tud_cdc_n_available(uint8_t itf) {
return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
}
-uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
-{
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) bufsize);
- _prep_out_transaction(p_cdc);
+ uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
+ _prep_out_transaction(itf);
return num_read;
}
-bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr)
-{
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
}
-void tud_cdc_n_read_flush (uint8_t itf)
-{
+void tud_cdc_n_read_flush(uint8_t itf) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
tu_fifo_clear(&p_cdc->rx_ff);
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(itf);
}
//--------------------------------------------------------------------+
// WRITE API
//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
-{
+uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) bufsize);
+ uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
// flush if queue more than packet size
- if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE )
- {
+ if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
+ #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
+ || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
+ #endif
+ ) {
tud_cdc_n_write_flush(itf);
}
return ret;
}
-uint32_t tud_cdc_n_write_flush (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_flush(uint8_t itf) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
// Skip if usb is not ready yet
- TU_VERIFY( tud_ready(), 0 );
+ TU_VERIFY(tud_ready(), 0);
// No data to send
- if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+ if (!tu_fifo_count(&p_cdc->tx_ff)) {
+ return 0;
+ }
- uint8_t const rhport = 0;
+ const uint8_t rhport = 0;
// Claim the endpoint
- TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+ TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_in), 0);
// Pull data from FIFO
- uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
+ const uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_epbuf->epin, CFG_TUD_CDC_EP_BUFSIZE);
- if ( count )
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+ if (count) {
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_epbuf->epin, count), 0);
return count;
- }else
- {
+ } else {
// Release endpoint since we don't make any transfer
// Note: data is dropped if terminal is not connected
usbd_edpt_release(rhport, p_cdc->ep_in);
@@ -210,33 +220,30 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
}
}
-uint32_t tud_cdc_n_write_available (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_available(uint8_t itf) {
return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
}
-bool tud_cdc_n_write_clear (uint8_t itf)
-{
+bool tud_cdc_n_write_clear(uint8_t itf) {
return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void cdcd_init(void)
-{
+void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
+ tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg));
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
p_cdc->wanted_char = (char) -1;
// default line coding is : stop bit = 1, parity = none, data bits = 8
- p_cdc->line_coding.bit_rate = 115200;
+ p_cdc->line_coding.bit_rate = 115200;
p_cdc->line_coding.stop_bits = 0;
- p_cdc->line_coding.parity = 0;
+ p_cdc->line_coding.parity = 0;
p_cdc->line_coding.data_bits = 8;
// Config RX fifo
@@ -247,87 +254,110 @@ void cdcd_init(void)
// In this way, the most current data is prioritized.
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
-#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
- tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
-#endif
+ #if OSAL_MUTEX_REQUIRED
+ osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
+ osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
+ TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
+
+ tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
+ tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
+ #endif
}
}
-void cdcd_reset(uint8_t rhport)
-{
+bool cdcd_deinit(void) {
+ #if OSAL_MUTEX_REQUIRED
+ for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
+ cdcd_interface_t* p_cdc = &_cdcd_itf[i];
+ osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
+ osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
+
+ if (mutex_rd) {
+ osal_mutex_delete(mutex_rd);
+ tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
+ }
+
+ if (mutex_wr) {
+ osal_mutex_delete(mutex_wr);
+ tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
+ }
+ }
+ #endif
+
+ return true;
+}
+
+void cdcd_reset(uint8_t rhport) {
(void) rhport;
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
+ for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&p_cdc->rx_ff);
- tu_fifo_clear(&p_cdc->tx_ff);
+ if (!_cdcd_fifo_cfg.rx_persistent) {
+ tu_fifo_clear(&p_cdc->rx_ff);
+ }
+ if (!_cdcd_fifo_cfg.tx_persistent) {
+ tu_fifo_clear(&p_cdc->tx_ff);
+ }
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
}
}
-uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t cdcd_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
// Only support ACM subclass
TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
// Find available interface
- cdcd_interface_t * p_cdc = NULL;
- for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
- {
- if ( _cdcd_itf[cdc_id].ep_in == 0 )
- {
- p_cdc = &_cdcd_itf[cdc_id];
+ cdcd_interface_t* p_cdc;
+ uint8_t cdc_id;
+ for (cdc_id = 0; cdc_id < CFG_TUD_CDC; cdc_id++) {
+ p_cdc = &_cdcd_itf[cdc_id];
+ if (p_cdc->ep_in == 0) {
break;
}
}
- TU_ASSERT(p_cdc, 0);
+ TU_ASSERT(cdc_id < CFG_TUD_CDC, 0);
//------------- Control Interface -------------//
p_cdc->itf_num = itf_desc->bInterfaceNumber;
uint16_t drv_len = sizeof(tusb_desc_interface_t);
- uint8_t const * p_desc = tu_desc_next( itf_desc );
+ const uint8_t* p_desc = tu_desc_next(itf_desc);
// Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
+ while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
// notification endpoint
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
- TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
- {
+ if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((const tusb_desc_interface_t*) p_desc)->bInterfaceClass)) {
// next to endpoint descriptor
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
// Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
- drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ drv_len += 2 * sizeof(tusb_desc_endpoint_t);
}
// Prepare for incoming data
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(cdc_id);
return drv_len;
}
@@ -335,51 +365,45 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// 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 cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
// Handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
+ uint8_t itf;
+ cdcd_interface_t* p_cdc;
// Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if ( p_cdc->itf_num == request->wIndex ) break;
+ for (itf = 0; itf < CFG_TUD_CDC; itf++) {
+ p_cdc = &_cdcd_itf[itf];
+ if (p_cdc->itf_num == request->wIndex) {
+ break;
+ }
}
+ TU_VERIFY(itf < CFG_TUD_CDC);
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case CDC_REQUEST_SET_LINE_CODING:
- if (stage == CONTROL_STAGE_SETUP)
- {
- TU_LOG2(" Set Line Coding\r\n");
+ if (stage == CONTROL_STAGE_SETUP) {
+ 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);
+ }
}
- else if ( stage == CONTROL_STAGE_ACK)
- {
- if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
- }
- break;
+ break;
case CDC_REQUEST_GET_LINE_CODING:
- if (stage == CONTROL_STAGE_SETUP)
- {
- TU_LOG2(" Get Line Coding\r\n");
+ if (stage == CONTROL_STAGE_SETUP) {
+ TU_LOG_DRV(" Get Line Coding\r\n");
tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
}
- break;
+ break;
case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
- }
- else if (stage == CONTROL_STAGE_ACK)
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
// CDC PSTN v1.2 section 6.3.12
// Bit 0: Indicates if DTE is present or not.
// This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
@@ -389,89 +413,89 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
bool const rts = tu_bit_test(request->wValue, 1);
p_cdc->line_state = (uint8_t) request->wValue;
-
+
// Disable fifo overwriting if DTR bit is set
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
- TU_LOG2(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
+ 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);
+ if (tud_cdc_line_state_cb) {
+ tud_cdc_line_state_cb(itf, dtr, rts);
+ }
}
- break;
+ break;
+
case CDC_REQUEST_SEND_BREAK:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
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);
+ }
}
- else if (stage == CONTROL_STAGE_ACK)
- {
- TU_LOG2(" Send Break\r\n");
- if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
- }
- break;
+ break;
- default: return false; // stall unsupported request
+ default:
+ return false; // stall unsupported request
}
return true;
}
-bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
uint8_t itf;
cdcd_interface_t* p_cdc;
// Identify which interface to use
- for (itf = 0; itf < CFG_TUD_CDC; itf++)
- {
+ for (itf = 0; itf < CFG_TUD_CDC; itf++) {
p_cdc = &_cdcd_itf[itf];
- if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
+ if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) {
+ break;
+ }
}
TU_ASSERT(itf < CFG_TUD_CDC);
+ cdcd_epbuf_t* p_epbuf = &_cdcd_epbuf[itf];
// Received new data
- if ( ep_addr == p_cdc->ep_out )
- {
- tu_fifo_write_n(&p_cdc->rx_ff, &p_cdc->epout_buf, (uint16_t) xferred_bytes);
-
+ if (ep_addr == p_cdc->ep_out) {
+ 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) )
- {
- for ( uint32_t i = 0; i < xferred_bytes; i++ )
- {
- if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) )
- {
+ if (tud_cdc_rx_wanted_cb && (((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);
}
}
}
-
+
// invoke receive callback (if there is still data)
- if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
-
+ if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) {
+ tud_cdc_rx_cb(itf);
+ }
+
// prepare for OUT transaction
- _prep_out_transaction(p_cdc);
+ _prep_out_transaction(itf);
}
-
+
// Data sent to host, we continue to fetch from tx fifo to send.
// Note: This will cause incorrect baudrate set in line coding.
// Though maybe the baudrate is not really important !!!
- if ( ep_addr == p_cdc->ep_in )
- {
+ 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);
+ if (tud_cdc_tx_complete_cb) {
+ tud_cdc_tx_complete_cb(itf);
+ }
- if ( 0 == tud_cdc_n_write_flush(itf) )
- {
+ if (0 == tud_cdc_n_write_flush(itf)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP Packet size and not zero
- if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
- {
- if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
- {
+ if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
+ if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
}
}
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index fbc7162a3..2d5ba2575 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,10 +24,9 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_CDC_DEVICE_H_
-#define _TUSB_CDC_DEVICE_H_
+#ifndef TUSB_CDC_DEVICE_H_
+#define TUSB_CDC_DEVICE_H_
-#include "common/tusb_common.h"
#include "cdc.h"
//--------------------------------------------------------------------+
@@ -46,208 +45,175 @@
extern "C" {
#endif
-/** \addtogroup CDC_Serial Serial
- * @{
- * \defgroup CDC_Serial_Device Device
- * @{ */
+//--------------------------------------------------------------------+
+// Driver Configuration
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect
+ uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect
+} tud_cdc_configure_fifo_t;
+
+// Configure CDC FIFOs behavior
+bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
//--------------------------------------------------------------------+
-// Application API (Multiple Ports)
-// CFG_TUD_CDC > 1
+// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
//--------------------------------------------------------------------+
+// Check if interface is ready
+bool tud_cdc_n_ready(uint8_t itf);
+
// Check if terminal is connected to this port
-bool tud_cdc_n_connected (uint8_t itf);
+bool tud_cdc_n_connected(uint8_t itf);
// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
-uint8_t tud_cdc_n_get_line_state (uint8_t itf);
+uint8_t tud_cdc_n_get_line_state(uint8_t itf);
// Get current line encoding: bit rate, stop bits parity etc ..
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding);
// Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted);
// Get the number of bytes available for reading
-uint32_t tud_cdc_n_available (uint8_t itf);
+uint32_t tud_cdc_n_available(uint8_t itf);
// Read received bytes
-uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize);
// Read a byte, return -1 if there is none
-static inline
-int32_t tud_cdc_n_read_char (uint8_t itf);
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_n_read_char(uint8_t itf) {
+ uint8_t ch;
+ return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
+}
// Clear the received FIFO
-void tud_cdc_n_read_flush (uint8_t itf);
+void tud_cdc_n_read_flush(uint8_t itf);
-// Get a byte from FIFO at the specified position without removing it
-bool tud_cdc_n_peek (uint8_t itf, uint8_t* ui8);
+// Get a byte from FIFO without removing it
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* ui8);
// Write bytes to TX FIFO, data may remain in the FIFO for a while
-uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize);
// Write a byte
-static inline
-uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) {
+ return tud_cdc_n_write(itf, &ch, 1);
+}
// Write a null-terminated string
-static inline
-uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_str(uint8_t itf, char const* str) {
+ return tud_cdc_n_write(itf, str, strlen(str));
+}
// Force sending data if possible, return number of forced bytes
-uint32_t tud_cdc_n_write_flush (uint8_t itf);
+uint32_t tud_cdc_n_write_flush(uint8_t itf);
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
-uint32_t tud_cdc_n_write_available (uint8_t itf);
+uint32_t tud_cdc_n_write_available(uint8_t itf);
// Clear the transmit FIFO
-bool tud_cdc_n_write_clear (uint8_t itf);
+bool tud_cdc_n_write_clear(uint8_t itf);
//--------------------------------------------------------------------+
// Application API (Single Port)
//--------------------------------------------------------------------+
-static inline bool tud_cdc_connected (void);
-static inline uint8_t tud_cdc_get_line_state (void);
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding);
-static inline void tud_cdc_set_wanted_char (char wanted);
-
-static inline uint32_t tud_cdc_available (void);
-static inline int32_t tud_cdc_read_char (void);
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize);
-static inline void tud_cdc_read_flush (void);
-static inline bool tud_cdc_peek (uint8_t* ui8);
-static inline uint32_t tud_cdc_write_char (char ch);
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_cdc_write_str (char const* str);
-static inline uint32_t tud_cdc_write_flush (void);
-static inline uint32_t tud_cdc_write_available (void);
-static inline bool tud_cdc_write_clear (void);
-
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
-
-// Invoked when received new data
-TU_ATTR_WEAK 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);
-
-// Invoked when space becomes available in TX buffer
-TU_ATTR_WEAK void tud_cdc_tx_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);
-
-// 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);
-
-// Invoked when received send break
-TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline int32_t tud_cdc_n_read_char (uint8_t itf)
-{
- uint8_t ch;
- return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
-}
-
-static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch)
-{
- return tud_cdc_n_write(itf, &ch, 1);
-}
-
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str)
-{
- return tud_cdc_n_write(itf, str, strlen(str));
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_ready(void) {
+ return tud_cdc_n_ready(0);
}
-static inline bool tud_cdc_connected (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_connected(void) {
return tud_cdc_n_connected(0);
}
-static inline uint8_t tud_cdc_get_line_state (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) {
return tud_cdc_n_get_line_state(0);
}
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) {
tud_cdc_n_get_line_coding(0, coding);
}
-static inline void tud_cdc_set_wanted_char (char wanted)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_wanted_char(char wanted) {
tud_cdc_n_set_wanted_char(0, wanted);
}
-static inline uint32_t tud_cdc_available (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_available(void) {
return tud_cdc_n_available(0);
}
-static inline int32_t tud_cdc_read_char (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_read_char(void) {
return tud_cdc_n_read_char(0);
}
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_read(void* buffer, uint32_t bufsize) {
return tud_cdc_n_read(0, buffer, bufsize);
}
-static inline void tud_cdc_read_flush (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_read_flush(void) {
tud_cdc_n_read_flush(0);
}
-static inline bool tud_cdc_peek (uint8_t* ui8)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_peek(uint8_t* ui8) {
return tud_cdc_n_peek(0, ui8);
}
-static inline uint32_t tud_cdc_write_char (char ch)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_char(char ch) {
return tud_cdc_n_write_char(0, ch);
}
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write(void const* buffer, uint32_t bufsize) {
return tud_cdc_n_write(0, buffer, bufsize);
}
-static inline uint32_t tud_cdc_write_str (char const* str)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_str(char const* str) {
return tud_cdc_n_write_str(0, str);
}
-static inline uint32_t tud_cdc_write_flush (void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_flush(void) {
return tud_cdc_n_write_flush(0);
}
-static inline uint32_t tud_cdc_write_available(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_available(void) {
return tud_cdc_n_write_available(0);
}
-static inline bool tud_cdc_write_clear(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) {
return tud_cdc_n_write_clear(0);
}
-/** @} */
-/** @} */
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK 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);
+
+// 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);
+
+// 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);
+
+// 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);
+
+// 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);
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void cdcd_init (void);
+bool cdcd_deinit (void);
void cdcd_reset (uint8_t rhport);
uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index ee824cb4e..e817ebc7e 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -22,6 +22,9 @@
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
+ *
+ * Contribution
+ * - Heiko Kuester: CH34x support
*/
#include "tusb_option.h"
@@ -29,232 +32,1649 @@
#if (CFG_TUH_ENABLED && CFG_TUH_CDC)
#include "host/usbh.h"
-#include "host/usbh_classdriver.h"
+#include "host/usbh_pvt.h"
#include "cdc_host.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUH_CDC_LOG_LEVEL
+ #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
+// Host CDC Interface
//--------------------------------------------------------------------+
+
typedef struct {
- uint8_t itf_num;
- uint8_t itf_protocol;
+ uint8_t daddr;
+ uint8_t bInterfaceNumber;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
-
+ uint8_t serial_drid; // Serial Driver ID
+ bool mounted; // Enumeration is complete
cdc_acm_capability_t acm_capability;
-} cdch_data_t;
+ TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
+ uint8_t line_state; // DTR (bit0), RTS (bit1)
+
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+ cdc_line_coding_t requested_line_coding;
+ // 1 byte padding
+ #endif
+
+ tuh_xfer_cb_t user_control_cb;
+
+ struct {
+ tu_edpt_stream_t tx;
+ tu_edpt_stream_t rx;
+
+ uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
+ uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
+ } stream;
+} cdch_interface_t;
+
+typedef struct {
+ TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE);
+ TUH_EPBUF_DEF(rx, CFG_TUH_CDC_TX_EPSIZE);
+} cdch_epbuf_t;
+
+static cdch_interface_t cdch_data[CFG_TUH_CDC];
+CFG_TUH_MEM_SECTION static cdch_epbuf_t cdch_epbuf[CFG_TUH_CDC];
+
+//--------------------------------------------------------------------+
+// Serial Driver
+//--------------------------------------------------------------------+
+
+//------------- ACM prototypes -------------//
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+static void acm_process_config(tuh_xfer_t* xfer);
+
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+//------------- FTDI prototypes -------------//
+#if CFG_TUH_CDC_FTDI
+#include "serial/ftdi_sio.h"
+
+static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
+
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+static void ftdi_process_config(tuh_xfer_t* xfer);
+
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- CP210X prototypes -------------//
+#if CFG_TUH_CDC_CP210X
+#include "serial/cp210x.h"
+
+static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
+
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+static void cp210x_process_config(tuh_xfer_t* xfer);
+
+static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- CH34x prototypes -------------//
+#if CFG_TUH_CDC_CH34X
+#include "serial/ch34x.h"
+
+static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len);
+static void ch34x_process_config(tuh_xfer_t* xfer);
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+#endif
+
+//------------- Common -------------//
+enum {
+ SERIAL_DRIVER_ACM = 0,
+
+#if CFG_TUH_CDC_FTDI
+ SERIAL_DRIVER_FTDI,
+#endif
+
+#if CFG_TUH_CDC_CP210X
+ SERIAL_DRIVER_CP210X,
+#endif
+
+#if CFG_TUH_CDC_CH34X
+ SERIAL_DRIVER_CH34X,
+#endif
+
+ SERIAL_DRIVER_COUNT
+};
+
+typedef struct {
+ uint16_t const (*vid_pid_list)[2];
+ uint16_t const vid_pid_count;
+ bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
+ void (*const process_set_config)(tuh_xfer_t* xfer);
+ bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+ bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+} cdch_serial_driver_t;
+
+// Note driver list must be in the same order as SERIAL_DRIVER enum
+static const cdch_serial_driver_t serial_drivers[] = {
+ {
+ .vid_pid_list = NULL,
+ .vid_pid_count = 0,
+ .open = acm_open,
+ .process_set_config = acm_process_config,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_baudrate,
+ .set_data_format = acm_set_data_format,
+ .set_line_coding = acm_set_line_coding
+ },
+
+ #if CFG_TUH_CDC_FTDI
+ {
+ .vid_pid_list = ftdi_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+ .open = ftdi_open,
+ .process_set_config = ftdi_process_config,
+ .set_control_line_state = ftdi_sio_set_modem_ctrl,
+ .set_baudrate = ftdi_sio_set_baudrate,
+ .set_data_format = ftdi_set_data_format,
+ .set_line_coding = ftdi_set_line_coding
+ },
+ #endif
+
+ #if CFG_TUH_CDC_CP210X
+ {
+ .vid_pid_list = cp210x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+ .open = cp210x_open,
+ .process_set_config = cp210x_process_config,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_data_format = cp210x_set_data_format,
+ .set_line_coding = cp210x_set_line_coding
+ },
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ {
+ .vid_pid_list = ch34x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+ .open = ch34x_open,
+ .process_set_config = ch34x_process_config,
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_data_format = ch34x_set_data_format,
+ .set_line_coding = ch34x_set_line_coding
+ },
+ #endif
+};
+
+TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-static cdch_data_t cdch_data[CFG_TUH_DEVICE_MAX];
-static inline cdch_data_t* get_itf(uint8_t dev_addr)
-{
- return &cdch_data[dev_addr-1];
-}
+static inline cdch_interface_t* get_itf(uint8_t idx) {
+ TU_ASSERT(idx < CFG_TUH_CDC, NULL);
+ cdch_interface_t* p_cdc = &cdch_data[idx];
-bool tuh_cdc_mounted(uint8_t dev_addr)
-{
- cdch_data_t* cdc = get_itf(dev_addr);
- return cdc->ep_in && cdc->ep_out;
+ return (p_cdc->daddr != 0) ? p_cdc : NULL;
}
-bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid)
-{
- if ( !tuh_cdc_mounted(dev_addr) ) return false;
+static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
+ for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
+ cdch_interface_t* p_cdc = &cdch_data[i];
+ if ( (p_cdc->daddr == daddr) &&
+ (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) {
+ return i;
+ }
+ }
- cdch_data_t const * p_cdc = get_itf(dev_addr);
+ return TUSB_INDEX_INVALID_8;
+}
- switch (pipeid)
- {
- case CDC_PIPE_NOTIFICATION:
- return usbh_edpt_busy(dev_addr, p_cdc->ep_notif );
+static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) {
+ for(uint8_t i=0; i<CFG_TUH_CDC; i++) {
+ if (cdch_data[i].daddr == 0) {
+ cdch_interface_t* p_cdc = &cdch_data[i];
+ p_cdc->daddr = daddr;
+ p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber;
+ p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass;
+ p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol;
+ p_cdc->line_state = 0;
+ return p_cdc;
+ }
+ }
- case CDC_PIPE_DATA_IN:
- return usbh_edpt_busy(dev_addr, p_cdc->ep_in );
+ return NULL;
+}
- case CDC_PIPE_DATA_OUT:
- return usbh_edpt_busy(dev_addr, p_cdc->ep_out );
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep);
+static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num);
+static void cdch_internal_control_complete(tuh_xfer_t* xfer);
- default:
- return false;
+//--------------------------------------------------------------------+
+// APPLICATION API
+//--------------------------------------------------------------------+
+
+uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) {
+ for (uint8_t i = 0; i < CFG_TUH_CDC; i++) {
+ const cdch_interface_t* p_cdc = &cdch_data[i];
+ if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
}
+
+ return TUSB_INDEX_INVALID_8;
+}
+
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && info);
+
+ info->daddr = p_cdc->daddr;
+
+ // re-construct descriptor
+ tusb_desc_interface_t* desc = &info->desc;
+ desc->bLength = sizeof(tusb_desc_interface_t);
+ desc->bDescriptorType = TUSB_DESC_INTERFACE;
+
+ desc->bInterfaceNumber = p_cdc->bInterfaceNumber;
+ desc->bAlternateSetting = 0;
+ desc->bNumEndpoints = 2u + (p_cdc->ep_notif ? 1u : 0u);
+ desc->bInterfaceClass = TUSB_CLASS_CDC;
+ desc->bInterfaceSubClass = p_cdc->bInterfaceSubClass;
+ desc->bInterfaceProtocol = p_cdc->bInterfaceProtocol;
+ desc->iInterface = 0; // not used yet
+
+ return true;
+}
+
+bool tuh_cdc_mounted(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+ return p_cdc->mounted;
+}
+
+bool tuh_cdc_get_dtr(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false;
+}
+
+bool tuh_cdc_get_rts(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false;
+}
+
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ *line_coding = p_cdc->line_coding;
+
+ return true;
}
//--------------------------------------------------------------------+
-// APPLICATION API (parameter validation needed)
+// Write
//--------------------------------------------------------------------+
-bool tuh_cdc_serial_is_mounted(uint8_t dev_addr)
-{
- // TODO consider all AT Command as serial candidate
- return tuh_cdc_mounted(dev_addr) &&
- (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
+
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_write(p_cdc->daddr, &p_cdc->stream.tx, buffer, bufsize);
}
-bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
-{
- (void) is_notify;
- TU_VERIFY( tuh_cdc_mounted(dev_addr) );
- TU_VERIFY( p_data != NULL && length);
+uint32_t tuh_cdc_write_flush(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_write_xfer(p_cdc->daddr, &p_cdc->stream.tx);
+}
- uint8_t const ep_out = cdch_data[dev_addr-1].ep_out;
- if ( usbh_edpt_busy(dev_addr, ep_out) ) return false;
+bool tuh_cdc_write_clear(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
- return usbh_edpt_xfer(dev_addr, ep_out, (void*)(uintptr_t) p_data, (uint16_t) length);
+ return tu_edpt_stream_clear(&p_cdc->stream.tx);
}
-bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
-{
- (void) is_notify;
- TU_VERIFY( tuh_cdc_mounted(dev_addr) );
- TU_VERIFY( p_buffer != NULL && length );
+uint32_t tuh_cdc_write_available(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
- uint8_t const ep_in = cdch_data[dev_addr-1].ep_in;
- if ( usbh_edpt_busy(dev_addr, ep_in) ) return false;
+ return tu_edpt_stream_write_available(p_cdc->daddr, &p_cdc->stream.tx);
+}
- return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, (uint16_t) length);
+//--------------------------------------------------------------------+
+// Read
+//--------------------------------------------------------------------+
+
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_read(p_cdc->daddr, &p_cdc->stream.rx, buffer, bufsize);
}
-bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb)
-{
- cdch_data_t const * p_cdc = get_itf(dev_addr);
+uint32_t tuh_cdc_read_available(uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
+ return tu_edpt_stream_read_available(&p_cdc->stream.rx);
+}
+
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ return tu_edpt_stream_peek(&p_cdc->stream.rx, ch);
+}
+
+bool tuh_cdc_read_clear (uint8_t idx) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc);
+
+ bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx);
+ tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
+ return ret;
+}
+
+//--------------------------------------------------------------------+
+// Control Endpoint API
+//--------------------------------------------------------------------+
+
+static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) {
+ uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+ uint16_t const value = tu_le16toh(xfer->setup->wValue);
+
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ switch (p_cdc->serial_drid) {
+ case SERIAL_DRIVER_ACM:
+ switch (xfer->setup->bRequest) {
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ p_cdc->line_state = (uint8_t) value;
+ break;
+
+ case CDC_REQUEST_SET_LINE_CODING: {
+ uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength));
+ memcpy(&p_cdc->line_coding, xfer->buffer, len);
+ break;
+ }
+
+ default: break;
+ }
+ break;
+
+ #if CFG_TUH_CDC_FTDI
+ case SERIAL_DRIVER_FTDI:
+ switch (xfer->setup->bRequest) {
+ case FTDI_SIO_MODEM_CTRL:
+ p_cdc->line_state = (uint8_t) value;
+ break;
+
+ case FTDI_SIO_SET_BAUD_RATE:
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ break;
+
+ default: break;
+ }
+ break;
+ #endif
+
+ #if CFG_TUH_CDC_CP210X
+ case SERIAL_DRIVER_CP210X:
+ switch(xfer->setup->bRequest) {
+ case CP210X_SET_MHS:
+ p_cdc->line_state = (uint8_t) value;
+ break;
+
+ case CP210X_SET_BAUDRATE: {
+ uint32_t baudrate;
+ memcpy(&baudrate, xfer->buffer, sizeof(uint32_t));
+ p_cdc->line_coding.bit_rate = tu_le32toh(baudrate);
+ break;
+ }
+
+ default: break;
+ }
+ break;
+ #endif
+
+ #if CFG_TUH_CDC_CH34X
+ case SERIAL_DRIVER_CH34X:
+ switch (xfer->setup->bRequest) {
+ case CH34X_REQ_WRITE_REG:
+ // register write request
+ switch (value) {
+ case CH34X_REG16_DIVISOR_PRESCALER:
+ // baudrate
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ break;
+
+ case CH32X_REG16_LCR2_LCR:
+ // data format
+ p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
+ p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
+ p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
+ break;
+
+ default: break;
+ }
+ break;
+
+ case CH34X_REQ_MODEM_CTRL: {
+ // set modem controls RTS/DTR request. Note: signals are inverted
+ uint16_t const modem_signal = ~value;
+ if (modem_signal & CH34X_BIT_RTS) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
+ }
+
+ if (modem_signal & CH34X_BIT_DTR) {
+ p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
+ } else {
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
+ }
+ break;
+ }
+
+ default: break;
+ }
+ break;
+ #endif
+
+ default: break;
+ }
+ }
+
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+}
+
+// internal control complete to update state such as line state, encoding
+static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ process_internal_control_complete(xfer, itf_num);
+}
+
+bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if (complete_cb) {
+ return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_state = (uint8_t) line_state;
+ return true;
+ }
+}
+
+bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if (complete_cb) {
+ return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.bit_rate = baudrate;
+ return true;
+ }
+}
+
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if (complete_cb) {
+ return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = stop_bits;
+ p_cdc->line_coding.parity = parity;
+ p_cdc->line_coding.data_bits = data_bits;
+ return true;
+ }
+}
+
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid];
+
+ if ( complete_cb ) {
+ return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data);
+ } else {
+ // blocking
+ xfer_result_t result = XFER_RESULT_INVALID;
+ bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result);
+
+ if (user_data) {
+ // user_data is not NULL, return result via user_data
+ *((xfer_result_t*) user_data) = result;
+ }
+
+ TU_VERIFY(ret && result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding = *line_coding;
+ return true;
+ }
+}
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API
+//--------------------------------------------------------------------+
+
+bool cdch_init(void) {
+ TU_LOG_DRV("sizeof(cdch_interface_t) = %u\r\n", sizeof(cdch_interface_t));
+ tu_memclr(cdch_data, sizeof(cdch_data));
+ for (size_t i = 0; i < CFG_TUH_CDC; i++) {
+ cdch_interface_t* p_cdc = &cdch_data[i];
+ cdch_epbuf_t* epbuf = &cdch_epbuf[i];
+ tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false,
+ p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE,
+ epbuf->tx, CFG_TUH_CDC_TX_EPSIZE);
+
+ tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false,
+ p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE,
+ epbuf->rx, CFG_TUH_CDC_RX_EPSIZE);
+ }
+
+ return true;
+}
+
+bool cdch_deinit(void) {
+ for (size_t i = 0; i < CFG_TUH_CDC; i++) {
+ cdch_interface_t* p_cdc = &cdch_data[i];
+ tu_edpt_stream_deinit(&p_cdc->stream.tx);
+ tu_edpt_stream_deinit(&p_cdc->stream.rx);
+ }
+ return true;
+}
+
+void cdch_close(uint8_t daddr) {
+ for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
+ cdch_interface_t* p_cdc = &cdch_data[idx];
+ if (p_cdc->daddr == daddr) {
+ TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx);
+
+ // Invoke application callback
+ if (tuh_cdc_umount_cb) {
+ tuh_cdc_umount_cb(idx);
+ }
+
+ p_cdc->daddr = 0;
+ p_cdc->bInterfaceNumber = 0;
+ p_cdc->mounted = false;
+ tu_edpt_stream_close(&p_cdc->stream.tx);
+ tu_edpt_stream_close(&p_cdc->stream.rx);
+ }
+ }
+}
+
+bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
+ // TODO handle stall response, retry failed transfer ...
+ TU_ASSERT(event == XFER_RESULT_SUCCESS);
+
+ uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc);
+
+ 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);
+ }
+
+ if ( 0 == tu_edpt_stream_write_xfer(daddr, &p_cdc->stream.tx) ) {
+ // If there is no data left, a ZLP should be sent if:
+ // - xferred_bytes is multiple of EP Packet size and not zero
+ tu_edpt_stream_write_zlp_if_needed(daddr, &p_cdc->stream.tx, xferred_bytes);
+ }
+ } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
+ #if CFG_TUH_CDC_FTDI
+ if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
+ // FTDI reserve 2 bytes for status
+ // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
+ tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
+ }else
+ #endif
{
+ tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
+ }
+
+ // invoke receive callback
+ if (tuh_cdc_rx_cb) {
+ tuh_cdc_rx_cb(idx);
+ }
+
+ // prepare for next transfer if needed
+ tu_edpt_stream_read_xfer(daddr, &p_cdc->stream.rx);
+ }else if ( ep_addr == p_cdc->ep_notif ) {
+ // TODO handle notification endpoint
+ }else {
+ TU_ASSERT(false);
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// Enumeration
+//--------------------------------------------------------------------+
+
+static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) {
+ for (size_t i = 0; i < 2; i++) {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
+ TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep));
+
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_open(&p_cdc->stream.rx, desc_ep);
+ } else {
+ tu_edpt_stream_open(&p_cdc->stream.tx, desc_ep);
+ }
+
+ desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep);
+ }
+
+ return true;
+}
+
+bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ (void) rhport;
+
+ // For CDC: only support ACM subclass
+ // Note: Protocol 0xFF can be RNDIS device
+ if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) {
+ return acm_open(daddr, itf_desc, max_len);
+ } else if (SERIAL_DRIVER_COUNT > 1 &&
+ TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) {
+ uint16_t vid, pid;
+ TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid));
+
+ for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) {
+ cdch_serial_driver_t const* driver = &serial_drivers[dr];
+ for (size_t i = 0; i < driver->vid_pid_count; i++) {
+ if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
+ return driver->open(daddr, itf_desc, max_len);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) {
+ TU_LOG_DRV("CDCh Set Configure complete\r\n");
+ p_cdc->mounted = true;
+ if (tuh_cdc_mount_cb) {
+ tuh_cdc_mount_cb(idx);
+ }
+
+ // Prepare for incoming data
+ tu_edpt_stream_read_xfer(p_cdc->daddr, &p_cdc->stream.rx);
+
+ // notify usbh that driver enumeration is complete
+ usbh_driver_set_config_complete(p_cdc->daddr, itf_num);
+}
+
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
+ tusb_control_request_t request;
+ request.wIndex = tu_htole16((uint16_t) itf_num);
+
+ // fake transfer to kick-off process
+ tuh_xfer_t xfer;
+ xfer.daddr = daddr;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.setup = &request;
+ xfer.user_data = 0; // initial state
+
+ uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+
+ serial_drivers[p_cdc->serial_drid].process_set_config(&xfer);
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// ACM
+//--------------------------------------------------------------------+
+
+enum {
+ CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
+ CONFIG_ACM_SET_LINE_CODING,
+ CONFIG_ACM_COMPLETE,
+};
+
+static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len;
+
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+ p_cdc->serial_drid = SERIAL_DRIVER_ACM;
+
+ //------------- Control Interface -------------//
+ uint8_t const* p_desc = tu_desc_next(itf_desc);
+
+ // Communication Functional Descriptors
+ while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) {
+ if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) {
+ // save ACM bmCapabilities
+ p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities;
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ // Open notification endpoint of control interface if any
+ if (itf_desc->bNumEndpoints == 1) {
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
+
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
+ p_desc = tu_desc_next(p_desc);
+ }
+
+ //------------- Data Interface (if any) -------------//
+ if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+ (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
+ // next to endpoint descriptor
+ p_desc = tu_desc_next(p_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc));
+ }
+
+ return true;
+}
+
+static void acm_process_config(tuh_xfer_t* xfer) {
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
+
+ switch (state) {
+ case CONFIG_ACM_SET_CONTROL_LINE_STATE:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request) {
+ TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),);
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_ACM_SET_LINE_CODING:
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ if (p_cdc->acm_capability.support_line_request) {
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),);
+ break;
+ }
+ #endif
+ TU_ATTR_FALLTHROUGH;
+
+ case CONFIG_ACM_COMPLETE:
+ // itf_num+1 to account for data interface as well
+ set_config_complete(p_cdc, idx, itf_num + 1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ TU_LOG_DRV("CDC ACM Set Control Line State\r\n");
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
- .wValue = tu_htole16((uint16_t) ((dtr ? 1u : 0u) | (rts ? 2u : 0u))),
- .wIndex = tu_htole16(p_cdc->itf_num),
+ .wValue = tu_htole16(line_state),
+ .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber),
.wLength = 0
};
- tuh_xfer_t xfer =
- {
- .daddr = dev_addr,
+ p_cdc->user_control_cb = complete_cb;
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .user_data = user_data
+ };
+
+ TU_ASSERT(tuh_control_xfer(&xfer));
+ return true;
+}
+
+static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC ACM Set Line Conding\r\n");
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = CDC_REQUEST_SET_LINE_CODING,
+ .wValue = 0,
+ .wIndex = tu_htole16(p_cdc->bInterfaceNumber),
+ .wLength = tu_htole16(sizeof(cdc_line_coding_t))
+ };
+
+ // use usbh enum buf to hold line coding since user line_coding variable does not live long enough
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t));
+
+ p_cdc->user_control_cb = complete_cb;
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call
+ .user_data = user_data
+ };
+
+ TU_ASSERT(tuh_control_xfer(&xfer));
+ return true;
+}
+
+static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC ACM Set Data Format\r\n");
+
+ cdc_line_coding_t line_coding;
+ line_coding.bit_rate = p_cdc->line_coding.bit_rate;
+ line_coding.stop_bits = stop_bits;
+ line_coding.parity = parity;
+ line_coding.data_bits = data_bits;
+
+ return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
+static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->acm_capability.support_line_request);
+ cdc_line_coding_t line_coding = p_cdc->line_coding;
+ line_coding.bit_rate = baudrate;
+ return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data);
+}
+
+//--------------------------------------------------------------------+
+// FTDI
+//--------------------------------------------------------------------+
+#if CFG_TUH_CDC_FTDI
+
+enum {
+ CONFIG_FTDI_RESET = 0,
+ CONFIG_FTDI_MODEM_CTRL,
+ CONFIG_FTDI_SET_BAUDRATE,
+ CONFIG_FTDI_SET_DATA,
+ CONFIG_FTDI_COMPLETE
+};
+
+static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) {
+ // FTDI Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ TU_LOG_DRV("FTDI opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_FTDI;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
+}
+
+// set request without data
+static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = command,
+ .wValue = tu_htole16(value),
+ .wIndex = 0,
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
- .user_data = 0
+ .user_data = user_data
};
return tuh_control_xfer(&xfer);
}
+static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data);
+}
+
+static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC FTDI Set Control Line State\r\n");
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data));
+ return true;
+}
+
+static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
+ const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+ uint32_t divisor;
+
+ /* divisor shifted 3 bits to the left */
+ uint32_t divisor3 = base / (2 * baud);
+ divisor = (divisor3 >> 3);
+ divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
+
+ /* Deal with special cases for highest baud rates. */
+ if (divisor == 1) { /* 1.0 */
+ divisor = 0;
+ }
+ else if (divisor == 0x4001) { /* 1.5 */
+ divisor = 1;
+ }
+
+ return divisor;
+}
+
+static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
+ return ftdi_232bm_baud_base_to_divisor(baud, 48000000u);
+}
+
+static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
+ TU_LOG_DRV("CDC FTDI Set BaudRate = %" PRIu32 ", divisor = 0x%04x\r\n", baudrate, divisor);
+
+ p_cdc->user_control_cb = complete_cb;
+ p_cdc->requested_line_coding.bit_rate = baudrate;
+ TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data));
+
+ return true;
+}
+
+static void ftdi_process_config(tuh_xfer_t* xfer) {
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ switch(state) {
+ // Note may need to read FTDI eeprom
+ case CONFIG_FTDI_RESET:
+ TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),);
+ break;
+
+ case CONFIG_FTDI_MODEM_CTRL:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+
+ case CONFIG_FTDI_SET_BAUDRATE: {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+ }
+
+ case CONFIG_FTDI_SET_DATA: {
+ #if 0 // TODO set data format
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),);
+ break;
+ #endif
+ #endif
+
+ TU_ATTR_FALLTHROUGH;
+ }
+
+ case CONFIG_FTDI_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif
+
//--------------------------------------------------------------------+
-// USBH-CLASS DRIVER API
+// CP210x
//--------------------------------------------------------------------+
-void cdch_init(void)
-{
- tu_memclr(cdch_data, sizeof(cdch_data));
+
+#if CFG_TUH_CDC_CP210X
+
+enum {
+ CONFIG_CP210X_IFC_ENABLE = 0,
+ CONFIG_CP210X_SET_BAUDRATE,
+ CONFIG_CP210X_SET_LINE_CTL,
+ CONFIG_CP210X_SET_DTR_RTS,
+ CONFIG_CP210X_COMPLETE
+};
+
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ // CP210x Interface includes 1 vendor interface + 2 bulk endpoints
+ TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
+ TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY(p_cdc);
+
+ TU_LOG_DRV("CP210x opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_CP210X;
+
+ // endpoint pair
+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ return open_ep_stream_pair(p_cdc, desc_ep);
}
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
-{
- (void) rhport;
- (void) max_len;
+static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = command,
+ .wValue = tu_htole16(value),
+ .wIndex = p_cdc->bInterfaceNumber,
+ .wLength = tu_htole16(length)
+ };
- // Only support ACM subclass
- // Protocol 0xFF can be RNDIS device for windows XP
- TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
- 0xFF != itf_desc->bInterfaceProtocol);
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
- cdch_data_t * p_cdc = get_itf(dev_addr);
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
- p_cdc->itf_num = itf_desc->bInterfaceNumber;
- p_cdc->itf_protocol = itf_desc->bInterfaceProtocol;
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
- //------------- Communication Interface -------------//
- uint16_t drv_len = tu_desc_len(itf_desc);
- uint8_t const * p_desc = tu_desc_next(itf_desc);
+ return tuh_control_xfer(&xfer);
+}
- // Communication Functional Descriptors
- while( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
- if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
- {
- // save ACM bmCapabilities
- p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
+static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
+}
+
+static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // TODO implement later
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+ return false;
+}
+
+static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC CP210x Set BaudRate = %" PRIu32 "\r\n", baudrate);
+ uint32_t baud_le = tu_htole32(baudrate);
+ p_cdc->user_control_cb = complete_cb;
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data);
+}
+
+static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ (void) p_cdc;
+ (void) stop_bits;
+ (void) parity;
+ (void) data_bits;
+ (void) complete_cb;
+ (void) user_data;
+ // TODO not implemented yet
+ return false;
+}
+
+static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("CDC CP210x Set Control Line State\r\n");
+ p_cdc->user_control_cb = complete_cb;
+ return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0,
+ complete_cb ? cdch_internal_control_complete : NULL, user_data);
+}
+
+static void cp210x_process_config(tuh_xfer_t* xfer) {
+ uintptr_t const state = xfer->user_data;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t *p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
+
+ switch (state) {
+ case CONFIG_CP210X_IFC_ENABLE:
+ TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),);
+ break;
+
+ case CONFIG_CP210X_SET_BAUDRATE: {
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
}
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ case CONFIG_CP210X_SET_LINE_CTL: {
+ #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
+ cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+ }
+
+ case CONFIG_CP210X_SET_DTR_RTS:
+ #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+ TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),);
+ break;
+ #else
+ TU_ATTR_FALLTHROUGH;
+ #endif
+
+ case CONFIG_CP210X_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ break;
+
+ default: break;
}
+}
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- // notification endpoint
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+#endif
- TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) );
- p_cdc->ep_notif = desc_ep->bEndpointAddress;
+//--------------------------------------------------------------------+
+// CH34x (CH340 & CH341)
+//--------------------------------------------------------------------+
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+#if CFG_TUH_CDC_CH34X
+
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+
+//------------- control request -------------//
+
+static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value,
+ uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request_setup = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_VENDOR,
+ .direction = direction & 0x01u
+ },
+ .bRequest = request,
+ .wValue = tu_htole16 (value),
+ .wIndex = tu_htole16 (index),
+ .wLength = tu_htole16 (length)
+ };
+
+ // use usbh enum buf since application variable does not live long enough
+ uint8_t* enum_buf = NULL;
+
+ if (buffer && length > 0) {
+ enum_buf = usbh_get_enum_buf();
+ if (direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
}
- //------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
- {
- // next to endpoint descriptor
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ tuh_xfer_t xfer = {
+ .daddr = p_cdc->daddr,
+ .ep_addr = 0,
+ .setup = &request_setup,
+ .buffer = enum_buf,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
- // data endpoints expected to be in pairs
- for(uint32_t i=0; i<2; i++)
- {
- tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
+ return tuh_control_xfer(&xfer);
+}
- TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
+static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data);
+}
- if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
- {
- p_cdc->ep_in = desc_ep->bEndpointAddress;
- }else
- {
- p_cdc->ep_out = desc_ep->bEndpointAddress;
- }
+static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index,
+ uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize,
+ complete_cb, user_data);
+}
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next( p_desc );
+static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
+}
+
+//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
+// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data )
+//{
+// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
+//}
+
+static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
+ TU_VERIFY(div_ps);
+ TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
+ complete_cb, user_data));
+ return true;
+}
+
+//------------- Driver API -------------//
+
+// internal control complete to update state such as line state, encoding
+static void ch34x_control_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ process_internal_control_complete(xfer, 0);
+}
+
+static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.stop_bits = stop_bits;
+ p_cdc->requested_line_coding.parity = parity;
+ p_cdc->requested_line_coding.data_bits = data_bits;
+
+ uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
+ TU_VERIFY(lcr);
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding.bit_rate = baudrate;
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+ return true;
+}
+
+static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc, );
+
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ // stage 1 success, continue to stage 2
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity,
+ p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), );
+ } else {
+ // stage 1 failed, notify user
+ xfer->complete_cb = p_cdc->user_control_cb;
+ if (xfer->complete_cb) {
+ xfer->complete_cb(xfer);
+ }
+ }
+}
+
+// 2 stages: set baudrate (stage1) + set data format (stage2)
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->requested_line_coding = *line_coding;
+ p_cdc->user_control_cb = complete_cb;
+
+ if (complete_cb) {
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate,
+ ch34x_set_line_coding_stage1_complete, user_data));
+ } else {
+ // sync call
+ xfer_result_t result;
+
+ // stage 1 set baudrate
+ TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.bit_rate = line_coding->bit_rate;
+
+ // stage 2 set data format
+ TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits,
+ NULL, (uintptr_t) &result));
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
+ p_cdc->line_coding.stop_bits = line_coding->stop_bits;
+ p_cdc->line_coding.parity = line_coding->parity;
+ p_cdc->line_coding.data_bits = line_coding->data_bits;
+
+ // update transfer result, user_data is expected to point to xfer_result_t
+ if (user_data) {
+ *((xfer_result_t*) user_data) = result;
}
}
return true;
}
-bool cdch_set_config(uint8_t dev_addr, uint8_t itf_num)
-{
- (void) dev_addr; (void) itf_num;
+static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ uint8_t control = 0;
+ if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
+ control |= CH34X_BIT_RTS;
+ }
+ if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
+ control |= CH34X_BIT_DTR;
+ }
+
+ // CH34x signals are inverted
+ control = ~control;
+
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
return true;
}
-bool cdch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
- (void) ep_addr;
- tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes );
+//------------- Enumeration -------------//
+enum {
+ CONFIG_CH34X_READ_VERSION = 0,
+ CONFIG_CH34X_SERIAL_INIT,
+ CONFIG_CH34X_SPECIAL_REG_WRITE,
+ CONFIG_CH34X_FLOW_CONTROL,
+ CONFIG_CH34X_MODEM_CONTROL,
+ CONFIG_CH34X_COMPLETE
+};
+
+static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
+ // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
+ TU_VERIFY (itf_desc->bNumEndpoints == 3);
+ TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len);
+
+ cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc);
+ TU_VERIFY (p_cdc);
+
+ TU_LOG_DRV ("CH34x opened\r\n");
+ p_cdc->serial_drid = SERIAL_DRIVER_CH34X;
+
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc);
+
+ // data endpoints expected to be in pairs
+ TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep));
+ desc_ep += 2;
+
+ // Interrupt endpoint: not used for now
+ TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) &&
+ TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer);
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
+ p_cdc->ep_notif = desc_ep->bEndpointAddress;
+
return true;
}
-void cdch_close(uint8_t dev_addr)
-{
- TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
+static void ch34x_process_config(tuh_xfer_t* xfer) {
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
+ uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
+ cdch_interface_t* p_cdc = get_itf(idx);
+ uintptr_t const state = xfer->user_data;
+ uint8_t buffer[2]; // TODO remove
+ TU_ASSERT (p_cdc,);
+ TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,);
+
+ switch (state) {
+ case CONFIG_CH34X_READ_VERSION:
+ TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
+ TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
+ break;
+
+ case CONFIG_CH34X_SERIAL_INIT: {
+ // handle version read data, set CH34x line coding (incl. baudrate)
+ uint8_t const version = xfer->buffer[0];
+ TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
+ // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
+ TU_ASSERT (version >= 0x30,);
+ // init CH34x with line coding
+ cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
+ TU_ASSERT(div_ps, );
+ uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
+ TU_ASSERT(lcr, );
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+ ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+ break;
+ }
+
+ case CONFIG_CH34X_SPECIAL_REG_WRITE:
+ // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
+ p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X);
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_FLOW_CONTROL:
+ // no hardware flow control
+ TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
+ break;
+
+ case CONFIG_CH34X_MODEM_CONTROL:
+ // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
+ TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
+ break;
+
+ case CONFIG_CH34X_COMPLETE:
+ set_config_complete(p_cdc, idx, itf_num);
+ break;
+
+ default:
+ TU_ASSERT (false,);
+ break;
+ }
+}
+
+//------------- CH34x helper -------------//
+
+// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ TU_VERIFY(baval != 0 && baval <= 2000000, 0);
+ switch (baval) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baval > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baval > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baval > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (uint8_t) (c / baval);
+ if (a == 0 || a == 0xFF) {
+ return 0;
+ }
+ if ((c / a - baval) > (baval - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t) (256 - a);
+ break;
+ }
+
+ // reg divisor = a, reg prescaler = b
+ // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
+ // otherwise the chip will buffer data.
+ return (uint16_t) ((uint16_t)a << 8 | 0x80 | b);
+}
+
+// calculate lcr value from data coding
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+ uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+ TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0);
+ lcr |= (uint8_t) (data_bits - 5);
+
+ switch(parity) {
+ case CDC_LINE_CODING_PARITY_NONE:
+ break;
+
+ case CDC_LINE_CODING_PARITY_ODD:
+ lcr |= CH34X_LCR_ENABLE_PAR;
+ break;
+
+ case CDC_LINE_CODING_PARITY_EVEN:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
+ break;
+
+ case CDC_LINE_CODING_PARITY_MARK:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE;
+ break;
- cdch_data_t * p_cdc = get_itf(dev_addr);
- tu_memclr(p_cdc, sizeof(cdch_data_t));
+ case CDC_LINE_CODING_PARITY_SPACE:
+ lcr |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
+ break;
+
+ default: break;
+ }
+
+ // 1.5 stop bits not supported
+ TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+ if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
+ lcr |= CH34X_LCR_STOP_BITS_2;
+ }
+
+ return lcr;
}
+
+#endif // CFG_TUH_CDC_CH34X
+
#endif
diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h
index 33dbd2efb..df975b2f0 100644
--- a/src/class/cdc/cdc_host.h
+++ b/src/class/cdc/cdc_host.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -34,94 +34,165 @@
#endif
//--------------------------------------------------------------------+
-// CDC APPLICATION PUBLIC API
+// Class Driver Configuration
//--------------------------------------------------------------------+
-/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
- * \addtogroup CDC_Serial Serial
- * @{
- * \defgroup CDC_Serial_Host Host
- * @{ */
-bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb);
+// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1)
+#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0
+#endif
-static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb)
-{
- return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb);
-}
+// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
+//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+//#endif
+
+// RX FIFO size
+#ifndef CFG_TUH_CDC_RX_BUFSIZE
+#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// RX Endpoint size
+#ifndef CFG_TUH_CDC_RX_EPSIZE
+#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// TX FIFO size
+#ifndef CFG_TUH_CDC_TX_BUFSIZE
+#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+// TX Endpoint size
+#ifndef CFG_TUH_CDC_TX_EPSIZE
+#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Get Interface index from device address + interface number
+// return TUSB_INDEX_INVALID_8 (0xFF) if not found
+uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
+
+// Get Interface information
+// return true if index is correct and interface is currently mounted
+bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
+
+// Check if a interface is mounted
+bool tuh_cdc_mounted(uint8_t idx);
+
+// Get current DTR status
+bool tuh_cdc_get_dtr(uint8_t idx);
+
+// Get current RTS status
+bool tuh_cdc_get_rts(uint8_t idx);
-static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb)
-{
- return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb);
+// Check if interface is connected (DTR active)
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) {
+ return tuh_cdc_get_dtr(idx);
}
-/** \brief Check if device support CDC Serial interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports
- * \retval false if device does not support or is not mounted
- */
-bool tuh_cdc_serial_is_mounted(uint8_t dev_addr);
+// Get local (saved/cached) version of line coding.
+// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
+// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
+// NOTE: This function does not make any USB transfer request to device.
+bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding);
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe.
- * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device
- * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
- * can be scheduled. User needs to make sure the corresponding interface is mounted
- * (by \ref tuh_cdc_serial_is_mounted) before calling this function.
- */
-bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid);
+//--------------------------------------------------------------------+
+// Write API
+//--------------------------------------------------------------------+
-/** \brief Perform USB OUT transfer to device
- * \param[in] dev_addr device address
- * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] length Number of bytes to be transferred via USB bus
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
- * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
- */
-bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify);
+// Get the number of bytes available for writing
+uint32_t tuh_cdc_write_available(uint8_t idx);
-/** \brief Perform USB IN transfer to get data from device
- * \param[in] dev_addr device address
- * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] length Number of bytes to be transferred via USB bus
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
- * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
- */
-bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify);
+// Write to cdc interface
+uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
+
+// Force sending data if possible, return number of forced bytes
+uint32_t tuh_cdc_write_flush(uint8_t idx);
+
+// Clear the transmit FIFO
+bool tuh_cdc_write_clear(uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Read API
+//--------------------------------------------------------------------+
+
+// Get the number of bytes available for reading
+uint32_t tuh_cdc_read_available(uint8_t idx);
+
+// Read from cdc interface
+uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
+
+// Get a byte from RX FIFO without removing it
+bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
+
+// Clear the received FIFO
+bool tuh_cdc_read_clear (uint8_t idx);
+
+//--------------------------------------------------------------------+
+// Control Endpoint (Request) API
+// Each Function will make a USB control transfer request to/from device
+// - If complete_cb is provided, the function will return immediately and invoke
+// the callback when request is complete.
+// - If complete_cb is NULL, the function will block until request is complete.
+// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result.
+// - The function will return true if transfer is successful, false otherwise.
+//--------------------------------------------------------------------+
+
+// Request to Set Control Line State: DTR (bit 0), RTS (bit 1)
+bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to set baudrate
+bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to set data format
+bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Set Line Coding = baudrate + data format
+// Note: only implemented by ACM and CH34x, not supported by FTDI and CP210x yet
+bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Request to Get Line Coding (ACM only)
+// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
+// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined
+// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding);
+
+// Connect by set both DTR, RTS
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
+}
+
+// Disconnect by clear both DTR, RTS
+TU_ATTR_ALWAYS_INLINE static inline
+bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
+}
//--------------------------------------------------------------------+
// CDC APPLICATION CALLBACKS
//--------------------------------------------------------------------+
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe
- * \param[in] xferred_bytes Number of bytes transferred via USB bus
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note
- */
-void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes);
+// 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);
+
+// Invoked when a device with CDC interface is unmounted
+TU_ATTR_WEAK 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);
-/// @} // group CDC_Serial_Host
-/// @}
+// 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);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void cdch_init (void);
+bool cdch_init (void);
+bool cdch_deinit (void);
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
diff --git a/src/class/cdc/cdc_rndis.h b/src/class/cdc/cdc_rndis.h
index e0f129fe3..ad153e0ac 100644
--- a/src/class/cdc/cdc_rndis.h
+++ b/src/class/cdc/cdc_rndis.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/class/cdc/cdc_rndis_host.c b/src/class/cdc/cdc_rndis_host.c
index 8f28f51ac..11a5355aa 100644
--- a/src/class/cdc/cdc_rndis_host.c
+++ b/src/class/cdc/cdc_rndis_host.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -50,8 +50,8 @@
//--------------------------------------------------------------------+
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
-CFG_TUSB_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
+CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
@@ -117,7 +117,7 @@ void rndish_init(void)
//------------- Task creation -------------//
- //------------- semaphore creation for notificaiton pipe -------------//
+ //------------- semaphore creation for notification pipe -------------//
for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
{
rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
diff --git a/src/class/cdc/cdc_rndis_host.h b/src/class/cdc/cdc_rndis_host.h
index 447cc4e97..bb431ec1f 100644
--- a/src/class/cdc/cdc_rndis_host.h
+++ b/src/class/cdc/cdc_rndis_host.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
new file mode 100644
index 000000000..c18066f57
--- /dev/null
+++ b/src/class/cdc/serial/ch34x.h
@@ -0,0 +1,84 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Heiko Kuester
+ *
+ * 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 _CH34X_H_
+#define _CH34X_H_
+
+// There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found
+// - https://github.com/WCHSoftGroup/ch341ser_linux
+// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
+// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uchcom.c
+
+// set line_coding @ enumeration
+#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM
+#else // this default is necessary to work properly
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+#endif
+
+// USB requests
+#define CH34X_REQ_READ_VERSION 0x5F // dec 95
+#define CH34X_REQ_WRITE_REG 0x9A // dec 154
+#define CH34X_REQ_READ_REG 0x95 // dec 149
+#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161
+#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164
+
+// registers
+#define CH34X_REG_BREAK 0x05
+#define CH34X_REG_PRESCALER 0x12
+#define CH34X_REG_DIVISOR 0x13
+#define CH34X_REG_LCR 0x18
+#define CH34X_REG_LCR2 0x25
+#define CH34X_REG_MCR_MSR 0x06
+#define CH34X_REG_MCR_MSR2 0x07
+#define CH34X_NBREAK_BITS 0x01
+
+#define CH341_REG_0x0F 0x0F // undocumented register
+#define CH341_REG_0x2C 0x2C // undocumented register
+#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
+
+#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
+#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR)
+
+// modem control bits
+#define CH34X_BIT_RTS ( 1 << 6 )
+#define CH34X_BIT_DTR ( 1 << 5 )
+
+// line control bits
+#define CH34X_LCR_ENABLE_RX 0x80
+#define CH34X_LCR_ENABLE_TX 0x40
+#define CH34X_LCR_MARK_SPACE 0x20
+#define CH34X_LCR_PAR_EVEN 0x10
+#define CH34X_LCR_ENABLE_PAR 0x08
+#define CH34X_LCR_PAR_MASK 0x38 // all parity bits
+#define CH34X_LCR_STOP_BITS_2 0x04
+#define CH34X_LCR_CS8 0x03
+#define CH34X_LCR_CS7 0x02
+#define CH34X_LCR_CS6 0x01
+#define CH34X_LCR_CS5 0x00
+#define CH34X_LCR_CS_MASK 0x03 // all CSx bits
+
+#endif /* _CH34X_H_ */
diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h
new file mode 100644
index 000000000..2c749f522
--- /dev/null
+++ b/src/class/cdc/serial/cp210x.h
@@ -0,0 +1,62 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach ([email protected]) for Adafruit Industries
+ *
+ * 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_CP210X_H
+#define TUSB_CP210X_H
+
+// Protocol details can be found at AN571: CP210x Virtual COM Port Interface
+// https://www.silabs.com/documents/public/application-notes/AN571.pdf
+
+#define TU_CP210X_VID 0x10C4
+
+/* Config request codes */
+#define CP210X_IFC_ENABLE 0x00
+#define CP210X_SET_BAUDDIV 0x01
+#define CP210X_GET_BAUDDIV 0x02
+#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits
+#define CP210X_GET_LINE_CTL 0x04
+#define CP210X_SET_BREAK 0x05
+#define CP210X_IMM_CHAR 0x06
+#define CP210X_SET_MHS 0x07 // Set DTR, RTS
+#define CP210X_GET_MDMSTS 0x08 // Get modem status (DTR, RTS, CTS, DSR, RI, DCD)
+#define CP210X_SET_XON 0x09
+#define CP210X_SET_XOFF 0x0A
+#define CP210X_SET_EVENTMASK 0x0B
+#define CP210X_GET_EVENTMASK 0x0C
+#define CP210X_SET_CHAR 0x0D
+#define CP210X_GET_CHARS 0x0E
+#define CP210X_GET_PROPS 0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET 0x11
+#define CP210X_PURGE 0x12
+#define CP210X_SET_FLOW 0x13
+#define CP210X_GET_FLOW 0x14
+#define CP210X_EMBED_EVENTS 0x15
+#define CP210X_GET_EVENTSTATE 0x16
+#define CP210X_SET_CHARS 0x19
+#define CP210X_GET_BAUDRATE 0x1D
+#define CP210X_SET_BAUDRATE 0x1E
+#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device
+
+#endif //TUSB_CP210X_H
diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h
new file mode 100644
index 000000000..0825f0719
--- /dev/null
+++ b/src/class/cdc/serial/ftdi_sio.h
@@ -0,0 +1,246 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach ([email protected]) for Adafruit Industries
+ *
+ * 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_FTDI_SIO_H
+#define TUSB_FTDI_SIO_H
+
+// VID for matching FTDI devices
+#define TU_FTDI_VID 0x0403
+
+// Commands
+#define FTDI_SIO_RESET 0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */
+#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
+#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
+#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */
+#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */
+#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
+#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
+
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_RESET
+ * wValue: Control Value
+ * 0 = Reset SIO
+ * 1 = Purge RX buffer
+ * 2 = Purge TX buffer
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * The Reset SIO command has this effect:
+ *
+ * Sets flow control set to 'none'
+ * Event char = $0D
+ * Event trigger = disabled
+ * Purge RX buffer
+ * Purge TX buffer
+ * Clear DTR
+ * Clear RTS
+ * baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+ */
+
+/* FTDI_SIO_MODEM_CTRL */
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_MODEM_CTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ */
+
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1)
+#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0)
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2)
+#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0)
+
+/*
+ * ControlValue
+ * B0 DTR state
+ * 0 = reset
+ * 1 = set
+ * B1 RTS state
+ * 0 = reset
+ * 1 = set
+ * B2..7 Reserved
+ * B8 DTR state enable
+ * 0 = ignore
+ * 1 = use DTR state
+ * B9 RTS state enable
+ * 0 = ignore
+ * 1 = use RTS state
+ * B10..15 Reserved
+ */
+
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS (0x1 << 8)
+#define FTDI_SIO_DTR_DSR_HS (0x2 << 8)
+#define FTDI_SIO_XON_XOFF_HS (0x4 << 8)
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_FLOW_CTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocol / lIndex is port
+ * wLength: 0
+ * Data: None
+ *
+ * hIndex protocol is:
+ * B0 Output handshaking using RTS/CTS
+ * 0 = disabled
+ * 1 = enabled
+ * B1 Output handshaking using DTR/DSR
+ * 0 = disabled
+ * 1 = enabled
+ * B2 Xon/Xoff handshaking
+ * 0 = disabled
+ * 1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+
+/* FTDI_SIO_SET_BAUD_RATE */
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_BAUDRATE
+ * wValue: BaudDivisor value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ * The BaudDivisor values are calculated as follows (too complicated):
+ */
+
+/* FTDI_SIO_SET_DATA */
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define FTDI_SIO_SET_BREAK (0x1 << 14)
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_DATA
+ * wValue: Data characteristics (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: No
+ *
+ * Data characteristics
+ *
+ * B0..7 Number of data bits
+ * B8..10 Parity
+ * 0 = None
+ * 1 = Odd
+ * 2 = Even
+ * 3 = Mark
+ * 4 = Space
+ * B11..13 Stop Bits
+ * 0 = 1
+ * 1 = 1.5
+ * 2 = 2
+ * B14
+ * 1 = TX ON (break)
+ * 0 = TX OFF (normal state)
+ * B15 Reserved
+ *
+ */
+
+/*
+* DATA FORMAT
+*
+* IN Endpoint
+*
+* The device reserves the first two bytes of data on this endpoint to contain
+* the current values of the modem and line status registers. In the absence of
+* data, the device generates a message consisting of these two status bytes
+ * every 40 ms
+ *
+ * Byte 0: Modem Status
+*
+* Offset Description
+* B0 Reserved - must be 1
+* B1 Reserved - must be 0
+* B2 Reserved - must be 0
+* B3 Reserved - must be 0
+* B4 Clear to Send (CTS)
+* B5 Data Set Ready (DSR)
+* B6 Ring Indicator (RI)
+* B7 Receive Line Signal Detect (RLSD)
+*
+* Byte 1: Line Status
+*
+* Offset Description
+* B0 Data Ready (DR)
+* B1 Overrun Error (OE)
+* B2 Parity Error (PE)
+* B3 Framing Error (FE)
+* B4 Break Interrupt (BI)
+* B5 Transmitter Holding Register (THRE)
+* B6 Transmitter Empty (TEMT)
+* B7 Error in RCVR FIFO
+*
+*/
+#define FTDI_RS0_CTS (1 << 4)
+#define FTDI_RS0_DSR (1 << 5)
+#define FTDI_RS0_RI (1 << 6)
+#define FTDI_RS0_RLSD (1 << 7)
+
+#define FTDI_RS_DR 1
+#define FTDI_RS_OE (1<<1)
+#define FTDI_RS_PE (1<<2)
+#define FTDI_RS_FE (1<<3)
+#define FTDI_RS_BI (1<<4)
+#define FTDI_RS_THRE (1<<5)
+#define FTDI_RS_TEMT (1<<6)
+#define FTDI_RS_FIFO (1<<7)
+
+#endif //TUSB_FTDI_SIO_H
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index aa5891ca9..d9e2d3f2f 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -37,11 +37,17 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_DFU_LOG_LEVEL
+ #define CFG_TUD_DFU_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_DFU_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t attrs;
uint8_t alt;
@@ -51,69 +57,65 @@ typedef struct
bool flashing_in_progress;
uint16_t block;
uint16_t length;
-
- CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
} dfu_state_ctx_t;
// Only a single dfu state is allowed
-CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_ctx;
+static dfu_state_ctx_t _dfu_ctx;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(transfer_buf, CFG_TUD_DFU_XFER_BUFSIZE);
+} _dfu_epbuf;
-static void reset_state(void)
-{
+static void reset_state(void) {
_dfu_ctx.state = DFU_IDLE;
_dfu_ctx.status = DFU_STATUS_OK;
_dfu_ctx.flashing_in_progress = false;
}
-static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
-static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
+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);
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
-static tu_lookup_entry_t const _dfu_request_lookup[] =
-{
- { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
- { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
- { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
- { .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
- { .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
- { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
- { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
+tu_static tu_lookup_entry_t const _dfu_request_lookup[] = {
+ { .key = DFU_REQUEST_DETACH , .data = "DETACH" },
+ { .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
+ { .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
+ { .key = DFU_REQUEST_GETSTATUS, .data = "GETSTATUS" },
+ { .key = DFU_REQUEST_CLRSTATUS, .data = "CLRSTATUS" },
+ { .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
+ { .key = DFU_REQUEST_ABORT , .data = "ABORT" },
};
-static tu_lookup_table_t const _dfu_request_table =
-{
+tu_static tu_lookup_table_t const _dfu_request_table = {
.count = TU_ARRAY_SIZE(_dfu_request_lookup),
.items = _dfu_request_lookup
};
-static tu_lookup_entry_t const _dfu_state_lookup[] =
-{
- { .key = APP_IDLE , .data = "APP_IDLE" },
- { .key = APP_DETACH , .data = "APP_DETACH" },
- { .key = DFU_IDLE , .data = "IDLE" },
- { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
- { .key = DFU_DNBUSY , .data = "DNBUSY" },
- { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
- { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
- { .key = DFU_MANIFEST , .data = "MANIFEST" },
- { .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
- { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
- { .key = DFU_ERROR , .data = "ERROR" },
+tu_static tu_lookup_entry_t const _dfu_state_lookup[] = {
+ { .key = APP_IDLE , .data = "APP_IDLE" },
+ { .key = APP_DETACH , .data = "APP_DETACH" },
+ { .key = DFU_IDLE , .data = "IDLE" },
+ { .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
+ { .key = DFU_DNBUSY , .data = "DNBUSY" },
+ { .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
+ { .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
+ { .key = DFU_MANIFEST , .data = "MANIFEST" },
+ { .key = DFU_MANIFEST_WAIT_RESET, .data = "MANIFEST_WAIT_RESET" },
+ { .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
+ { .key = DFU_ERROR , .data = "ERROR" },
};
-static tu_lookup_table_t const _dfu_state_table =
-{
+tu_static tu_lookup_table_t const _dfu_state_table = {
.count = TU_ARRAY_SIZE(_dfu_state_lookup),
.items = _dfu_state_lookup
};
-static tu_lookup_entry_t const _dfu_status_lookup[] =
-{
+tu_static tu_lookup_entry_t const _dfu_status_lookup[] = {
{ .key = DFU_STATUS_OK , .data = "OK" },
{ .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
{ .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
@@ -132,8 +134,7 @@ static tu_lookup_entry_t const _dfu_status_lookup[] =
{ .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
};
-static tu_lookup_table_t const _dfu_status_table =
-{
+tu_static tu_lookup_table_t const _dfu_status_table = {
.count = TU_ARRAY_SIZE(_dfu_status_lookup),
.items = _dfu_status_lookup
};
@@ -143,34 +144,32 @@ static tu_lookup_table_t const _dfu_status_table =
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void dfu_moded_reset(uint8_t rhport)
-{
+void dfu_moded_reset(uint8_t rhport) {
(void) rhport;
-
_dfu_ctx.attrs = 0;
_dfu_ctx.alt = 0;
-
reset_state();
}
-void dfu_moded_init(void)
-{
+void dfu_moded_init(void) {
dfu_moded_reset(0);
}
-uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+bool dfu_moded_deinit(void) {
+ return true;
+}
+
+uint16_t dfu_moded_open(uint8_t rhport, const tusb_desc_interface_t* itf_desc, uint16_t max_len) {
(void) rhport;
//------------- Interface (with Alt) descriptor -------------//
- uint8_t const itf_num = itf_desc->bInterfaceNumber;
+ const uint8_t itf_num = itf_desc->bInterfaceNumber;
uint8_t alt_count = 0;
uint16_t drv_len = 0;
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 0);
- while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
- {
+ while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU) {
TU_ASSERT(max_len > drv_len, 0);
// Alternate must have the same interface number
@@ -181,18 +180,18 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
alt_count++;
drv_len += tu_desc_len(itf_desc);
- itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
+ itf_desc = (const tusb_desc_interface_t*) tu_desc_next(itf_desc);
}
//------------- DFU Functional descriptor -------------//
- tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
+ const tusb_desc_dfu_functional_t*func_desc = (const tusb_desc_dfu_functional_t*) itf_desc;
TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
drv_len += sizeof(tusb_desc_dfu_functional_t);
_dfu_ctx.attrs = func_desc->bAttributes;
// CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
- uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
+ const uint16_t transfer_size = tu_le16toh( tu_unaligned_read16((const uint8_t*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
return drv_len;
@@ -201,99 +200,85 @@ uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// 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 dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
+ TU_LOG_DRV(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
- TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
-
- if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
- {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
// Standard request include GET/SET_INTERFACE
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case TUSB_REQ_SET_INTERFACE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
// Switch Alt interface and reset state machine
- _dfu_ctx.alt = (uint8_t) request->wValue;
+ _dfu_ctx.alt = (uint8_t)request->wValue;
reset_state();
return tud_control_status(rhport, request);
}
- break;
+ break;
case TUSB_REQ_GET_INTERFACE:
- if(stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
}
- break;
+ break;
// unsupported request
default: return false;
}
- }
- else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
- {
- TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
+ } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
+ TU_LOG_DRV(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
// Class request
- switch ( request->bRequest )
- {
+ switch (request->bRequest) {
case DFU_REQUEST_DETACH:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ 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();
+ }
}
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
- }
- break;
+ break;
case DFU_REQUEST_CLRSTATUS:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
reset_state();
tud_control_status(rhport, request);
}
- break;
+ break;
case DFU_REQUEST_GETSTATE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
}
- break;
+ break;
case DFU_REQUEST_ABORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
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);
+ }
}
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
- }
- break;
+ break;
case DFU_REQUEST_UPLOAD:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ 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);
- uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
+ const uint16_t xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_epbuf.transfer_buf,
+ request->wLength);
- return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
+ return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, xfer_len);
}
- break;
+ break;
case DFU_REQUEST_DNLOAD:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
@@ -302,104 +287,86 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_reque
_dfu_ctx.flashing_in_progress = true;
// save block and length for flashing
- _dfu_ctx.block = request->wValue;
+ _dfu_ctx.block = request->wValue;
_dfu_ctx.length = request->wLength;
- if ( request->wLength )
- {
+ if (request->wLength) {
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
- }
- else
- {
+ return tud_control_xfer(rhport, request, _dfu_epbuf.transfer_buf, request->wLength);
+ } else {
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
return tud_control_status(rhport, request);
}
}
- break;
+ break;
case DFU_REQUEST_GETSTATUS:
- switch ( _dfu_ctx.state )
- {
+ switch (_dfu_ctx.state) {
case DFU_DNLOAD_SYNC:
return process_download_get_status(rhport, stage, request);
- break;
+ break;
case DFU_MANIFEST_SYNC:
return process_manifest_get_status(rhport, stage, request);
- break;
+ break;
default:
- if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
- break;
+ if (stage == CONTROL_STAGE_SETUP) {
+ return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ }
+ break;
}
- break;
+ break;
default: return false; // stall unsupported request
}
- }else
- {
+ } else {
return false; // unsupported request
}
return true;
}
-void tud_dfu_finish_flashing(uint8_t status)
-{
+void tud_dfu_finish_flashing(uint8_t status) {
_dfu_ctx.flashing_in_progress = false;
- if ( status == DFU_STATUS_OK )
- {
- if (_dfu_ctx.state == DFU_DNBUSY)
- {
+ if (status == DFU_STATUS_OK) {
+ if (_dfu_ctx.state == DFU_DNBUSY) {
_dfu_ctx.state = DFU_DNLOAD_SYNC;
- }
- else if (_dfu_ctx.state == DFU_MANIFEST)
- {
+ } else if (_dfu_ctx.state == DFU_MANIFEST) {
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
- ? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
+ ? DFU_MANIFEST_SYNC
+ : DFU_MANIFEST_WAIT_RESET;
}
- }
- else
- {
+ } else {
// failed while flashing, move to dfuError
_dfu_ctx.state = DFU_ERROR;
_dfu_ctx.status = (dfu_status_t)status;
}
}
-static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
+static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ if (stage == CONTROL_STAGE_SETUP) {
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
- if ( _dfu_ctx.flashing_in_progress )
- {
+ if (_dfu_ctx.flashing_in_progress) {
next_state = DFU_DNBUSY;
- timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
- }
- else
- {
+ timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t)next_state);
+ } else {
next_state = DFU_DNLOAD_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( _dfu_ctx.flashing_in_progress )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_DNBUSY;
- tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length);
- }else
- {
+ tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_epbuf.transfer_buf, _dfu_ctx.length);
+ } else {
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
}
@@ -407,36 +374,26 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_cont
return true;
}
-static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
+static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ if (stage == CONTROL_STAGE_SETUP) {
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
- if ( _dfu_ctx.flashing_in_progress )
- {
+ if (_dfu_ctx.flashing_in_progress) {
next_state = DFU_MANIFEST;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
- }
- else
- {
+ } else {
next_state = DFU_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
- if ( _dfu_ctx.flashing_in_progress )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
+ if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_MANIFEST;
tud_dfu_manifest_cb(_dfu_ctx.alt);
- }
- else
- {
+ } else {
_dfu_ctx.state = DFU_IDLE;
}
}
@@ -444,15 +401,15 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_cont
return true;
}
-static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
-{
+static bool reply_getstatus(uint8_t rhport, const tusb_control_request_t* request, dfu_state_t state,
+ dfu_status_t status, uint32_t timeout) {
dfu_status_response_t resp;
- resp.bStatus = (uint8_t) status;
+ resp.bStatus = (uint8_t)status;
resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
- resp.bState = (uint8_t) state;
- resp.iString = 0;
+ resp.bState = (uint8_t)state;
+ resp.iString = 0;
return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
diff --git a/src/class/dfu/dfu_device.h b/src/class/dfu/dfu_device.h
index fecf8596f..00c22ea8b 100644
--- a/src/class/dfu/dfu_device.h
+++ b/src/class/dfu/dfu_device.h
@@ -86,6 +86,7 @@ TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
// Internal Class Driver API
//--------------------------------------------------------------------+
void dfu_moded_init(void);
+bool dfu_moded_deinit(void);
void dfu_moded_reset(uint8_t rhport);
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/dfu/dfu_rt_device.c b/src/class/dfu/dfu_rt_device.c
index b9cd6096b..a63c23d9a 100644
--- a/src/class/dfu/dfu_rt_device.c
+++ b/src/class/dfu/dfu_rt_device.c
@@ -37,6 +37,13 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_DFU_RUNTIME_LOG_LEVEL
+ #define CFG_TUD_DFU_RUNTIME_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_DFU_RUNTIME_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
@@ -44,13 +51,15 @@
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void dfu_rtd_init(void)
-{
+void dfu_rtd_init(void) {
}
-void dfu_rtd_reset(uint8_t rhport)
-{
- (void) rhport;
+bool dfu_rtd_deinit(void) {
+ return true;
+}
+
+void dfu_rtd_reset(uint8_t rhport) {
+ (void) rhport;
}
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
@@ -99,7 +108,7 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
{
case DFU_REQUEST_DETACH:
{
- TU_LOG2(" DFU RT Request: DETACH\r\n");
+ TU_LOG_DRV(" DFU RT Request: DETACH\r\n");
tud_control_status(rhport, request);
tud_dfu_runtime_reboot_to_dfu_cb();
}
@@ -107,17 +116,17 @@ bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
case DFU_REQUEST_GETSTATUS:
{
- TU_LOG2(" DFU RT Request: GETSTATUS\r\n");
+ TU_LOG_DRV(" DFU RT Request: GETSTATUS\r\n");
dfu_status_response_t resp;
// Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
- memset(&resp, 0x00, sizeof(dfu_status_response_t));
+ TU_VERIFY(tu_memset_s(&resp, sizeof(resp), 0x00, sizeof(resp))==0);
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
break;
default:
{
- TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
+ TU_LOG_DRV(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
return false; // stall unsupported request
}
}
diff --git a/src/class/dfu/dfu_rt_device.h b/src/class/dfu/dfu_rt_device.h
index babaa8214..67eb26d95 100644
--- a/src/class/dfu/dfu_rt_device.h
+++ b/src/class/dfu/dfu_rt_device.h
@@ -43,6 +43,7 @@ void tud_dfu_runtime_reboot_to_dfu_cb(void);
// Internal Class Driver API
//--------------------------------------------------------------------+
void dfu_rtd_init(void);
+bool dfu_rtd_deinit(void);
void dfu_rtd_reset(uint8_t rhport);
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index 44a464be1..c2434c20d 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -300,6 +300,19 @@ typedef struct TU_ATTR_PACKED
int8_t pan; // using AC Pan
} hid_mouse_report_t;
+
+// Absolute Mouse: same as the Standard (relative) Mouse Report but
+// with int16_t instead of int8_t for X and Y coordinates.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
+ int16_t x; /**< Current x position of the mouse. */
+ int16_t y; /**< Current y position of the mouse. */
+ int8_t wheel; /**< Current delta wheel movement on the mouse. */
+ int8_t pan; // using AC Pan
+} hid_abs_mouse_report_t;
+
+
/// Standard Mouse Buttons Bitmap
typedef enum
{
@@ -313,6 +326,29 @@ typedef enum
/// @}
//--------------------------------------------------------------------+
+// Digitizer Stylus Pen
+//--------------------------------------------------------------------+
+/** \addtogroup ClassDriver_HID_Stylus Stylus
+ * @{ */
+
+// Standard Stylus Pen Report.
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t attr; /**< Attribute mask for describing current status of the stylus pen. */
+ uint16_t x; /**< Current x position of the mouse. */
+ uint16_t y; /**< Current y position of the mouse. */
+} hid_stylus_report_t;
+
+// Standard Stylus Pen Attributes Bitmap.
+typedef enum
+{
+ STYLUS_ATTR_TIP_SWITCH = TU_BIT(0), ///< Tip switch
+ STYLUS_ATTR_IN_RANGE = TU_BIT(1), ///< In-range bit.
+} hid_stylus_attr_bm_t;
+
+/// @}
+
+//--------------------------------------------------------------------+
// Keyboard
//--------------------------------------------------------------------+
/** \addtogroup ClassDriver_HID_Keyboard Keyboard
@@ -353,177 +389,224 @@ typedef enum
//--------------------------------------------------------------------+
// HID KEYCODE
//--------------------------------------------------------------------+
-#define HID_KEY_NONE 0x00
-#define HID_KEY_A 0x04
-#define HID_KEY_B 0x05
-#define HID_KEY_C 0x06
-#define HID_KEY_D 0x07
-#define HID_KEY_E 0x08
-#define HID_KEY_F 0x09
-#define HID_KEY_G 0x0A
-#define HID_KEY_H 0x0B
-#define HID_KEY_I 0x0C
-#define HID_KEY_J 0x0D
-#define HID_KEY_K 0x0E
-#define HID_KEY_L 0x0F
-#define HID_KEY_M 0x10
-#define HID_KEY_N 0x11
-#define HID_KEY_O 0x12
-#define HID_KEY_P 0x13
-#define HID_KEY_Q 0x14
-#define HID_KEY_R 0x15
-#define HID_KEY_S 0x16
-#define HID_KEY_T 0x17
-#define HID_KEY_U 0x18
-#define HID_KEY_V 0x19
-#define HID_KEY_W 0x1A
-#define HID_KEY_X 0x1B
-#define HID_KEY_Y 0x1C
-#define HID_KEY_Z 0x1D
-#define HID_KEY_1 0x1E
-#define HID_KEY_2 0x1F
-#define HID_KEY_3 0x20
-#define HID_KEY_4 0x21
-#define HID_KEY_5 0x22
-#define HID_KEY_6 0x23
-#define HID_KEY_7 0x24
-#define HID_KEY_8 0x25
-#define HID_KEY_9 0x26
-#define HID_KEY_0 0x27
-#define HID_KEY_ENTER 0x28
-#define HID_KEY_ESCAPE 0x29
-#define HID_KEY_BACKSPACE 0x2A
-#define HID_KEY_TAB 0x2B
-#define HID_KEY_SPACE 0x2C
-#define HID_KEY_MINUS 0x2D
-#define HID_KEY_EQUAL 0x2E
-#define HID_KEY_BRACKET_LEFT 0x2F
-#define HID_KEY_BRACKET_RIGHT 0x30
-#define HID_KEY_BACKSLASH 0x31
-#define HID_KEY_EUROPE_1 0x32
-#define HID_KEY_SEMICOLON 0x33
-#define HID_KEY_APOSTROPHE 0x34
-#define HID_KEY_GRAVE 0x35
-#define HID_KEY_COMMA 0x36
-#define HID_KEY_PERIOD 0x37
-#define HID_KEY_SLASH 0x38
-#define HID_KEY_CAPS_LOCK 0x39
-#define HID_KEY_F1 0x3A
-#define HID_KEY_F2 0x3B
-#define HID_KEY_F3 0x3C
-#define HID_KEY_F4 0x3D
-#define HID_KEY_F5 0x3E
-#define HID_KEY_F6 0x3F
-#define HID_KEY_F7 0x40
-#define HID_KEY_F8 0x41
-#define HID_KEY_F9 0x42
-#define HID_KEY_F10 0x43
-#define HID_KEY_F11 0x44
-#define HID_KEY_F12 0x45
-#define HID_KEY_PRINT_SCREEN 0x46
-#define HID_KEY_SCROLL_LOCK 0x47
-#define HID_KEY_PAUSE 0x48
-#define HID_KEY_INSERT 0x49
-#define HID_KEY_HOME 0x4A
-#define HID_KEY_PAGE_UP 0x4B
-#define HID_KEY_DELETE 0x4C
-#define HID_KEY_END 0x4D
-#define HID_KEY_PAGE_DOWN 0x4E
-#define HID_KEY_ARROW_RIGHT 0x4F
-#define HID_KEY_ARROW_LEFT 0x50
-#define HID_KEY_ARROW_DOWN 0x51
-#define HID_KEY_ARROW_UP 0x52
-#define HID_KEY_NUM_LOCK 0x53
-#define HID_KEY_KEYPAD_DIVIDE 0x54
-#define HID_KEY_KEYPAD_MULTIPLY 0x55
-#define HID_KEY_KEYPAD_SUBTRACT 0x56
-#define HID_KEY_KEYPAD_ADD 0x57
-#define HID_KEY_KEYPAD_ENTER 0x58
-#define HID_KEY_KEYPAD_1 0x59
-#define HID_KEY_KEYPAD_2 0x5A
-#define HID_KEY_KEYPAD_3 0x5B
-#define HID_KEY_KEYPAD_4 0x5C
-#define HID_KEY_KEYPAD_5 0x5D
-#define HID_KEY_KEYPAD_6 0x5E
-#define HID_KEY_KEYPAD_7 0x5F
-#define HID_KEY_KEYPAD_8 0x60
-#define HID_KEY_KEYPAD_9 0x61
-#define HID_KEY_KEYPAD_0 0x62
-#define HID_KEY_KEYPAD_DECIMAL 0x63
-#define HID_KEY_EUROPE_2 0x64
-#define HID_KEY_APPLICATION 0x65
-#define HID_KEY_POWER 0x66
-#define HID_KEY_KEYPAD_EQUAL 0x67
-#define HID_KEY_F13 0x68
-#define HID_KEY_F14 0x69
-#define HID_KEY_F15 0x6A
-#define HID_KEY_F16 0x6B
-#define HID_KEY_F17 0x6C
-#define HID_KEY_F18 0x6D
-#define HID_KEY_F19 0x6E
-#define HID_KEY_F20 0x6F
-#define HID_KEY_F21 0x70
-#define HID_KEY_F22 0x71
-#define HID_KEY_F23 0x72
-#define HID_KEY_F24 0x73
-#define HID_KEY_EXECUTE 0x74
-#define HID_KEY_HELP 0x75
-#define HID_KEY_MENU 0x76
-#define HID_KEY_SELECT 0x77
-#define HID_KEY_STOP 0x78
-#define HID_KEY_AGAIN 0x79
-#define HID_KEY_UNDO 0x7A
-#define HID_KEY_CUT 0x7B
-#define HID_KEY_COPY 0x7C
-#define HID_KEY_PASTE 0x7D
-#define HID_KEY_FIND 0x7E
-#define HID_KEY_MUTE 0x7F
-#define HID_KEY_VOLUME_UP 0x80
-#define HID_KEY_VOLUME_DOWN 0x81
-#define HID_KEY_LOCKING_CAPS_LOCK 0x82
-#define HID_KEY_LOCKING_NUM_LOCK 0x83
-#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
-#define HID_KEY_KEYPAD_COMMA 0x85
-#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
-#define HID_KEY_KANJI1 0x87
-#define HID_KEY_KANJI2 0x88
-#define HID_KEY_KANJI3 0x89
-#define HID_KEY_KANJI4 0x8A
-#define HID_KEY_KANJI5 0x8B
-#define HID_KEY_KANJI6 0x8C
-#define HID_KEY_KANJI7 0x8D
-#define HID_KEY_KANJI8 0x8E
-#define HID_KEY_KANJI9 0x8F
-#define HID_KEY_LANG1 0x90
-#define HID_KEY_LANG2 0x91
-#define HID_KEY_LANG3 0x92
-#define HID_KEY_LANG4 0x93
-#define HID_KEY_LANG5 0x94
-#define HID_KEY_LANG6 0x95
-#define HID_KEY_LANG7 0x96
-#define HID_KEY_LANG8 0x97
-#define HID_KEY_LANG9 0x98
-#define HID_KEY_ALTERNATE_ERASE 0x99
-#define HID_KEY_SYSREQ_ATTENTION 0x9A
-#define HID_KEY_CANCEL 0x9B
-#define HID_KEY_CLEAR 0x9C
-#define HID_KEY_PRIOR 0x9D
-#define HID_KEY_RETURN 0x9E
-#define HID_KEY_SEPARATOR 0x9F
-#define HID_KEY_OUT 0xA0
-#define HID_KEY_OPER 0xA1
-#define HID_KEY_CLEAR_AGAIN 0xA2
-#define HID_KEY_CRSEL_PROPS 0xA3
-#define HID_KEY_EXSEL 0xA4
-// RESERVED 0xA5-DF
-#define HID_KEY_CONTROL_LEFT 0xE0
-#define HID_KEY_SHIFT_LEFT 0xE1
-#define HID_KEY_ALT_LEFT 0xE2
-#define HID_KEY_GUI_LEFT 0xE3
-#define HID_KEY_CONTROL_RIGHT 0xE4
-#define HID_KEY_SHIFT_RIGHT 0xE5
-#define HID_KEY_ALT_RIGHT 0xE6
-#define HID_KEY_GUI_RIGHT 0xE7
+#define HID_KEY_NONE 0x00
+#define HID_KEY_A 0x04
+#define HID_KEY_B 0x05
+#define HID_KEY_C 0x06
+#define HID_KEY_D 0x07
+#define HID_KEY_E 0x08
+#define HID_KEY_F 0x09
+#define HID_KEY_G 0x0A
+#define HID_KEY_H 0x0B
+#define HID_KEY_I 0x0C
+#define HID_KEY_J 0x0D
+#define HID_KEY_K 0x0E
+#define HID_KEY_L 0x0F
+#define HID_KEY_M 0x10
+#define HID_KEY_N 0x11
+#define HID_KEY_O 0x12
+#define HID_KEY_P 0x13
+#define HID_KEY_Q 0x14
+#define HID_KEY_R 0x15
+#define HID_KEY_S 0x16
+#define HID_KEY_T 0x17
+#define HID_KEY_U 0x18
+#define HID_KEY_V 0x19
+#define HID_KEY_W 0x1A
+#define HID_KEY_X 0x1B
+#define HID_KEY_Y 0x1C
+#define HID_KEY_Z 0x1D
+#define HID_KEY_1 0x1E
+#define HID_KEY_2 0x1F
+#define HID_KEY_3 0x20
+#define HID_KEY_4 0x21
+#define HID_KEY_5 0x22
+#define HID_KEY_6 0x23
+#define HID_KEY_7 0x24
+#define HID_KEY_8 0x25
+#define HID_KEY_9 0x26
+#define HID_KEY_0 0x27
+#define HID_KEY_ENTER 0x28
+#define HID_KEY_ESCAPE 0x29
+#define HID_KEY_BACKSPACE 0x2A
+#define HID_KEY_TAB 0x2B
+#define HID_KEY_SPACE 0x2C
+#define HID_KEY_MINUS 0x2D
+#define HID_KEY_EQUAL 0x2E
+#define HID_KEY_BRACKET_LEFT 0x2F
+#define HID_KEY_BRACKET_RIGHT 0x30
+#define HID_KEY_BACKSLASH 0x31
+#define HID_KEY_EUROPE_1 0x32
+#define HID_KEY_SEMICOLON 0x33
+#define HID_KEY_APOSTROPHE 0x34
+#define HID_KEY_GRAVE 0x35
+#define HID_KEY_COMMA 0x36
+#define HID_KEY_PERIOD 0x37
+#define HID_KEY_SLASH 0x38
+#define HID_KEY_CAPS_LOCK 0x39
+#define HID_KEY_F1 0x3A
+#define HID_KEY_F2 0x3B
+#define HID_KEY_F3 0x3C
+#define HID_KEY_F4 0x3D
+#define HID_KEY_F5 0x3E
+#define HID_KEY_F6 0x3F
+#define HID_KEY_F7 0x40
+#define HID_KEY_F8 0x41
+#define HID_KEY_F9 0x42
+#define HID_KEY_F10 0x43
+#define HID_KEY_F11 0x44
+#define HID_KEY_F12 0x45
+#define HID_KEY_PRINT_SCREEN 0x46
+#define HID_KEY_SCROLL_LOCK 0x47
+#define HID_KEY_PAUSE 0x48
+#define HID_KEY_INSERT 0x49
+#define HID_KEY_HOME 0x4A
+#define HID_KEY_PAGE_UP 0x4B
+#define HID_KEY_DELETE 0x4C
+#define HID_KEY_END 0x4D
+#define HID_KEY_PAGE_DOWN 0x4E
+#define HID_KEY_ARROW_RIGHT 0x4F
+#define HID_KEY_ARROW_LEFT 0x50
+#define HID_KEY_ARROW_DOWN 0x51
+#define HID_KEY_ARROW_UP 0x52
+#define HID_KEY_NUM_LOCK 0x53
+#define HID_KEY_KEYPAD_DIVIDE 0x54
+#define HID_KEY_KEYPAD_MULTIPLY 0x55
+#define HID_KEY_KEYPAD_SUBTRACT 0x56
+#define HID_KEY_KEYPAD_ADD 0x57
+#define HID_KEY_KEYPAD_ENTER 0x58
+#define HID_KEY_KEYPAD_1 0x59
+#define HID_KEY_KEYPAD_2 0x5A
+#define HID_KEY_KEYPAD_3 0x5B
+#define HID_KEY_KEYPAD_4 0x5C
+#define HID_KEY_KEYPAD_5 0x5D
+#define HID_KEY_KEYPAD_6 0x5E
+#define HID_KEY_KEYPAD_7 0x5F
+#define HID_KEY_KEYPAD_8 0x60
+#define HID_KEY_KEYPAD_9 0x61
+#define HID_KEY_KEYPAD_0 0x62
+#define HID_KEY_KEYPAD_DECIMAL 0x63
+#define HID_KEY_EUROPE_2 0x64
+#define HID_KEY_APPLICATION 0x65
+#define HID_KEY_POWER 0x66
+#define HID_KEY_KEYPAD_EQUAL 0x67
+#define HID_KEY_F13 0x68
+#define HID_KEY_F14 0x69
+#define HID_KEY_F15 0x6A
+#define HID_KEY_F16 0x6B
+#define HID_KEY_F17 0x6C
+#define HID_KEY_F18 0x6D
+#define HID_KEY_F19 0x6E
+#define HID_KEY_F20 0x6F
+#define HID_KEY_F21 0x70
+#define HID_KEY_F22 0x71
+#define HID_KEY_F23 0x72
+#define HID_KEY_F24 0x73
+#define HID_KEY_EXECUTE 0x74
+#define HID_KEY_HELP 0x75
+#define HID_KEY_MENU 0x76
+#define HID_KEY_SELECT 0x77
+#define HID_KEY_STOP 0x78
+#define HID_KEY_AGAIN 0x79
+#define HID_KEY_UNDO 0x7A
+#define HID_KEY_CUT 0x7B
+#define HID_KEY_COPY 0x7C
+#define HID_KEY_PASTE 0x7D
+#define HID_KEY_FIND 0x7E
+#define HID_KEY_MUTE 0x7F
+#define HID_KEY_VOLUME_UP 0x80
+#define HID_KEY_VOLUME_DOWN 0x81
+#define HID_KEY_LOCKING_CAPS_LOCK 0x82
+#define HID_KEY_LOCKING_NUM_LOCK 0x83
+#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
+#define HID_KEY_KEYPAD_COMMA 0x85
+#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
+#define HID_KEY_KANJI1 0x87
+#define HID_KEY_KANJI2 0x88
+#define HID_KEY_KANJI3 0x89
+#define HID_KEY_KANJI4 0x8A
+#define HID_KEY_KANJI5 0x8B
+#define HID_KEY_KANJI6 0x8C
+#define HID_KEY_KANJI7 0x8D
+#define HID_KEY_KANJI8 0x8E
+#define HID_KEY_KANJI9 0x8F
+#define HID_KEY_LANG1 0x90
+#define HID_KEY_LANG2 0x91
+#define HID_KEY_LANG3 0x92
+#define HID_KEY_LANG4 0x93
+#define HID_KEY_LANG5 0x94
+#define HID_KEY_LANG6 0x95
+#define HID_KEY_LANG7 0x96
+#define HID_KEY_LANG8 0x97
+#define HID_KEY_LANG9 0x98
+#define HID_KEY_ALTERNATE_ERASE 0x99
+#define HID_KEY_SYSREQ_ATTENTION 0x9A
+#define HID_KEY_CANCEL 0x9B
+#define HID_KEY_CLEAR 0x9C
+#define HID_KEY_PRIOR 0x9D
+#define HID_KEY_RETURN 0x9E
+#define HID_KEY_SEPARATOR 0x9F
+#define HID_KEY_OUT 0xA0
+#define HID_KEY_OPER 0xA1
+#define HID_KEY_CLEAR_AGAIN 0xA2
+#define HID_KEY_CRSEL_PROPS 0xA3
+#define HID_KEY_EXSEL 0xA4
+// RESERVED 0xA5-AF
+#define HID_KEY_KEYPAD_00 0xB0
+#define HID_KEY_KEYPAD_000 0xB1
+#define HID_KEY_THOUSANDS_SEPARATOR 0xB2
+#define HID_KEY_DECIMAL_SEPARATOR 0xB3
+#define HID_KEY_CURRENCY_UNIT 0xB4
+#define HID_KEY_CURRENCY_SUBUNIT 0xB5
+#define HID_KEY_KEYPAD_LEFT_PARENTHESIS 0xB6
+#define HID_KEY_KEYPAD_RIGHT_PARENTHESIS 0xB7
+#define HID_KEY_KEYPAD_LEFT_BRACE 0xB8
+#define HID_KEY_KEYPAD_RIGHT_BRACE 0xB9
+#define HID_KEY_KEYPAD_TAB 0xBA
+#define HID_KEY_KEYPAD_BACKSPACE 0xBB
+#define HID_KEY_KEYPAD_A 0xBC
+#define HID_KEY_KEYPAD_B 0xBD
+#define HID_KEY_KEYPAD_C 0xBE
+#define HID_KEY_KEYPAD_D 0xBF
+#define HID_KEY_KEYPAD_E 0xC0
+#define HID_KEY_KEYPAD_F 0xC1
+#define HID_KEY_KEYPAD_XOR 0xC2
+#define HID_KEY_KEYPAD_CARET 0xC3
+#define HID_KEY_KEYPAD_PERCENT 0xC4
+#define HID_KEY_KEYPAD_LESS_THAN 0xC5
+#define HID_KEY_KEYPAD_GREATER_THAN 0xC6
+#define HID_KEY_KEYPAD_AMPERSAND 0xC7
+#define HID_KEY_KEYPAD_DOUBLE_AMPERSAND 0xC8
+#define HID_KEY_KEYPAD_VERTICAL_BAR 0xC9
+#define HID_KEY_KEYPAD_DOUBLE_VERTICAL_BAR 0xCA
+#define HID_KEY_KEYPAD_COLON 0xCB
+#define HID_KEY_KEYPAD_HASH 0xCC
+#define HID_KEY_KEYPAD_SPACE 0xCD
+#define HID_KEY_KEYPAD_AT 0xCE
+#define HID_KEY_KEYPAD_EXCLAMATION 0xCF
+#define HID_KEY_KEYPAD_MEMORY_STORE 0xD0
+#define HID_KEY_KEYPAD_MEMORY_RECALL 0xD1
+#define HID_KEY_KEYPAD_MEMORY_CLEAR 0xD2
+#define HID_KEY_KEYPAD_MEMORY_ADD 0xD3
+#define HID_KEY_KEYPAD_MEMORY_SUBTRACT 0xD4
+#define HID_KEY_KEYPAD_MEMORY_MULTIPLY 0xD5
+#define HID_KEY_KEYPAD_MEMORY_DIVIDE 0xD6
+#define HID_KEY_KEYPAD_PLUS_MINUS 0xD7
+#define HID_KEY_KEYPAD_CLEAR 0xD8
+#define HID_KEY_KEYPAD_CLEAR_ENTRY 0xD9
+#define HID_KEY_KEYPAD_BINARY 0xDA
+#define HID_KEY_KEYPAD_OCTAL 0xDB
+#define HID_KEY_KEYPAD_DECIMAL_2 0xDC
+#define HID_KEY_KEYPAD_HEXADECIMAL 0xDD
+// RESERVED 0xDE-DF
+#define HID_KEY_CONTROL_LEFT 0xE0
+#define HID_KEY_SHIFT_LEFT 0xE1
+#define HID_KEY_ALT_LEFT 0xE2
+#define HID_KEY_GUI_LEFT 0xE3
+#define HID_KEY_CONTROL_RIGHT 0xE4
+#define HID_KEY_SHIFT_RIGHT 0xE5
+#define HID_KEY_ALT_RIGHT 0xE6
+#define HID_KEY_GUI_RIGHT 0xE7
//--------------------------------------------------------------------+
@@ -680,35 +763,58 @@ enum {
//--------------------------------------------------------------------+
// Usage Table
+/* Usage Types Data
+ Sel Selector Array
+ SV Static Value Constant, Variable, Absolute
+ SF Static Flag Constant, Variable, Absolute
+ DV Dynamic Value Constant, Variable, Absolute
+ DF Dynamic Flag Constant, Variable, Absolute
+*/
+/* Usage Types Collection
+ NAry Named Array Logical
+ CA Collection Application Application
+ CL Collection Logical Logical
+ CP Collection Physical Physical
+ US Usage Switch Logical
+ UM Usage Modifier Logical
+*/
//--------------------------------------------------------------------+
/// HID Usage Table - Table 1: Usage Page Summary
enum {
- HID_USAGE_PAGE_DESKTOP = 0x01,
- HID_USAGE_PAGE_SIMULATE = 0x02,
- HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
- HID_USAGE_PAGE_SPORT = 0x04,
- HID_USAGE_PAGE_GAME = 0x05,
- HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
- HID_USAGE_PAGE_KEYBOARD = 0x07,
- HID_USAGE_PAGE_LED = 0x08,
- HID_USAGE_PAGE_BUTTON = 0x09,
- HID_USAGE_PAGE_ORDINAL = 0x0a,
- HID_USAGE_PAGE_TELEPHONY = 0x0b,
- HID_USAGE_PAGE_CONSUMER = 0x0c,
- HID_USAGE_PAGE_DIGITIZER = 0x0d,
- HID_USAGE_PAGE_PID = 0x0f,
- HID_USAGE_PAGE_UNICODE = 0x10,
- HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
- HID_USAGE_PAGE_MEDICAL = 0x40,
- HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
- HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
- HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
- HID_USAGE_PAGE_SCALE = 0x8d,
- HID_USAGE_PAGE_MSR = 0x8e,
- HID_USAGE_PAGE_CAMERA = 0x90,
- HID_USAGE_PAGE_ARCADE = 0x91,
- HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
+ HID_USAGE_PAGE_DESKTOP = 0x01,
+ HID_USAGE_PAGE_SIMULATE = 0x02,
+ HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
+ HID_USAGE_PAGE_SPORT = 0x04,
+ HID_USAGE_PAGE_GAME = 0x05,
+ HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
+ HID_USAGE_PAGE_KEYBOARD = 0x07,
+ HID_USAGE_PAGE_LED = 0x08,
+ HID_USAGE_PAGE_BUTTON = 0x09,
+ HID_USAGE_PAGE_ORDINAL = 0x0a,
+ HID_USAGE_PAGE_TELEPHONY = 0x0b,
+ HID_USAGE_PAGE_CONSUMER = 0x0c,
+ HID_USAGE_PAGE_DIGITIZER = 0x0d,
+ HID_USAGE_PAGE_PID = 0x0f,
+ HID_USAGE_PAGE_UNICODE = 0x10,
+ HID_USAGE_PAGE_SOC = 0x11,
+ HID_USAGE_PAGE_EYE_AND_HEAD_TRACKERS = 0x12,
+ // 0x13 is reserved
+ HID_USAGE_PAGE_AUXILIARY_DISPLAY = 0x14,
+ // 0x15 - 0x1f is reserved
+ HID_USAGE_PAGE_SENSORS = 0x20,
+ // 0x21 - 0x3f is reserved
+ 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_BARCODE_SCANNER = 0x8c,
+ HID_USAGE_PAGE_SCALE = 0x8d,
+ HID_USAGE_PAGE_MSR = 0x8e,
+ HID_USAGE_PAGE_CAMERA = 0x90,
+ HID_USAGE_PAGE_ARCADE = 0x91,
+ HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page
+ HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
};
/// HID Usage Table - Table 6: Generic Desktop Page
@@ -784,11 +890,9 @@ enum {
HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7
};
-
/// HID Usage Table: Consumer Page (0x0C)
/// Only contains controls that supported by Windows (whole list is too long)
-enum
-{
+enum {
// Generic Control
HID_USAGE_CONSUMER_CONTROL = 0x0001,
@@ -844,6 +948,281 @@ enum
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
};
+/// HID Usage Table: Digitizer Page (0x0D)
+enum {
+ HID_USAGE_DIGITIZER_UNDEFINED = 0x00,
+ HID_USAGE_DIGITIZER_DIGITIZER = 0x01, // CA
+ HID_USAGE_DIGITIZER_PEN = 0x02, // CA
+ HID_USAGE_DIGITIZER_LIGHT_PEN = 0x03, // CA
+ HID_USAGE_DIGITIZER_TOUCH_SCREEN = 0x04, // CA
+ HID_USAGE_DIGITIZER_TOUCH_PAD = 0x05, // CA
+ HID_USAGE_DIGITIZER_WHITEBOARD = 0x06, // CA
+ HID_USAGE_DIGITIZER_COORDINATE_MEASURING_MACHINE = 0x07, // CA
+ HID_USAGE_DIGITIZER_3D_DIGITIZER = 0x08, // CA
+ HID_USAGE_DIGITIZER_STEREO_PLOTTER = 0x09, // CA
+ HID_USAGE_DIGITIZER_ARTICULATED_ARM = 0x0A, // CA
+ HID_USAGE_DIGITIZER_ARMATURE = 0x0B, // CA
+ HID_USAGE_DIGITIZER_MULTIPLE_POINT_DIGITIZER = 0x0C, // CA
+ HID_USAGE_DIGITIZER_FREE_SPACE_WAND = 0x0D, // CA
+ HID_USAGE_DIGITIZER_DEVICE_CONFIGURATION = 0x0E, // CA
+ HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_DIGITIZER = 0x0F, // CA
+ // Reserved (0x10 - 0x1F)
+ HID_USAGE_DIGITIZER_STYLUS = 0x20, // CA/CL
+ HID_USAGE_DIGITIZER_PUCK = 0x21, // CL
+ HID_USAGE_DIGITIZER_FINGER = 0x22, // CL
+ HID_USAGE_DIGITIZER_DEVICE_SETTINGS = 0x23, // CL
+ HID_USAGE_DIGITIZER_CHARACTER_GESTURE = 0x24, // CL
+ // Reserved (0x25 - 0x2F)
+ HID_USAGE_DIGITIZER_TIP_PRESSURE = 0x30, // DV
+ HID_USAGE_DIGITIZER_BARREL_PRESSURE = 0x31, // DV
+ HID_USAGE_DIGITIZER_IN_RANGE = 0x32, // MC
+ HID_USAGE_DIGITIZER_TOUCH = 0x33, // MC
+ HID_USAGE_DIGITIZER_UNTOUCH = 0x34, // OSC
+ HID_USAGE_DIGITIZER_TAP = 0x35, // OSC
+ HID_USAGE_DIGITIZER_QUALITY = 0x36, // DV
+ HID_USAGE_DIGITIZER_DATA_VALID = 0x37, // MC
+ HID_USAGE_DIGITIZER_TRANSDUCER_INDEX = 0x38, // DV
+ HID_USAGE_DIGITIZER_TABLET_FUNCTION_KEYS = 0x39, // CL
+ HID_USAGE_DIGITIZER_PROGRAM_CHANGE_KEYS = 0x3A, // CL
+ HID_USAGE_DIGITIZER_BATTERY_STRENGTH = 0x3B, // DV
+ HID_USAGE_DIGITIZER_INVERT = 0x3C, // MC
+ HID_USAGE_DIGITIZER_X_TILT = 0x3D, // DV
+ HID_USAGE_DIGITIZER_Y_TILT = 0x3E, // DV
+ HID_USAGE_DIGITIZER_AZIMUTH = 0x3F, // DV
+ HID_USAGE_DIGITIZER_ALTITUDE = 0x40, // DV
+ HID_USAGE_DIGITIZER_TWIST = 0x41, // DV
+ HID_USAGE_DIGITIZER_TIP_SWITCH = 0x42, // MC
+ HID_USAGE_DIGITIZER_SECONDARY_TIP_SWITCH = 0x43, // MC
+ HID_USAGE_DIGITIZER_BARREL_SWITCH = 0x44, // MC
+ HID_USAGE_DIGITIZER_ERASER = 0x45, // MC
+ HID_USAGE_DIGITIZER_TABLET_PICK = 0x46, // MC
+ HID_USAGE_DIGITIZER_TOUCH_VALID = 0x47, // MC
+ HID_USAGE_DIGITIZER_WIDTH = 0x48, // DV
+ HID_USAGE_DIGITIZER_HEIGHT = 0x49, // DV
+ // Reserved (0x4A - 0x50)
+ HID_USAGE_DIGITIZER_CONTACT_IDENTIFIER = 0x51, // DV
+ HID_USAGE_DIGITIZER_DEVICE_MODE = 0x52, // DV
+ HID_USAGE_DIGITIZER_DEVICE_IDENTIFIER = 0x53, // DV/SV
+ HID_USAGE_DIGITIZER_CONTACT_COUNT = 0x54, // DV
+ HID_USAGE_DIGITIZER_CONTACT_COUNT_MAXIMUM = 0x55, // SV
+ HID_USAGE_DIGITIZER_SCAN_TIME = 0x56, // DV
+ HID_USAGE_DIGITIZER_SURFACE_SWITCH = 0x57, // DF
+ HID_USAGE_DIGITIZER_BUTTON_SWITCH = 0x58, // DF
+ HID_USAGE_DIGITIZER_PAD_TYPE = 0x59, // SF
+ HID_USAGE_DIGITIZER_TRANSDUCER_SERIAL_NUMBER = 0x5B, // SV
+ HID_USAGE_DIGITIZER_PREFERRED_COLOR = 0x5C, // DV
+ HID_USAGE_DIGITIZER_PREFERRED_COLOR_LOCKED = 0x5D, // MC
+ HID_USAGE_DIGITIZER_PREFERRED_LINE_WIDTH = 0x5E, // DV
+ HID_USAGE_DIGITIZER_PREFERRED_LINE_WIDTH_LOCKED = 0x5F, // MC
+ HID_USAGE_DIGITIZER_LATENCY_MODE = 0x60, // DF
+ HID_USAGE_DIGITIZER_GESTURE_CHARACTER_QUALITY = 0x61, // DV
+ HID_USAGE_DIGITIZER_CHARACTER_GESTURE_DATA_LENGTH = 0x62, // DV
+ HID_USAGE_DIGITIZER_CHARACTER_GESTURE_DATA = 0x63, // DV
+ HID_USAGE_DIGITIZER_GESTURE_CHARACTER_ENCODING = 0x64, // NAry
+ HID_USAGE_DIGITIZER_UTF8_CHARACTER_GESTURE_ENCODING = 0x65, // Sel
+ HID_USAGE_DIGITIZER_UTF16_LE_CHARACTER_GESTURE_ENCODING = 0x66, // Sel
+ HID_USAGE_DIGITIZER_UTF16_BE_CHARACTER_GESTURE_ENCODING = 0x67, // Sel
+ HID_USAGE_DIGITIZER_UTF32_LE_CHARACTER_GESTURE_ENCODING = 0x68, // Sel
+ HID_USAGE_DIGITIZER_UTF32_BE_CHARACTER_GESTURE_ENCODING = 0x69, // Sel
+ HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_VENDOR_ID = 0x6A, // SV
+ HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_VERSION = 0x6B, // SV
+ HID_USAGE_DIGITIZER_CAPACITIVE_HEAT_MAP_FRAME_DATA = 0x6C, // DV
+ HID_USAGE_DIGITIZER_GESTURE_CHARACTER_ENABLE = 0x6D, // DF
+ HID_USAGE_DIGITIZER_TRANSDUCER_SERIAL_NUMBER_PART2 = 0x6E, // SV
+ HID_USAGE_DIGITIZER_NO_PREFERRED_COLOR = 0x6F, // DF
+ HID_USAGE_DIGITIZER_PREFERRED_LINE_STYLE = 0x70, // NAry
+ HID_USAGE_DIGITIZER_PREFERRED_LINE_STYLE_LOCKED = 0x71, // MC
+ HID_USAGE_DIGITIZER_INK = 0x72, // Sel
+ HID_USAGE_DIGITIZER_PENCIL = 0x73, // Sel
+ HID_USAGE_DIGITIZER_HIGHLIGHTER = 0x74, // Sel
+ HID_USAGE_DIGITIZER_CHISEL_MARKER = 0x75, // Sel
+ HID_USAGE_DIGITIZER_BRUSH = 0x76, // Sel
+ HID_USAGE_DIGITIZER_NO_PREFERENCE = 0x77, // Sel
+ // Reserved (0x78 - 0x7F)
+ HID_USAGE_DIGITIZER_DIGITIZER_DIAGNOSTIC = 0x80, // CL
+ HID_USAGE_DIGITIZER_DIGITIZER_ERROR = 0x81, // NAry
+ HID_USAGE_DIGITIZER_ERR_NORMAL_STATUS = 0x82, // Sel
+ HID_USAGE_DIGITIZER_ERR_TRANSDUCERS_EXCEEDED = 0x83, // Sel
+ HID_USAGE_DIGITIZER_ERR_FULL_TRANS_FEATURES_UNAVAILABLE = 0x84, // Sel
+ HID_USAGE_DIGITIZER_ERR_CHARGE_LOW = 0x85, // Sel
+ // Reserved (0x86 - 0x8F)
+ HID_USAGE_DIGITIZER_TRANSDUCER_SOFTWARE_INFO = 0x90, // CL
+ HID_USAGE_DIGITIZER_TRANSDUCER_VENDOR_ID = 0x91, // SV
+ HID_USAGE_DIGITIZER_TRANSDUCER_PRODUCT_ID = 0x92, // SV
+ HID_USAGE_DIGITIZER_DEVICE_SUPPORTED_PROTOCOLS = 0x93, // NAry/CL
+ HID_USAGE_DIGITIZER_TRANSDUCER_SUPPORTED_PROTOCOLS = 0x94, // NAry/CL
+ HID_USAGE_DIGITIZER_NO_PROTOCOL = 0x95, // Sel
+ HID_USAGE_DIGITIZER_WACOM_AES_PROTOCOL = 0x96, // Sel
+ HID_USAGE_DIGITIZER_USI_PROTOCOL = 0x97, // Sel
+ HID_USAGE_DIGITIZER_MICROSOFT_PEN_PROTOCOL = 0x98, // Sel
+ // Reserved (0x99 - 0x9F)
+ HID_USAGE_DIGITIZER_SUPPORTED_REPORT_RATES = 0xA0, // SV/CL
+ HID_USAGE_DIGITIZER_REPORT_RATE = 0xA1, // DV
+ HID_USAGE_DIGITIZER_TRANSDUCER_CONNECTED = 0xA2, // SF
+ HID_USAGE_DIGITIZER_SWITCH_DISABLED = 0xA3, // Sel
+ HID_USAGE_DIGITIZER_SWITCH_UNIMPLEMENTED = 0xA4, // Sel
+ HID_USAGE_DIGITIZER_TRANSDUCER_SWITCHES = 0xA5, // CL
+ HID_USAGE_DIGITIZER_TRANSDUCER_INDEX_SELECTOR = 0xA6, // DV
+ // Reserved (0xA7 - 0xAF)
+ HID_USAGE_DIGITIZER_BUTTON_PRESS_THRESHOLD = 0xB0, // DV
+
+ // Reserved (0xB1 - 0xFFFF)
+};
+
+/// HID Usage Table: Physical Input Device Page (0x0F)
+enum {
+ HID_USAGE_PID_UNDEFINED = 0x00,
+ HID_USAGE_PID_PHYSICAL_INPUT_DEVICE = 0x01,
+ HID_USAGE_PID_NORMAL = 0x20,
+ HID_USAGE_PID_SET_EFFECT_REPORT = 0x21,
+ HID_USAGE_PID_EFFECT_PARAMETER_BLOCK_INDEX = 0x22,
+ HID_USAGE_PID_PARAMETER_BLOCK_OFFSET = 0x23,
+ HID_USAGE_PID_ROM_FLAG = 0x24,
+ HID_USAGE_PID_EFFECT_TYPE = 0x25,
+ HID_USAGE_PID_ET_CONSTANTFORCE = 0x26,
+ HID_USAGE_PID_ET_RAMP = 0x27,
+ HID_USAGE_PID_ET_CUSTOMFORCE = 0x28,
+ HID_USAGE_PID_ET_SQUARE = 0x30,
+ HID_USAGE_PID_ET_SINE = 0x31,
+ HID_USAGE_PID_ET_TRIANGLE = 0x32,
+ HID_USAGE_PID_ET_SAWTOOTH_UP = 0x33,
+ HID_USAGE_PID_ET_SAWTOOTH_DOWN = 0x34,
+ HID_USAGE_PID_ET_SPRING = 0x40,
+ HID_USAGE_PID_ET_DAMPER = 0x41,
+ HID_USAGE_PID_ET_INERTIA = 0x42,
+ HID_USAGE_PID_ET_FRICTION = 0x43,
+ HID_USAGE_PID_DURATION = 0x50,
+ HID_USAGE_PID_SAMPLE_PERIOD = 0x51,
+ HID_USAGE_PID_GAIN = 0x52,
+ HID_USAGE_PID_TRIGGER_BUTTON = 0x53,
+ HID_USAGE_PID_TRIGGER_REPEAT_INTERVAL = 0x54,
+ HID_USAGE_PID_AXES_ENABLE = 0x55,
+ HID_USAGE_PID_DIRECTION_ENABLE = 0x56,
+ HID_USAGE_PID_DIRECTION = 0x57,
+ HID_USAGE_PID_TYPE_SPECIFIC_BLOCK_OFFSET = 0x58,
+ HID_USAGE_PID_BLOCK_TYPE = 0x59,
+ HID_USAGE_PID_SET_ENVELOPE_REPORT = 0x5a,
+ HID_USAGE_PID_ATTACK_LEVEL = 0x5b,
+ HID_USAGE_PID_ATTACK_TIME = 0x5c,
+ HID_USAGE_PID_FADE_LEVEL = 0x5d,
+ HID_USAGE_PID_FADE_TIME = 0x5e,
+ HID_USAGE_PID_SET_CONDITION_REPORT = 0x5f,
+ HID_USAGE_PID_CENTERPOINT_OFFSET = 0x60,
+ HID_USAGE_PID_POSITIVE_COEFFICIENT = 0x61,
+ HID_USAGE_PID_NEGATIVE_COEFFICIENT = 0x62,
+ HID_USAGE_PID_POSITIVE_SATURATION = 0x63,
+ HID_USAGE_PID_NEGATIVE_SATURATION = 0x64,
+ HID_USAGE_PID_DEAD_BAND = 0x65,
+ HID_USAGE_PID_DOWNLOAD_FORCE_SAMPLE = 0x66,
+ HID_USAGE_PID_ISOCH_CUSTOMFORCE_ENABLE = 0x67,
+ HID_USAGE_PID_CUSTOMFORCE_DATA_REPORT = 0x68,
+ HID_USAGE_PID_CUSTOMFORCE_DATA = 0x69,
+ HID_USAGE_PID_CUSTOMFORCE_VENDOR_DEFINED_DATA = 0x6a,
+ HID_USAGE_PID_SET_CUSTOMFORCE_REPORT = 0x6b,
+ HID_USAGE_PID_CUSTOMFORCE_DATA_OFFSET = 0x6c,
+ HID_USAGE_PID_SAMPLE_COUNT = 0x6d,
+ HID_USAGE_PID_SET_PERIODIC_REPORT = 0x6e,
+ HID_USAGE_PID_OFFSET = 0x6f,
+ HID_USAGE_PID_MAGNITUDE = 0x70,
+ HID_USAGE_PID_PHASE = 0x71,
+ HID_USAGE_PID_PERIOD = 0x72,
+ HID_USAGE_PID_SET_CONSTANTFORCE_REPORT = 0x73,
+ HID_USAGE_PID_SET_RAMPFORCE_REPORT = 0x74,
+ HID_USAGE_PID_RAMP_START = 0x75,
+ HID_USAGE_PID_RAMP_END = 0x76,
+ HID_USAGE_PID_EFFECT_OPERATION_REPORT = 0x77,
+ HID_USAGE_PID_EFFECT_OPERATION = 0x78,
+ HID_USAGE_PID_OP_EFFECT_START = 0x79,
+ HID_USAGE_PID_OP_EFFECT_START_SOLO = 0x7a,
+ HID_USAGE_PID_OP_EFFECT_STOP = 0x7b,
+ HID_USAGE_PID_LOOP_COUNT = 0x7c,
+ HID_USAGE_PID_DEVICE_GAIN_REPORT = 0x7d,
+ HID_USAGE_PID_DEVICE_GAIN = 0x7e,
+ HID_USAGE_PID_PARAMETER_BLOCK_POOLS_REPORT = 0x7f,
+ HID_USAGE_PID_RAM_POOL_SIZE = 0x80,
+ HID_USAGE_PID_ROM_POOL_SIZE = 0x81,
+ HID_USAGE_PID_ROM_EFFECT_BLOCK_COUNT = 0x82,
+ HID_USAGE_PID_SIMULTANEOUS_EFFECTS_MAX = 0x83,
+ HID_USAGE_PID_POOL_ALIGNMENT = 0x84,
+ HID_USAGE_PID_PARAMETER_BLOCK_MOVE_REPORT = 0x85,
+ HID_USAGE_PID_MOVE_SOURCE = 0x86,
+ HID_USAGE_PID_MOVE_DESTINATION = 0x87,
+ HID_USAGE_PID_MOVE_LENGTH = 0x88,
+ HID_USAGE_PID_EFFECT_PARAMETER_BLOCK_LOAD_REPORT = 0x89,
+ HID_USAGE_PID_EFFECT_PARAMETER_BLOCK_LOAD_STATUS = 0x8b,
+ HID_USAGE_PID_BLOCK_LOAD_SUCCESS = 0x8c,
+ HID_USAGE_PID_BLOCK_LOAD_FULL = 0x8d,
+ HID_USAGE_PID_BLOCK_LOAD_ERROR = 0x8e,
+ HID_USAGE_PID_BLOCK_HANDLE = 0x8f,
+ HID_USAGE_PID_EFFECT_PARAMETER_BLOCK_FREE_REPORT = 0x90,
+ HID_USAGE_PID_TYPE_SPECIFIC_BLOCK_HANDLE = 0x91,
+ HID_USAGE_PID_PID_STATE_REPORT = 0x92,
+ HID_USAGE_PID_EFFECT_PLAYING = 0x94,
+ HID_USAGE_PID_PID_DEVICE_CONTROL_REPORT = 0x95,
+ HID_USAGE_PID_PID_DEVICE_CONTROL = 0x96,
+ HID_USAGE_PID_DC_ENABLE_ACTUATORS = 0x97,
+ HID_USAGE_PID_DC_DISABLE_ACTUATORS = 0x98,
+ HID_USAGE_PID_DC_STOP_ALL_EFFECTS = 0x99,
+ HID_USAGE_PID_DC_RESET = 0x9a,
+ HID_USAGE_PID_DC_PAUSE = 0x9b,
+ HID_USAGE_PID_DC_CONTINUE = 0x9c,
+ HID_USAGE_PID_DEVICE_PAUSED = 0x9f,
+ HID_USAGE_PID_ACTUATORS_ENABLED = 0xa0,
+ HID_USAGE_PID_SAFETY_SWITCH = 0xa4,
+ HID_USAGE_PID_ACTUATOR_OVERRIDE_SWITCH = 0xa5,
+ HID_USAGE_PID_ACTUATOR_POWER = 0xa6,
+ HID_USAGE_PID_START_DELAY = 0xa7,
+ HID_USAGE_PID_PARAMETER_BLOCK_SIZE = 0xa8,
+ HID_USAGE_PID_DEVICEMANAGED_POOL = 0xa9,
+ HID_USAGE_PID_SHARED_PARAMETER_BLOCKS = 0xaa,
+ HID_USAGE_PID_CREATE_NEW_EFFECT_PARAMETER_BLOCK_REPORT = 0xab,
+ HID_USAGE_PID_RAM_POOL_AVAILABLE = 0xac,
+};
+
+/// HID Usage Table - Lighting And Illumination Page (0x59)
+enum {
+ HID_USAGE_LIGHTING_LAMP_ARRAY = 0x01,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT = 0x02,
+ HID_USAGE_LIGHTING_LAMP_COUNT = 0x03,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS = 0x04,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS = 0x05,
+ HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS = 0x06,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_KIND = 0x07,
+ HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
+ HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT = 0x20,
+ HID_USAGE_LIGHTING_LAMP_ID = 0x21,
+ HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT = 0x22,
+ HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS = 0x23,
+ HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS = 0x24,
+ HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS = 0x25,
+ HID_USAGE_LIGHTING_LAMP_PURPOSES = 0x26,
+ HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS = 0x27,
+ HID_USAGE_LIGHTING_RED_LEVEL_COUNT = 0x28,
+ HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT = 0x29,
+ HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT = 0x2A,
+ HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT = 0x2B,
+ HID_USAGE_LIGHTING_IS_PROGRAMMABLE = 0x2C,
+ HID_USAGE_LIGHTING_INPUT_BINDING = 0x2D,
+ HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT = 0x50,
+ HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL = 0x51,
+ HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL = 0x52,
+ HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL = 0x53,
+ HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL = 0x54,
+ HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS = 0x55,
+ HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT = 0x60,
+ HID_USAGE_LIGHTING_LAMP_ID_START = 0x61,
+ HID_USAGE_LIGHTING_LAMP_ID_END = 0x62,
+ HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT = 0x70,
+ HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
+};
+
+/// HID Usage Table: FIDO Alliance Page (0xF1D0)
+enum {
+ HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
+ HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report
+ HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report
+};
+
/*--------------------------------------------------------------------
* ASCII to KEYCODE Conversion
* Expand to array of [128][2] (shift, keycode)
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index 8077e4deb..b4f24902b 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -39,96 +39,114 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
- uint8_t ep_out; // optional Out endpoint
- uint8_t itf_protocol; // Boot mouse or keyboard
+ uint8_t ep_out; // optional Out endpoint
+ uint8_t itf_protocol; // Boot mouse or keyboard
+ uint16_t report_desc_len;
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
uint8_t idle_rate; // up to application to handle idle rate
- uint16_t report_desc_len;
-
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
// TODO save hid descriptor since host can specifically request this after enumeration
// Note: HID descriptor may be not available from application after enumeration
- tusb_hid_descriptor_hid_t const * hid_descriptor;
+ const tusb_hid_descriptor_hid_t*hid_descriptor;
} hidd_interface_t;
-CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
+typedef struct {
+ TUD_EPBUF_DEF(ctrl , CFG_TUD_HID_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epin , CFG_TUD_HID_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_HID_EP_BUFSIZE);
+} hidd_epbuf_t;
+
+static hidd_interface_t _hidd_itf[CFG_TUD_HID];
+CFG_TUD_MEM_SECTION static hidd_epbuf_t _hidd_epbuf[CFG_TUD_HID];
/*------------- Helpers -------------*/
-static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
-{
- for (uint8_t i=0; i < CFG_TUD_HID; i++ )
- {
- if ( itf_num == _hidd_itf[i].itf_num ) return i;
- }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t get_index_by_itfnum(uint8_t itf_num) {
+ for (uint8_t i = 0; i < CFG_TUD_HID; i++) {
+ if (itf_num == _hidd_itf[i].itf_num) {
+ return i;
+ }
+ }
+ return 0xFF;
+}
+
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
+ (void) instance;
+ (void) protocol;
+}
+
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate) {
+ (void) instance;
+ (void) idle_rate;
+ return true;
+}
+
+TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) {
+ (void) instance;
+ (void) report;
+ (void) len;
+}
- return 0xFF;
+// Invoked when a transfer wasn't successful
+TU_ATTR_WEAK void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes) {
+ (void) instance;
+ (void) report_type;
+ (void) report;
+ (void) xferred_bytes;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_hid_n_ready(uint8_t instance)
-{
+bool tud_hid_n_ready(uint8_t instance) {
uint8_t const rhport = 0;
uint8_t const ep_in = _hidd_itf[instance].ep_in;
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
}
-bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len)
-{
- uint8_t const rhport = 0;
- hidd_interface_t * p_hid = &_hidd_itf[instance];
+bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) {
+ TU_VERIFY(instance < CFG_TUD_HID);
+ const uint8_t rhport = 0;
+ hidd_interface_t *p_hid = &_hidd_itf[instance];
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
// claim endpoint
- TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+ TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
// prepare data
- if (report_id)
- {
- len = tu_min16(len, CFG_TUD_HID_EP_BUFSIZE-1);
-
- p_hid->epin_buf[0] = report_id;
- memcpy(p_hid->epin_buf+1, report, len);
+ if (report_id) {
+ p_epbuf->epin[0] = report_id;
+ TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
len++;
- }else
- {
- // If report id = 0, skip ID field
- len = tu_min16(len, CFG_TUD_HID_EP_BUFSIZE);
- memcpy(p_hid->epin_buf, report, len);
+ } else {
+ TU_VERIFY(0 == tu_memcpy_s(p_epbuf->epin, CFG_TUD_HID_EP_BUFSIZE, report, len));
}
- return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
+ return usbd_edpt_xfer(rhport, p_hid->ep_in, p_epbuf->epin, len);
}
-uint8_t tud_hid_n_interface_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
return _hidd_itf[instance].itf_protocol;
}
-uint8_t tud_hid_n_get_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_get_protocol(uint8_t instance) {
return _hidd_itf[instance].protocol_mode;
}
-bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]) {
hid_keyboard_report_t report;
-
report.modifier = modifier;
report.reserved = 0;
- if ( keycode )
- {
- memcpy(report.keycode, keycode, 6);
- }else
- {
+ if (keycode) {
+ memcpy(report.keycode, keycode, sizeof(report.keycode));
+ } else {
tu_memclr(report.keycode, 6);
}
@@ -136,33 +154,51 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
}
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
- uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
- hid_mouse_report_t report =
- {
+ uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+ hid_mouse_report_t report = {
.buttons = buttons,
- .x = x,
- .y = y,
- .wheel = vertical,
- .pan = horizontal
+ .x = x,
+ .y = y,
+ .wheel = vertical,
+ .pan = horizontal
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
}
-bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
- int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
-{
- hid_gamepad_report_t report =
- {
- .x = x,
- .y = y,
- .z = z,
- .rz = rz,
- .rx = rx,
- .ry = ry,
- .hat = hat,
+bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id,
+ uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+ hid_abs_mouse_report_t report = {
.buttons = buttons,
+ .x = x,
+ .y = y,
+ .wheel = vertical,
+ .pan = horizontal
+ };
+ return tud_hid_n_report(instance, report_id, &report, sizeof(report));
+}
+
+bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
+ int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
+ hid_gamepad_report_t report = {
+ .x = x,
+ .y = y,
+ .z = z,
+ .rz = rz,
+ .rx = rx,
+ .ry = ry,
+ .hat = hat,
+ .buttons = buttons,
+ };
+
+ return tud_hid_n_report(instance, report_id, &report, sizeof(report));
+}
+
+bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
+ hid_stylus_report_t report = {
+ .attr = attrs,
+ .x = x,
+ .y = y,
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
@@ -171,19 +207,20 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
//--------------------------------------------------------------------+
// USBD-CLASS API
//--------------------------------------------------------------------+
-void hidd_init(void)
-{
+void hidd_init(void) {
hidd_reset(0);
}
-void hidd_reset(uint8_t rhport)
-{
- (void) rhport;
+bool hidd_deinit(void) {
+ return true;
+}
+
+void hidd_reset(uint8_t rhport) {
+ (void)rhport;
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
-uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
-{
+uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
// len = interface + hid + n*endpoints
@@ -192,45 +229,41 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1
TU_ASSERT(max_len >= drv_len, 0);
// Find available interface
- hidd_interface_t * p_hid = NULL;
+ hidd_interface_t *p_hid;
uint8_t hid_id;
- for(hid_id=0; hid_id<CFG_TUD_HID; hid_id++)
- {
- if ( _hidd_itf[hid_id].ep_in == 0 )
- {
- p_hid = &_hidd_itf[hid_id];
+ for (hid_id = 0; hid_id < CFG_TUD_HID; hid_id++) {
+ p_hid = &_hidd_itf[hid_id];
+ if (p_hid->ep_in == 0) {
break;
}
}
- TU_ASSERT(p_hid, 0);
+ TU_ASSERT(hid_id < CFG_TUD_HID, 0);
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_id];
- uint8_t const *p_desc = (uint8_t const *) desc_itf;
+ uint8_t const *p_desc = (uint8_t const *)desc_itf;
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(HID_DESC_TYPE_HID == tu_desc_type(p_desc), 0);
- p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
+ p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *)p_desc;
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
- if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+ if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) {
+ p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+ }
p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
- p_hid->itf_num = desc_itf->bInterfaceNumber;
+ p_hid->itf_num = desc_itf->bInterfaceNumber;
// Use offsetof to avoid pointer to the odd/misaligned address
- p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
+ p_hid->report_desc_len = tu_unaligned_read16((uint8_t const *)p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
// Prepare for output endpoint
- if (p_hid->ep_out)
- {
- if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
- {
- TU_LOG_FAILED();
- TU_BREAKPOINT();
- }
+ if (p_hid->ep_out) {
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE), drv_len);
}
return drv_len;
@@ -239,178 +272,148 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1
// 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 hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
- uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+ uint8_t const hid_itf = get_index_by_itfnum((uint8_t)request->wIndex);
TU_VERIFY(hid_itf < CFG_TUD_HID);
+ hidd_interface_t *p_hid = &_hidd_itf[hid_itf];
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[hid_itf];
- hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
-
- if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
- {
+ if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
//------------- STD Request -------------//
- if ( stage == CONTROL_STAGE_SETUP )
- {
- uint8_t const desc_type = tu_u16_high(request->wValue);
- //uint8_t const desc_index = tu_u16_low (request->wValue);
+ if (stage == CONTROL_STAGE_SETUP) {
+ uint8_t const desc_type = tu_u16_high(request->wValue);
+ // uint8_t const desc_index = tu_u16_low (request->wValue);
- if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
- {
+ if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID) {
TU_VERIFY(p_hid->hid_descriptor);
- TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
- }
- else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
- {
- uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
- tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_report, p_hid->report_desc_len);
- }
- else
- {
+ TU_VERIFY(tud_control_xfer(rhport, request, (void *)(uintptr_t)p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
+ } else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT) {
+ uint8_t const *desc_report = tud_hid_descriptor_report_cb(hid_itf);
+ tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_report, p_hid->report_desc_len);
+ } else {
return false; // stall unsupported request
}
}
- }
- else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
- {
+ } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
//------------- Class Specific Request -------------//
- switch( request->bRequest )
- {
+ switch (request->bRequest) {
case HID_REQ_CONTROL_GET_REPORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
uint8_t const report_type = tu_u16_high(request->wValue);
- uint8_t const report_id = tu_u16_low(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t* report_buf = p_hid->epin_buf;
+ uint8_t* report_buf = p_epbuf->ctrl;
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
-
uint16_t xferlen = 0;
// If host request a specific Report ID, add ID to as 1 byte of response
- if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
- {
+ if ((report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1)) {
*report_buf++ = report_id;
req_len--;
-
xferlen++;
}
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
- TU_ASSERT( xferlen > 0 );
+ TU_ASSERT(xferlen > 0);
- tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
+ tud_control_xfer(rhport, request, p_epbuf->ctrl, xferlen);
}
- break;
+ break;
- case HID_REQ_CONTROL_SET_REPORT:
- if ( stage == CONTROL_STAGE_SETUP )
- {
- TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
- tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
+ case HID_REQ_CONTROL_SET_REPORT:
+ if (stage == CONTROL_STAGE_SETUP) {
+ TU_VERIFY(request->wLength <= CFG_TUD_HID_EP_BUFSIZE);
+ tud_control_xfer(rhport, request, p_epbuf->ctrl, request->wLength);
+ } else if (stage == CONTROL_STAGE_ACK) {
uint8_t const report_type = tu_u16_high(request->wValue);
- uint8_t const report_id = tu_u16_low(request->wValue);
+ uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t const* report_buf = p_hid->epout_buf;
+ uint8_t const* report_buf = p_epbuf->ctrl;
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
// If host request a specific Report ID, extract report ID in buffer before invoking callback
- if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
- {
+ if ((report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0])) {
report_buf++;
report_len--;
}
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
}
- break;
+ break;
case HID_REQ_CONTROL_SET_IDLE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
p_hid->idle_rate = tu_u16_high(request->wValue);
- if ( tud_hid_set_idle_cb )
- {
- // stall request if callback return false
- TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
- }
-
+ TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); // stall if false
tud_control_status(rhport, request);
}
- break;
+ break;
case HID_REQ_CONTROL_GET_IDLE:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
// TODO idle rate of report
tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
}
- break;
+ break;
case HID_REQ_CONTROL_GET_PROTOCOL:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
}
- break;
+ break;
case HID_REQ_CONTROL_SET_PROTOCOL:
- if ( stage == CONTROL_STAGE_SETUP )
- {
+ if (stage == CONTROL_STAGE_SETUP) {
tud_control_status(rhport, request);
- }
- else if ( stage == CONTROL_STAGE_ACK )
- {
+ } else if (stage == CONTROL_STAGE_ACK) {
p_hid->protocol_mode = (uint8_t) request->wValue;
- if (tud_hid_set_protocol_cb)
- {
- tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
- }
+ tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
}
- break;
+ break;
- default: return false; // stall unsupported request
+ default:
+ return false; // stall unsupported request
}
- }else
- {
+ } else {
return false; // stall unsupported request
}
return true;
}
-bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) result;
-
- uint8_t instance = 0;
- hidd_interface_t * p_hid = _hidd_itf;
+bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ uint8_t instance;
+ hidd_interface_t *p_hid;
// Identify which interface to use
- for (instance = 0; instance < CFG_TUD_HID; instance++)
- {
+ for (instance = 0; instance < CFG_TUD_HID; instance++) {
p_hid = &_hidd_itf[instance];
- if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
+ if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in)) {
+ break;
+ }
}
TU_ASSERT(instance < CFG_TUD_HID);
+ hidd_epbuf_t *p_epbuf = &_hidd_epbuf[instance];
- // Sent report successfully
- if (ep_addr == p_hid->ep_in)
- {
- if (tud_hid_report_complete_cb)
- {
- tud_hid_report_complete_cb(instance, p_hid->epin_buf, (/*uint16_t*/ uint8_t) xferred_bytes);
+ if (ep_addr == p_hid->ep_in) {
+ // Input report
+ if (XFER_RESULT_SUCCESS == result) {
+ tud_hid_report_complete_cb(instance, p_epbuf->epin, (uint16_t) xferred_bytes);
+ } else {
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_epbuf->epin, (uint16_t) xferred_bytes);
}
- }
- // Received report
- else if (ep_addr == p_hid->ep_out)
- {
- tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (uint16_t) xferred_bytes);
- TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
+ } else {
+ // Output report
+ if (XFER_RESULT_SUCCESS == result) {
+ tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t)xferred_bytes);
+ } else {
+ tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_epbuf->epout, (uint16_t) xferred_bytes);
+ }
+
+ // prepare for new transfer
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_epbuf->epout, CFG_TUD_HID_EP_BUFSIZE));
}
return true;
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index 3143b1024..fc1dbcbd8 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_HID_DEVICE_H_
-#define _TUSB_HID_DEVICE_H_
+#ifndef TUSB_HID_DEVICE_H_
+#define TUSB_HID_DEVICE_H_
#include "hid.h"
@@ -48,8 +48,7 @@
#endif
//--------------------------------------------------------------------+
-// Application API (Multiple Instances)
-// CFG_TUD_HID > 1
+// Application API (Multiple Instances) i.e. CFG_TUD_HID > 1
//--------------------------------------------------------------------+
// Check if the interface is ready to use
@@ -66,29 +65,64 @@ bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, u
// KEYBOARD: convenient helper to send keyboard report if application
// use template layout report as defined by hid_keyboard_report_t
-bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]);
// MOUSE: convenient helper to send mouse report if application
// use template layout report as defined by hid_mouse_report_t
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+// ABSOLUTE MOUSE: convenient helper to send absolute mouse report if application
+// use template layout report as defined by hid_abs_mouse_report_t
+bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
+
// Gamepad: convenient helper to send gamepad report if application
// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+// STYLUS PEN: convenient helper to send absolute stylus pen report if application
+bool tud_hid_n_stylus_report(uint8_t instance, uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y);
+
//--------------------------------------------------------------------+
// Application API (Single Port)
//--------------------------------------------------------------------+
-static inline bool tud_hid_ready(void);
-static inline uint8_t tud_hid_interface_protocol(void);
-static inline uint8_t tud_hid_get_protocol(void);
-static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len);
-static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
-static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
-static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_ready(void) {
+ return tud_hid_n_ready(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_interface_protocol(void) {
+ return tud_hid_n_interface_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_get_protocol(void) {
+ return tud_hid_n_get_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) {
+ return tud_hid_n_report(0, report_id, report, len);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, const uint8_t keycode[6]) {
+ return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+ return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+ return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
+ return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_stylus_report(uint8_t report_id, uint8_t attrs, uint16_t x, uint16_t y) {
+ return tud_hid_n_stylus_report(0, report_id, attrs, x, y);
+}
//--------------------------------------------------------------------+
-// Callbacks (Weak is optional)
+// Application Callbacks
//--------------------------------------------------------------------+
// Invoked when received GET HID REPORT DESCRIPTOR request
@@ -101,61 +135,25 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance);
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
// Invoked when received SET_REPORT control request or
-// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+// received data on OUT endpoint (Report ID = 0, Type = OUTPUT)
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
// Invoked when received SET_PROTOCOL request
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
-TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
// Invoked when received SET_IDLE request. return false will stall the request
-// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate = 0 : only send report if there is changes, i.e. skip duplication
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
+bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
-TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, /*uint16_t*/ uint8_t len );
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline bool tud_hid_ready(void)
-{
- return tud_hid_n_ready(0);
-}
-
-static inline uint8_t tud_hid_interface_protocol(void)
-{
- return tud_hid_n_interface_protocol(0);
-}
-
-static inline uint8_t tud_hid_get_protocol(void)
-{
- return tud_hid_n_get_protocol(0);
-}
-
-static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len)
-{
- return tud_hid_n_report(0, report_id, report, len);
-}
-
-static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
- return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
-}
-
-static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
- return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
-}
-
-static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
-{
- return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
-}
+// Invoked when a transfer wasn't successful
+void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes);
/* --------------------------------------------------------------------+
* HID Report Descriptor Template
@@ -182,7 +180,7 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
- /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \
+ /* 8 bits Modifier Keys (Shift, Control, Alt) */ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
HID_USAGE_MIN ( 224 ) ,\
HID_USAGE_MAX ( 231 ) ,\
@@ -266,6 +264,90 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_COLLECTION_END , \
HID_COLLECTION_END \
+// 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_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_LOGICAL_MIN ( 0 ), \
+ HID_LOGICAL_MAX ( 1 ), \
+ HID_REPORT_SIZE ( 1 ), \
+ HID_REPORT_COUNT( 2 ), \
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
+ HID_REPORT_SIZE ( 1 ), \
+ HID_REPORT_COUNT( 6 ), \
+ HID_INPUT ( HID_CONSTANT | HID_ARRAY | HID_ABSOLUTE), \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), \
+ HID_PHYSICAL_MAX_N( 0x7fff, 2 ), \
+ HID_LOGICAL_MAX_N ( 0x7fff, 2 ), \
+ HID_REPORT_SIZE ( 16 ), \
+ HID_REPORT_COUNT( 1 ), \
+ HID_UNIT_EXPONENT( 0x0f ), \
+ HID_UNIT ( HID_VARIABLE | HID_NONLINEAR ), \
+ HID_PHYSICAL_MIN( 0 ), \
+ HID_PHYSICAL_MAX( 0 ), \
+ HID_USAGE ( HID_USAGE_DESKTOP_X ), \
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
+ HID_USAGE ( HID_USAGE_DESKTOP_Y ), \
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), \
+ HID_COLLECTION_END , \
+ HID_COLLECTION_END \
+
+// Absolute Mouse Report Descriptor Template
+#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */\
+ __VA_ARGS__ \
+ HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
+ HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
+ HID_USAGE_MIN ( 1 ) ,\
+ HID_USAGE_MAX ( 5 ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX ( 1 ) ,\
+ /* Left, Right, Middle, Backward, Forward buttons */ \
+ HID_REPORT_COUNT( 5 ) ,\
+ HID_REPORT_SIZE ( 1 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* 3 bit padding */ \
+ HID_REPORT_COUNT( 1 ) ,\
+ HID_REPORT_SIZE ( 3 ) ,\
+ HID_INPUT ( HID_CONSTANT ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
+ /* X, Y absolute position [0, 32767] */ \
+ HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
+ HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
+ HID_LOGICAL_MIN ( 0x00 ) ,\
+ HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
+ HID_REPORT_SIZE ( 16 ) ,\
+ HID_REPORT_COUNT ( 2 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* Vertical wheel scroll [-127, 127] */ \
+ HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
+ HID_LOGICAL_MIN ( 0x81 ) ,\
+ HID_LOGICAL_MAX ( 0x7f ) ,\
+ HID_REPORT_COUNT( 1 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
+ /* Horizontal wheel scroll [-127, 127] */ \
+ HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
+ HID_LOGICAL_MIN ( 0x81 ), \
+ HID_LOGICAL_MAX ( 0x7f ), \
+ HID_REPORT_COUNT( 1 ), \
+ HID_REPORT_SIZE ( 8 ), \
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
+ HID_COLLECTION_END , \
+ HID_COLLECTION_END \
+
// Consumer Control Report Descriptor Template
#define TUD_HID_REPORT_DESC_CONSUMER(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
@@ -352,6 +434,31 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
HID_COLLECTION_END \
+// FIDO U2F Authenticator Descriptor Template
+// - 1st parameter is report size, which is 64 bytes maximum in U2F
+// - 2nd parameter is HID_REPORT_ID(n) (optional)
+#define TUD_HID_REPORT_DESC_FIDO_U2F(report_size, ...) \
+ HID_USAGE_PAGE_N ( HID_USAGE_PAGE_FIDO, 2 ) ,\
+ HID_USAGE ( HID_USAGE_FIDO_U2FHID ) ,\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
+ /* Report ID if any */ \
+ __VA_ARGS__ \
+ /* Usage Data In */ \
+ HID_USAGE ( HID_USAGE_FIDO_DATA_IN ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_REPORT_COUNT ( report_size ) ,\
+ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ /* Usage Data Out */ \
+ HID_USAGE ( HID_USAGE_FIDO_DATA_OUT ) ,\
+ HID_LOGICAL_MIN ( 0 ) ,\
+ HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
+ HID_REPORT_SIZE ( 8 ) ,\
+ HID_REPORT_COUNT ( report_size ) ,\
+ HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
+ HID_COLLECTION_END \
+
// HID Generic Input & Output
// - 1st parameter is report size (mandatory)
// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
@@ -377,10 +484,183 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
HID_COLLECTION_END \
+// HID Lighting and Illumination Report Descriptor Template
+// - 1st parameter is report id (required)
+// Creates 6 report ids for lighting HID usages in the following order:
+// report_id+0: HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT
+// report_id+1: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT
+// report_id+2: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT
+// report_id+3: HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT
+// report_id+4: HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT
+// report_id+5: HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT
+#define TUD_HID_REPORT_DESC_LIGHTING(report_id) \
+ HID_USAGE_PAGE ( HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY ),\
+ HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
+ /* Lamp Array Attributes Report */ \
+ HID_REPORT_ID (report_id ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_KIND ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
+ HID_REPORT_SIZE ( 32 ),\
+ HID_REPORT_COUNT ( 5 ),\
+ HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Attributes Request Report */ \
+ HID_REPORT_ID ( report_id + 1 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Attributes Response Report */ \
+ HID_REPORT_ID ( report_id + 2 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_PURPOSES ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
+ HID_REPORT_SIZE ( 32 ),\
+ HID_REPORT_COUNT ( 5 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_IS_PROGRAMMABLE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INPUT_BINDING ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 6 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Multi-Update Report */ \
+ HID_REPORT_ID ( report_id + 3 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 8 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 2 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 8 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 32 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Range Update Report */ \
+ HID_REPORT_ID ( report_id + 4 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 8 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_START ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_END ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 65535, 3 ),\
+ HID_REPORT_SIZE ( 16 ),\
+ HID_REPORT_COUNT ( 2 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX_N ( 255, 2 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 4 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ /* Lamp Array Control Report */ \
+ HID_REPORT_ID ( report_id + 5 ) \
+ HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT ),\
+ HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
+ HID_USAGE ( HID_USAGE_LIGHTING_AUTONOMOUS_MODE ),\
+ HID_LOGICAL_MIN ( 0 ),\
+ HID_LOGICAL_MAX ( 1 ),\
+ HID_REPORT_SIZE ( 8 ),\
+ HID_REPORT_COUNT ( 1 ),\
+ HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+ HID_COLLECTION_END ,\
+ HID_COLLECTION_END \
+
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void hidd_init (void);
+bool hidd_deinit (void);
void hidd_reset (uint8_t rhport);
uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
@@ -390,4 +670,4 @@ bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t ev
}
#endif
-#endif /* _TUSB_HID_DEVICE_H_ */
+#endif
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index ca745464c..a3cc7d6d7 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -29,362 +29,501 @@
#if (CFG_TUH_ENABLED && CFG_TUH_HID)
#include "host/usbh.h"
-#include "host/usbh_classdriver.h"
+#include "host/usbh_pvt.h"
#include "hid_host.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUH_HID_LOG_LEVEL
+ #define CFG_TUH_HID_LOG_LEVEL CFG_TUH_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_HID_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-
-typedef struct
-{
+typedef struct {
+ uint8_t daddr;
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
+ bool mounted; // Enumeration is complete
uint8_t itf_protocol; // None, Keyboard, Mouse
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
- uint8_t report_desc_type;
+ uint8_t report_desc_type;
uint16_t report_desc_len;
uint16_t epin_size;
uint16_t epout_size;
-
- uint8_t epin_buf[CFG_TUH_HID_EPIN_BUFSIZE];
- uint8_t epout_buf[CFG_TUH_HID_EPOUT_BUFSIZE];
} hidh_interface_t;
-typedef struct
-{
- uint8_t inst_count;
- hidh_interface_t instances[CFG_TUH_HID];
-} hidh_device_t;
+typedef struct {
+ TUH_EPBUF_DEF(epin, CFG_TUH_HID_EPIN_BUFSIZE);
+ TUH_EPBUF_DEF(epout, CFG_TUH_HID_EPOUT_BUFSIZE);
+} hidh_epbuf_t;
+
+static hidh_interface_t _hidh_itf[CFG_TUH_HID];
+CFG_TUH_MEM_SECTION static hidh_epbuf_t _hidh_epbuf[CFG_TUH_HID];
-static hidh_device_t _hidh_dev[CFG_TUH_DEVICE_MAX];
+static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
+
+//--------------------------------------------------------------------+
+// Helper
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) {
+ TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL);
+ hidh_interface_t* p_hid = &_hidh_itf[idx];
+ return (p_hid->daddr == daddr) ? p_hid : NULL;
+}
-//------------- Internal prototypes -------------//
+TU_ATTR_ALWAYS_INLINE static inline hidh_epbuf_t* get_hid_epbuf(uint8_t idx) {
+ return &_hidh_epbuf[idx];
+}
-// Get HID device & interface
-TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr);
-TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance);
-static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf);
-static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr);
+// Get instance ID by endpoint address
+static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
+ for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
+ hidh_interface_t const* p_hid = &_hidh_itf[idx];
+ if (p_hid->daddr == daddr &&
+ (p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr)) {
+ return idx;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+static hidh_interface_t* find_new_itf(void) {
+ for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
+ if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
+ }
+ return NULL;
+}
//--------------------------------------------------------------------+
// Interface API
//--------------------------------------------------------------------+
+uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
+ uint8_t count = 0;
+ for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
+ if (_hidh_itf[i].daddr == daddr) count++;
+ }
+ return count;
+}
+
+uint8_t tuh_hid_itf_get_total_count(void) {
+ uint8_t count = 0;
+ for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
+ if (_hidh_itf[i].daddr != 0) count++;
+ }
+ return count;
+}
-uint8_t tuh_hid_instance_count(uint8_t dev_addr)
-{
- return get_dev(dev_addr)->inst_count;
+bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ return p_hid->mounted;
+}
+
+bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid && info);
+
+ info->daddr = daddr;
+
+ // re-construct descriptor
+ tusb_desc_interface_t* desc = &info->desc;
+ desc->bLength = sizeof(tusb_desc_interface_t);
+ desc->bDescriptorType = TUSB_DESC_INTERFACE;
+
+ desc->bInterfaceNumber = p_hid->itf_num;
+ desc->bAlternateSetting = 0;
+ desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
+ desc->bInterfaceClass = TUSB_CLASS_HID;
+ desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE);
+ desc->bInterfaceProtocol = p_hid->itf_protocol;
+ desc->iInterface = 0; // not used yet
+
+ return true;
}
-bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- return (hid_itf->ep_in != 0) || (hid_itf->ep_out != 0);
+uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) {
+ for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
+ hidh_interface_t const* p_hid = &_hidh_itf[idx];
+ if (p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
+ }
+
+ return TUSB_INDEX_INVALID_8;
}
-uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- return hid_itf->itf_protocol;
+uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ return p_hid ? p_hid->itf_protocol : 0;
}
//--------------------------------------------------------------------+
// Control Endpoint API
//--------------------------------------------------------------------+
-
-uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- return hid_itf->protocol_mode;
+uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ return p_hid ? p_hid->protocol_mode : 0;
}
-static void set_protocol_complete(tuh_xfer_t* xfer)
-{
- uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- uint8_t const daddr = xfer->daddr;
- uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num);
- hidh_interface_t* hid_itf = get_instance(daddr, instance);
+static void set_protocol_complete(tuh_xfer_t* xfer) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ uint8_t const daddr = xfer->daddr;
+ uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
+
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid,);
- if (XFER_RESULT_SUCCESS == xfer->result)
- {
- hid_itf->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ if (XFER_RESULT_SUCCESS == xfer->result) {
+ 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, instance, hid_itf->protocol_mode);
+ if (tuh_hid_set_protocol_complete_cb) {
+ tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
}
}
+void tuh_hid_set_default_protocol(uint8_t protocol) {
+ _hidh_default_protocol = protocol;
+}
-static bool _hidh_set_protocol(uint8_t dev_addr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
- TU_LOG2("HID Set Protocol = %d\r\n", protocol);
+static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
- .wValue = protocol,
- .wIndex = itf_num,
- .wLength = 0
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
+ .wValue = protocol,
+ .wIndex = itf_num,
+ .wLength = 0
};
- tuh_xfer_t xfer =
- {
- .daddr = dev_addr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = NULL,
- .complete_cb = complete_cb,
- .user_data = user_data
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
};
- TU_ASSERT( tuh_control_xfer(&xfer) );
- return true;
+ return tuh_control_xfer(&xfer);
}
-bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE);
+bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
- return _hidh_set_protocol(dev_addr, hid_itf->itf_num, protocol, set_protocol_complete, 0);
+ return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
}
-static void set_report_complete(tuh_xfer_t* xfer)
-{
- TU_LOG2("HID Set Report complete\r\n");
+static void get_report_complete(tuh_xfer_t* xfer) {
+ TU_LOG_DRV("HID Get 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 instance = get_instance_id_by_itfnum(xfer->daddr, itf_num);
+ 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 report_type = tu_u16_high(xfer->setup->wValue);
- uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
+ uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
- tuh_hid_set_report_complete_cb(xfer->daddr, instance, report_id, report_type,
+ 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_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
- TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
+bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = HID_REQ_CONTROL_SET_REPORT,
- .wValue = tu_u16(report_type, report_id),
- .wIndex = hid_itf->itf_num,
- .wLength = len
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = HID_REQ_CONTROL_GET_REPORT,
+ .wValue = tu_htole16(tu_u16(report_type, report_id)),
+ .wIndex = tu_htole16((uint16_t) p_hid->itf_num),
+ .wLength = len
};
- tuh_xfer_t xfer =
- {
- .daddr = dev_addr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = report,
- .complete_cb = set_report_complete,
- .user_data = 0
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = report,
+ .complete_cb = get_report_complete,
+ .user_data = 0
};
- TU_ASSERT( tuh_control_xfer(&xfer) );
- return true;
+ return tuh_control_xfer(&xfer);
}
-static bool _hidh_set_idle(uint8_t dev_addr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
- // SET IDLE request, device can stall if not support this request
- TU_LOG2("HID Set Idle \r\n");
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = HID_REQ_CONTROL_SET_IDLE,
- .wValue = idle_rate,
- .wIndex = itf_num,
- .wLength = 0
+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 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);
+ }
+}
+
+bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_REPORT,
+ .wValue = tu_htole16(tu_u16(report_type, report_id)),
+ .wIndex = tu_htole16((uint16_t) p_hid->itf_num),
+ .wLength = len
};
- tuh_xfer_t xfer =
- {
- .daddr = dev_addr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = NULL,
- .complete_cb = complete_cb,
- .user_data = user_data
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = report,
+ .complete_cb = set_report_complete,
+ .user_data = 0
};
- TU_ASSERT( tuh_control_xfer(&xfer) );
+ return tuh_control_xfer(&xfer);
+}
- return true;
+static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // SET IDLE request, device can stall if not support this request
+ TU_LOG_DRV("HID Set Idle \r\n");
+
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = HID_REQ_CONTROL_SET_IDLE,
+ .wValue = tu_htole16(idle_rate),
+ .wIndex = tu_htole16((uint16_t) itf_num),
+ .wLength = 0
+ };
+
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
+ };
+
+ return tuh_control_xfer(&xfer);
}
//--------------------------------------------------------------------+
// Interrupt Endpoint API
//--------------------------------------------------------------------+
-bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+// Check if HID interface is ready to receive report
+bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
+ TU_VERIFY(p_hid);
+ return !usbh_edpt_busy(dev_addr, p_hid->ep_in);
+}
+
+bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
// claim endpoint
- TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) );
+ TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in));
- if ( !usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size) )
- {
- usbh_edpt_claim(dev_addr, hid_itf->ep_in);
+ if (!usbh_edpt_xfer(daddr, p_hid->ep_in, epbuf->epin, p_hid->epin_size)) {
+ usbh_edpt_release(daddr, p_hid->ep_in);
return false;
}
return true;
}
+bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
+ TU_VERIFY(p_hid);
+ return tuh_edpt_abort_xfer(dev_addr, p_hid->ep_in);
+}
+
+bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) {
+ hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
+ TU_VERIFY(p_hid);
+ return !usbh_edpt_busy(dev_addr, p_hid->ep_out);
+}
-//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance)
-//{
-// TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance));
-//
-// hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
-// return !usbh_edpt_busy(dev_addr, hid_itf->ep_in);
-//}
+bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) {
+ TU_LOG_DRV("HID Send Report %d\r\n", report_id);
-//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len);
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
+
+ if (p_hid->ep_out == 0) {
+ // This HID does not have an out endpoint (other than control)
+ return false;
+ } else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
+ (report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) {
+ // ep_out buffer is not large enough to hold contents
+ return false;
+ }
+
+ // claim endpoint
+ TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_out));
+
+ if (report_id == 0) {
+ // No report ID in transmission
+ memcpy(&epbuf->epout[0], report, len);
+ } else {
+ epbuf->epout[0] = report_id;
+ memcpy(&epbuf->epout[1], report, len);
+ ++len; // 1 more byte for report_id
+ }
+
+ TU_LOG3_MEM(epbuf->epout, len, 2);
+
+ if (!usbh_edpt_xfer(daddr, p_hid->ep_out, epbuf->epout, len)) {
+ usbh_edpt_release(daddr, p_hid->ep_out);
+ return false;
+ }
+
+ return true;
+}
//--------------------------------------------------------------------+
// USBH API
//--------------------------------------------------------------------+
-void hidh_init(void)
-{
- tu_memclr(_hidh_dev, sizeof(_hidh_dev));
+bool hidh_init(void) {
+ TU_LOG_DRV("sizeof(hidh_interface_t) = %u\r\n", sizeof(hidh_interface_t));
+ tu_memclr(_hidh_itf, sizeof(_hidh_itf));
+ return true;
}
-bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool hidh_deinit(void) {
+ return true;
+}
+
+bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
uint8_t const dir = tu_edpt_dir(ep_addr);
- uint8_t const instance = get_instance_id_by_epaddr(dev_addr, ep_addr);
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+ uint8_t const idx = get_idx_by_epaddr(daddr, ep_addr);
+
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid);
+ hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
- if ( dir == TUSB_DIR_IN )
- {
- TU_LOG2(" Get Report callback (%u, %u)\r\n", dev_addr, instance);
- TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2);
- tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, (uint16_t) xferred_bytes);
- }else
- {
- if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(dev_addr, instance, hid_itf->epout_buf, (uint16_t) xferred_bytes);
+ if (dir == TUSB_DIR_IN) {
+ TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
+ 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);
+ }
}
return true;
}
-void hidh_close(uint8_t dev_addr)
-{
- TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
-
- hidh_device_t* hid_dev = get_dev(dev_addr);
-
- if (tuh_hid_umount_cb)
- {
- for (uint8_t inst = 0; inst < hid_dev->inst_count; inst++ ) tuh_hid_umount_cb(dev_addr, inst);
+void hidh_close(uint8_t daddr) {
+ for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
+ 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);
+ tu_memclr(p_hid, sizeof(hidh_interface_t));
+ }
}
-
- tu_memclr(hid_dev, sizeof(hidh_device_t));
}
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
-bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
-{
+bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
(void) rhport;
(void) max_len;
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
-
- TU_LOG2("[%u] HID opening Interface %u\r\n", dev_addr, desc_itf->bInterfaceNumber);
+ TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
// len = interface + hid + n*endpoints
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(max_len >= drv_len);
-
- uint8_t const *p_desc = (uint8_t const *) desc_itf;
+ uint8_t const* p_desc = (uint8_t const*) desc_itf;
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
- tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
+ tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
- // not enough interface, try to increase CFG_TUH_HID
- // TODO multiple devices
- hidh_device_t* hid_dev = get_dev(dev_addr);
- TU_ASSERT(hid_dev->inst_count < CFG_TUH_HID, 0);
-
- hidh_interface_t* hid_itf = get_instance(dev_addr, hid_dev->inst_count);
+ hidh_interface_t* p_hid = find_new_itf();
+ TU_ASSERT(p_hid); // not enough interface, try to increase CFG_TUH_HID
+ p_hid->daddr = daddr;
//------------- Endpoint Descriptors -------------//
p_desc = tu_desc_next(p_desc);
- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
- for(int i = 0; i < desc_itf->bNumEndpoints; i++)
- {
+ for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
- TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) );
+ TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
- if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
- {
- hid_itf->ep_in = desc_ep->bEndpointAddress;
- hid_itf->epin_size = tu_edpt_packet_size(desc_ep);
- }
- else
- {
- hid_itf->ep_out = desc_ep->bEndpointAddress;
- hid_itf->epout_size = tu_edpt_packet_size(desc_ep);
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ p_hid->ep_in = desc_ep->bEndpointAddress;
+ p_hid->epin_size = tu_edpt_packet_size(desc_ep);
+ } else {
+ p_hid->ep_out = desc_ep->bEndpointAddress;
+ p_hid->epout_size = tu_edpt_packet_size(desc_ep);
}
p_desc = tu_desc_next(p_desc);
- desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ desc_ep = (tusb_desc_endpoint_t const*) p_desc;
}
- hid_dev->inst_count++;
-
- hid_itf->itf_num = desc_itf->bInterfaceNumber;
+ p_hid->itf_num = desc_itf->bInterfaceNumber;
// Assume bNumDescriptors = 1
- hid_itf->report_desc_type = desc_hid->bReportType;
- hid_itf->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
+ p_hid->report_desc_type = desc_hid->bReportType;
+ p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
- hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
- if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass ) hid_itf->itf_protocol = desc_itf->bInterfaceProtocol;
+ p_hid->protocol_mode = _hidh_default_protocol;
+ if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) {
+ p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+ }
return true;
}
@@ -400,18 +539,17 @@ enum {
CONFIG_COMPLETE
};
-static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
+static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len);
static void process_set_config(tuh_xfer_t* xfer);
-bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
-{
+bool hidh_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
tuh_xfer_t xfer;
- xfer.daddr = dev_addr;
- xfer.result = XFER_RESULT_SUCCESS;
- xfer.setup = &request;
+ xfer.daddr = daddr;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.setup = &request;
xfer.user_data = CONFG_SET_IDLE;
// fake request to kick-off the set config process
@@ -420,94 +558,93 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
return true;
}
-static void process_set_config(tuh_xfer_t* xfer)
-{
- // Stall is a valid response for SET_IDLE, therefore we could ignore its result
- if ( xfer->setup->bRequest != HID_REQ_CONTROL_SET_IDLE )
- {
- TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
+static void process_set_config(tuh_xfer_t* xfer) {
+ // Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well
+ // therefore we could ignore its result
+ if (!(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
+ xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL)) {
+ TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS,);
}
uintptr_t const state = xfer->user_data;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
- uint8_t const daddr = xfer->daddr;
+ uint8_t const daddr = xfer->daddr;
- uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num);
- hidh_interface_t* hid_itf = get_instance(daddr, instance);
+ uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid,);
- switch(state)
- {
- case CONFG_SET_IDLE:
- {
+ switch (state) {
+ case CONFG_SET_IDLE: {
// Idle rate = 0 mean only report when there is changes
const uint16_t idle_rate = 0;
- const uintptr_t next_state = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
+ const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE)
+ ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
_hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
+ break;
}
- break;
case CONFIG_SET_PROTOCOL:
- _hidh_set_protocol(daddr, hid_itf->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC);
- break;
+ _hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
+ break;
case CONFIG_GET_REPORT_DESC:
// Get Report Descriptor if possible
// using usbh enumeration buffer since report descriptor can be very long
- if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
- {
- TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len);
+ if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) {
+ TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
// Driver is mounted without report descriptor
- config_driver_mount_complete(daddr, instance, NULL, 0);
- }else
- {
- tuh_descriptor_get_hid_report(daddr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, process_set_config, CONFIG_COMPLETE);
+ config_driver_mount_complete(daddr, idx, NULL, 0);
+ } else {
+ tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0,
+ usbh_get_enum_buf(), p_hid->report_desc_len,
+ process_set_config, CONFIG_COMPLETE);
}
break;
- case CONFIG_COMPLETE:
- {
+ case CONFIG_COMPLETE: {
uint8_t const* desc_report = usbh_get_enum_buf();
- uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
+ uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
- config_driver_mount_complete(daddr, instance, desc_report, desc_len);
+ config_driver_mount_complete(daddr, idx, desc_report, desc_len);
+ break;
}
- break;
- default: break;
+ default:
+ break;
}
}
-static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
-{
- hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
+static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) {
+ hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+ TU_VERIFY(p_hid,);
+ p_hid->mounted = true;
// enumeration is complete
- tuh_hid_mount_cb(dev_addr, instance, desc_report, desc_len);
+ if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
// notify usbh that driver enumeration is complete
- usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num);
+ usbh_driver_set_config_complete(daddr, p_hid->itf_num);
}
//--------------------------------------------------------------------+
// Report Descriptor Parser
//--------------------------------------------------------------------+
-uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
-{
+uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count,
+ uint8_t const* desc_report, uint16_t desc_len) {
// Report Item 6.2.2.2 USB HID 1.11
- union TU_ATTR_PACKED
- {
+ union TU_ATTR_PACKED {
uint8_t byte;
- struct TU_ATTR_PACKED
- {
- uint8_t size : 2;
- uint8_t type : 2;
- uint8_t tag : 4;
+ struct TU_ATTR_PACKED {
+ uint8_t size : 2;
+ uint8_t type : 2;
+ uint8_t tag : 4;
};
} header;
- tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
+ tu_memclr(report_info_arr, arr_count * sizeof(tuh_hid_report_info_t));
uint8_t report_num = 0;
tuh_hid_report_info_t* info = report_info_arr;
@@ -517,160 +654,111 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
// uint8_t ri_report_size = 0;
uint8_t ri_collection_depth = 0;
-
- while(desc_len && report_num < arr_count)
- {
+ while (desc_len && report_num < arr_count) {
header.byte = *desc_report++;
desc_len--;
- uint8_t const tag = header.tag;
+ uint8_t const tag = header.tag;
uint8_t const type = header.type;
uint8_t const size = header.size;
uint8_t const data8 = desc_report[0];
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
- for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
+ for (uint32_t i = 0; i < size; i++) {
+ TU_LOG(3, "%02X ", desc_report[i]);
+ }
TU_LOG(3, "\r\n");
- switch(type)
- {
+ switch (type) {
case RI_TYPE_MAIN:
- switch (tag)
- {
+ switch (tag) {
case RI_MAIN_INPUT: break;
case RI_MAIN_OUTPUT: break;
case RI_MAIN_FEATURE: break;
-
case RI_MAIN_COLLECTION:
ri_collection_depth++;
- break;
+ break;
case RI_MAIN_COLLECTION_END:
ri_collection_depth--;
- if (ri_collection_depth == 0)
- {
+ if (ri_collection_depth == 0) {
info++;
report_num++;
}
- break;
+ break;
- default: break;
+ default:break;
}
- break;
+ break;
case RI_TYPE_GLOBAL:
- switch(tag)
- {
+ switch (tag) {
case RI_GLOBAL_USAGE_PAGE:
// only take in account the "usage page" before REPORT ID
- if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
- break;
+ if (ri_collection_depth == 0) memcpy(&info->usage_page, desc_report, size);
+ break;
- case RI_GLOBAL_LOGICAL_MIN : break;
- case RI_GLOBAL_LOGICAL_MAX : break;
- case RI_GLOBAL_PHYSICAL_MIN : break;
- case RI_GLOBAL_PHYSICAL_MAX : break;
+ case RI_GLOBAL_LOGICAL_MIN: break;
+ case RI_GLOBAL_LOGICAL_MAX: break;
+ case RI_GLOBAL_PHYSICAL_MIN: break;
+ case RI_GLOBAL_PHYSICAL_MAX: break;
case RI_GLOBAL_REPORT_ID:
info->report_id = data8;
- break;
+ break;
case RI_GLOBAL_REPORT_SIZE:
// ri_report_size = data8;
- break;
+ break;
case RI_GLOBAL_REPORT_COUNT:
// ri_report_count = data8;
- break;
+ break;
- case RI_GLOBAL_UNIT_EXPONENT : break;
- case RI_GLOBAL_UNIT : break;
- case RI_GLOBAL_PUSH : break;
- case RI_GLOBAL_POP : break;
+ case RI_GLOBAL_UNIT_EXPONENT: break;
+ case RI_GLOBAL_UNIT: break;
+ case RI_GLOBAL_PUSH: break;
+ case RI_GLOBAL_POP: break;
default: break;
}
- break;
+ break;
case RI_TYPE_LOCAL:
- switch(tag)
- {
+ switch (tag) {
case RI_LOCAL_USAGE:
// only take in account the "usage" before starting REPORT ID
- if ( ri_collection_depth == 0 ) info->usage = data8;
- break;
+ if (ri_collection_depth == 0) info->usage = data8;
+ break;
- case RI_LOCAL_USAGE_MIN : break;
- case RI_LOCAL_USAGE_MAX : break;
- case RI_LOCAL_DESIGNATOR_INDEX : break;
- case RI_LOCAL_DESIGNATOR_MIN : break;
- case RI_LOCAL_DESIGNATOR_MAX : break;
- case RI_LOCAL_STRING_INDEX : break;
- case RI_LOCAL_STRING_MIN : break;
- case RI_LOCAL_STRING_MAX : break;
- case RI_LOCAL_DELIMITER : break;
+ case RI_LOCAL_USAGE_MIN: break;
+ case RI_LOCAL_USAGE_MAX: break;
+ case RI_LOCAL_DESIGNATOR_INDEX: break;
+ case RI_LOCAL_DESIGNATOR_MIN: break;
+ case RI_LOCAL_DESIGNATOR_MAX: break;
+ case RI_LOCAL_STRING_INDEX: break;
+ case RI_LOCAL_STRING_MIN: break;
+ case RI_LOCAL_STRING_MAX: break;
+ case RI_LOCAL_DELIMITER: break;
default: break;
}
- break;
+ break;
- // error
+ // error
default: break;
}
desc_report += size;
- desc_len -= size;
+ desc_len -= size;
}
- for ( uint8_t i = 0; i < report_num; i++ )
- {
- info = report_info_arr+i;
- TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
+ for (uint8_t i = 0; i < report_num; i++) {
+ info = report_info_arr + i;
+ TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
}
return report_num;
}
-//--------------------------------------------------------------------+
-// Helper
-//--------------------------------------------------------------------+
-
-// Get Device by address
-TU_ATTR_ALWAYS_INLINE static inline hidh_device_t* get_dev(uint8_t dev_addr)
-{
- return &_hidh_dev[dev_addr-1];
-}
-
-// Get Interface by instance number
-TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_instance(uint8_t dev_addr, uint8_t instance)
-{
- return &_hidh_dev[dev_addr-1].instances[instance];
-}
-
-// Get instance ID by interface number
-static uint8_t get_instance_id_by_itfnum(uint8_t dev_addr, uint8_t itf)
-{
- for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ )
- {
- hidh_interface_t *hid = get_instance(dev_addr, inst);
-
- if ( (hid->itf_num == itf) && (hid->ep_in || hid->ep_out) ) return inst;
- }
-
- return 0xff;
-}
-
-// Get instance ID by endpoint address
-static uint8_t get_instance_id_by_epaddr(uint8_t dev_addr, uint8_t ep_addr)
-{
- for ( uint8_t inst = 0; inst < CFG_TUH_HID; inst++ )
- {
- hidh_interface_t *hid = get_instance(dev_addr, inst);
-
- if ( (ep_addr == hid->ep_in) || ( ep_addr == hid->ep_out) ) return inst;
- }
-
- return 0xff;
-}
-
#endif
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index fe09b03b2..9681c704b 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -30,7 +30,7 @@
#include "hid.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
//--------------------------------------------------------------------+
@@ -47,10 +47,9 @@
#endif
-typedef struct
-{
- uint8_t report_id;
- uint8_t usage;
+typedef struct {
+ uint8_t report_id;
+ uint8_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
@@ -62,18 +61,32 @@ typedef struct
// Interface API
//--------------------------------------------------------------------+
-// Get the number of HID instances
-uint8_t tuh_hid_instance_count(uint8_t dev_addr);
+// Get the total number of mounted HID interfaces of a device
+uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
-// Check if HID instance is mounted
-bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance);
+// Get all mounted interfaces across devices
+uint8_t tuh_hid_itf_get_total_count(void);
+
+// backward compatible rename
+#define tuh_hid_instance_count tuh_hid_itf_get_count
+
+// Get Interface information
+bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* itf_info);
+
+// Get Interface index from device address + interface number
+// return TUSB_INDEX_INVALID_8 (0xFF) if not found
+uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
-uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance);
+uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t idx);
+
+// Check if HID interface is mounted
+bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
-uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
+TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
+ uint8_t const* desc_report, uint16_t desc_len);
//--------------------------------------------------------------------+
// Control Endpoint API
@@ -82,31 +95,46 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr,
// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
// Note: Device will be initialized in Boot protocol for simplicity.
// Application can use set_protocol() to switch back to Report protocol.
-uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance);
+uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t idx);
+
+// Device by default is enumerated in Boot protocol for simplicity. Application
+// can use this to modify the default protocol for next enumeration.
+void tuh_hid_set_default_protocol(uint8_t protocol);
// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
-bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol);
+bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
+
+// Get Report using control endpoint
+// report_type is either Input, Output or Feature, (value from hid_report_type_t)
+bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
// Set Report using control endpoint
-// report_type is either Intput, Output or Feature, (value from hid_report_type_t)
-bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
+// report_type is either Input, Output or Feature, (value from hid_report_type_t)
+bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
+ void* report, uint16_t len);
//--------------------------------------------------------------------+
// Interrupt Endpoint API
//--------------------------------------------------------------------+
-// Check if the interface is ready to use
-//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance);
+// Check if HID interface is ready to receive report
+bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx);
// Try to receive next report on Interrupt Endpoint. Immediately return
// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available
// - false if failed to queue the transfer e.g endpoint is busy
-bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance);
+bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx);
+
+// Abort receiving report on Interrupt Endpoint
+bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
+
+// Check if HID interface is ready to send report
+bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
// Send report using interrupt endpoint
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
-//void tuh_hid_send_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t const* report, uint16_t len);
+bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len);
//--------------------------------------------------------------------+
// Callbacks (Weak is optional)
@@ -117,33 +145,38 @@ bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance);
// can be used to parse common/simple enough descriptor.
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
// therefore report_desc = NULL, desc_len = 0
-void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report_desc, uint16_t desc_len);
+TU_ATTR_WEAK 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 instance);
+TU_ATTR_WEAK 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 instance, uint8_t const* report, uint16_t len);
+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 instance, uint8_t const* report, uint16_t len);
+TU_ATTR_WEAK 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);
// 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 instance, uint8_t report_id, uint8_t report_type, uint16_t 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);
// Invoked when Set Protocol request is complete
-TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t protocol);
+TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void hidh_init (void);
-bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
-bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
-bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-void hidh_close (uint8_t dev_addr);
+bool hidh_init(void);
+bool hidh_deinit(void);
+bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
+bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void hidh_close(uint8_t dev_addr);
#ifdef __cplusplus
}
diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h
index ea2cfec75..68a784c60 100644
--- a/src/class/midi/midi.h
+++ b/src/class/midi/midi.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -71,8 +71,8 @@ typedef enum
MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message
MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data
MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data
- MIDI_CIN_NOTE_ON = 9,
MIDI_CIN_NOTE_OFF = 8,
+ MIDI_CIN_NOTE_ON = 9,
MIDI_CIN_POLY_KEYPRESS = 10,
MIDI_CIN_CONTROL_CHANGE = 11,
MIDI_CIN_PROGRAM_CHANGE = 12,
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index c6c99db29..4ae8ea8fa 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -40,15 +40,13 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t buffer[4];
uint8_t index;
uint8_t total;
-}midid_stream_t;
+} midid_stream_t;
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
@@ -70,36 +68,36 @@ typedef struct
osal_mutex_def_t rx_ff_mutex;
osal_mutex_def_t tx_ff_mutex;
#endif
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
-
} midid_interface_t;
#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
+// Endpoint Transfer buffer
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(epin, CFG_TUD_MIDI_EP_BUFSIZE);
+ TUD_EPBUF_DEF(epout, CFG_TUD_MIDI_EP_BUFSIZE);
+} _midid_epbuf[CFG_TUD_MIDI];
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
+static midid_interface_t _midid_itf[CFG_TUD_MIDI];
-bool tud_midi_n_mounted (uint8_t itf)
-{
+bool tud_midi_n_mounted (uint8_t itf) {
midid_interface_t* midi = &_midid_itf[itf];
return midi->ep_in && midi->ep_out;
}
-static void _prep_out_transaction (midid_interface_t* p_midi)
-{
- uint8_t const rhport = 0;
+static void _prep_out_transaction(uint8_t idx) {
+ const uint8_t rhport = 0;
+ midid_interface_t* p_midi = &_midid_itf[idx];
uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
// Prepare for incoming data but only allow what we can store in the ring buffer.
// TODO Actually we can still carry out the transfer, keeping count of received bytes
// and slowly move it to the FIFO when read().
// This pre-check reduces endpoint claiming
- TU_VERIFY(available >= sizeof(p_midi->epout_buf), );
+ TU_VERIFY(available >= CFG_TUD_MIDI_EP_BUFSIZE, );
// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
@@ -107,8 +105,8 @@ static void _prep_out_transaction (midid_interface_t* p_midi)
// fifo can be changed before endpoint is claimed
available = tu_fifo_remaining(&p_midi->rx_ff);
- if ( available >= sizeof(p_midi->epout_buf) ) {
- usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, sizeof(p_midi->epout_buf));
+ if ( available >= CFG_TUD_MIDI_EP_BUFSIZE ) {
+ usbd_edpt_xfer(rhport, p_midi->ep_out, _midid_epbuf[idx].epout, CFG_TUD_MIDI_EP_BUFSIZE);
}else
{
// Release endpoint since we don't make any transfer
@@ -124,7 +122,7 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
(void) cable_num;
midid_interface_t* midi = &_midid_itf[itf];
- midid_stream_t const* stream = &midi->stream_read;
+ const midid_stream_t* stream = &midi->stream_read;
// when using with packet API stream total & index are both zero
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
@@ -182,7 +180,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize);
// Skip the header (1st byte) in the buffer
- memcpy(buf8, stream->buffer + 1 + stream->index, count);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
total_read += count;
stream->index += count;
@@ -205,8 +203,8 @@ bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_out);
- uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
- _prep_out_transaction(midi);
+ const uint32_t num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
+ _prep_out_transaction(itf);
return (num_read == 4);
}
@@ -214,31 +212,31 @@ bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
// WRITE API
//--------------------------------------------------------------------+
-static uint32_t write_flush(midid_interface_t* midi)
-{
- // No data to send
- if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
+static uint32_t write_flush(uint8_t idx) {
+ midid_interface_t* midi = &_midid_itf[idx];
- uint8_t const rhport = 0;
+ if (!tu_fifo_count(&midi->tx_ff)) {
+ return 0; // No data to send
+ }
+
+ const uint8_t rhport = 0;
// skip if previous transfer not complete
TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
- uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
+ uint16_t count = tu_fifo_read_n(&midi->tx_ff, _midid_epbuf[idx].epin, CFG_TUD_MIDI_EP_BUFSIZE);
- if (count)
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
+ if (count) {
+ TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, _midid_epbuf[idx].epin, count), 0 );
return count;
- }else
- {
+ }else {
// Release endpoint since we don't make any transfer
usbd_edpt_release(rhport, midi->ep_in);
return 0;
}
}
-uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
+uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t* buffer, uint32_t bufsize)
{
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_in, 0);
@@ -248,24 +246,23 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
uint32_t i = 0;
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
{
- uint8_t const data = buffer[i];
+ const uint8_t data = buffer[i];
i++;
if ( stream->index == 0 )
{
//------------- New event packet -------------//
-
- uint8_t const msg = data >> 4;
+ const uint8_t msg = data >> 4;
stream->index = 2;
stream->buffer[1] = data;
// Check to see if we're still in a SysEx transmit.
- if ( stream->buffer[0] == MIDI_CIN_SYSEX_START )
+ if ( ((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START )
{
if ( data == MIDI_STATUS_SYSEX_END )
{
- stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
+ stream->buffer[0] = (uint8_t) ((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
stream->total = 2;
}
else
@@ -308,6 +305,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
stream->total = 2;
}
+ stream->buffer[0] |= (uint8_t)(cable_num << 4);
}
else
{
@@ -328,9 +326,9 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
stream->index++;
// See if this byte ends a SysEx.
- if ( stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
+ if ( (stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
{
- stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1);
+ stream->buffer[0] = (uint8_t) ((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
stream->total = stream->index;
}
}
@@ -339,9 +337,11 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
if ( stream->index == stream->total )
{
// zeroes unused bytes
- for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0;
+ for (uint8_t idx = stream->total; idx < 4; idx++) {
+ stream->buffer[idx] = 0;
+ }
- uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
+ const uint16_t count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = stream->total = 0;
@@ -351,20 +351,21 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const*
}
}
- write_flush(midi);
+ write_flush(itf);
return i;
}
-bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
-{
+bool tud_midi_n_packet_write (uint8_t itf, const uint8_t packet[4]) {
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_in);
- if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
+ if (tu_fifo_remaining(&midi->tx_ff) < 4) {
+ return false;
+ }
tu_fifo_write_n(&midi->tx_ff, packet, 4);
- write_flush(midi);
+ write_flush(itf);
return true;
}
@@ -372,12 +373,10 @@ bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void midid_init(void)
-{
+void midid_init(void) {
tu_memclr(_midid_itf, sizeof(_midid_itf));
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
+ for (uint8_t i = 0; i < CFG_TUD_MIDI; i++) {
midid_interface_t* midi = &_midid_itf[i];
// config fifo
@@ -385,12 +384,38 @@ void midid_init(void)
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
- tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
+ osal_mutex_t mutex_rd = osal_mutex_create(&midi->rx_ff_mutex);
+ osal_mutex_t mutex_wr = osal_mutex_create(&midi->tx_ff_mutex);
+ TU_ASSERT(mutex_wr != NULL && mutex_wr != NULL, );
+
+ tu_fifo_config_mutex(&midi->rx_ff, NULL, mutex_rd);
+ tu_fifo_config_mutex(&midi->tx_ff, mutex_wr, NULL);
#endif
}
}
+bool midid_deinit(void) {
+ #if CFG_FIFO_MUTEX
+ for(uint8_t i=0; i<CFG_TUD_MIDI; i++) {
+ midid_interface_t* midi = &_midid_itf[i];
+ osal_mutex_t mutex_rd = midi->rx_ff.mutex_rd;
+ osal_mutex_t mutex_wr = midi->tx_ff.mutex_wr;
+
+ if (mutex_rd) {
+ osal_mutex_delete(mutex_rd);
+ tu_fifo_config_mutex(&midi->rx_ff, NULL, NULL);
+ }
+
+ if (mutex_wr) {
+ osal_mutex_delete(mutex_wr);
+ tu_fifo_config_mutex(&midi->tx_ff, NULL, NULL);
+ }
+ }
+ #endif
+
+ return true;
+}
+
void midid_reset(uint8_t rhport)
{
(void) rhport;
@@ -404,32 +429,28 @@ void midid_reset(uint8_t rhport)
}
}
-uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
-{
+uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
uint16_t drv_len = 0;
uint8_t const * p_desc = (uint8_t const *)desc_itf;
- // 1st Interface is Audio Control v1
+
+ // 1st Interface is Audio Control v1 (optional)
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
- AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
- AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol)
- {
+ AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
drv_len = tu_desc_len(desc_itf);
p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- }
- else
- {
- TU_LOG1("Warning: MIDI Device has no Audio Control Interface");
+ } else {
+ TU_LOG2("Warning: MIDI Device has no Audio Control Interface");
}
// 2nd Interface is MIDI Streaming
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
- tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
+ const tusb_desc_interface_t* desc_midi = (const tusb_desc_interface_t*) p_desc;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
@@ -437,11 +458,10 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
// Find available interface
midid_interface_t * p_midi = NULL;
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
- {
- p_midi = &_midid_itf[i];
+ uint8_t idx;
+ for(idx=0; idx<CFG_TUD_MIDI; idx++) {
+ if ( _midid_itf[idx].ep_in == 0 && _midid_itf[idx].ep_out == 0 ) {
+ p_midi = &_midid_itf[idx];
break;
}
}
@@ -460,8 +480,8 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
{
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
- TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
- uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ TU_ASSERT(usbd_edpt_open(rhport, (const tusb_desc_endpoint_t*) p_desc), 0);
+ uint8_t ep_addr = ((const tusb_desc_endpoint_t*) p_desc)->bEndpointAddress;
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
{
@@ -482,7 +502,7 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
}
// Prepare for incoming data
- _prep_out_transaction(p_midi);
+ _prep_out_transaction(idx);
return drv_len;
}
@@ -490,14 +510,9 @@ uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint
// 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 midid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- (void) rhport;
- (void) stage;
- (void) request;
-
- // driver doesn't support any request yet
- return false;
+bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_request_t* request) {
+ (void) rhport; (void) stage; (void) request;
+ return false; // driver doesn't support any request yet
}
bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
@@ -505,40 +520,37 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
(void) result;
(void) rhport;
- uint8_t itf;
+ uint8_t idx;
midid_interface_t* p_midi;
// Identify which interface to use
- for (itf = 0; itf < CFG_TUD_MIDI; itf++)
- {
- p_midi = &_midid_itf[itf];
- if ( ( ep_addr == p_midi->ep_out ) || ( ep_addr == p_midi->ep_in ) ) break;
+ for (idx = 0; idx < CFG_TUD_MIDI; idx++) {
+ p_midi = &_midid_itf[idx];
+ if ((ep_addr == p_midi->ep_out) || (ep_addr == p_midi->ep_in)) {
+ break;
+ }
}
- TU_ASSERT(itf < CFG_TUD_MIDI);
+ TU_ASSERT(idx < CFG_TUD_MIDI);
// receive new data
- if ( ep_addr == p_midi->ep_out )
- {
- tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, (uint16_t) xferred_bytes);
+ if (ep_addr == p_midi->ep_out) {
+ 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(itf);
+ if (tud_midi_rx_cb) {
+ 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,
// and does not need to claim like ep_in
- _prep_out_transaction(p_midi);
- }
- else if ( ep_addr == p_midi->ep_in )
- {
- if (0 == write_flush(p_midi))
- {
+ _prep_out_transaction(idx);
+ } else if (ep_addr == p_midi->ep_in) {
+ if (0 == write_flush(idx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
- if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
- {
- if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
- {
+ if (!tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE))) {
+ if (usbd_edpt_claim(rhport, p_midi->ep_in)) {
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
}
}
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index 211edc8d1..3e89cc0a3 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -158,6 +158,7 @@ static inline bool tud_midi_packet_write (uint8_t const packet[4])
// Internal Class Driver API
//--------------------------------------------------------------------+
void midid_init (void);
+bool midid_deinit (void);
void midid_reset (uint8_t rhport);
uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index 84b6e4d79..bbfd35a43 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -53,7 +53,7 @@ enum {
};
/// \brief MassStorage Protocol.
-/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy
+/// \details CBI only approved to use with full-speed floppy disk & should not used with highspeed or device other than floppy
typedef enum
{
MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
@@ -97,7 +97,7 @@ typedef struct TU_ATTR_PACKED
{
uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
- uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
+ uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResidue the difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
}msc_csw_t;
@@ -120,14 +120,14 @@ typedef enum
SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
- SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them.
+ SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests that the device server transfer the specified logical block(s) from the data-out buffer and write them.
}scsi_cmd_type_t;
/// SCSI Sense Key
typedef enum
{
SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
- SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive.
+ SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< Indicates the last command completed successfully with some recovery action performed by the disc drive.
SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
@@ -138,7 +138,7 @@ typedef enum
SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
- SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium.
+ SCSI_SENSE_MISCOMPARE = 0x0e ///< Indicates that the source data did not match the data read from the medium.
}scsi_sense_key_type_t;
//--------------------------------------------------------------------+
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 00b0a1d06..6670045aa 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -34,15 +34,17 @@
#include "msc_device.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_MSC_LOG_LEVEL
+ #define CFG_TUD_MSC_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MSC_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-
-// Can be selectively disabled to reduce logging when troubleshooting other driver
-#define MSC_DEBUG 2
-
-enum
-{
+enum {
MSC_STAGE_CMD = 0,
MSC_STAGE_DATA,
MSC_STAGE_STATUS,
@@ -50,11 +52,9 @@ enum
MSC_STAGE_NEED_RESET,
};
-typedef struct
-{
- // TODO optimize alignment
- CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
- CFG_TUSB_MEM_ALIGN msc_csw_t csw;
+typedef struct {
+ TU_ATTR_ALIGNED(4) msc_cbw_t cbw;
+ TU_ATTR_ALIGNED(4) msc_csw_t csw;
uint8_t itf_num;
uint8_t ep_in;
@@ -62,6 +62,7 @@ typedef struct
// Bulk Only Transfer (BOT) Protocol
uint8_t stage;
+
uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
@@ -71,8 +72,11 @@ typedef struct
uint8_t add_sense_qualifier;
}mscd_interface_t;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
+static mscd_interface_t _mscd_itf;
+
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_MSC_EP_BUFSIZE);
+} _mscd_epbuf;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -83,28 +87,24 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
-TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) {
return tu_bit_test(dir, 7);
}
-static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
-{
+static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc) {
// Data residue is always = host expect - actual transferred
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
-
p_msc->stage = MSC_STAGE_STATUS_SENT;
- return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
+ memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t));
+ return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t));
}
-static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
-{
+static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc) {
p_msc->stage = MSC_STAGE_CMD;
- return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
+ return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t));
}
-static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
-{
+static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status) {
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
@@ -113,83 +113,63 @@ static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status
p_msc->stage = MSC_STAGE_STATUS;
// failed but sense key is not set: default to Illegal Request
- if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ if (p_msc->sense_key == 0) {
+ tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ }
// If there is data stage and not yet complete, stall it
- if ( p_cbw->total_bytes && p_csw->data_residue )
- {
- if ( is_data_in(p_cbw->dir) )
- {
+ if (p_cbw->total_bytes && p_csw->data_residue) {
+ if (is_data_in(p_cbw->dir)) {
usbd_edpt_stall(rhport, p_msc->ep_in);
- }
- else
- {
+ } else {
usbd_edpt_stall(rhport, p_msc->ep_out);
}
}
}
-static inline uint32_t rdwr10_get_lba(uint8_t const command[])
-{
+static inline uint32_t rdwr10_get_lba(uint8_t const command[]) {
// use offsetof to avoid pointer to the odd/unaligned address
- uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
-
- // lba is in Big Endian
- return tu_ntohl(lba);
+ const uint32_t lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
+ return tu_ntohl(lba); // lba is in Big Endian
}
-static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
-{
+static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw) {
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
return tu_ntohs(block_count);
}
-static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
-{
+static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw) {
// first extract block count in the command
uint16_t const block_count = rdwr10_get_blockcount(cbw);
-
- // invalid block count
- if (block_count == 0) return 0;
-
+ if (block_count == 0) {
+ return 0; // invalid block count
+ }
return (uint16_t) (cbw->total_bytes / block_count);
}
-uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
-{
+static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) {
uint8_t status = MSC_CSW_STATUS_PASSED;
uint16_t const block_count = rdwr10_get_blockcount(cbw);
- if ( cbw->total_bytes == 0 )
- {
- if ( block_count )
- {
- TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
+ if (cbw->total_bytes == 0) {
+ if (block_count) {
+ TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }else
- {
+ } else {
// no data transfer, only exist in complaint test suite
}
- }else
- {
- if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
- {
- TU_LOG(MSC_DEBUG, " SCSI case 10 (Ho <> Di)\r\n");
+ } else {
+ if (SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir)) {
+ TU_LOG_DRV(" SCSI case 10 (Ho <> Di)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }
- else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
- {
- TU_LOG(MSC_DEBUG, " SCSI case 8 (Hi <> Do)\r\n");
+ } else if (SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir)) {
+ TU_LOG_DRV(" SCSI case 8 (Hi <> Do)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
- }
- else if ( 0 == block_count )
- {
- TU_LOG(MSC_DEBUG, " SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
- status = MSC_CSW_STATUS_FAILED;
- }
- else if ( cbw->total_bytes / block_count == 0 )
- {
- TU_LOG(MSC_DEBUG, " Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
+ } else if (0 == block_count) {
+ TU_LOG_DRV(" SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
+ status = MSC_CSW_STATUS_FAILED;
+ } else if (cbw->total_bytes / block_count == 0) {
+ TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
status = MSC_CSW_STATUS_PHASE_ERROR;
}
}
@@ -200,10 +180,9 @@ uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= 2
+#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
-TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
-{
+TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] = {
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
{ .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
@@ -217,8 +196,7 @@ TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
-TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
-{
+TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table = {
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
};
@@ -228,19 +206,15 @@ TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
-bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
-{
+bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) {
(void) lun;
-
_mscd_itf.sense_key = sense_key;
_mscd_itf.add_sense_code = add_sense_code;
_mscd_itf.add_sense_qualifier = add_sense_qualifier;
-
return true;
}
-static inline void set_sense_medium_not_present(uint8_t lun)
-{
+static inline void set_sense_medium_not_present(uint8_t lun) {
// default sense is NOT READY, MEDIUM NOT PRESENT
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
}
@@ -248,48 +222,43 @@ static inline void set_sense_medium_not_present(uint8_t lun)
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void mscd_init(void)
-{
+void mscd_init(void) {
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
-void mscd_reset(uint8_t rhport)
-{
+bool mscd_deinit(void) {
+ return true; // nothing to do
+}
+
+void mscd_reset(uint8_t rhport) {
(void) rhport;
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
-uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
// only support SCSI's BOT protocol
TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
-
- // msc driver length is fixed
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
-
- // Max length must be at least 1 interface + 2 endpoints
- TU_ASSERT(max_len >= drv_len, 0);
+ TU_ASSERT(max_len >= drv_len, 0); // Max length must be at least 1 interface + 2 endpoints
mscd_interface_t * p_msc = &_mscd_itf;
p_msc->itf_num = itf_desc->bInterfaceNumber;
// Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0);
// Prepare for Command Block Wrapper
- TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
+ TU_ASSERT(prepare_cbw(rhport, p_msc), drv_len);
return drv_len;
}
-static void proc_bot_reset(mscd_interface_t* p_msc)
-{
+static void proc_bot_reset(mscd_interface_t* p_msc) {
p_msc->stage = MSC_STAGE_CMD;
p_msc->total_len = 0;
p_msc->xferred_len = 0;
-
p_msc->sense_key = 0;
p_msc->add_sense_code = 0;
p_msc->add_sense_qualifier = 0;
@@ -298,10 +267,10 @@ static void proc_bot_reset(mscd_interface_t* p_msc)
// 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 mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- // nothing to do with DATA & ACK stage
- if (stage != CONTROL_STAGE_SETUP) return true;
+bool mscd_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
+ }
mscd_interface_t* p_msc = &_mscd_itf;
@@ -309,35 +278,25 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
- TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
- {
+ TUSB_REQ_FEATURE_EDPT_HALT == request->wValue ) {
uint8_t const ep_addr = tu_u16_low(request->wIndex);
- if ( p_msc->stage == MSC_STAGE_NEED_RESET )
- {
+ if (p_msc->stage == MSC_STAGE_NEED_RESET) {
// reset recovery is required to recover from this stage
// Clear Stall request cannot resolve this -> continue to stall endpoint
usbd_edpt_stall(rhport, ep_addr);
- }
- else
- {
- if ( ep_addr == p_msc->ep_in )
- {
- if ( p_msc->stage == MSC_STAGE_STATUS )
- {
+ } else {
+ if (ep_addr == p_msc->ep_in) {
+ if (p_msc->stage == MSC_STAGE_STATUS) {
// resume sending SCSI status if we are in this stage previously before stalled
- TU_ASSERT( send_csw(rhport, p_msc) );
+ TU_ASSERT(send_csw(rhport, p_msc));
}
- }
- else if ( ep_addr == p_msc->ep_out )
- {
- if ( p_msc->stage == MSC_STAGE_CMD )
- {
+ } else if (ep_addr == p_msc->ep_out) {
+ if (p_msc->stage == MSC_STAGE_CMD) {
// part of reset recovery (probably due to invalid CBW) -> prepare for new command
// Note: skip if already queued previously
- if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
- {
- TU_ASSERT( prepare_cbw(rhport, p_msc) );
+ if (usbd_edpt_ready(rhport, p_msc->ep_out)) {
+ TU_ASSERT(prepare_cbw(rhport, p_msc));
}
}
}
@@ -349,33 +308,27 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
// From this point only handle class request only
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
- switch ( request->bRequest )
- {
+ switch ( request->bRequest ) {
case MSC_REQ_RESET:
- TU_LOG(MSC_DEBUG, " MSC BOT Reset\r\n");
+ TU_LOG_DRV(" MSC BOT Reset\r\n");
TU_VERIFY(request->wValue == 0 && request->wLength == 0);
-
- // driver state reset
- proc_bot_reset(p_msc);
-
+ proc_bot_reset(p_msc); // driver state reset
tud_control_status(rhport, request);
break;
- case MSC_REQ_GET_MAX_LUN:
- {
- TU_LOG(MSC_DEBUG, " MSC Get Max Lun\r\n");
+ case MSC_REQ_GET_MAX_LUN: {
+ 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();
+ if (tud_msc_get_maxlun_cb) {
+ maxlun = tud_msc_get_maxlun_cb();
+ }
TU_VERIFY(maxlun);
-
- // MAX LUN is minus 1 by specs
- maxlun--;
-
+ maxlun--; // MAX LUN is minus 1 by specs
tud_control_xfer(rhport, request, &maxlun, 1);
+ break;
}
- break;
default: return false; // stall unsupported request
}
@@ -383,37 +336,36 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
return true;
}
-bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
+bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
(void) event;
mscd_interface_t* p_msc = &_mscd_itf;
- msc_cbw_t const * p_cbw = &p_msc->cbw;
- msc_csw_t * p_csw = &p_msc->csw;
+ msc_cbw_t * p_cbw = &p_msc->cbw;
+ msc_csw_t * p_csw = &p_msc->csw;
- switch (p_msc->stage)
- {
+ switch (p_msc->stage) {
case MSC_STAGE_CMD:
//------------- new CBW received -------------//
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
- if(ep_addr != p_msc->ep_out) return true;
+ if (ep_addr != p_msc->ep_out) {
+ return true;
+ }
- if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
- {
- TU_LOG(MSC_DEBUG, " SCSI CBW is not valid\r\n");
+ const uint32_t signature = tu_le32toh(tu_unaligned_read32(_mscd_epbuf.buf));
+ if (!(xferred_bytes == sizeof(msc_cbw_t) && signature == MSC_CBW_SIGNATURE)) {
// BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
+ TU_LOG_DRV(" SCSI CBW is not valid\r\n");
p_msc->stage = MSC_STAGE_NEED_RESET;
-
- // invalid CBW stall both endpoints
usbd_edpt_stall(rhport, p_msc->ep_in);
usbd_edpt_stall(rhport, p_msc->ep_out);
-
return false;
}
- TU_LOG(MSC_DEBUG, " SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
- //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
+ memcpy(p_cbw, _mscd_epbuf.buf, sizeof(msc_cbw_t));
+
+ TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
@@ -426,87 +378,65 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
p_msc->xferred_len = 0;
// Read10 or Write10
- if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
- {
+ if ((SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0])) {
uint8_t const status = rdwr10_validate_cmd(p_cbw);
- if ( status != MSC_CSW_STATUS_PASSED)
- {
+ if (status != MSC_CSW_STATUS_PASSED) {
fail_scsi_op(rhport, p_msc, status);
- }else if ( p_cbw->total_bytes )
- {
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
+ } else if (p_cbw->total_bytes) {
+ if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
proc_read10_cmd(rhport, p_msc);
- }else
- {
+ } else {
proc_write10_cmd(rhport, p_msc);
}
- }else
- {
+ } else {
// no data transfer, only exist in complaint test suite
p_msc->stage = MSC_STAGE_STATUS;
}
- }
- else
- {
+ } else {
// For other SCSI commands
// 1. OUT : queue transfer (invoke app callback after done)
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
- if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
- {
- if (p_cbw->total_bytes > sizeof(_mscd_buf))
- {
- TU_LOG(MSC_DEBUG, " SCSI reject non READ10/WRITE10 with large data\r\n");
+ if ((p_cbw->total_bytes > 0) && !is_data_in(p_cbw->dir)) {
+ if (p_cbw->total_bytes > CFG_TUD_MSC_EP_BUFSIZE) {
+ TU_LOG_DRV(" SCSI reject non READ10/WRITE10 with large data\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
// but it is OK to just receive data then responded with failed status
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, (uint16_t) p_msc->total_len) );
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
}
- }else
- {
+ } else {
// First process if it is a built-in commands
- int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
+ int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, CFG_TUD_MSC_EP_BUFSIZE);
// Invoke user callback if not built-in
- if ( (resplen < 0) && (p_msc->sense_key == 0) )
- {
- resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
+ if ((resplen < 0) && (p_msc->sense_key == 0)) {
+ resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t)p_msc->total_len);
}
- if ( resplen < 0 )
- {
+ if (resplen < 0) {
// unsupported command
- TU_LOG(MSC_DEBUG, " SCSI unsupported or failed command\r\n");
+ TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }
- else if (resplen == 0)
- {
- if (p_cbw->total_bytes)
- {
+ } else if (resplen == 0) {
+ if (p_cbw->total_bytes) {
// 6.7 The 13 Cases: case 4 (Hi > Dn)
- // TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
+ // TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// case 1 Hn = Dn: all good
p_msc->stage = MSC_STAGE_STATUS;
}
- }
- else
- {
- if ( p_cbw->total_bytes == 0 )
- {
+ } else {
+ if (p_cbw->total_bytes == 0) {
// 6.7 The 13 Cases: case 2 (Hn < Di)
- // TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
+ // TU_LOG_DRV(" SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ } else {
// cannot return more than host expect
- p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) p_msc->total_len) );
+ p_msc->total_len = tu_min32((uint32_t)resplen, p_cbw->total_bytes);
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) p_msc->total_len));
}
}
}
@@ -514,53 +444,41 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
case MSC_STAGE_DATA:
- TU_LOG(MSC_DEBUG, " SCSI Data [Lun%u]\r\n", p_cbw->lun);
- //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
+ TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
+ TU_ASSERT(xferred_bytes <= CFG_TUD_MSC_EP_BUFSIZE); // sanity check to avoid buffer overflow
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, _mscd_epbuf.buf, xferred_bytes, 2);
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
+ if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
p_msc->xferred_len += xferred_bytes;
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if ( p_msc->xferred_len >= p_msc->total_len ) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }else
- {
+ }else {
proc_read10_cmd(rhport, p_msc);
}
- }
- else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
- {
+ } else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) {
proc_write10_new_data(rhport, p_msc, xferred_bytes);
- }
- else
- {
+ } else {
p_msc->xferred_len += xferred_bytes;
// OUT transfer, invoke callback if needed
- if ( !is_data_in(p_cbw->dir) )
- {
- int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
+ if ( !is_data_in(p_cbw->dir) ) {
+ int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_epbuf.buf, (uint16_t) p_msc->total_len);
- if ( cb_result < 0 )
- {
+ if ( cb_result < 0 ) {
// unsupported command
- TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
+ TU_LOG_DRV(" SCSI unsupported command\r\n");
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
+ }else {
// TODO haven't implement this scenario any further yet
}
}
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if ( p_msc->xferred_len >= p_msc->total_len ) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }
- else
- {
+ } else {
// This scenario with command that take more than one transfer is already rejected at Command stage
TU_BREAKPOINT();
}
@@ -573,53 +491,52 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
case MSC_STAGE_STATUS_SENT:
// Wait for the Status phase to complete
- if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
- {
- TU_LOG(MSC_DEBUG, " SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
- // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
+ if ((ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t))) {
+ TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_csw, xferred_bytes, 2);
// Invoke complete callback if defined
// Note: There is racing issue with samd51 + qspi flash testing with arduino
// if complete_cb() is invoked after queuing the status.
- switch(p_cbw->command[0])
- {
+ switch (p_cbw->command[0]) {
case SCSI_CMD_READ_10:
- if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
- break;
+ if (tud_msc_read10_complete_cb) {
+ 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);
- break;
+ if (tud_msc_write10_complete_cb) {
+ 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);
- break;
+ if (tud_msc_scsi_complete_cb) {
+ tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
+ }
+ break;
}
- TU_ASSERT( prepare_cbw(rhport, p_msc) );
- }else
- {
+ TU_ASSERT(prepare_cbw(rhport, p_msc));
+ } else {
// Any xfer ended here is consider unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
- break;
+ break;
- default : break;
+ default: break;
}
- if ( p_msc->stage == MSC_STAGE_STATUS )
- {
+ if (p_msc->stage == MSC_STAGE_STATUS) {
// skip status if epin is currently stalled, will do it when received Clear Stall request
- if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
- {
- if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
- {
+ if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) {
+ if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) {
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
- // TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
+ // TU_LOG_DRV(" SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
usbd_edpt_stall(rhport, p_msc->ep_in);
- }else
- {
- TU_ASSERT( send_csw(rhport, p_msc) );
+ } else {
+ TU_ASSERT(send_csw(rhport, p_msc));
}
}
@@ -627,8 +544,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
// There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
// hope everything will work
- if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
- {
+ if ( usbd_edpt_stalled(rhport, p_msc->ep_in) ) {
usbd_edpt_clear_stall(rhport, p_msc->ep_in);
send_csw(rhport, p_msc);
}
@@ -644,82 +560,97 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
// In case of a failed status, sense key must be set for reason of failure
-static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
- (void) bufsize; // TODO refractor later
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize) {
+ (void)bufsize; // TODO refractor later
int32_t resplen;
mscd_interface_t* p_msc = &_mscd_itf;
- switch ( scsi_cmd[0] )
- {
+ switch (scsi_cmd[0]) {
case SCSI_CMD_TEST_UNIT_READY:
resplen = 0;
- if ( !tud_msc_test_unit_ready_cb(lun) )
- {
+ if (!tud_msc_test_unit_ready_cb(lun)) {
// Failed status response
- resplen = - 1;
+ resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
}
- break;
+ break;
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) )
- {
+ 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;
+ resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
}
}
- break;
+ break;
+
+ 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;
- case SCSI_CMD_READ_CAPACITY_10:
- {
+ // set default sense if not set by callback
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
+ }
+ }
+ break;
+
+
+ case SCSI_CMD_READ_CAPACITY_10: {
uint32_t block_count;
uint32_t block_size;
uint16_t block_size_u16;
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
- block_size = (uint32_t) block_size_u16;
+ block_size = (uint32_t)block_size_u16;
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
+ if (block_count == 0 || block_size == 0) {
resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
- }else
- {
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
+ } else {
scsi_read_capacity10_resp_t read_capa10;
- read_capa10.last_lba = tu_htonl(block_count-1);
+ read_capa10.last_lba = tu_htonl(block_count-1);
read_capa10.block_size = tu_htonl(block_size);
resplen = sizeof(read_capa10);
- memcpy(buffer, &read_capa10, (size_t) resplen);
+ TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) resplen));
}
}
break;
- case SCSI_CMD_READ_FORMAT_CAPACITY:
- {
+ case SCSI_CMD_READ_FORMAT_CAPACITY: {
scsi_read_format_capacity_data_t read_fmt_capa =
{
- .list_length = 8,
- .block_num = 0,
- .descriptor_type = 2, // formatted media
- .block_size_u16 = 0
+ .list_length = 8,
+ .block_num = 0,
+ .descriptor_type = 2, // formatted media
+ .block_size_u16 = 0
};
uint32_t block_count;
@@ -729,31 +660,30 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
// Invalid block size/count from callback, possibly unit is not ready
// stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
+ if (block_count == 0 || block_size == 0) {
resplen = -1;
// set default sense if not set by callback
- if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
- }else
- {
+ if (p_msc->sense_key == 0) {
+ set_sense_medium_not_present(lun);
+ }
+ } else {
read_fmt_capa.block_num = tu_htonl(block_count);
read_fmt_capa.block_size_u16 = tu_htons(block_size);
resplen = sizeof(read_fmt_capa);
- memcpy(buffer, &read_fmt_capa, (size_t) resplen);
+ TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
}
}
break;
- case SCSI_CMD_INQUIRY:
- {
+ case SCSI_CMD_INQUIRY: {
scsi_inquiry_resp_t inquiry_rsp =
{
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
+ .is_removable = 1,
+ .version = 2,
+ .response_data_format = 2,
+ .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
};
// vendor_id, product_id, product_rev is space padded string
@@ -764,54 +694,50 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
resplen = sizeof(inquiry_rsp);
- memcpy(buffer, &inquiry_rsp, (size_t) resplen);
+ TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &inquiry_rsp, (size_t) resplen));
}
break;
- case SCSI_CMD_MODE_SENSE_6:
- {
+ case SCSI_CMD_MODE_SENSE_6: {
scsi_mode_sense6_resp_t mode_resp =
{
- .data_len = 3,
- .medium_type = 0,
- .write_protected = false,
- .reserved = 0,
- .block_descriptor_len = 0 // no block descriptor are included
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
};
bool writable = true;
- if ( tud_msc_is_writable_cb )
- {
+ if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
resplen = sizeof(mode_resp);
- memcpy(buffer, &mode_resp, (size_t) resplen);
+ TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &mode_resp, (size_t) resplen));
}
break;
- case SCSI_CMD_REQUEST_SENSE:
- {
+ case SCSI_CMD_REQUEST_SENSE: {
scsi_sense_fixed_resp_t sense_rsp =
{
- .response_code = 0x70, // current, fixed format
- .valid = 1
+ .response_code = 0x70, // current, fixed format
+ .valid = 1
};
- sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
- sense_rsp.sense_key = (uint8_t) (p_msc->sense_key & 0x0F);
- sense_rsp.add_sense_code = p_msc->add_sense_code;
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+ sense_rsp.sense_key = (uint8_t)(p_msc->sense_key & 0x0F);
+ sense_rsp.add_sense_code = p_msc->add_sense_code;
sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
resplen = sizeof(sense_rsp);
- memcpy(buffer, &sense_rsp, (size_t) resplen);
+ 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);
+ if (tud_msc_request_sense_cb) {
+ resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t)bufsize);
}
// Clear sense data after copy
@@ -819,15 +745,15 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
}
break;
- default: resplen = -1; break;
+ default: resplen = -1;
+ break;
}
return resplen;
}
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
@@ -836,45 +762,37 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
// remaining bytes capped at class buffer
- int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+ int32_t nbytes = (int32_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
// Application can consume smaller bytes
uint32_t const offset = p_msc->xferred_len % block_sz;
- nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
+ nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes);
- if ( nbytes < 0 )
- {
+ if (nbytes < 0) {
// negative means error -> endpoint is stalled & status in CSW set to failed
- TU_LOG(MSC_DEBUG, " tud_msc_read10_cb() return -1\r\n");
+ TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n");
// set sense
set_sense_medium_not_present(p_cbw->lun);
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }
- else if ( nbytes == 0 )
- {
+ } else if (nbytes == 0) {
// zero means not ready -> simulate an transfer complete so that this driver callback will fired again
dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
- }
- else
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) nbytes), );
+ } else {
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
}
}
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
bool writable = true;
- if ( tud_msc_is_writable_cb )
- {
+ if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(p_cbw->lun);
}
- if ( !writable )
- {
+ if (!writable) {
// Not writable, complete this SCSI op with error
// Sense = Write protected
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
@@ -883,16 +801,15 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
}
// remaining bytes capped at class buffer
- uint16_t nbytes = (uint16_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
+ uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len);
// Write10 callback will be called later when usb transfer complete
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
+ TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),);
}
// process new data arrived from WRITE10
-static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
+static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes) {
+ msc_cbw_t const* p_cbw = &p_msc->cbw;
// block size already verified not zero
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
@@ -902,46 +819,36 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3
// Invoke callback to consume new data
uint32_t const offset = p_msc->xferred_len % block_sz;
- int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
+ int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
- if ( nbytes < 0 )
- {
+ if (nbytes < 0) {
// negative means error -> failed this scsi op
- TU_LOG(MSC_DEBUG, " tud_msc_write10_cb() return -1\r\n");
+ TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n");
// update actual byte before failed
p_msc->xferred_len += xferred_bytes;
- // Set sense
set_sense_medium_not_present(p_cbw->lun);
-
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
- }else
- {
- // Application consume less than what we got (including zero)
- if ( (uint32_t) nbytes < xferred_bytes )
- {
- uint32_t const left_over = xferred_bytes - (uint32_t) nbytes;
- if ( nbytes > 0 )
- {
- p_msc->xferred_len += (uint16_t) nbytes;
- memmove(_mscd_buf, _mscd_buf+nbytes, left_over);
+ } else {
+ if ((uint32_t)nbytes < xferred_bytes) {
+ // Application consume less than what we got (including zero)
+ const uint32_t left_over = xferred_bytes - (uint32_t)nbytes;
+ if (nbytes > 0) {
+ p_msc->xferred_len += (uint16_t)nbytes;
+ memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
}
- // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
+ // simulate a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
- }
- else
- {
+ } else {
// Application consume all bytes in our buffer
p_msc->xferred_len += xferred_bytes;
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
+ if (p_msc->xferred_len >= p_msc->total_len) {
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
- }else
- {
+ } else {
// prepare to receive more data from host
proc_write10_cmd(rhport, p_msc);
}
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 5839b168d..29acd280a 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -131,6 +131,9 @@ TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
// - 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);
+//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);
+
// Invoked when received REQUEST_SENSE
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
@@ -150,6 +153,7 @@ TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
// Internal Class Driver API
//--------------------------------------------------------------------+
void mscd_init (void);
+bool mscd_deinit (void);
void mscd_reset (uint8_t rhport);
uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index 934f79ff7..ef0635bbe 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -29,124 +29,129 @@
#if CFG_TUH_ENABLED && CFG_TUH_MSC
#include "host/usbh.h"
-#include "host/usbh_classdriver.h"
+#include "host/usbh_pvt.h"
#include "msc_host.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUH_MSC_LOG_LEVEL
+ #define CFG_TUH_MSC_LOG_LEVEL CFG_TUH_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MSC_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-enum
-{
+enum {
MSC_STAGE_IDLE = 0,
MSC_STAGE_CMD,
MSC_STAGE_DATA,
MSC_STAGE_STATUS,
};
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
-
uint8_t max_lun;
volatile bool configured; // Receive SET_CONFIGURE
volatile bool mounted; // Enumeration is complete
+ // SCSI command data
+ uint8_t stage;
+ void* buffer;
+ tuh_msc_complete_cb_t complete_cb;
+ uintptr_t complete_arg;
+
struct {
uint32_t block_size;
uint32_t block_count;
} capacity[CFG_TUH_MSC_MAXLUN];
+} msch_interface_t;
- //------------- SCSI -------------//
- uint8_t stage;
- void* buffer;
- tuh_msc_complete_cb_t complete_cb;
-
- msc_cbw_t cbw;
- msc_csw_t csw;
-}msch_interface_t;
+typedef struct {
+ TUH_EPBUF_TYPE_DEF(msc_cbw_t, cbw);
+ TUH_EPBUF_TYPE_DEF(msc_csw_t, csw);
+} msch_epbuf_t;
-CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
+static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
+CFG_TUH_MEM_SECTION static msch_epbuf_t _msch_epbuf[CFG_TUH_DEVICE_MAX];
-// buffer used to read scsi information when mounted
-// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4)
-static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
+TU_ATTR_ALWAYS_INLINE static inline msch_interface_t* get_itf(uint8_t daddr) {
+ return &_msch_itf[daddr - 1];
+}
-TU_ATTR_ALWAYS_INLINE
-static inline msch_interface_t* get_itf(uint8_t dev_addr)
-{
- return &_msch_itf[dev_addr-1];
+TU_ATTR_ALWAYS_INLINE static inline msch_epbuf_t* get_epbuf(uint8_t daddr) {
+ return &_msch_epbuf[daddr - 1];
}
//--------------------------------------------------------------------+
// PUBLIC API
//--------------------------------------------------------------------+
-uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
-{
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->max_lun;
}
-uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->capacity[lun].block_count;
}
-uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
-{
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->capacity[lun].block_size;
}
-bool tuh_msc_mounted(uint8_t dev_addr)
-{
+bool tuh_msc_mounted(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
return p_msc->mounted;
}
-bool tuh_msc_ready(uint8_t dev_addr)
-{
+bool tuh_msc_ready(uint8_t dev_addr) {
msch_interface_t* p_msc = get_itf(dev_addr);
- return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in);
+ return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
}
//--------------------------------------------------------------------+
// PUBLIC API: SCSI COMMAND
//--------------------------------------------------------------------+
-static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
-{
+static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
tu_memclr(cbw, sizeof(msc_cbw_t));
cbw->signature = MSC_CBW_SIGNATURE;
cbw->tag = 0x54555342; // TUSB
cbw->lun = lun;
}
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb)
-{
- msch_interface_t* p_msc = get_itf(dev_addr);
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+ msch_interface_t* p_msc = get_itf(daddr);
TU_VERIFY(p_msc->configured);
- // TODO claim endpoint
+ // claim endpoint
+ TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
+ msch_epbuf_t* epbuf = get_epbuf(daddr);
- p_msc->cbw = *cbw;
- p_msc->stage = MSC_STAGE_CMD;
+ epbuf->cbw = *cbw;
p_msc->buffer = data;
p_msc->complete_cb = complete_cb;
+ p_msc->complete_arg = arg;
+ p_msc->stage = MSC_STAGE_CMD;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
+ if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &epbuf->cbw, sizeof(msc_cbw_t))) {
+ usbh_edpt_release(daddr, p_msc->ep_out);
+ return false;
+ }
return true;
}
-bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb)
-{
- msch_interface_t* p_msc = get_itf(dev_addr);
- TU_VERIFY(p_msc->configured);
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
@@ -156,11 +161,11 @@ bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_r
cbw.cmd_len = sizeof(scsi_read_capacity10_t);
cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
- return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb)
-{
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
@@ -171,18 +176,16 @@ bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* respons
cbw.dir = TUSB_DIR_IN_MASK;
cbw.cmd_len = sizeof(scsi_inquiry_t);
- scsi_inquiry_t const cmd_inquiry =
- {
- .cmd_code = SCSI_CMD_INQUIRY,
- .alloc_length = sizeof(scsi_inquiry_resp_t)
+ scsi_inquiry_t const cmd_inquiry = {
+ .cmd_code = SCSI_CMD_INQUIRY,
+ .alloc_length = sizeof(scsi_inquiry_resp_t)
};
memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
- return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb)
-{
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->configured);
@@ -190,16 +193,16 @@ bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_
cbw_init(&cbw, lun);
cbw.total_bytes = 0;
- cbw.dir = TUSB_DIR_OUT;
- cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
- cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
- cbw.command[1] = lun; // according to wiki TODO need verification
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+ cbw.command[1] = lun; // according to wiki TODO need verification
- return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb);
+ return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
}
-bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb)
-{
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msc_cbw_t cbw;
cbw_init(&cbw, lun);
@@ -207,73 +210,64 @@ bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_ms
cbw.dir = TUSB_DIR_IN_MASK;
cbw.cmd_len = sizeof(scsi_request_sense_t);
- scsi_request_sense_t const cmd_request_sense =
- {
- .cmd_code = SCSI_CMD_REQUEST_SENSE,
- .alloc_length = 18
+ scsi_request_sense_t const cmd_request_sense = {
+ .cmd_code = SCSI_CMD_REQUEST_SENSE,
+ .alloc_length = 18
};
-
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
- return tuh_msc_scsi_command(dev_addr, &cbw, resposne, complete_cb);
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
}
-bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
-{
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
-
- cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
- cbw.dir = TUSB_DIR_IN_MASK;
- cbw.cmd_len = sizeof(scsi_read10_t);
-
- scsi_read10_t const cmd_read10 =
- {
- .cmd_code = SCSI_CMD_READ_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+
+ cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read10_t);
+
+ scsi_read10_t const cmd_read10 = {
+ .cmd_code = SCSI_CMD_READ_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
-
memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
-
- return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
}
-
-bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
-{
+
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
+ tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
msch_interface_t* p_msc = get_itf(dev_addr);
TU_VERIFY(p_msc->mounted);
msc_cbw_t cbw;
cbw_init(&cbw, lun);
- cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
cbw.dir = TUSB_DIR_OUT;
cbw.cmd_len = sizeof(scsi_write10_t);
- scsi_write10_t const cmd_write10 =
- {
- .cmd_code = SCSI_CMD_WRITE_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+ scsi_write10_t const cmd_write10 = {
+ .cmd_code = SCSI_CMD_WRITE_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
-
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
- return tuh_msc_scsi_command(dev_addr, &cbw, (void*)(uintptr_t) buffer, complete_cb);
+ return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
}
#if 0
// MSC interface Reset (not used now)
-bool tuh_msc_reset(uint8_t dev_addr)
-{
- tusb_control_request_t const new_request =
- {
- .bmRequestType_bit =
- {
+bool tuh_msc_reset(uint8_t dev_addr) {
+ tusb_control_request_t const new_request = {
+ .bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
@@ -290,65 +284,78 @@ bool tuh_msc_reset(uint8_t dev_addr)
//--------------------------------------------------------------------+
// CLASS-USBH API
//--------------------------------------------------------------------+
-void msch_init(void)
-{
+bool msch_init(void) {
+ TU_LOG_DRV("sizeof(msch_interface_t) = %u\r\n", sizeof(msch_interface_t));
+ TU_LOG_DRV("sizeof(msch_epbuf_t) = %u\r\n", sizeof(msch_epbuf_t));
tu_memclr(_msch_itf, sizeof(_msch_itf));
+ return true;
}
-void msch_close(uint8_t dev_addr)
-{
- TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
+bool msch_deinit(void) {
+ return true;
+}
+void msch_close(uint8_t dev_addr) {
+ TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured,);
+
+ TU_LOG_DRV(" MSCh close addr = %d\r\n", dev_addr);
// invoke Application Callback
- if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
+ if (p_msc->mounted) {
+ if (tuh_msc_umount_cb) {
+ tuh_msc_umount_cb(dev_addr);
+ }
+ }
tu_memclr(p_msc, sizeof(msch_interface_t));
}
-bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
msch_interface_t* p_msc = get_itf(dev_addr);
- msc_cbw_t const * cbw = &p_msc->cbw;
- msc_csw_t * csw = &p_msc->csw;
+ msch_epbuf_t* epbuf = get_epbuf(dev_addr);
+ msc_cbw_t const * cbw = &epbuf->cbw;
+ msc_csw_t * csw = &epbuf->csw;
- switch (p_msc->stage)
- {
+ switch (p_msc->stage) {
case MSC_STAGE_CMD:
// Must be Command Block
- TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
-
- if ( cbw->total_bytes && p_msc->buffer )
- {
+ TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
+ if (cbw->total_bytes && p_msc->buffer) {
// Data stage if any
p_msc->stage = MSC_STAGE_DATA;
-
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
- }else
- {
- // Status stage
- p_msc->stage = MSC_STAGE_STATUS;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
+ break;
}
- break;
+
+ TU_ATTR_FALLTHROUGH; // fallthrough to status stage
case MSC_STAGE_DATA:
// Status stage
p_msc->stage = MSC_STAGE_STATUS;
- TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
- break;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) csw, (uint16_t) sizeof(msc_csw_t)));
+ break;
case MSC_STAGE_STATUS:
// SCSI op is complete
p_msc->stage = MSC_STAGE_IDLE;
- if (p_msc->complete_cb) p_msc->complete_cb(dev_addr, cbw, csw);
- break;
+ if (p_msc->complete_cb) {
+ tuh_msc_complete_data_t const cb_data = {
+ .cbw = cbw,
+ .csw = csw,
+ .scsi_data = p_msc->buffer,
+ .user_arg = p_msc->complete_arg
+ };
+ p_msc->complete_cb(dev_addr, &cb_data);
+ }
+ break;
- // unknown state
- default: break;
+ // unknown state
+ default:
+ break;
}
return true;
@@ -357,39 +364,35 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
//--------------------------------------------------------------------+
// MSC Enumeration
//--------------------------------------------------------------------+
+static void config_get_maxlun_complete(tuh_xfer_t* xfer);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
+static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
+static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
-static void config_get_maxlun_complete (tuh_xfer_t* xfer);
-static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
-static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
-static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
-
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
-{
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
(void) rhport;
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
+ MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
// msc driver length is fixed
- uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+ uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
+ desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
TU_ASSERT(drv_len <= max_len);
msch_interface_t* p_msc = get_itf(dev_addr);
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
+ tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
- for(uint32_t i=0; i<2; i++)
- {
+ for (uint32_t i = 0; i < 2; i++) {
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
- if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
- {
+ if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
p_msc->ep_in = ep_desc->bEndpointAddress;
- }else
- {
+ } else {
p_msc->ep_out = ep_desc->bEndpointAddress;
}
- ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
+ ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
}
p_msc->itf_num = desc_itf->bInterfaceNumber;
@@ -397,98 +400,106 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
return true;
}
-bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
-{
- msch_interface_t* p_msc = get_itf(dev_addr);
+bool msch_set_config(uint8_t daddr, uint8_t itf_num) {
+ msch_interface_t* p_msc = get_itf(daddr);
TU_ASSERT(p_msc->itf_num == itf_num);
-
p_msc->configured = true;
//------------- Get Max Lun -------------//
- TU_LOG2("MSC Get Max Lun\r\n");
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN
- },
- .bRequest = MSC_REQ_GET_MAX_LUN,
- .wValue = 0,
- .wIndex = itf_num,
- .wLength = 1
+ TU_LOG_DRV("MSC Get Max Lun\r\n");
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = MSC_REQ_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = itf_num,
+ .wLength = 1
};
- tuh_xfer_t xfer =
- {
- .daddr = dev_addr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = &p_msc->max_lun,
- .complete_cb = config_get_maxlun_complete,
- .user_data = 0
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = enum_buf,
+ .complete_cb = config_get_maxlun_complete,
+ .user_data = 0
};
TU_ASSERT(tuh_control_xfer(&xfer));
return true;
}
-static void config_get_maxlun_complete (tuh_xfer_t* xfer)
-{
+static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
uint8_t const daddr = xfer->daddr;
msch_interface_t* p_msc = get_itf(daddr);
- // STALL means zero
- p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0;
- p_msc->max_lun++; // MAX LUN is minus 1 by specs
+ // MAXLUN's response is minus 1 by specs, STALL means 1
+ if (XFER_RESULT_SUCCESS == xfer->result) {
+ uint8_t* enum_buf = usbh_get_enum_buf();
+ p_msc->max_lun = enum_buf[0] + 1;
+ } else {
+ p_msc->max_lun = 1;
+ }
+
+ TU_LOG_DRV(" Max LUN = %u\r\n", p_msc->max_lun);
// TODO multiple LUN support
- TU_LOG2("SCSI Test Unit Ready\r\n");
+ TU_LOG_DRV("SCSI Test Unit Ready\r\n");
uint8_t const lun = 0;
- tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete);
+ tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
}
-static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
-{
- if (csw->status == 0)
- {
+static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
+ msc_cbw_t const* cbw = cb_data->cbw;
+ msc_csw_t const* csw = cb_data->csw;
+ uint8_t* enum_buf = usbh_get_enum_buf();
+
+ if (csw->status == 0) {
// Unit is ready, read its capacity
- TU_LOG2("SCSI Read Capacity\r\n");
- tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete);
- }else
- {
+ TU_LOG_DRV("SCSI Read Capacity\r\n");
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf,
+ config_read_capacity_complete, 0);
+ } else {
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
// with Request Sense to start working !!
// TODO limit number of retries
- TU_LOG2("SCSI Request Sense\r\n");
- TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete));
+ TU_LOG_DRV("SCSI Request Sense\r\n");
+ TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, enum_buf, config_request_sense_complete, 0));
}
return true;
}
-static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
-{
+static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
+ msc_cbw_t const* cbw = cb_data->cbw;
+ msc_csw_t const* csw = cb_data->csw;
+
TU_ASSERT(csw->status == 0);
- TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete));
+ TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete, 0));
return true;
}
-static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
-{
+static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
+ msc_cbw_t const* cbw = cb_data->cbw;
+ msc_csw_t const* csw = cb_data->csw;
TU_ASSERT(csw->status == 0);
-
msch_interface_t* p_msc = get_itf(dev_addr);
+ uint8_t* enum_buf = usbh_get_enum_buf();
// Capacity response field: Block size and Last LBA are both Big-Endian
- scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
+ scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) (uintptr_t) enum_buf;
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
// Mark enumeration is complete
p_msc->mounted = true;
- if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
+ if (tuh_msc_mount_cb) {
+ 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 8d95980f9..09d777066 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MSC_HOST_H_
-#define _TUSB_MSC_HOST_H_
+#ifndef TUSB_MSC_HOST_H_
+#define TUSB_MSC_HOST_H_
#include "msc.h"
@@ -41,7 +41,14 @@
#define CFG_TUH_MSC_MAXLUN 4
#endif
-typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+typedef struct {
+ msc_cbw_t const* cbw; // SCSI command
+ msc_csw_t const* csw; // SCSI status
+ void* scsi_data; // SCSI Data
+ uintptr_t user_arg; // user argument
+}tuh_msc_complete_data_t;
+
+typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
//--------------------------------------------------------------------+
// Application API
@@ -66,33 +73,38 @@ uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
// Complete callback is invoked when SCSI op is complete.
// return true if success, false if there is already pending operation.
-bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb);
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
+bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Inquiry command
// Complete callback is invoked when SCSI op is complete.
-bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb);
+// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Test Unit Ready command
// Complete callback is invoked when SCSI op is complete.
-bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb);
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Request Sense 10 command
// Complete callback is invoked when SCSI op is complete.
-bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb);
+// NOTE: response must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
// Complete callback is invoked when SCSI op is complete.
-bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
// Complete callback is invoked when SCSI op is complete.
-bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
+// NOTE: buffer must be accessible by USB/DMA controller, aligned correctly and multiple of cache line if enabled
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
// Perform SCSI Read Capacity 10 command
// Complete callback is invoked when SCSI op is complete.
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
-bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb);
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
//------------- Application Callback -------------//
@@ -106,9 +118,10 @@ TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
// Internal Class Driver API
//--------------------------------------------------------------------+
-void msch_init (void);
+bool msch_init (void);
+bool msch_deinit (void);
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
-bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
+bool msch_set_config (uint8_t daddr, uint8_t itf_num);
void msch_close (uint8_t dev_addr);
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
@@ -116,4 +129,4 @@ bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, ui
}
#endif
-#endif /* _TUSB_MSC_HOST_H_ */
+#endif
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index 5f316762f..a54e6d662 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Peter Lawrence
@@ -35,13 +35,18 @@
#include "net_device.h"
#include "rndis_protocol.h"
-void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+extern void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+
+#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
+#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
+
+#define NETD_PACKET_SIZE (CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN)
+#define NETD_CONTROL_SIZE 120
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
@@ -51,98 +56,67 @@ typedef struct
bool ecm_mode;
- // Endpoint descriptor use to open/close when receving SetInterface
+ // Endpoint descriptor use to open/close when receiving SetInterface
// TODO since configuration descriptor may not be long-lived memory, we should
// keep a copy of endpoint attribute instead
uint8_t const * ecm_desc_epdata;
-
} netd_interface_t;
-#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
-#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
-
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
-
-struct ecm_notify_struct
-{
+typedef struct ecm_notify_struct {
tusb_control_request_t header;
uint32_t downlink, uplink;
-};
-
-static const struct ecm_notify_struct ecm_notify_nc =
-{
- .header = {
- .bmRequestType = 0xA1,
- .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
- .wValue = 1 /* Connected */,
- .wLength = 0,
- },
-};
+} ecm_notify_t;
-static const struct ecm_notify_struct ecm_notify_csc =
-{
- .header = {
- .bmRequestType = 0xA1,
- .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
- .wLength = 8,
- },
- .downlink = 9728000,
- .uplink = 9728000,
-};
+typedef struct {
+ TUD_EPBUF_DEF(rx, NETD_PACKET_SIZE);
+ TUD_EPBUF_DEF(tx, NETD_PACKET_SIZE);
-// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union
-{
- uint8_t rndis_buf[120];
- struct ecm_notify_struct ecm_buf;
-} notify;
+ TUD_EPBUF_DEF(notify, sizeof(ecm_notify_t));
+ TUD_EPBUF_DEF(ctrl, NETD_CONTROL_SIZE);
+} netd_epbuf_t;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-// TODO remove CFG_TUSB_MEM_SECTION
-CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf;
-
+static netd_interface_t _netd_itf;
+CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
static bool can_xmit;
-void tud_network_recv_renew(void)
-{
- usbd_edpt_xfer(0, _netd_itf.ep_out, received, sizeof(received));
+void tud_network_recv_renew(void) {
+ usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
}
-static void do_in_xfer(uint8_t *buf, uint16_t len)
-{
+static void do_in_xfer(uint8_t *buf, uint16_t len) {
can_xmit = false;
usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len);
}
-void netd_report(uint8_t *buf, uint16_t len)
-{
- uint8_t const rhport = 0;
+void netd_report(uint8_t *buf, uint16_t len) {
+ const uint8_t rhport = 0;
+ len = tu_min16(len, sizeof(ecm_notify_t));
- // skip if previous report not yet acknowledged by host
- if ( usbd_edpt_busy(rhport, _netd_itf.ep_notif) ) return;
- usbd_edpt_xfer(rhport, _netd_itf.ep_notif, buf, len);
+ TU_VERIFY(usbd_edpt_claim(rhport, _netd_itf.ep_notif), );
+ memcpy(_netd_epbuf.notify, buf, len);
+ usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void netd_init(void)
-{
+void netd_init(void) {
tu_memclr(&_netd_itf, sizeof(_netd_itf));
}
-void netd_reset(uint8_t rhport)
-{
- (void) rhport;
+bool netd_deinit(void) {
+ return true;
+}
+void netd_reset(uint8_t rhport) {
+ (void) rhport;
netd_init();
}
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
@@ -166,21 +140,19 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
uint8_t const * p_desc = tu_desc_next( itf_desc );
// Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
+ while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// notification endpoint (if any)
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
- _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ _netd_itf.ep_notif = ((tusb_desc_endpoint_t const*)p_desc)->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface -------------//
@@ -190,27 +162,24 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// - 1 : IN & OUT endpoints for active networking
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
- do
- {
+ do {
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
- }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
+ } while (_netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
- if ( _netd_itf.ecm_mode )
- {
+ if (_netd_itf.ecm_mode) {
// ECM by default is in-active, save the endpoint attribute
// to open later when received setInterface
_netd_itf.ecm_desc_epdata = p_desc;
- }else
- {
+ } else {
// Open endpoint pair for RNDIS
- TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0);
tud_network_init_cb();
@@ -226,38 +195,50 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
return drv_len;
}
-static void ecm_report(bool nc)
-{
- notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
- notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
- netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
+static void ecm_report(bool nc) {
+ const ecm_notify_t ecm_notify_nc = {
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
+ .wValue = 1, /* Connected */
+ .wLength = 0,
+ },
+ };
+
+ const ecm_notify_t ecm_notify_csc = {
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */
+ .wLength = 8,
+ },
+ .downlink = 9728000,
+ .uplink = 9728000,
+ };
+
+ ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc;
+ notify.header.wIndex = _netd_itf.itf_num;
+ netd_report((uint8_t *)&notify, (nc) ? sizeof(notify.header) : sizeof(notify));
}
// 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 netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage == CONTROL_STAGE_SETUP )
- {
- switch ( request->bmRequestType_bit.type )
- {
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+ if (stage == CONTROL_STAGE_SETUP) {
+ switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
- switch ( request->bRequest )
- {
- case TUSB_REQ_GET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
+ switch (request->bRequest) {
+ case TUSB_REQ_GET_INTERFACE: {
+ uint8_t const req_itfnum = (uint8_t)request->wIndex;
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
}
break;
- case TUSB_REQ_SET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- uint8_t const req_alt = (uint8_t) request->wValue;
+ case TUSB_REQ_SET_INTERFACE: {
+ uint8_t const req_itfnum = (uint8_t)request->wIndex;
+ uint8_t const req_alt = (uint8_t)request->wValue;
// Only valid for Data Interface with Alternate is either 0 or 1
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
@@ -267,14 +248,14 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
_netd_itf.itf_data_alt = req_alt;
- if ( _netd_itf.itf_data_alt )
- {
+ if (_netd_itf.itf_data_alt) {
// TODO since we don't actually close endpoint
// hack here to not re-open it
- if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
- {
+ if (_netd_itf.ep_in == 0 && _netd_itf.ep_out == 0) {
TU_ASSERT(_netd_itf.ecm_desc_epdata);
- TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+ TU_ASSERT(
+ usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &
+ _netd_itf.ep_in));
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
@@ -282,8 +263,7 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
can_xmit = true; // we are ready to transmit a packet
tud_network_recv_renew(); // prepare for incoming packets
}
- }else
- {
+ } else {
// TODO close the endpoint pair
// For now pretend that we did, this should have no harm since host won't try to
// communicate with the endpoints again
@@ -297,50 +277,39 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
// unsupported request
default: return false;
}
- break;
+ break;
case TUSB_REQ_TYPE_CLASS:
- TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+ TU_VERIFY(_netd_itf.itf_num == request->wIndex);
- if (_netd_itf.ecm_mode)
- {
+ if (_netd_itf.ecm_mode) {
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
- if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
- {
+ if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
tud_control_xfer(rhport, request, NULL, 0);
ecm_report(true);
}
- }
- else
- {
- if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
- {
- rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
+ } else {
+ if (request->bmRequestType_bit.direction == TUSB_DIR_IN) {
+ rndis_generic_msg_t* rndis_msg = (rndis_generic_msg_t*)((void*)_netd_epbuf.ctrl);
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
- TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
- tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) msglen);
- }
- else
- {
- tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) sizeof(notify.rndis_buf));
+ TU_ASSERT(msglen <= NETD_CONTROL_SIZE);
+ tud_control_xfer(rhport, request, _netd_epbuf.ctrl, (uint16_t)msglen);
+ } else {
+ tud_control_xfer(rhport, request, _netd_epbuf.ctrl, NETD_CONTROL_SIZE);
}
}
- break;
+ break;
// unsupported request
default: return false;
}
- }
- else if ( stage == CONTROL_STAGE_DATA )
- {
+ } else if (stage == CONTROL_STAGE_DATA) {
// Handle RNDIS class control OUT only
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
- request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
- _netd_itf.itf_num == request->wIndex)
- {
- if ( !_netd_itf.ecm_mode )
- {
- rndis_class_set_handler(notify.rndis_buf, request->wLength);
+ request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
+ _netd_itf.itf_num == request->wIndex) {
+ if (!_netd_itf.ecm_mode) {
+ rndis_class_set_handler(_netd_epbuf.ctrl, request->wLength);
}
}
}
@@ -348,92 +317,77 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
return true;
}
-static void handle_incoming_packet(uint32_t len)
-{
- uint8_t *pnt = received;
+static void handle_incoming_packet(uint32_t len) {
+ uint8_t* pnt = _netd_epbuf.rx;
uint32_t size = 0;
- if (_netd_itf.ecm_mode)
- {
+ if (_netd_itf.ecm_mode) {
size = len;
- }
- else
- {
- rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
- if (len >= sizeof(rndis_data_packet_t))
- if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
- if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
- {
- pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
+ } else {
+ rndis_data_packet_t* r = (rndis_data_packet_t*)((void*)pnt);
+ if (len >= sizeof(rndis_data_packet_t)) {
+ if ((r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len)) {
+ if ((r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len) {
+ pnt = &_netd_epbuf.rx[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
size = r->DataLength;
}
+ }
+ }
}
- if (!tud_network_recv_cb(pnt, (uint16_t) size))
- {
+ if (!tud_network_recv_cb(pnt, (uint16_t)size)) {
/* if a buffer was never handled by user code, we must renew on the user's behalf */
tud_network_recv_renew();
}
}
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) rhport;
- (void) result;
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void)rhport;
+ (void)result;
/* new packet received */
- if ( ep_addr == _netd_itf.ep_out )
- {
+ if (ep_addr == _netd_itf.ep_out) {
handle_incoming_packet(xferred_bytes);
}
/* data transmission finished */
- if ( ep_addr == _netd_itf.ep_in )
- {
+ if (ep_addr == _netd_itf.ep_in) {
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
- if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
- {
+ if (xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE))) {
do_in_xfer(NULL, 0); /* a ZLP is needed */
- }
- else
- {
+ } else {
/* we're finally finished */
can_xmit = true;
}
}
- if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
- {
- if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
+ if (_netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif)) {
+ if (sizeof(tusb_control_request_t) == xferred_bytes) {
+ ecm_report(false);
+ }
}
return true;
}
-bool tud_network_can_xmit(uint16_t size)
-{
+bool tud_network_can_xmit(uint16_t size) {
(void)size;
-
return can_xmit;
}
-void tud_network_xmit(void *ref, uint16_t arg)
-{
- uint8_t *data;
- uint16_t len;
-
- if (!can_xmit)
+void tud_network_xmit(void *ref, uint16_t arg) {
+ if (!can_xmit) {
return;
+ }
- len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
- data = transmitted + len;
+ uint16_t len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
+ uint8_t* data = _netd_epbuf.tx + len;
len += tud_network_xmit_cb(data, ref, arg);
- if (!_netd_itf.ecm_mode)
- {
- rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
+ if (!_netd_itf.ecm_mode) {
+ rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) _netd_epbuf.tx);
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = len;
@@ -441,7 +395,7 @@ void tud_network_xmit(void *ref, uint16_t arg)
hdr->DataLength = len - sizeof(rndis_data_packet_t);
}
- do_in_xfer(transmitted, len);
+ do_in_xfer(_netd_epbuf.tx, len);
}
#endif
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 96ba11fbc..0245a87f2 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2021, Ha Thach (tinyusb.org)
+ * Copyright (c) 2024, Hardy Griech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +25,58 @@
* This file is part of the TinyUSB stack.
*/
-
#ifndef _TUSB_NCM_H_
#define _TUSB_NCM_H_
#include "common/tusb_common.h"
-#ifdef __cplusplus
- extern "C" {
+// NTB buffers size for reception side, must be >> MTU to avoid TCP retransmission (driver issue ?)
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
#endif
-// Table 4.3 Data Class Interface Protocol Codes
-typedef enum
-{
- NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
-} ncm_data_interface_protocol_code_t;
+// NTB buffers size for reception side, must be > MTU
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
+ #define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
+#endif
+
+// Number of NTB buffers for reception side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+// 1 - good performance
+// 2 - up to 30% more performance with iperf with small packets
+// >2 - no performance gain
+// On High-Speed (STM32F7) :
+// No performance gain
+#ifndef CFG_TUD_NCM_OUT_NTB_N
+ #define CFG_TUD_NCM_OUT_NTB_N 1
+#endif
+// Number of NTB buffers for transmission side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+// 1 - good performance but SystemView shows lost events (on load test)
+// 2 - up to 50% more performance with iperf with small packets, "tud_network_can_xmit: request blocked"
+// happens from time to time with SystemView
+// 3 - "tud_network_can_xmit: request blocked" never happens
+// >3 - no performance gain
+// On High-Speed (STM32F7) :
+// No performance gain
+#ifndef CFG_TUD_NCM_IN_NTB_N
+ #define CFG_TUD_NCM_IN_NTB_N 1
+#endif
+
+// How many datagrams it is allowed to put into an NTB for transmission side
+#ifndef CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB
+ #define CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB 8
+#endif
+
+// This tells the host how many datagrams it is allowed to put into an NTB
+#ifndef CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB
+ #define CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB 6
+#endif
// Table 6.2 Class-Specific Request Codes for Network Control Model subclass
typedef enum
@@ -62,8 +99,66 @@ typedef enum
NCM_SET_CRC_MODE = 0x8A,
} ncm_request_code_t;
-#ifdef __cplusplus
- }
-#endif
+#define NTH16_SIGNATURE 0x484D434E
+#define NDP16_SIGNATURE_NCM0 0x304D434E
+#define NDP16_SIGNATURE_NCM1 0x314D434E
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t wLength;
+ uint16_t bmNtbFormatsSupported;
+ uint32_t dwNtbInMaxSize;
+ uint16_t wNdbInDivisor;
+ uint16_t wNdbInPayloadRemainder;
+ uint16_t wNdbInAlignment;
+ uint16_t wReserved;
+ uint32_t dwNtbOutMaxSize;
+ uint16_t wNdbOutDivisor;
+ uint16_t wNdbOutPayloadRemainder;
+ uint16_t wNdbOutAlignment;
+ uint16_t wNtbOutMaxDatagrams;
+} ntb_parameters_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t dwSignature;
+ uint16_t wHeaderLength;
+ uint16_t wSequence;
+ uint16_t wBlockLength;
+ uint16_t wNdpIndex;
+} nth16_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t wDatagramIndex;
+ uint16_t wDatagramLength;
+} ndp16_datagram_t;
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t dwSignature;
+ uint16_t wLength;
+ uint16_t wNextNdpIndex;
+ //ndp16_datagram_t datagram[];
+} ndp16_t;
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ nth16_t nth;
+ ndp16_t ndp;
+ ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB + 1];
+ };
+ uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
+} xmit_ntb_t;
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ nth16_t nth;
+ // only the header is at a guaranteed position
+ };
+ uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+} recv_ntb_t;
+
+typedef struct {
+ tusb_control_request_t header;
+ uint32_t downlink;
+ uint32_t uplink;
+} ncm_notify_t;
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index 00892b49c..f9fda0698 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -1,9 +1,10 @@
/*
* The MIT License (MIT)
*
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2024 Hardy Griech
* Copyright (c) 2020 Jacob Berg Potter
* Copyright (c) 2020 Peter Lawrence
- * 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
@@ -26,486 +27,918 @@
* This file is part of the TinyUSB stack.
*/
+/**
+ * Small Glossary (from the spec)
+ * --------------
+ * Datagram - A collection of bytes forming a single item of information, passed as a unit from source to destination.
+ * NCM - Network Control Model
+ * NDP - NCM Datagram Pointer: NTB structure that delineates Datagrams (typically Ethernet frames) within an NTB
+ * NTB - NCM Transfer Block: a data structure for efficient USB encapsulation of one or more datagrams
+ * Each NTB is designed to be a single USB transfer
+ * NTH - NTB Header: a data structure at the front of each NTB, which provides the information needed to validate
+ * the NTB and begin decoding
+ *
+ * Some explanations
+ * -----------------
+ * - rhport is the USB port of the device, in most cases "0"
+ * - itf_data_alt if != 0 -> data xmit/recv are allowed (see spec)
+ * - ep_in IN endpoints take data from the device intended to go in to the host (the device transmits)
+ * - ep_out OUT endpoints send data out of the host to the device (the device receives)
+ */
+
#include "tusb_option.h"
-#if ( CFG_TUD_ENABLED && CFG_TUD_NCM )
+#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
#include "device/usbd.h"
#include "device/usbd_pvt.h"
+
+#include "ncm.h"
#include "net_device.h"
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_NCM_LOG_LEVEL
+ #define CFG_TUD_NCM_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
-#define NTH16_SIGNATURE 0x484D434E
-#define NDP16_SIGNATURE_NCM0 0x304D434E
-#define NDP16_SIGNATURE_NCM1 0x314D434E
+// Alignment must be 4
+#define TUD_NCM_ALIGNMENT 4
+// calculate alignment of xmit datagrams within an NTB
+#define XMIT_ALIGN_OFFSET(x) ((TUD_NCM_ALIGNMENT - ((x) & (TUD_NCM_ALIGNMENT - 1))) & (TUD_NCM_ALIGNMENT - 1))
-typedef struct TU_ATTR_PACKED
-{
- uint16_t wLength;
- uint16_t bmNtbFormatsSupported;
- uint32_t dwNtbInMaxSize;
- uint16_t wNdbInDivisor;
- uint16_t wNdbInPayloadRemainder;
- uint16_t wNdbInAlignment;
- uint16_t wReserved;
- uint32_t dwNtbOutMaxSize;
- uint16_t wNdbOutDivisor;
- uint16_t wNdbOutPayloadRemainder;
- uint16_t wNdbOutAlignment;
- uint16_t wNtbOutMaxDatagrams;
-} ntb_parameters_t;
+//-----------------------------------------------------------------------------
+//
+// Module global things
+//
+#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
+#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
-typedef struct TU_ATTR_PACKED
-{
- uint32_t dwSignature;
- uint16_t wHeaderLength;
- uint16_t wSequence;
- uint16_t wBlockLength;
- uint16_t wNdpIndex;
-} nth16_t;
+typedef struct {
+ // general
+ uint8_t ep_in; // endpoint for outgoing datagrams (naming is a little bit confusing)
+ uint8_t ep_out; // endpoint for incoming datagrams (naming is a little bit confusing)
+ uint8_t ep_notif; // endpoint for notifications
+ uint8_t itf_num; // interface number
+ uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
+ uint8_t rhport; // storage of \a rhport because some callbacks are done without it
-typedef struct TU_ATTR_PACKED
-{
- uint16_t wDatagramIndex;
- uint16_t wDatagramLength;
-} ndp16_datagram_t;
+ // recv handling
+ recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
+ recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic
+ recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver
+ recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic
+ uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
-typedef struct TU_ATTR_PACKED
-{
- uint32_t dwSignature;
- uint16_t wLength;
- uint16_t wNextNdpIndex;
- ndp16_datagram_t datagram[];
-} ndp16_t;
+ // xmit handling
+ xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs
+ xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB
+ xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB
+ xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver
+ uint16_t xmit_sequence; // NTB sequence counter
+ uint16_t xmit_glue_ntb_datagram_ndx; // index into \a xmit_glue_ntb_datagram
-typedef union TU_ATTR_PACKED {
+ // notification handling
+ enum {
+ NOTIFICATION_SPEED,
+ NOTIFICATION_CONNECTED,
+ NOTIFICATION_DONE
+ } notification_xmit_state; // state of notification transmission
+ bool notification_xmit_is_running; // notification is currently transmitted
+
+ // misc
+ bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
+ bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again
+} ncm_interface_t;
+
+typedef struct {
struct {
- nth16_t nth;
- ndp16_t ndp;
- };
- uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
-} transmit_ntb_t;
+ TUD_EPBUF_TYPE_DEF(recv_ntb_t, ntb);
+ } recv[RECV_NTB_N];
+
+ struct {
+ TUD_EPBUF_TYPE_DEF(xmit_ntb_t, ntb);
+ } xmit[XMIT_NTB_N];
+
+ TUD_EPBUF_TYPE_DEF(ncm_notify_t, epnotif);
+} ncm_epbuf_t;
+
+static ncm_interface_t ncm_interface;
+CFG_TUD_MEM_SECTION static ncm_epbuf_t ncm_epbuf;
-struct ecm_notify_struct
-{
- tusb_control_request_t header;
- uint32_t downlink, uplink;
+/**
+ * This is the NTB parameter structure
+ *
+ * \attention
+ * We are lucky, that byte order is correct
+ */
+TU_ATTR_ALIGNED(4) static const ntb_parameters_t ntb_parameters = {
+ .wLength = sizeof(ntb_parameters_t),
+ .bmNtbFormatsSupported = 0x01,// 16-bit NTB supported
+ .dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
+ .wNdbInDivisor = 1,
+ .wNdbInPayloadRemainder = 0,
+ .wNdbInAlignment = TUD_NCM_ALIGNMENT,
+ .wReserved = 0,
+ .dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
+ .wNdbOutDivisor = 1,
+ .wNdbOutPayloadRemainder = 0,
+ .wNdbOutAlignment = TUD_NCM_ALIGNMENT,
+ .wNtbOutMaxDatagrams = CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB,
};
-typedef struct
-{
- uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
- uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
+// Some confusing remarks about wNtbOutMaxDatagrams...
+// ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
+// ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
+// ==6 -> SystemView runs fine, iperf also
+// >6 -> iperf starts to show errors
+// -> 6 seems to be the best value. Why? Don't know, perhaps only on my system?
+//
+// iperf: for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
+// sysview: SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
+//
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
+//-----------------------------------------------------------------------------
+//
+// everything about notifications
+//
- const ndp16_t *ndp;
- uint8_t num_datagrams, current_datagram_index;
+/**
+ * Transmit next notification to the host (if appropriate).
+ * Notifications are transferred to the host once during connection setup.
+ */
+static void notification_xmit(uint8_t rhport, bool force_next) {
+ TU_LOG_DRV("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
- enum {
- REPORT_SPEED,
- REPORT_CONNECTED,
- REPORT_DONE
- } report_state;
- bool report_pending;
+ if (!force_next && ncm_interface.notification_xmit_is_running) {
+ return;
+ }
+
+ if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
+ TU_LOG_DRV(" NOTIFICATION_SPEED\n");
+ ncm_notify_t notify_speed_change = {
+ .header = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
+ .wValue = 0,
+ .wIndex = ncm_interface.itf_num,
+ .wLength = 8
+ }
+ };
+ if (tud_speed_get() == TUSB_SPEED_HIGH) {
+ notify_speed_change.downlink = 480000000;
+ notify_speed_change.uplink = 480000000;
+ } else {
+ notify_speed_change.downlink = 12000000;
+ notify_speed_change.uplink = 12000000;
+ }
+
+ uint16_t notif_len = sizeof(notify_speed_change.header) + notify_speed_change.header.wLength;
+ ncm_epbuf.epnotif = notify_speed_change;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_epbuf.epnotif, notif_len);
+
+ ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
+ ncm_interface.notification_xmit_is_running = true;
+ } else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
+ TU_LOG_DRV(" NOTIFICATION_CONNECTED\n");
+ ncm_notify_t notify_connected = {
+ .header = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = CDC_NOTIF_NETWORK_CONNECTION,
+ .wValue = 1 /* Connected */,
+ .wIndex = ncm_interface.itf_num,
+ .wLength = 0,
+ },
+ };
- uint8_t current_ntb; // Index in transmit_ntb[] that is currently being filled with datagrams
- uint8_t datagram_count; // Number of datagrams in transmit_ntb[current_ntb]
- uint16_t next_datagram_offset; // Offset in transmit_ntb[current_ntb].data to place the next datagram
- uint16_t ntb_in_size; // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
- uint8_t max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
+ uint16_t notif_len = sizeof(notify_connected.header) + notify_connected.header.wLength;
+ ncm_epbuf.epnotif = notify_connected;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_epbuf.epnotif, notif_len);
- uint16_t nth_sequence; // Sequence number counter for transmitted NTBs
+ ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
+ ncm_interface.notification_xmit_is_running = true;
+ } else {
+ TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
+ }
+} // notification_xmit
- bool transferring;
+//-----------------------------------------------------------------------------
+//
+// everything about packet transmission (driver -> TinyUSB)
+//
-} ncm_interface_t;
+/**
+ * Put NTB into the transmitter free list.
+ */
+static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb) {
+ TU_LOG_DRV("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+ if (free_ntb == NULL) { // can happen due to ZLPs
+ return;
+ }
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static const ntb_parameters_t ntb_parameters = {
- .wLength = sizeof(ntb_parameters_t),
- .bmNtbFormatsSupported = 0x01,
- .dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
- .wNdbInDivisor = 4,
- .wNdbInPayloadRemainder = 0,
- .wNdbInAlignment = CFG_TUD_NCM_ALIGNMENT,
- .wReserved = 0,
- .dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
- .wNdbOutDivisor = 4,
- .wNdbOutPayloadRemainder = 0,
- .wNdbOutAlignment = CFG_TUD_NCM_ALIGNMENT,
- .wNtbOutMaxDatagrams = 0
-};
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] == NULL) {
+ ncm_interface.xmit_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) xmit_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // xmit_put_ntb_into_free_list
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static transmit_ntb_t transmit_ntb[2];
+/**
+ * Get an NTB from the free list
+ */
+static xmit_ntb_t *xmit_get_free_ntb(void) {
+ TU_LOG_DRV("xmit_get_free_ntb()\n");
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] != NULL) {
+ xmit_ntb_t *free = ncm_interface.xmit_free_ntb[i];
+ ncm_interface.xmit_free_ntb[i] = NULL;
+ return free;
+ }
+ }
+ return NULL;
+} // xmit_get_free_ntb
-static ncm_interface_t ncm_interface;
+/**
+ * Put a filled NTB into the ready list
+ */
+static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
+ TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
-/*
- * Set up the NTB state in ncm_interface to be ready to add datagrams.
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_ready_ntb[i] == NULL) {
+ ncm_interface.xmit_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // xmit_put_ntb_into_ready_list
+
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
*/
-static void ncm_prepare_for_tx(void) {
- ncm_interface.datagram_count = 0;
- // datagrams start after all the headers
- ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
- + ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
-}
+static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
+ xmit_ntb_t *r = NULL;
-/*
- * If not already transmitting, start sending the current NTB to the host and swap buffers
- * to start filling the other one with datagrams.
+ r = ncm_interface.xmit_ready_ntb[0];
+ memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
+ ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
+
+ TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // xmit_get_next_ready_ntb
+
+/**
+ * Transmit a ZLP if required
+ *
+ * \note
+ * Insertion of the ZLPs is a little bit different then described in the spec.
+ * But the below implementation actually works. Don't know if this is a spec
+ * or TinyUSB issue.
+ *
+ * \pre
+ * This must be called from netd_xfer_cb() so that ep_in is ready
+ */
+static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
+ TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
+
+ if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+ return false;
+ }
+
+ TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
+ TU_ASSERT(!usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
+
+ TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
+
+ // start transmission of the ZLP
+ usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
+
+ return true;
+} // xmit_insert_required_zlp
+
+/**
+ * Start transmission if it there is a waiting packet and if can be done from interface side.
*/
-static void ncm_start_tx(void) {
- if (ncm_interface.transferring) {
+static void xmit_start_if_possible(uint8_t rhport) {
+ TU_LOG_DRV("xmit_start_if_possible()\n");
+
+ if (ncm_interface.xmit_tinyusb_ntb != NULL) {
+ TU_LOG_DRV(" !xmit_start_if_possible 1\n");
return;
}
+ if (ncm_interface.itf_data_alt != 1) {
+ TU_LOG_DRV("(EE) !xmit_start_if_possible 2\n");
+ return;
+ }
+ if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
+ TU_LOG_DRV(" !xmit_start_if_possible 3\n");
+ return;
+ }
+
+ ncm_interface.xmit_tinyusb_ntb = xmit_get_next_ready_ntb();
+ if (ncm_interface.xmit_tinyusb_ntb == NULL) {
+ if (ncm_interface.xmit_glue_ntb == NULL || ncm_interface.xmit_glue_ntb_datagram_ndx == 0) {
+ // -> really nothing is waiting
+ return;
+ }
+ ncm_interface.xmit_tinyusb_ntb = ncm_interface.xmit_glue_ntb;
+ ncm_interface.xmit_glue_ntb = NULL;
+ }
+
+ #if CFG_TUD_NCM_LOG_LEVEL >= 3
+ {
+ uint16_t len = ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength;
+ TU_LOG_BUF(3, ncm_interface.xmit_tinyusb_ntb->data[i], len);
+ }
+ #endif
+
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx != 1) {
+ TU_LOG_DRV(">> %d %d\n", ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength, ncm_interface.xmit_glue_ntb_datagram_ndx);
+ }
+
+ // Kick off an endpoint transfer
+ usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
+} // xmit_start_if_possible
+
+/**
+ * check if a new datagram fits into the current NTB
+ */
+static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size) {
+ TU_LOG_DRV("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
+
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_IN_NTB_MAX_SIZE) {
+ return false;
+ }
+ return true;
+} // xmit_requested_datagram_fits_into_current_ntb
+
+/**
+ * Setup an NTB for the glue logic
+ */
+static bool xmit_setup_next_glue_ntb(void) {
+ TU_LOG_DRV("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
+
+ if (ncm_interface.xmit_glue_ntb != NULL) {
+ // put NTB into waiting list (the new datagram did not fit in)
+ xmit_put_ntb_into_ready_list(ncm_interface.xmit_glue_ntb);
+ }
+
+ ncm_interface.xmit_glue_ntb = xmit_get_free_ntb();// get next buffer (if any)
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ TU_LOG_DRV(" xmit_setup_next_glue_ntb - nothing free\n");// should happen rarely
+ return false;
+ }
+
+ ncm_interface.xmit_glue_ntb_datagram_ndx = 0;
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t ntb_length = ncm_interface.next_datagram_offset;
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
// Fill in NTB header
ntb->nth.dwSignature = NTH16_SIGNATURE;
- ntb->nth.wHeaderLength = sizeof(nth16_t);
- ntb->nth.wSequence = ncm_interface.nth_sequence++;
- ntb->nth.wBlockLength = ntb_length;
- ntb->nth.wNdpIndex = sizeof(nth16_t);
+ ntb->nth.wHeaderLength = sizeof(ntb->nth);
+ ntb->nth.wSequence = ncm_interface.xmit_sequence++;
+ ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+ ntb->nth.wNdpIndex = sizeof(ntb->nth);
// Fill in NDP16 header and terminator
ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
- ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
+ ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
ntb->ndp.wNextNdpIndex = 0;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
- // Kick off an endpoint transfer
- usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length);
- ncm_interface.transferring = true;
+ memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
+ return true;
+} // xmit_setup_next_glue_ntb
- // Swap to the other NTB and clear it out
- ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
- ncm_prepare_for_tx();
-}
+//-----------------------------------------------------------------------------
+//
+// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
+//
-static struct ecm_notify_struct ncm_notify_connected =
-{
- .header = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN
- },
- .bRequest = CDC_NOTIF_NETWORK_CONNECTION,
- .wValue = 1 /* Connected */,
- .wLength = 0,
- },
-};
-
-static struct ecm_notify_struct ncm_notify_speed_change =
-{
- .header = {
- .bmRequestType_bit = {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN
- },
- .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
- .wLength = 8,
- },
- .downlink = 10000000,
- .uplink = 10000000,
-};
+/**
+ * Return pointer to an available receive buffer or NULL.
+ * Returned buffer (if any) has the size \a CFG_TUD_NCM_OUT_NTB_MAX_SIZE.
+ */
+static recv_ntb_t *recv_get_free_ntb(void) {
+ TU_LOG_DRV("recv_get_free_ntb()\n");
-void tud_network_recv_renew(void)
-{
- if (!ncm_interface.num_datagrams)
- {
- usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
- return;
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] != NULL) {
+ recv_ntb_t *free = ncm_interface.recv_free_ntb[i];
+ ncm_interface.recv_free_ntb[i] = NULL;
+ return free;
+ }
}
+ return NULL;
+} // recv_get_free_ntb
- const ndp16_t *ndp = ncm_interface.ndp;
- const int i = ncm_interface.current_datagram_index;
- ncm_interface.current_datagram_index++;
- ncm_interface.num_datagrams--;
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
+static recv_ntb_t *recv_get_next_ready_ntb(void) {
+ recv_ntb_t *r = NULL;
- tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
-}
+ r = ncm_interface.recv_ready_ntb[0];
+ memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
+ ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
+ TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // recv_get_next_ready_ntb
-void netd_init(void)
-{
- tu_memclr(&ncm_interface, sizeof(ncm_interface));
- ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
- ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
- ncm_prepare_for_tx();
-}
+/**
+ * Put NTB into the receiver free list.
+ */
+static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) {
+ TU_LOG_DRV("recv_put_ntb_into_free_list(%p)\n", free_ntb);
-void netd_reset(uint8_t rhport)
-{
- (void) rhport;
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] == NULL) {
+ ncm_interface.recv_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) recv_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // recv_put_ntb_into_free_list
- netd_init();
-}
+/**
+ * \a ready_ntb holds a validated NTB,
+ * put this buffer into the waiting list.
+ */
+static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) {
+ TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
+
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_ready_ntb[i] == NULL) {
+ ncm_interface.recv_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // recv_put_ntb_into_ready_list
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
- // confirm interface hasn't already been allocated
- TU_ASSERT(0 == ncm_interface.ep_notif, 0);
+/**
+ * If possible, start a new reception TinyUSB -> driver.
+ */
+static void recv_try_to_start_new_reception(uint8_t rhport) {
+ TU_LOG_DRV("recv_try_to_start_new_reception(%d)\n", rhport);
- //------------- Management Interface -------------//
- ncm_interface.itf_num = itf_desc->bInterfaceNumber;
+ if (ncm_interface.itf_data_alt != 1) {
+ return;
+ }
+ if (ncm_interface.recv_tinyusb_ntb != NULL) {
+ return;
+ }
+ if (usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
+ return;
+ }
- uint16_t drv_len = sizeof(tusb_desc_interface_t);
- uint8_t const * p_desc = tu_desc_next( itf_desc );
+ ncm_interface.recv_tinyusb_ntb = recv_get_free_ntb();
+ if (ncm_interface.recv_tinyusb_ntb == NULL) {
+ return;
+ }
- // Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
- {
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ // initiate transfer
+ TU_LOG_DRV(" start reception\n");
+ bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ if (!r) {
+ recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
+ ncm_interface.recv_tinyusb_ntb = NULL;
}
+} // recv_try_to_start_new_reception
- // notification endpoint (if any)
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+/**
+ * Validate incoming datagram.
+ * \return true if valid
+ *
+ * \note
+ * \a ndp16->wNextNdpIndex != 0 is not supported
+ */
+static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) {
+ const nth16_t *nth16 = &(ntb->nth);
- ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ TU_LOG_DRV("recv_validate_datagram(%p, %d)\n", ntb, (int) len);
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
+ // check header
+ if (nth16->wHeaderLength != sizeof(nth16_t)) {
+ TU_LOG_DRV("(EE) ill nth16 length: %d\n", nth16->wHeaderLength);
+ return false;
+ }
+ if (nth16->dwSignature != NTH16_SIGNATURE) {
+ TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) nth16->dwSignature);
+ return false;
+ }
+ if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+ TU_LOG_DRV("(EE) ill min len: %lu\n", len);
+ return false;
+ }
+ if (nth16->wBlockLength > len) {
+ TU_LOG_DRV("(EE) ill block length: %d > %lu\n", nth16->wBlockLength, len);
+ return false;
+ }
+ if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ return false;
+ }
+ if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) {
+ TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len);
+ return false;
}
- //------------- Data Interface -------------//
- // - CDC-NCM data interface has 2 alternate settings
- // - 0 : zero endpoints for inactive (default)
- // - 1 : IN & OUT endpoints for transfer of NTBs
- TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+ // check (first) NDP(16)
+ const ndp16_t *ndp16 = (const ndp16_t *) (ntb->data + nth16->wNdpIndex);
- do
- {
- tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
- TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
+ if (ndp16->wLength < sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+ TU_LOG_DRV("(EE) ill ndp16 length: %d\n", ndp16->wLength);
+ return false;
+ }
+ if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
+ TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) ndp16->dwSignature);
+ return false;
+ }
+ if (ndp16->wNextNdpIndex != 0) {
+ TU_LOG_DRV("(EE) cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
+ return false;
+ }
- drv_len += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
- } while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
+ const ndp16_datagram_t *ndp16_datagram = (const ndp16_datagram_t *) (ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
+ int ndx = 0;
+ uint16_t max_ndx = (uint16_t) ((ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t));
- // Pair of endpoints
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+ if (max_ndx > 2) { // number of datagrams in NTB > 1
+ TU_LOG_DRV("<< %d (%d)\n", max_ndx - 1, ntb->nth.wBlockLength);
+ }
+ if (ndp16_datagram[max_ndx - 1].wDatagramIndex != 0 || ndp16_datagram[max_ndx - 1].wDatagramLength != 0) {
+ TU_LOG_DRV(" max_ndx != 0\n");
+ return false;
+ }
+ while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
+ TU_LOG_DRV(" << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
+ if (ndp16_datagram[ndx].wDatagramIndex > len) {
+ TU_LOG_DRV("(EE) ill start of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
+ return false;
+ }
+ if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
+ TU_LOG_DRV("(EE) ill end of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
+ return false;
+ }
+ ++ndx;
+ }
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
+ #if CFG_TUD_NCM_LOG_LEVEL >= 3
+ TU_LOG_BUF(3, ntb->data[i], len);
+ #endif
- drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ // -> ntb contains a valid packet structure
+ // ok... I did not check for garbage within the datagram indices...
+ return true;
+} // recv_validate_datagram
- return drv_len;
-}
+/**
+ * Transfer the next (pending) datagram to the glue logic and return receive buffer if empty.
+ */
+static void recv_transfer_datagram_to_glue_logic(void) {
+ TU_LOG_DRV("recv_transfer_datagram_to_glue_logic()\n");
-static void ncm_report(void)
-{
- uint8_t const rhport = 0;
- if (ncm_interface.report_state == REPORT_SPEED) {
- ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
- ncm_interface.report_state = REPORT_CONNECTED;
- ncm_interface.report_pending = true;
- } else if (ncm_interface.report_state == REPORT_CONNECTED) {
- ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
- usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
- ncm_interface.report_state = REPORT_DONE;
- ncm_interface.report_pending = true;
+ if (ncm_interface.recv_glue_ntb == NULL) {
+ ncm_interface.recv_glue_ntb = recv_get_next_ready_ntb();
+ TU_LOG_DRV(" new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb_datagram_ndx = 0;
}
-}
-TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
-{
- (void)state;
-}
+ if (ncm_interface.recv_glue_ntb != NULL) {
+ const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *) (ncm_interface.recv_glue_ntb->data + ncm_interface.recv_glue_ntb->nth.wNdpIndex + sizeof(ndp16_t));
-// Handle class control request
-// return false to stall control endpoint (e.g unsupported request)
-bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
- if ( stage != CONTROL_STAGE_SETUP ) return true;
+ if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex == 0) {
+ TU_LOG_DRV("(EE) SOMETHING WENT WRONG 1\n");
+ } else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
+ TU_LOG_DRV("(EE) SOMETHING WENT WRONG 2\n");
+ } else {
+ uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
+ uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
- switch ( request->bmRequestType_bit.type )
- {
- case TUSB_REQ_TYPE_STANDARD:
- switch ( request->bRequest )
- {
- case TUSB_REQ_GET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
+ TU_LOG_DRV(" recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
+ if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
+ // send datagram successfully to glue logic
+ TU_LOG_DRV(" OK\n");
+ datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
+ datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
- tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+ if (datagramIndex != 0 && datagramLength != 0) {
+ // -> next datagram
+ ++ncm_interface.recv_glue_ntb_datagram_ndx;
+ } else {
+ // end of datagrams reached
+ recv_put_ntb_into_free_list(ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb = NULL;
}
- break;
+ }
+ }
+ }
+} // recv_transfer_datagram_to_glue_logic
- case TUSB_REQ_SET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- uint8_t const req_alt = (uint8_t) request->wValue;
+//-----------------------------------------------------------------------------
+//
+// all the tud_network_*() stuff (glue logic -> driver)
+//
- // Only valid for Data Interface with Alternate is either 0 or 1
- TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
+/**
+ * Check if the glue logic is allowed to call tud_network_xmit().
+ * This function also fetches a next buffer if required, so that tud_network_xmit() is ready for copy
+ * and transmission operation.
+ */
+bool tud_network_can_xmit(uint16_t size) {
+ TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
- if (req_alt != ncm_interface.itf_data_alt) {
- ncm_interface.itf_data_alt = req_alt;
+ TU_ASSERT(size <= CFG_TUD_NCM_IN_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
- if (ncm_interface.itf_data_alt) {
- if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
- tud_network_recv_renew(); // prepare for incoming datagrams
- }
- if (!ncm_interface.report_pending) {
- ncm_report();
- }
- }
+ if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
+ // -> everything is fine
+ return true;
+ }
+ xmit_start_if_possible(ncm_interface.rhport);
+ TU_LOG_DRV("(II) tud_network_can_xmit: request blocked\n");// could happen if all xmit buffers are full (but should happen rarely)
+ return false;
+} // tud_network_can_xmit
- tud_network_link_state_cb(ncm_interface.itf_data_alt);
- }
+/**
+ * Put a datagram into a waiting NTB.
+ * If currently no transmission is started, then initiate transmission.
+ */
+void tud_network_xmit(void *ref, uint16_t arg) {
+ TU_LOG_DRV("tud_network_xmit(%p, %d)\n", ref, arg);
- tud_control_status(rhport, request);
- }
- break;
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ TU_LOG_DRV("(EE) tud_network_xmit: no buffer\n");// must not happen (really)
+ return;
+ }
- // unsupported request
- default: return false;
- }
- break;
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
- case TUSB_REQ_TYPE_CLASS:
- TU_VERIFY (ncm_interface.itf_num == request->wIndex);
+ // copy new datagram to the end of the current NTB
+ uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
- if (NCM_GET_NTB_PARAMETERS == request->bRequest)
- {
- tud_control_xfer(rhport, request, (void*)&ntb_parameters, sizeof(ntb_parameters));
- }
+ // correct NTB internals
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramIndex = ntb->nth.wBlockLength;
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramLength = size;
+ ncm_interface.xmit_glue_ntb_datagram_ndx += 1;
- break;
+ ntb->nth.wBlockLength += (uint16_t) (size + XMIT_ALIGN_OFFSET(size));
- // unsupported request
- default: return false;
+ if (ntb->nth.wBlockLength > CFG_TUD_NCM_IN_NTB_MAX_SIZE) {
+ TU_LOG_DRV("(EE) tud_network_xmit: buffer overflow\n"); // must not happen (really)
+ return;
}
- return true;
-}
+ xmit_start_if_possible(ncm_interface.rhport);
+} // tud_network_xmit
+
+/**
+ * Keep the receive logic busy and transfer pending packets to the glue logic.
+ * Avoid recursive calls due to wrong expectations of the net glue logic,
+ * see https://github.com/hathach/tinyusb/issues/2711
+ */
+void tud_network_recv_renew(void) {
+ TU_LOG_DRV("tud_network_recv_renew()\n");
-static void handle_incoming_datagram(uint32_t len)
-{
- uint32_t size = len;
+ ncm_interface.tud_network_recv_renew_process_again = true;
- if (len == 0) {
+ if (ncm_interface.tud_network_recv_renew_active) {
+ TU_LOG_DRV("Re-entrant into tud_network_recv_renew, will process later\n");
return;
}
- TU_ASSERT(size >= sizeof(nth16_t), );
+ while (ncm_interface.tud_network_recv_renew_process_again) {
+ ncm_interface.tud_network_recv_renew_process_again = false;
- const nth16_t *hdr = (const nth16_t *)receive_ntb;
- TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
- TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
+ // If the current function is called within recv_transfer_datagram_to_glue_logic,
+ // tud_network_recv_renew_process_again will become true, and the loop will run again
+ // Otherwise the loop will not run again
+ ncm_interface.tud_network_recv_renew_active = true;
+ recv_transfer_datagram_to_glue_logic();
+ ncm_interface.tud_network_recv_renew_active = false;
+ }
+ recv_try_to_start_new_reception(ncm_interface.rhport);
+} // tud_network_recv_renew
- const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
- TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
- TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
+/**
+ * Same as tud_network_recv_renew() but knows \a rhport
+ */
+static void tud_network_recv_renew_r(uint8_t rhport) {
+ TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
- int num_datagrams = (ndp->wLength - 12) / 4;
- ncm_interface.current_datagram_index = 0;
- ncm_interface.num_datagrams = 0;
- ncm_interface.ndp = ndp;
- for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++)
- {
- ncm_interface.num_datagrams++;
- }
-
+ ncm_interface.rhport = rhport;
tud_network_recv_renew();
+} // tud_network_recv_renew
+
+//-----------------------------------------------------------------------------
+//
+// all the netd_*() stuff (interface TinyUSB -> driver)
+//
+/**
+ * Initialize the driver data structures.
+ * Might be called several times.
+ */
+void netd_init(void) {
+ TU_LOG_DRV("netd_init()\n");
+
+ memset(&ncm_interface, 0, sizeof(ncm_interface));
+
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ ncm_interface.xmit_free_ntb[i] = &ncm_epbuf.xmit[i].ntb;
+ }
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
+ }
+} // netd_init
+
+/**
+ * Deinit driver
+ */
+bool netd_deinit(void) {
+ return true;
}
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+/**
+ * Resets the port.
+ * In this driver this is the same as netd_init()
+ */
+void netd_reset(uint8_t rhport) {
(void) rhport;
- (void) result;
- /* new datagram receive_ntb */
- if (ep_addr == ncm_interface.ep_out )
- {
- handle_incoming_datagram(xferred_bytes);
- }
+ netd_init();
+} // netd_reset
- /* data transmission finished */
- if (ep_addr == ncm_interface.ep_in )
- {
- if (ncm_interface.transferring) {
- ncm_interface.transferring = false;
- }
+/**
+ * Open the USB interface.
+ * - parse the USB descriptor \a TUD_CDC_NCM_DESCRIPTOR for itfnum and endpoints
+ * - a specific order of elements in the descriptor is tested.
+ *
+ * \note
+ * Actually all of the information could be read directly from \a itf_desc, because the
+ * structure and the values are well known. But we do it this way.
+ *
+ * \post
+ * - \a itf_num set
+ * - \a ep_notif, \a ep_in and \a ep_out are set
+ * - USB interface is open
+ */
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ TU_ASSERT(ncm_interface.ep_notif == 0, 0);// assure that the interface is only opened once
- // If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
- if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) {
- ncm_start_tx();
- }
- }
+ ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
- if (ep_addr == ncm_interface.ep_notif )
- {
- ncm_interface.report_pending = false;
- ncm_report();
+ // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const *p_desc = tu_desc_next(itf_desc);
+ while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- return true;
-}
+ // get notification endpoint
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+ TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
+ ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
-// poll network driver for its ability to accept another packet to transmit
-bool tud_network_can_xmit(uint16_t size)
-{
- TU_VERIFY(ncm_interface.itf_data_alt == 1);
+ // skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
+ while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
+ tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+ TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
- if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
- TU_LOG2("NTB full [by count]\r\n");
- return false;
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
- if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
- TU_LOG2("ntb full [by size]\r\n");
- return false;
+ // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+ TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
+ drv_len += 2 * sizeof(tusb_desc_endpoint_t);
+
+ return drv_len;
+} // netd_open
+
+/**
+ * Handle TinyUSB requests to process transfer events.
+ */
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ (void) result;
+
+ if (ep_addr == ncm_interface.ep_out) {
+ // new NTB received
+ // - make the NTB valid
+ // - if ready transfer datagrams to the glue logic for further processing
+ // - if there is a free receive buffer, initiate reception
+ if (!recv_validate_datagram(ncm_interface.recv_tinyusb_ntb, xferred_bytes)) {
+ // verification failed: ignore NTB and return it to free
+ TU_LOG_DRV("Invalid datatagram. Ignoring NTB\n");
+ recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
+ } else {
+ // packet ok -> put it into ready list
+ recv_put_ntb_into_ready_list(ncm_interface.recv_tinyusb_ntb);
+ }
+ ncm_interface.recv_tinyusb_ntb = NULL;
+ tud_network_recv_renew_r(rhport);
+ } else if (ep_addr == ncm_interface.ep_in) {
+ // transmission of an NTB finished
+ // - free the transmitted NTB buffer
+ // - insert ZLPs when necessary
+ // - if there is another transmit NTB waiting, try to start transmission
+ xmit_put_ntb_into_free_list(ncm_interface.xmit_tinyusb_ntb);
+ ncm_interface.xmit_tinyusb_ntb = NULL;
+ if (!xmit_insert_required_zlp(rhport, xferred_bytes)) {
+ xmit_start_if_possible(rhport);
+ }
+ } else if (ep_addr == ncm_interface.ep_notif) {
+ // next transfer on notification channel
+ notification_xmit(rhport, true);
}
return true;
-}
+} // netd_xfer_cb
+
+/**
+ * Respond to TinyUSB control requests.
+ * At startup transmission of notification packets are done here.
+ */
+bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ switch (request->bmRequestType_bit.type) {
+ case TUSB_REQ_TYPE_STANDARD:
-void tud_network_xmit(void *ref, uint16_t arg)
-{
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
+ switch (request->bRequest) {
+ case TUSB_REQ_GET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
- uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
+ tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+ } break;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
+ case TUSB_REQ_SET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
- ncm_interface.datagram_count++;
- next_datagram_offset += size;
+ ncm_interface.itf_data_alt = (uint8_t) request->wValue;
- // round up so the next datagram is aligned correctly
- next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
- next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
+ if (ncm_interface.itf_data_alt == 1) {
+ tud_network_recv_renew_r(rhport);
+ notification_xmit(rhport, false);
+ }
+ tud_control_status(rhport, request);
+ } break;
- ncm_interface.next_datagram_offset = next_datagram_offset;
+ // unsupported request
+ default:
+ return false;
+ }
+ break;
- ncm_start_tx();
-}
+ case TUSB_REQ_TYPE_CLASS:
+ TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
+ switch (request->bRequest) {
+ case NCM_GET_NTB_PARAMETERS: {
+ // transfer NTB parameters to host.
+ tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
+ } break;
-#endif
+ // unsupported request
+ default:
+ return false;
+ }
+ break;
+ // unsupported request
+ default:
+ return false;
+ }
+
+ return true;
+} // netd_control_xfer_cb
+
+#endif // ( CFG_TUD_ENABLED && CFG_TUD_NCM )
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index 6e294465b..4c9a92f2d 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Peter Lawrence
@@ -28,14 +28,13 @@
#ifndef _TUSB_NET_DEVICE_H_
#define _TUSB_NET_DEVICE_H_
+#include <stdint.h>
#include "class/cdc/cdc.h"
#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
#endif
-#include "ncm.h"
-
/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
@@ -44,21 +43,13 @@
#define CFG_TUD_NET_MTU 1514
#endif
-#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
-#endif
-
-#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
-#endif
-#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
-#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
-#endif
+// Table 4.3 Data Class Interface Protocol Codes
+typedef enum
+{
+ NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
+} ncm_data_interface_protocol_code_t;
-#ifndef CFG_TUD_NCM_ALIGNMENT
-#define CFG_TUD_NCM_ALIGNMENT 4
-#endif
#ifdef __cplusplus
extern "C" {
@@ -94,17 +85,13 @@ void tud_network_init_cb(void);
// client must provide this: 48-bit MAC address
// TODO removed later since it is not part of tinyusb stack
-extern const uint8_t tud_network_mac_address[6];
-
-//------------- NCM -------------//
-
-// callback to client providing optional indication of internal state of network driver
-void tud_network_link_state_cb(bool state);
+extern uint8_t tud_network_mac_address[6];
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void netd_init (void);
+bool netd_deinit (void);
void netd_reset (uint8_t rhport);
uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index e7016ae24..327de087c 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -158,7 +158,7 @@ enum {
USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u,
USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u,
};
-// bult-in halt errors
+// built-in halt errors
enum {
USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a
/// Bulk-IN transfer is in progress
@@ -184,6 +184,23 @@ typedef enum {
} usmtmc_request_type_enum;
typedef enum {
+ // The last and first valid bNotify1 for use by the USBTMC class specification.
+ USBTMC_bNOTIFY1_USBTMC_FIRST = 0x00,
+ USBTMC_bNOTIFY1_USBTMC_LAST = 0x3F,
+
+ // The last and first valid bNotify1 for use by vendors.
+ USBTMC_bNOTIFY1_VENDOR_SPECIFIC_FIRST = 0x40,
+ USBTMC_bNOTIFY1_VENDOR_SPECIFIC_LAST = 0x7F,
+
+ // The last and first valid bNotify1 for use by USBTMC subclass specifications.
+ USBTMC_bNOTIFY1_SUBCLASS_FIRST = 0x80,
+ USBTMC_bNOTIFY1_SUBCLASS_LAST = 0xFF,
+
+ // From the USB488 Subclass Specification, Section 3.4.
+ USB488_bNOTIFY1_SRQ = 0x81,
+} usbtmc_int_in_payload_format;
+
+typedef enum {
USBTMC_STATUS_SUCCESS = 0x01,
USBTMC_STATUS_PENDING = 0x02,
USBTMC_STATUS_FAILED = 0x80,
@@ -262,14 +279,14 @@ typedef struct TU_ATTR_PACKED
struct TU_ATTR_PACKED
{
- unsigned int listenOnly :1;
- unsigned int talkOnly :1;
- unsigned int supportsIndicatorPulse :1;
+ uint8_t listenOnly :1;
+ uint8_t talkOnly :1;
+ uint8_t supportsIndicatorPulse :1;
} bmIntfcCapabilities;
struct TU_ATTR_PACKED
{
- unsigned int canEndBulkInOnTermChar :1;
+ uint8_t canEndBulkInOnTermChar :1;
} bmDevCapabilities;
uint8_t _reserved2[6];
@@ -277,17 +294,17 @@ typedef struct TU_ATTR_PACKED
struct TU_ATTR_PACKED
{
- unsigned int is488_2 :1;
- unsigned int supportsREN_GTL_LLO :1;
- unsigned int supportsTrigger :1;
+ uint8_t supportsTrigger :1;
+ uint8_t supportsREN_GTL_LLO :1;
+ uint8_t is488_2 :1;
} bmIntfcCapabilities488;
struct TU_ATTR_PACKED
{
- unsigned int SCPI :1;
- unsigned int SR1 :1;
- unsigned int RL1 :1;
- unsigned int DT1 :1;
+ uint8_t DT1 :1;
+ uint8_t RL1 :1;
+ uint8_t SR1 :1;
+ uint8_t SCPI :1;
} bmDevCapabilities488;
uint8_t _reserved3[8];
} usbtmc_response_capabilities_488_t;
@@ -305,6 +322,14 @@ TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length")
typedef struct TU_ATTR_PACKED
{
+ uint8_t bNotify1; // Must be USB488_bNOTIFY1_SRQ
+ uint8_t StatusByte;
+} usbtmc_srq_interrupt_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_srq_interrupt_488_t) == 2u, "struct wrong length");
+
+typedef struct TU_ATTR_PACKED
+{
struct TU_ATTR_PACKED
{
unsigned int bTag : 7;
@@ -316,4 +341,3 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_interrupt_488_t) == 2u, "struct wrong length");
#endif
-
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index af4a92732..abde9679f 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -78,14 +78,19 @@
#ifdef xDEBUG
#include "uart_util.h"
-static char logMsg[150];
+tu_static char logMsg[150];
#endif
// Buffer size must be an exact multiple of the max packet size for both
// bulk (up to 64 bytes for FS, 512 bytes for HS). In addation, this driver
-// imposes a minimum buffer size of 32 bytes.
+// imposes a minimum buffer size of 32 bytes.
#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
+#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
+#define CFG_TUD_USBTMC_INT_EP_SIZE 2
+#endif
+
/*
* The state machine does not allow simultaneous reading and writing. This is
* consistent with USBTMC.
@@ -124,14 +129,8 @@ typedef struct
uint8_t ep_bulk_in;
uint8_t ep_bulk_out;
uint8_t ep_int_in;
- // IN buffer is only used for first packet, not the remainder
- // in order to deal with prepending header
- CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
uint32_t ep_bulk_in_wMaxPacketSize;
- // OUT buffer receives one packet at a time
- CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
uint32_t ep_bulk_out_wMaxPacketSize;
-
uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
@@ -143,28 +142,45 @@ typedef struct
usbtmc_capabilities_specific_t const * capabilities;
} usbtmc_interface_state_t;
-CFG_TUSB_MEM_SECTION static usbtmc_interface_state_t usbtmc_state =
-{
- .itf_id = 0xFF,
+typedef struct {
+ // IN buffer is only used for first packet, not the remainder in order to deal with prepending header
+ TUD_EPBUF_DEF(epin, USBTMCD_BUFFER_SIZE);
+
+ // OUT buffer receives one packet at a time
+ TUD_EPBUF_DEF(epout, USBTMCD_BUFFER_SIZE);
+
+ // Buffer int msg
+ TUD_EPBUF_DEF(epnotif, CFG_TUD_USBTMC_INT_EP_SIZE);
+} usbtmc_epbuf_t;
+
+static usbtmc_interface_state_t usbtmc_state = {
+ .itf_id = 0xFF,
};
+CFG_TUD_MEM_SECTION static usbtmc_epbuf_t usbtmc_epbuf;
+
// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers
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);
-static uint8_t termChar;
-static uint8_t termCharRequested = false;
+#ifndef NDEBUG
+tu_static uint8_t termChar;
+#endif
-osal_mutex_def_t usbtmcLockBuffer;
-static osal_mutex_t usbtmcLock;
+tu_static uint8_t termCharRequested = false;
+
+#if OSAL_MUTEX_REQUIRED
+static OSAL_MUTEX_DEF(usbtmcLockBuffer);
+#endif
+osal_mutex_t usbtmcLock;
// Our own private lock, mostly for the state variable.
-#define criticalEnter() do {osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
-#define criticalLeave() do {osal_mutex_unlock(usbtmcLock); } while (0)
+#define criticalEnter() do { (void) osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
+#define criticalLeave() do { (void) osal_mutex_unlock(usbtmcLock); } while (0)
-bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
+static bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
{
bool ret = true;
criticalEnter();
@@ -193,7 +209,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
bool endOfMessage,
bool usingTermChar)
{
- const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
+ const unsigned int txBufLen = USBTMCD_BUFFER_SIZE;
#ifndef NDEBUG
TU_ASSERT(len > 0u);
@@ -208,7 +224,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
#endif
TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
- usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_state.ep_bulk_in_buf;
+ usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_epbuf.epin;
tu_varclr(hdr);
hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
hdr->header.bTag = usbtmc_state.lastBulkInTag;
@@ -223,7 +239,7 @@ bool tud_usbtmc_transmit_dev_msg_data(
len : (txBufLen - headerLen);
const size_t packetLen = headerLen + dataLen;
- memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
+ memcpy((uint8_t*)(usbtmc_epbuf.epin) + headerLen, data, dataLen);
usbtmc_state.transfer_size_remaining = len - dataLen;
usbtmc_state.transfer_size_sent = dataLen;
usbtmc_state.devInBuffer = (uint8_t const*) data + (dataLen);
@@ -231,7 +247,20 @@ bool tud_usbtmc_transmit_dev_msg_data(
bool stateChanged =
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
TU_VERIFY(stateChanged);
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t)packetLen));
+ return true;
+}
+
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
+{
+#ifndef NDEBUG
+ TU_ASSERT(len > 0);
+ TU_ASSERT(usbtmc_state.ep_int_in != 0);
+#endif
+ TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
+
+ TU_VERIFY(tu_memcpy_s(usbtmc_epbuf.epnotif, CFG_TUD_USBTMC_INT_EP_SIZE, data, len) == 0);
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_epbuf.epnotif, (uint16_t)len));
return true;
}
@@ -255,6 +284,13 @@ void usbtmcd_init_cb(void)
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
}
+bool usbtmcd_deinit(void) {
+ #if OSAL_MUTEX_REQUIRED
+ osal_mutex_delete(usbtmcLock);
+ #endif
+ return true;
+}
+
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void)rhport;
@@ -348,7 +384,7 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
// processing a command (such as a clear). Returns true if it was
// in the NAK state and successfully transitioned to the ACK wait
// state.
-bool tud_usbtmc_start_bus_read()
+bool tud_usbtmc_start_bus_read(void)
{
usbtmcd_state_enum oldState = usbtmc_state.state;
switch(oldState)
@@ -362,9 +398,9 @@ bool tud_usbtmc_start_bus_read()
case STATE_RCV:
break;
default:
- TU_VERIFY(false);
+ return false;
}
- TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, 64));
+ TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_epbuf.epout, (uint16_t)usbtmc_state.ep_bulk_out_wMaxPacketSize));
return true;
}
@@ -440,7 +476,10 @@ static bool handle_devMsgIn(void *data, size_t len)
usbtmc_state.transfer_size_sent = 0u;
termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
+
+#ifndef NDEBUG
termChar = msg->TermChar;
+#endif
if(termCharRequested)
TU_VERIFY(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
@@ -464,53 +503,53 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
switch(usbtmc_state.state)
{
case STATE_IDLE:
- TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
- msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf);
- uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
- TU_VERIFY(msg->header.bTag == invInvTag);
- TU_VERIFY(msg->header.bTag != 0x00);
+ {
+ TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
+ msg = (usbtmc_msg_generic_t*)(usbtmc_epbuf.epout);
+ uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
+ TU_VERIFY(msg->header.bTag == invInvTag);
+ TU_VERIFY(msg->header.bTag != 0x00);
- switch(msg->header.MsgID) {
- case USBTMC_MSGID_DEV_DEP_MSG_OUT:
- if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
- {
- usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
- TU_VERIFY(false);
- }
- break;
+ switch(msg->header.MsgID) {
+ case USBTMC_MSGID_DEV_DEP_MSG_OUT:
+ if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
+ {
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ return false;
+ }
+ break;
- case USBTMC_MSGID_DEV_DEP_MSG_IN:
- TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
- break;
+ case USBTMC_MSGID_DEV_DEP_MSG_IN:
+ TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
+ break;
#if (CFG_TUD_USBTMC_ENABLE_488)
- case USBTMC_MSGID_USB488_TRIGGER:
- // Spec says we halt the EP if we didn't declare we support it.
- TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
- TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
+ case USBTMC_MSGID_USB488_TRIGGER:
+ // Spec says we halt the EP if we didn't declare we support it.
+ TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
+ TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
- break;
+ break;
#endif
- case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
- case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
- default:
- usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
- TU_VERIFY(false);
- return false;
+ case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
+ case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
+ default:
+ usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
+ return false;
+ }
+ return true;
}
- return true;
-
case STATE_RCV:
- if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
+ if(!handle_devMsgOut(rhport, usbtmc_epbuf.epout, xferred_bytes, xferred_bytes))
{
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
- TU_VERIFY(false);
+ return false;
}
return true;
case STATE_ABORTING_BULK_OUT:
- TU_VERIFY(false);
- return false; // Should be stalled by now, shouldn't have received a packet.
+ // Should be stalled by now, shouldn't have received a packet.
+ return false;
case STATE_TX_REQUESTED:
case STATE_TX_INITIATED:
@@ -518,7 +557,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_ABORTING_BULK_IN_SHORTED:
case STATE_ABORTING_BULK_IN_ABORTED:
default:
- TU_VERIFY(false);
+ return false;
}
}
else if(ep_addr == usbtmc_state.ep_bulk_in)
@@ -530,23 +569,23 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
break;
case STATE_TX_INITIATED:
- if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
+ if(usbtmc_state.transfer_size_remaining >= USBTMCD_BUFFER_SIZE)
{
- // FIXME! This removes const below!
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
- (void*)(uintptr_t) usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf)));
- usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
- usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
- usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
+ // Copy buffer to ensure alignment correctness
+ memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, USBTMCD_BUFFER_SIZE);
+ TU_VERIFY(usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, USBTMCD_BUFFER_SIZE));
+ usbtmc_state.devInBuffer += USBTMCD_BUFFER_SIZE;
+ usbtmc_state.transfer_size_remaining -= USBTMCD_BUFFER_SIZE;
+ usbtmc_state.transfer_size_sent += USBTMCD_BUFFER_SIZE;
}
else // last packet
{
size_t packetLen = usbtmc_state.transfer_size_remaining;
- memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
+ memcpy(usbtmc_epbuf.epin, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.transfer_size_remaining);
usbtmc_state.transfer_size_remaining = 0;
usbtmc_state.devInBuffer = NULL;
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen) );
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin, (uint16_t)packetLen) );
if(((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0 ))
{
usbtmc_state.state = STATE_TX_SHORTED;
@@ -556,7 +595,7 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
case STATE_ABORTING_BULK_IN:
// need to send short packet (ZLP?)
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
return true;
@@ -567,11 +606,12 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
default:
TU_ASSERT(false);
- return false;
}
}
else if (ep_addr == usbtmc_state.ep_int_in) {
- // Good?
+ if (tud_usbtmc_notification_complete_cb) {
+ TU_VERIFY(tud_usbtmc_notification_complete_cb());
+ }
return true;
}
return false;
@@ -598,8 +638,8 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
uint32_t ep_addr = (request->wIndex);
// At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT,
- // the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if
- // the EP is not halted. The only USBD API interface to do this is to stall and then unstall the EP.
+ // the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if
+ // the EP is not halted. The only USBD API interface to do this is to stall and then un-stall the EP.
if(ep_addr == usbtmc_state.ep_bulk_out)
{
criticalEnter();
@@ -707,7 +747,7 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
if(usbtmc_state.transfer_size_sent == 0)
{
// Send short packet, nothing is in the buffer yet
- TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
+ TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_epbuf.epin,(uint16_t)0u));
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
}
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
@@ -871,16 +911,13 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
case USB488_bREQUEST_LOCAL_LOCKOUT:
{
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
- TU_VERIFY(false);
return false;
}
#endif
default:
- TU_VERIFY(false);
return false;
}
- TU_VERIFY(false);
}
#endif /* CFG_TUD_TSMC */
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 144b3315d..b85ef12b5 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -36,7 +36,7 @@
#endif
/***********************************************
- * Functions to be implemeted by the class implementation
+ * Functions to be implemented by the class implementation
*/
// In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after:
@@ -73,6 +73,10 @@ bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
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);
+
// 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);
@@ -82,31 +86,33 @@ 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();
#endif
-/*******************************************
- * Called from app
- *
- * We keep a reference to the buffer, so it MUST not change until the app is
- * notified that the transfer is complete.
- ******************************************/
-
+// Called from app
+//
+// We keep a reference to the buffer, so it MUST not change until the app is
+// notified that the transfer is complete.
bool tud_usbtmc_transmit_dev_msg_data(
const void * data, size_t len,
bool endOfMessage, bool usingTermChar);
+// Buffers a notification to be sent to the host. The data starts
+// with the bNotify1 field, see the USBTMC Specification, Table 13.
+//
+// If the previous notification data has not yet been sent, this
+// returns false.
+//
+// Requires an interrupt endpoint in the interface.
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len);
+
bool tud_usbtmc_start_bus_read(void);
/* "callbacks" from USB device core */
+void usbtmcd_init_cb(void);
+bool usbtmcd_deinit(void);
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
void usbtmcd_reset_cb(uint8_t rhport);
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
-void usbtmcd_init_cb(void);
-
-/************************************************************
- * USBTMC Descriptor Templates
- *************************************************************/
-
#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 3b81a108f..6f9b4853f 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -36,172 +36,173 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
/*------------- From this point, data is not cleared by bus reset -------------*/
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
+ struct {
+ tu_edpt_stream_t stream;
+ #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
+ uint8_t ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
+ #endif
+ } tx;
- uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
+ struct {
+ tu_edpt_stream_t stream;
+ #if CFG_TUD_VENDOR_RX_BUFSIZE > 0
+ uint8_t ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
+ #endif
+ } rx;
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
-#endif
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
} vendord_interface_t;
-CFG_TUSB_MEM_SECTION static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
+#define ITF_MEM_RESET_SIZE (offsetof(vendord_interface_t, itf_num) + sizeof(((vendord_interface_t *)0)->itf_num))
-#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, rx_ff)
+static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
+typedef struct {
+ TUD_EPBUF_DEF(epout, CFG_TUD_VENDOR_EPSIZE);
+ TUD_EPBUF_DEF(epin, CFG_TUD_VENDOR_EPSIZE);
+} vendord_epbuf_t;
-bool tud_vendor_n_mounted (uint8_t itf)
-{
- return _vendord_itf[itf].ep_in && _vendord_itf[itf].ep_out;
-}
+CFG_TUD_MEM_SECTION static vendord_epbuf_t _vendord_epbuf[CFG_TUD_VENDOR];
-uint32_t tud_vendor_n_available (uint8_t itf)
-{
- return tu_fifo_count(&_vendord_itf[itf].rx_ff);
-}
+//--------------------------------------------------------------------
+// Application API
+//--------------------------------------------------------------------
-bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8)
-{
- return tu_fifo_peek(&_vendord_itf[itf].rx_ff, u8);
+bool tud_vendor_n_mounted(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR);
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
+ return p_itf->rx.stream.ep_addr || p_itf->tx.stream.ep_addr;
}
//--------------------------------------------------------------------+
// Read API
//--------------------------------------------------------------------+
-static void _prep_out_transaction (vendord_interface_t* p_itf)
-{
- uint8_t const rhport = 0;
+uint32_t tud_vendor_n_available(uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
- // skip if previous transfer not complete
- if ( usbd_edpt_busy(rhport, p_itf->ep_out) ) return;
+ return tu_edpt_stream_read_available(&p_itf->rx.stream);
+}
- // Prepare for incoming data but only allow what we can store in the ring buffer.
- uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff);
- if ( max_read >= CFG_TUD_VENDOR_EPSIZE )
- {
- usbd_edpt_xfer(rhport, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
- }
+bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR);
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
+
+ return tu_edpt_stream_peek(&p_itf->rx.stream, u8);
}
-uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
-{
+uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, (uint16_t) bufsize);
- _prep_out_transaction(p_itf);
- return num_read;
+ const uint8_t rhport = 0;
+
+ return tu_edpt_stream_read(rhport, &p_itf->rx.stream, buffer, bufsize);
}
-void tud_vendor_n_read_flush (uint8_t itf)
-{
+void tud_vendor_n_read_flush (uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, );
vendord_interface_t* p_itf = &_vendord_itf[itf];
- tu_fifo_clear(&p_itf->rx_ff);
- _prep_out_transaction(p_itf);
+ const uint8_t rhport = 0;
+
+ tu_edpt_stream_clear(&p_itf->rx.stream);
+ tu_edpt_stream_read_xfer(rhport, &p_itf->rx.stream);
}
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
-static uint16_t maybe_transmit(vendord_interface_t* p_itf)
-{
- uint8_t const rhport = 0;
-
- // skip if previous transfer not complete
- TU_VERIFY( !usbd_edpt_busy(rhport, p_itf->ep_in) );
+uint32_t tud_vendor_n_write (uint8_t itf, const void* buffer, uint32_t bufsize) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
+ vendord_interface_t* p_itf = &_vendord_itf[itf];
+ const uint8_t rhport = 0;
- uint16_t count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, CFG_TUD_VENDOR_EPSIZE);
- if (count > 0)
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_itf->ep_in, p_itf->epin_buf, count) );
- }
- return count;
+ return tu_edpt_stream_write(rhport, &p_itf->tx.stream, buffer, (uint16_t) bufsize);
}
-uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
-{
+uint32_t tud_vendor_n_write_flush (uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, (uint16_t) bufsize);
- if (tu_fifo_count(&p_itf->tx_ff) >= CFG_TUD_VENDOR_EPSIZE) {
- maybe_transmit(p_itf);
- }
- return ret;
+ const uint8_t rhport = 0;
+
+ return tu_edpt_stream_write_xfer(rhport, &p_itf->tx.stream);
}
-uint32_t tud_vendor_n_flush (uint8_t itf)
-{
+uint32_t tud_vendor_n_write_available (uint8_t itf) {
+ TU_VERIFY(itf < CFG_TUD_VENDOR, 0);
vendord_interface_t* p_itf = &_vendord_itf[itf];
- uint32_t ret = maybe_transmit(p_itf);
-
- return ret;
-}
+ const uint8_t rhport = 0;
-uint32_t tud_vendor_n_write_available (uint8_t itf)
-{
- return tu_fifo_remaining(&_vendord_itf[itf].tx_ff);
+ return tu_edpt_stream_write_available(rhport, &p_itf->tx.stream);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void vendord_init(void)
-{
+void vendord_init(void) {
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
- {
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
vendord_interface_t* p_itf = &_vendord_itf[i];
+ vendord_epbuf_t* p_epbuf = &_vendord_epbuf[i];
- // config fifo
- tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
- tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
+ uint8_t* rx_ff_buf =
+ #if CFG_TUD_VENDOR_RX_BUFSIZE > 0
+ p_itf->rx.ff_buf;
+ #else
+ NULL;
+ #endif
-#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
- tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
-#endif
+ tu_edpt_stream_init(&p_itf->rx.stream, false, false, false,
+ rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE,
+ p_epbuf->epout, CFG_TUD_VENDOR_EPSIZE);
+
+ uint8_t* tx_ff_buf =
+ #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
+ p_itf->tx.ff_buf;
+ #else
+ NULL;
+ #endif
+
+ tu_edpt_stream_init(&p_itf->tx.stream, false, true, false,
+ tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE,
+ p_epbuf->epin, CFG_TUD_VENDOR_EPSIZE);
+ }
+}
+
+bool vendord_deinit(void) {
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
+ vendord_interface_t* p_itf = &_vendord_itf[i];
+ tu_edpt_stream_deinit(&p_itf->rx.stream);
+ tu_edpt_stream_deinit(&p_itf->tx.stream);
}
+ return true;
}
-void vendord_reset(uint8_t rhport)
-{
+void vendord_reset(uint8_t rhport) {
(void) rhport;
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
- {
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
vendord_interface_t* p_itf = &_vendord_itf[i];
-
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&p_itf->rx_ff);
- tu_fifo_clear(&p_itf->tx_ff);
+ tu_edpt_stream_clear(&p_itf->rx.stream);
+ tu_edpt_stream_clear(&p_itf->tx.stream);
+ tu_edpt_stream_close(&p_itf->rx.stream);
+ tu_edpt_stream_close(&p_itf->tx.stream);
}
}
-uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
-{
+uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
-
- uint8_t const * p_desc = tu_desc_next(desc_itf);
- uint8_t const * desc_end = p_desc + max_len;
+ const uint8_t* p_desc = tu_desc_next(desc_itf);
+ const uint8_t* desc_end = p_desc + max_len;
// Find available interface
vendord_interface_t* p_vendor = NULL;
- for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
- {
- if ( _vendord_itf[i].ep_in == 0 && _vendord_itf[i].ep_out == 0 )
- {
+ for(uint8_t i=0; i<CFG_TUD_VENDOR; i++) {
+ if (!tud_vendor_n_mounted(i)) {
p_vendor = &_vendord_itf[i];
break;
}
@@ -209,61 +210,72 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, ui
TU_VERIFY(p_vendor, 0);
p_vendor->itf_num = desc_itf->bInterfaceNumber;
- if (desc_itf->bNumEndpoints)
- {
+ uint8_t found_ep = 0;
+ while (found_ep < desc_itf->bNumEndpoints) {
// skip non-endpoint descriptors
- while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) )
- {
+ while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) ) {
p_desc = tu_desc_next(p_desc);
}
+ if (p_desc >= desc_end) {
+ break;
+ }
- // Open endpoint pair with usbd helper
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
-
- p_desc += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
+ TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
+ found_ep++;
- // Prepare for incoming data
- if ( p_vendor->ep_out )
- {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)), 0);
+ if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
+ tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
+ } else {
+ tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
+ TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
}
- if ( p_vendor->ep_in ) maybe_transmit(p_vendor);
+ p_desc = tu_desc_next(p_desc);
}
return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf);
}
-bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) rhport;
+bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
- uint8_t itf = 0;
- vendord_interface_t* p_itf = _vendord_itf;
-
- for ( ; ; itf++, p_itf++)
- {
- if (itf >= TU_ARRAY_SIZE(_vendord_itf)) return false;
+ uint8_t itf;
+ vendord_interface_t* p_vendor;
- if ( ( ep_addr == p_itf->ep_out ) || ( ep_addr == p_itf->ep_in ) ) break;
+ for (itf = 0; itf < CFG_TUD_VENDOR; itf++) {
+ p_vendor = &_vendord_itf[itf];
+ if ((ep_addr == p_vendor->rx.stream.ep_addr) || (ep_addr == p_vendor->tx.stream.ep_addr)) {
+ break;
+ }
}
+ TU_VERIFY(itf < CFG_TUD_VENDOR);
+ vendord_epbuf_t* p_epbuf = &_vendord_epbuf[itf];
- if ( ep_addr == p_itf->ep_out )
- {
- // Receive new data
- tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, (uint16_t) xferred_bytes);
+ if ( ep_addr == p_vendor->rx.stream.ep_addr ) {
+ // Received new data: put into stream's fifo
+ 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);
+ if (tud_vendor_rx_cb) {
+ tud_vendor_rx_cb(itf, p_epbuf->epout, (uint16_t) xferred_bytes);
+ }
- _prep_out_transaction(p_itf);
- }
- else if ( ep_addr == p_itf->ep_in )
- {
- if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
- // Send complete, try to send more if possible
- maybe_transmit(p_itf);
+ 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);
+ }
+
+ #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
+ // try to send more if possible
+ if ( 0 == tu_edpt_stream_write_xfer(rhport, &p_vendor->tx.stream) ) {
+ // If there is no data left, a ZLP should be sent if xferred_bytes is multiple of EP Packet size and not zero
+ tu_edpt_stream_write_zlp_if_needed(rhport, &p_vendor->tx.stream, xferred_bytes);
+ }
+ #endif
}
return true;
diff --git a/src/class/vendor/vendor_device.h b/src/class/vendor/vendor_device.h
index 4a873e5fc..149ae2d56 100644
--- a/src/class/vendor/vendor_device.h
+++ b/src/class/vendor/vendor_device.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -33,107 +33,106 @@
#define CFG_TUD_VENDOR_EPSIZE 64
#endif
+// RX FIFO can be disabled by setting this value to 0
+#ifndef CFG_TUD_VENDOR_RX_BUFSIZE
+#define CFG_TUD_VENDOR_RX_BUFSIZE 64
+#endif
+
+// TX FIFO can be disabled by setting this value to 0
+#ifndef CFG_TUD_VENDOR_TX_BUFSIZE
+#define CFG_TUD_VENDOR_TX_BUFSIZE 64
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
-// Application API (Multiple Interfaces)
+// Application API (Multiple Interfaces) i.e CFG_TUD_VENDOR > 1
//--------------------------------------------------------------------+
bool tud_vendor_n_mounted (uint8_t itf);
-
uint32_t tud_vendor_n_available (uint8_t itf);
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
void tud_vendor_n_read_flush (uint8_t itf);
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
+uint32_t tud_vendor_n_write_flush (uint8_t itf);
uint32_t tud_vendor_n_write_available (uint8_t itf);
-static inline
-uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
-uint32_t tud_vendor_n_flush (uint8_t itf);
-
-//--------------------------------------------------------------------+
-// Application API (Single Port)
-//--------------------------------------------------------------------+
-static inline bool tud_vendor_mounted (void);
-static inline uint32_t tud_vendor_available (void);
-static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
-static inline bool tud_vendor_peek (uint8_t* ui8);
-static inline void tud_vendor_read_flush (void);
-static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_vendor_write_str (char const* str);
-static inline uint32_t tud_vendor_write_available (void);
-static inline uint32_t tud_vendor_flush (void);
-
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
-// Invoked when received new data
-TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf);
-// Invoked when last rx transfer finished
-TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
+// backward compatible
+#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf)
//--------------------------------------------------------------------+
-// Inline Functions
+// Application API (Single Port) i.e CFG_TUD_VENDOR = 1
//--------------------------------------------------------------------+
-static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str)
-{
- return tud_vendor_n_write(itf, str, strlen(str));
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_n_write_str(uint8_t itf, char const* str) {
+ return tud_vendor_n_write(itf, str, strlen(str));
}
-static inline bool tud_vendor_mounted (void)
-{
- return tud_vendor_n_mounted(0);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_mounted(void) {
+ return tud_vendor_n_mounted(0);
}
-static inline uint32_t tud_vendor_available (void)
-{
- return tud_vendor_n_available(0);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_available(void) {
+ return tud_vendor_n_available(0);
}
-static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
-{
- return tud_vendor_n_read(0, buffer, bufsize);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) {
+ return tud_vendor_n_read(0, buffer, bufsize);
}
-static inline bool tud_vendor_peek (uint8_t* ui8)
-{
- return tud_vendor_n_peek(0, ui8);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_vendor_peek(uint8_t* ui8) {
+ return tud_vendor_n_peek(0, ui8);
}
-static inline void tud_vendor_read_flush(void)
-{
- tud_vendor_n_read_flush(0);
+TU_ATTR_ALWAYS_INLINE static inline void tud_vendor_read_flush(void) {
+ tud_vendor_n_read_flush(0);
}
-static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
-{
- return tud_vendor_n_write(0, buffer, bufsize);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write(void const* buffer, uint32_t bufsize) {
+ return tud_vendor_n_write(0, buffer, bufsize);
}
-static inline uint32_t tud_vendor_write_str (char const* str)
-{
- return tud_vendor_n_write_str(0, str);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_str(char const* str) {
+ return tud_vendor_n_write_str(0, str);
}
-static inline uint32_t tud_vendor_write_available (void)
-{
- return tud_vendor_n_write_available(0);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_flush(void) {
+ return tud_vendor_n_write_flush(0);
}
-static inline uint32_t tud_vendor_flush (void)
-{
- return tud_vendor_n_flush(0);
+#if CFG_TUD_VENDOR_TX_BUFSIZE > 0
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_vendor_write_available(void) {
+ return tud_vendor_n_write_available(0);
}
+#endif
+
+// backward compatible
+#define tud_vendor_flush() tud_vendor_write_flush()
+
+//--------------------------------------------------------------------+
+// Application Callback API (weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked when received new data
+TU_ATTR_WEAK 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);
+
+//--------------------------------------------------------------------+
+// Inline Functions
+//--------------------------------------------------------------------+
+
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void vendord_init(void);
+bool vendord_deinit(void);
void vendord_reset(uint8_t rhport);
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
diff --git a/src/class/vendor/vendor_host.c b/src/class/vendor/vendor_host.c
index dbea1228d..e66c5007f 100644
--- a/src/class/vendor/vendor_host.c
+++ b/src/class/vendor/vendor_host.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/class/vendor/vendor_host.h b/src/class/vendor/vendor_host.h
index 65223fbca..acfebe7a4 100644
--- a/src/class/vendor/vendor_host.h
+++ b/src/class/vendor/vendor_host.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/class/video/video.h b/src/class/video/video.h
index e8227ea60..f348e187b 100644
--- a/src/class/video/video.h
+++ b/src/class/video/video.h
@@ -29,6 +29,10 @@
#include "common/tusb_common.h"
+enum {
+ VIDEO_BCD_1_50 = 0x0150,
+};
+
// Table 3-19 Color Matching Descriptor
typedef enum {
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
@@ -156,22 +160,23 @@ typedef enum {
/* A.9.1 VideoControl Interface Control Selectors */
typedef enum {
VIDEO_VC_CTL_UNDEFINED = 0x00,
- VIDEO_VC_CTL_VIDEO_POWER_MODE,
- VIDEO_VC_CTL_REQUEST_ERROR_CODE,
+ VIDEO_VC_CTL_VIDEO_POWER_MODE, // 0x01
+ VIDEO_VC_CTL_REQUEST_ERROR_CODE, // 0x02
} video_interface_control_selector_t;
/* A.9.8 VideoStreaming Interface Control Selectors */
typedef enum {
VIDEO_VS_CTL_UNDEFINED = 0x00,
- VIDEO_VS_CTL_PROBE,
- VIDEO_VS_CTL_COMMIT,
- VIDEO_VS_CTL_STILL_PROBE,
- VIDEO_VS_CTL_STILL_COMMIT,
- VIDEO_VS_CTL_STILL_IMAGE_TRIGGER,
- VIDEO_VS_CTL_STREAM_ERROR_CODE,
- VIDEO_VS_CTL_GENERATE_KEY_FRAME,
- VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT,
- VIDEO_VS_CTL_SYNCH_DELAY_CONTROL,
+ VIDEO_VS_CTL_PROBE, // 0x01
+ VIDEO_VS_CTL_COMMIT, // 0x02
+ VIDEO_VS_CTL_STILL_PROBE, // 0x03
+ VIDEO_VS_CTL_STILL_COMMIT, // 0x04
+ VIDEO_VS_CTL_STILL_IMAGE_TRIGGER, // 0x05
+ VIDEO_VS_CTL_STREAM_ERROR_CODE, // 0x06
+ VIDEO_VS_CTL_GENERATE_KEY_FRAME, // 0x07
+ VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT, // 0x08
+ VIDEO_VS_CTL_SYNCH_DELAY_CONTROL, // 0x09
+
} video_interface_streaming_selector_t;
/* B. Terminal Types */
@@ -198,55 +203,98 @@ typedef enum {
} video_terminal_type_t;
//--------------------------------------------------------------------+
-// Descriptors
+// Video Control (VC) Descriptors
//--------------------------------------------------------------------+
/* 2.3.4.2 */
+#define tusb_desc_video_control_header_nitf_t(_nitf) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint16_t bcdUVC; \
+ uint16_t wTotalLength; \
+ uint32_t dwClockFrequency; /* deprecated */ \
+ uint8_t bInCollection; \
+ uint8_t baInterfaceNr[_nitf]; \
+ }
+
+typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t;
+typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t;
+typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t;
+typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t;
+typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t;
+
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
- uint16_t bcdUVC;
- uint16_t wTotalLength;
- uint32_t dwClockFrequency;
- uint8_t bInCollection;
- uint8_t baInterfaceNr[];
-} tusb_desc_cs_video_ctl_itf_hdr_t;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+} tusb_desc_video_control_input_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct");
-/* 2.4.3.3 */
typedef struct TU_ATTR_PACKED {
- uint8_t bHeaderLength;
- union {
- uint8_t bmHeaderInfo;
- struct {
- uint8_t FrameID: 1;
- uint8_t EndOfFrame: 1;
- uint8_t PresentationTime: 1;
- uint8_t SourceClockReference: 1;
- uint8_t PayloadSpecific: 1;
- uint8_t StillImage: 1;
- uint8_t Error: 1;
- uint8_t EndOfHeader: 1;
- };
- };
-} tusb_video_payload_header_t;
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} tusb_desc_video_control_output_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct");
-/* 3.9.2.1 */
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
- uint8_t bNumFormats;
- uint16_t wTotalLength;
- uint8_t bEndpointAddress;
- uint8_t bmInfo;
- uint8_t bTerminalLink;
- uint8_t bStillCaptureMethod;
- uint8_t bTriggerSupport;
- uint8_t bTriggerUsage;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+
+ uint16_t wObjectiveFocalLengthMin;
+ uint16_t wObjectiveFocalLengthMax;
+ uint16_t wOcularFocalLength;
uint8_t bControlSize;
- uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_in_hdr_t;
+ uint8_t bmControls[3];
+} tusb_desc_video_control_camera_terminal_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Video Streaming (VS) Descriptors
+//--------------------------------------------------------------------+
+
+/* 3.9.2.1 */
+#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \
+ uint16_t wTotalLength; \
+ uint8_t bEndpointAddress; \
+ uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \
+ uint8_t bTerminalLink; \
+ uint8_t bStillCaptureMethod; \
+ uint8_t bTriggerSupport; /* Hardware trigger supported */ \
+ uint8_t bTriggerUsage; \
+ uint8_t bControlSize; /* sizeof of each control item */ \
+ uint8_t bmaControls[_nb]; \
+ }
+
+typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t;
+typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t;
/* 3.9.2.2 */
typedef struct TU_ATTR_PACKED {
@@ -259,7 +307,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bTerminalLink;
uint8_t bControlSize;
uint8_t bmaControls[];
-} tusb_desc_cs_video_stm_itf_out_hdr_t;
+} tusb_desc_video_streaming_output_header_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -285,14 +333,33 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmaControls[];
} output;
};
-} tusb_desc_cs_video_stm_itf_hdr_t;
+} tusb_desc_video_streaming_inout_header_t;
+
+// 3.9.2.6 Color Matching Descriptor
+typedef struct TU_ATTR_PACKED {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bColorPrimaries;
+ uint8_t bTransferCharacteristics;
+ uint8_t bMatrixCoefficients;
+} tusb_desc_video_streaming_color_matching_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Format and Frame Descriptor
+// Note: bFormatIndex & bFrameIndex are 1-based index
+//--------------------------------------------------------------------+
+//------------- Uncompressed -------------//
+// Uncompressed payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
- uint8_t bNumFrameDescriptors;
+ uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format
uint8_t guidFormat[16];
uint8_t bBitsPerPixel;
uint8_t bDefaultFrameIndex;
@@ -300,22 +367,69 @@ typedef struct TU_ATTR_PACKED {
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_uncompressed_t;
+} tusb_desc_video_format_uncompressed_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_uncompressed_t) == 27, "size is not correct");
+
+// Uncompressed payload specs: 3.1.2 frame descriptor
+#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \
+ struct TU_ATTR_PACKED { \
+ uint8_t bLength; \
+ uint8_t bDescriptorType; \
+ uint8_t bDescriptorSubType; \
+ uint8_t bFrameIndex; \
+ uint8_t bmCapabilities; \
+ uint16_t wWidth; \
+ uint16_t wHeight; \
+ uint32_t dwMinBitRate; \
+ uint32_t dwMaxBitRate; \
+ uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \
+ uint32_t dwDefaultFrameInterval; /* 100ns unit */\
+ uint8_t bFrameIntervalType; \
+ uint32_t dwFrameInterval[_nint]; \
+ }
+
+typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t;
+typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t;
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct");
+
+//------------- MJPEG -------------//
+// MJPEG payload specs: 3.1.1 format descriptor
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
- uint8_t bmFlags;
+ uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes)
uint8_t bDefaultFrameIndex;
uint8_t bAspectRatioX;
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
-} tusb_desc_cs_video_fmt_mjpeg_t;
+} tusb_desc_video_format_mjpeg_t;
+
+TU_VERIFY_STATIC(sizeof(tusb_desc_video_format_mjpeg_t) == 11, "size is not correct");
+// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed)
+typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t;
+typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t;
+typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t;
+typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t;
+typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t;
+
+// continuous = 3 intervals: min, max, step
+typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t;
+
+//------------- DV -------------//
+// DV payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -323,8 +437,9 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFormatIndex;
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
uint8_t bFormatType;
-} tusb_desc_cs_video_fmt_dv_t;
+} tusb_desc_video_format_dv_t;
+// Frame Based payload specs: 3.1.1
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -339,25 +454,7 @@ typedef struct TU_ATTR_PACKED {
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
uint8_t bVaribaleSize;
-} tusb_desc_cs_video_fmt_frame_based_t;
-
-typedef struct TU_ATTR_PACKED {
- uint8_t bLength;
- uint8_t bDescriptorType;
- uint8_t bDescriptorSubType;
- uint8_t bFrameIndex;
- uint8_t bmCapabilities;
- uint16_t wWidth;
- uint16_t wHeight;
- uint32_t dwMinBitRate;
- uint32_t dwMaxBitRate;
- uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
- uint32_t dwDefaultFrameInterval;
- uint8_t bFrameIntervalType;
- uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_uncompressed_t;
-
-typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t;
+} tusb_desc_video_format_framebased_t;
typedef struct TU_ATTR_PACKED {
uint8_t bLength;
@@ -373,12 +470,30 @@ typedef struct TU_ATTR_PACKED {
uint8_t bFrameIntervalType;
uint32_t dwBytesPerLine;
uint32_t dwFrameInterval[];
-} tusb_desc_cs_video_frm_frame_based_t;
+} tusb_desc_video_frame_framebased_t;
//--------------------------------------------------------------------+
// Requests
//--------------------------------------------------------------------+
+/* 2.4.3.3 */
+typedef struct TU_ATTR_PACKED {
+ uint8_t bHeaderLength;
+ union {
+ uint8_t bmHeaderInfo;
+ struct {
+ uint8_t FrameID: 1;
+ uint8_t EndOfFrame: 1;
+ uint8_t PresentationTime: 1;
+ uint8_t SourceClockReference: 1;
+ uint8_t PayloadSpecific: 1;
+ uint8_t StillImage: 1;
+ uint8_t Error: 1;
+ uint8_t EndOfHeader: 1;
+ };
+ };
+} tusb_video_payload_header_t;
+
/* 4.3.1.1 */
typedef struct TU_ATTR_PACKED {
union {
@@ -425,32 +540,36 @@ typedef struct TU_ATTR_PACKED {
TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not correct");
-#define TUD_VIDEO_DESC_IAD_LEN 8
-#define TUD_VIDEO_DESC_STD_VC_LEN 9
-#define TUD_VIDEO_DESC_CS_VC_LEN 12
-#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8
-#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9
-#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18
-#define TUD_VIDEO_DESC_STD_VS_LEN 9
-#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13
-#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9
-#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27
-#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11
-#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38
-#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26
-#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38
-#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26
-#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6
+#define TUD_VIDEO_DESC_IAD_LEN 8
+#define TUD_VIDEO_DESC_STD_VC_LEN 9
+#define TUD_VIDEO_DESC_CS_VC_LEN 12
+#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8
+#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9
+#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18
+#define TUD_VIDEO_DESC_STD_VS_LEN 9
+#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13
+#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9
+#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27
+#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11
+#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN 28
+#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38
+#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26
+#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38
+#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26
+#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN 38
+#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN 26
+#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6
/* 2.2 compression formats */
#define TUD_VIDEO_GUID_YUY2 0x59,0x55,0x59,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
#define TUD_VIDEO_GUID_NV12 0x4E,0x56,0x31,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
#define TUD_VIDEO_GUID_M420 0x4D,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
#define TUD_VIDEO_GUID_I420 0x49,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
+#define TUD_VIDEO_GUID_H264 0x48,0x32,0x36,0x34,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
-#define TUD_VIDEO_DESC_IAD(_firstitfs, _nitfs, _stridx) \
+#define TUD_VIDEO_DESC_IAD(_firstitf, _nitfs, _stridx) \
TUD_VIDEO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, \
- _firstitfs, _nitfs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_INTERFACE_COLLECTION, \
+ _firstitf, _nitfs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_INTERFACE_COLLECTION, \
VIDEO_ITF_PROTOCOL_UNDEFINED, _stridx
#define TUD_VIDEO_DESC_STD_VC(_itfnum, _nEPs, _stridx) \
@@ -537,10 +656,29 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
- TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \
+ TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
+/* Motion-Frame-Based 3.1.1 Table 3-1 */
+#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED(_fmtidx, _numfrmdesc, _guid, _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize) \
+ TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED, \
+ _fmtidx, _numfrmdesc, TUD_VIDEO_GUID(_guid), _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize
+
+/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-3 */
+#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, _minfrminterval, _maxfrminterval, _frmintervalstep) \
+ TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \
+ _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
+ U32_TO_U8S_LE(_frminterval), 0, U32_TO_U8S_LE(_bytesperline), \
+ U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
+
+/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-4 */
+#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, ...) \
+ TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
+ TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \
+ _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
+U32_TO_U8S_LE(_frminterval), U32_TO_U8S_LE(_bytesperline), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
+
/* 3.9.2.6 */
#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \
@@ -549,7 +687,7 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c
/* 3.10.1.1 */
#define TUD_VIDEO_DESC_EP_ISO(_ep, _epsize, _ep_interval) \
- 7, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS,\
+ 7, TUSB_DESC_ENDPOINT, _ep, (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS),\
U16_TO_U8S_LE(_epsize), _ep_interval
/* 3.10.1.2 */
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index a6d2724c1..3124d5596 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Koji KITAYAMA
@@ -34,22 +34,33 @@
#include "video_device.h"
+// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
+#ifndef CFG_TUD_VIDEO_LOG_LEVEL
+ #define CFG_TUD_VIDEO_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#endif
+
+#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_VIDEO_LOG_LEVEL, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+#define VS_STATE_PROBING 0 /* Configuration in progress */
+#define VS_STATE_COMMITTED 1 /* Ready for streaming or Streaming via bulk endpoint */
+#define VS_STATE_STREAMING 2 /* Streaming via isochronous endpoint */
+
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
+ tusb_desc_video_control_header_t ctl;
} tusb_desc_vc_itf_t;
typedef struct {
tusb_desc_interface_t std;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_vs_itf_t;
typedef union {
- tusb_desc_cs_video_ctl_itf_hdr_t ctl;
- tusb_desc_cs_video_stm_itf_hdr_t stm;
+ tusb_desc_video_control_header_t ctl;
+ tusb_desc_video_streaming_inout_header_t stm;
} tusb_desc_video_itf_hdr_t;
typedef struct TU_ATTR_PACKED {
@@ -67,9 +78,9 @@ typedef union {
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
};
- tusb_desc_cs_video_fmt_uncompressed_t uncompressed;
- tusb_desc_cs_video_fmt_mjpeg_t mjpeg;
- tusb_desc_cs_video_fmt_frame_based_t frame_based;
+ tusb_desc_video_format_uncompressed_t uncompressed;
+ tusb_desc_video_format_mjpeg_t mjpeg;
+ tusb_desc_video_format_framebased_t frame_based;
} tusb_desc_cs_video_fmt_t;
typedef union {
@@ -82,9 +93,9 @@ typedef union {
uint16_t wWidth;
uint16_t wHeight;
};
- tusb_desc_cs_video_frm_uncompressed_t uncompressed;
- tusb_desc_cs_video_frm_mjpeg_t mjpeg;
- tusb_desc_cs_video_frm_frame_based_t frame_based;
+ tusb_desc_video_frame_uncompressed_t uncompressed;
+ tusb_desc_video_frame_mjpeg_t mjpeg;
+ tusb_desc_video_frame_framebased_t frame_based;
} tusb_desc_cs_video_frm_t;
/* video streaming interface */
@@ -102,42 +113,95 @@ typedef struct TU_ATTR_PACKED {
uint32_t offset; /* offset for the next payload transfer */
uint32_t max_payload_transfer_size;
uint8_t error_code;/* error code */
- /*------------- From this point, data is not cleared by bus reset -------------*/
- CFG_TUSB_MEM_ALIGN uint8_t ep_buf[CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE]; /* EP transfer buffer for streaming */
+ uint8_t state; /* 0:probing 1:committed 2:streaming */
+
+ video_probe_and_commit_control_t probe_commit_payload; /* Probe and Commit control */
} videod_streaming_interface_t;
+typedef struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE);
+} videod_streaming_epbuf_t;
+
/* video control interface */
typedef struct TU_ATTR_PACKED {
- void const *beg; /* The head of the first video control interface descriptor */
- uint16_t len; /* Byte length of the descriptors */
- uint16_t cur; /* offset for current video control interface */
- uint8_t stm[CFG_TUD_VIDEO_STREAMING]; /* Indices of streaming interface */
- uint8_t error_code; /* error code */
- uint8_t power_mode;
-
- /*------------- From this point, data is not cleared by bus reset -------------*/
- // CFG_TUSB_MEM_ALIGN uint8_t ctl_buf[64]; /* EP transfer buffer for interrupt transfer */
-
+ const uint8_t*beg; /* The head of the first video control interface descriptor */
+ uint16_t len; /* Byte length of the descriptors */
+ uint16_t cur; /* offset for current video control interface */
+ uint8_t stm[CFG_TUD_VIDEO_STREAMING]; /* Indices of streaming interface */
+ uint8_t error_code; /* error code */
+ uint8_t power_mode;
} videod_interface_t;
-#define ITF_STM_MEM_RESET_SIZE offsetof(videod_streaming_interface_t, ep_buf)
-
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static videod_interface_t _videod_itf[CFG_TUD_VIDEO];
-CFG_TUSB_MEM_SECTION static videod_streaming_interface_t _videod_streaming_itf[CFG_TUD_VIDEO_STREAMING];
+static videod_interface_t _videod_itf[CFG_TUD_VIDEO];
+
+static videod_streaming_interface_t _videod_streaming_itf[CFG_TUD_VIDEO_STREAMING];
+CFG_TUD_MEM_SECTION static videod_streaming_epbuf_t _videod_streaming_epbuf[CFG_TUD_VIDEO_STREAMING];
static uint8_t const _cap_get = 0x1u; /* support for GET */
static uint8_t const _cap_get_set = 0x3u; /* support for GET and SET */
+//--------------------------------------------------------------------+
+// Debug
+//--------------------------------------------------------------------+
+#if CFG_TUSB_DEBUG >= CFG_TUD_VIDEO_LOG_LEVEL
+
+static tu_lookup_entry_t const tu_lookup_video_request[] = {
+ {.key = VIDEO_REQUEST_UNDEFINED, .data = "Undefined"},
+ {.key = VIDEO_REQUEST_SET_CUR, .data = "SetCur"},
+ {.key = VIDEO_REQUEST_SET_CUR_ALL, .data = "SetCurAll"},
+ {.key = VIDEO_REQUEST_GET_CUR, .data = "GetCur"},
+ {.key = VIDEO_REQUEST_GET_MIN, .data = "GetMin"},
+ {.key = VIDEO_REQUEST_GET_MAX, .data = "GetMax"},
+ {.key = VIDEO_REQUEST_GET_RES, .data = "GetRes"},
+ {.key = VIDEO_REQUEST_GET_LEN, .data = "GetLen"},
+ {.key = VIDEO_REQUEST_GET_INFO, .data = "GetInfo"},
+ {.key = VIDEO_REQUEST_GET_DEF, .data = "GetDef"},
+ {.key = VIDEO_REQUEST_GET_CUR_ALL, .data = "GetCurAll"},
+ {.key = VIDEO_REQUEST_GET_MIN_ALL, .data = "GetMinAll"},
+ {.key = VIDEO_REQUEST_GET_MAX_ALL, .data = "GetMaxAll"},
+ {.key = VIDEO_REQUEST_GET_RES_ALL, .data = "GetResAll"},
+ {.key = VIDEO_REQUEST_GET_DEF_ALL, .data = "GetDefAll"},
+};
+
+static tu_lookup_table_t const tu_table_video_request = {
+ .count = TU_ARRAY_SIZE(tu_lookup_video_request),
+ .items = tu_lookup_video_request
+};
+
+static char const* const tu_str_video_vc_control_selector[] = {
+ "Undefined",
+ "Video Power Mode",
+ "Request Error Code",
+};
+
+static char const* const tu_str_video_vs_control_selector[] = {
+ "Undefined",
+ "Probe",
+ "Commit",
+ "Still Probe",
+ "Still Commit",
+ "Still Image Trigger",
+ "Stream Error Code",
+ "Generate Key Frame",
+ "Update Frame Segment",
+ "Sync Delay",
+};
+
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
/** Get interface number from the interface descriptor
*
* @param[in] desc interface descriptor
*
* @return bInterfaceNumber */
-static inline uint8_t _desc_itfnum(void const *desc)
-{
+static inline uint8_t _desc_itfnum(void const *desc) {
return ((uint8_t const*)desc)[2];
}
@@ -146,8 +210,7 @@ static inline uint8_t _desc_itfnum(void const *desc)
* @param[in] desc endpoint descriptor
*
* @return bEndpointAddress */
-static inline uint8_t _desc_ep_addr(void const *desc)
-{
+static inline uint8_t _desc_ep_addr(void const *desc) {
return ((uint8_t const*)desc)[2];
}
@@ -157,8 +220,7 @@ static inline uint8_t _desc_ep_addr(void const *desc)
* @param[in] stm_idx index number of streaming interface
*
* @return instance */
-static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
-{
+static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
videod_interface_t *ctl = &_videod_itf[ctl_idx];
if (!ctl->beg) return NULL;
videod_streaming_interface_t *stm = &_videod_streaming_itf[ctl->stm[stm_idx]];
@@ -166,15 +228,13 @@ static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_id
return stm;
}
-static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self)
-{
+static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self) {
return (tusb_desc_vc_itf_t const *)(self->beg + self->cur);
}
-static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self)
-{
+static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self) {
if (!self->desc.cur) return NULL;
- void const *desc = _videod_itf[self->index_vc].beg;
+ uint8_t const *desc = _videod_itf[self->index_vc].beg;
return (tusb_desc_vs_itf_t const*)(desc + self->desc.cur);
}
@@ -186,8 +246,7 @@ static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const
*
* @return The pointer for interface descriptor.
* @retval end did not found interface descriptor */
-static void const* _find_desc(void const *beg, void const *end, uint_fast8_t desc_type)
-{
+static void const* _find_desc(void const *beg, void const *end, uint_fast8_t desc_type) {
void const *cur = beg;
while ((cur < end) && (desc_type != tu_desc_type(cur))) {
cur = tu_desc_next(cur);
@@ -195,6 +254,24 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des
return cur;
}
+/** Find the first descriptor of two given types
+ *
+ * @param[in] beg The head of descriptor byte array.
+ * @param[in] end The tail of descriptor byte array.
+ * @param[in] desc_type_0 The first target descriptor type.
+ * @param[in] desc_type_1 The second target descriptor type.
+ *
+ * @return The pointer for interface descriptor.
+ * @retval end did not found interface descriptor */
+static void const* _find_desc_2_type(void const *beg, void const *end, uint_fast8_t desc_type_0, uint_fast8_t desc_type_1)
+{
+ void const *cur = beg;
+ while ((cur < end) && (desc_type_0 != tu_desc_type(cur)) && (desc_type_1 != tu_desc_type(cur))) {
+ cur = tu_desc_next(cur);
+ }
+ return cur;
+}
+
/** Find the first descriptor specified by the arguments
*
* @param[in] beg The head of descriptor byte array.
@@ -208,8 +285,7 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des
static void const* _find_desc_3(void const *beg, void const *end,
uint_fast8_t desc_type,
uint_fast8_t element_0,
- uint_fast8_t element_1)
-{
+ uint_fast8_t element_1) {
for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, desc_type)) {
uint8_t const *p = (uint8_t const *)cur;
if ((p[2] == element_0) && (p[3] == element_1)) {
@@ -221,19 +297,22 @@ static void const* _find_desc_3(void const *beg, void const *end,
}
/** Return the next interface descriptor which has another interface number.
+ * If there are multiple VC interfaces, there will be an IAD descriptor before
+ * the next interface descriptor. Check both the IAD descriptor and the interface
+ * descriptor.
+ * 3.1 Descriptor Layout Overview
*
* @param[in] beg The head of descriptor byte array.
* @param[in] end The tail of descriptor byte array.
*
* @return The pointer for interface descriptor.
* @retval end did not found interface descriptor */
-static void const* _next_desc_itf(void const *beg, void const *end)
-{
+static void const* _next_desc_itf(void const *beg, void const *end) {
void const *cur = beg;
uint_fast8_t itfnum = ((tusb_desc_interface_t const*)cur)->bInterfaceNumber;
while ((cur < end) &&
(itfnum == ((tusb_desc_interface_t const*)cur)->bInterfaceNumber)) {
- cur = _find_desc(tu_desc_next(cur), end, TUSB_DESC_INTERFACE);
+ cur = _find_desc_2_type(tu_desc_next(cur), end, TUSB_DESC_INTERFACE, TUSB_DESC_INTERFACE_ASSOCIATION);
}
return cur;
}
@@ -247,9 +326,9 @@ static void const* _next_desc_itf(void const *beg, void const *end)
*
* @return The pointer for interface descriptor.
* @retval end did not found interface descriptor */
-static inline void const* _find_desc_itf(void const *beg, void const *end, uint_fast8_t itfnum, uint_fast8_t altnum)
+static inline uint8_t const* _find_desc_itf(void const *beg, void const *end, uint_fast8_t itfnum, uint_fast8_t altnum)
{
- return _find_desc_3(beg, end, TUSB_DESC_INTERFACE, itfnum, altnum);
+ return (uint8_t const*) _find_desc_3(beg, end, TUSB_DESC_INTERFACE, itfnum, altnum);
}
/** Find the first endpoint descriptor belonging to the current interface descriptor.
@@ -275,7 +354,7 @@ static void const* _find_desc_ep(void const *beg, void const *end)
static inline void const* _end_of_control_descriptor(void const *desc)
{
tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)desc;
- return desc + vc->std.bLength + vc->ctl.wTotalLength;
+ return ((uint8_t const*) desc) + vc->std.bLength + tu_le16toh(vc->ctl.wTotalLength);
}
/** Find the first entity descriptor with the entity ID
@@ -305,7 +384,7 @@ static void const* _find_desc_entity(void const *desc, uint_fast8_t entityid)
static inline void const* _end_of_streaming_descriptor(void const *desc)
{
tusb_desc_vs_itf_t const *vs = (tusb_desc_vs_itf_t const *)desc;
- return desc + vs->std.bLength + vs->stm.wTotalLength;
+ return ((uint8_t const*) desc) + vs->std.bLength + tu_le16toh(vs->stm.wTotalLength);
}
/** Find the first format descriptor with the specified format number. */
@@ -317,7 +396,7 @@ static inline void const *_find_desc_format(void const *beg, void const *end, ui
if ((fmt == VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED ||
fmt == VIDEO_CS_ITF_VS_FORMAT_MJPEG ||
fmt == VIDEO_CS_ITF_VS_FORMAT_DV ||
- fmt == VIDEO_CS_ITF_VS_FRAME_FRAME_BASED) &&
+ fmt == VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED) &&
fmtnum == p[3]) {
return cur;
}
@@ -379,8 +458,13 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
param->wCompQuality = 1; /* 1 to 10000 */
break;
- case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
+
+ case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
+ break;
+
+ case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED:
break;
+
default: return false;
}
@@ -401,9 +485,15 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8;
break;
+
case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */
break;
+
+ case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED:
+ frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */
+ break;
+
default: break;
}
param->dwMaxVideoFrameSize = frame_size;
@@ -422,8 +512,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm
uint_fast32_t interval_ms = interval / 10000;
TU_ASSERT(interval_ms);
uint_fast32_t payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
return true;
}
@@ -443,10 +534,12 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
if (_get_desc_vs(stm))
param->bFormatIndex = _get_desc_vs(stm)->stm.bNumFormats;
break;
+
case VIDEO_REQUEST_GET_MIN:
case VIDEO_REQUEST_GET_DEF:
param->bFormatIndex = 1;
break;
+
default: return false;
}
/* Set the parameters determined by the format */
@@ -475,18 +568,26 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
case VIDEO_REQUEST_GET_MAX:
frmnum = fmt->bNumFrameDescriptors;
break;
+
case VIDEO_REQUEST_GET_MIN:
frmnum = 1;
break;
+
case VIDEO_REQUEST_GET_DEF:
switch (fmt->bDescriptorSubType) {
- case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
- frmnum = fmt->uncompressed.bDefaultFrameIndex;
- break;
- case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
- frmnum = fmt->mjpeg.bDefaultFrameIndex;
- break;
- default: return false;
+ case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
+ frmnum = fmt->uncompressed.bDefaultFrameIndex;
+ break;
+
+ case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
+ frmnum = fmt->mjpeg.bDefaultFrameIndex;
+ break;
+
+ case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED:
+ frmnum = fmt->frame_based.bDefaultFrameIndex;
+ break;
+
+ default: return false;
}
break;
default: return false;
@@ -499,9 +600,15 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->uncompressed.bBitsPerPixel / 8;
break;
+
case VIDEO_CS_ITF_VS_FORMAT_MJPEG:
frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */
break;
+
+ case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED:
+ frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */
+ break;
+
default: return false;
}
param->dwMaxVideoFrameSize = frame_size;
@@ -517,41 +624,43 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
uint_fast32_t interval, interval_ms;
switch (request) {
- case VIDEO_REQUEST_GET_MAX:
- {
- uint_fast32_t min_interval, max_interval;
- uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
- max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1];
- min_interval = frm->uncompressed.dwFrameInterval[0];
- interval = max_interval;
- interval_ms = min_interval / 10000;
- }
+ case VIDEO_REQUEST_GET_MAX: {
+ uint_fast32_t min_interval, max_interval;
+ uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
+ max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1];
+ min_interval = frm->uncompressed.dwFrameInterval[0];
+ interval = max_interval;
+ interval_ms = min_interval / 10000;
break;
- case VIDEO_REQUEST_GET_MIN:
- {
- uint_fast32_t min_interval, max_interval;
- uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
- max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1];
- min_interval = frm->uncompressed.dwFrameInterval[0];
- interval = min_interval;
- interval_ms = max_interval / 10000;
- }
+ }
+
+ case VIDEO_REQUEST_GET_MIN: {
+ uint_fast32_t min_interval, max_interval;
+ uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
+ max_interval = num_intervals ? frm->uncompressed.dwFrameInterval[num_intervals - 1]: frm->uncompressed.dwFrameInterval[1];
+ min_interval = frm->uncompressed.dwFrameInterval[0];
+ interval = min_interval;
+ interval_ms = max_interval / 10000;
break;
+ }
+
case VIDEO_REQUEST_GET_DEF:
interval = frm->uncompressed.dwDefaultFrameInterval;
interval_ms = interval / 10000;
break;
- case VIDEO_REQUEST_GET_RES:
- {
- uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
- if (num_intervals) {
- interval = 0;
- } else {
- interval = frm->uncompressed.dwFrameInterval[2];
- interval_ms = interval / 10000;
- }
+
+ case VIDEO_REQUEST_GET_RES: {
+ uint_fast8_t num_intervals = frm->uncompressed.bFrameIntervalType;
+ if (num_intervals) {
+ interval = 0;
+ interval_ms = 0;
+ } else {
+ interval = frm->uncompressed.dwFrameInterval[2];
+ interval_ms = interval / 10000;
}
break;
+ }
+
default: return false;
}
param->dwFrameInterval = interval;
@@ -565,8 +674,9 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
} else {
payload_size = (frame_size + interval_ms - 1) / interval_ms + 2;
}
- if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size)
+ if (CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE < payload_size) {
payload_size = CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE;
+ }
param->dwMaxPayloadTransferSize = payload_size;
}
return true;
@@ -581,8 +691,10 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const *
static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self)
{
tusb_desc_vc_itf_t const *vc = _get_desc_vc(self);
+
/* The next descriptor after the class-specific VC interface header descriptor. */
- void const *cur = (void const*)vc + vc->std.bLength + vc->ctl.bLength;
+ void const *cur = (uint8_t const*)vc + vc->std.bLength + vc->ctl.bLength;
+
/* The end of the video control interface descriptor. */
void const *end = _end_of_control_descriptor(vc);
if (vc->std.bNumEndpoints) {
@@ -602,24 +714,26 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self)
* @param[in] altnum The target alternate setting number. */
static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t altnum)
{
- TU_LOG2(" open VC %d\n", altnum);
- void const *beg = self->beg;
- void const *end = beg + self->len;
+ TU_LOG_DRV(" open VC %d\r\n", altnum);
+ uint8_t const *beg = self->beg;
+ uint8_t const *end = beg + self->len;
+
/* The first descriptor is a video control interface descriptor. */
- void const *cur = _find_desc_itf(beg, end, _desc_itfnum(beg), altnum);
- TU_LOG2(" cur %d\n", cur - beg);
+ uint8_t const *cur = _find_desc_itf(beg, end, _desc_itfnum(beg), altnum);
+ TU_LOG_DRV(" cur %" PRId32 "\r\n", (int32_t) (cur - beg));
TU_VERIFY(cur < end);
tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)cur;
- TU_LOG2(" bInCollection %d\n", vc->ctl.bInCollection);
+ TU_LOG_DRV(" bInCollection %d\r\n", vc->ctl.bInCollection);
/* Support for up to 2 streaming interfaces only. */
TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING);
/* Update to point the end of the video control interface descriptor. */
- end = _end_of_control_descriptor(cur);
+ end = _end_of_control_descriptor(cur);
+
/* Advance to the next descriptor after the class-specific VC interface header descriptor. */
cur += vc->std.bLength + vc->ctl.bLength;
- TU_LOG2(" bNumEndpoints %d\n", vc->std.bNumEndpoints);
+ TU_LOG_DRV(" bNumEndpoints %d\r\n", vc->std.bNumEndpoints);
/* Open the notification endpoint if it exist. */
if (vc->std.bNumEndpoints) {
/* Support for 1 endpoint only. */
@@ -631,10 +745,19 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
/* Open the notification endpoint */
TU_ASSERT(usbd_edpt_open(rhport, notif));
}
- self->cur = (uint16_t) ((void const*)vc - beg);
+ self->cur = (uint16_t) ((uint8_t const*)vc - beg);
return true;
}
+static bool _init_vs_configuration(videod_streaming_interface_t *stm) {
+ /* initialize streaming settings */
+ stm->state = VS_STATE_PROBING;
+ stm->max_payload_transfer_size = 0;
+ video_probe_and_commit_control_t *param = &stm->probe_commit_payload;
+ tu_memclr(param, sizeof(*param));
+ return _update_streaming_parameters(stm, param);
+}
+
/** Set the alternate setting to own video streaming interface.
*
* @param[in,out] stm Streaming interface context.
@@ -642,85 +765,84 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t
static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint_fast8_t altnum)
{
uint_fast8_t i;
- TU_LOG2(" reopen VS %d\n", altnum);
- void const *desc = _videod_itf[stm->index_vc].beg;
+ TU_LOG_DRV(" reopen VS %d\r\n", altnum);
+ uint8_t const *desc = _videod_itf[stm->index_vc].beg;
+#ifndef TUP_DCD_EDPT_ISO_ALLOC
/* Close endpoints of previous settings. */
for (i = 0; i < TU_ARRAY_SIZE(stm->desc.ep); ++i) {
uint_fast16_t ofs_ep = stm->desc.ep[i];
if (!ofs_ep) break;
- uint8_t ep_adr = _desc_ep_addr(desc + ofs_ep);
- usbd_edpt_close(rhport, ep_adr);
- stm->desc.ep[i] = 0;
- TU_LOG2(" close EP%02x\n", ep_adr);
+ tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
+ /* Only ISO endpoints needs to be closed */
+ if(ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ stm->desc.ep[i] = 0;
+ usbd_edpt_close(rhport, ep->bEndpointAddress);
+ TU_LOG_DRV(" close EP%02x\r\n", ep->bEndpointAddress);
+ }
}
+#endif
+
/* clear transfer management information */
stm->buffer = NULL;
stm->bufsize = 0;
stm->offset = 0;
/* Find a alternate interface */
- void const *beg = desc + stm->desc.beg;
- void const *end = desc + stm->desc.end;
- void const *cur = _find_desc_itf(beg, end, _desc_itfnum(beg), altnum);
+ uint8_t const *beg = desc + stm->desc.beg;
+ uint8_t const *end = desc + stm->desc.end;
+ uint8_t const *cur = _find_desc_itf(beg, end, _desc_itfnum(beg), altnum);
TU_VERIFY(cur < end);
+
uint_fast8_t numeps = ((tusb_desc_interface_t const *)cur)->bNumEndpoints;
TU_ASSERT(numeps <= TU_ARRAY_SIZE(stm->desc.ep));
- stm->desc.cur = (uint16_t) (cur - desc); /* Save the offset of the new settings */
- if (!altnum) {
- /* initialize streaming settings */
- stm->max_payload_transfer_size = 0;
- video_probe_and_commit_control_t *param =
- (video_probe_and_commit_control_t *)&stm->ep_buf;
- tu_memclr(param, sizeof(*param));
- TU_LOG2(" done 0\n");
- return _update_streaming_parameters(stm, param);
+ stm->desc.cur = (uint16_t)(cur - desc); /* Save the offset of the new settings */
+ if (!altnum && (VS_STATE_COMMITTED != stm->state)) {
+ TU_VERIFY(_init_vs_configuration(stm));
}
- /* Open endpoints of the new settings. */
+ /* Open bulk or isochronous endpoints of the new settings. */
for (i = 0, cur = tu_desc_next(cur); i < numeps; ++i, cur = tu_desc_next(cur)) {
cur = _find_desc_ep(cur, end);
TU_ASSERT(cur < end);
tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)cur;
- if (!stm->max_payload_transfer_size) {
- video_probe_and_commit_control_t const *param = (video_probe_and_commit_control_t const*)&stm->ep_buf;
- uint_fast32_t max_size = param->dwMaxPayloadTransferSize;
- if ((TUSB_XFER_ISOCHRONOUS == ep->bmAttributes.xfer) &&
- (tu_edpt_packet_size(ep) < max_size))
- {
- /* FS must be less than or equal to max packet size */
- return false;
- }
- /* Set the negotiated value */
- stm->max_payload_transfer_size = max_size;
+ uint_fast32_t max_size = stm->max_payload_transfer_size;
+ if (altnum && (TUSB_XFER_ISOCHRONOUS == ep->bmAttributes.xfer)) {
+ /* FS must be less than or equal to max packet size */
+ TU_VERIFY (tu_edpt_packet_size(ep) >= max_size);
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ usbd_edpt_iso_activate(rhport, ep);
+#else
+ TU_ASSERT(usbd_edpt_open(rhport, ep));
+#endif
+ } else {
+ TU_VERIFY(TUSB_XFER_BULK == ep->bmAttributes.xfer);
+ TU_ASSERT(usbd_edpt_open(rhport, ep));
}
- TU_ASSERT(usbd_edpt_open(rhport, ep));
stm->desc.ep[i] = (uint16_t) (cur - desc);
- TU_LOG2(" open EP%02x\n", _desc_ep_addr(cur));
+ TU_LOG_DRV(" open EP%02x\r\n", _desc_ep_addr(cur));
}
- /* initialize payload header */
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf;
- hdr->bHeaderLength = sizeof(*hdr);
- hdr->bmHeaderInfo = 0;
-
- TU_LOG2(" done\n");
+ if (altnum) {
+ stm->state = VS_STATE_STREAMING;
+ }
+ TU_LOG_DRV(" done\r\n");
return true;
}
/** Prepare the next packet payload. */
-static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm)
-{
+static uint_fast16_t _prepare_in_payload(videod_streaming_interface_t *stm, uint8_t* ep_buf) {
uint_fast16_t remaining = stm->bufsize - stm->offset;
- uint_fast16_t hdr_len = stm->ep_buf[0];
+ uint_fast16_t hdr_len = ep_buf[0];
uint_fast16_t pkt_len = stm->max_payload_transfer_size;
if (hdr_len + remaining < pkt_len) {
pkt_len = hdr_len + remaining;
}
+ TU_ASSERT(pkt_len >= hdr_len);
uint_fast16_t data_len = pkt_len - hdr_len;
- memcpy(&stm->ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
+ memcpy(&ep_buf[hdr_len], stm->buffer + stm->offset, data_len);
stm->offset += data_len;
remaining -= data_len;
if (!remaining) {
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf;
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*) ep_buf;
hdr->EndOfFrame = 1;
}
return hdr_len + data_len;
@@ -731,6 +853,7 @@ static int handle_video_ctl_std_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
uint_fast8_t ctl_idx)
{
+ TU_LOG_DRV("\r\n");
switch (request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
if (stage == CONTROL_STAGE_SETUP)
@@ -768,7 +891,10 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage,
videod_interface_t *self = &_videod_itf[ctl_idx];
/* 4.2.1 Interface Control Request */
- switch (TU_U16_HIGH(request->wValue)) {
+ uint8_t const ctrl_sel = TU_U16_HIGH(request->wValue);
+ TU_LOG_DRV("%s_Control(%s)\r\n", tu_str_video_vc_control_selector[ctrl_sel], tu_lookup_find(&tu_table_video_request, request->bRequest));
+
+ switch (ctrl_sel) {
case VIDEO_VC_CTL_VIDEO_POWER_MODE:
switch (request->bRequest) {
case VIDEO_REQUEST_SET_CUR:
@@ -832,19 +958,19 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
uint_fast8_t ctl_idx)
{
- uint_fast8_t entity_id;
switch (request->bmRequestType_bit.type) {
case TUSB_REQ_TYPE_STANDARD:
return handle_video_ctl_std_req(rhport, stage, request, ctl_idx);
- case TUSB_REQ_TYPE_CLASS:
- entity_id = TU_U16_HIGH(request->wIndex);
+ case TUSB_REQ_TYPE_CLASS: {
+ uint_fast8_t entity_id = TU_U16_HIGH(request->wIndex);
if (!entity_id) {
return handle_video_ctl_cs_req(rhport, stage, request, ctl_idx);
} else {
TU_VERIFY(_find_desc_entity(_get_desc_vc(&_videod_itf[ctl_idx]), entity_id), VIDEO_ERROR_INVALID_REQUEST);
return VIDEO_ERROR_NONE;
}
+ }
default:
return VIDEO_ERROR_INVALID_REQUEST;
@@ -855,6 +981,7 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
uint_fast8_t stm_idx)
{
+ TU_LOG_DRV("\r\n");
videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
switch (request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
@@ -870,8 +997,7 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
return VIDEO_ERROR_NONE;
case TUSB_REQ_SET_INTERFACE:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(_open_vs_itf(rhport, self, request->wValue), VIDEO_ERROR_UNKNOWN);
tud_control_status(rhport, request);
}
@@ -885,26 +1011,27 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage,
static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
tusb_control_request_t const *request,
- uint_fast8_t stm_idx)
-{
+ uint_fast8_t stm_idx) {
(void)rhport;
- videod_streaming_interface_t *self = &_videod_streaming_itf[stm_idx];
+ videod_streaming_interface_t *stm = &_videod_streaming_itf[stm_idx];
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[stm_idx];
+
+ uint8_t const ctrl_sel = TU_U16_HIGH(request->wValue);
+ TU_LOG_DRV("%s_Control(%s)\r\n", tu_str_video_vs_control_selector[ctrl_sel], tu_lookup_find(&tu_table_video_request, request->bRequest));
/* 4.2.1 Interface Control Request */
- switch (TU_U16_HIGH(request->wValue)) {
+ switch (ctrl_sel) {
case VIDEO_VS_CTL_STREAM_ERROR_CODE:
switch (request->bRequest) {
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
/* TODO */
- TU_VERIFY(tud_control_xfer(rhport, request, &self->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->error_code, sizeof(uint8_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get, sizeof(_cap_get)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -914,23 +1041,25 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
break;
case VIDEO_VS_CTL_PROBE:
+ if (stm->state != VS_STATE_PROBING) {
+ stm->state = VS_STATE_PROBING;
+ }
+
switch (request->bRequest) {
case VIDEO_REQUEST_SET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)),
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)),
VIDEO_ERROR_UNKNOWN);
} else if (stage == CONTROL_STAGE_DATA) {
- TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf),
+ TU_VERIFY(_update_streaming_parameters(stm, &stm->probe_commit_payload),
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
@@ -938,19 +1067,16 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
case VIDEO_REQUEST_GET_MAX:
case VIDEO_REQUEST_GET_RES:
case VIDEO_REQUEST_GET_DEF:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- video_probe_and_commit_control_t tmp;
- tmp = *(video_probe_and_commit_control_t*)&self->ep_buf;
- TU_VERIFY(_negotiate_streaming_parameters(self, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ video_probe_and_commit_control_t tmp = stm->probe_commit_payload;
+ TU_VERIFY(_negotiate_streaming_parameters(stm, request->bRequest, &tmp), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
TU_VERIFY(tud_control_xfer(rhport, request, &tmp, sizeof(tmp)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_LEN:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(2 == request->wLength, VIDEO_ERROR_UNKNOWN);
uint16_t len = sizeof(video_probe_and_commit_control_t);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)&len, sizeof(len)), VIDEO_ERROR_UNKNOWN);
@@ -958,8 +1084,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t)&_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN);
}
@@ -973,27 +1098,38 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
switch (request->bRequest) {
case VIDEO_REQUEST_SET_CUR:
if (stage == CONTROL_STAGE_SETUP) {
- TU_VERIFY(sizeof(video_probe_and_commit_control_t) == request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
} else if (stage == CONTROL_STAGE_DATA) {
- TU_VERIFY(_update_streaming_parameters(self, (video_probe_and_commit_control_t*)self->ep_buf), VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE);
+ video_probe_and_commit_control_t *param = &stm->probe_commit_payload;
+ 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) {
- return tud_video_commit_cb(self->index_vc, self->index_vs, (video_probe_and_commit_control_t*)self->ep_buf);
+ 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;
+ stm->bufsize = 0;
+ stm->offset = 0;
+ /* initialize payload header */
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
+ hdr->bHeaderLength = sizeof(*hdr);
+ hdr->bmHeaderInfo = 0;
}
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_CUR:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(request->wLength, VIDEO_ERROR_UNKNOWN);
- TU_VERIFY(tud_control_xfer(rhport, request, self->ep_buf, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
+ TU_VERIFY(tud_control_xfer(rhport, request, &stm->probe_commit_payload, sizeof(video_probe_and_commit_control_t)), VIDEO_ERROR_UNKNOWN);
}
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_LEN:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(2 == request->wLength, VIDEO_ERROR_UNKNOWN);
uint16_t len = sizeof(video_probe_and_commit_control_t);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)&len, sizeof(len)), VIDEO_ERROR_UNKNOWN);
@@ -1001,8 +1137,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage,
return VIDEO_ERROR_NONE;
case VIDEO_REQUEST_GET_INFO:
- if (stage == CONTROL_STAGE_SETUP)
- {
+ if (stage == CONTROL_STAGE_SETUP) {
TU_VERIFY(1 == request->wLength, VIDEO_ERROR_UNKNOWN);
TU_VERIFY(tud_control_xfer(rhport, request, (uint8_t*)(uintptr_t) &_cap_get_set, sizeof(_cap_get_set)), VIDEO_ERROR_UNKNOWN);
}
@@ -1043,7 +1178,6 @@ static int handle_video_stm_req(uint8_t rhport, uint8_t stage,
default: return VIDEO_ERROR_INVALID_REQUEST;
}
- return VIDEO_ERROR_UNKNOWN;
}
//--------------------------------------------------------------------+
@@ -1064,19 +1198,33 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
if (!stm || !stm->desc.ep[0]) return false;
+ if (stm->state == VS_STATE_PROBING) return false;
+
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ uint8_t const *desc = _videod_itf[stm->index_vc].beg;
+ uint_fast16_t ofs_ep = stm->desc.ep[0];
+ tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
+ if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ if (stm->state == VS_STATE_COMMITTED) return false;
+ }
+#endif
+
return true;
}
-bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize)
-{
+bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize) {
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO);
TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING);
+
if (!buffer || !bufsize) return false;
videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[ctl_idx];
+
if (!stm || !stm->desc.ep[0] || stm->buffer) return false;
+ if (stm->state == VS_STATE_PROBING) return false;
/* Find EP address */
- void const *desc = _videod_itf[stm->index_vc].beg;
+ uint8_t const *desc = _videod_itf[stm->index_vc].beg;
uint8_t ep_addr = 0;
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
uint_fast16_t ofs_ep = stm->desc.ep[i];
@@ -1088,34 +1236,36 @@ bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *bu
TU_VERIFY( usbd_edpt_claim(0, ep_addr) );
/* update the packet header */
- tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm->ep_buf;
+ tusb_video_payload_header_t *hdr = (tusb_video_payload_header_t*)stm_epbuf->buf;
hdr->FrameID ^= 1;
hdr->EndOfFrame = 0;
/* update the packet data */
stm->buffer = (uint8_t*)buffer;
stm->bufsize = bufsize;
- uint_fast16_t pkt_len = _prepare_in_payload(stm);
- TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0);
+ uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
+ TU_ASSERT( usbd_edpt_xfer(0, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
return true;
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
-void videod_init(void)
-{
+void videod_init(void) {
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) {
videod_interface_t* ctl = &_videod_itf[i];
tu_memclr(ctl, sizeof(*ctl));
}
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[i];
- tu_memclr(stm, ITF_STM_MEM_RESET_SIZE);
+ tu_memclr(stm, sizeof(videod_streaming_interface_t));
}
}
-void videod_reset(uint8_t rhport)
-{
+bool videod_deinit(void) {
+ return true;
+}
+
+void videod_reset(uint8_t rhport) {
(void) rhport;
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) {
videod_interface_t* ctl = &_videod_itf[i];
@@ -1123,12 +1273,11 @@ void videod_reset(uint8_t rhport)
}
for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[i];
- tu_memclr(stm, ITF_STM_MEM_RESET_SIZE);
+ tu_memclr(stm, sizeof(videod_streaming_interface_t));
}
}
-uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
TU_VERIFY((TUSB_CLASS_VIDEO == itf_desc->bInterfaceClass) &&
(VIDEO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass) &&
(VIDEO_ITF_PROTOCOL_15 == itf_desc->bInterfaceProtocol), 0);
@@ -1143,13 +1292,15 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
}
TU_ASSERT(ctl_idx < CFG_TUD_VIDEO, 0);
- void const *end = (void const*)itf_desc + max_len;
- self->beg = itf_desc;
+ uint8_t const *end = (uint8_t const*)itf_desc + max_len;
+ self->beg = (uint8_t const*) itf_desc;
self->len = max_len;
+
/*------------- Video Control Interface -------------*/
TU_VERIFY(_open_vc_itf(rhport, self, 0), 0);
tusb_desc_vc_itf_t const *vc = _get_desc_vc(self);
uint_fast8_t bInCollection = vc->ctl.bInCollection;
+
/* Find the end of the video interface descriptor */
void const *cur = _next_desc_itf(itf_desc, end);
for (uint8_t stm_idx = 0; stm_idx < bInCollection; ++stm_idx) {
@@ -1167,6 +1318,34 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
stm->desc.beg = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc);
cur = _next_desc_itf(cur, end);
stm->desc.end = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc);
+ stm->state = VS_STATE_PROBING;
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ /* Allocate ISO endpoints */
+ uint16_t ep_size = 0;
+ uint8_t ep_addr = 0;
+ uint8_t const *p_desc = (uint8_t const*)itf_desc + stm->desc.beg;
+ uint8_t const *p_desc_end = (uint8_t const*)itf_desc + stm->desc.end;
+ while (p_desc < p_desc_end) {
+ if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
+ tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ ep_addr = desc_ep->bEndpointAddress;
+ ep_size = TU_MAX(tu_edpt_packet_size(desc_ep), ep_size);
+ }
+ }
+ p_desc = tu_desc_next(p_desc);
+ }
+ if(ep_addr > 0 && ep_size > 0) usbd_edpt_iso_alloc(rhport, ep_addr, ep_size);
+#endif
+ if (0 == stm_idx && 1 == bInCollection) {
+ /* If there is only one streaming interface and no alternate settings,
+ * host may not issue set_interface so open the streaming interface here. */
+ uint8_t const *sbeg = (uint8_t const*)itf_desc + stm->desc.beg;
+ uint8_t const *send = (uint8_t const*)itf_desc + stm->desc.end;
+ if (send == _find_desc_itf(sbeg, send, _desc_itfnum(sbeg), 1)) {
+ TU_VERIFY(_open_vs_itf(rhport, stm, 0), 0);
+ }
+ }
}
self->len = (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc);
return (uint16_t) ((uintptr_t)cur - (uintptr_t)itf_desc);
@@ -1175,12 +1354,10 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
// 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 videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
int err;
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
uint_fast8_t itfnum = tu_u16_low(request->wIndex);
-
/* Identify which control interface to use */
uint_fast8_t itf;
for (itf = 0; itf < CFG_TUD_VIDEO; ++itf) {
@@ -1190,6 +1367,7 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
}
if (itf < CFG_TUD_VIDEO) {
+ TU_LOG_DRV(" VC[%d]: ", itf);
err = handle_video_ctl_req(rhport, stage, request, itf);
_videod_itf[itf].error_code = (uint8_t)err;
if (err) return false;
@@ -1200,11 +1378,12 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) {
videod_streaming_interface_t *stm = &_videod_streaming_itf[itf];
if (!stm->desc.beg) continue;
- void const *desc = _videod_itf[stm->index_vc].beg;
+ uint8_t const *desc = _videod_itf[stm->index_vc].beg;
if (itfnum == _desc_itfnum(desc + stm->desc.beg)) break;
}
if (itf < CFG_TUD_VIDEO_STREAMING) {
+ TU_LOG_DRV(" VS[%d]: ", itf);
err = handle_video_stm_req(rhport, stage, request, itf);
_videod_streaming_itf[itf].error_code = (uint8_t)err;
if (err) return false;
@@ -1213,8 +1392,7 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_
return false;
}
-bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void)result; (void)xferred_bytes;
/* find streaming handle */
@@ -1226,16 +1404,17 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
uint_fast16_t const ep_ofs = stm->desc.ep[0];
if (!ep_ofs) continue;
ctl = &_videod_itf[stm->index_vc];
- void const *desc = ctl->beg;
+ uint8_t const *desc = ctl->beg;
if (ep_addr == _desc_ep_addr(desc + ep_ofs)) break;
}
-
TU_ASSERT(itf < CFG_TUD_VIDEO_STREAMING);
+ videod_streaming_epbuf_t *stm_epbuf = &_videod_streaming_epbuf[itf];
+
if (stm->offset < stm->bufsize) {
/* Claim the endpoint */
TU_VERIFY( usbd_edpt_claim(rhport, ep_addr), 0);
- uint_fast16_t pkt_len = _prepare_in_payload(stm);
- TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm->ep_buf, (uint16_t) pkt_len), 0);
+ uint_fast16_t pkt_len = _prepare_in_payload(stm, stm_epbuf->buf);
+ TU_ASSERT( usbd_edpt_xfer(rhport, ep_addr, stm_epbuf->buf, (uint16_t) pkt_len), 0);
} else {
stm->buffer = NULL;
stm->bufsize = 0;
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index ee2fcb9d5..648a221d5 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -40,6 +40,8 @@ extern "C" {
// CFG_TUD_VIDEO > 1
//--------------------------------------------------------------------+
+bool tud_video_n_connected(uint_fast8_t ctl_idx);
+
/** Return true if streaming
*
* @param[in] ctl_idx Destination control interface index
@@ -85,6 +87,7 @@ TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
void videod_init (void);
+bool videod_deinit (void);
void videod_reset (uint8_t rhport);
uint16_t videod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index b1ee40a1a..74cac965d 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -37,6 +37,7 @@
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
#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))
#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low)))
#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff))
@@ -53,6 +54,8 @@
#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32)
#define TU_BIT(n) (1UL << (n))
+
+// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111
#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
//--------------------------------------------------------------------+
@@ -62,6 +65,7 @@
// Standard Headers
#include <stdbool.h>
#include <stdint.h>
+#include <inttypes.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
@@ -73,7 +77,20 @@
#include "tusb_types.h"
#include "tusb_debug.h"
-#include "tusb_timeout.h" // TODO remove
+//--------------------------------------------------------------------+
+// Optional API implemented by application if needed
+// TODO move to a more obvious place/file
+//--------------------------------------------------------------------+
+
+// flush data cache
+TU_ATTR_WEAK 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);
+
+// 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);
//--------------------------------------------------------------------+
// Internal Inline Functions
@@ -83,15 +100,38 @@
#define tu_memclr(buffer, size) memset((buffer), 0, (size))
#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
+// This is a backport of memset_s from c11
+TU_ATTR_ALWAYS_INLINE static inline int tu_memset_s(void *dest, size_t destsz, int ch, size_t count) {
+ // TODO may check if desst and src is not NULL
+ if ( count > destsz ) {
+ return -1;
+ }
+ memset(dest, ch, count);
+ return 0;
+}
+
+// This is a backport of memcpy_s from c11
+TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count) {
+ // TODO may check if desst and src is not NULL
+ if ( count > destsz ) {
+ return -1;
+ }
+ memcpy(dest, src, count);
+ return 0;
+}
+
+
//------------- Bytes -------------//
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0)
-{
- return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0;
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) {
+ return (((uint32_t)b3) << 24) | (((uint32_t)b2) << 16) | (((uint32_t)b1) << 8) | b0;
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low)
-{
- return (uint16_t) ((((uint16_t) high) << 8) | low);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32_from_u16(uint16_t high, uint16_t low) {
+ return (((uint32_t)high) << 16) | low;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) {
+ return (uint16_t)((((uint16_t)high) << 8) | low);
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); }
@@ -121,18 +161,23 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) {
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
//------------- Align -------------//
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) {
return value & ((uint32_t) ~(alignment-1));
}
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
+TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { return (value & 0x1FUL) == 0; }
+TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; }
+
//------------- Mathematics -------------//
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return TU_DIV_CEIL(v, d); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_round_up(uint32_t v, uint32_t f) { return tu_div_ceil(v, f) * f; }
// log2 of a value is its MSB's position
// TODO use clz TODO remove
@@ -222,11 +267,21 @@ TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_
#else
// MCU that could access unaligned memory natively
-TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32 (const void* mem) { return *((uint32_t const *) mem); }
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16 (const void* mem) { return *((uint16_t const *) mem); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) {
+ return *((uint32_t const *) mem);
+}
-TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32 (void* mem, uint32_t value ) { *((uint32_t*) mem) = value; }
-TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16 (void* mem, uint16_t value ) { *((uint16_t*) mem) = value; }
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) {
+ return *((uint16_t const *) mem);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) {
+ *((uint32_t *) mem) = value;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) {
+ *((uint16_t *) mem) = value;
+}
#endif
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 2c30daf6f..9b33a6f61 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -56,11 +56,18 @@
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TU_VERIFY_STATIC _Static_assert
#elif defined(__CCRX__)
- #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0];
+ #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
#else
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
#endif
+/* --------------------- Fuzzing types -------------------------------------- */
+#ifdef _FUZZ
+ #define tu_static static __thread
+#else
+ #define tu_static static
+#endif
+
// for declaration of reserved field, make use of _TU_COUNTER_
#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
@@ -76,9 +83,9 @@
* - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
*------------------------------------------------------------------*/
#if !defined(__CCRX__)
-#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__,_RSEQ_N())
+#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N())
#else
-#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__,_RSEQ_N())
+#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N())
#endif
#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__)
@@ -112,6 +119,14 @@
#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8)
//--------------------------------------------------------------------+
+// Macro for function default arguments
+//--------------------------------------------------------------------+
+#define TU_GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
+
+// function expand with number of arguments
+#define TU_FUNC_OPTIONAL_ARG(func, ...) TU_XSTRCAT(func##_arg, TU_ARGS_NUM(__VA_ARGS__))(__VA_ARGS__)
+
+//--------------------------------------------------------------------+
// Compiler porting with Attribute and Endian
//--------------------------------------------------------------------+
@@ -121,7 +136,10 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
- #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
+ #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
+ #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
+ #endif
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
@@ -131,10 +149,14 @@
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
#define TU_ATTR_BIT_FIELD_ORDER_END
- #if __has_attribute(__fallthrough__)
- #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
- #else
+ #if __GNUC__ < 5
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
+ #else
+ #if __has_attribute(__fallthrough__)
+ #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
+ #else
+ #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
+ #endif
#endif
// Endian conversion use well-known host to network (big endian) naming
@@ -144,8 +166,17 @@
#define TU_BYTE_ORDER TU_BIG_ENDIAN
#endif
- #define TU_BSWAP16(u16) (__builtin_bswap16(u16))
- #define TU_BSWAP32(u32) (__builtin_bswap32(u32))
+ // Unfortunately XC16 doesn't provide builtins for 32bit endian conversion
+ #if defined(__XC16)
+ #define TU_BSWAP16(u16) (__builtin_swap(u16))
+ #define TU_BSWAP32(u32) ((((u32) & 0xff000000) >> 24) | \
+ (((u32) & 0x00ff0000) >> 8) | \
+ (((u32) & 0x0000ff00) << 8) | \
+ (((u32) & 0x000000ff) << 24))
+ #else
+ #define TU_BSWAP16(u16) (__builtin_bswap16(u16))
+ #define TU_BSWAP32(u32) (__builtin_bswap32(u32))
+ #endif
#ifndef __ARMCC_VERSION
// List of obsolete callback function that is renamed and should not be defined.
@@ -158,6 +189,7 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
@@ -185,11 +217,14 @@
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
#define TU_ATTR_WEAK __attribute__ ((weak))
- #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
+ // #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f)))
+ #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
+ #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
+ #endif
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
- #define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
+ #define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
#define TU_ATTR_PACKED_BEGIN
#define TU_ATTR_PACKED_END
@@ -211,6 +246,7 @@
#define TU_ATTR_SECTION(sec_name)
#define TU_ATTR_PACKED
#define TU_ATTR_WEAK
+ // #define TU_ATTR_WEAK_ALIAS(f)
#define TU_ATTR_ALWAYS_INLINE
#define TU_ATTR_DEPRECATED(mess)
#define TU_ATTR_UNUSED
@@ -232,7 +268,7 @@
#define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16))
#define TU_BSWAP32(u32) (_builtin_revl(u32))
-#else
+#else
#error "Compiler attribute porting is required"
#endif
diff --git a/src/common/tusb_debug.h b/src/common/tusb_debug.h
index ac5bee6ec..2e9f1d9cd 100644
--- a/src/common/tusb_debug.h
+++ b/src/common/tusb_debug.h
@@ -43,9 +43,10 @@
#if CFG_TUSB_DEBUG
// Enum to String for debugging purposes
-#if CFG_TUSB_DEBUG >= 2
+#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
extern char const* const tu_str_speed[];
extern char const* const tu_str_std_request[];
+extern char const* const tu_str_xfer_result[];
#endif
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
@@ -57,16 +58,15 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
#define tu_printf printf
#endif
-static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize)
-{
+static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) {
for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
+ tu_printf("\r\n");
}
// Log with Level
#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
-#define TU_LOG_ARR(n, ...) TU_XSTRCAT3(TU_LOG, n, _ARR)(__VA_ARGS__)
-#define TU_LOG_VAR(n, ...) TU_XSTRCAT3(TU_LOG, n, _VAR)(__VA_ARGS__)
+#define TU_LOG_BUF(n, ...) TU_XSTRCAT3(TU_LOG, n, _BUF)(__VA_ARGS__)
#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
#define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
@@ -75,17 +75,15 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize)
// Log Level 1: Error
#define TU_LOG1 tu_printf
#define TU_LOG1_MEM tu_print_mem
-#define TU_LOG1_ARR(_x, _n) tu_print_arr((uint8_t const*)(_x), _n)
-#define TU_LOG1_VAR(_x) tu_print_arr((uint8_t const*)(_x), sizeof(*(_x)))
+#define TU_LOG1_BUF(_x, _n) tu_print_buf((uint8_t const*)(_x), _n)
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
-#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (unsigned long) (_x) )
+#define TU_LOG1_HEX(_x) tu_printf(#_x " = 0x%lX\r\n", (unsigned long) (_x) )
// Log Level 2: Warn
#if CFG_TUSB_DEBUG >= 2
#define TU_LOG2 TU_LOG1
#define TU_LOG2_MEM TU_LOG1_MEM
- #define TU_LOG2_ARR TU_LOG1_ARR
- #define TU_LOG2_VAR TU_LOG1_VAR
+ #define TU_LOG2_BUF TU_LOG1_BUF
#define TU_LOG2_INT TU_LOG1_INT
#define TU_LOG2_HEX TU_LOG1_HEX
#endif
@@ -94,30 +92,25 @@ static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize)
#if CFG_TUSB_DEBUG >= 3
#define TU_LOG3 TU_LOG1
#define TU_LOG3_MEM TU_LOG1_MEM
- #define TU_LOG3_ARR TU_LOG1_ARR
- #define TU_LOG3_VAR TU_LOG1_VAR
+ #define TU_LOG3_BUF TU_LOG1_BUF
#define TU_LOG3_INT TU_LOG1_INT
#define TU_LOG3_HEX TU_LOG1_HEX
#endif
-typedef struct
-{
+typedef struct {
uint32_t key;
const char* data;
} tu_lookup_entry_t;
-typedef struct
-{
+typedef struct {
uint16_t count;
tu_lookup_entry_t const* items;
} tu_lookup_table_t;
-static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key)
-{
- static char not_found[11];
+static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) {
+ tu_static char not_found[11];
- for(uint16_t i=0; i<p_table->count; i++)
- {
+ for(uint16_t i=0; i<p_table->count; i++) {
if (p_table->items[i].key == key) return p_table->items[i].data;
}
@@ -132,7 +125,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
#ifndef TU_LOG
#define TU_LOG(n, ...)
#define TU_LOG_MEM(n, ...)
- #define TU_LOG_VAR(n, ...)
+ #define TU_LOG_BUF(n, ...)
#define TU_LOG_INT(n, ...)
#define TU_LOG_HEX(n, ...)
#define TU_LOG_LOCATION()
@@ -143,14 +136,14 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
#define TU_LOG0(...)
#define TU_LOG0_MEM(...)
-#define TU_LOG0_VAR(...)
+#define TU_LOG0_BUF(...)
#define TU_LOG0_INT(...)
#define TU_LOG0_HEX(...)
#ifndef TU_LOG1
#define TU_LOG1(...)
#define TU_LOG1_MEM(...)
- #define TU_LOG1_VAR(...)
+ #define TU_LOG1_BUF(...)
#define TU_LOG1_INT(...)
#define TU_LOG1_HEX(...)
#endif
@@ -158,7 +151,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
#ifndef TU_LOG2
#define TU_LOG2(...)
#define TU_LOG2_MEM(...)
- #define TU_LOG2_VAR(...)
+ #define TU_LOG2_BUF(...)
#define TU_LOG2_INT(...)
#define TU_LOG2_HEX(...)
#endif
@@ -166,7 +159,7 @@ static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint3
#ifndef TU_LOG3
#define TU_LOG3(...)
#define TU_LOG3_MEM(...)
- #define TU_LOG3_VAR(...)
+ #define TU_LOG3_BUF(...)
#define TU_LOG3_INT(...)
#define TU_LOG3_HEX(...)
#endif
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 895b9208b..5f2dcabad 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -28,21 +28,22 @@
#include "osal/osal.h"
#include "tusb_fifo.h"
-// Supress IAR warning
+#define TU_FIFO_DBG 0
+
+// Suppress IAR warning
// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
#if defined(__ICCARM__)
#pragma diag_suppress = Pa082
#endif
-// implement mutex lock and unlock
-#if CFG_FIFO_MUTEX
+#if OSAL_MUTEX_REQUIRED
-static inline void _ff_lock(tu_fifo_mutex_t mutex)
+TU_ATTR_ALWAYS_INLINE static inline void _ff_lock(osal_mutex_t mutex)
{
if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
-static inline void _ff_unlock(tu_fifo_mutex_t mutex)
+TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
{
if (mutex) osal_mutex_unlock(mutex);
}
@@ -61,28 +62,27 @@ static inline void _ff_unlock(tu_fifo_mutex_t mutex)
typedef enum
{
TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
+#ifdef TUP_MEM_CONST_ADDR
TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
+#endif
} tu_fifo_copy_mode_t;
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
{
- if (depth > 0x8000) return false; // Maximum depth is 2^15 items
+ // Limit index space to 2*depth - this allows for a fast "modulo" calculation
+ // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
+ // only if overflow happens once (important for unsupervised DMA applications)
+ if (depth > 0x8000) return false;
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
- f->buffer = (uint8_t*) buffer;
- f->depth = depth;
- f->item_size = item_size;
+ f->buffer = (uint8_t*) buffer;
+ f->depth = depth;
+ f->item_size = (uint16_t) (item_size & 0x7FFF);
f->overwritable = overwritable;
-
- // Limit index space to 2*depth - this allows for a fast "modulo" calculation
- // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
- // only if overflow happens once (important for unsupervised DMA applications)
- f->max_pointer_idx = (uint16_t) (2*depth - 1);
- f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
-
- f->rd_idx = f->wr_idx = 0;
+ f->rd_idx = 0;
+ f->wr_idx = 0;
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
@@ -90,25 +90,23 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
return true;
}
-// Static functions are intended to work on local variables
-static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
-{
- while ( idx >= depth) idx -= depth;
- return idx;
-}
+//--------------------------------------------------------------------+
+// Pull & Push
+//--------------------------------------------------------------------+
+#ifdef TUP_MEM_CONST_ADDR
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
-// Code adapted from dcd_synopsis.c
+// Code adapted from dcd_synopsys.c
// TODO generalize with configurable 1 byte or 4 byte each read
static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
{
- volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
+ volatile const uint32_t * reg_rx = (volatile const uint32_t *) app_buf;
// Reading full available 32 bit words from const app address
uint16_t full_words = len >> 2;
while(full_words--)
{
- tu_unaligned_write32(ff_buf, *rx_fifo);
+ tu_unaligned_write32(ff_buf, *reg_rx);
ff_buf += 4;
}
@@ -116,7 +114,7 @@ static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t
uint8_t const bytes_rem = len & 0x03;
if ( bytes_rem )
{
- uint32_t tmp32 = *rx_fifo;
+ uint32_t tmp32 = *reg_rx;
memcpy(ff_buf, &tmp32, bytes_rem);
}
}
@@ -125,49 +123,50 @@ static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t
// where all data is written to a constant address in full word copies
static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
{
- volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
+ volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf;
- // Pushing full available 32 bit words to const app address
+ // Write full available 32 bit words to const address
uint16_t full_words = len >> 2;
while(full_words--)
{
- *tx_fifo = tu_unaligned_read32(ff_buf);
+ *reg_tx = tu_unaligned_read32(ff_buf);
ff_buf += 4;
}
- // Write the remaining 1-3 bytes into const app address
+ // Write the remaining 1-3 bytes into const address
uint8_t const bytes_rem = len & 0x03;
if ( bytes_rem )
{
uint32_t tmp32 = 0;
memcpy(&tmp32, ff_buf, bytes_rem);
- *tx_fifo = tmp32;
+ *reg_tx = tmp32;
}
}
+#endif
-// send one item to FIFO WITHOUT updating write pointer
+// send one item to fifo WITHOUT updating write pointer
static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
{
memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
}
-// send n items to FIFO WITHOUT updating write pointer
-static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
+// send n items to fifo WITHOUT updating write pointer
+static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t wr_ptr, tu_fifo_copy_mode_t copy_mode)
{
- uint16_t const nLin = f->depth - rel;
- uint16_t const nWrap = n - nLin;
+ uint16_t const lin_count = f->depth - wr_ptr;
+ uint16_t const wrap_count = n - lin_count;
- uint16_t nLin_bytes = nLin * f->item_size;
- uint16_t nWrap_bytes = nWrap * f->item_size;
+ uint16_t lin_bytes = lin_count * f->item_size;
+ uint16_t wrap_bytes = wrap_count * f->item_size;
// current buffer of fifo
- uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+ uint8_t* ff_buf = f->buffer + (wr_ptr * f->item_size);
switch (copy_mode)
{
case TU_FIFO_COPY_INC:
- if(n <= nLin)
+ if(n <= lin_count)
{
// Linear only
memcpy(ff_buf, app_buf, n*f->item_size);
@@ -177,16 +176,17 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
// Wrap around
// Write data to linear part of buffer
- memcpy(ff_buf, app_buf, nLin_bytes);
+ memcpy(ff_buf, app_buf, lin_bytes);
// Write data wrapped around
- memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
+ // TU_ASSERT(nWrap_bytes <= f->depth, );
+ memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes);
}
break;
-
+#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
// Intended for hardware buffers from which it can be read word by word only
- if(n <= nLin)
+ if(n <= lin_count)
{
// Linear only
_ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
@@ -196,17 +196,18 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
// Wrap around case
// Write full words to linear part of buffer
- uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
+ uint16_t nLin_4n_bytes = lin_bytes & 0xFFFC;
_ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
ff_buf += nLin_4n_bytes;
// There could be odd 1-3 bytes before the wrap-around boundary
- volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
- uint8_t rem = nLin_bytes & 0x03;
+ uint8_t rem = lin_bytes & 0x03;
if (rem > 0)
{
- uint8_t remrem = (uint8_t) tu_min16(nWrap_bytes, 4-rem);
- nWrap_bytes -= remrem;
+ volatile const uint32_t * rx_fifo = (volatile const uint32_t *) app_buf;
+
+ uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem);
+ wrap_bytes -= remrem;
uint32_t tmp32 = *rx_fifo;
uint8_t * src_u8 = ((uint8_t *) &tmp32);
@@ -224,34 +225,36 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
}
// Write data wrapped part
- if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
+ if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
}
break;
+#endif
+ default: break;
}
}
-// get one item from FIFO WITHOUT updating read pointer
+// get one item from fifo WITHOUT updating read pointer
static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
{
memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
}
-// get n items from FIFO WITHOUT updating read pointer
-static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
+// get n items from fifo WITHOUT updating read pointer
+static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr, tu_fifo_copy_mode_t copy_mode)
{
- uint16_t const nLin = f->depth - rel;
- uint16_t const nWrap = n - nLin; // only used if wrapped
+ uint16_t const lin_count = f->depth - rd_ptr;
+ uint16_t const wrap_count = n - lin_count; // only used if wrapped
- uint16_t nLin_bytes = nLin * f->item_size;
- uint16_t nWrap_bytes = nWrap * f->item_size;
+ uint16_t lin_bytes = lin_count * f->item_size;
+ uint16_t wrap_bytes = wrap_count * f->item_size;
// current buffer of fifo
- uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+ uint8_t* ff_buf = f->buffer + (rd_ptr * f->item_size);
switch (copy_mode)
{
case TU_FIFO_COPY_INC:
- if ( n <= nLin )
+ if ( n <= lin_count )
{
// Linear only
memcpy(app_buf, ff_buf, n*f->item_size);
@@ -261,15 +264,15 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu
// Wrap around
// Read data from linear part of buffer
- memcpy(app_buf, ff_buf, nLin_bytes);
+ memcpy(app_buf, ff_buf, lin_bytes);
// Read data wrapped part
- memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
+ memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes);
}
break;
-
+#ifdef TUP_MEM_CONST_ADDR
case TU_FIFO_COPY_CST_FULL_WORDS:
- if ( n <= nLin )
+ if ( n <= lin_count )
{
// Linear only
_ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
@@ -279,17 +282,18 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu
// Wrap around case
// Read full words from linear part of buffer
- uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
- _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
- ff_buf += nLin_4n_bytes;
+ uint16_t lin_4n_bytes = lin_bytes & 0xFFFC;
+ _ff_pull_const_addr(app_buf, ff_buf, lin_4n_bytes);
+ ff_buf += lin_4n_bytes;
// There could be odd 1-3 bytes before the wrap-around boundary
- volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
- uint8_t rem = nLin_bytes & 0x03;
+ uint8_t rem = lin_bytes & 0x03;
if (rem > 0)
{
- uint8_t remrem = (uint8_t) tu_min16(nWrap_bytes, 4-rem);
- nWrap_bytes -= remrem;
+ volatile uint32_t * reg_tx = (volatile uint32_t *) app_buf;
+
+ uint8_t remrem = (uint8_t) tu_min16(wrap_bytes, 4-rem);
+ wrap_bytes -= remrem;
uint32_t tmp32=0;
uint8_t * dst_u8 = (uint8_t *)&tmp32;
@@ -301,7 +305,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu
ff_buf = f->buffer;
while(remrem--) *dst_u8++ = *ff_buf++;
- *tx_fifo = tmp32;
+ *reg_tx = tmp32;
}
else
{
@@ -309,186 +313,240 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu
}
// Read data wrapped part
- if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
+ if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
}
break;
-
+#endif
default: break;
}
}
-// Advance an absolute pointer
-static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+//--------------------------------------------------------------------+
+// Helper
+//--------------------------------------------------------------------+
+
+// return only the index difference and as such can be used to determine an overflow i.e overflowable count
+TU_ATTR_ALWAYS_INLINE static inline
+uint16_t _ff_count(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx)
{
- // We limit the index space of p such that a correct wrap around happens
- // Check for a wrap around or if we are in unused index space - This has to be checked first!!
- // We are exploiting the wrap around to the correct index
- if ((p > (uint16_t)(p + offset)) || ((uint16_t)(p + offset) > f->max_pointer_idx))
+ // In case we have non-power of two depth we need a further modification
+ if (wr_idx >= rd_idx)
{
- p = (uint16_t) ((p + offset) + f->non_used_index_space);
- }
- else
+ return (uint16_t) (wr_idx - rd_idx);
+ } else
{
- p += offset;
+ return (uint16_t) (2*depth - (rd_idx - wr_idx));
}
- return p;
}
-// Backward an absolute pointer
-static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+// return remaining slot in fifo
+TU_ATTR_ALWAYS_INLINE static inline
+uint16_t _ff_remaining(uint16_t depth, uint16_t wr_idx, uint16_t rd_idx)
+{
+ uint16_t const count = _ff_count(depth, wr_idx, rd_idx);
+ return (depth > count) ? (depth - count) : 0;
+}
+
+//--------------------------------------------------------------------+
+// Index Helper
+//--------------------------------------------------------------------+
+
+// Advance an absolute index
+// "absolute" index is only in the range of [0..2*depth)
+static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset)
{
// We limit the index space of p such that a correct wrap around happens
// Check for a wrap around or if we are in unused index space - This has to be checked first!!
// We are exploiting the wrap around to the correct index
- if ((p < (uint16_t)(p - offset)) || ((uint16_t)(p - offset) > f->max_pointer_idx))
- {
- p = (uint16_t) ((p - offset) - f->non_used_index_space);
- }
- else
+ uint16_t new_idx = (uint16_t) (idx + offset);
+ if ( (idx > new_idx) || (new_idx >= 2*depth) )
{
- p -= offset;
+ uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1));
+ new_idx = (uint16_t) (new_idx + non_used_index_space);
}
- return p;
-}
-// get relative from absolute pointer
-static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p)
-{
- return _ff_mod(p, f->depth);
+ return new_idx;
}
-// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
-static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+#if 0 // not used but
+// Backward an absolute index
+static uint16_t backward_index(uint16_t depth, uint16_t idx, uint16_t offset)
{
- uint16_t cnt = wAbs-rAbs;
-
- // In case we have non-power of two depth we need a further modification
- if (rAbs > wAbs) cnt -= f->non_used_index_space;
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!!
+ // We are exploiting the wrap around to the correct index
+ uint16_t new_idx = (uint16_t) (idx - offset);
+ if ( (idx < new_idx) || (new_idx >= 2*depth) )
+ {
+ uint16_t const non_used_index_space = (uint16_t) (UINT16_MAX - (2*depth-1));
+ new_idx = (uint16_t) (new_idx - non_used_index_space);
+ }
- return cnt;
+ return new_idx;
}
+#endif
-// Works on local copies of w and r
-static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
+// index to pointer, simply an modulo with minus.
+TU_ATTR_ALWAYS_INLINE static inline
+uint16_t idx2ptr(uint16_t depth, uint16_t idx)
{
- return wAbs == rAbs;
+ // Only run at most 3 times since index is limit in the range of [0..2*depth)
+ while ( idx >= depth ) idx -= depth;
+ return idx;
}
-// Works on local copies of w and r
-static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+// Works on local copies of w
+// When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms
+// an full fifo i.e _ff_count() = depth
+TU_ATTR_ALWAYS_INLINE static inline
+uint16_t _ff_correct_read_index(tu_fifo_t* f, uint16_t wr_idx)
{
- return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
-}
+ uint16_t rd_idx;
+ if ( wr_idx >= f->depth )
+ {
+ rd_idx = wr_idx - f->depth;
+ }else
+ {
+ rd_idx = wr_idx + f->depth;
+ }
-// Works on local copies of w and r
-// BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
-// Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
-// write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
-// write pointer wraps and you pointer states are messed up. This can only happen if you
-// use DMAs, write functions do not allow such an error.
-static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
-{
- return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
-}
+ f->rd_idx = rd_idx;
-// Works on local copies of w
-// For more details see _tu_fifo_overflow()!
-static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
-{
- f->rd_idx = backward_pointer(f, wAbs, f->depth);
+ return rd_idx;
}
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
-static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
+static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wr_idx, uint16_t rd_idx)
{
- uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+ uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
+
+ // nothing to peek
+ if ( cnt == 0 ) return false;
// Check overflow and correct if required
- if (cnt > f->depth)
+ if ( cnt > f->depth )
{
- _tu_fifo_correct_read_pointer(f, wAbs);
+ rd_idx = _ff_correct_read_index(f, wr_idx);
cnt = f->depth;
}
- // Skip beginning of buffer
- if (cnt == 0) return false;
-
- uint16_t rRel = get_relative_pointer(f, rAbs);
+ uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
// Peek data
- _ff_pull(f, p_buffer, rRel);
+ _ff_pull(f, p_buffer, rd_ptr);
return true;
}
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
-static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
+static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, tu_fifo_copy_mode_t copy_mode)
{
- uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+ uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
+
+ // nothing to peek
+ if ( cnt == 0 ) return 0;
// Check overflow and correct if required
- if (cnt > f->depth)
+ if ( cnt > f->depth )
{
- _tu_fifo_correct_read_pointer(f, wAbs);
- rAbs = f->rd_idx;
+ rd_idx = _ff_correct_read_index(f, wr_idx);
cnt = f->depth;
}
- // Skip beginning of buffer
- if (cnt == 0) return 0;
-
// Check if we can read something at and after offset - if too less is available we read what remains
- if (cnt < n) n = cnt;
+ if ( cnt < n ) n = cnt;
- uint16_t rRel = get_relative_pointer(f, rAbs);
+ uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
// Peek data
- _ff_pull_n(f, p_buffer, n, rRel, copy_mode);
+ _ff_pull_n(f, p_buffer, n, rd_ptr, copy_mode);
return n;
}
-// Works on local copies of w and r
-static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
-{
- return f->depth - _tu_fifo_count(f, wAbs, rAbs);
-}
-
static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
{
if ( n == 0 ) return 0;
_ff_lock(f->mutex_wr);
- uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint16_t wr_idx = f->wr_idx;
+ uint16_t rd_idx = f->rd_idx;
+
uint8_t const* buf8 = (uint8_t const*) data;
- if (!f->overwritable)
+ TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ",
+ rd_idx, wr_idx, _ff_count(f->depth, wr_idx, rd_idx), _ff_remaining(f->depth, wr_idx, rd_idx), n);
+
+ if ( !f->overwritable )
{
- // Not overwritable limit up to full
- n = tu_min16(n, _tu_fifo_remaining(f, w, r));
+ // limit up to full
+ uint16_t const remain = _ff_remaining(f->depth, wr_idx, rd_idx);
+ n = tu_min16(n, remain);
}
- else if (n >= f->depth)
+ else
{
- // Only copy last part
- buf8 = buf8 + (n - f->depth) * f->item_size;
- n = f->depth;
+ // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case,
+ // oldest data in fifo i.e at read pointer data will be overwritten
+ // Note: we can modify read buffer contents but we must not modify the read index itself within a write function!
+ // Since it would end up in a race condition with read functions!
+ if ( n >= f->depth )
+ {
+ // Only copy last part
+ if ( copy_mode == TU_FIFO_COPY_INC )
+ {
+ buf8 += (n - f->depth) * f->item_size;
+ }else
+ {
+ // TODO should read from hw fifo to discard data, however reading an odd number could
+ // accidentally discard data.
+ }
+
+ n = f->depth;
- // We start writing at the read pointer's position since we fill the complete
- // buffer and we do not want to modify the read pointer within a write function!
- // This would end up in a race condition with read functions!
- w = r;
+ // We start writing at the read pointer's position since we fill the whole buffer
+ wr_idx = rd_idx;
+ }
+ else
+ {
+ uint16_t const overflowable_count = _ff_count(f->depth, wr_idx, rd_idx);
+ if (overflowable_count + n >= 2*f->depth)
+ {
+ // Double overflowed
+ // Index is bigger than the allowed range [0,2*depth)
+ // re-position write index to have a full fifo after pushed
+ wr_idx = advance_index(f->depth, rd_idx, f->depth - n);
+
+ // TODO we should also shift out n bytes from read index since we avoid changing rd index !!
+ // However memmove() is expensive due to actual copying + wrapping consideration.
+ // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory
+ // currently deliberately not implemented --> result in incorrect data read back
+ }else
+ {
+ // normal + single overflowed:
+ // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read()
+ // Therefore we just increase write index
+ // we will correct (re-position) read index later on in fifo_read() function
+ }
+ }
}
- uint16_t wRel = get_relative_pointer(f, w);
+ if (n)
+ {
+ uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
- // Write data
- _ff_push_n(f, buf8, n, wRel, copy_mode);
+ TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr);
- // Advance pointer
- f->wr_idx = advance_pointer(f, w, n);
+ // Write data
+ _ff_push_n(f, buf8, n, wr_ptr, copy_mode);
+
+ // Advance index
+ f->wr_idx = advance_index(f->depth, wr_idx, n);
+
+ TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx);
+ }
_ff_unlock(f->mutex_wr);
@@ -504,12 +562,16 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo
n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode);
// Advance read pointer
- f->rd_idx = advance_pointer(f, f->rd_idx, n);
+ f->rd_idx = advance_index(f->depth, f->rd_idx, n);
_ff_unlock(f->mutex_rd);
return n;
}
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
/******************************************************************************/
/*!
@brief Get number of items in FIFO.
@@ -527,7 +589,7 @@ static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo
/******************************************************************************/
uint16_t tu_fifo_count(tu_fifo_t* f)
{
- return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
+ return tu_min16(_ff_count(f->depth, f->wr_idx, f->rd_idx), f->depth);
}
/******************************************************************************/
@@ -545,7 +607,7 @@ uint16_t tu_fifo_count(tu_fifo_t* f)
/******************************************************************************/
bool tu_fifo_empty(tu_fifo_t* f)
{
- return _tu_fifo_empty(f->wr_idx, f->rd_idx);
+ return f->wr_idx == f->rd_idx;
}
/******************************************************************************/
@@ -563,7 +625,7 @@ bool tu_fifo_empty(tu_fifo_t* f)
/******************************************************************************/
bool tu_fifo_full(tu_fifo_t* f)
{
- return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
+ return _ff_count(f->depth, f->wr_idx, f->rd_idx) >= f->depth;
}
/******************************************************************************/
@@ -581,7 +643,7 @@ bool tu_fifo_full(tu_fifo_t* f)
/******************************************************************************/
uint16_t tu_fifo_remaining(tu_fifo_t* f)
{
- return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx);
+ return _ff_remaining(f->depth, f->wr_idx, f->rd_idx);
}
/******************************************************************************/
@@ -607,14 +669,14 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f)
/******************************************************************************/
bool tu_fifo_overflowed(tu_fifo_t* f)
{
- return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
+ return _ff_count(f->depth, f->wr_idx, f->rd_idx) > f->depth;
}
// Only use in case tu_fifo_overflow() returned true!
void tu_fifo_correct_read_pointer(tu_fifo_t* f)
{
_ff_lock(f->mutex_rd);
- _tu_fifo_correct_read_pointer(f, f->wr_idx);
+ _ff_correct_read_index(f, f->wr_idx);
_ff_unlock(f->mutex_rd);
}
@@ -643,7 +705,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx);
// Advance pointer
- f->rd_idx = advance_pointer(f, f->rd_idx, ret);
+ f->rd_idx = advance_index(f->depth, f->rd_idx, ret);
_ff_unlock(f->mutex_rd);
return ret;
@@ -670,10 +732,29 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
}
+#ifdef TUP_MEM_CONST_ADDR
+/******************************************************************************/
+/*!
+ @brief This function will read n elements from the array index specified by
+ the read pointer and increment the read index.
+ This function checks for an overflow and corrects read pointer if required.
+ The dest address will not be incremented which is useful for writing to registers.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] buffer
+ The pointer to data location
+ @param[in] n
+ Number of element that buffer can afford
+
+ @returns number of items read from the FIFO
+ */
+/******************************************************************************/
uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
{
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
+#endif
/******************************************************************************/
/*!
@@ -682,8 +763,6 @@ uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint1
@param[in] f
Pointer to the FIFO buffer to manipulate
- @param[in] offset
- Position to read from in the FIFO buffer with respect to read pointer
@param[in] p_buffer
Pointer to the place holder for data read from the buffer
@@ -742,20 +821,20 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
_ff_lock(f->mutex_wr);
bool ret;
- uint16_t const w = f->wr_idx;
+ uint16_t const wr_idx = f->wr_idx;
- if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable )
+ if ( tu_fifo_full(f) && !f->overwritable )
{
ret = false;
}else
{
- uint16_t wRel = get_relative_pointer(f, w);
+ uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
// Write data
- _ff_push(f, data, wRel);
+ _ff_push(f, data, wr_ptr);
// Advance pointer
- f->wr_idx = advance_pointer(f, w, 1);
+ f->wr_idx = advance_index(f->depth, wr_idx, 1);
ret = true;
}
@@ -784,6 +863,7 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
}
+#ifdef TUP_MEM_CONST_ADDR
/******************************************************************************/
/*!
@brief This function will write n elements into the array index specified by
@@ -803,6 +883,7 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data,
{
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
+#endif
/******************************************************************************/
/*!
@@ -817,9 +898,8 @@ bool tu_fifo_clear(tu_fifo_t *f)
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
- f->rd_idx = f->wr_idx = 0;
- f->max_pointer_idx = (uint16_t) (2*f->depth-1);
- f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
+ f->rd_idx = 0;
+ f->wr_idx = 0;
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
@@ -857,7 +937,7 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
As long as the DMA is the only process writing into the FIFO this is safe
to use.
- USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+ USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE!
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -867,7 +947,7 @@ bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
/******************************************************************************/
void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
{
- f->wr_idx = advance_pointer(f, f->wr_idx, n);
+ f->wr_idx = advance_index(f->depth, f->wr_idx, n);
}
/******************************************************************************/
@@ -878,7 +958,7 @@ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
FIFO. As long as the DMA is the only process reading from the FIFO this is
safe to use.
- USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+ USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE!
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -888,7 +968,7 @@ void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
/******************************************************************************/
void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
{
- f->rd_idx = advance_pointer(f, f->rd_idx, n);
+ f->rd_idx = advance_index(f->depth, f->rd_idx, n);
}
/******************************************************************************/
@@ -909,17 +989,18 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
{
// Operate on temporary values in case they change in between
- uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint16_t wr_idx = f->wr_idx;
+ uint16_t rd_idx = f->rd_idx;
- uint16_t cnt = _tu_fifo_count(f, w, r);
+ uint16_t cnt = _ff_count(f->depth, wr_idx, rd_idx);
// Check overflow and correct if required - may happen in case a DMA wrote too fast
if (cnt > f->depth)
{
_ff_lock(f->mutex_rd);
- _tu_fifo_correct_read_pointer(f, w);
+ rd_idx = _ff_correct_read_index(f, wr_idx);
_ff_unlock(f->mutex_rd);
- r = f->rd_idx;
+
cnt = f->depth;
}
@@ -934,22 +1015,25 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
}
// Get relative pointers
- w = get_relative_pointer(f, w);
- r = get_relative_pointer(f, r);
+ uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
+ uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
// Copy pointer to buffer to start reading from
- info->ptr_lin = &f->buffer[r];
+ info->ptr_lin = &f->buffer[rd_ptr];
// Check if there is a wrap around necessary
- if (w > r) {
+ if (wr_ptr > rd_ptr)
+ {
// Non wrapping case
info->len_lin = cnt;
+
info->len_wrap = 0;
info->ptr_wrap = NULL;
}
else
{
- info->len_lin = f->depth - r; // Also the case if FIFO was full
+ info->len_lin = f->depth - rd_ptr; // Also the case if FIFO was full
+
info->len_wrap = cnt - info->len_lin;
info->ptr_wrap = f->buffer;
}
@@ -972,36 +1056,37 @@ void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
/******************************************************************************/
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
{
- uint16_t w = f->wr_idx, r = f->rd_idx;
- uint16_t free = _tu_fifo_remaining(f, w, r);
+ uint16_t wr_idx = f->wr_idx;
+ uint16_t rd_idx = f->rd_idx;
+ uint16_t remain = _ff_remaining(f->depth, wr_idx, rd_idx);
- if (free == 0)
+ if (remain == 0)
{
- info->len_lin = 0;
+ info->len_lin = 0;
info->len_wrap = 0;
- info->ptr_lin = NULL;
+ info->ptr_lin = NULL;
info->ptr_wrap = NULL;
return;
}
// Get relative pointers
- w = get_relative_pointer(f, w);
- r = get_relative_pointer(f, r);
+ uint16_t wr_ptr = idx2ptr(f->depth, wr_idx);
+ uint16_t rd_ptr = idx2ptr(f->depth, rd_idx);
// Copy pointer to buffer to start writing to
- info->ptr_lin = &f->buffer[w];
+ info->ptr_lin = &f->buffer[wr_ptr];
- if (w < r)
+ if (wr_ptr < rd_ptr)
{
// Non wrapping case
- info->len_lin = r-w;
+ info->len_lin = rd_ptr-wr_ptr;
info->len_wrap = 0;
info->ptr_wrap = NULL;
}
else
{
- info->len_lin = f->depth - w;
- info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth
- info->ptr_wrap = f->buffer; // Always start of buffer
+ info->len_lin = f->depth - wr_ptr;
+ info->len_wrap = remain - info->len_lin; // Remaining length - n already was limited to remain or FIFO depth
+ info->ptr_wrap = f->buffer; // Always start of buffer
}
}
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 18db289a1..879acda4f 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -32,7 +32,7 @@
extern "C" {
#endif
-// Due to the use of unmasked pointers, this FIFO does not suffer from loosing
+// Due to the use of unmasked pointers, this FIFO does not suffer from losing
// one item slice. Furthermore, write and read operations are completely
// decoupled as write and read functions do not modify a common state. Henceforth,
// writing or reading from the FIFO within an ISR is safe as long as no other
@@ -42,79 +42,129 @@ extern "C" {
// within a certain number (see tu_fifo_overflow()).
#include "common/tusb_common.h"
+#include "osal/osal.h"
// mutex is only needed for RTOS
// for OS None, we don't get preempted
-#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE)
+#define CFG_FIFO_MUTEX OSAL_MUTEX_REQUIRED
-#if CFG_FIFO_MUTEX
-#include "osal/osal.h"
-#define tu_fifo_mutex_t osal_mutex_t
-#endif
-
-typedef struct
-{
- uint8_t* buffer ; ///< buffer pointer
- uint16_t depth ; ///< max items
- uint16_t item_size ; ///< size of each item
- bool overwritable ;
+/* Write/Read index is always in the range of:
+ * 0 .. 2*depth-1
+ * The extra window allow us to determine the fifo state of empty or full with only 2 indices
+ * Following are examples with depth = 3
+ *
+ * - empty: W = R
+ * |
+ * -------------------------
+ * | 0 | RW| 2 | 3 | 4 | 5 |
+ *
+ * - full 1: W > R
+ * |
+ * -------------------------
+ * | 0 | R | 2 | 3 | W | 5 |
+ *
+ * - full 2: W < R
+ * |
+ * -------------------------
+ * | 0 | 1 | W | 3 | 4 | R |
+ *
+ * - Number of items in the fifo can be determined in either cases:
+ * - case W >= R: Count = W - R
+ * - case W < R: Count = 2*depth - (R - W)
+ *
+ * In non-overwritable mode, computed Count (in above 2 cases) is at most equal to depth.
+ * However, in over-writable mode, write index can be repeatedly increased and count can be
+ * temporarily larger than depth (overflowed condition) e.g
+ *
+ * - Overflowed 1: write(3), write(1)
+ * In this case we will adjust Read index when read()/peek() is called so that count = depth.
+ * |
+ * -------------------------
+ * | R | 1 | 2 | 3 | W | 5 |
+ *
+ * - Double Overflowed i.e index is out of allowed range [0,2*depth)
+ * This occurs when we continue to write after 1st overflowed to 2nd overflowed. e.g:
+ * write(3), write(1), write(2)
+ * This must be prevented since it will cause unrecoverable state, in above example
+ * if not handled the fifo will be empty instead of continue-to-be full. Since we must not modify
+ * read index in write() function, which cause race condition. We will re-position write index so that
+ * after data is written it is a full fifo i.e W = depth - R
+ *
+ * re-position W = 1 before write(2)
+ * Note: we should also move data from mem[3] to read index as well, but deliberately skipped here
+ * since it is an expensive operation !!!
+ * |
+ * -------------------------
+ * | R | W | 2 | 3 | 4 | 5 |
+ *
+ * perform write(2), result is still a full fifo.
+ *
+ * |
+ * -------------------------
+ * | R | 1 | 2 | W | 4 | 5 |
+ */
+typedef struct {
+ uint8_t* buffer ; // buffer pointer
+ uint16_t depth ; // max items
- uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
- uint16_t max_pointer_idx ; ///< maximum absolute pointer index
+ struct TU_ATTR_PACKED {
+ uint16_t item_size : 15; // size of each item
+ bool overwritable : 1 ; // ovwerwritable when full
+ };
- volatile uint16_t wr_idx ; ///< write pointer
- volatile uint16_t rd_idx ; ///< read pointer
+ volatile uint16_t wr_idx ; // write index
+ volatile uint16_t rd_idx ; // read index
-#if CFG_FIFO_MUTEX
- tu_fifo_mutex_t mutex_wr;
- tu_fifo_mutex_t mutex_rd;
+#if OSAL_MUTEX_REQUIRED
+ osal_mutex_t mutex_wr;
+ osal_mutex_t mutex_rd;
#endif
} tu_fifo_t;
-typedef struct
-{
+typedef struct {
uint16_t len_lin ; ///< linear length in item size
uint16_t len_wrap ; ///< wrapped length in item size
void * ptr_lin ; ///< linear part start pointer
void * ptr_wrap ; ///< wrapped part start pointer
} tu_fifo_buffer_info_t;
-#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \
-{ \
+#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable){\
.buffer = _buffer, \
.depth = _depth, \
.item_size = sizeof(_type), \
.overwritable = _overwritable, \
- .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \
- .max_pointer_idx = 2*(_depth)-1, \
}
#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
-
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
-#if CFG_FIFO_MUTEX
+#if OSAL_MUTEX_REQUIRED
TU_ATTR_ALWAYS_INLINE static inline
-void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl)
-{
- f->mutex_wr = write_mutex_hdl;
- f->mutex_rd = read_mutex_hdl;
+void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
+ f->mutex_wr = wr_mutex;
+ f->mutex_rd = rd_mutex;
}
+#else
+#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
#endif
-bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
-uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
+bool tu_fifo_write (tu_fifo_t* f, void const * data);
+uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * data, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
+#endif
-bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
+bool tu_fifo_read (tu_fifo_t* f, void * buffer);
+uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
+#endif
bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
@@ -127,13 +177,12 @@ bool tu_fifo_overflowed (tu_fifo_t* f);
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
TU_ATTR_ALWAYS_INLINE static inline
-uint16_t tu_fifo_depth(tu_fifo_t* f)
-{
+uint16_t tu_fifo_depth(tu_fifo_t* f) {
return f->depth;
}
// Pointer modifications intended to be used in combinations with DMAs.
-// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
+// USE WITH CARE - NO SAFETY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n);
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
@@ -143,7 +192,6 @@ void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info);
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
-
#ifdef __cplusplus
}
#endif
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 86c68baf8..a0175d664 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -28,16 +28,22 @@
#define TUSB_MCU_H_
//--------------------------------------------------------------------+
-// Port Specific
-// TUP stand for TinyUSB Port (can be renamed)
+// Port/Platform Specific
+// TUP stand for TinyUSB Port/Platform (can be renamed)
//--------------------------------------------------------------------+
//------------- Unaligned Memory Access -------------//
-// ARMv7+ (M3-M7, M23-M33) can access unaligned memory
-#if (defined(__ARM_ARCH) && (__ARM_ARCH >= 7))
- #define TUP_ARCH_STRICT_ALIGN 0
+#ifdef __ARM_ARCH
+ // ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access
+ #if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1
+ #define TUP_ARCH_STRICT_ALIGN 0
+ #else
+ #define TUP_ARCH_STRICT_ALIGN 1
+ #endif
#else
+ // TODO default to strict align for others
+ // Should investigate other architecture such as risv, xtensa, mips for optimal setting
#define TUP_ARCH_STRICT_ALIGN 1
#endif
@@ -48,87 +54,139 @@
* - RHPORT_HIGHSPEED: support highspeed with on-chip PHY
*/
-//------------- NXP -------------//
+//--------------------------------------------------------------------+
+// NXP
+//--------------------------------------------------------------------+
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
+ #define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 5
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_USBIP_OHCI
-
-#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
- // TODO USB0 has 6, USB1 has 4
- #define TUP_USBIP_CHIPIDEA_HS
- #define TUP_USBIP_EHCI
-
- #define TUP_DCD_ENDPOINT_MAX 6
- #define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 FS
+ #define TUP_OHCI_RHPORTS 2
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
+ #define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 5
-#elif TU_CHECK_MCU(OPT_MCU_LPC54XXX)
+#elif TU_CHECK_MCU(OPT_MCU_LPC54)
// TODO USB0 has 5, USB1 has 6
+ #define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 6
-#elif TU_CHECK_MCU(OPT_MCU_LPC55XX)
+#elif TU_CHECK_MCU(OPT_MCU_LPC55)
// TODO USB0 has 5, USB1 has 6
+ #define TUP_USBIP_IP3511
#define TUP_DCD_ENDPOINT_MAX 6
-#elif TU_CHECK_MCU(OPT_MCU_MIMXRT)
+#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
+ // USB0 has 6 with HS PHY, USB1 has 4 only FS
+ #define TUP_USBIP_CHIPIDEA_HS
+ #define TUP_USBIP_EHCI
+
+ #define TUP_DCD_ENDPOINT_MAX 6
+ #define TUP_RHPORT_HIGHSPEED 1
+
+#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
+ // USB0 is chipidea FS
+ #define TUP_USBIP_CHIPIDEA_FS
+ #define TUP_USBIP_CHIPIDEA_FS_MCX
+
+ // USB1 is chipidea HS
#define TUP_USBIP_CHIPIDEA_HS
#define TUP_USBIP_EHCI
#define TUP_DCD_ENDPOINT_MAX 8
- #define TUP_RHPORT_HIGHSPEED 1 // Port0 HS, Port1 HS
+ #define TUP_RHPORT_HIGHSPEED 1
-#elif TU_CHECK_MCU(OPT_MCU_MKL25ZXX, OPT_MCU_K32L2BXX)
+#elif TU_CHECK_MCU(OPT_MCU_MCXA15)
+ // USB0 is chipidea FS
+ #define TUP_USBIP_CHIPIDEA_FS
+ #define TUP_USBIP_CHIPIDEA_FS_MCX
+
+ #define TUP_DCD_ENDPOINT_MAX 16
+
+#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX)
+ #include "fsl_device_registers.h"
+
+ #define TUP_USBIP_CHIPIDEA_HS
+ #define TUP_USBIP_EHCI
+
+ #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_RHPORT_HIGHSPEED 1
+
+ #if __CORTEX_M == 7
+ #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 1
+ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 1
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE 32
+ #endif
+
+#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K)
+ #define TUP_USBIP_CHIPIDEA_FS
+ #define TUP_USBIP_CHIPIDEA_FS_KINETIS
#define TUP_DCD_ENDPOINT_MAX 16
#elif TU_CHECK_MCU(OPT_MCU_MM32F327X)
#define TUP_DCD_ENDPOINT_MAX 16
-//------------- Nordic -------------//
+//--------------------------------------------------------------------+
+// Nordic
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
// 8 CBI + 1 ISO
#define TUP_DCD_ENDPOINT_MAX 9
-//------------- Microchip -------------//
+//--------------------------------------------------------------------+
+// Microchip
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \
TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22)
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_SAMG)
#define TUP_DCD_ENDPOINT_MAX 6
- #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
#elif TU_CHECK_MCU(OPT_MCU_SAMX7X)
#define TUP_DCD_ENDPOINT_MAX 10
#define TUP_RHPORT_HIGHSPEED 1
- #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ)
#define TUP_DCD_ENDPOINT_MAX 8
- #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \
TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33)
#define TUP_DCD_ENDPOINT_MAX 16
- #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
-//------------- ST -------------//
+//--------------------------------------------------------------------+
+// ST
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_STM32F0)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32F1)
+ // - F102, F103 use fsdev
+ // - F105, F107 use dwc2
#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
defined (STM32F107xB) || defined (STM32F107xC)
#define TUP_USBIP_DWC2
#define TUP_USBIP_DWC2_STM32
+ #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
#define TUP_DCD_ENDPOINT_MAX 4
- #else
+ #elif defined(STM32F102x6) || defined(STM32F102xB) || \
+ defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
+ #else
+ #error "Unsupported STM32F1 mcu"
#endif
#elif TU_CHECK_MCU(OPT_MCU_STM32F2)
@@ -139,6 +197,8 @@
#define TUP_DCD_ENDPOINT_MAX 6
#elif TU_CHECK_MCU(OPT_MCU_STM32F3)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32F4)
@@ -166,13 +226,39 @@
#define TUP_DCD_ENDPOINT_MAX 9
+#elif TU_CHECK_MCU(OPT_MCU_STM32H5)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
#elif TU_CHECK_MCU(OPT_MCU_STM32G4)
+ // Device controller
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+
+ // TypeC controller
+ #define TUP_USBIP_TYPEC_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+ #define TUP_TYPEC_RHPORTS_NUM 1
+
+#elif TU_CHECK_MCU(OPT_MCU_STM32G0)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_STM32C0)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32L4)
+ // - L4x2, L4x3 use fsdev
+ // - L4x4, L4x6, L4x7, L4x9 use dwc2
#if defined (STM32L475xx) || defined (STM32L476xx) || \
defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \
@@ -182,36 +268,88 @@
#define TUP_USBIP_DWC2_STM32
#define TUP_DCD_ENDPOINT_MAX 6
- #else
+ #elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \
+ defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
+ #else
+ #error "Unsupported STM32L4 mcu"
#endif
#elif TU_CHECK_MCU(OPT_MCU_STM32WB)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_STM32U5)
+ #if defined (STM32U535xx) || defined (STM32U545xx)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+ #else
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_STM32
+
+ // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
+ #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
+ defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
+ #define TUP_DCD_ENDPOINT_MAX 9
+ #define TUP_RHPORT_HIGHSPEED 1
+ #else
+ #define TUP_DCD_ENDPOINT_MAX 6
+ #endif
+ #endif
+
+#elif TU_CHECK_MCU(OPT_MCU_STM32L5)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_STM32U0)
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_STM32
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_STM32H7RS)
#define TUP_USBIP_DWC2
#define TUP_USBIP_DWC2_STM32
- #define TUP_DCD_ENDPOINT_MAX 6
-//------------- Sony -------------//
+ // FS has 6, HS has 9
+ #define TUP_DCD_ENDPOINT_MAX 9
+
+ // MCU with on-chip HS Phy
+ #define TUP_RHPORT_HIGHSPEED 1
+
+//--------------------------------------------------------------------+
+// Sony
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_CXD56)
#define TUP_DCD_ENDPOINT_MAX 7
#define TUP_RHPORT_HIGHSPEED 1
- #define TUP_DCD_ENDPOINT_EXCLUSIVE_NUMBER
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
-//------------- TI -------------//
+//--------------------------------------------------------------------+
+// TI
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx)
#define TUP_DCD_ENDPOINT_MAX 8
#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
+ #define TUP_USBIP_MUSB
+ #define TUP_USBIP_MUSB_TI
#define TUP_DCD_ENDPOINT_MAX 8
-//------------- ValentyUSB -------------//
+//--------------------------------------------------------------------+
+// ValentyUSB (Litex)
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI)
#define TUP_DCD_ENDPOINT_MAX 16
-//------------- Nuvoton -------------//
+//--------------------------------------------------------------------+
+// Nuvoton
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126)
#define TUP_DCD_ENDPOINT_MAX 8
@@ -222,66 +360,205 @@
#define TUP_DCD_ENDPOINT_MAX 12
#define TUP_RHPORT_HIGHSPEED 1
-//------------- Espressif -------------//
+//--------------------------------------------------------------------+
+// Espressif
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
#define TUP_USBIP_DWC2
- #define TUP_DCD_ENDPOINT_MAX 6
+ #define TUP_USBIP_DWC2_ESP32
+ #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+
+ // Disable slave if DMA is enabled
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
+
+#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
+ #define TUP_USBIP_DWC2
+ #define TUP_USBIP_DWC2_ESP32
+ #define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS
+ #define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep
+
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+
+ // Disable slave if DMA is enabled
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
-//------------- Dialog -------------//
+ // Enable dcache if DMA is enabled
+ #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_DWC2_DMA_ENABLE
+ #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)
+ #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
+ #error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
+ #endif
+
+ #define TUP_DCD_ENDPOINT_MAX 0
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
+
+//--------------------------------------------------------------------+
+// Dialog
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_DA1469X)
#define TUP_DCD_ENDPOINT_MAX 4
-//------------- Raspberry Pi -------------//
+//--------------------------------------------------------------------+
+// Raspberry Pi
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
+ #define TUP_DCD_EDPT_ISO_ALLOC
#define TUP_DCD_ENDPOINT_MAX 16
#define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))
-//------------- Silabs -------------//
+//--------------------------------------------------------------------+
+// Silabs
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
#define TUP_USBIP_DWC2
#define TUP_DCD_ENDPOINT_MAX 7
-//------------- Renesas -------------//
-#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
+//--------------------------------------------------------------------+
+// Renesas
+//--------------------------------------------------------------------+
+#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX)
+ #define TUP_USBIP_RUSB2
#define TUP_DCD_ENDPOINT_MAX 10
-//------------- GigaDevice -------------//
+//--------------------------------------------------------------------+
+// GigaDevice
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
#define TUP_USBIP_DWC2
#define TUP_DCD_ENDPOINT_MAX 4
-//------------- Broadcom -------------//
+//--------------------------------------------------------------------+
+// Broadcom
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
#define TUP_USBIP_DWC2
#define TUP_DCD_ENDPOINT_MAX 8
#define TUP_RHPORT_HIGHSPEED 1
-//------------- Broadcom -------------//
+//--------------------------------------------------------------------+
+// Infineon
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
#define TUP_USBIP_DWC2
#define TUP_DCD_ENDPOINT_MAX 8
-//------------- BridgeTek -------------//
+//--------------------------------------------------------------------+
+// BridgeTek
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_FT90X)
#define TUP_DCD_ENDPOINT_MAX 8
#define TUP_RHPORT_HIGHSPEED 1
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
#elif TU_CHECK_MCU(OPT_MCU_FT93X)
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_RHPORT_HIGHSPEED 1
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
-//------------ Allwinner -------------//
+//--------------------------------------------------------------------+
+// Allwinner
+//--------------------------------------------------------------------+
#elif TU_CHECK_MCU(OPT_MCU_F1C100S)
#define TUP_DCD_ENDPOINT_MAX 4
+//--------------------------------------------------------------------+
+// WCH
+//--------------------------------------------------------------------+
+#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
+ #define TUP_USBIP_WCH_USBHS
+ #define TUP_USBIP_WCH_USBFS
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+ #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V103)
+ #define TUP_USBIP_WCH_USBFS
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 1
+ #endif
+
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
+ // v20x support both FSDEV (USBD) and USBFS, default to FSDEV
+ #define TUP_USBIP_WCH_USBFS
+ #define TUP_USBIP_FSDEV
+ #define TUP_USBIP_FSDEV_CH32
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+
+ #if !defined(CFG_TUD_WCH_USBIP_FSDEV)
+ #define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_DCD_ENDPOINT_MAX 8
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
+ // v307 support both FS and HS, default to HS
+ #define TUP_USBIP_WCH_USBHS
+ #define TUP_USBIP_WCH_USBFS
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+ #define CFG_TUD_WCH_USBIP_USBFS 0
+ #endif
+
+ #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+ #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+ #endif
+
+ #define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
+ #define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+//--------------------------------------------------------------------+
+// Analog Devices
+//--------------------------------------------------------------------+
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ #define TUP_USBIP_MUSB
+ #define TUP_USBIP_MUSB_ADI
+ #define TUP_DCD_ENDPOINT_MAX 12
+ #define TUP_RHPORT_HIGHSPEED 1
+ #define TUD_ENDPOINT_ONE_DIRECTION_ONLY
+
+#endif
+
+//--------------------------------------------------------------------+
+// External USB controller
+//--------------------------------------------------------------------+
+
+#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
+ #ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL
+ #define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1))
+ #endif
#endif
+
//--------------------------------------------------------------------+
// Default Values
//--------------------------------------------------------------------+
-#ifndef TUP_DCD_ENDPOINT_MAX
+#ifndef TUP_MCU_MULTIPLE_CORE
+#define TUP_MCU_MULTIPLE_CORE 0
+#endif
+
+#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
#define TUP_DCD_ENDPOINT_MAX 8
#endif
@@ -296,4 +573,13 @@
#define TU_ATTR_FAST_FUNC
#endif
+// USBIP that support ISO alloc & activate API
+#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) || defined(TUP_USBIP_MUSB)
+ #define TUP_DCD_EDPT_ISO_ALLOC
+#endif
+
+#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA_ENABLE == 0
+ #define TUP_MEM_CONST_ADDR
+#endif
+
#endif
diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h
index b34506f65..c71775abb 100644
--- a/src/common/tusb_private.h
+++ b/src/common/tusb_private.h
@@ -24,23 +24,46 @@
* This file is part of the TinyUSB stack.
*/
+#ifndef TUSB_PRIVATE_H_
+#define TUSB_PRIVATE_H_
-#ifndef _TUSB_PRIVATE_H_
-#define _TUSB_PRIVATE_H_
+// Internal Helper used by Host and Device Stack
#ifdef __cplusplus
extern "C" {
#endif
-typedef struct TU_ATTR_PACKED
-{
+#define TUP_USBIP_CONTROLLER_NUM 2
+extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM];
+
+//--------------------------------------------------------------------+
+// Endpoint
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
volatile uint8_t busy : 1;
volatile uint8_t stalled : 1;
volatile uint8_t claimed : 1;
}tu_edpt_state_t;
+typedef struct {
+ struct TU_ATTR_PACKED {
+ uint8_t is_host : 1; // 1: host, 0: device
+ uint8_t is_mps512 : 1; // 1: 512, 0: 64 since stream is used for Bulk only
+ };
+ uint8_t ep_addr;
+ uint16_t ep_bufsize;
+
+ uint8_t* ep_buf; // TODO xfer_fifo can skip this buffer
+ tu_fifo_t ff;
+
+ // mutex: read if rx, otherwise write
+ OSAL_MUTEX_DEF(ff_mutexdef);
+
+}tu_edpt_stream_t;
+
//--------------------------------------------------------------------+
-// Internal Helper used by Host and Device Stack
+// Endpoint
//--------------------------------------------------------------------+
// Check if endpoint descriptor is valid per USB specs
@@ -58,8 +81,92 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
// Release an endpoint with provided mutex
bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
+//--------------------------------------------------------------------+
+// Endpoint Stream
+//--------------------------------------------------------------------+
+
+// Init an endpoint stream
+bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
+ void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize);
+
+// Deinit an endpoint stream
+bool tu_edpt_stream_deinit(tu_edpt_stream_t* s);
+
+// Open an stream for an endpoint
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_edpt_stream_open(tu_edpt_stream_t* s, tusb_desc_endpoint_t const *desc_ep) {
+ tu_fifo_clear(&s->ff);
+ s->ep_addr = desc_ep->bEndpointAddress;
+ s->is_mps512 = (tu_edpt_packet_size(desc_ep) == 512) ? 1 : 0;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_edpt_stream_close(tu_edpt_stream_t* s) {
+ s->ep_addr = 0;
+}
+
+// Clear fifo
+TU_ATTR_ALWAYS_INLINE static inline
+bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
+ return tu_fifo_clear(&s->ff);
+}
+
+//--------------------------------------------------------------------+
+// Stream Write
+//--------------------------------------------------------------------+
+
+// Write to stream
+uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize);
+
+// Start an usb transfer if endpoint is not busy
+uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s);
+
+// Start an zero-length packet if needed
+bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes);
+
+// Get the number of bytes available for writing to FIFO
+// Note: if no fifo, return endpoint size if not busy, 0 otherwise
+uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s);
+
+//--------------------------------------------------------------------+
+// Stream Read
+//--------------------------------------------------------------------+
+
+// Read from stream
+uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize);
+
+// Start an usb transfer if endpoint is not busy
+uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s);
+
+// Must be called in the transfer complete callback
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) {
+ if (tu_fifo_depth(&s->ff)) {
+ tu_fifo_write_n(&s->ff, s->ep_buf, (uint16_t) xferred_bytes);
+ }
+}
+
+// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_edpt_stream_read_xfer_complete_offset(tu_edpt_stream_t* s, uint32_t xferred_bytes, uint32_t skip_offset) {
+ if (tu_fifo_depth(&s->ff) && (skip_offset < xferred_bytes)) {
+ tu_fifo_write_n(&s->ff, s->ep_buf + skip_offset, (uint16_t) (xferred_bytes - skip_offset));
+ }
+}
+
+// Get the number of bytes available for reading
+TU_ATTR_ALWAYS_INLINE static inline
+uint32_t tu_edpt_stream_read_available(tu_edpt_stream_t* s) {
+ return (uint32_t) tu_fifo_count(&s->ff);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline
+bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) {
+ return tu_fifo_peek(&s->ff, ch);
+}
+
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_PRIVATE_H_ */
+#endif
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index e11f08dd1..9e4d9380c 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -24,12 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup group_usb_definitions
- * \defgroup USBDef_Type USB Types
- * @{ */
-
-#ifndef _TUSB_TYPES_H_
-#define _TUSB_TYPES_H_
+#ifndef TUSB_TYPES_H_
+#define TUSB_TYPES_H_
#include <stdbool.h>
#include <stdint.h>
@@ -39,39 +35,87 @@
extern "C" {
#endif
+//------------- Device DCache declaration -------------//
+#define TUD_EPBUF_DCACHE_SIZE(_size) (CFG_TUD_MEM_DCACHE_ENABLE ? \
+ (TU_DIV_CEIL(_size, CFG_TUD_MEM_DCACHE_LINE_SIZE) * CFG_TUD_MEM_DCACHE_LINE_SIZE) : (_size))
+
+// Declare an endpoint buffer with uint8_t[size]
+#define TUD_EPBUF_DEF(_name, _size) \
+ union { \
+ CFG_TUD_MEM_ALIGN uint8_t _name[_size]; \
+ uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(_size)]; \
+ }
+
+// Declare an endpoint buffer with a type
+#define TUD_EPBUF_TYPE_DEF(_type, _name) \
+ union { \
+ CFG_TUD_MEM_ALIGN _type _name; \
+ uint8_t _name##_dcache_padding[TUD_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ }
+
+//------------- Host DCache declaration -------------//
+#define TUH_EPBUF_DCACHE_SIZE(_size) (CFG_TUH_MEM_DCACHE_ENABLE ? \
+ (TU_DIV_CEIL(_size, CFG_TUH_MEM_DCACHE_LINE_SIZE) * CFG_TUH_MEM_DCACHE_LINE_SIZE) : (_size))
+
+// Declare an endpoint buffer with uint8_t[size]
+#define TUH_EPBUF_DEF(_name, _size) \
+ union { \
+ CFG_TUH_MEM_ALIGN uint8_t _name[_size]; \
+ uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(_size)]; \
+ }
+
+// Declare an endpoint buffer with a type
+#define TUH_EPBUF_TYPE_DEF(_type, _name) \
+ union { \
+ CFG_TUH_MEM_ALIGN _type _name; \
+ uint8_t _name##_dcache_padding[TUH_EPBUF_DCACHE_SIZE(sizeof(_type))]; \
+ }
+
+
/*------------------------------------------------------------------*/
/* CONSTANTS
*------------------------------------------------------------------*/
+typedef enum {
+ TUSB_ROLE_INVALID = 0,
+ TUSB_ROLE_DEVICE = 0x1,
+ TUSB_ROLE_HOST = 0x2,
+} tusb_role_t;
+
/// defined base on EHCI specs value for Endpoint Speed
-typedef enum
-{
+typedef enum {
TUSB_SPEED_FULL = 0,
TUSB_SPEED_LOW = 1,
TUSB_SPEED_HIGH = 2,
+ TUSB_SPEED_AUTO = 0xaa,
TUSB_SPEED_INVALID = 0xff,
-}tusb_speed_t;
+} tusb_speed_t;
/// defined base on USB Specs Endpoint's bmAttributes
-typedef enum
-{
- TUSB_XFER_CONTROL = 0 ,
- TUSB_XFER_ISOCHRONOUS ,
- TUSB_XFER_BULK ,
- TUSB_XFER_INTERRUPT
-}tusb_xfer_type_t;
+typedef enum {
+ TUSB_XFER_CONTROL = 0,
+ TUSB_XFER_ISOCHRONOUS = 1,
+ TUSB_XFER_BULK = 2,
+ TUSB_XFER_INTERRUPT = 3
+} tusb_xfer_type_t;
-typedef enum
-{
+typedef enum {
TUSB_DIR_OUT = 0,
TUSB_DIR_IN = 1,
TUSB_DIR_IN_MASK = 0x80
-}tusb_dir_t;
+} tusb_dir_t;
-/// Isochronous End Point Attributes
-typedef enum
-{
+enum {
+ TUSB_EPSIZE_BULK_FS = 64,
+ TUSB_EPSIZE_BULK_HS = 512,
+
+ TUSB_EPSIZE_ISO_FS_MAX = 1023,
+ TUSB_EPSIZE_ISO_HS_MAX = 1024,
+};
+
+/// Isochronous Endpoint Attributes
+typedef enum {
TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
@@ -79,11 +123,10 @@ typedef enum
TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
-}tusb_iso_ep_attribute_t;
+} tusb_iso_ep_attribute_t;
/// USB Descriptor Types
-typedef enum
-{
+typedef enum {
TUSB_DESC_DEVICE = 0x01,
TUSB_DESC_CONFIGURATION = 0x02,
TUSB_DESC_STRING = 0x03,
@@ -110,10 +153,9 @@ typedef enum
TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
-}tusb_desc_type_t;
+} tusb_desc_type_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_GET_STATUS = 0 ,
TUSB_REQ_CLEAR_FEATURE = 1 ,
TUSB_REQ_RESERVED = 2 ,
@@ -127,25 +169,22 @@ typedef enum
TUSB_REQ_GET_INTERFACE = 10 ,
TUSB_REQ_SET_INTERFACE = 11 ,
TUSB_REQ_SYNCH_FRAME = 12
-}tusb_request_code_t;
+} tusb_request_code_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_FEATURE_EDPT_HALT = 0,
TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
TUSB_REQ_FEATURE_TEST_MODE = 2
-}tusb_request_feature_selector_t;
+} tusb_request_feature_selector_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_TYPE_STANDARD = 0,
TUSB_REQ_TYPE_CLASS,
TUSB_REQ_TYPE_VENDOR,
TUSB_REQ_TYPE_INVALID
} tusb_request_type_t;
-typedef enum
-{
+typedef enum {
TUSB_REQ_RCPT_DEVICE =0,
TUSB_REQ_RCPT_INTERFACE,
TUSB_REQ_RCPT_ENDPOINT,
@@ -153,8 +192,7 @@ typedef enum
} tusb_request_recipient_t;
// https://www.usb.org/defined-class-codes
-typedef enum
-{
+typedef enum {
TUSB_CLASS_UNSPECIFIED = 0 ,
TUSB_CLASS_AUDIO = 1 ,
TUSB_CLASS_CDC = 2 ,
@@ -178,26 +216,23 @@ typedef enum
TUSB_CLASS_MISC = 0xEF ,
TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
-}tusb_class_code_t;
+} tusb_class_code_t;
typedef enum
{
MISC_SUBCLASS_COMMON = 2
}misc_subclass_type_t;
-typedef enum
-{
+typedef enum {
MISC_PROTOCOL_IAD = 1
-}misc_protocol_type_t;
+} misc_protocol_type_t;
-typedef enum
-{
+typedef enum {
APP_SUBCLASS_USBTMC = 0x03,
APP_SUBCLASS_DFU_RUNTIME = 0x01
} app_subclass_type_t;
-typedef enum
-{
+typedef enum {
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
@@ -214,38 +249,46 @@ typedef enum
DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
-}device_capability_type_t;
+} device_capability_type_t;
enum {
- TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
- TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6),
+ TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
+ TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
};
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
-typedef enum
-{
- XFER_RESULT_SUCCESS,
+// USB 2.0 Spec Table 9-7: Test Mode Selectors
+typedef enum {
+ TUSB_FEATURE_TEST_J = 1,
+ TUSB_FEATURE_TEST_K = 2,
+ TUSB_FEATURE_TEST_SE0_NAK = 3,
+ TUSB_FEATURE_TEST_PACKET = 4,
+ TUSB_FEATURE_TEST_FORCE_ENABLE = 5,
+} tusb_feature_test_mode_t;
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+typedef enum {
+ XFER_RESULT_SUCCESS = 0,
XFER_RESULT_FAILED,
XFER_RESULT_STALLED,
XFER_RESULT_TIMEOUT,
XFER_RESULT_INVALID
-}xfer_result_t;
+} xfer_result_t;
-enum // TODO remove
-{
+// TODO remove
+enum {
DESC_OFFSET_LEN = 0,
DESC_OFFSET_TYPE = 1
};
-enum
-{
+enum {
INTERFACE_INVALID_NUMBER = 0xff
};
-
-typedef enum
-{
+typedef enum {
MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
@@ -257,14 +300,25 @@ typedef enum
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
} microsoft_os_20_type_t;
-enum
-{
- CONTROL_STAGE_IDLE,
+enum {
+ CONTROL_STAGE_IDLE = 0,
CONTROL_STAGE_SETUP,
CONTROL_STAGE_DATA,
CONTROL_STAGE_ACK
};
+enum {
+ TUSB_INDEX_INVALID_8 = 0xFFu
+};
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+typedef struct {
+ tusb_role_t role;
+ tusb_speed_t speed;
+} tusb_rhport_init_t;
+
//--------------------------------------------------------------------+
// USB Descriptors
//--------------------------------------------------------------------+
@@ -274,15 +328,14 @@ TU_ATTR_PACKED_BEGIN
TU_ATTR_BIT_FIELD_ORDER_BEGIN
/// USB Device Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
- uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
+ uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
- uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
- uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
- uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
+ uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF).
+ uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF).
+ uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF).
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
@@ -298,8 +351,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
// USB Binary Device Object Store (BOS) Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
@@ -309,8 +361,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
/// USB Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
@@ -325,8 +376,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
/// USB Interface Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
@@ -342,8 +392,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
/// USB Endpoint Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; // Size of this descriptor in bytes
uint8_t bDescriptorType ; // ENDPOINT Descriptor Type
@@ -363,8 +412,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
/// USB Other Speed Configuration Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
uint16_t wTotalLength ; ///< Total length of data returned
@@ -377,8 +425,7 @@ typedef struct TU_ATTR_PACKED
} tusb_desc_other_speed_t;
/// USB Device Qualifier Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Device Qualifier Type
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
@@ -395,8 +442,7 @@ typedef struct TU_ATTR_PACKED
TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
/// USB Interface Association Descriptor (IAD ECN)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
@@ -410,17 +456,17 @@ typedef struct TU_ATTR_PACKED
uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
} tusb_desc_interface_assoc_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct");
+
// USB String Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength ; ///< Size of this descriptor in bytes
uint8_t bDescriptorType ; ///< Descriptor Type
uint16_t unicode_string[];
} tusb_desc_string_t;
// USB Binary Device Object Store (BOS)
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType ;
uint8_t bDevCapabilityType;
@@ -429,9 +475,8 @@ typedef struct TU_ATTR_PACKED
uint8_t CapabilityData[];
} tusb_desc_bos_platform_t;
-// USB WebuSB URL Descriptor
-typedef struct TU_ATTR_PACKED
-{
+// USB WebUSB URL Descriptor
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bScheme;
@@ -439,8 +484,7 @@ typedef struct TU_ATTR_PACKED
} tusb_desc_webusb_url_t;
// DFU Functional Descriptor
-typedef struct TU_ATTR_PACKED
-{
+typedef struct TU_ATTR_PACKED {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -461,10 +505,11 @@ typedef struct TU_ATTR_PACKED
uint16_t bcdDFUVersion;
} tusb_desc_dfu_functional_t;
-/*------------------------------------------------------------------*/
-/* Types
- *------------------------------------------------------------------*/
-typedef struct TU_ATTR_PACKED{
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
union {
struct TU_ATTR_PACKED {
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
@@ -483,7 +528,6 @@ typedef struct TU_ATTR_PACKED{
TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
-
TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END
@@ -492,37 +536,26 @@ TU_ATTR_BIT_FIELD_ORDER_END
//--------------------------------------------------------------------+
// Get direction from Endpoint address
-TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
}
// Get Endpoint number from address
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
}
-TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep)
-{
- return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
+ return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
}
#if CFG_TUSB_DEBUG
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir)
-{
- static const char *str[] = {"out", "in"};
- return str[dir];
-}
-
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t)
-{
- static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
+TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
+ tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
return str[t];
}
#endif
@@ -530,26 +563,34 @@ TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_
//--------------------------------------------------------------------+
// Descriptor helper
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc)
-{
+
+// return next descriptor
+TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
uint8_t const* desc8 = (uint8_t const*) desc;
return desc8 + desc8[DESC_OFFSET_LEN];
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc)
-{
+// get descriptor type
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
}
-TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc)
-{
+// get descriptor length
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
}
+// find descriptor that match byte1 (type)
+uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1);
+
+// find descriptor that match byte1 (type) and byte2
+uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2);
+
+// find descriptor that match byte1 (type) and byte2
+uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3);
+
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_TYPES_H_ */
-
-/** @} */
+#endif // TUSB_TYPES_H_
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index a52a6d269..6d02d3572 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -56,12 +56,8 @@
* #define TU_VERIFY(cond) if(cond) return false;
* #define TU_VERIFY(cond,ret) if(cond) return ret;
*
- * #define TU_VERIFY_HDLR(cond,handler) if(cond) {handler; return false;}
- * #define TU_VERIFY_HDLR(cond,ret,handler) if(cond) {handler; return ret;}
- *
- * #define TU_ASSERT(cond) if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;}
- * #define TU_ASSERT(cond,ret) if(cond) {_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
@@ -74,20 +70,20 @@
#if CFG_TUSB_DEBUG
#include <stdio.h>
- #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
+ #define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
#else
- #define _MESS_FAILED() do {} while (0)
+ #define TU_MESS_FAILED() do {} while (0)
#endif
-// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33
-#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
- #define TU_BREAKPOINT() do \
- { \
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
+ defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
+ #define TU_BREAKPOINT() do { \
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
} while(0)
-#elif defined(__riscv)
+#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
#elif defined(_mips)
@@ -98,39 +94,19 @@
#endif
/*------------------------------------------------------------------*/
-/* Macro Generator
- *------------------------------------------------------------------*/
-
-// Helper to implement optional parameter for TU_VERIFY Macro family
-#define _GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
-#define _GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
-
-/*------------- Generator for TU_VERIFY and TU_VERIFY_HDLR -------------*/
-#define TU_VERIFY_DEFINE(_cond, _handler, _ret) do \
-{ \
- if ( !(_cond) ) { _handler; return _ret; } \
-} while(0)
-
-/*------------------------------------------------------------------*/
/* TU_VERIFY
* - TU_VERIFY_1ARGS : return false if failed
* - TU_VERIFY_2ARGS : return provided value if failed
*------------------------------------------------------------------*/
-#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, , false)
-#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, , _ret)
-
-#define TU_VERIFY(...) _GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, UNUSED)(__VA_ARGS__)
-
+#define TU_VERIFY_DEFINE(_cond, _ret) \
+ do { \
+ if ( !(_cond) ) { return _ret; } \
+ } while(0)
-/*------------------------------------------------------------------*/
-/* TU_VERIFY WITH HANDLER
- * - TU_VERIFY_HDLR_2ARGS : execute handler, return false if failed
- * - TU_VERIFY_HDLR_3ARGS : execute handler, return provided error if failed
- *------------------------------------------------------------------*/
-#define TU_VERIFY_HDLR_2ARGS(_cond, _handler) TU_VERIFY_DEFINE(_cond, _handler, false)
-#define TU_VERIFY_HDLR_3ARGS(_cond, _handler, _ret) TU_VERIFY_DEFINE(_cond, _handler, _ret)
+#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false)
+#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret)
-#define TU_VERIFY_HDLR(...) _GET_4TH_ARG(__VA_ARGS__, TU_VERIFY_HDLR_3ARGS, TU_VERIFY_HDLR_2ARGS,UNUSED)(__VA_ARGS__)
+#define TU_VERIFY(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__)
/*------------------------------------------------------------------*/
/* ASSERT
@@ -138,19 +114,20 @@
* - 1 arg : return false if failed
* - 2 arg : return error if failed
*------------------------------------------------------------------*/
-#define ASSERT_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false)
-#define ASSERT_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret)
+#define TU_ASSERT_DEFINE(_cond, _ret) \
+ do { \
+ if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
+ } while(0)
+
+#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false)
+#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret)
#ifndef TU_ASSERT
-#define TU_ASSERT(...) _GET_3RD_ARG(__VA_ARGS__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__)
+#define TU_ASSERT(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__)
#endif
-/*------------------------------------------------------------------*/
-/* ASSERT HDLR
- *------------------------------------------------------------------*/
-
#ifdef __cplusplus
}
#endif
-#endif /* TUSB_VERIFY_H_ */
+#endif
diff --git a/src/device/dcd.h b/src/device/dcd.h
index c1780f656..789552d47 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_DCD_H_
-#define _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
+#define TUSB_DCD_H_
#include "common/tusb_common.h"
#include "osal/osal.h"
@@ -36,42 +36,27 @@
#endif
//--------------------------------------------------------------------+
-// Configuration
-//--------------------------------------------------------------------+
-
-#ifndef CFG_TUD_ENDPPOINT_MAX
- #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX
-#endif
-
-//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
-typedef enum
-{
- DCD_EVENT_INVALID = 0,
- DCD_EVENT_BUS_RESET,
- DCD_EVENT_UNPLUGGED,
- DCD_EVENT_SOF,
- DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support
- DCD_EVENT_RESUME,
-
- DCD_EVENT_SETUP_RECEIVED,
- DCD_EVENT_XFER_COMPLETE,
-
- // Not an DCD event, just a convenient way to defer ISR function
- USBD_EVENT_FUNC_CALL,
-
+typedef enum {
+ DCD_EVENT_INVALID = 0, // 0
+ DCD_EVENT_BUS_RESET, // 1
+ DCD_EVENT_UNPLUGGED, // 2
+ DCD_EVENT_SOF, // 3
+ DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support
+ DCD_EVENT_RESUME, // 5
+ DCD_EVENT_SETUP_RECEIVED, // 6
+ DCD_EVENT_XFER_COMPLETE, // 7
+ USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function
DCD_EVENT_COUNT
} dcd_eventid_t;
-typedef struct TU_ATTR_ALIGNED(4)
-{
+typedef struct TU_ATTR_ALIGNED(4) {
uint8_t rhport;
uint8_t event_id;
- union
- {
+ union {
// BUS RESET
struct {
tusb_speed_t speed;
@@ -103,11 +88,30 @@ typedef struct TU_ATTR_ALIGNED(4)
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
//--------------------------------------------------------------------+
+// Memory API
+//--------------------------------------------------------------------+
+
+// clean/flush data cache: write cache -> memory.
+// Required before an DMA TX transfer to make sure data is in memory
+bool dcd_dcache_clean(const void* addr, uint32_t data_size);
+
+// invalidate data cache: mark cache as invalid, next read will read from memory
+// Required BOTH before and after an DMA RX transfer
+bool dcd_dcache_invalidate(const void* addr, uint32_t data_size);
+
+// clean and invalidate data cache
+// Required before an DMA transfer where memory is both read/write by DMA
+bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size);
+
+//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
// Initialize controller to device mode
-void dcd_init (uint8_t rhport);
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
+
+// Deinitialize controller, unset device mode.
+bool dcd_deinit(uint8_t rhport);
// Interrupt Handler
void dcd_int_handler(uint8_t rhport);
@@ -125,21 +129,25 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
void dcd_remote_wakeup(uint8_t rhport);
// Connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_connect(uint8_t rhport);
// Disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_disconnect(uint8_t rhport);
// Enable/Disable Start-of-frame interrupt. Default is disabled
void dcd_sof_enable(uint8_t rhport, bool en);
+#if CFG_TUD_TEST_MODE
+// Put device into a test mode (needs power cycle to quit)
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector);
+#endif
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
@@ -149,10 +157,6 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc
// required for multiple configuration support.
void dcd_edpt_close_all (uint8_t rhport);
-// Close an endpoint.
-// Since it is weak, caller must TU_ASSERT this function's existence before calling it.
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
-
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
@@ -167,6 +171,20 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
// This API never calls with control endpoints, since it is auto cleared when receiving setup packet
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+// Allocate packet buffer used by ISO endpoints
+// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
+
+// Configure and enable an ISO endpoint according to descriptor
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
+
+#else
+// Close an endpoint.
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr);
+
+#endif
+
//--------------------------------------------------------------------+
// Event API (implemented by stack)
//--------------------------------------------------------------------+
@@ -175,44 +193,46 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
// helper to send bus signal event
-TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
-{
- dcd_event_t event = { .rhport = rhport, .event_id = eid };
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) {
+ dcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = eid;
dcd_event_handler(&event, in_isr);
}
// helper to send bus reset event
-TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
-{
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) {
+ dcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = DCD_EVENT_BUS_RESET;
event.bus_reset.speed = speed;
dcd_event_handler(&event, in_isr);
}
// helper to send setup received
-TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
-{
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
- memcpy(&event.setup_received, setup, 8);
-
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) {
+ dcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = DCD_EVENT_SETUP_RECEIVED;
+ memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
dcd_event_handler(&event, in_isr);
}
// helper to send transfer complete event
-TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
-{
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
-
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) {
+ dcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = DCD_EVENT_XFER_COMPLETE;
event.xfer_complete.ep_addr = ep_addr;
event.xfer_complete.len = xferred_bytes;
event.xfer_complete.result = result;
-
dcd_event_handler(&event, in_isr);
}
-static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr)
-{
- dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SOF };
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) {
+ dcd_event_t event;
+ event.rhport = rhport;
+ event.event_id = DCD_EVENT_SOF;
event.sof.frame_count = frame_count;
dcd_event_handler(&event, in_isr);
}
@@ -221,4 +241,4 @@ static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_i
}
#endif
-#endif /* _TUSB_DCD_H_ */
+#endif
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 1cede1289..20f803563 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -38,25 +38,89 @@
//--------------------------------------------------------------------+
// USBD Configuration
//--------------------------------------------------------------------+
-
-// Debug level of USBD
-#define USBD_DBG 2
-
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void) rhport; (void) eventid; (void) in_isr;
+}
+
+TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) {
+ (void) frame_count;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_bos_cb(void) {
+ return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void) {
+ return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+ return NULL;
+}
+
+TU_ATTR_WEAK void tud_mount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_umount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en) {
+ (void) remote_wakeup_en;
+}
+
+TU_ATTR_WEAK void tud_resume_cb(void) {
+}
+
+TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
+ (void) rhport; (void) stage; (void) request;
+ return false;
+}
+
+TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) {
+ (void) rhport;
+ return false;
+}
+
+TU_ATTR_WEAK void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+}
+
+TU_ATTR_WEAK void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+}
+
+TU_ATTR_WEAK bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
+TU_ATTR_WEAK bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
+TU_ATTR_WEAK bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return true;
+}
+
+//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
// Invalid driver ID in itf2drv[] ep2drv[][] mapping
enum { DRVID_INVALID = 0xFFu };
-typedef struct
-{
- struct TU_ATTR_PACKED
- {
+typedef struct {
+ struct TU_ATTR_PACKED {
volatile uint8_t connected : 1;
volatile uint8_t addressed : 1;
volatile uint8_t suspended : 1;
@@ -65,9 +129,9 @@ typedef struct
uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute
uint8_t self_powered : 1; // configuration descriptor's attribute
};
-
volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
uint8_t speed;
+ volatile uint8_t sof_consumer;
uint8_t itf2drv[CFG_TUD_INTERFACE_MAX]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
@@ -76,208 +140,224 @@ typedef struct
}usbd_device_t;
-static usbd_device_t _usbd_dev;
+tu_static usbd_device_t _usbd_dev;
+static volatile uint8_t _usbd_queued_setup;
//--------------------------------------------------------------------+
// Class Driver
//--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= 2
- #define DRIVER_NAME(_name) .name = _name,
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+ #define DRIVER_NAME(_name) _name
#else
- #define DRIVER_NAME(_name)
+ #define DRIVER_NAME(_name) NULL
#endif
// Built-in class drivers
-static usbd_class_driver_t const _usbd_driver[] =
-{
- #if CFG_TUD_CDC
- {
- DRIVER_NAME("CDC")
- .init = cdcd_init,
- .reset = cdcd_reset,
- .open = cdcd_open,
- .control_xfer_cb = cdcd_control_xfer_cb,
- .xfer_cb = cdcd_xfer_cb,
- .sof = NULL
- },
- #endif
+tu_static usbd_class_driver_t const _usbd_driver[] = {
+ #if CFG_TUD_CDC
+ {
+ .name = DRIVER_NAME("CDC"),
+ .init = cdcd_init,
+ .deinit = cdcd_deinit,
+ .reset = cdcd_reset,
+ .open = cdcd_open,
+ .control_xfer_cb = cdcd_control_xfer_cb,
+ .xfer_cb = cdcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MSC
- {
- DRIVER_NAME("MSC")
- .init = mscd_init,
- .reset = mscd_reset,
- .open = mscd_open,
- .control_xfer_cb = mscd_control_xfer_cb,
- .xfer_cb = mscd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MSC
+ {
+ .name = DRIVER_NAME("MSC"),
+ .init = mscd_init,
+ .deinit = NULL,
+ .reset = mscd_reset,
+ .open = mscd_open,
+ .control_xfer_cb = mscd_control_xfer_cb,
+ .xfer_cb = mscd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_HID
- {
- DRIVER_NAME("HID")
- .init = hidd_init,
- .reset = hidd_reset,
- .open = hidd_open,
- .control_xfer_cb = hidd_control_xfer_cb,
- .xfer_cb = hidd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_HID
+ {
+ .name = DRIVER_NAME("HID"),
+ .init = hidd_init,
+ .deinit = hidd_deinit,
+ .reset = hidd_reset,
+ .open = hidd_open,
+ .control_xfer_cb = hidd_control_xfer_cb,
+ .xfer_cb = hidd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_AUDIO
- {
- DRIVER_NAME("AUDIO")
- .init = audiod_init,
- .reset = audiod_reset,
- .open = audiod_open,
- .control_xfer_cb = audiod_control_xfer_cb,
- .xfer_cb = audiod_xfer_cb,
- .sof = audiod_sof_isr
- },
- #endif
+ #if CFG_TUD_AUDIO
+ {
+ .name = DRIVER_NAME("AUDIO"),
+ .init = audiod_init,
+ .deinit = audiod_deinit,
+ .reset = audiod_reset,
+ .open = audiod_open,
+ .control_xfer_cb = audiod_control_xfer_cb,
+ .xfer_cb = audiod_xfer_cb,
+ .sof = audiod_sof_isr
+ },
+ #endif
- #if CFG_TUD_VIDEO
- {
- DRIVER_NAME("VIDEO")
- .init = videod_init,
- .reset = videod_reset,
- .open = videod_open,
- .control_xfer_cb = videod_control_xfer_cb,
- .xfer_cb = videod_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VIDEO
+ {
+ .name = DRIVER_NAME("VIDEO"),
+ .init = videod_init,
+ .deinit = videod_deinit,
+ .reset = videod_reset,
+ .open = videod_open,
+ .control_xfer_cb = videod_control_xfer_cb,
+ .xfer_cb = videod_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_MIDI
- {
- DRIVER_NAME("MIDI")
- .init = midid_init,
- .open = midid_open,
- .reset = midid_reset,
- .control_xfer_cb = midid_control_xfer_cb,
- .xfer_cb = midid_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_MIDI
+ {
+ .name = DRIVER_NAME("MIDI"),
+ .init = midid_init,
+ .deinit = midid_deinit,
+ .open = midid_open,
+ .reset = midid_reset,
+ .control_xfer_cb = midid_control_xfer_cb,
+ .xfer_cb = midid_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_VENDOR
- {
- DRIVER_NAME("VENDOR")
- .init = vendord_init,
- .reset = vendord_reset,
- .open = vendord_open,
- .control_xfer_cb = tud_vendor_control_xfer_cb,
- .xfer_cb = vendord_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_VENDOR
+ {
+ .name = DRIVER_NAME("VENDOR"),
+ .init = vendord_init,
+ .deinit = vendord_deinit,
+ .reset = vendord_reset,
+ .open = vendord_open,
+ .control_xfer_cb = tud_vendor_control_xfer_cb,
+ .xfer_cb = vendord_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_USBTMC
- {
- DRIVER_NAME("TMC")
- .init = usbtmcd_init_cb,
- .reset = usbtmcd_reset_cb,
- .open = usbtmcd_open_cb,
- .control_xfer_cb = usbtmcd_control_xfer_cb,
- .xfer_cb = usbtmcd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_USBTMC
+ {
+ .name = DRIVER_NAME("TMC"),
+ .init = usbtmcd_init_cb,
+ .deinit = usbtmcd_deinit,
+ .reset = usbtmcd_reset_cb,
+ .open = usbtmcd_open_cb,
+ .control_xfer_cb = usbtmcd_control_xfer_cb,
+ .xfer_cb = usbtmcd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU_RUNTIME
- {
- DRIVER_NAME("DFU-RUNTIME")
- .init = dfu_rtd_init,
- .reset = dfu_rtd_reset,
- .open = dfu_rtd_open,
- .control_xfer_cb = dfu_rtd_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU_RUNTIME
+ {
+ .name = DRIVER_NAME("DFU-RUNTIME"),
+ .init = dfu_rtd_init,
+ .deinit = dfu_rtd_deinit,
+ .reset = dfu_rtd_reset,
+ .open = dfu_rtd_open,
+ .control_xfer_cb = dfu_rtd_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_DFU
- {
- DRIVER_NAME("DFU")
- .init = dfu_moded_init,
- .reset = dfu_moded_reset,
- .open = dfu_moded_open,
- .control_xfer_cb = dfu_moded_control_xfer_cb,
- .xfer_cb = NULL,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_DFU
+ {
+ .name = DRIVER_NAME("DFU"),
+ .init = dfu_moded_init,
+ .deinit = dfu_moded_deinit,
+ .reset = dfu_moded_reset,
+ .open = dfu_moded_open,
+ .control_xfer_cb = dfu_moded_control_xfer_cb,
+ .xfer_cb = NULL,
+ .sof = NULL
+ },
+ #endif
- #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
- {
- DRIVER_NAME("NET")
- .init = netd_init,
- .reset = netd_reset,
- .open = netd_open,
- .control_xfer_cb = netd_control_xfer_cb,
- .xfer_cb = netd_xfer_cb,
- .sof = NULL,
- },
- #endif
+ #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
+ {
+ .name = DRIVER_NAME("NET"),
+ .init = netd_init,
+ .deinit = netd_deinit,
+ .reset = netd_reset,
+ .open = netd_open,
+ .control_xfer_cb = netd_control_xfer_cb,
+ .xfer_cb = netd_xfer_cb,
+ .sof = NULL,
+ },
+ #endif
- #if CFG_TUD_BTH
- {
- DRIVER_NAME("BTH")
- .init = btd_init,
- .reset = btd_reset,
- .open = btd_open,
- .control_xfer_cb = btd_control_xfer_cb,
- .xfer_cb = btd_xfer_cb,
- .sof = NULL
- },
- #endif
+ #if CFG_TUD_BTH
+ {
+ .name = DRIVER_NAME("BTH"),
+ .init = btd_init,
+ .deinit = btd_deinit,
+ .reset = btd_reset,
+ .open = btd_open,
+ .control_xfer_cb = btd_control_xfer_cb,
+ .xfer_cb = btd_xfer_cb,
+ .sof = NULL
+ },
+ #endif
};
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
// Additional class drivers implemented by application
-static usbd_class_driver_t const * _app_driver = NULL;
-static uint8_t _app_driver_count = 0;
+tu_static usbd_class_driver_t const * _app_driver = NULL;
+tu_static uint8_t _app_driver_count = 0;
+
+#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
// virtually joins built-in and application drivers together.
// Application is positioned first to allow overwriting built-in ones.
-static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
-{
- // Application drivers
- if ( usbd_app_driver_get_cb )
- {
- if ( drvid < _app_driver_count ) return &_app_driver[drvid];
- drvid -= _app_driver_count;
+TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8_t drvid) {
+ usbd_class_driver_t const * driver = NULL;
+ if ( drvid < _app_driver_count ) {
+ // Application drivers
+ driver = &_app_driver[drvid];
+ } else if ( drvid < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0 ){
+ driver = &_usbd_driver[drvid - _app_driver_count];
}
-
- // Built-in drivers
- if (drvid < BUILTIN_DRIVER_COUNT) return &_usbd_driver[drvid];
-
- return NULL;
+ return driver;
}
-#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
//--------------------------------------------------------------------+
// DCD Event
//--------------------------------------------------------------------+
enum { RHPORT_INVALID = 0xFFu };
-static uint8_t _usbd_rhport = RHPORT_INVALID;
+tu_static uint8_t _usbd_rhport = RHPORT_INVALID;
// Event queue
// usbd_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
-static osal_queue_t _usbd_q;
+tu_static osal_queue_t _usbd_q;
-// Mutex for claiming endpoint, only needed when using with preempted RTOS
-#if CFG_TUSB_OS != OPT_OS_NONE
-static osal_mutex_def_t _ubsd_mutexdef;
-static osal_mutex_t _usbd_mutex;
+// Mutex for claiming endpoint
+#if OSAL_MUTEX_REQUIRED
+ tu_static osal_mutex_def_t _ubsd_mutexdef;
+ tu_static osal_mutex_t _usbd_mutex;
+#else
+ #define _usbd_mutex NULL
#endif
+TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
+ TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr));
+ tud_event_hook_cb(event->rhport, event->event_id, in_isr);
+ return true;
+}
//--------------------------------------------------------------------+
// Prototypes
@@ -286,6 +366,16 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
+#if CFG_TUD_TEST_MODE
+static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+ TU_VERIFY(CONTROL_STAGE_ACK == stage);
+ uint8_t const selector = tu_u16_high(request->wIndex);
+ TU_LOG_USBD(" Enter Test Mode (test selector index: %d)\r\n", selector);
+ dcd_enter_test_mode(rhport, (tusb_feature_test_mode_t) selector);
+ return true;
+}
+#endif
+
// from usbd_control.c
void usbd_control_reset(void);
void usbd_control_set_request(tusb_control_request_t const *request);
@@ -296,29 +386,25 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event,
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= 2
-static char const* const _usbd_event_str[DCD_EVENT_COUNT] =
-{
- "Invalid" ,
- "Bus Reset" ,
- "Unplugged" ,
- "SOF" ,
- "Suspend" ,
- "Resume" ,
- "Setup Received" ,
- "Xfer Complete" ,
- "Func Call"
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+tu_static char const* const _usbd_event_str[DCD_EVENT_COUNT] = {
+ "Invalid",
+ "Bus Reset",
+ "Unplugged",
+ "SOF",
+ "Suspend",
+ "Resume",
+ "Setup Received",
+ "Xfer Complete",
+ "Func Call"
};
// for usbd_control to print the name of control complete driver
-void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
-{
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
- {
- usbd_class_driver_t const * driver = get_driver(i);
- if ( driver && driver->control_xfer_cb == callback )
- {
- TU_LOG(USBD_DBG, " %s control complete\r\n", driver->name);
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback) {
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if (driver && driver->control_xfer_cb == callback) {
+ TU_LOG_USBD("%s control complete\r\n", driver->name);
return;
}
}
@@ -329,67 +415,67 @@ void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback)
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
-tusb_speed_t tud_speed_get(void)
-{
+tusb_speed_t tud_speed_get(void) {
return (tusb_speed_t) _usbd_dev.speed;
}
-bool tud_connected(void)
-{
+bool tud_connected(void) {
return _usbd_dev.connected;
}
-bool tud_mounted(void)
-{
+bool tud_mounted(void) {
return _usbd_dev.cfg_num ? true : false;
}
-bool tud_suspended(void)
-{
+bool tud_suspended(void) {
return _usbd_dev.suspended;
}
-bool tud_remote_wakeup(void)
-{
+bool tud_remote_wakeup(void) {
// only wake up host if this feature is supported and enabled and we are suspended
- TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en );
+ TU_VERIFY (_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
dcd_remote_wakeup(_usbd_rhport);
return true;
}
-bool tud_disconnect(void)
-{
- TU_VERIFY(dcd_disconnect);
+bool tud_disconnect(void) {
dcd_disconnect(_usbd_rhport);
return true;
}
-bool tud_connect(void)
-{
- TU_VERIFY(dcd_connect);
+bool tud_connect(void) {
dcd_connect(_usbd_rhport);
return true;
}
+void tud_sof_cb_enable(bool en) {
+ usbd_sof_enable(_usbd_rhport, SOF_CONSUMER_USER, en);
+}
+
//--------------------------------------------------------------------+
// USBD Task
//--------------------------------------------------------------------+
-bool tud_inited(void)
-{
+bool tud_inited(void) {
return _usbd_rhport != RHPORT_INVALID;
}
-bool tud_init (uint8_t rhport)
-{
- // skip if already initialized
- if ( tud_inited() ) return true;
+bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ if (tud_inited()) {
+ return true; // skip if already initialized
+ }
+ TU_ASSERT(rh_init);
- TU_LOG(USBD_DBG, "USBD init on controller %u\r\n", rhport);
- TU_LOG_INT(USBD_DBG, sizeof(usbd_device_t));
+ TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport,
+ rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
+ TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
+ TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
+ TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
+ TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t));
tu_varclr(&_usbd_dev);
+ _usbd_queued_setup = 0;
-#if CFG_TUSB_OS != OPT_OS_NONE
+#if OSAL_MUTEX_REQUIRED
// Init device mutex
_usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
TU_ASSERT(_usbd_mutex);
@@ -400,54 +486,83 @@ bool tud_init (uint8_t rhport)
TU_ASSERT(_usbd_q);
// Get application driver if available
- if ( usbd_app_driver_get_cb )
- {
+ if (usbd_app_driver_get_cb) {
_app_driver = usbd_app_driver_get_cb(&_app_driver_count);
}
// Init class drivers
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
- {
- usbd_class_driver_t const * driver = get_driver(i);
- TU_ASSERT(driver);
- TU_LOG(USBD_DBG, "%s init\r\n", driver->name);
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ TU_ASSERT(driver && driver->init);
+ TU_LOG_USBD("%s init\r\n", driver->name);
driver->init();
}
_usbd_rhport = rhport;
// Init device controller driver
- dcd_init(rhport);
+ TU_ASSERT(dcd_init(rhport, rh_init));
dcd_int_enable(rhport);
return true;
}
-static void configuration_reset(uint8_t rhport)
-{
- for ( uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++ )
- {
- usbd_class_driver_t const * driver = get_driver(i);
- TU_ASSERT(driver, );
+bool tud_deinit(uint8_t rhport) {
+ if (!tud_inited()) {
+ return true; // skip if not initialized
+ }
+
+ TU_LOG_USBD("USBD deinit on controller %u\r\n", rhport);
+
+ // Deinit device controller driver
+ dcd_int_disable(rhport);
+ dcd_disconnect(rhport);
+ dcd_deinit(rhport);
+
+ // Deinit class drivers
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if(driver && driver->deinit) {
+ TU_LOG_USBD("%s deinit\r\n", driver->name);
+ driver->deinit();
+ }
+ }
+
+ // Deinit device queue & task
+ osal_queue_delete(_usbd_q);
+ _usbd_q = NULL;
+
+#if OSAL_MUTEX_REQUIRED
+ // TODO make sure there is no task waiting on this mutex
+ osal_mutex_delete(_usbd_mutex);
+ _usbd_mutex = NULL;
+#endif
+
+ _usbd_rhport = RHPORT_INVALID;
+
+ return true;
+}
+
+static void configuration_reset(uint8_t rhport) {
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ TU_ASSERT(driver,);
driver->reset(rhport);
}
tu_varclr(&_usbd_dev);
memset(_usbd_dev.itf2drv, DRVID_INVALID, sizeof(_usbd_dev.itf2drv)); // invalid mapping
- memset(_usbd_dev.ep2drv , DRVID_INVALID, sizeof(_usbd_dev.ep2drv )); // invalid mapping
+ memset(_usbd_dev.ep2drv, DRVID_INVALID, sizeof(_usbd_dev.ep2drv)); // invalid mapping
}
-static void usbd_reset(uint8_t rhport)
-{
+static void usbd_reset(uint8_t rhport) {
configuration_reset(rhport);
usbd_control_reset();
}
-bool tud_task_event_ready(void)
-{
+bool tud_task_event_ready(void) {
// Skip if stack is not initialized
- if ( !tusb_inited() ) return false;
-
+ if (!tud_inited()) return false;
return !osal_queue_empty(_usbd_q);
}
@@ -455,139 +570,133 @@ bool tud_task_event_ready(void)
* This top level thread manages all device controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
*
- @code
- int main(void)
- {
+ int main(void) {
application_init();
- tusb_init();
+ tusb_init(0, TUSB_ROLE_DEVICE);
- while(1) // the mainloop
- {
+ while(1) { // the mainloop
application_code();
tud_task(); // tinyusb device task
}
}
- @endcode
*/
-void tud_task_ext(uint32_t timeout_ms, bool in_isr)
-{
+void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
- if ( !tusb_inited() ) return;
+ if (!tud_inited()) return;
// Loop until there is no more events in the queue
- while (1)
- {
+ while (1) {
dcd_event_t event;
- if ( !osal_queue_receive(_usbd_q, &event, timeout_ms) ) return;
+ if (!osal_queue_receive(_usbd_q, &event, timeout_ms)) return;
-#if CFG_TUSB_DEBUG >= 2
- if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG(USBD_DBG, "\r\n"); // extra line for setup
- TU_LOG(USBD_DBG, "USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+ if (event.event_id == DCD_EVENT_SETUP_RECEIVED) TU_LOG_USBD("\r\n"); // extra line for setup
+ TU_LOG_USBD("USBD %s ", event.event_id < DCD_EVENT_COUNT ? _usbd_event_str[event.event_id] : "CORRUPTED");
#endif
- switch ( event.event_id )
- {
+ switch (event.event_id) {
case DCD_EVENT_BUS_RESET:
- TU_LOG(USBD_DBG, ": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]);
+ TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]);
usbd_reset(event.rhport);
_usbd_dev.speed = event.bus_reset.speed;
- break;
+ break;
case DCD_EVENT_UNPLUGGED:
- TU_LOG(USBD_DBG, "\r\n");
+ TU_LOG_USBD("\r\n");
usbd_reset(event.rhport);
-
- // invoke callback
- if (tud_umount_cb) tud_umount_cb();
- break;
+ tud_umount_cb();
+ break;
case DCD_EVENT_SETUP_RECEIVED:
- TU_LOG_VAR(USBD_DBG, &event.setup_received);
- TU_LOG(USBD_DBG, "\r\n");
+ TU_ASSERT(_usbd_queued_setup > 0,);
+ _usbd_queued_setup--;
+ TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8);
+ if (_usbd_queued_setup) {
+ TU_LOG_USBD(" Skipped since there is other SETUP in queue\r\n");
+ break;
+ }
// Mark as connected after receiving 1st setup packet.
// But it is easier to set it every time instead of wasting time to check then set
_usbd_dev.connected = 1;
// mark both in & out control as free
- _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = false;
+ _usbd_dev.ep_status[0][TUSB_DIR_OUT].busy = 0;
_usbd_dev.ep_status[0][TUSB_DIR_OUT].claimed = 0;
- _usbd_dev.ep_status[0][TUSB_DIR_IN ].busy = false;
- _usbd_dev.ep_status[0][TUSB_DIR_IN ].claimed = 0;
+ _usbd_dev.ep_status[0][TUSB_DIR_IN].busy = 0;
+ _usbd_dev.ep_status[0][TUSB_DIR_IN].claimed = 0;
// Process control request
- if ( !process_control_request(event.rhport, &event.setup_received) )
- {
- TU_LOG(USBD_DBG, " Stall EP0\r\n");
+ if (!process_control_request(event.rhport, &event.setup_received)) {
+ TU_LOG_USBD(" Stall EP0\r\n");
// Failed -> stall both control endpoint IN and OUT
dcd_edpt_stall(event.rhport, 0);
dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK);
}
- break;
+ break;
- case DCD_EVENT_XFER_COMPLETE:
- {
+ case DCD_EVENT_XFER_COMPLETE: {
// Invoke the class callback associated with the endpoint address
uint8_t const ep_addr = event.xfer_complete.ep_addr;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = tu_edpt_dir(ep_addr);
- TU_LOG(USBD_DBG, "on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
+ TU_LOG_USBD("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
- _usbd_dev.ep_status[epnum][ep_dir].busy = false;
+ _usbd_dev.ep_status[epnum][ep_dir].busy = 0;
_usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
- if ( 0 == epnum )
- {
- usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
- }
- else
- {
- usbd_class_driver_t const * driver = get_driver( _usbd_dev.ep2drv[epnum][ep_dir] );
- TU_ASSERT(driver, );
+ if (0 == epnum) {
+ usbd_control_xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result,
+ event.xfer_complete.len);
+ } else {
+ usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]);
+ TU_ASSERT(driver,);
- TU_LOG(USBD_DBG, " %s xfer callback\r\n", driver->name);
- driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
+ TU_LOG_USBD(" %s xfer callback\r\n", driver->name);
+ driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
}
+ break;
}
- break;
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event
// e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
- if ( _usbd_dev.connected )
- {
- TU_LOG(USBD_DBG, ": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
- if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
- }else
- {
- TU_LOG(USBD_DBG, " Skipped\r\n");
+ if (_usbd_dev.connected) {
+ TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
+ tud_suspend_cb(_usbd_dev.remote_wakeup_en);
+ } else {
+ TU_LOG_USBD(" Skipped\r\n");
}
- break;
+ break;
case DCD_EVENT_RESUME:
- if ( _usbd_dev.connected )
- {
- TU_LOG(USBD_DBG, "\r\n");
- if (tud_resume_cb) tud_resume_cb();
- }else
- {
- TU_LOG(USBD_DBG, " Skipped\r\n");
+ if (_usbd_dev.connected) {
+ TU_LOG_USBD("\r\n");
+ tud_resume_cb();
+ } else {
+ TU_LOG_USBD(" Skipped\r\n");
}
- break;
+ break;
case USBD_EVENT_FUNC_CALL:
- TU_LOG(USBD_DBG, "\r\n");
- if ( event.func_call.func ) event.func_call.func(event.func_call.param);
- break;
+ TU_LOG_USBD("\r\n");
+ if (event.func_call.func) event.func_call.func(event.func_call.param);
+ break;
case DCD_EVENT_SOF:
+ if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+ TU_LOG_USBD("\r\n");
+ tud_sof_cb(event.sof.frame_count);
+ }
+ break;
+
default:
TU_BREAKPOINT();
- break;
+ break;
}
#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
@@ -602,44 +711,35 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr)
//--------------------------------------------------------------------+
// Helper to invoke class driver control request handler
-static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request)
-{
+static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * driver, tusb_control_request_t const * request) {
usbd_control_set_complete_callback(driver->control_xfer_cb);
- TU_LOG(USBD_DBG, " %s control request\r\n", driver->name);
+ TU_LOG_USBD(" %s control request\r\n", driver->name);
return driver->control_xfer_cb(rhport, CONTROL_STAGE_SETUP, request);
}
// This handles the actual request and its response.
-// return false will cause its caller to stall control endpoint
-static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
-{
+// Returns false if unable to complete the request, causing caller to stall control endpoints.
+static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) {
usbd_control_set_complete_callback(NULL);
-
TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID);
// Vendor request
- if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR )
- {
- TU_VERIFY(tud_vendor_control_xfer_cb);
-
+ if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) {
usbd_control_set_complete_callback(tud_vendor_control_xfer_cb);
return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request);
}
-#if CFG_TUSB_DEBUG >= 2
- if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME)
- {
- TU_LOG(USBD_DBG, " %s", tu_str_std_request[p_request->bRequest]);
- if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG(USBD_DBG, "\r\n");
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+ if (TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type && p_request->bRequest <= TUSB_REQ_SYNCH_FRAME) {
+ TU_LOG_USBD(" %s", tu_str_std_request[p_request->bRequest]);
+ if (TUSB_REQ_GET_DESCRIPTOR != p_request->bRequest) TU_LOG_USBD("\r\n");
}
#endif
- switch ( p_request->bmRequestType_bit.recipient )
- {
+ switch ( p_request->bmRequestType_bit.recipient ) {
//------------- Device Requests e.g in enumeration -------------//
case TUSB_REQ_RCPT_DEVICE:
- if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type )
- {
+ if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) {
uint8_t const itf = tu_u16_low(p_request->wIndex);
TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
@@ -650,15 +750,13 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
return invoke_class_control(rhport, driver, p_request);
}
- if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
- {
- // Non standard request is not supported
+ if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
+ // Non-standard request is not supported
TU_BREAKPOINT();
return false;
}
- switch ( p_request->bRequest )
- {
+ switch ( p_request->bRequest ) {
case TUSB_REQ_SET_ADDRESS:
// Depending on mcu, status phase could be sent either before or after changing device address,
// or even require stack to not response with status at all
@@ -669,24 +767,23 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
_usbd_dev.addressed = 1;
break;
- case TUSB_REQ_GET_CONFIGURATION:
- {
+ case TUSB_REQ_GET_CONFIGURATION: {
uint8_t cfg_num = _usbd_dev.cfg_num;
tud_control_xfer(rhport, p_request, &cfg_num, 1);
}
break;
- case TUSB_REQ_SET_CONFIGURATION:
- {
+ case TUSB_REQ_SET_CONFIGURATION: {
uint8_t const cfg_num = (uint8_t) p_request->wValue;
// Only process if new configure is different
- if (_usbd_dev.cfg_num != cfg_num)
- {
- if ( _usbd_dev.cfg_num )
- {
+ if (_usbd_dev.cfg_num != cfg_num) {
+ if ( _usbd_dev.cfg_num ) {
// already configured: need to clear all endpoints and driver first
- TU_LOG(USBD_DBG, " Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
+ TU_LOG_USBD(" Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
+
+ // disable SOF
+ dcd_sof_enable(rhport, false);
// close all non-control endpoints, cancel all pending transfers if any
dcd_edpt_close_all(rhport);
@@ -698,11 +795,23 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
_usbd_dev.speed = speed; // restore speed
}
- // switch to new configuration if not zero
- if ( cfg_num ) TU_ASSERT( process_set_config(rhport, cfg_num) );
+ _usbd_dev.cfg_num = cfg_num;
+
+ // Handle the new configuration and execute the corresponding callback
+ if ( cfg_num ) {
+ // switch to new configuration if not zero
+ if (!process_set_config(rhport, cfg_num)) {
+ TU_MESS_FAILED();
+ TU_BREAKPOINT();
+ _usbd_dev.cfg_num = 0;
+ return false;
+ }
+ tud_mount_cb();
+ } else {
+ tud_umount_cb();
+ }
}
- _usbd_dev.cfg_num = cfg_num;
tud_control_status(rhport, p_request);
}
break;
@@ -712,36 +821,52 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
break;
case TUSB_REQ_SET_FEATURE:
- // Only support remote wakeup for device feature
- TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+ switch(p_request->wValue) {
+ case TUSB_REQ_FEATURE_REMOTE_WAKEUP:
+ TU_LOG_USBD(" Enable Remote Wakeup\r\n");
+ // Host may enable remote wake up before suspending especially HID device
+ _usbd_dev.remote_wakeup_en = true;
+ tud_control_status(rhport, p_request);
+ break;
- TU_LOG(USBD_DBG, " Enable Remote Wakeup\r\n");
+ #if CFG_TUD_TEST_MODE
+ case TUSB_REQ_FEATURE_TEST_MODE: {
+ // Only handle the test mode if supported and valid
+ TU_VERIFY(0 == tu_u16_low(p_request->wIndex));
- // Host may enable remote wake up before suspending especially HID device
- _usbd_dev.remote_wakeup_en = true;
- tud_control_status(rhport, p_request);
+ uint8_t const selector = tu_u16_high(p_request->wIndex);
+ TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE);
+
+ usbd_control_set_complete_callback(process_test_mode_cb);
+ tud_control_status(rhport, p_request);
+ break;
+ }
+ #endif /* CFG_TUD_TEST_MODE */
+
+ // Stall unsupported feature selector
+ default: return false;
+ }
break;
case TUSB_REQ_CLEAR_FEATURE:
// Only support remote wakeup for device feature
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
- TU_LOG(USBD_DBG, " Disable Remote Wakeup\r\n");
+ TU_LOG_USBD(" Disable Remote Wakeup\r\n");
// Host may disable remote wake up after resuming
_usbd_dev.remote_wakeup_en = false;
tud_control_status(rhport, p_request);
break;
- case TUSB_REQ_GET_STATUS:
- {
+ case TUSB_REQ_GET_STATUS: {
// Device status bit mask
// - Bit 0: Self Powered
// - Bit 1: Remote Wakeup enabled
uint16_t status = (uint16_t) ((_usbd_dev.self_powered ? 1u : 0u) | (_usbd_dev.remote_wakeup_en ? 2u : 0u));
tud_control_xfer(rhport, p_request, &status, 2);
+ break;
}
- break;
// Unknown/Unsupported request
default: TU_BREAKPOINT(); return false;
@@ -749,8 +874,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
break;
//------------- Class/Interface Specific Request -------------//
- case TUSB_REQ_RCPT_INTERFACE:
- {
+ case TUSB_REQ_RCPT_INTERFACE: {
uint8_t const itf = tu_u16_low(p_request->wIndex);
TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv));
@@ -759,25 +883,21 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// all requests to Interface (STD or Class) is forwarded to class driver.
// notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE
- if ( !invoke_class_control(rhport, driver, p_request) )
- {
+ if ( !invoke_class_control(rhport, driver, p_request) ) {
// For GET_INTERFACE and SET_INTERFACE, it is mandatory to respond even if the class
// driver doesn't use alternate settings or implement this
TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
- switch(p_request->bRequest)
- {
+ switch(p_request->bRequest) {
case TUSB_REQ_GET_INTERFACE:
case TUSB_REQ_SET_INTERFACE:
// Clear complete callback if driver set since it can also stall the request.
usbd_control_set_complete_callback(NULL);
- if (TUSB_REQ_GET_INTERFACE == p_request->bRequest)
- {
+ if (TUSB_REQ_GET_INTERFACE == p_request->bRequest) {
uint8_t alternate = 0;
tud_control_xfer(rhport, p_request, &alternate, 1);
- }else
- {
+ }else {
tud_control_status(rhport, p_request);
}
break;
@@ -785,54 +905,42 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
default: return false;
}
}
+ break;
}
- break;
//------------- Endpoint Request -------------//
- case TUSB_REQ_RCPT_ENDPOINT:
- {
+ case TUSB_REQ_RCPT_ENDPOINT: {
uint8_t const ep_addr = tu_u16_low(p_request->wIndex);
uint8_t const ep_num = tu_edpt_number(ep_addr);
uint8_t const ep_dir = tu_edpt_dir(ep_addr);
TU_ASSERT(ep_num < TU_ARRAY_SIZE(_usbd_dev.ep2drv) );
-
usbd_class_driver_t const * driver = get_driver(_usbd_dev.ep2drv[ep_num][ep_dir]);
- if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type )
- {
+ if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
// Forward class request to its driver
TU_VERIFY(driver);
return invoke_class_control(rhport, driver, p_request);
- }
- else
- {
+ } else {
// Handle STD request to endpoint
- switch ( p_request->bRequest )
- {
- case TUSB_REQ_GET_STATUS:
- {
+ switch ( p_request->bRequest ) {
+ case TUSB_REQ_GET_STATUS: {
uint16_t status = usbd_edpt_stalled(rhport, ep_addr) ? 0x0001 : 0x0000;
tud_control_xfer(rhport, p_request, &status, 2);
}
break;
case TUSB_REQ_CLEAR_FEATURE:
- case TUSB_REQ_SET_FEATURE:
- {
- if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue )
- {
- if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest )
- {
+ case TUSB_REQ_SET_FEATURE: {
+ if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue ) {
+ if ( TUSB_REQ_CLEAR_FEATURE == p_request->bRequest ) {
usbd_edpt_clear_stall(rhport, ep_addr);
- }else
- {
+ }else {
usbd_edpt_stall(rhport, ep_addr);
}
}
- if (driver)
- {
+ if (driver) {
// Some classes such as USBTMC needs to clear/re-init its buffer when receiving CLEAR_FEATURE request
// We will also forward std request targeted endpoint to class drivers as well
@@ -848,14 +956,18 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
break;
// Unknown/Unsupported request
- default: TU_BREAKPOINT(); return false;
+ default:
+ TU_BREAKPOINT();
+ return false;
}
}
}
break;
// Unknown recipient
- default: TU_BREAKPOINT(); return false;
+ default:
+ TU_BREAKPOINT();
+ return false;
}
return true;
@@ -909,7 +1021,7 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
if ( (sizeof(tusb_desc_interface_t) <= drv_len) && (drv_len <= remaining_len) )
{
// Open successfully
- TU_LOG(USBD_DBG, " %s opened\r\n", driver->name);
+ TU_LOG_USBD(" %s opened\r\n", driver->name);
// Some drivers use 2 or more interfaces but may not have IAD e.g MIDI (always) or
// BTH (even CDC) with class in device descriptor (single interface)
@@ -960,9 +1072,6 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
TU_ASSERT(drv_id < TOTAL_DRIVER_COUNT);
}
- // invoke callback
- if (tud_mount_cb) tud_mount_cb();
-
return true;
}
@@ -974,39 +1083,34 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
switch(desc_type)
{
- case TUSB_DESC_DEVICE:
- {
- TU_LOG(USBD_DBG, " Device\r\n");
+ case TUSB_DESC_DEVICE: {
+ TU_LOG_USBD(" Device\r\n");
void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb();
+ TU_ASSERT(desc_device);
// Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has.
// This only happens with the very first get device descriptor and EP0 size = 8 or 16.
if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed &&
- ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t))
- {
+ ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) {
// Hack here: we modify the request length to prevent usbd_control response with zlp
// since we are responding with 1 packet & less data than wLength.
tusb_control_request_t mod_request = *p_request;
mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE;
return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE);
- }else
- {
+ }else {
return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t));
}
}
// break; // unreachable
- case TUSB_DESC_BOS:
- {
- TU_LOG(USBD_DBG, " BOS\r\n");
+ case TUSB_DESC_BOS: {
+ TU_LOG_USBD(" BOS\r\n");
// requested by host if USB > 2.0 ( i.e 2.1 or 3.x )
- if (!tud_descriptor_bos_cb) return false;
-
uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb();
- TU_ASSERT(desc_bos);
+ TU_VERIFY(desc_bos);
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) );
@@ -1016,24 +1120,20 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
// break; // unreachable
case TUSB_DESC_CONFIGURATION:
- case TUSB_DESC_OTHER_SPEED_CONFIG:
- {
+ case TUSB_DESC_OTHER_SPEED_CONFIG: {
uintptr_t desc_config;
- if ( desc_type == TUSB_DESC_CONFIGURATION )
- {
- TU_LOG(USBD_DBG, " Configuration[%u]\r\n", desc_index);
+ if ( desc_type == TUSB_DESC_CONFIGURATION ) {
+ TU_LOG_USBD(" Configuration[%u]\r\n", desc_index);
desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index);
- }else
- {
+ TU_ASSERT(desc_config);
+ }else {
// Host only request this after getting Device Qualifier descriptor
- TU_LOG(USBD_DBG, " Other Speed Configuration\r\n");
- TU_VERIFY( tud_descriptor_other_speed_configuration_cb );
+ TU_LOG_USBD(" Other Speed Configuration\r\n");
desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index);
+ TU_VERIFY(desc_config);
}
- TU_ASSERT(desc_config);
-
// Use offsetof to avoid pointer to the odd/misaligned address
uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) );
@@ -1043,7 +1143,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
case TUSB_DESC_STRING:
{
- TU_LOG(USBD_DBG, " String[%u]\r\n", desc_index);
+ TU_LOG_USBD(" String[%u]\r\n", desc_index);
// String Descriptor always uses the desc set from user
uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, tu_le16toh(p_request->wIndex));
@@ -1054,16 +1154,10 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
}
// break; // unreachable
- case TUSB_DESC_DEVICE_QUALIFIER:
- {
- TU_LOG(USBD_DBG, " Device Qualifier\r\n");
-
- TU_VERIFY( tud_descriptor_device_qualifier_cb );
-
+ case TUSB_DESC_DEVICE_QUALIFIER: {
+ TU_LOG_USBD(" Device Qualifier\r\n");
uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb();
TU_VERIFY(desc_qualifier);
-
- // first byte of descriptor is its size
return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier));
}
// break; // unreachable
@@ -1075,66 +1169,72 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
//--------------------------------------------------------------------+
// DCD Event Handler
//--------------------------------------------------------------------+
-TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const * event, bool in_isr)
-{
- switch (event->event_id)
- {
+TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr) {
+ bool send = false;
+ switch (event->event_id) {
case DCD_EVENT_UNPLUGGED:
- _usbd_dev.connected = 0;
- _usbd_dev.addressed = 0;
- _usbd_dev.cfg_num = 0;
- _usbd_dev.suspended = 0;
- osal_queue_send(_usbd_q, event, in_isr);
- break;
+ _usbd_dev.connected = 0;
+ _usbd_dev.addressed = 0;
+ _usbd_dev.cfg_num = 0;
+ _usbd_dev.suspended = 0;
+ send = true;
+ break;
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ).
// In addition, some MCUs such as SAMD or boards that haven no VBUS detection cannot distinguish
// suspended vs disconnected. We will skip handling SUSPEND/RESUME event if not currently connected
- if ( _usbd_dev.connected )
- {
+ if (_usbd_dev.connected) {
_usbd_dev.suspended = 1;
- osal_queue_send(_usbd_q, event, in_isr);
+ send = true;
}
- break;
+ break;
case DCD_EVENT_RESUME:
// skip event if not connected (especially required for SAMD)
- if ( _usbd_dev.connected )
- {
+ if (_usbd_dev.connected) {
_usbd_dev.suspended = 0;
- osal_queue_send(_usbd_q, event, in_isr);
+ send = true;
}
- break;
+ break;
case DCD_EVENT_SOF:
// SOF driver handler in ISR context
- for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++)
- {
- usbd_class_driver_t const * driver = get_driver(i);
- if (driver && driver->sof)
- {
+ for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+ usbd_class_driver_t const* driver = get_driver(i);
+ if (driver && driver->sof) {
driver->sof(event->rhport, event->sof.frame_count);
}
}
// Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
// which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
- if ( _usbd_dev.suspended )
- {
+ if (_usbd_dev.suspended) {
_usbd_dev.suspended = 0;
- dcd_event_t const event_resume = { .rhport = event->rhport, .event_id = DCD_EVENT_RESUME };
- osal_queue_send(_usbd_q, &event_resume, in_isr);
+ dcd_event_t const event_resume = {.rhport = event->rhport, .event_id = DCD_EVENT_RESUME};
+ queue_event(&event_resume, in_isr);
}
- // skip osal queue for SOF in usbd task
- break;
+ if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+ dcd_event_t const event_sof = {.rhport = event->rhport, .event_id = DCD_EVENT_SOF, .sof.frame_count = event->sof.frame_count};
+ queue_event(&event_sof, in_isr);
+ }
+ break;
+
+ case DCD_EVENT_SETUP_RECEIVED:
+ _usbd_queued_setup++;
+ send = true;
+ break;
default:
- osal_queue_send(_usbd_q, event, in_isr);
- break;
+ send = true;
+ break;
+ }
+
+ if (send) {
+ queue_event(event, in_isr);
}
}
@@ -1178,26 +1278,22 @@ bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count
}
// Helper to defer an isr function
-void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr)
-{
- dcd_event_t event =
- {
+void usbd_defer_func(osal_task_func_t func, void* param, bool in_isr) {
+ dcd_event_t event = {
.rhport = 0,
.event_id = USBD_EVENT_FUNC_CALL,
};
-
event.func_call.func = func;
event.func_call.param = param;
- dcd_event_handler(&event, in_isr);
+ queue_event(&event, in_isr);
}
//--------------------------------------------------------------------+
// USBD Endpoint API
//--------------------------------------------------------------------+
-bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
-{
+bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
rhport = _usbd_rhport;
TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX);
@@ -1206,67 +1302,59 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
return dcd_edpt_open(rhport, desc_ep);
}
-bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr)
-{
+bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
// TODO add this check later, also make sure we don't starve an out endpoint while suspending
// TU_VERIFY(tud_ready());
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir];
-#if TUSB_OPT_MUTEX
return tu_edpt_claim(ep_state, _usbd_mutex);
-#else
- return tu_edpt_claim(ep_state, NULL);
-#endif
}
-bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr)
-{
+bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
tu_edpt_state_t* ep_state = &_usbd_dev.ep_status[epnum][dir];
-#if TUSB_OPT_MUTEX
return tu_edpt_release(ep_state, _usbd_mutex);
-#else
- return tu_edpt_release(ep_state, NULL);
-#endif
}
-bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
rhport = _usbd_rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
// TODO skip ready() check for now since enumeration also use this API
// TU_VERIFY(tud_ready());
- TU_LOG(USBD_DBG, " Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes);
+ TU_LOG_USBD(" Queue EP %02X with %u bytes ...\r\n", ep_addr, total_bytes);
+#if CFG_TUD_LOG_LEVEL >= 3
+ if(dir == TUSB_DIR_IN) {
+ TU_LOG_MEM(CFG_TUD_LOG_LEVEL, buffer, total_bytes, 2);
+ }
+#endif
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer()
// could return and USBD task can preempt and clear the busy
- _usbd_dev.ep_status[epnum][dir].busy = true;
+ _usbd_dev.ep_status[epnum][dir].busy = 1;
- if ( dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes) )
- {
+ if (dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes)) {
return true;
- }else
- {
+ } else {
// DCD error, mark endpoint as ready to allow next transfer
- _usbd_dev.ep_status[epnum][dir].busy = false;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
_usbd_dev.ep_status[epnum][dir].claimed = 0;
- TU_LOG(USBD_DBG, "FAILED\r\n");
+ TU_LOG_USBD("FAILED\r\n");
TU_BREAKPOINT();
return false;
}
@@ -1276,122 +1364,150 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
-bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
+bool usbd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) {
rhport = _usbd_rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- TU_LOG(USBD_DBG, " Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes);
+ TU_LOG_USBD(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes);
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
// and usbd task can preempt and clear the busy
- _usbd_dev.ep_status[epnum][dir].busy = true;
+ _usbd_dev.ep_status[epnum][dir].busy = 1;
- if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes))
- {
- TU_LOG(USBD_DBG, "OK\r\n");
+ if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes)) {
+ TU_LOG_USBD("OK\r\n");
return true;
- }else
- {
+ } else {
// DCD error, mark endpoint as ready to allow next transfer
- _usbd_dev.ep_status[epnum][dir].busy = false;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
_usbd_dev.ep_status[epnum][dir].claimed = 0;
- TU_LOG(USBD_DBG, "failed\r\n");
+ TU_LOG_USBD("failed\r\n");
TU_BREAKPOINT();
return false;
}
}
-bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
-{
+bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
return _usbd_dev.ep_status[epnum][dir].busy;
}
-void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
+void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
rhport = _usbd_rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
// only stalled if currently cleared
- if ( !_usbd_dev.ep_status[epnum][dir].stalled )
- {
- TU_LOG(USBD_DBG, " Stall EP %02X\r\n", ep_addr);
- dcd_edpt_stall(rhport, ep_addr);
- _usbd_dev.ep_status[epnum][dir].stalled = true;
- _usbd_dev.ep_status[epnum][dir].busy = true;
- }
+ TU_LOG_USBD(" Stall EP %02X\r\n", ep_addr);
+ dcd_edpt_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = 1;
+ _usbd_dev.ep_status[epnum][dir].busy = 1;
}
-void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
+void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
rhport = _usbd_rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
// only clear if currently stalled
- if ( _usbd_dev.ep_status[epnum][dir].stalled )
- {
- TU_LOG(USBD_DBG, " Clear Stall EP %02X\r\n", ep_addr);
- dcd_edpt_clear_stall(rhport, ep_addr);
- _usbd_dev.ep_status[epnum][dir].stalled = false;
- _usbd_dev.ep_status[epnum][dir].busy = false;
- }
+ TU_LOG_USBD(" Clear Stall EP %02X\r\n", ep_addr);
+ dcd_edpt_clear_stall(rhport, ep_addr);
+ _usbd_dev.ep_status[epnum][dir].stalled = 0;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
}
-bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
-{
+bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
return _usbd_dev.ep_status[epnum][dir].stalled;
}
/**
* usbd_edpt_close will disable an endpoint.
- *
* In progress transfers on this EP may be delivered after this call.
- *
*/
-void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
+void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ (void) rhport; (void) ep_addr;
+ // ISO alloc/activate Should be used instead
+#else
rhport = _usbd_rhport;
- TU_ASSERT(dcd_edpt_close, /**/);
- TU_LOG(USBD_DBG, " CLOSING Endpoint: 0x%02X\r\n", ep_addr);
+ TU_LOG_USBD(" CLOSING Endpoint: 0x%02X\r\n", ep_addr);
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_edpt_close(rhport, ep_addr);
- _usbd_dev.ep_status[epnum][dir].stalled = false;
- _usbd_dev.ep_status[epnum][dir].busy = false;
- _usbd_dev.ep_status[epnum][dir].claimed = false;
+ _usbd_dev.ep_status[epnum][dir].stalled = 0;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+#endif
return;
}
-void usbd_sof_enable(uint8_t rhport, bool en)
-{
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) {
rhport = _usbd_rhport;
- // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more.
- // Only if all drivers switched off SOF calls the SOF interrupt may be disabled
- dcd_sof_enable(rhport, en);
+ uint8_t consumer_old = _usbd_dev.sof_consumer;
+ // Keep track how many class instances need the SOF interrupt
+ if (en) {
+ _usbd_dev.sof_consumer |= (uint8_t)(1 << consumer);
+ } else {
+ _usbd_dev.sof_consumer &= (uint8_t)(~(1 << consumer));
+ }
+
+ // Test logically unequal
+ if(!_usbd_dev.sof_consumer != !consumer_old) {
+ dcd_sof_enable(rhport, _usbd_dev.sof_consumer);
+ }
+}
+
+bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ rhport = _usbd_rhport;
+
+ TU_ASSERT(tu_edpt_number(ep_addr) < CFG_TUD_ENDPPOINT_MAX);
+ return dcd_edpt_iso_alloc(rhport, ep_addr, largest_packet_size);
+#else
+ (void) rhport; (void) ep_addr; (void) largest_packet_size;
+ return false;
+#endif
+}
+
+bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+ rhport = _usbd_rhport;
+
+ uint8_t const epnum = tu_edpt_number(desc_ep->bEndpointAddress);
+ uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+
+ TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX);
+ TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed));
+
+ _usbd_dev.ep_status[epnum][dir].stalled = 0;
+ _usbd_dev.ep_status[epnum][dir].busy = 0;
+ _usbd_dev.ep_status[epnum][dir].claimed = 0;
+ return dcd_edpt_iso_activate(rhport, desc_ep);
+#else
+ (void) rhport; (void) desc_ep;
+ return false;
+#endif
}
#endif
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 731c57aad..de6007fb3 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -37,8 +37,23 @@ extern "C" {
// Application API
//--------------------------------------------------------------------+
-// Init device stack
-bool tud_init (uint8_t rhport);
+// New API to replace tud_init() to init device stack on specific roothub port
+bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
+
+// Init device stack on roothub port
+#if TUSB_VERSION_NUMBER > 2000 // 0.20.0
+TU_ATTR_DEPRECATED("Please use tusb_init(rhport, rh_init) instead")
+#endif
+TU_ATTR_ALWAYS_INLINE static inline bool tud_init (uint8_t rhport) {
+ const tusb_rhport_init_t rh_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
+ };
+ return tud_rhport_init(rhport, &rh_init);
+}
+
+// Deinit device stack on roothub port
+bool tud_deinit(uint8_t rhport);
// Check if device stack is already initialized
bool tud_inited(void);
@@ -50,15 +65,14 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr);
// Task function should be called in main/rtos loop
TU_ATTR_ALWAYS_INLINE static inline
-void tud_task (void)
-{
+void tud_task (void) {
tud_task_ext(UINT32_MAX, false);
}
// Check if there is pending events need processing by tud_task()
bool tud_task_event_ready(void);
-#ifndef _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
extern void dcd_int_handler(uint8_t rhport);
#endif
@@ -80,8 +94,7 @@ bool tud_suspended(void);
// Check if device is ready to transfer
TU_ATTR_ALWAYS_INLINE static inline
-bool tud_ready(void)
-{
+bool tud_ready(void) {
return tud_mounted() && !tud_suspended();
}
@@ -96,6 +109,9 @@ bool tud_disconnect(void);
// Return false on unsupported MCUs
bool tud_connect(void);
+// Enable or disable the Start Of Frame callback support
+void tud_sof_cb_enable(bool en);
+
// Carry out Data and Status stage of control transfer
// - If len = 0, it is equivalent to sending status only
// - If len > wLength : it will be truncated
@@ -105,7 +121,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
-// Application Callbacks (WEAK is optional)
+// Application Callbacks
//--------------------------------------------------------------------+
// Invoked when received GET DEVICE DESCRIPTOR request
@@ -122,34 +138,40 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid);
// Invoked when received GET BOS DESCRIPTOR request
// Application return pointer to descriptor
-TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void);
+uint8_t const * tud_descriptor_bos_cb(void);
// 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.
-TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void);
+uint8_t const* tud_descriptor_device_qualifier_cb(void);
// 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
-TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
// Invoked when device is mounted (configured)
-TU_ATTR_WEAK void tud_mount_cb(void);
+void tud_mount_cb(void);
// Invoked when device is unmounted
-TU_ATTR_WEAK void tud_umount_cb(void);
+void tud_umount_cb(void);
// Invoked when usb bus is suspended
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
-TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
+void tud_suspend_cb(bool remote_wakeup_en);
// Invoked when usb bus is resumed
-TU_ATTR_WEAK void tud_resume_cb(void);
+void tud_resume_cb(void);
+
+// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
+void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
+
+// Invoked when a new (micro) frame started
+void tud_sof_cb(uint32_t frame_count);
// Invoked when received control request with VENDOR TYPE
-TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
// Binary Device Object Store (BOS) Descriptor Templates
@@ -217,8 +239,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
/* CDC Call */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
- /* CDC ACM: support line request */\
- 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\
+ /* CDC ACM: support line request + send break */\
+ 4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\
/* CDC Union */\
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
/* Endpoint Notification */\
@@ -293,7 +315,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* MIDI Streaming (MS) Interface */\
9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 0, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\
/* MS Header */\
- 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN)
+ 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0100), U16_TO_U8S_LE(7 + (_numcables) * TUD_MIDI_DESC_JACK_LEN + 2 * TUD_MIDI_DESC_EP_LEN(_numcables))
#define TUD_MIDI_JACKID_IN_EMB(_cablenum) \
(uint8_t)(((_cablenum) - 1) * 4 + 1)
@@ -317,6 +339,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EMBEDDED, TUD_MIDI_JACKID_OUT_EMB(_cablenum), 1, TUD_MIDI_JACKID_IN_EXT(_cablenum), 1, _stridx,\
/* MS Out Jack (External), connected to In Jack Embedded */\
9, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_OUT_JACK, MIDI_JACK_EXTERNAL, TUD_MIDI_JACKID_OUT_EXT(_cablenum), 1, TUD_MIDI_JACKID_IN_EMB(_cablenum), 1, _stridx
+
#define TUD_MIDI_DESC_JACK(_cablenum) TUD_MIDI_DESC_JACK_DESC(_cablenum, 0)
#define TUD_MIDI_DESC_EP_LEN(_numcables) (9 + 4 + (_numcables))
@@ -346,8 +369,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Standard Interface Association Descriptor (IAD) */
#define TUD_AUDIO_DESC_IAD_LEN 8
-#define TUD_AUDIO_DESC_IAD(_firstitfs, _nitfs, _stridx) \
- TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitfs, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx
+#define TUD_AUDIO_DESC_IAD(_firstitf, _nitfs, _stridx) \
+ TUD_AUDIO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, _firstitf, _nitfs, TUSB_CLASS_AUDIO, AUDIO_FUNCTION_SUBCLASS_UNDEFINED, AUDIO_FUNC_PROTOCOL_CODE_V2, _stridx
/* Standard AC Interface Descriptor(4.7.1) */
#define TUD_AUDIO_DESC_STD_AC_LEN 9
@@ -391,6 +414,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
// For more channels, add definitions here
+/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */
+#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7
+#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \
+ TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval
+
/* Standard AS Interface Descriptor(4.9.1) */
#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
@@ -418,8 +446,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */
#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7
-#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \
- TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_NO_SYNC | TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval
+#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _epsize, _interval) \
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(_epsize), _interval
// AUDIO simple descriptor (UAC2) for 1 microphone input
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
@@ -442,7 +470,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
@@ -466,7 +494,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 0x04 : 0x01),\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
@@ -491,7 +519,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
@@ -515,7 +543,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 0x04 : 0x01),\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
@@ -537,9 +565,9 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN)
-#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize, _epfb) \
+#define TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epfb, _epfbsize) \
/* Standard Interface Association Descriptor (IAD) */\
- TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
+ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\
/* Class-Specific AC Interface Header Descriptor(4.7.2) */\
@@ -551,7 +579,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Output Terminal Descriptor(4.7.2.5) */\
TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
- TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\
+ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
/* Standard AS Interface Descriptor(4.9.1) */\
/* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\
TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\
@@ -563,11 +591,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ TUD_OPT_HIGH_SPEED ? 0x04 : 0x01),\
+ TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ 0x01),\
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
- TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_interval*/ 1)\
+ TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(/*_ep*/ _epfb, /*_epsize*/ _epfbsize, /*_interval*/ 1)
// Calculate wMaxPacketSize of Endpoints
#define TUD_AUDIO_EP_SIZE(_maxFrequency, _nBytesPerSample, _nChannels) \
@@ -603,7 +631,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* optional interrupt endpoint */ \
// _int_pollingInterval : for LS/FS, expressed in frames (1ms each). 16 may be a good number?
#define TUD_USBTMC_INT_DESCRIPTOR(_ep_interrupt, _ep_interrupt_size, _int_pollingInterval ) \
- 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), 0x16
+ 7, TUSB_DESC_ENDPOINT, _ep_interrupt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_interrupt_size), _int_pollingInterval
#define TUD_USBTMC_INT_DESCRIPTOR_LEN (7u)
@@ -648,7 +676,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9)
// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size
-// Note: Alternate count must be numberic or macro, string index is increased by one for each Alt interface
+// Note: Alternate count must be numeric or macro, string index is increased by one for each Alt interface
#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \
TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \
/* Function */ \
@@ -772,10 +800,6 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER 0x01
#define TUD_BT_PROTOCOL_AMP_CONTROLLER 0x02
-#ifndef CFG_TUD_BTH_ISO_ALT_COUNT
-#define CFG_TUD_BTH_ISO_ALT_COUNT 0
-#endif
-
// Length of template descriptor: 38 bytes + number of ISO alternatives * 23
#define TUD_BTH_DESC_LEN (8 + 9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7))
diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c
index 187547ae3..c9700fd9d 100644
--- a/src/device/usbd_control.c
+++ b/src/device/usbd_control.c
@@ -32,98 +32,90 @@
#include "tusb.h"
#include "device/usbd_pvt.h"
-#if CFG_TUSB_DEBUG >= 2
-extern void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
-#endif
+//--------------------------------------------------------------------+
+// Callback weak stubs (called if application does not provide)
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t* request) {
+ (void) rhport;
+ (void) request;
+}
-enum
-{
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+enum {
EDPT_CTRL_OUT = 0x00,
- EDPT_CTRL_IN = 0x80
+ EDPT_CTRL_IN = 0x80
};
-typedef struct
-{
+typedef struct {
tusb_control_request_t request;
-
uint8_t* buffer;
uint16_t data_len;
uint16_t total_xferred;
-
usbd_control_xfer_cb_t complete_cb;
} usbd_control_xfer_t;
static usbd_control_xfer_t _ctrl_xfer;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
-static uint8_t _usbd_ctrl_buf[/*CFG_TUD_ENDPOINT0_SIZE*/ 64];
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(buf, CFG_TUD_ENDPOINT0_SIZE);
+} _ctrl_epbuf;
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Queue ZLP status transaction
-static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
-{
+static inline bool status_stage_xact(uint8_t rhport, const tusb_control_request_t* request) {
// Opposite to endpoint in Data Phase
- uint8_t const ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
+ const uint8_t ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN;
return usbd_edpt_xfer(rhport, ep_addr, NULL, 0);
}
// Status phase
-bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
- return _status_stage_xact(rhport, request);
+ return status_stage_xact(rhport, request);
}
// Queue a transaction in Data Stage
// Each transaction has up to Endpoint0's max packet size.
// This function can also transfer an zero-length packet
-static bool _data_stage_xact(uint8_t rhport)
-{
- uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
-
+static bool data_stage_xact(uint8_t rhport) {
+ const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr = EDPT_CTRL_IN;
- if ( xact_len ) memcpy(_usbd_ctrl_buf, _ctrl_xfer.buffer, xact_len);
+ if (xact_len) {
+ TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_SIZE, _ctrl_xfer.buffer, xact_len));
+ }
}
- return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len);
+ return usbd_edpt_xfer(rhport, ep_addr, xact_len ? _ctrl_epbuf.buf : NULL, xact_len);
}
// Transmit data to/from the control endpoint.
// If the request's wLength is zero, a status packet is sent instead.
-bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = (uint8_t*) buffer;
+bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t* request, void* buffer, uint16_t len) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = (uint8_t*) buffer;
_ctrl_xfer.total_xferred = 0U;
- _ctrl_xfer.data_len = tu_min16(len, request->wLength);
+ _ctrl_xfer.data_len = tu_min16(len, request->wLength);
- if (request->wLength > 0U)
- {
- if(_ctrl_xfer.data_len > 0U)
- {
+ if (request->wLength > 0U) {
+ if (_ctrl_xfer.data_len > 0U) {
TU_ASSERT(buffer);
}
-
-// TU_LOG2(" Control total data length is %u bytes\r\n", _ctrl_xfer.data_len);
-
- // Data stage
- TU_ASSERT( _data_stage_xact(rhport) );
- }
- else
- {
- // Status stage
- TU_ASSERT( _status_stage_xact(rhport, request) );
+ TU_ASSERT(data_stage_xact(rhport));
+ } else {
+ TU_ASSERT(status_stage_xact(rhport, request));
}
return true;
@@ -132,49 +124,42 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
//--------------------------------------------------------------------+
// USBD API
//--------------------------------------------------------------------+
-
void usbd_control_reset(void);
-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);
+void usbd_control_set_request(const tusb_control_request_t* 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 result, uint32_t xferred_bytes);
-void usbd_control_reset(void)
-{
+void usbd_control_reset(void) {
tu_varclr(&_ctrl_xfer);
}
// Set complete callback
-void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp )
-{
+void usbd_control_set_complete_callback(usbd_control_xfer_cb_t fp) {
_ctrl_xfer.complete_cb = fp;
}
// for dcd_set_address where DCD is responsible for status response
-void usbd_control_set_request(tusb_control_request_t const *request)
-{
- _ctrl_xfer.request = (*request);
- _ctrl_xfer.buffer = NULL;
+void usbd_control_set_request(const tusb_control_request_t* request) {
+ _ctrl_xfer.request = (*request);
+ _ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
- _ctrl_xfer.data_len = 0;
+ _ctrl_xfer.data_len = 0;
}
// callback when a transaction complete on
// - DATA stage of control endpoint or
// - Status stage
-bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
// Endpoint Address is opposite to direction bit, this is Status Stage complete event
- if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
- {
+ if (tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction) {
TU_ASSERT(0 == xferred_bytes);
// invoke optional dcd hook if available
- if (dcd_edpt0_status_complete) dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
+ dcd_edpt0_status_complete(rhport, &_ctrl_xfer.request);
- if (_ctrl_xfer.complete_cb)
- {
+ if (_ctrl_xfer.complete_cb) {
// TODO refactor with usbd_driver_print_control_complete_name
_ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_ACK, &_ctrl_xfer.request);
}
@@ -182,11 +167,10 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
return true;
}
- if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
- {
+ if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT) {
TU_VERIFY(_ctrl_xfer.buffer);
- memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
- TU_LOG_MEM(2, _usbd_ctrl_buf, xferred_bytes, 2);
+ memcpy(_ctrl_xfer.buffer, _ctrl_epbuf.buf, xferred_bytes);
+ TU_LOG_MEM(CFG_TUD_LOG_LEVEL, _ctrl_xfer.buffer, xferred_bytes, 2);
}
_ctrl_xfer.total_xferred += (uint16_t) xferred_bytes;
@@ -194,37 +178,31 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
// Data Stage is complete when all request's length are transferred or
// a short packet is sent including zero-length packet.
- if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE) )
- {
+ if ((_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) ||
+ (xferred_bytes < CFG_TUD_ENDPOINT0_SIZE)) {
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
- if ( _ctrl_xfer.complete_cb )
- {
- #if CFG_TUSB_DEBUG >= 2
+ if (_ctrl_xfer.complete_cb) {
+ #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
usbd_driver_print_control_complete_name(_ctrl_xfer.complete_cb);
#endif
is_ok = _ctrl_xfer.complete_cb(rhport, CONTROL_STAGE_DATA, &_ctrl_xfer.request);
}
- if ( is_ok )
- {
- // Send status
- TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
- }else
- {
+ if (is_ok) {
+ TU_ASSERT(status_stage_xact(rhport, &_ctrl_xfer.request));
+ } else {
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
}
- }
- else
- {
+ } else {
// More data to transfer
- TU_ASSERT( _data_stage_xact(rhport) );
+ TU_ASSERT(data_stage_xact(rhport));
}
return true;
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 6fad46db3..190d6fd7f 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -23,27 +23,36 @@
*
* This file is part of the TinyUSB stack.
*/
-#ifndef USBD_PVT_H_
-#define USBD_PVT_H_
+#ifndef TUSB_USBD_PVT_H_
+#define TUSB_USBD_PVT_H_
#include "osal/osal.h"
#include "common/tusb_fifo.h"
+#include "common/tusb_private.h"
#ifdef __cplusplus
extern "C" {
#endif
+#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__)
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+typedef enum {
+ SOF_CONSUMER_USER = 0,
+ SOF_CONSUMER_AUDIO,
+} sof_consumer_t;
+
//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+
-typedef struct
-{
- #if CFG_TUSB_DEBUG >= 2
+typedef struct {
char const* name;
- #endif
-
void (* init ) (void);
+ bool (* deinit ) (void);
void (* reset ) (uint8_t rhport);
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
@@ -52,7 +61,7 @@ typedef struct
} usbd_class_driver_t;
// Invoked when initializing device stack to get additional class drivers.
-// Can optionally implemented by application to extend/overwrite class driver support.
+// 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;
@@ -96,26 +105,35 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr);
// Check if endpoint is stalled
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr);
+// Allocate packet buffer used by ISO endpoints
+bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
+
+// Configure and enable an ISO endpoint according to descriptor
+bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
+
// Check if endpoint is ready (not busy and not stalled)
TU_ATTR_ALWAYS_INLINE static inline
-bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr)
-{
+bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) {
return !usbd_edpt_busy(rhport, ep_addr) && !usbd_edpt_stalled(rhport, ep_addr);
}
// Enable SOF interrupt
-void usbd_sof_enable(uint8_t rhport, bool en);
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en);
/*------------------------------------------------------------------*/
/* Helper
*------------------------------------------------------------------*/
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
-void usbd_defer_func( osal_task_func_t func, void* param, bool in_isr );
+void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr);
+#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+void usbd_driver_print_control_complete_name(usbd_control_xfer_cb_t callback);
+#endif
+
#ifdef __cplusplus
}
#endif
-#endif /* USBD_PVT_H_ */
+#endif
diff --git a/src/host/hcd.h b/src/host/hcd.h
index 97b668d79..56b6fdb5d 100644
--- a/src/host/hcd.h
+++ b/src/host/hcd.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -39,8 +39,10 @@
// Configuration
//--------------------------------------------------------------------+
+// Max number of endpoints pair per device
+// TODO optimize memory usage
#ifndef CFG_TUH_ENDPOINT_MAX
- #define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3 + CFG_TUH_MIDI*2)
+ #define CFG_TUH_ENDPOINT_MAX 16
// #ifdef TUP_HCD_ENDPOINT_MAX
// #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX
// #else
@@ -51,26 +53,21 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-typedef enum
-{
+typedef enum {
HCD_EVENT_DEVICE_ATTACH,
HCD_EVENT_DEVICE_REMOVE,
HCD_EVENT_XFER_COMPLETE,
- // Not an HCD event, just a convenient way to defer ISR function
- USBH_EVENT_FUNC_CALL,
-
+ USBH_EVENT_FUNC_CALL, // Not an HCD event
HCD_EVENT_COUNT
} hcd_eventid_t;
-typedef struct
-{
+typedef struct {
uint8_t rhport;
uint8_t event_id;
uint8_t dev_addr;
- union
- {
+ union {
// Attach, Remove
struct {
uint8_t hub_addr;
@@ -91,11 +88,9 @@ typedef struct
void* param;
}func_call;
};
-
} hcd_event_t;
-typedef struct
-{
+typedef struct {
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
@@ -103,17 +98,36 @@ typedef struct
} hcd_devtree_info_t;
//--------------------------------------------------------------------+
+// Memory API
+//--------------------------------------------------------------------+
+
+// clean/flush data cache: write cache -> memory.
+// Required before an DMA TX transfer to make sure data is in memory
+bool hcd_dcache_clean(void const* addr, uint32_t data_size);
+
+// invalidate data cache: mark cache as invalid, next read will read from memory
+// Required BOTH before and after an DMA RX transfer
+bool hcd_dcache_invalidate(void const* addr, uint32_t data_size);
+
+// clean and invalidate data cache
+// Required before an DMA transfer where memory is both read/write by DMA
+bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size);
+
+//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-// optional hcd configuration, called by tuh_config()
-bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_ATTR_WEAK;
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
// Initialize controller to host mode
-bool hcd_init(uint8_t rhport);
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
+
+// De-initialize controller
+bool hcd_deinit(uint8_t rhport);
// Interrupt Handler
-void hcd_int_handler(uint8_t rhport);
+void hcd_int_handler(uint8_t rhport, bool in_isr);
// Enable USB interrupt
void hcd_int_enable (uint8_t rhport);
@@ -131,10 +145,11 @@ uint32_t hcd_frame_number(uint8_t rhport);
// Get the current connect status of roothub port
bool hcd_port_connect_status(uint8_t rhport);
-// Reset USB bus on the port
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
void hcd_port_reset(uint8_t rhport);
-// TODO implement later
+// Complete bus reset sequence, may be required by some controllers
void hcd_port_reset_end(uint8_t rhport);
// Get port link speed
@@ -148,16 +163,20 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
//--------------------------------------------------------------------+
// Open an endpoint
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc);
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]);
// clear stall, data toggle is also reset to DATA0
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
//--------------------------------------------------------------------+
// USBH implemented API
@@ -175,20 +194,19 @@ extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
// Helper to send device attach event
TU_ATTR_ALWAYS_INLINE static inline
-void hcd_event_device_attach(uint8_t rhport, bool in_isr)
-{
+void hcd_event_device_attach(uint8_t rhport, bool in_isr) {
hcd_event_t event;
event.rhport = rhport;
event.event_id = HCD_EVENT_DEVICE_ATTACH;
event.connection.hub_addr = 0;
event.connection.hub_port = 0;
+
hcd_event_handler(&event, in_isr);
}
// Helper to send device removal event
TU_ATTR_ALWAYS_INLINE static inline
-void hcd_event_device_remove(uint8_t rhport, bool in_isr)
-{
+void hcd_event_device_remove(uint8_t rhport, bool in_isr) {
hcd_event_t event;
event.rhport = rhport;
event.event_id = HCD_EVENT_DEVICE_REMOVE;
@@ -200,10 +218,8 @@ void hcd_event_device_remove(uint8_t rhport, bool in_isr)
// Helper to send USB transfer event
TU_ATTR_ALWAYS_INLINE static inline
-void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
-{
- hcd_event_t event =
- {
+void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr) {
+ hcd_event_t event = {
.rhport = 0, // TODO correct rhport
.event_id = HCD_EVENT_XFER_COMPLETE,
.dev_addr = dev_addr,
@@ -212,7 +228,6 @@ void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred
event.xfer_complete.result = result;
event.xfer_complete.len = xferred_bytes;
-
hcd_event_handler(&event, in_isr);
}
diff --git a/src/host/hub.c b/src/host/hub.c
index 3da5358b2..e97014443 100644
--- a/src/host/hub.c
+++ b/src/host/hub.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -30,9 +30,13 @@
#include "hcd.h"
#include "usbh.h"
-#include "usbh_classdriver.h"
+#include "usbh_pvt.h"
#include "hub.h"
+// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
+#define HUB_DEBUG 2
+#define TU_LOG_DRV(...) TU_LOG(HUB_DEBUG, __VA_ARGS__)
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
@@ -41,14 +45,14 @@ typedef struct
uint8_t itf_num;
uint8_t ep_in;
uint8_t port_count;
- uint8_t status_change; // data from status change interrupt endpoint
- hub_port_status_response_t port_status;
- hub_status_response_t hub_status;
+ CFG_TUH_MEM_ALIGN uint8_t status_change;
+ CFG_TUH_MEM_ALIGN hub_port_status_response_t port_status;
+ CFG_TUH_MEM_ALIGN hub_status_response_t hub_status;
} hub_interface_t;
-CFG_TUSB_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB];
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
+CFG_TUH_MEM_SECTION static hub_interface_t hub_data[CFG_TUH_HUB];
+CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t _hub_buffer[sizeof(descriptor_hub_desc_t)];
TU_ATTR_ALWAYS_INLINE
static inline hub_interface_t* get_itf(uint8_t dev_addr)
@@ -178,9 +182,13 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
//--------------------------------------------------------------------+
// CLASS-USBH API (don't require to verify parameters)
//--------------------------------------------------------------------+
-void hub_init(void)
-{
+bool hub_init(void) {
tu_memclr(hub_data, sizeof(hub_data));
+ return true;
+}
+
+bool hub_deinit(void) {
+ return true;
}
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
@@ -202,7 +210,7 @@ bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
-
+
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
hub_interface_t* p_hub = get_itf(dev_addr);
@@ -218,7 +226,10 @@ void hub_close(uint8_t dev_addr)
TU_VERIFY(dev_addr > CFG_TUH_DEVICE_MAX, );
hub_interface_t* p_hub = get_itf(dev_addr);
- if (p_hub->ep_in) tu_memclr(p_hub, sizeof( hub_interface_t));
+ if (p_hub->ep_in) {
+ TU_LOG_DRV(" HUB close addr = %d\r\n", dev_addr);
+ tu_memclr(p_hub, sizeof( hub_interface_t));
+ }
}
bool hub_edpt_status_xfer(uint8_t dev_addr)
@@ -320,34 +331,35 @@ static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
static void connection_port_reset_complete (tuh_xfer_t* xfer);
// callback as response of interrupt endpoint polling
-bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) xferred_bytes; // TODO can be more than 1 for hub with lots of ports
(void) ep_addr;
- TU_ASSERT(result == XFER_RESULT_SUCCESS);
+ TU_VERIFY(result == XFER_RESULT_SUCCESS);
hub_interface_t* p_hub = get_itf(dev_addr);
- TU_LOG2(" Hub Status Change = 0x%02X\r\n", p_hub->status_change);
+ uint8_t const status_change = p_hub->status_change;
+ TU_LOG2(" Hub Status Change = 0x%02X\r\n", status_change);
- // Hub bit 0 is for the hub device events
- if (tu_bit_test(p_hub->status_change, 0))
- {
- if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false)
- {
+ if ( status_change == 0 ) {
+ // The status change event was neither for the hub, nor for any of its ports.
+ // This shouldn't happen, but it does with some devices.
+ // Initiate the next interrupt poll here.
+ return hub_edpt_status_xfer(dev_addr);
+ }
+
+ if (tu_bit_test(status_change, 0)) {
+ // Hub bit 0 is for the hub device events
+ if (hub_port_get_status(dev_addr, 0, &p_hub->hub_status, hub_get_status_complete, 0) == false) {
//Hub status control transfer failed, retry
hub_edpt_status_xfer(dev_addr);
}
}
- else
- {
+ else {
// Hub bits 1 to n are hub port events
- for (uint8_t port=1; port <= p_hub->port_count; port++)
- {
- if ( tu_bit_test(p_hub->status_change, port) )
- {
- if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false)
- {
+ for (uint8_t port=1; port <= p_hub->port_count; port++) {
+ if ( tu_bit_test(status_change, port) ) {
+ if (hub_port_get_status(dev_addr, port, &p_hub->port_status, hub_port_get_status_complete, 0) == false) {
//Hub status control transfer failed, retry
hub_edpt_status_xfer(dev_addr);
}
@@ -357,7 +369,6 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32
}
// NOTE: next status transfer is queued by usbh.c after handling this request
-
return true;
}
@@ -428,9 +439,12 @@ static void hub_port_get_status_complete (tuh_xfer_t* xfer)
// Other changes are: L1 state
// TODO clear change
- // prepare for next hub status
- // TODO continue with status_change, or maybe we can do it again with status
- hub_edpt_status_xfer(daddr);
+ else
+ {
+ // prepare for next hub status
+ // TODO continue with status_change, or maybe we can do it again with status
+ hub_edpt_status_xfer(daddr);
+ }
}
}
diff --git a/src/host/hub.h b/src/host/hub.h
index 390740e1f..385efe6b2 100644
--- a/src/host/hub.h
+++ b/src/host/hub.h
@@ -187,16 +187,14 @@ bool hub_port_get_status (uint8_t hub_addr, uint8_t hub_port, void* resp,
bool hub_edpt_status_xfer(uint8_t dev_addr);
// Reset a port
-static inline bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+TU_ATTR_ALWAYS_INLINE static inline
+bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data);
}
// Clear Reset Change
-static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+TU_ATTR_ALWAYS_INLINE static inline
+bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
}
@@ -204,7 +202,8 @@ static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_por
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void hub_init (void);
+bool hub_init (void);
+bool hub_deinit (void);
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool hub_set_config (uint8_t dev_addr, uint8_t itf_num);
bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 44b4d3217..6f7a8b8bf 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -30,47 +30,70 @@
#include "host/hcd.h"
#include "tusb.h"
-#include "common/tusb_private.h"
-#include "host/usbh_classdriver.h"
+#include "host/usbh_pvt.h"
#include "hub.h"
//--------------------------------------------------------------------+
// USBH Configuration
//--------------------------------------------------------------------+
-
#ifndef CFG_TUH_TASK_QUEUE_SZ
-#define CFG_TUH_TASK_QUEUE_SZ 16
+ #define CFG_TUH_TASK_QUEUE_SZ 16
#endif
#ifndef CFG_TUH_INTERFACE_MAX
-#define CFG_TUH_INTERFACE_MAX 8
+ #define CFG_TUH_INTERFACE_MAX 8
#endif
-// Debug level of USBD
-#define USBH_DBG_LVL 2
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+ return false;
+}
+
+TU_ATTR_WEAK bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ (void) cfg_id;
+ (void) cfg_param;
+ return false;
+}
+
+TU_ATTR_WEAK void tuh_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+ (void) rhport;
+ (void) eventid;
+ (void) in_isr;
+}
+
+TU_ATTR_WEAK bool hcd_dcache_clean(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
+
+TU_ATTR_WEAK bool hcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
+
+TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ (void) addr; (void) data_size;
+ return false;
+}
//--------------------------------------------------------------------+
// USBH-HCD common data structure
//--------------------------------------------------------------------+
-
-// device0 struct must be strictly a subset of normal device struct
-// TODO refactor later
-typedef struct
-{
+typedef struct {
// port
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
- uint8_t speed;
- struct TU_ATTR_PACKED
- {
- volatile uint8_t connected : 1;
- volatile uint8_t addressed : 1;
- volatile uint8_t configured : 1;
- volatile uint8_t suspended : 1;
+ struct TU_ATTR_PACKED {
+ uint8_t speed : 4; // packed speed to save footprint
+ volatile uint8_t enumerating : 1; // enumeration is in progress, false if not connected or all interfaces are configured
+ uint8_t TU_RESERVED : 3;
};
-
} usbh_dev0_t;
typedef struct {
@@ -82,10 +105,12 @@ typedef struct {
// Device State
struct TU_ATTR_PACKED {
- volatile uint8_t connected : 1;
- volatile uint8_t addressed : 1;
- volatile uint8_t configured : 1;
- volatile uint8_t suspended : 1;
+ volatile uint8_t connected : 1; // After 1st transfer
+ volatile uint8_t addressed : 1; // After SET_ADDR
+ volatile uint8_t configured : 1; // After SET_CONFIG and all drivers are configured
+ volatile uint8_t suspended : 1; // Bus suspended
+
+ // volatile uint8_t removing : 1; // Physically disconnected, waiting to be processed by usbh
};
// Device Descriptor
@@ -120,91 +145,105 @@ typedef struct {
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-
-// Invalid driver ID in itf2drv[] ep2drv[][] mapping
-enum { DRVID_INVALID = 0xFFu };
-enum { CONTROLLER_INVALID = 0xFFu };
-
-#if CFG_TUSB_DEBUG >= 2
- #define DRIVER_NAME(_name) .name = _name,
+#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
+ #define DRIVER_NAME(_name) _name
#else
- #define DRIVER_NAME(_name)
+ #define DRIVER_NAME(_name) NULL
#endif
-static usbh_class_driver_t const usbh_class_drivers[] =
-{
+static usbh_class_driver_t const usbh_class_drivers[] = {
#if CFG_TUH_CDC
- {
- DRIVER_NAME("CDC")
+ {
+ .name = DRIVER_NAME("CDC"),
.init = cdch_init,
+ .deinit = cdch_deinit,
.open = cdch_open,
.set_config = cdch_set_config,
.xfer_cb = cdch_xfer_cb,
.close = cdch_close
- },
+ },
#endif
#if CFG_TUH_MSC
- {
- DRIVER_NAME("MSC")
+ {
+ .name = DRIVER_NAME("MSC"),
.init = msch_init,
+ .deinit = msch_deinit,
.open = msch_open,
.set_config = msch_set_config,
.xfer_cb = msch_xfer_cb,
.close = msch_close
- },
+ },
#endif
#if CFG_TUH_HID
- {
- DRIVER_NAME("HID")
+ {
+ .name = DRIVER_NAME("HID"),
.init = hidh_init,
+ .deinit = hidh_deinit,
.open = hidh_open,
.set_config = hidh_set_config,
.xfer_cb = hidh_xfer_cb,
.close = hidh_close
- },
+ },
#endif
#if CFG_TUH_MIDI
- {
- DRIVER_NAME("MIDI")
+ {
+ .name = DRIVER_NAME("MIDI"),
.init = midih_init,
.open = midih_open,
.set_config = midih_set_config,
.xfer_cb = midih_xfer_cb,
.close = midih_close
- },
+ },
#endif
#if CFG_TUH_HUB
- {
- DRIVER_NAME("HUB")
+ {
+ .name = DRIVER_NAME("HUB"),
.init = hub_init,
+ .deinit = hub_deinit,
.open = hub_open,
.set_config = hub_set_config,
.xfer_cb = hub_xfer_cb,
.close = hub_close
- },
+ },
#endif
#if CFG_TUH_VENDOR
- {
- DRIVER_NAME("VENDOR")
- .init = cush_init,
- .open = cush_open_subtask,
- .xfer_cb = cush_isr,
- .close = cush_close
- }
+ {
+ .name = DRIVER_NAME("VENDOR"),
+ .init = cush_init,
+ .deinit = cush_deinit,
+ .open = cush_open,
+ .set_config = cush_set_config,
+ .xfer_cb = cush_isr,
+ .close = cush_close
+ }
#endif
};
-enum { USBH_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
+enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(usbh_class_drivers) };
+enum { CONFIG_NUM = 1 }; // default to use configuration 1
-enum { RESET_DELAY = 500 }; // 200 USB specs say only 50ms but many devices require much longer
+// Additional class drivers implemented by application
+tu_static usbh_class_driver_t const * _app_driver = NULL;
+tu_static uint8_t _app_driver_count = 0;
-enum { CONFIG_NUM = 1 }; // default to use configuration 1
+#define TOTAL_DRIVER_COUNT (_app_driver_count + BUILTIN_DRIVER_COUNT)
+
+static inline usbh_class_driver_t const *get_driver(uint8_t drv_id) {
+ usbh_class_driver_t const *driver = NULL;
+
+ if ( drv_id < _app_driver_count ) {
+ driver = &_app_driver[drv_id];
+ } else if ( drv_id < TOTAL_DRIVER_COUNT && BUILTIN_DRIVER_COUNT > 0) {
+ driver = &usbh_class_drivers[drv_id - _app_driver_count];
+ }
+ return driver;
+}
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
@@ -213,7 +252,7 @@ enum { CONFIG_NUM = 1 }; // default to use configuration 1
// sum of end device + hub
#define TOTAL_DEVICES (CFG_TUH_DEVICE_MAX + CFG_TUH_HUB)
-static uint8_t _usbh_controller = CONTROLLER_INVALID;
+static uint8_t _usbh_controller = TUSB_INDEX_INVALID_8;
// Device with address = 0 for enumeration
static usbh_dev0_t _dev0;
@@ -221,30 +260,14 @@ static usbh_dev0_t _dev0;
// all devices excluding zero-address
// hub address start from CFG_TUH_DEVICE_MAX+1
// TODO: hub can has its own simpler struct to save memory
-CFG_TUSB_MEM_SECTION usbh_device_t _usbh_devices[TOTAL_DEVICES];
-
-// Mutex for claiming endpoint, only needed when using with preempted RTOS
-#if TUSB_OPT_MUTEX
-static osal_mutex_def_t _usbh_mutexdef;
-static osal_mutex_t _usbh_mutex;
-
-TU_ATTR_ALWAYS_INLINE static inline void usbh_lock(void)
-{
- osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void)
-{
- osal_mutex_unlock(_usbh_mutex);
-}
+static usbh_device_t _usbh_devices[TOTAL_DEVICES];
+// Mutex for claiming endpoint
+#if OSAL_MUTEX_REQUIRED
+ static osal_mutex_def_t _usbh_mutexdef;
+ static osal_mutex_t _usbh_mutex;
#else
-
-#define _usbh_mutex NULL
-
-#define usbh_lock()
-#define usbh_unlock()
-
+ #define _usbh_mutex NULL
#endif
// Event queue
@@ -252,15 +275,10 @@ TU_ATTR_ALWAYS_INLINE static inline void usbh_unlock(void)
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN
-static uint8_t _usbh_ctrl_buf[CFG_TUH_ENUMERATION_BUFSIZE];
-
-// Control transfer: since most controller does not support multiple control transfer
-// on multiple devices concurrently. And control transfer is not used much except enumeration
-// We will only execute control transfer one at a time.
-struct
-{
- tusb_control_request_t request TU_ATTR_ALIGNED(4);
+// Control transfers: since most controllers do not support multiple control transfers
+// on multiple devices concurrently and control transfers are not used much except for
+// enumeration, we will only execute control transfers one at a time.
+static struct {
uint8_t* buffer;
tuh_xfer_cb_t complete_cb;
uintptr_t user_data;
@@ -268,61 +286,48 @@ struct
uint8_t daddr;
volatile uint8_t stage;
volatile uint16_t actual_len;
-}_ctrl_xfer;
+} _ctrl_xfer;
+
+typedef struct {
+ TUH_EPBUF_TYPE_DEF(tusb_control_request_t, request);
+ TUH_EPBUF_DEF(ctrl, CFG_TUH_ENUMERATION_BUFSIZE);
+} usbh_epbuf_t;
+
+CFG_TUH_MEM_SECTION static usbh_epbuf_t _usbh_epbuf;
//------------- Helper Function -------------//
-TU_ATTR_ALWAYS_INLINE
-static inline usbh_device_t* get_device(uint8_t dev_addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline usbh_device_t* get_device(uint8_t dev_addr) {
TU_VERIFY(dev_addr > 0 && dev_addr <= TOTAL_DEVICES, NULL);
return &_usbh_devices[dev_addr-1];
}
static bool enum_new_device(hcd_event_t* event);
-static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
+static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port);
static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size);
static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-#if CFG_TUSB_OS == OPT_OS_NONE
-// TODO rework time-related function later
-void osal_task_delay(uint32_t msec)
-{
- (void) msec;
-
- const uint32_t start = hcd_frame_number(_usbh_controller);
- while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {}
+TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
+ TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr));
+ tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
+ return true;
}
-#endif
//--------------------------------------------------------------------+
-// PUBLIC API (Parameter Verification is required)
+// Device API
//--------------------------------------------------------------------+
-bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param)
-{
- if (hcd_configure)
- {
- return hcd_configure(rhport, cfg_id, cfg_param);
- }else
- {
- return false;
- }
-}
-
-bool tuh_mounted(uint8_t dev_addr)
-{
- usbh_device_t* dev = get_device(dev_addr);
+bool tuh_mounted(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
TU_VERIFY(dev);
return dev->configured;
}
-bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid)
-{
+bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t *vid, uint16_t *pid) {
*vid = *pid = 0;
- usbh_device_t const* dev = get_device(dev_addr);
- TU_VERIFY(dev && dev->configured);
+ usbh_device_t const *dev = get_device(dev_addr);
+ TU_VERIFY(dev && dev->addressed && dev->vid != 0);
*vid = dev->vid;
*pid = dev->pid;
@@ -330,70 +335,144 @@ bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid)
return true;
}
-tusb_speed_t tuh_speed_get (uint8_t dev_addr)
-{
- usbh_device_t* dev = get_device(dev_addr);
+tusb_speed_t tuh_speed_get(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
return (tusb_speed_t) (dev ? get_device(dev_addr)->speed : _dev0.speed);
}
-static void clear_device(usbh_device_t* dev)
-{
+bool tuh_rhport_is_active(uint8_t rhport) {
+ return _usbh_controller == rhport;
+}
+
+bool tuh_rhport_reset_bus(uint8_t rhport, bool active) {
+ TU_VERIFY(tuh_rhport_is_active(rhport));
+ if ( active ) {
+ hcd_port_reset(rhport);
+ } else {
+ hcd_port_reset_end(rhport);
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// PUBLIC API (Parameter Verification is required)
+//--------------------------------------------------------------------+
+
+bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
+ return hcd_configure(rhport, cfg_id, cfg_param);
+}
+
+static void clear_device(usbh_device_t* dev) {
tu_memclr(dev, sizeof(usbh_device_t));
- memset(dev->itf2drv, DRVID_INVALID, sizeof(dev->itf2drv)); // invalid mapping
- memset(dev->ep2drv , DRVID_INVALID, sizeof(dev->ep2drv )); // invalid mapping
+ memset(dev->itf2drv, TUSB_INDEX_INVALID_8, sizeof(dev->itf2drv)); // invalid mapping
+ memset(dev->ep2drv , TUSB_INDEX_INVALID_8, sizeof(dev->ep2drv )); // invalid mapping
}
-bool tuh_inited(void)
-{
- return _usbh_controller != CONTROLLER_INVALID;
+bool tuh_inited(void) {
+ return _usbh_controller != TUSB_INDEX_INVALID_8;
}
-bool tuh_init(uint8_t controller_id)
-{
- // skip if already initialized
- if ( tuh_inited() ) return true;
+bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ if (tuh_rhport_is_active(rhport)) {
+ return true; // skip if already initialized
+ }
- TU_LOG2("USBH init on controller %u\r\n", controller_id);
- TU_LOG2_INT(sizeof(usbh_device_t));
- TU_LOG2_INT(sizeof(hcd_event_t));
- TU_LOG2_INT(sizeof(_ctrl_xfer));
- TU_LOG2_INT(sizeof(tuh_xfer_t));
+ TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport,
+ rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
- // Event queue
- _usbh_q = osal_queue_create( &_usbh_qdef );
- TU_ASSERT(_usbh_q != NULL);
+ // Init host stack if not already
+ if (!tuh_inited()) {
+ TU_LOG_INT_USBH(sizeof(usbh_device_t));
+ TU_LOG_INT_USBH(sizeof(hcd_event_t));
+ TU_LOG_INT_USBH(sizeof(_ctrl_xfer));
+ TU_LOG_INT_USBH(sizeof(tuh_xfer_t));
+ TU_LOG_INT_USBH(sizeof(tu_fifo_t));
+ TU_LOG_INT_USBH(sizeof(tu_edpt_stream_t));
-#if TUSB_OPT_MUTEX
- // Mutex
- _usbh_mutex = osal_mutex_create(&_usbh_mutexdef);
- TU_ASSERT(_usbh_mutex);
+ // Event queue
+ _usbh_q = osal_queue_create(&_usbh_qdef);
+ TU_ASSERT(_usbh_q != NULL);
+
+#if OSAL_MUTEX_REQUIRED
+ // Init mutex
+ _usbh_mutex = osal_mutex_create(&_usbh_mutexdef);
+ TU_ASSERT(_usbh_mutex);
#endif
- // Device
- tu_memclr(&_dev0, sizeof(_dev0));
- tu_memclr(_usbh_devices, sizeof(_usbh_devices));
- tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
+ // Get application driver if available
+ if (usbh_app_driver_get_cb) {
+ _app_driver = usbh_app_driver_get_cb(&_app_driver_count);
+ }
- for(uint8_t i=0; i<TOTAL_DEVICES; i++)
- {
- clear_device(&_usbh_devices[i]);
- }
+ // Device
+ tu_memclr(&_dev0, sizeof(_dev0));
+ tu_memclr(_usbh_devices, sizeof(_usbh_devices));
+ tu_memclr(&_ctrl_xfer, sizeof(_ctrl_xfer));
- // Class drivers
- for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
- {
- TU_LOG2("%s init\r\n", usbh_class_drivers[drv_id].name);
- usbh_class_drivers[drv_id].init();
+ for (uint8_t i = 0; i < TOTAL_DEVICES; i++) {
+ clear_device(&_usbh_devices[i]);
+ }
+
+ // Class drivers
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
+ usbh_class_driver_t const* driver = get_driver(drv_id);
+ if (driver) {
+ TU_LOG_USBH("%s init\r\n", driver->name);
+ driver->init();
+ }
+ }
}
- _usbh_controller = controller_id;;
+ // Init host controller
+ _usbh_controller = rhport;
+ TU_ASSERT(hcd_init(rhport, rh_init));
+ hcd_int_enable(rhport);
- TU_ASSERT(hcd_init(controller_id));
- hcd_int_enable(controller_id);
+ return true;
+}
+
+bool tuh_deinit(uint8_t rhport) {
+ if (!tuh_rhport_is_active(rhport)) return true;
+
+ // deinit host controller
+ hcd_int_disable(rhport);
+ hcd_deinit(rhport);
+ _usbh_controller = TUSB_INDEX_INVALID_8;
+
+ // "unplug" all devices on this rhport (hub_addr = 0, hub_port = 0)
+ process_removing_device(rhport, 0, 0);
+
+ // deinit host stack if no controller is active
+ if (!tuh_inited()) {
+ // Class drivers
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
+ usbh_class_driver_t const* driver = get_driver(drv_id);
+ if (driver && driver->deinit) {
+ TU_LOG_USBH("%s deinit\r\n", driver->name);
+ driver->deinit();
+ }
+ }
+
+ osal_queue_delete(_usbh_q);
+ _usbh_q = NULL;
+
+ #if OSAL_MUTEX_REQUIRED
+ // TODO make sure there is no task waiting on this mutex
+ osal_mutex_delete(_usbh_mutex);
+ _usbh_mutex = NULL;
+ #endif
+ }
return true;
}
+bool tuh_task_event_ready(void) {
+ if (!tuh_inited()) {
+ return false; // Skip if stack is not initialized
+ }
+ return !osal_queue_empty(_usbh_q);
+}
+
/* USB Host Driver task
* This top level thread manages all host controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
@@ -402,7 +481,7 @@ bool tuh_init(uint8_t controller_id)
int main(void)
{
application_init();
- tusb_init();
+ tusb_init(0, TUSB_ROLE_HOST);
while(1) // the mainloop
{
@@ -412,83 +491,89 @@ bool tuh_init(uint8_t controller_id)
}
@endcode
*/
-void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
-{
+void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
(void) in_isr; // not implemented yet
// Skip if stack is not initialized
- if ( !tusb_inited() ) return;
+ if (!tuh_inited()) return;
// Loop until there is no more events in the queue
- while (1)
- {
+ while (1) {
hcd_event_t event;
- if ( !osal_queue_receive(_usbh_q, &event, timeout_ms) ) return;
+ if (!osal_queue_receive(_usbh_q, &event, timeout_ms)) return;
- switch (event.event_id)
- {
+ switch (event.event_id) {
case HCD_EVENT_DEVICE_ATTACH:
- // TODO due to the shared _usbh_ctrl_buf, we must complete enumerating
- // one device before enumerating another one.
- TU_LOG2("[%u:] USBH DEVICE ATTACH\r\n", event.rhport);
- enum_new_device(&event);
- break;
+ // due to the shared control buffer, we must complete enumerating one device before enumerating another one.
+ // TODO better to have an separated queue for newly attached devices
+ if (_dev0.enumerating) {
+ // Some device can cause multiple duplicated attach events
+ // drop current enumerating and start over for a proper port reset
+ if (event.rhport == _dev0.rhport && event.connection.hub_addr == _dev0.hub_addr &&
+ event.connection.hub_port == _dev0.hub_port) {
+ // abort/cancel current enumeration and start new one
+ TU_LOG1("[%u:] USBH Device Attach (duplicated)\r\n", event.rhport);
+ tuh_edpt_abort_xfer(0, 0);
+ enum_new_device(&event);
+ } else {
+ TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport);
+
+ bool is_empty = osal_queue_empty(_usbh_q);
+ queue_event(&event, in_isr);
+
+ if (is_empty) {
+ // Exit if this is the only event in the queue, otherwise we may loop forever
+ return;
+ }
+ }
+ } else {
+ TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
+ _dev0.enumerating = 1;
+ enum_new_device(&event);
+ }
+ break;
case HCD_EVENT_DEVICE_REMOVE:
- TU_LOG2("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
- process_device_unplugged(event.rhport, event.connection.hub_addr, event.connection.hub_port);
+ TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
+ process_removing_device(event.rhport, event.connection.hub_addr, event.connection.hub_port);
#if CFG_TUH_HUB
// TODO remove
- if ( event.connection.hub_addr != 0)
- {
+ if (event.connection.hub_addr != 0 && event.connection.hub_port != 0) {
// done with hub, waiting for next data on status pipe
- (void) hub_edpt_status_xfer( event.connection.hub_addr );
+ (void) hub_edpt_status_xfer(event.connection.hub_addr);
}
#endif
- break;
+ break;
- case HCD_EVENT_XFER_COMPLETE:
- {
+ case HCD_EVENT_XFER_COMPLETE: {
uint8_t const ep_addr = event.xfer_complete.ep_addr;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const ep_dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
- TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
+ TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, tu_str_xfer_result[event.xfer_complete.result]);
- if (event.dev_addr == 0)
- {
+ if (event.dev_addr == 0) {
// device 0 only has control endpoint
- TU_ASSERT(epnum == 0, );
- usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
- }
- else
- {
+ TU_ASSERT(epnum == 0,);
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
+ } else {
usbh_device_t* dev = get_device(event.dev_addr);
- TU_ASSERT(dev, );
+ TU_VERIFY(dev && dev->connected,);
- dev->ep_status[epnum][ep_dir].busy = 0;
+ dev->ep_status[epnum][ep_dir].busy = 0;
dev->ep_status[epnum][ep_dir].claimed = 0;
- if ( 0 == epnum )
- {
- usbh_control_xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
- }else
- {
- uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
- if(drv_id < USBH_CLASS_DRIVER_COUNT)
- {
- TU_LOG2("%s xfer callback\r\n", usbh_class_drivers[drv_id].name);
- usbh_class_drivers[drv_id].xfer_cb(event.dev_addr, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
- }
- else
- {
-#if CFG_TUH_API_EDPT_XFER
- tuh_xfer_cb_t complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb;
- if ( complete_cb )
- {
- tuh_xfer_t xfer =
- {
+ if (0 == epnum) {
+ usbh_control_xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result, event.xfer_complete.len);
+ } else {
+ // Prefer application callback over built-in one if available. This occurs when tuh_edpt_xfer() is used
+ // with enabled driver e.g HID endpoint
+ #if CFG_TUH_API_EDPT_XFER
+ tuh_xfer_cb_t const complete_cb = dev->ep_callback[epnum][ep_dir].complete_cb;
+ if ( complete_cb ) {
+ // re-construct xfer info
+ tuh_xfer_t xfer = {
.daddr = event.dev_addr,
.ep_addr = ep_addr,
.result = event.xfer_complete.result,
@@ -497,27 +582,33 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
.buffer = NULL, // not available
.complete_cb = complete_cb,
.user_data = dev->ep_callback[epnum][ep_dir].user_data
- };
-
- complete_cb(&xfer);
- }else
-#endif
- {
+ };
+ complete_cb(&xfer);
+ }else
+ #endif
+ {
+ uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
+ usbh_class_driver_t const* driver = get_driver(drv_id);
+ if (driver) {
+ TU_LOG_USBH("%s xfer callback\r\n", driver->name);
+ driver->xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
+ event.xfer_complete.len);
+ } else {
// no driver/callback responsible for this transfer
- TU_ASSERT(false, );
+ TU_ASSERT(false,);
}
-
}
}
}
+ break;
}
- break;
case USBH_EVENT_FUNC_CALL:
- if ( event.func_call.func ) event.func_call.func(event.func_call.param);
- break;
+ if (event.func_call.func) event.func_call.func(event.func_call.param);
+ break;
- default: break;
+ default:
+ break;
}
#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
@@ -531,53 +622,54 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
// Control transfer
//--------------------------------------------------------------------+
-static void _control_blocking_complete_cb(tuh_xfer_t* xfer)
-{
+static void _control_blocking_complete_cb(tuh_xfer_t* xfer) {
// update result
*((xfer_result_t*) xfer->user_data) = xfer->result;
}
// TODO timeout_ms is not supported yet
-bool tuh_control_xfer (tuh_xfer_t* xfer)
-{
+bool tuh_control_xfer (tuh_xfer_t* xfer) {
// EP0 with setup packet
TU_VERIFY(xfer->ep_addr == 0 && xfer->setup);
+ // Check if device is still connected (enumerating for dev0)
+ const uint8_t daddr = xfer->daddr;
+ if (daddr == 0) {
+ TU_VERIFY(_dev0.enumerating);
+ } else {
+ const usbh_device_t* dev = get_device(daddr);
+ TU_VERIFY(dev && dev->connected);
+ }
+
// pre-check to help reducing mutex lock
TU_VERIFY(_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
-
- uint8_t const daddr = xfer->daddr;
-
- // TODO probably better to use semaphore as resource management than mutex
- usbh_lock();
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
bool const is_idle = (_ctrl_xfer.stage == CONTROL_STAGE_IDLE);
- if (is_idle)
- {
+ if (is_idle) {
_ctrl_xfer.stage = CONTROL_STAGE_SETUP;
_ctrl_xfer.daddr = daddr;
_ctrl_xfer.actual_len = 0;
- _ctrl_xfer.request = (*xfer->setup);
_ctrl_xfer.buffer = xfer->buffer;
_ctrl_xfer.complete_cb = xfer->complete_cb;
_ctrl_xfer.user_data = xfer->user_data;
+ _usbh_epbuf.request = (*xfer->setup);
}
- usbh_unlock();
+ (void) osal_mutex_unlock(_usbh_mutex);
TU_VERIFY(is_idle);
const uint8_t rhport = usbh_get_rhport(daddr);
- TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->setup->bRequest] : "Unknown Request");
- TU_LOG2_VAR(xfer->setup);
- TU_LOG2("\r\n");
+ TU_LOG_USBH("[%u:%u] %s: ", rhport, daddr,
+ (xfer->setup->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME) ?
+ tu_str_std_request[xfer->setup->bRequest] : "Class Request");
+ TU_LOG_BUF_USBH(xfer->setup, 8);
- if (xfer->complete_cb)
- {
- TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
- }else
- {
+ if (xfer->complete_cb) {
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_usbh_epbuf.request) );
+ }else {
// blocking if complete callback is not provided
// change callback to internal blocking, and result as user argument
volatile xfer_result_t result = XFER_RESULT_INVALID;
@@ -586,19 +678,21 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
_ctrl_xfer.user_data = (uintptr_t) &result;
_ctrl_xfer.complete_cb = _control_blocking_complete_cb;
- TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
-
- while (result == XFER_RESULT_INVALID)
- {
- // only need to call task if not preempted RTOS
- #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO
- tuh_task();
- #endif
+ TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_usbh_epbuf.request) );
+ while (result == XFER_RESULT_INVALID) {
+ // Note: this can be called within an callback ie. part of tuh_task()
+ // therefore event with RTOS tuh_task() still need to be invoked
+ if (tuh_task_event_ready()) {
+ tuh_task();
+ }
// TODO probably some timeout to prevent hanged
}
- // update transfer result
+ // update transfer result, user_data is expected to point to xfer_result_t
+ if (xfer->user_data != 0) {
+ *((xfer_result_t*) xfer->user_data) = result;
+ }
xfer->result = result;
xfer->actual_len = _ctrl_xfer.actual_len;
}
@@ -606,21 +700,18 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage)
-{
- usbh_lock();
+TU_ATTR_ALWAYS_INLINE static inline void _set_control_xfer_stage(uint8_t stage) {
+ (void) osal_mutex_lock(_usbh_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
_ctrl_xfer.stage = stage;
- usbh_unlock();
+ (void) osal_mutex_unlock(_usbh_mutex);
}
-static void _xfer_complete(uint8_t daddr, xfer_result_t result)
-{
- TU_LOG2("\r\n");
+static void _control_xfer_complete(uint8_t daddr, xfer_result_t result) {
+ TU_LOG_USBH("\r\n");
// duplicate xfer since user can execute control transfer within callback
- tusb_control_request_t const request = _ctrl_xfer.request;
- tuh_xfer_t xfer_temp =
- {
+ tusb_control_request_t const request = _usbh_epbuf.request;
+ tuh_xfer_t xfer_temp = {
.daddr = daddr,
.ep_addr = 0,
.result = result,
@@ -631,60 +722,63 @@ static void _xfer_complete(uint8_t daddr, xfer_result_t result)
.user_data = _ctrl_xfer.user_data
};
- usbh_lock();
- _ctrl_xfer.stage = CONTROL_STAGE_IDLE;
- usbh_unlock();
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE);
- if (xfer_temp.complete_cb)
- {
+ if (xfer_temp.complete_cb) {
xfer_temp.complete_cb(&xfer_temp);
}
}
-static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) ep_addr;
- const uint8_t rhport = usbh_get_rhport(dev_addr);
- tusb_control_request_t const * request = &_ctrl_xfer.request;
+ const uint8_t rhport = usbh_get_rhport(daddr);
+ tusb_control_request_t const * request = &_usbh_epbuf.request;
- if (XFER_RESULT_SUCCESS != result)
- {
- TU_LOG1("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
+ if (XFER_RESULT_SUCCESS != result) {
+ TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
+ TU_LOG_BUF_USBH(request, 8);
// terminate transfer if any stage failed
- _xfer_complete(dev_addr, result);
- }else
- {
- switch(_ctrl_xfer.stage)
- {
+ _control_xfer_complete(daddr, result);
+ }else {
+ switch(_ctrl_xfer.stage) {
case CONTROL_STAGE_SETUP:
- if (request->wLength)
- {
+ if (request->wLength) {
// DATA stage: initial data toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_DATA);
- TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
+ TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
return true;
}
TU_ATTR_FALLTHROUGH;
case CONTROL_STAGE_DATA:
- if (request->wLength)
- {
- TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr);
- TU_LOG2_MEM(_ctrl_xfer.buffer, xferred_bytes, 2);
+ if (request->wLength) {
+ TU_LOG_USBH("[%u:%u] Control data:\r\n", rhport, daddr);
+ TU_LOG_MEM_USBH(_ctrl_xfer.buffer, xferred_bytes, 2);
}
_ctrl_xfer.actual_len = (uint16_t) xferred_bytes;
// ACK stage: toggle is always 1
_set_control_xfer_stage(CONTROL_STAGE_ACK);
- TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0) );
- break;
+ TU_ASSERT( hcd_edpt_xfer(rhport, daddr, tu_edpt_addr(0, 1 - request->bmRequestType_bit.direction), NULL, 0) );
+ break;
- case CONTROL_STAGE_ACK:
- _xfer_complete(dev_addr, result);
- break;
+ case CONTROL_STAGE_ACK: {
+ // Abort all pending transfers if SET_CONFIGURATION request
+ // NOTE: should we force closing all non-control endpoints in the future?
+ if (request->bRequest == TUSB_REQ_SET_CONFIGURATION && request->bmRequestType == 0x00) {
+ for(uint8_t epnum=1; epnum<CFG_TUH_ENDPOINT_MAX; epnum++) {
+ for(uint8_t dir=0; dir<2; dir++) {
+ tuh_edpt_abort_xfer(daddr, tu_edpt_addr(epnum, dir));
+ }
+ }
+ }
+
+ _control_xfer_complete(daddr, result);
+ break;
+ }
default: return false;
}
@@ -697,17 +791,15 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
//
//--------------------------------------------------------------------+
-bool tuh_edpt_xfer(tuh_xfer_t* xfer)
-{
- uint8_t const daddr = xfer->daddr;
+bool tuh_edpt_xfer(tuh_xfer_t* xfer) {
+ uint8_t const daddr = xfer->daddr;
uint8_t const ep_addr = xfer->ep_addr;
TU_VERIFY(daddr && ep_addr);
-
TU_VERIFY(usbh_edpt_claim(daddr, ep_addr));
- if ( !usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen, xfer->complete_cb, xfer->user_data) )
- {
+ if (!usbh_edpt_xfer_with_callback(daddr, ep_addr, xfer->buffer, (uint16_t) xfer->buflen,
+ xfer->complete_cb, xfer->user_data)) {
usbh_edpt_release(daddr, ep_addr);
return false;
}
@@ -715,69 +807,104 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer)
return true;
}
+bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) {
+ TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr);
+
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+
+ if (epnum == 0) {
+ // Also include dev0 for aborting enumerating
+ const uint8_t rhport = usbh_get_rhport(daddr);
+
+ // control transfer: only 1 control at a time, check if we are aborting the current one
+ TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE);
+ hcd_edpt_abort_xfer(rhport, daddr, ep_addr);
+ _set_control_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle
+ } else {
+ usbh_device_t* dev = get_device(daddr);
+ TU_VERIFY(dev);
+
+ TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy
+ hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr);
+
+ // mark as ready and release endpoint if transfer is aborted
+ dev->ep_status[epnum][dir].busy = false;
+ tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
+ }
+
+ return true;
+}
+
//--------------------------------------------------------------------+
// USBH API For Class Driver
//--------------------------------------------------------------------+
-uint8_t usbh_get_rhport(uint8_t dev_addr)
-{
- usbh_device_t* dev = get_device(dev_addr);
+uint8_t usbh_get_rhport(uint8_t dev_addr) {
+ usbh_device_t *dev = get_device(dev_addr);
return dev ? dev->rhport : _dev0.rhport;
}
-uint8_t* usbh_get_enum_buf(void)
-{
- return _usbh_ctrl_buf;
+uint8_t *usbh_get_enum_buf(void) {
+ return _usbh_epbuf.ctrl;
}
-void usbh_int_set(bool enabled)
-{
- // TODO all host controller if multiple is used
- if (enabled)
- {
+void usbh_int_set(bool enabled) {
+ // TODO all host controller if multiple are used since they shared the same event queue
+ if (enabled) {
hcd_int_enable(_usbh_controller);
- }else
- {
+ } else {
hcd_int_disable(_usbh_controller);
}
}
+void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr) {
+ hcd_event_t event = { 0 };
+ event.event_id = USBH_EVENT_FUNC_CALL;
+ event.func_call.func = func;
+ event.func_call.param = param;
+
+ queue_event(&event, in_isr);
+}
+
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-// TODO has some duplication code with device, refactor later
-bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr)
-{
+// Claim an endpoint for transfer
+bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) {
+ // Note: addr0 only use tuh_control_xfer
usbh_device_t* dev = get_device(dev_addr);
-
- // addr0 only use tuh_control_xfer
- TU_ASSERT(dev);
+ TU_ASSERT(dev && dev->connected);
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_VERIFY(tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex));
+ TU_LOG_USBH("[%u] Claimed EP 0x%02x\r\n", dev_addr, ep_addr);
- return tu_edpt_claim(&dev->ep_status[epnum][dir], _usbh_mutex);
+ return true;
}
-// TODO has some duplication code with device, refactor later
-bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr)
-{
+// Release an claimed endpoint due to failed transfer attempt
+bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) {
+ // Note: addr0 only use tuh_control_xfer
usbh_device_t* dev = get_device(dev_addr);
-
- // addr0 only use tuh_control_xfer
- TU_ASSERT(dev);
+ TU_VERIFY(dev && dev->connected);
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
+ TU_VERIFY(tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex));
+ TU_LOG_USBH("[%u] Released EP 0x%02x\r\n", dev_addr, ep_addr);
- return tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex);
+ return true;
}
-// TODO has some duplication code with device, refactor later
-bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+// Submit an transfer
+// TODO call usbh_edpt_release if failed
+bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
(void) complete_cb;
(void) user_data;
@@ -785,10 +912,10 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
TU_VERIFY(dev);
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
tu_edpt_state_t* ep_state = &dev->ep_status[epnum][dir];
- TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
+ TU_LOG_USBH(" Queue EP %02X with %u bytes ... \r\n", ep_addr, total_bytes);
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(ep_state->busy == 0);
@@ -802,27 +929,22 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
dev->ep_callback[epnum][dir].user_data = user_data;
#endif
- if ( hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes) )
- {
- TU_LOG2("OK\r\n");
+ if (hcd_edpt_xfer(dev->rhport, dev_addr, ep_addr, buffer, total_bytes)) {
+ TU_LOG_USBH("OK\r\n");
return true;
- }else
- {
+ } else {
// HCD error, mark endpoint as ready to allow next transfer
- ep_state->busy = 0;
+ ep_state->busy = 0;
ep_state->claimed = 0;
TU_LOG1("Failed\r\n");
- TU_BREAKPOINT();
+// TU_BREAKPOINT();
return false;
}
}
-static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
-{
- TU_LOG2("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
-
- tusb_desc_endpoint_t ep0_desc =
- {
+static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size) {
+ TU_LOG_USBH("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
+ tusb_desc_endpoint_t ep0_desc = {
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
.bEndpointAddress = 0,
@@ -834,21 +956,18 @@ static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, &ep0_desc);
}
-bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep)
-{
- TU_ASSERT( tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)) );
-
+bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const* desc_ep) {
+ TU_ASSERT(tu_edpt_validate(desc_ep, tuh_speed_get(dev_addr)));
return hcd_edpt_open(usbh_get_rhport(dev_addr), dev_addr, desc_ep);
}
-bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
+bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) {
usbh_device_t* dev = get_device(dev_addr);
TU_VERIFY(dev);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+
return dev->ep_status[epnum][dir].busy;
}
@@ -856,33 +975,38 @@ bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
// HCD Event Handler
//--------------------------------------------------------------------+
-void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info)
-{
+void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info) {
usbh_device_t const* dev = get_device(dev_addr);
-
- if (dev)
- {
- devtree_info->rhport = dev->rhport;
+ if (dev) {
+ devtree_info->rhport = dev->rhport;
devtree_info->hub_addr = dev->hub_addr;
devtree_info->hub_port = dev->hub_port;
- devtree_info->speed = dev->speed;
- }else
- {
- devtree_info->rhport = _dev0.rhport;
+ devtree_info->speed = dev->speed;
+ } else {
+ devtree_info->rhport = _dev0.rhport;
devtree_info->hub_addr = _dev0.hub_addr;
devtree_info->hub_port = _dev0.hub_port;
- devtree_info->speed = _dev0.speed;
+ devtree_info->speed = _dev0.speed;
}
}
-TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
-{
- switch (event->event_id)
- {
- default:
- osal_queue_send(_usbh_q, event, in_isr);
- break;
+TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr) {
+ switch (event->event_id) {
+ case HCD_EVENT_DEVICE_REMOVE:
+ // FIXME device remove from a hub need an HCD API for hcd to free up endpoint
+ // mark device as removing to prevent further xfer before the event is processed in usbh task
+
+ // Check if dev0 is removed
+ if ((event->rhport == _dev0.rhport) && (event->connection.hub_addr == _dev0.hub_addr) &&
+ (event->connection.hub_port == _dev0.hub_port)) {
+ _dev0.enumerating = 0;
+ }
+ break;
+
+ default: break;
}
+
+ queue_event(event, in_isr);
}
//--------------------------------------------------------------------+
@@ -890,14 +1014,11 @@ TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
//--------------------------------------------------------------------+
// generic helper to get a descriptor
-// if blocking, user_data could be pointed to xfer_result
+// if blocking, user_data is pointed to xfer_result
static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_DEVICE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_IN
@@ -907,9 +1028,7 @@ static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t
.wIndex = tu_htole16(language_id),
.wLength = tu_htole16(len)
};
-
- tuh_xfer_t xfer =
- {
+ tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
@@ -918,41 +1037,29 @@ static bool _get_descriptor(uint8_t daddr, uint8_t type, uint8_t index, uint16_t
.user_data = user_data
};
- bool const ret = tuh_control_xfer(&xfer);
-
- // if blocking, user_data could be pointed to xfer_result
- if ( !complete_cb && user_data )
- {
- *((xfer_result_t*) user_data) = xfer.result;
- }
-
- return ret;
+ return tuh_control_xfer(&xfer);
}
bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return _get_descriptor(daddr, type, index, 0x0000, buffer, len, complete_cb, user_data);
}
bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
len = tu_min16(len, sizeof(tusb_desc_device_t));
return tuh_descriptor_get(daddr, TUSB_DESC_DEVICE, 0, buffer, len, complete_cb, user_data);
}
bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return tuh_descriptor_get(daddr, TUSB_DESC_CONFIGURATION, index, buffer, len, complete_cb, user_data);
}
//------------- String Descriptor -------------//
bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return _get_descriptor(daddr, TUSB_DESC_STRING, index, language_id, buffer, len, complete_cb, user_data);
}
@@ -967,8 +1074,7 @@ bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id,
// Get product string descriptor
bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
usbh_device_t const* dev = get_device(daddr);
TU_VERIFY(dev && dev->i_product);
return tuh_descriptor_get_string(daddr, dev->i_product, language_id, buffer, len, complete_cb, user_data);
@@ -976,81 +1082,87 @@ bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void
// Get serial string descriptor
bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
usbh_device_t const* dev = get_device(daddr);
TU_VERIFY(dev && dev->i_serial);
return tuh_descriptor_get_string(daddr, dev->i_serial, language_id, buffer, len, complete_cb, user_data);
}
// Get HID report descriptor
-// if blocking, user_data could be pointed to xfer_result
+// if blocking, user_data is pointed to xfer_result
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
- TU_LOG2("HID Get Report Descriptor\r\n");
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_STANDARD,
- .direction = TUSB_DIR_IN
- },
- .bRequest = TUSB_REQ_GET_DESCRIPTOR,
- .wValue = tu_htole16(TU_U16(desc_type, index)),
- .wIndex = tu_htole16((uint16_t) itf_num),
- .wLength = len
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_USBH("HID Get Report Descriptor\r\n");
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = TUSB_REQ_GET_DESCRIPTOR,
+ .wValue = tu_htole16(TU_U16(desc_type, index)),
+ .wIndex = tu_htole16((uint16_t) itf_num),
+ .wLength = len
};
-
- tuh_xfer_t xfer =
- {
- .daddr = daddr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = buffer,
- .complete_cb = complete_cb,
- .user_data = user_data
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = buffer,
+ .complete_cb = complete_cb,
+ .user_data = user_data
};
- bool const ret = tuh_control_xfer(&xfer);
-
- // if blocking, user_data could be pointed to xfer_result
- if ( !complete_cb && user_data )
- {
- *((xfer_result_t*) user_data) = xfer.result;
- }
-
- return ret;
+ return tuh_control_xfer(&xfer);
}
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data)
-{
- TU_LOG2("Set Configuration = %d\r\n", config_num);
-
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_DEVICE,
- .type = TUSB_REQ_TYPE_STANDARD,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = TUSB_REQ_SET_CONFIGURATION,
- .wValue = tu_htole16(config_num),
- .wIndex = 0,
- .wLength = 0
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_USBH("Set Configuration = %d\r\n", config_num);
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_CONFIGURATION,
+ .wValue = tu_htole16(config_num),
+ .wIndex = 0,
+ .wLength = 0
+ };
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
};
- tuh_xfer_t xfer =
- {
- .daddr = daddr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = NULL,
- .complete_cb = complete_cb,
- .user_data = user_data
+ return tuh_control_xfer(&xfer);
+}
+
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_INTERFACE,
+ .wValue = tu_htole16(itf_alt),
+ .wIndex = tu_htole16(itf_num),
+ .wLength = 0
+ };
+ tuh_xfer_t xfer = {
+ .daddr = daddr,
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = complete_cb,
+ .user_data = user_data
};
return tuh_control_xfer(&xfer);
@@ -1065,110 +1177,148 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
TU_VERIFY(_async_func(__VA_ARGS__, NULL, (uintptr_t) &result), XFER_RESULT_TIMEOUT); \
return (uint8_t) result
-uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get, daddr, type, index, buffer, len);
}
-uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_device, daddr, buffer, len);
}
-uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_configuration, daddr, index, buffer, len);
}
-uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_hid_report, daddr, itf_num, desc_type, index, buffer, len);
}
-uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_string, daddr, index, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_manufacturer_string, daddr, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_product_string, daddr, language_id, buffer, len);
}
-uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len)
-{
+uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id,
+ void* buffer, uint16_t len) {
_CONTROL_SYNC_API(tuh_descriptor_get_serial_string, daddr, language_id, buffer, len);
}
//--------------------------------------------------------------------+
-//
+// Detaching
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE
-static inline bool is_hub_addr(uint8_t daddr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_hub_addr(uint8_t daddr) {
return (CFG_TUH_HUB > 0) && (daddr > CFG_TUH_DEVICE_MAX);
}
+//static void mark_removing_device_isr(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
+// for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
+// usbh_device_t *dev = &_usbh_devices[dev_id];
+// uint8_t const daddr = dev_id + 1;
+//
+// // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
+// if (dev->rhport == rhport && dev->connected &&
+// (hub_addr == 0 || dev->hub_addr == hub_addr) &&
+// (hub_port == 0 || dev->hub_port == hub_port)) {
+// if (is_hub_addr(daddr)) {
+// // If the device itself is a usb hub, mark all downstream devices.
+// // FIXME recursive calls
+// mark_removing_device_isr(rhport, daddr, 0);
+// }
+//
+// dev->removing = 1;
+// }
+// }
+//}
+
// a device unplugged from rhport:hub_addr:hub_port
-static void process_device_unplugged(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port)
-{
+static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hub_port) {
//------------- find the all devices (star-network) under port that is unplugged -------------//
// TODO mark as disconnected in ISR, also handle dev0
- for ( uint8_t dev_id = 0; dev_id < TU_ARRAY_SIZE(_usbh_devices); dev_id++ )
- {
- usbh_device_t* dev = &_usbh_devices[dev_id];
- uint8_t const dev_addr = dev_id+1;
+ uint32_t removing_hubs = 0;
+ do {
+ for (uint8_t dev_id = 0; dev_id < TOTAL_DEVICES; dev_id++) {
+ usbh_device_t* dev = &_usbh_devices[dev_id];
+ uint8_t const daddr = dev_id + 1;
- // TODO Hub multiple level
- if (dev->rhport == rhport &&
- (hub_addr == 0 || dev->hub_addr == hub_addr) && // hub_addr = 0 means roothub
- (hub_port == 0 || dev->hub_port == hub_port) && // hub_port = 0 means all devices of downstream hub
- dev->connected)
- {
- TU_LOG2(" Address = %u\r\n", dev_addr);
+ // hub_addr = 0 means roothub, hub_port = 0 means all devices of downstream hub
+ if (dev->rhport == rhport && dev->connected &&
+ (hub_addr == 0 || dev->hub_addr == hub_addr) &&
+ (hub_port == 0 || dev->hub_port == hub_port)) {
+ TU_LOG_USBH("[%u:%u:%u] unplugged address = %u\r\n", rhport, hub_addr, hub_port, daddr);
- if (is_hub_addr(dev_addr))
- {
- TU_LOG(USBH_DBG_LVL, "HUB address = %u is unmounted\r\n", dev_addr);
- // If the device itself is a usb hub, unplug downstream devices.
- // FIXME un-roll recursive calls to prevent potential stack overflow
- process_device_unplugged(rhport, dev_addr, 0);
- }else
- {
- // Invoke callback before closing driver
- if (tuh_umount_cb) tuh_umount_cb(dev_addr);
- }
+ if (is_hub_addr(daddr)) {
+ TU_LOG_USBH(" is a HUB device %u\r\n", daddr);
+ removing_hubs |= TU_BIT(dev_id - CFG_TUH_DEVICE_MAX);
+ } else {
+ // Invoke callback before closing driver (maybe call it later ?)
+ if (tuh_umount_cb) tuh_umount_cb(daddr);
+ }
- // Close class driver
- for (uint8_t drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
- {
- TU_LOG2("%s close\r\n", usbh_class_drivers[drv_id].name);
- usbh_class_drivers[drv_id].close(dev_addr);
+ // Close class driver
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
+ usbh_class_driver_t const* driver = get_driver(drv_id);
+ if (driver) driver->close(daddr);
+ }
+
+ hcd_device_close(rhport, daddr);
+ clear_device(dev);
+
+ // abort on-going control xfer on this device if any
+ if (_ctrl_xfer.daddr == daddr) _set_control_xfer_stage(CONTROL_STAGE_IDLE);
}
+ }
- hcd_device_close(rhport, dev_addr);
- clear_device(dev);
- // abort on-going control xfer if any
- if (_ctrl_xfer.daddr == dev_addr) _set_control_xfer_stage(CONTROL_STAGE_IDLE);
+ // if removing a hub, we need to remove its downstream devices
+ #if CFG_TUH_HUB
+ if (removing_hubs == 0) break;
+
+ // find a marked hub to process
+ for (uint8_t h_id = 0; h_id < CFG_TUH_HUB; h_id++) {
+ if (tu_bit_test(removing_hubs, h_id)) {
+ removing_hubs &= ~TU_BIT(h_id);
+
+ // update hub_addr and hub_port for next loop
+ hub_addr = h_id + 1 + CFG_TUH_DEVICE_MAX;
+ hub_port = 0;
+ break;
+ }
}
- }
+ #else
+ (void) removing_hubs;
+ break;
+ #endif
+ } while(1);
}
//--------------------------------------------------------------------+
// Enumeration Process
// is a lengthy process with a series of control transfer to configure
// newly attached device.
-// NOTE: due to the shared _usbh_ctrl_buf, we must complete enumerating
+// NOTE: due to the shared control buffer, we must complete enumerating
// one device before enumerating another one.
//--------------------------------------------------------------------+
enum {
+ ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms
+ ENUM_DEBOUNCING_DELAY_MS = 450, // when plug/unplug a device, physical connection can be bouncing and may
+ // generate a series of attach/detach event. This delay wait for stable connection
+};
+
+enum {
ENUM_IDLE,
ENUM_RESET_1, // 1st reset when attached
//ENUM_HUB_GET_STATUS_1,
@@ -1191,329 +1341,345 @@ static bool _parse_configuration_descriptor (uint8_t dev_addr, tusb_desc_configu
static void enum_full_complete(void);
// process device enumeration
-static void process_enumeration(tuh_xfer_t* xfer)
-{
- if (XFER_RESULT_SUCCESS != xfer->result)
- {
- // stop enumeration, maybe we could retry this
- enum_full_complete();
+static void process_enumeration(tuh_xfer_t* xfer) {
+ // Retry a few times with transfers in enumeration since device can be unstable when starting up
+ enum {
+ ATTEMPT_COUNT_MAX = 3,
+ ATTEMPT_DELAY_MS = 100
+ };
+ static uint8_t failed_count = 0;
+
+ if (XFER_RESULT_SUCCESS != xfer->result) {
+ // retry if not reaching max attempt
+ bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX);
+ if ( retry ) {
+ failed_count++;
+ tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit
+ TU_LOG1("Enumeration attempt %u\r\n", failed_count);
+ retry = tuh_control_xfer(xfer);
+ }
+
+ if (!retry) {
+ enum_full_complete();
+ }
+
return;
}
+ failed_count = 0;
uint8_t const daddr = xfer->daddr;
uintptr_t const state = xfer->user_data;
- switch(state)
- {
-#if CFG_TUH_HUB
+ switch (state) {
+ #if CFG_TUH_HUB
//case ENUM_HUB_GET_STATUS_1: break;
- case ENUM_HUB_CLEAR_RESET_1:
- {
+ case ENUM_HUB_CLEAR_RESET_1: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+ memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
- if ( !port_status.status.connection )
- {
+ if (!port_status.status.connection) {
// device unplugged while delaying, nothing else to do
enum_full_complete();
return;
}
_dev0.speed = (port_status.status.high_speed) ? TUSB_SPEED_HIGH :
- (port_status.status.low_speed ) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+ (port_status.status.low_speed) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
// Acknowledge Port Reset Change
- if (port_status.change.reset)
- {
- hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_ADDR0_DEVICE_DESC);
+ if (port_status.change.reset) {
+ hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port,
+ process_enumeration, ENUM_ADDR0_DEVICE_DESC);
}
+ break;
}
- break;
case ENUM_HUB_GET_STATUS_2:
- osal_task_delay(RESET_DELAY);
- TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2), );
- break;
+ tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
+ TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_epbuf.ctrl,
+ process_enumeration, ENUM_HUB_CLEAR_RESET_2),);
+ break;
- case ENUM_HUB_CLEAR_RESET_2:
- {
+ case ENUM_HUB_CLEAR_RESET_2: {
hub_port_status_response_t port_status;
- memcpy(&port_status, _usbh_ctrl_buf, sizeof(hub_port_status_response_t));
+ memcpy(&port_status, _usbh_epbuf.ctrl, sizeof(hub_port_status_response_t));
// Acknowledge Port Reset Change if Reset Successful
- if (port_status.change.reset)
- {
- TU_ASSERT( hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_SET_ADDR), );
+ if (port_status.change.reset) {
+ TU_ASSERT(hub_port_clear_reset_change(_dev0.hub_addr, _dev0.hub_port,
+ process_enumeration, ENUM_SET_ADDR),);
}
+ break;
}
- break;
-#endif
+ #endif
- case ENUM_ADDR0_DEVICE_DESC:
- {
+ case ENUM_ADDR0_DEVICE_DESC: {
// TODO probably doesn't need to open/close each enumeration
uint8_t const addr0 = 0;
- TU_ASSERT( usbh_edpt_control_open(addr0, 8), );
+ TU_ASSERT(usbh_edpt_control_open(addr0, 8),);
// Get first 8 bytes of device descriptor for Control Endpoint size
- TU_LOG2("Get 8 byte of Device Descriptor\r\n");
- TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_ctrl_buf, 8, process_enumeration, ENUM_SET_ADDR), );
+ TU_LOG_USBH("Get 8 byte of Device Descriptor\r\n");
+ TU_ASSERT(tuh_descriptor_get_device(addr0, _usbh_epbuf.ctrl, 8,
+ process_enumeration, ENUM_SET_ADDR),);
+ break;
}
- break;
#if 0
- case ENUM_RESET_2:
- // TODO not used by now, but may be needed for some devices !?
- // Reset device again before Set Address
- TU_LOG2("Port reset2 \r\n");
- if (_dev0.hub_addr == 0)
- {
- // connected directly to roothub
- hcd_port_reset( _dev0.rhport );
- osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
- // sof of controller may not running while reseting
- hcd_port_reset_end(_dev0.rhport);
- // TODO: fall through to SET ADDRESS, refactor later
- }
- #if CFG_TUH_HUB
- else
- {
- // after RESET_DELAY the hub_port_reset() already complete
- TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port, process_enumeration, ENUM_HUB_GET_STATUS_2), );
- break;
- }
- #endif
- __attribute__((fallthrough));
+ case ENUM_RESET_2:
+ // TODO not used by now, but may be needed for some devices !?
+ // Reset device again before Set Address
+ TU_LOG_USBH("Port reset2 \r\n");
+ if (_dev0.hub_addr == 0) {
+ // connected directly to roothub
+ hcd_port_reset( _dev0.rhport );
+ tusb_time_delay_ms_api(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
+ // sof of controller may not running while resetting
+ hcd_port_reset_end(_dev0.rhport);
+ // TODO: fall through to SET ADDRESS, refactor later
+ }
+#if CFG_TUH_HUB
+ else {
+ // after RESET_DELAY the hub_port_reset() already complete
+ TU_ASSERT( hub_port_reset(_dev0.hub_addr, _dev0.hub_port,
+ process_enumeration, ENUM_HUB_GET_STATUS_2), );
+ break;
+ }
+#endif
+ TU_ATTR_FALLTHROUGH;
#endif
case ENUM_SET_ADDR:
enum_request_set_addr();
- break;
+ break;
- case ENUM_GET_DEVICE_DESC:
- {
- uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
+ case ENUM_GET_DEVICE_DESC: {
+ // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
+ tusb_time_delay_ms_api(2);
+
+ const uint8_t new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
usbh_device_t* new_dev = get_device(new_addr);
- TU_ASSERT(new_dev, );
+ TU_ASSERT(new_dev,);
new_dev->addressed = 1;
// Close device 0
hcd_device_close(_dev0.rhport, 0);
// open control pipe for new address
- TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size), );
+ TU_ASSERT(usbh_edpt_control_open(new_addr, new_dev->ep0_size),);
// Get full device descriptor
- TU_LOG2("Get Device Descriptor\r\n");
- TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_ctrl_buf, sizeof(tusb_desc_device_t), process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC), );
+ TU_LOG_USBH("Get Device Descriptor\r\n");
+ TU_ASSERT(tuh_descriptor_get_device(new_addr, _usbh_epbuf.ctrl, sizeof(tusb_desc_device_t),
+ process_enumeration, ENUM_GET_9BYTE_CONFIG_DESC),);
+ break;
}
- break;
- case ENUM_GET_9BYTE_CONFIG_DESC:
- {
- tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+ case ENUM_GET_9BYTE_CONFIG_DESC: {
+ tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
usbh_device_t* dev = get_device(daddr);
- TU_ASSERT(dev, );
+ TU_ASSERT(dev,);
- dev->vid = desc_device->idVendor;
- dev->pid = desc_device->idProduct;
+ dev->vid = desc_device->idVendor;
+ dev->pid = desc_device->idProduct;
dev->i_manufacturer = desc_device->iManufacturer;
- dev->i_product = desc_device->iProduct;
- dev->i_serial = desc_device->iSerialNumber;
+ dev->i_product = desc_device->iProduct;
+ dev->i_serial = desc_device->iSerialNumber;
if (tuh_desc_device_cb) tuh_desc_device_cb(daddr, (tusb_desc_device_t const*) _usbh_ctrl_buf);
// Get 9-byte for total length
uint8_t const config_idx = CONFIG_NUM - 1;
- TU_LOG2("Get Configuration[0] Descriptor (9 bytes)\r\n");
- TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, 9, process_enumeration, ENUM_GET_FULL_CONFIG_DESC), );
+ TU_LOG_USBH("Get Configuration[0] Descriptor (9 bytes)\r\n");
+ TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_epbuf.ctrl, 9,
+ process_enumeration, ENUM_GET_FULL_CONFIG_DESC),);
+ break;
}
- break;
- case ENUM_GET_FULL_CONFIG_DESC:
- {
- uint8_t const * desc_config = _usbh_ctrl_buf;
+ case ENUM_GET_FULL_CONFIG_DESC: {
+ uint8_t const* desc_config = _usbh_epbuf.ctrl;
// Use offsetof to avoid pointer to the odd/misaligned address
- uint16_t const total_len = tu_le16toh( tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)) );
+ uint16_t const total_len = tu_le16toh(
+ tu_unaligned_read16(desc_config + offsetof(tusb_desc_configuration_t, wTotalLength)));
// TODO not enough buffer to hold configuration descriptor
- TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE, );
+ TU_ASSERT(total_len <= CFG_TUH_ENUMERATION_BUFSIZE,);
// Get full configuration descriptor
uint8_t const config_idx = CONFIG_NUM - 1;
- TU_LOG2("Get Configuration[0] Descriptor\r\n");
- TU_ASSERT( tuh_descriptor_get_configuration(daddr, config_idx, _usbh_ctrl_buf, total_len, process_enumeration, ENUM_SET_CONFIG), );
+ TU_LOG_USBH("Get Configuration[0] Descriptor\r\n");
+ TU_ASSERT(tuh_descriptor_get_configuration(daddr, config_idx, _usbh_epbuf.ctrl, total_len,
+ process_enumeration, ENUM_SET_CONFIG),);
+ break;
}
- break;
case ENUM_SET_CONFIG:
- // Got the whole configuration descriptor. Make a copy
if (tuh_desc_config_cb) tuh_desc_config_cb(daddr, (const tusb_desc_configuration_t*) _usbh_ctrl_buf);
- // Parse configuration & set up drivers
- // Driver open aren't allowed to make any usb transfer yet
- TU_ASSERT( _parse_configuration_descriptor(daddr, (const tusb_desc_configuration_t* ) _usbh_ctrl_buf), );
-
- TU_ASSERT( tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER), );
- break;
+ TU_ASSERT(tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER),);
+ break;
- case ENUM_CONFIG_DRIVER:
- {
- TU_LOG2("Device configured\r\n");
+ case ENUM_CONFIG_DRIVER: {
+ TU_LOG_USBH("Device configured\r\n");
usbh_device_t* dev = get_device(daddr);
- TU_ASSERT(dev, );
+ TU_ASSERT(dev,);
dev->configured = 1;
- // Start the Set Configuration process for interfaces (itf = DRVID_INVALID)
+ // Parse configuration & set up drivers
+ // driver_open() must not make any usb transfer
+ TU_ASSERT(_parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_epbuf.ctrl),);
+
+ // Start the Set Configuration process for interfaces (itf = TUSB_INDEX_INVALID_8)
// Since driver can perform control transfer within its set_config, this is done asynchronously.
// The process continue with next interface when class driver complete its sequence with usbh_driver_set_config_complete()
- // TODO use separated API instead of using DRVID_INVALID
- usbh_driver_set_config_complete(daddr, DRVID_INVALID);
+ // TODO use separated API instead of using TUSB_INDEX_INVALID_8
+ usbh_driver_set_config_complete(daddr, TUSB_INDEX_INVALID_8);
+ break;
}
- break;
default:
// stop enumeration if unknown state
enum_full_complete();
- break;
+ break;
}
}
-static bool enum_new_device(hcd_event_t* event)
-{
- _dev0.rhport = event->rhport;
+
+
+static bool enum_new_device(hcd_event_t* event) {
+ _dev0.rhport = event->rhport;
_dev0.hub_addr = event->connection.hub_addr;
_dev0.hub_port = event->connection.hub_port;
- if (_dev0.hub_addr == 0)
- {
- // connected/disconnected directly with roothub
- // wait until device is stable TODO non blocking
+ if (_dev0.hub_addr == 0) {
+ // connected directly to roothub
hcd_port_reset(_dev0.rhport);
- osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since
- // sof of controller may not running while reseting
- hcd_port_reset_end( _dev0.rhport);
+
+ // Since we are in middle of rhport reset, frame number is not available yet.
+ // need to depend on tusb_time_millis_api()
+ tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS);
+
+ hcd_port_reset_end(_dev0.rhport);
+
+ // wait until device connection is stable TODO non blocking
+ tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
// device unplugged while delaying
- if ( !hcd_port_connect_status(_dev0.rhport) ) return true;
+ if (!hcd_port_connect_status(_dev0.rhport)) {
+ enum_full_complete();
+ return true;
+ }
- _dev0.speed = hcd_port_speed_get(_dev0.rhport );
- TU_LOG2("%s Speed\r\n", tu_str_speed[_dev0.speed]);
+ _dev0.speed = hcd_port_speed_get(_dev0.rhport);
+ TU_LOG_USBH("%s Speed\r\n", tu_str_speed[_dev0.speed]);
// fake transfer to kick-off the enumeration process
tuh_xfer_t xfer;
- xfer.daddr = 0;
- xfer.result = XFER_RESULT_SUCCESS;
+ xfer.daddr = 0;
+ xfer.result = XFER_RESULT_SUCCESS;
xfer.user_data = ENUM_ADDR0_DEVICE_DESC;
process_enumeration(&xfer);
-
}
#if CFG_TUH_HUB
- else
- {
- // connected/disconnected via external hub
- // wait until device is stable
- osal_task_delay(RESET_DELAY);
+ else {
+ // connected via external hub
+ // wait until device connection is stable TODO non blocking
+ tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS);
// ENUM_HUB_GET_STATUS
- //TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete, 0) );
- TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_1) );
+ TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_epbuf.ctrl,
+ process_enumeration, ENUM_HUB_CLEAR_RESET_1));
}
#endif // hub
return true;
}
-static uint8_t get_new_address(bool is_hub)
-{
+static uint8_t get_new_address(bool is_hub) {
uint8_t start;
uint8_t end;
- if ( is_hub )
- {
+
+ if ( is_hub ) {
start = CFG_TUH_DEVICE_MAX;
end = start + CFG_TUH_HUB;
- }else
- {
+ }else {
start = 0;
end = start + CFG_TUH_DEVICE_MAX;
}
- for ( uint8_t idx = start; idx < end; idx++)
- {
+ for (uint8_t idx = start; idx < end; idx++) {
if (!_usbh_devices[idx].connected) return (idx+1);
}
return 0; // invalid address
}
-static bool enum_request_set_addr(void)
-{
- tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+static bool enum_request_set_addr(void) {
+ tusb_desc_device_t const* desc_device = (tusb_desc_device_t const*) _usbh_epbuf.ctrl;
// Get new address
uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
TU_ASSERT(new_addr != 0);
-
- TU_LOG2("Set Address = %d\r\n", new_addr);
+ TU_LOG_USBH("Set Address = %d\r\n", new_addr);
usbh_device_t* new_dev = get_device(new_addr);
-
- new_dev->rhport = _dev0.rhport;
- new_dev->hub_addr = _dev0.hub_addr;
- new_dev->hub_port = _dev0.hub_port;
- new_dev->speed = _dev0.speed;
+ new_dev->rhport = _dev0.rhport;
+ new_dev->hub_addr = _dev0.hub_addr;
+ new_dev->hub_port = _dev0.hub_port;
+ new_dev->speed = _dev0.speed;
new_dev->connected = 1;
- new_dev->ep0_size = desc_device->bMaxPacketSize0;
+ new_dev->ep0_size = desc_device->bMaxPacketSize0;
- tusb_control_request_t const request =
- {
- .bmRequestType_bit =
- {
- .recipient = TUSB_REQ_RCPT_DEVICE,
- .type = TUSB_REQ_TYPE_STANDARD,
- .direction = TUSB_DIR_OUT
- },
- .bRequest = TUSB_REQ_SET_ADDRESS,
- .wValue = tu_htole16(new_addr),
- .wIndex = 0,
- .wLength = 0
+ tusb_control_request_t const request = {
+ .bmRequestType_bit = {
+ .recipient = TUSB_REQ_RCPT_DEVICE,
+ .type = TUSB_REQ_TYPE_STANDARD,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = TUSB_REQ_SET_ADDRESS,
+ .wValue = tu_htole16(new_addr),
+ .wIndex = 0,
+ .wLength = 0
};
-
- tuh_xfer_t xfer =
- {
- .daddr = 0, // dev0
- .ep_addr = 0,
- .setup = &request,
- .buffer = NULL,
- .complete_cb = process_enumeration,
- .user_data = ENUM_GET_DEVICE_DESC
+ tuh_xfer_t xfer = {
+ .daddr = 0, // dev0
+ .ep_addr = 0,
+ .setup = &request,
+ .buffer = NULL,
+ .complete_cb = process_enumeration,
+ .user_data = ENUM_GET_DEVICE_DESC
};
- TU_ASSERT( tuh_control_xfer(&xfer) );
-
+ TU_ASSERT(tuh_control_xfer(&xfer));
return true;
}
-static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg)
-{
+static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg) {
usbh_device_t* dev = get_device(dev_addr);
-
- uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength);
+ uint16_t const total_len = tu_le16toh(desc_cfg->wTotalLength);
+ uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + total_len;
uint8_t const* p_desc = tu_desc_next(desc_cfg);
+ TU_LOG_USBH("Parsing Configuration descriptor (wTotalLength = %u)\r\n", total_len);
+
// parse each interfaces
- while( p_desc < desc_end )
- {
+ while( p_desc < desc_end ) {
+ if ( 0 == tu_desc_len(p_desc) ) {
+ // A zero length descriptor indicates that the device is off spec (e.g. wrong wTotalLength).
+ // Parsed interfaces should still be usable
+ TU_LOG_USBH("Encountered a zero-length descriptor after %u bytes\r\n", (uint32_t)p_desc - (uint32_t)desc_cfg);
+ break;
+ }
+
uint8_t assoc_itf_count = 1;
// Class will always starts with Interface Association (if any) and then Interface descriptor
- if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
- {
+ if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) ) {
tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
assoc_itf_count = desc_iad->bInterfaceCount;
@@ -1529,12 +1695,21 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
#if CFG_TUH_MIDI
// MIDI has 2 interfaces (Audio Control v1 + MIDIStreaming) but does not have IAD
- // manually increase the associated count
+ // manually force associated count = 2
if (1 == assoc_itf_count &&
TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
- AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol)
- {
+ AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
+ assoc_itf_count = 2;
+ }
+#endif
+
+#if CFG_TUH_CDC
+ // Some legacy CDC device does not use IAD but rather use device class as hint to combine 2 interfaces
+ // manually force associated count = 2
+ if (1 == assoc_itf_count &&
+ TUSB_CLASS_CDC == desc_itf->bInterfaceClass &&
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == desc_itf->bInterfaceSubClass) {
assoc_itf_count = 2;
}
#endif
@@ -1543,23 +1718,18 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
TU_ASSERT(drv_len >= sizeof(tusb_desc_interface_t));
// Find driver for this interface
- uint8_t drv_id;
- for (drv_id = 0; drv_id < USBH_CLASS_DRIVER_COUNT; drv_id++)
- {
- usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
-
- if ( driver->open(dev->rhport, dev_addr, desc_itf, drv_len) )
- {
+ for (uint8_t drv_id = 0; drv_id < TOTAL_DRIVER_COUNT; drv_id++) {
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+ if (driver && driver->open(dev->rhport, dev_addr, desc_itf, drv_len) ) {
// open successfully
- TU_LOG2(" %s opened\r\n", driver->name);
+ TU_LOG_USBH(" %s opened\r\n", driver->name);
// bind (associated) interfaces to found driver
- for(uint8_t i=0; i<assoc_itf_count; i++)
- {
+ for(uint8_t i=0; i<assoc_itf_count; i++) {
uint8_t const itf_num = desc_itf->bInterfaceNumber+i;
// Interface number must not be used already
- TU_ASSERT( DRVID_INVALID == dev->itf2drv[itf_num] );
+ TU_ASSERT( TUSB_INDEX_INVALID_8 == dev->itf2drv[itf_num] );
dev->itf2drv[itf_num] = drv_id;
}
@@ -1569,10 +1739,9 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
break; // exit driver find loop
}
- if( drv_id >= USBH_CLASS_DRIVER_COUNT )
- {
- TU_LOG(USBH_DBG_LVL, "Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
- desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
+ if ( drv_id == TOTAL_DRIVER_COUNT - 1 ) {
+ TU_LOG_USBH("[%u:%u] Interface %u: class = %u subclass = %u protocol = %u is not supported\r\n",
+ dev->rhport, dev_addr, desc_itf->bInterfaceNumber, desc_itf->bInterfaceClass, desc_itf->bInterfaceSubClass, desc_itf->bInterfaceProtocol);
}
}
@@ -1583,42 +1752,39 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur
return true;
}
-void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num)
-{
+void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) {
usbh_device_t* dev = get_device(dev_addr);
- for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++)
- {
+ for(itf_num++; itf_num < CFG_TUH_INTERFACE_MAX; itf_num++) {
// continue with next valid interface
- // TODO skip IAD binding interface such as CDCs
+ // IAD binding interface such as CDCs should return itf_num + 1 when complete
+ // with usbh_driver_set_config_complete()
uint8_t const drv_id = dev->itf2drv[itf_num];
- if (drv_id != DRVID_INVALID)
- {
- usbh_class_driver_t const * driver = &usbh_class_drivers[drv_id];
- TU_LOG2("%s set config: itf = %u\r\n", driver->name, itf_num);
+ usbh_class_driver_t const * driver = get_driver(drv_id);
+ if (driver) {
+ TU_LOG_USBH("%s set config: itf = %u\r\n", driver->name, itf_num);
driver->set_config(dev_addr, itf_num);
break;
}
}
// all interface are configured
- if (itf_num == CFG_TUH_INTERFACE_MAX)
- {
+ if (itf_num == CFG_TUH_INTERFACE_MAX) {
enum_full_complete();
- if (is_hub_addr(dev_addr))
- {
- TU_LOG(USBH_DBG_LVL, "HUB address = %u is mounted\r\n", dev_addr);
- }else
- {
+ if (is_hub_addr(dev_addr)) {
+ 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);
}
}
}
-static void enum_full_complete(void)
-{
+static void enum_full_complete(void) {
+ // mark enumeration as complete
+ _dev0.enumerating = 0;
+
#if CFG_TUH_HUB
// get next hub status
if (_dev0.hub_addr) hub_edpt_status_xfer(_dev0.hub_addr);
diff --git a/src/host/usbh.h b/src/host/usbh.h
index b39d1f205..500b044f8 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -47,18 +47,17 @@ typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
// it is advised to initialize it using member name
// Note2: not all field is available/meaningful in callback,
// some info is not saved by usbh to save SRAM
-struct tuh_xfer_s
-{
+struct tuh_xfer_s {
uint8_t daddr;
uint8_t ep_addr;
-
+ uint8_t TU_RESERVED; // reserved
xfer_result_t result;
+
uint32_t actual_len; // excluding setup packet
- union
- {
+ union {
tusb_control_request_t const* setup; // setup packet pointer if control transfer
- uint32_t buflen; // expected length if not control transfer (not available in callback)
+ uint32_t buflen; // expected length if not control transfer (not available in callback)
};
uint8_t* buffer; // not available in callback if not control transfer
@@ -68,12 +67,31 @@ struct tuh_xfer_s
// uint32_t timeout_ms; // place holder, not supported yet
};
-// ConfigID for tuh_config()
-enum
-{
- TUH_CFGID_RPI_PIO_USB_CONFIGURATION = OPT_MCU_RP2040 << 8 // cfg_param: pio_usb_configuration_t
+// Subject to change
+typedef struct {
+ uint8_t daddr;
+ tusb_desc_interface_t desc;
+} tuh_itf_info_t;
+
+// ConfigID for tuh_configure()
+enum {
+ TUH_CFGID_INVALID = 0,
+ TUH_CFGID_RPI_PIO_USB_CONFIGURATION = 100, // cfg_param: pio_usb_configuration_t
+ TUH_CFGID_MAX3421 = 200,
};
+typedef struct {
+ uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage
+ uint8_t cpuctl; // R16: CPU Control Register
+ uint8_t pinctl; // R17: Pin Control Register. FDUPSPI bit is ignored
+} tuh_configure_max3421_t;
+
+typedef union {
+ // For TUH_CFGID_RPI_PIO_USB_CONFIGURATION use pio_usb_configuration_t
+
+ tuh_configure_max3421_t max3421;
+} tuh_configure_param_t;
+
//--------------------------------------------------------------------+
// APPLICATION CALLBACK
//--------------------------------------------------------------------+
@@ -84,12 +102,18 @@ TU_ATTR_WEAK void tuh_desc_device_cb(uint8_t daddr, const tusb_desc_device_t *de
// Give the application an opportunity to grab the configuration descriptor
TU_ATTR_WEAK void tuh_desc_config_cb(uint8_t daddr, const tusb_desc_configuration_t *desc_config);
-// Invoked when device is mounted (configured)
+// Invoked when a device is mounted (configured)
TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
-/// Invoked when device is unmounted (bus reset/unplugged)
+// Invoked when a device failed to mount during enumeration process
+// TU_ATTR_WEAK 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);
+// 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);
+
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
@@ -98,12 +122,28 @@ TU_ATTR_WEAK void tuh_umount_cb(uint8_t daddr);
// Should be called before tuh_init()
// - cfg_id : configure ID (TBD)
// - cfg_param: configure data, structure depends on the ID
-bool tuh_configure(uint8_t controller_id, uint32_t cfg_id, const void* cfg_param);
+bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
+
+// New API to replace tuh_init() to init host stack on specific roothub port
+bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
// Init host stack
-bool tuh_init(uint8_t controller_id);
+#if TUSB_VERSION_NUMBER > 2000 // 0.20.0
+TU_ATTR_DEPRECATED("Please use tusb_init(rhport, rh_init) instead")
+#endif
+TU_ATTR_ALWAYS_INLINE static inline bool tuh_init(uint8_t rhport) {
+ const tusb_rhport_init_t rh_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL,
+ };
+ return tuh_rhport_init(rhport, &rh_init);
+}
+
+// Deinit host stack on rhport
+bool tuh_deinit(uint8_t rhport);
-// Check if host stack is already initialized
+// Check if host stack is already initialized with any roothub ports
+// To check if an rhport is initialized, use tuh_rhport_is_active()
bool tuh_inited(void);
// Task function should be called in main/rtos loop, extended version of tuh_task()
@@ -113,20 +153,37 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr);
// Task function should be called in main/rtos loop
TU_ATTR_ALWAYS_INLINE static inline
-void tuh_task(void)
-{
+void tuh_task(void) {
tuh_task_ext(UINT32_MAX, false);
}
+// Check if there is pending events need processing by tuh_task()
+bool tuh_task_event_ready(void);
+
#ifndef _TUSB_HCD_H_
-extern void hcd_int_handler(uint8_t rhport);
+extern void hcd_int_handler(uint8_t rhport, bool in_isr);
#endif
-// Interrupt handler, name alias to HCD
-#define tuh_int_handler hcd_int_handler
+// Interrupt handler alias to HCD with in_isr as optional parameter
+#define _tuh_int_handler_arg0() TU_VERIFY_STATIC(false, "tuh_int_handler() must have 1 or 2 arguments")
+#define _tuh_int_handler_arg1(_rhport) hcd_int_handler(_rhport, true)
+#define _tuh_int_handler_arg2(_rhport, _in_isr) hcd_int_handler(_rhport, _in_isr)
+#define tuh_int_handler(...) TU_FUNC_OPTIONAL_ARG(_tuh_int_handler, __VA_ARGS__)
+// Check if roothub port is initialized and active as a host
+bool tuh_rhport_is_active(uint8_t rhport);
+
+// Assert/de-assert Bus Reset signal to roothub port. USB specs: it should last 10-50ms
+bool tuh_rhport_reset_bus(uint8_t rhport, bool active);
+
+//--------------------------------------------------------------------+
+// Device API
+//--------------------------------------------------------------------+
+
+// Get VID/PID of device
bool tuh_vid_pid_get(uint8_t daddr, uint16_t* vid, uint16_t* pid);
+// Get speed of device
tusb_speed_t tuh_speed_get(uint8_t daddr);
// Check if device is connected and configured
@@ -134,8 +191,7 @@ bool tuh_mounted(uint8_t daddr);
// Check if device is suspended
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_suspended(uint8_t daddr)
-{
+bool tuh_suspended(uint8_t daddr) {
// TODO implement suspend & resume on host
(void) daddr;
return false;
@@ -143,8 +199,7 @@ bool tuh_suspended(uint8_t daddr)
// Check if device is ready to communicate with
TU_ATTR_ALWAYS_INLINE static inline
-bool tuh_ready(uint8_t daddr)
-{
+bool tuh_ready(uint8_t daddr) {
return tuh_mounted(daddr) && !tuh_suspended(daddr);
}
@@ -162,15 +217,26 @@ bool tuh_control_xfer(tuh_xfer_t* xfer);
// - sync : blocking if complete callback is NULL.
bool tuh_edpt_xfer(tuh_xfer_t* xfer);
-// Open an non-control endpoint
-bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep);
+// Open a non-control endpoint
+bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
// Set Configuration (control transfer)
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+// Set Interface (control transfer)
+// true on success, false if there is on-going control transfer or incorrect parameters
+// if complete_cb == NULL i.e blocking, user_data should be pointed to xfer_reuslt_t*
+bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
//--------------------------------------------------------------------+
// Descriptors Asynchronous (non-blocking)
//--------------------------------------------------------------------+
diff --git a/src/host/usbh_classdriver.h b/src/host/usbh_pvt.h
index c156afea0..95de915e9 100644
--- a/src/host/usbh_classdriver.h
+++ b/src/host/usbh_pvt.h
@@ -24,32 +24,46 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_USBH_CLASSDRIVER_H_
-#define _TUSB_USBH_CLASSDRIVER_H_
+#ifndef _TUSB_USBH_PVT_H_
+#define _TUSB_USBH_PVT_H_
#include "osal/osal.h"
#include "common/tusb_fifo.h"
+#include "common/tusb_private.h"
#ifdef __cplusplus
extern "C" {
#endif
+#define TU_LOG_USBH(...) TU_LOG(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_MEM_USBH(...) TU_LOG_MEM(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_BUF_USBH(...) TU_LOG_BUF(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_INT_USBH(...) TU_LOG_INT(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
+#define TU_LOG_HEX_USBH(...) TU_LOG_HEX(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
+
+enum {
+ USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
+};
+
//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+
typedef struct {
- #if CFG_TUSB_DEBUG >= 2
char const* name;
- #endif
-
- void (* const init )(void);
+ bool (* const init )(void);
+ bool (* const deinit )(void);
bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void (* const close )(uint8_t dev_addr);
} usbh_class_driver_t;
+// 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;
+
// 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);
@@ -59,6 +73,8 @@ uint8_t* usbh_get_enum_buf(void);
void usbh_int_set(bool enabled);
+void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr);
+
//--------------------------------------------------------------------+
// USBH Endpoint API
//--------------------------------------------------------------------+
@@ -68,12 +84,10 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
TU_ATTR_ALWAYS_INLINE
-static inline bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+static inline bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) {
return usbh_edpt_xfer_with_callback(dev_addr, ep_addr, buffer, total_bytes, NULL, 0);
}
-
// Claim an endpoint before submitting a transfer.
// If caller does not make any transfer, it must release endpoint for others.
bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr);
diff --git a/src/osal/osal.h b/src/osal/osal.h
index 9d11866df..38d45da44 100644
--- a/src/osal/osal.h
+++ b/src/osal/osal.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -33,17 +33,24 @@
#include "common/tusb_common.h"
-// Return immediately
-#define OSAL_TIMEOUT_NOTIMEOUT (0)
-// Default timeout
-#define OSAL_TIMEOUT_NORMAL (10)
-// Wait forever
-#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX)
+typedef void (*osal_task_func_t)( void * );
+// Timeout
+#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately
+#define OSAL_TIMEOUT_NORMAL (10) // Default timeout
+#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever
#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER
-typedef void (*osal_task_func_t)( void * );
+// Mutex is required when using a preempted RTOS or MCU has multiple cores
+#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE
+ #define OSAL_MUTEX_REQUIRED 0
+ #define OSAL_MUTEX_DEF(_name) uint8_t :0
+#else
+ #define OSAL_MUTEX_REQUIRED 1
+ #define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name
+#endif
+// OS thin implementation
#if CFG_TUSB_OS == OPT_OS_NONE
#include "osal_none.h"
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
@@ -56,6 +63,8 @@ typedef void (*osal_task_func_t)( void * );
#include "osal_rtthread.h"
#elif CFG_TUSB_OS == OPT_OS_RTX4
#include "osal_rtx4.h"
+#elif CFG_TUSB_OS == OPT_OS_ZEPHYR
+ #include "osal_zephyr.h"
#elif CFG_TUSB_OS == OPT_OS_CUSTOM
#include "tusb_os_custom.h" // implemented by application
#else
@@ -67,15 +76,18 @@ typedef void (*osal_task_func_t)( void * );
// Should be implemented as static inline function in osal_port.h header
/*
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
+ bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
+ bool osal_mutex_delete(osal_mutex_t mutex_hdl)
bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
+ bool osal_queue_delete(osal_queue_t qhdl);
bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
bool osal_queue_empty(osal_queue_t qhdl);
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index 52db336f5..a3a0f3a3f 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_FREERTOS_H_
-#define _TUSB_OSAL_FREERTOS_H_
+#ifndef TUSB_OSAL_FREERTOS_H_
+#define TUSB_OSAL_FREERTOS_H_
// FreeRTOS Headers
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,FreeRTOS.h)
@@ -37,48 +37,93 @@
extern "C" {
#endif
-TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec)
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+#if configSUPPORT_STATIC_ALLOCATION
+ typedef StaticSemaphore_t osal_semaphore_def_t;
+ typedef StaticSemaphore_t osal_mutex_def_t;
+#else
+ // not used therefore defined to smallest possible type to save space
+ typedef uint8_t osal_semaphore_def_t;
+ typedef uint8_t osal_mutex_def_t;
+#endif
+
+typedef SemaphoreHandle_t osal_semaphore_t;
+typedef SemaphoreHandle_t osal_mutex_t;
+typedef QueueHandle_t osal_queue_t;
+
+typedef struct
{
- if (msec == OSAL_TIMEOUT_WAIT_FOREVER) return portMAX_DELAY;
- if (msec == 0) return 0;
+ uint16_t depth;
+ uint16_t item_sz;
+ void* buf;
+
+#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
+ char const* name;
+#endif
+
+#if configSUPPORT_STATIC_ALLOCATION
+ StaticQueue_t sq;
+#endif
+} osal_queue_def_t;
+
+#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
+ #define _OSAL_Q_NAME(_name) .name = #_name
+#else
+ #define _OSAL_Q_NAME(_name)
+#endif
+
+// _int_set is not used with an RTOS
+#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
+ static _type _name##_##buf[_depth];\
+ osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) }
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
+ if ( msec == OSAL_TIMEOUT_WAIT_FOREVER ) return portMAX_DELAY;
+ if ( msec == 0 ) return 0;
uint32_t ticks = pdMS_TO_TICKS(msec);
// configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms
// we still need to delay at least 1 tick
- if (ticks == 0) ticks =1 ;
+ if ( ticks == 0 ) ticks = 1;
return ticks;
}
-//--------------------------------------------------------------------+
-// TASK API
-//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
-{
- vTaskDelay( pdMS_TO_TICKS(msec) );
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
+ vTaskDelay(pdMS_TO_TICKS(msec));
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+
-typedef StaticSemaphore_t osal_semaphore_def_t;
-typedef SemaphoreHandle_t osal_semaphore_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
+#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateBinaryStatic(semdef);
+#else
+ (void) semdef;
+ return xSemaphoreCreateBinary();
+#endif
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
-{
- if ( !in_isr )
- {
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ vSemaphoreDelete(semd_hdl);
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
+ if ( !in_isr ) {
return xSemaphoreGive(sem_hdl) != 0;
- }
- else
- {
- BaseType_t xHigherPriorityTaskWoken;
+ } else {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
@@ -92,34 +137,37 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t se
}
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
return xSemaphoreTake(sem_hdl, _osal_ms2tick(msec));
}
-TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
xQueueReset(sem_hdl);
}
//--------------------------------------------------------------------+
// MUTEX API (priority inheritance)
//--------------------------------------------------------------------+
-typedef StaticSemaphore_t osal_mutex_def_t;
-typedef SemaphoreHandle_t osal_mutex_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
+#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateMutexStatic(mdef);
+#else
+ (void) mdef;
+ return xSemaphoreCreateMutex();
+#endif
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ vSemaphoreDelete(mutex_hdl);
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
return osal_semaphore_wait(mutex_hdl, msec);
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
return xSemaphoreGive(mutex_hdl);
}
@@ -127,45 +175,40 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
// QUEUE API
//--------------------------------------------------------------------+
-// _int_set is not used with an RTOS
-#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
- static _type _name##_##buf[_depth];\
- osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
+ osal_queue_t q;
-typedef struct
-{
- uint16_t depth;
- uint16_t item_sz;
- void* buf;
+#if configSUPPORT_STATIC_ALLOCATION
+ q = xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
+#else
+ q = xQueueCreate(qdef->depth, qdef->item_sz);
+#endif
- StaticQueue_t sq;
-}osal_queue_def_t;
+#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
+ vQueueAddToRegistry(q, qdef->name);
+#endif
-typedef QueueHandle_t osal_queue_t;
+ return q;
+}
-TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
-{
- return xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ vQueueDelete(qhdl);
+ return true;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
return xQueueReceive(qhdl, data, _osal_ms2tick(msec));
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
-{
- if ( !in_isr )
- {
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
+ if ( !in_isr ) {
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
- }
- else
- {
- BaseType_t xHigherPriorityTaskWoken;
+ } else {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
- // not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7
+ // not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7 (IDF v5)
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
@@ -175,13 +218,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
}
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
return uxQueueMessagesWaiting(qhdl) == 0;
}
#ifdef __cplusplus
- }
+}
#endif
#endif
diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h
index b8ea2087c..16def0d2a 100644
--- a/src/osal/osal_mynewt.h
+++ b/src/osal/osal_mynewt.h
@@ -36,8 +36,7 @@
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
os_time_delay( os_time_ms_to_ticks32(msec) );
}
@@ -47,25 +46,26 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
typedef struct os_sem osal_semaphore_def_t;
typedef struct os_sem* osal_semaphore_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
return (os_sem_init(semdef, 0) == OS_OK) ? (osal_semaphore_t) semdef : NULL;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ (void) semd_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
(void) in_isr;
return os_sem_release(sem_hdl) == OS_OK;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
return os_sem_pend(sem_hdl, ticks) == OS_OK;
}
-static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
-{
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
// TODO implement later
}
@@ -75,19 +75,21 @@ static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
typedef struct os_mutex osal_mutex_def_t;
typedef struct os_mutex* osal_mutex_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
return (os_mutex_init(mdef) == OS_OK) ? (osal_mutex_t) mdef : NULL;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ (void) mutex_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
return os_mutex_pend(mutex_hdl, ticks) == OS_OK;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
return os_mutex_release(mutex_hdl) == OS_OK;
}
@@ -101,8 +103,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
static struct os_event _name##_##evbuf[_depth];\
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, .evbuf = _name##_##evbuf};\
-typedef struct
-{
+typedef struct {
uint16_t depth;
uint16_t item_sz;
void* buf;
@@ -116,17 +117,20 @@ typedef struct
typedef osal_queue_def_t* osal_queue_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
-{
- if ( OS_OK != os_mempool_init(&qdef->mpool, qdef->depth, qdef->item_sz, qdef->buf, "usbd queue") ) return NULL;
- if ( OS_OK != os_mempool_init(&qdef->epool, qdef->depth, sizeof(struct os_event), qdef->evbuf, "usbd evqueue") ) return NULL;
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
+ if ( OS_OK != os_mempool_init(&qdef->mpool, qdef->depth, qdef->item_sz, qdef->buf, "usb queue") ) return NULL;
+ if ( OS_OK != os_mempool_init(&qdef->epool, qdef->depth, sizeof(struct os_event), qdef->evbuf, "usb evqueue") ) return NULL;
os_eventq_init(&qdef->evq);
return (osal_queue_t) qdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ (void) qhdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
(void) msec; // os_eventq_get() does not take timeout, always behave as msec = WAIT_FOREVER
struct os_event* ev;
@@ -139,8 +143,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, v
return true;
}
-static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
-{
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr) {
(void) in_isr;
// get a block from mem pool for data
@@ -150,8 +153,7 @@ static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in
// get a block from event pool to put into queue
struct os_event* ev = (struct os_event*) os_memblock_get(&qhdl->epool);
- if (!ev)
- {
+ if (!ev) {
os_memblock_put(&qhdl->mpool, ptr);
return false;
}
@@ -163,8 +165,7 @@ static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
return STAILQ_EMPTY(&qhdl->evq.evq_list);
}
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index 9c80e4548..40e9bb83a 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,54 +24,59 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_NONE_H_
-#define _TUSB_OSAL_NONE_H_
+#ifndef TUSB_OSAL_NONE_H_
+#define TUSB_OSAL_NONE_H_
#ifdef __cplusplus
- extern "C" {
+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
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
volatile uint16_t count;
-}osal_semaphore_def_t;
+} osal_semaphore_def_t;
typedef osal_semaphore_def_t* osal_semaphore_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
semdef->count = 0;
return semdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ (void) semd_hdl;
+ return true; // nothing to do
+}
+
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
(void) in_isr;
sem_hdl->count++;
return true;
}
// TODO blocking for now
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
(void) msec;
- while (sem_hdl->count == 0) { }
+ while (sem_hdl->count == 0) {}
sem_hdl->count--;
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
sem_hdl->count = 0;
}
@@ -82,32 +87,45 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t s
typedef osal_semaphore_def_t osal_mutex_def_t;
typedef osal_semaphore_t osal_mutex_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
-{
+#if OSAL_MUTEX_REQUIRED
+// Note: multiple cores MCUs usually do provide IPC API for mutex
+// or we can use std atomic function
+
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
mdef->count = 1;
return mdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ (void) mutex_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
return osal_semaphore_wait(mutex_hdl, msec);
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
return osal_semaphore_post(mutex_hdl, false);
}
+#else
+
+#define osal_mutex_create(_mdef) (NULL)
+#define osal_mutex_lock(_mutex_hdl, _ms) (true)
+#define osal_mutex_unlock(_mutex_hdl) (true)
+
+#endif
+
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
#include "common/tusb_fifo.h"
-typedef struct
-{
- void (*interrupt_set)(bool);
+typedef struct {
+ void (* interrupt_set)(bool);
tu_fifo_t ff;
-}osal_queue_def_t;
+} osal_queue_def_t;
typedef osal_queue_def_t* osal_queue_t;
@@ -119,63 +137,48 @@ typedef osal_queue_def_t* osal_queue_t;
.ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
}
-// lock queue by disable USB interrupt
-TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl)
-{
- // disable dcd/hcd interrupt
- qhdl->interrupt_set(false);
-}
-
-// unlock queue
-TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl)
-{
- // enable dcd/hcd interrupt
- qhdl->interrupt_set(true);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ (void) qhdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
(void) msec; // not used, always behave as msec = 0
- _osal_q_lock(qhdl);
- bool success = tu_fifo_read(&qhdl->ff, data);
- _osal_q_unlock(qhdl);
+ qhdl->interrupt_set(false);
+ const bool success = tu_fifo_read(&qhdl->ff, data);
+ qhdl->interrupt_set(true);
return success;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
if (!in_isr) {
- _osal_q_lock(qhdl);
+ qhdl->interrupt_set(false);
}
- bool success = tu_fifo_write(&qhdl->ff, data);
+ const bool success = tu_fifo_write(&qhdl->ff, data);
if (!in_isr) {
- _osal_q_unlock(qhdl);
+ qhdl->interrupt_set(true);
}
- TU_ASSERT(success);
-
return success;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
// Skip queue lock/unlock since this function is primarily called
// with interrupt disabled before going into low power mode
return tu_fifo_empty(&qhdl->ff);
}
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_OSAL_NONE_H_ */
+#endif
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 8b428d642..315de0950 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
@@ -24,8 +24,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_PICO_H_
-#define _TUSB_OSAL_PICO_H_
+#ifndef TUSB_OSAL_PICO_H_
+#define TUSB_OSAL_PICO_H_
#include "pico/time.h"
#include "pico/sem.h"
@@ -33,42 +33,42 @@
#include "pico/critical_section.h"
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
sleep_ms(msec);
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+
-typedef struct semaphore osal_semaphore_def_t, *osal_semaphore_t;
+typedef struct semaphore osal_semaphore_def_t, * osal_semaphore_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
sem_init(semdef, 0, 255);
return semdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ (void) semd_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
(void) in_isr;
sem_release(sem_hdl);
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
return sem_acquire_timeout_ms(sem_hdl, msec);
}
-TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
sem_reset(sem_hdl, 0);
}
@@ -76,21 +76,23 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t s
// MUTEX API
// Within tinyusb, mutex is never used in ISR context
//--------------------------------------------------------------------+
-typedef struct mutex osal_mutex_def_t, *osal_mutex_t;
+typedef struct mutex osal_mutex_def_t, * osal_mutex_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
mutex_init(mdef);
return mdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ (void) mutex_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
return mutex_enter_timeout_ms(mutex_hdl, msec);
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
mutex_exit(mutex_hdl);
return true;
}
@@ -100,75 +102,53 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
//--------------------------------------------------------------------+
#include "common/tusb_fifo.h"
-typedef struct
-{
- tu_fifo_t ff;
- struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
+typedef struct {
+ tu_fifo_t ff;
+ struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
} osal_queue_def_t;
typedef osal_queue_def_t* osal_queue_t;
// role device/host is used by OS NONE for mutex (disable usb isr) only
-#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
+#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
osal_queue_def_t _name = { \
.ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
}
-// lock queue by disable USB interrupt
-TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl)
-{
- critical_section_enter_blocking(&qhdl->critsec);
-}
-
-// unlock queue
-TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl)
-{
- critical_section_exit(&qhdl->critsec);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
critical_section_init(&qdef->critsec);
tu_fifo_clear(&qdef->ff);
return (osal_queue_t) qdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
-{
- (void) msec; // not used, always behave as msec = 0
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ osal_queue_def_t* qdef = (osal_queue_def_t*) qhdl;
+ critical_section_deinit(&qdef->critsec);
+ return true;
+}
- // TODO: revisit... docs say that mutexes are never used from IRQ context,
- // however osal_queue_recieve may be. therefore my assumption is that
- // the fifo mutex is not populated for queues used from an IRQ context
- //assert(!qhdl->ff.mutex);
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
+ (void) msec; // not used, always behave as msec = 0
- _osal_q_lock(qhdl);
+ critical_section_enter_blocking(&qhdl->critsec);
bool success = tu_fifo_read(&qhdl->ff, data);
- _osal_q_unlock(qhdl);
+ critical_section_exit(&qhdl->critsec);
return success;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
-{
- // TODO: revisit... docs say that mutexes are never used from IRQ context,
- // however osal_queue_recieve may be. therefore my assumption is that
- // the fifo mutex is not populated for queues used from an IRQ context
- //assert(!qhdl->ff.mutex);
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
(void) in_isr;
- _osal_q_lock(qhdl);
+ critical_section_enter_blocking(&qhdl->critsec);
bool success = tu_fifo_write(&qhdl->ff, data);
- _osal_q_unlock(qhdl);
-
- TU_ASSERT(success);
+ critical_section_exit(&qhdl->critsec);
return success;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
// TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
// volatile read.
@@ -178,7 +158,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
}
#ifdef __cplusplus
- }
+}
#endif
-#endif /* _TUSB_OSAL_PICO_H_ */
+#endif
diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h
index f8452bfb2..c27814835 100644
--- a/src/osal/osal_rtthread.h
+++ b/src/osal/osal_rtthread.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2020 tfx2001 ([email protected])
+ * Copyright (c) 2020 yekai ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_RTTHREAD_H_
-#define _TUSB_OSAL_RTTHREAD_H_
+#ifndef TUSB_OSAL_RTTHREAD_H_
+#define TUSB_OSAL_RTTHREAD_H_
// RT-Thread Headers
#include "rtthread.h"
@@ -47,23 +48,27 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
typedef struct rt_semaphore osal_semaphore_def_t;
typedef rt_sem_t osal_semaphore_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t
-osal_semaphore_create(osal_semaphore_def_t *semdef) {
- rt_sem_init(semdef, "tusb", 0, RT_IPC_FLAG_PRIO);
- return semdef;
+TU_ATTR_ALWAYS_INLINE static inline
+osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
+ rt_sem_init(semdef, "tusb", 0, RT_IPC_FLAG_PRIO);
+ return semdef;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ return RT_EOK == rt_sem_detach(semd_hdl);
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
- (void) in_isr;
- return rt_sem_release(sem_hdl) == RT_EOK;
+ (void) in_isr;
+ return rt_sem_release(sem_hdl) == RT_EOK;
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
- return rt_sem_take(sem_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
+ return rt_sem_take(sem_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
- // TODO: implement
+ rt_sem_control(sem_hdl, RT_IPC_CMD_RESET, 0);
}
//--------------------------------------------------------------------+
@@ -73,16 +78,20 @@ typedef struct rt_mutex osal_mutex_def_t;
typedef rt_mutex_t osal_mutex_t;
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
- rt_mutex_init(mdef, "tusb", RT_IPC_FLAG_PRIO);
- return mdef;
+ rt_mutex_init(mdef, "tusb", RT_IPC_FLAG_PRIO);
+ return mdef;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ return RT_EOK == rt_mutex_detach(mutex_hdl);
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
- return rt_mutex_take(mutex_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
+ return rt_mutex_take(mutex_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
- return rt_mutex_release(mutex_hdl) == RT_EOK;
+ return rt_mutex_release(mutex_hdl) == RT_EOK;
}
//--------------------------------------------------------------------+
@@ -105,28 +114,35 @@ typedef struct {
typedef rt_mq_t osal_queue_t;
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t *qdef) {
- rt_mq_init(&(qdef->sq), "tusb", qdef->buf, qdef->item_sz,
- qdef->item_sz * qdef->depth, RT_IPC_FLAG_PRIO);
- return &(qdef->sq);
+ rt_mq_init(&(qdef->sq), "tusb", qdef->buf, qdef->item_sz,
+ qdef->item_sz * qdef->depth, RT_IPC_FLAG_PRIO);
+ return &(qdef->sq);
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec) {
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ return RT_EOK == rt_mq_detach(qhdl);
+}
- rt_tick_t tick = rt_tick_from_millisecond((rt_int32_t) msec);
- return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) == RT_EOK;
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec) {
+ rt_tick_t tick = rt_tick_from_millisecond((rt_int32_t) msec);
+#if RT_VERSION_MAJOR >= 5
+ return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) > 0;
+#else
+ return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) == RT_EOK;
+#endif /* RT_VERSION_MAJOR >= 5 */
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
- (void) in_isr;
- return rt_mq_send(qhdl, (void *)data, qhdl->msg_size) == RT_EOK;
+ (void) in_isr;
+ return rt_mq_send(qhdl, (void *)data, qhdl->msg_size) == RT_EOK;
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
- return (qhdl->entry) == 0;
+ return (qhdl->entry) == 0;
}
#ifdef __cplusplus
}
#endif
-#endif /* _TUSB_OSAL_RTTHREAD_H_ */
+#endif
diff --git a/src/osal/osal_rtx4.h b/src/osal/osal_rtx4.h
index dea1c12c8..35909e4d6 100644
--- a/src/osal/osal_rtx4.h
+++ b/src/osal/osal_rtx4.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Tian Yunhao (t123yh)
@@ -25,8 +25,8 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_OSAL_RTX4_H_
-#define _TUSB_OSAL_RTX4_H_
+#ifndef TUSB_OSAL_RTX4_H_
+#define TUSB_OSAL_RTX4_H_
#include <rtl.h>
@@ -37,8 +37,7 @@ extern "C" {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
uint16_t hi = msec >> 16;
uint16_t lo = msec;
while (hi--) {
@@ -48,12 +47,13 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) {
- if (msec == OSAL_TIMEOUT_WAIT_FOREVER)
+ if (msec == OSAL_TIMEOUT_WAIT_FOREVER) {
return 0xFFFF;
- else if (msec >= 0xFFFE)
+ } else if (msec >= 0xFFFE) {
return 0xFFFE;
- else
+ } else {
return msec;
+ }
}
//--------------------------------------------------------------------+
@@ -67,6 +67,11 @@ TU_ATTR_ALWAYS_INLINE static inline OS_ID osal_semaphore_create(osal_semaphore_d
return semdef;
}
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ (void) semd_hdl;
+ return true; // nothing to do
+}
+
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
if ( !in_isr ) {
os_sem_send(sem_hdl);
@@ -90,19 +95,21 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t c
typedef OS_MUT osal_mutex_def_t;
typedef OS_ID osal_mutex_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
os_mut_init(mdef);
return mdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ (void) mutex_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
return os_mut_wait(mutex_hdl, msec2wait(msec)) != OS_R_TMO;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
return os_mut_release(mutex_hdl) == OS_R_OK;
}
@@ -115,10 +122,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
os_mbx_declare(_name##__mbox, _depth); \
_declare_box(_name##__pool, sizeof(_type), _depth); \
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .pool = _name##__pool, .mbox = _name##__mbox };
-
-typedef struct
-{
+typedef struct {
uint16_t depth;
uint16_t item_sz;
U32* pool;
@@ -127,15 +132,13 @@ typedef struct
typedef osal_queue_def_t* osal_queue_t;
-TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
-{
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
os_mbx_init(qdef->mbox, (qdef->depth + 4) * 4);
_init_box(qdef->pool, ((qdef->item_sz+3)/4)*(qdef->depth) + 3, qdef->item_sz);
return qdef;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
void* buf;
os_mbx_wait(qhdl->mbox, &buf, msec2wait(msec));
memcpy(data, buf, qhdl->item_sz);
@@ -143,23 +146,23 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, v
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ (void) qhdl;
+ return true; // nothing to do ?
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr) {
void* buf = _alloc_box(qhdl->pool);
memcpy(buf, data, qhdl->item_sz);
- if ( !in_isr )
- {
+ if ( !in_isr ) {
os_mbx_send(qhdl->mbox, buf, 0xFFFF);
- }
- else
- {
+ } else {
isr_mbx_send(qhdl->mbox, buf);
}
return true;
}
-TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
return os_mbx_check(qhdl->mbox) == qhdl->depth;
}
diff --git a/src/osal/osal_zephyr.h b/src/osal/osal_zephyr.h
new file mode 100644
index 000000000..8ecb13c6d
--- /dev/null
+++ b/src/osal/osal_zephyr.h
@@ -0,0 +1,123 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 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
+ * 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_OSAL_ZEPHYR_H
+#define TUSB_OSAL_ZEPHYR_H
+
+#include <zephyr/kernel.h>
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
+ k_msleep(msec);
+}
+
+//--------------------------------------------------------------------+
+// Binary Semaphore API
+//--------------------------------------------------------------------+
+typedef struct k_sem osal_semaphore_def_t, * osal_semaphore_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
+ k_sem_init(semdef, 0, 255);
+ return semdef;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
+ (void) semd_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
+ (void) in_isr;
+ k_sem_give(sem_hdl);
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
+ return 0 == k_sem_take(sem_hdl, K_MSEC(msec));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
+ k_sem_reset(sem_hdl);
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API
+//--------------------------------------------------------------------+
+typedef struct k_mutex osal_mutex_def_t, *osal_mutex_t;
+
+TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
+ if ( 0 == k_mutex_init(mdef) ) {
+ return mdef;
+ } else {
+ return NULL;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
+ (void) mutex_hdl;
+ return true; // nothing to do
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
+ return 0 == k_mutex_lock(mutex_hdl, K_MSEC(msec));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
+ return 0 == k_mutex_unlock(mutex_hdl);
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+typedef struct k_msgq osal_queue_def_t, * osal_queue_t;
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) K_MSGQ_DEFINE(_name, sizeof(_type), _depth, 4)
+
+TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
+ // K_MSGQ_DEFINE already initializes the queue
+ return (osal_queue_t) qdef;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
+ (void) qhdl;
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
+ return 0 == k_msgq_get(qhdl, data, K_MSEC(msec));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
+ return 0 == k_msgq_put(qhdl, data, in_isr ? K_NO_WAIT : K_FOREVER);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
+ return 0 == k_msgq_num_used_get(qhdl);
+}
+
+#endif
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
new file mode 100644
index 000000000..c5e924266
--- /dev/null
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -0,0 +1,1076 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
+
+#include <stdatomic.h>
+#include "host/hcd.h"
+#include "host/usbh.h"
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+// Command format is
+// Reg [7:3] | 0 [2] | Dir [1] | Ack [0]
+
+enum {
+ CMDBYTE_WRITE = 0x02,
+};
+
+enum {
+ RCVVFIFO_ADDR = 1u << 3, // 0x08
+ SNDFIFO_ADDR = 2u << 3, // 0x10
+ SUDFIFO_ADDR = 4u << 3, // 0x20
+ RCVBC_ADDR = 6u << 3, // 0x30
+ SNDBC_ADDR = 7u << 3, // 0x38
+ USBIRQ_ADDR = 13u << 3, // 0x68
+ USBIEN_ADDR = 14u << 3, // 0x70
+ USBCTL_ADDR = 15u << 3, // 0x78
+ CPUCTL_ADDR = 16u << 3, // 0x80
+ PINCTL_ADDR = 17u << 3, // 0x88
+ REVISION_ADDR = 18u << 3, // 0x90
+ // 19 is not used
+ IOPINS1_ADDR = 20u << 3, // 0xA0
+ IOPINS2_ADDR = 21u << 3, // 0xA8
+ GPINIRQ_ADDR = 22u << 3, // 0xB0
+ GPINIEN_ADDR = 23u << 3, // 0xB8
+ GPINPOL_ADDR = 24u << 3, // 0xC0
+ HIRQ_ADDR = 25u << 3, // 0xC8
+ HIEN_ADDR = 26u << 3, // 0xD0
+ MODE_ADDR = 27u << 3, // 0xD8
+ PERADDR_ADDR = 28u << 3, // 0xE0
+ HCTL_ADDR = 29u << 3, // 0xE8
+ HXFR_ADDR = 30u << 3, // 0xF0
+ HRSL_ADDR = 31u << 3, // 0xF8
+};
+
+enum {
+ USBIRQ_OSCOK_IRQ = 1u << 0,
+ USBIRQ_NOVBUS_IRQ = 1u << 5,
+ USBIRQ_VBUS_IRQ = 1u << 6,
+};
+
+enum {
+ USBCTL_PWRDOWN = 1u << 4,
+ USBCTL_CHIPRES = 1u << 5,
+};
+
+enum {
+ CPUCTL_IE = 1u << 0,
+ CPUCTL_PULSEWID0 = 1u << 6,
+ CPUCTL_PULSEWID1 = 1u << 7,
+};
+
+enum {
+ PINCTL_GPXA = 1u << 0,
+ PINCTL_GPXB = 1u << 1,
+ PINCTL_POSINT = 1u << 2,
+ PINCTL_INTLEVEL = 1u << 3,
+ PINCTL_FDUPSPI = 1u << 4,
+};
+
+enum {
+ HIRQ_BUSEVENT_IRQ = 1u << 0,
+ HIRQ_RWU_IRQ = 1u << 1,
+ HIRQ_RCVDAV_IRQ = 1u << 2,
+ HIRQ_SNDBAV_IRQ = 1u << 3,
+ HIRQ_SUSDN_IRQ = 1u << 4,
+ HIRQ_CONDET_IRQ = 1u << 5,
+ HIRQ_FRAME_IRQ = 1u << 6,
+ HIRQ_HXFRDN_IRQ = 1u << 7,
+};
+
+enum {
+ MODE_HOST = 1u << 0,
+ MODE_LOWSPEED = 1u << 1,
+ MODE_HUBPRE = 1u << 2,
+ MODE_SOFKAENAB = 1u << 3,
+ MODE_SEPIRQ = 1u << 4,
+ MODE_DELAYISO = 1u << 5,
+ MODE_DMPULLDN = 1u << 6,
+ MODE_DPPULLDN = 1u << 7,
+};
+
+enum {
+ HCTL_BUSRST = 1u << 0,
+ HCTL_FRMRST = 1u << 1,
+ HCTL_SAMPLEBUS = 1u << 2,
+ HCTL_SIGRSM = 1u << 3,
+ HCTL_RCVTOG0 = 1u << 4,
+ HCTL_RCVTOG1 = 1u << 5,
+ HCTL_SNDTOG0 = 1u << 6,
+ HCTL_SNDTOG1 = 1u << 7,
+};
+
+enum {
+ HXFR_EPNUM_MASK = 0x0f,
+ HXFR_SETUP = 1u << 4,
+ HXFR_OUT_NIN = 1u << 5,
+ HXFR_ISO = 1u << 6,
+ HXFR_HS = 1u << 7,
+};
+
+enum {
+ HRSL_RESULT_MASK = 0x0f,
+ HRSL_RCVTOGRD = 1u << 4,
+ HRSL_SNDTOGRD = 1u << 5,
+ HRSL_KSTATUS = 1u << 6,
+ HRSL_JSTATUS = 1u << 7,
+};
+
+enum {
+ HRSL_SUCCESS = 0,
+ HRSL_BUSY,
+ HRSL_BAD_REQ,
+ HRSL_UNDEF,
+ HRSL_NAK,
+ HRSL_STALL,
+ HRSL_TOG_ERR,
+ HRSL_WRONG_PID,
+ HRSL_BAD_BYTECOUNT,
+ HRSL_PID_ERR,
+ HRSL_PKT_ERR,
+ HRSL_CRC_ERR,
+ HRSL_K_ERR,
+ HRSL_J_ERR,
+ HRSL_TIMEOUT,
+ HRSL_BABBLE,
+};
+
+enum {
+ DEFAULT_HIEN = HIRQ_CONDET_IRQ | HIRQ_FRAME_IRQ | HIRQ_HXFRDN_IRQ | HIRQ_RCVDAV_IRQ
+};
+
+enum {
+ MAX_NAK_DEFAULT = 1 // Number of NAK per endpoint per usb frame to save CPU/SPI bus usage
+};
+
+enum {
+ EP_STATE_IDLE = 0,
+ EP_STATE_COMPLETE = 1,
+ EP_STATE_ABORTING = 2,
+ EP_STATE_ATTEMPT_1 = 3, // Number of attempts to transfer in a frame. Incremented after each NAK
+ EP_STATE_ATTEMPT_MAX = 15
+};
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t ep_num : 4;
+ uint8_t is_setup : 1;
+ uint8_t is_out : 1;
+ uint8_t is_iso : 1;
+} hxfr_bm_t;
+
+TU_VERIFY_STATIC(sizeof(hxfr_bm_t) == 1, "size is not correct");
+
+typedef struct {
+ uint8_t daddr;
+
+ union {
+ hxfr_bm_t hxfr_bm;
+ uint8_t hxfr;
+ };
+
+ struct TU_ATTR_PACKED {
+ uint8_t state : 4;
+ uint8_t data_toggle : 1;
+ uint16_t packet_size : 11;
+ };
+
+ uint16_t total_len;
+ uint16_t xferred_len;
+ uint8_t* buf;
+} max3421_ep_t;
+
+TU_VERIFY_STATIC(sizeof(max3421_ep_t) == 12, "size is not correct");
+
+typedef struct {
+ volatile uint16_t frame_count;
+
+ // cached register
+ uint8_t sndbc;
+ uint8_t hirq;
+ uint8_t hien;
+ uint8_t mode;
+ uint8_t peraddr;
+ union {
+ hxfr_bm_t hxfr_bm;
+ uint8_t hxfr;
+ };
+
+ // owner of data in SNDFIFO, for retrying NAKed without re-writing to FIFO
+ struct {
+ uint8_t daddr;
+ uint8_t hxfr;
+ }sndfifo_owner;
+
+ atomic_flag busy; // busy transferring
+
+#if OSAL_MUTEX_REQUIRED
+ OSAL_MUTEX_DEF(spi_mutexdef);
+ osal_mutex_t spi_mutex;
+#endif
+
+ max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL]; // [0] is reserved for addr0
+} max3421_data_t;
+
+static max3421_data_t _hcd_data;
+
+// max NAK before giving up in a frame. 0 means infinite NAKs
+static tuh_configure_max3421_t _tuh_cfg = {
+ .max_nak = MAX_NAK_DEFAULT,
+ .cpuctl = 0, // default: INT pulse width = 10.6 us
+ .pinctl = 0, // default: negative edge interrupt
+};
+
+//--------------------------------------------------------------------+
+// API: SPI transfer with MAX3421E
+// - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application
+// - reg_read(), reg_write(): is implemented by this driver, can be used by application
+//--------------------------------------------------------------------+
+
+// API to control MAX3421 SPI CS
+extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
+
+// API to transfer data with MAX3421 SPI
+// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
+extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
+
+// API to enable/disable MAX3421 INTR pin interrupt
+extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
+
+// API to read MAX3421's register. Implemented by TinyUSB
+uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
+
+// API to write MAX3421's register. Implemented by TinyUSB
+bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
+
+//--------------------------------------------------------------------+
+// SPI Commands and Helper
+//--------------------------------------------------------------------+
+
+#define reg_read tuh_max3421_reg_read
+#define reg_write tuh_max3421_reg_write
+
+static void max3421_spi_lock(uint8_t rhport, bool in_isr) {
+ // disable interrupt and mutex lock (for pre-emptive RTOS) if not in_isr
+ if (!in_isr) {
+ (void) osal_mutex_lock(_hcd_data.spi_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
+ tuh_max3421_int_api(rhport, false);
+ }
+
+ // assert CS
+ tuh_max3421_spi_cs_api(rhport, true);
+}
+
+static void max3421_spi_unlock(uint8_t rhport, bool in_isr) {
+ // de-assert CS
+ tuh_max3421_spi_cs_api(rhport, false);
+
+ // mutex unlock and re-enable interrupt
+ if (!in_isr) {
+ tuh_max3421_int_api(rhport, true);
+ (void) osal_mutex_unlock(_hcd_data.spi_mutex);
+ }
+}
+
+uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr) {
+ uint8_t tx_buf[2] = {reg, 0};
+ uint8_t rx_buf[2] = {0, 0};
+
+ max3421_spi_lock(rhport, in_isr);
+ bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
+ max3421_spi_unlock(rhport, in_isr);
+
+ _hcd_data.hirq = rx_buf[0];
+ return ret ? rx_buf[1] : 0;
+}
+
+bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr) {
+ uint8_t tx_buf[2] = {reg | CMDBYTE_WRITE, data};
+ uint8_t rx_buf[2] = {0, 0};
+
+ max3421_spi_lock(rhport, in_isr);
+ bool ret = tuh_max3421_spi_xfer_api(rhport, tx_buf, rx_buf, 2);
+ max3421_spi_unlock(rhport, in_isr);
+
+ // HIRQ register since we are in full-duplex mode
+ _hcd_data.hirq = rx_buf[0];
+
+ return ret;
+}
+
+//--------------------------------------------------------------------
+// Register helper
+//--------------------------------------------------------------------
+TU_ATTR_ALWAYS_INLINE static inline void hirq_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ reg_write(rhport, HIRQ_ADDR, data, in_isr);
+ // HIRQ write 1 is clear
+ _hcd_data.hirq &= (uint8_t) ~data;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void hien_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ _hcd_data.hien = data;
+ reg_write(rhport, HIEN_ADDR, data, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ _hcd_data.mode = data;
+ reg_write(rhport, MODE_ADDR, data, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ if ( _hcd_data.peraddr == data ) return; // no need to change address
+
+ _hcd_data.peraddr = data;
+ reg_write(rhport, PERADDR_ADDR, data, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void hxfr_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ _hcd_data.hxfr = data;
+ reg_write(rhport, HXFR_ADDR, data, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
+ _hcd_data.sndbc = data;
+ reg_write(rhport, SNDBC_ADDR, data, in_isr);
+}
+
+//--------------------------------------------------------------------
+// FIFO access (receive, send, setup)
+//--------------------------------------------------------------------
+static void hwfifo_write(uint8_t rhport, uint8_t reg, const uint8_t* buffer, uint8_t len, bool in_isr) {
+ uint8_t hirq;
+ reg |= CMDBYTE_WRITE;
+
+ max3421_spi_lock(rhport, in_isr);
+
+ tuh_max3421_spi_xfer_api(rhport, &reg, &hirq, 1);
+ _hcd_data.hirq = hirq;
+ tuh_max3421_spi_xfer_api(rhport, buffer, NULL, len);
+
+ max3421_spi_unlock(rhport, in_isr);
+}
+
+// Write to SNDFIFO if len > 0 and update SNDBC
+TU_ATTR_ALWAYS_INLINE static inline void hwfifo_send(uint8_t rhport, const uint8_t* buffer, uint8_t len, bool in_isr) {
+ if (len) {
+ hwfifo_write(rhport, SNDFIFO_ADDR, buffer, len, in_isr);
+ }
+ sndbc_write(rhport, len, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint8_t* buffer, bool in_isr) {
+ hwfifo_write(rhport, SUDFIFO_ADDR, buffer, 8, in_isr);
+}
+
+static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
+ uint8_t hirq;
+ uint8_t const reg = RCVVFIFO_ADDR;
+
+ max3421_spi_lock(rhport, in_isr);
+
+ tuh_max3421_spi_xfer_api(rhport, &reg, &hirq, 1);
+ _hcd_data.hirq = hirq;
+ tuh_max3421_spi_xfer_api(rhport, NULL, buffer, len);
+
+ max3421_spi_unlock(rhport, in_isr);
+}
+
+//--------------------------------------------------------------------+
+// Endpoint helper
+//--------------------------------------------------------------------+
+
+static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
+ uint8_t const is_out = 1-ep_dir;
+ for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+ max3421_ep_t* ep = &_hcd_data.ep[i];
+ // control endpoint is bi-direction (skip check)
+ if (daddr == ep->daddr && ep_num == ep->hxfr_bm.ep_num && (ep_num == 0 || is_out == ep->hxfr_bm.is_out)) {
+ return ep;
+ }
+ }
+
+ return NULL;
+}
+
+// daddr = 0 and ep_num = 0 means find a free (allocate) endpoint
+TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * allocate_ep(void) {
+ return find_ep_not_addr0(0, 0, 0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * find_opened_ep(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
+ if (daddr == 0 && ep_num == 0) {
+ return &_hcd_data.ep[0];
+ }else{
+ return find_ep_not_addr0(daddr, ep_num, ep_dir);
+ }
+}
+
+// free all endpoints belong to device address
+static void free_ep(uint8_t daddr) {
+ for (size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+ max3421_ep_t* ep = &_hcd_data.ep[i];
+ if (ep->daddr == daddr) {
+ tu_memclr(ep, sizeof(max3421_ep_t));
+ }
+ }
+}
+
+// Check if endpoint has a queued transfer and not reach max NAK in this frame
+TU_ATTR_ALWAYS_INLINE static inline bool is_ep_pending(max3421_ep_t const * ep) {
+ uint8_t const state = ep->state;
+ return ep->packet_size && (state >= EP_STATE_ATTEMPT_1) &&
+ (_tuh_cfg.max_nak == 0 || state < EP_STATE_ATTEMPT_1 + _tuh_cfg.max_nak);
+}
+
+// Find the next pending endpoint using round-robin scheduling, starting from next endpoint.
+// return NULL if not found
+// TODO respect interrupt endpoint's interval
+static max3421_ep_t * find_next_pending_ep(max3421_ep_t * cur_ep) {
+ size_t const idx = (size_t) (cur_ep - _hcd_data.ep);
+
+ // starting from next endpoint
+ for (size_t i = idx + 1; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+ max3421_ep_t* ep = &_hcd_data.ep[i];
+ if (is_ep_pending(ep)) {
+ return ep;
+ }
+ }
+
+ // wrap around including current endpoint
+ for (size_t i = 0; i <= idx; i++) {
+ max3421_ep_t* ep = &_hcd_data.ep[i];
+ if (is_ep_pending(ep)) {
+ return ep;
+ }
+ }
+
+ return NULL;
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ TU_VERIFY(cfg_id == TUH_CFGID_MAX3421 && cfg_param != NULL);
+
+ tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
+ _tuh_cfg = cfg->max3421;
+ _tuh_cfg.max_nak = tu_min8(_tuh_cfg.max_nak, EP_STATE_ATTEMPT_MAX-EP_STATE_ATTEMPT_1);
+ return true;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+
+ tuh_max3421_int_api(rhport, false);
+
+ TU_LOG2_INT(sizeof(max3421_ep_t));
+ TU_LOG2_INT(sizeof(max3421_data_t));
+ TU_LOG2_INT(offsetof(max3421_data_t, ep));
+
+ tu_memclr(&_hcd_data, sizeof(_hcd_data));
+ _hcd_data.peraddr = 0xff; // invalid
+
+#if OSAL_MUTEX_REQUIRED
+ _hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
+#endif
+
+ // NOTE: driver does not seem to work without nRST pin signal
+
+ // full duplex, interrupt negative edge
+ reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false);
+
+ // v1 is 0x01, v2 is 0x12, v3 is 0x13
+ // Note: v1 and v2 has host OUT errata whose workaround is not implemented in this driver
+ uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
+ TU_LOG2_HEX(revision);
+ TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
+
+ // reset
+ reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
+ reg_write(rhport, USBCTL_ADDR, 0, false);
+ while( !(reg_read(rhport, USBIRQ_ADDR, false) & USBIRQ_OSCOK_IRQ) ) {
+ // wait for oscillator to stabilize
+ }
+
+ // Mode: Host and DP/DM pull down
+ mode_write(rhport, MODE_DPPULLDN | MODE_DMPULLDN | MODE_HOST, false);
+
+ // frame reset & bus reset, this will trigger CONDET IRQ if device is already connected
+ reg_write(rhport, HCTL_ADDR, HCTL_BUSRST | HCTL_FRMRST, false);
+
+ // clear all previously pending IRQ
+ hirq_write(rhport, 0xff, false);
+
+ // Enable IRQ
+ hien_write(rhport, DEFAULT_HIEN, false);
+
+ tuh_max3421_int_api(rhport, true);
+
+ // Enable Interrupt pin
+ reg_write(rhport, CPUCTL_ADDR, _tuh_cfg.cpuctl | CPUCTL_IE, false);
+
+ return true;
+}
+
+bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+
+ // disable interrupt
+ tuh_max3421_int_api(rhport, false);
+
+ // reset max3421 and power down
+ reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
+ reg_write(rhport, USBCTL_ADDR, USBCTL_PWRDOWN, false);
+
+ #if OSAL_MUTEX_REQUIRED
+ osal_mutex_delete(_hcd_data.spi_mutex);
+ _hcd_data.spi_mutex = NULL;
+ #endif
+
+ return true;
+}
+
+// Enable USB interrupt
+// Not actually enable GPIO interrupt, just set variable to prevent handler to process
+void hcd_int_enable (uint8_t rhport) {
+ tuh_max3421_int_api(rhport, true);
+}
+
+// Disable USB interrupt
+// Not actually disable GPIO interrupt, just set variable to prevent handler to process
+void hcd_int_disable(uint8_t rhport) {
+ tuh_max3421_int_api(rhport, false);
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+ return (uint32_t ) _hcd_data.frame_count;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+ return (_hcd_data.mode & MODE_SOFKAENAB) ? true : false;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
+void hcd_port_reset(uint8_t rhport) {
+ reg_write(rhport, HCTL_ADDR, HCTL_BUSRST, false);
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport) {
+ reg_write(rhport, HCTL_ADDR, 0, false);
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+ return (_hcd_data.mode & MODE_LOWSPEED) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ (void) dev_addr;
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc) {
+ (void) rhport;
+
+ uint8_t const ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
+ tusb_dir_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
+
+ max3421_ep_t * ep;
+ if (daddr == 0 && ep_num == 0) {
+ ep = &_hcd_data.ep[0];
+ }else {
+ ep = allocate_ep();
+ TU_ASSERT(ep);
+ ep->daddr = daddr;
+ ep->hxfr_bm.ep_num = (uint8_t) (ep_num & 0x0f);
+ ep->hxfr_bm.is_out = (ep_dir == TUSB_DIR_OUT) ? 1 : 0;
+ ep->hxfr_bm.is_iso = (TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer) ? 1 : 0;
+ }
+
+ ep->packet_size = (uint16_t) (tu_edpt_packet_size(ep_desc) & 0x7ff);
+
+ return true;
+}
+
+/* The microcontroller repeatedly writes the SNDFIFO register R2 to load the FIFO with up to 64 data bytes.
+ * Then the microcontroller writes the SNDBC register, which this does three things:
+ * 1. Tells the MAX3421E SIE (Serial Interface Engine) how many bytes in the FIFO to send.
+ * 2. Connects the SNDFIFO and SNDBC register to the USB logic for USB transmission.
+ * 3. Clears the SNDBAVIRQ interrupt flag. If the second FIFO is available for µC loading, the SNDBAVIRQ immediately re-asserts.
+
+ +-----------+
+ --->| SNDBC-A |
+ / | SNDFIFO-A |
+ / +-----------+
+ +------+ +-------------+ / +----------+
+ | MCU |------>| R2: SNDFIFO |---- << Write R7 Flip >> ---| MAX3241E |
+ |(hcd) | | R7: SNDBC | / | SIE |
+ +------+ +-------------+ / +----------+
+ +-----------+ /
+ | SNDBC-B | /
+ | SNDFIFO-B |<---
+ +-----------+
+ Note: xact_out() is called when starting a new transfer, continue a transfer (isr) or retry a transfer (NAK)
+ For NAK retry, we do not need to write to FIFO or SNDBC register again.
+*/
+static void xact_out(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
+ // Page 12: Programming BULK-OUT Transfers
+ // TODO: double buffering for ISO transfer
+ if (switch_ep) {
+ peraddr_write(rhport, ep->daddr, in_isr);
+ const uint8_t hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0);
+ reg_write(rhport, HCTL_ADDR, hctl, in_isr);
+ }
+
+ // Only write to sndfifo and sdnbc register if it is not a NAKed retry
+ if (!(ep->daddr == _hcd_data.sndfifo_owner.daddr && ep->hxfr == _hcd_data.sndfifo_owner.hxfr)) {
+ // skip SNDBAV IRQ check, overwrite sndfifo if needed
+ const uint8_t xact_len = (uint8_t) tu_min16(ep->total_len - ep->xferred_len, ep->packet_size);
+ hwfifo_send(rhport, ep->buf, xact_len, in_isr);
+ }
+ _hcd_data.sndfifo_owner.daddr = ep->daddr;
+ _hcd_data.sndfifo_owner.hxfr = ep->hxfr;
+
+ hxfr_write(rhport, ep->hxfr, in_isr);
+}
+
+static void xact_in(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
+ // Page 13: Programming BULK-IN Transfers
+ if (switch_ep) {
+ peraddr_write(rhport, ep->daddr, in_isr);
+
+ uint8_t const hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0);
+ reg_write(rhport, HCTL_ADDR, hctl, in_isr);
+ }
+
+ hxfr_write(rhport, ep->hxfr, in_isr);
+}
+
+static void xact_setup(uint8_t rhport, max3421_ep_t *ep, bool in_isr) {
+ peraddr_write(rhport, ep->daddr, in_isr);
+ hwfifo_setup(rhport, ep->buf, in_isr);
+ hxfr_write(rhport, HXFR_SETUP, in_isr);
+}
+
+static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool in_isr) {
+ if (ep->hxfr_bm.ep_num == 0 ) {
+ // setup
+ if (ep->hxfr_bm.is_setup) {
+ xact_setup(rhport, ep, in_isr);
+ return;
+ }
+
+ // status
+ if (ep->buf == NULL || ep->total_len == 0) {
+ const uint8_t hxfr = (uint8_t) (HXFR_HS | (ep->hxfr & HXFR_OUT_NIN));
+ peraddr_write(rhport, ep->daddr, in_isr);
+ hxfr_write(rhport, hxfr, in_isr);
+ return;
+ }
+ }
+
+ if (ep->hxfr_bm.is_out) {
+ xact_out(rhport, ep, switch_ep, in_isr);
+ }else {
+ xact_in(rhport, ep, switch_ep, in_isr);
+ }
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
+ TU_VERIFY(ep);
+
+ if (ep_num == 0) {
+ // control transfer can switch direction
+ ep->hxfr_bm.is_out = ep_dir ? 0 : 1;
+ ep->hxfr_bm.is_setup = 0;
+ ep->data_toggle = 1;
+ }
+
+ ep->buf = buffer;
+ ep->total_len = buflen;
+ ep->xferred_len = 0;
+ ep->state = EP_STATE_ATTEMPT_1;
+
+ // carry out transfer if not busy
+ if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ xact_generic(rhport, ep, true, false);
+ }
+
+ return true;
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+ max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
+ TU_VERIFY(ep);
+
+ if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
+ hcd_int_disable(rhport);
+ ep->state = EP_STATE_ABORTING;
+ hcd_int_enable(rhport);
+ }
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+
+ max3421_ep_t* ep = find_opened_ep(daddr, 0, 0);
+ TU_ASSERT(ep);
+
+ ep->hxfr_bm.is_out = 1;
+ ep->hxfr_bm.is_setup = 1;
+ ep->buf = (uint8_t*)(uintptr_t) setup_packet;
+ ep->total_len = 8;
+ ep->xferred_len = 0;
+ ep->state = EP_STATE_ATTEMPT_1;
+
+ // carry out transfer if not busy
+ if (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ xact_setup(rhport, ep, false);
+ }
+
+ return true;
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// Interrupt Handler
+//--------------------------------------------------------------------+
+
+static void handle_connect_irq(uint8_t rhport, bool in_isr) {
+ uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
+ uint8_t const jk = hrsl & (HRSL_JSTATUS | HRSL_KSTATUS);
+
+ uint8_t new_mode = MODE_DPPULLDN | MODE_DMPULLDN | MODE_HOST;
+ TU_LOG2_HEX(jk);
+
+ switch(jk) {
+ case 0x00: // SEO is disconnected
+ case (HRSL_JSTATUS | HRSL_KSTATUS): // SE1 is illegal
+ mode_write(rhport, new_mode, in_isr);
+
+ // port reset anyway, this will help to stable bus signal for next connection
+ reg_write(rhport, HCTL_ADDR, HCTL_BUSRST, in_isr);
+ hcd_event_device_remove(rhport, in_isr);
+ reg_write(rhport, HCTL_ADDR, 0, in_isr);
+ break;
+
+ default: {
+ // Bus Reset also cause CONDET IRQ, skip if we are already connected and doing bus reset
+ if ((_hcd_data.hirq & HIRQ_BUSEVENT_IRQ) && (_hcd_data.mode & MODE_SOFKAENAB)) {
+ break;
+ }
+
+ // Low speed if (LS = 1 and J-state) or (LS = 0 and K-State)
+ // However, since we are always in full speed mode, we can just check J-state
+ if (jk == HRSL_KSTATUS) {
+ new_mode |= MODE_LOWSPEED;
+ TU_LOG3("Low speed\r\n");
+ }else {
+ TU_LOG3("Full speed\r\n");
+ }
+ new_mode |= MODE_SOFKAENAB;
+ mode_write(rhport, new_mode, in_isr);
+
+ // FIXME multiple MAX3421 rootdevice address is not 1
+ uint8_t const daddr = 1;
+ free_ep(daddr);
+
+ hcd_event_device_attach(rhport, in_isr);
+ break;
+ }
+ }
+}
+
+static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
+ uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
+ uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
+
+ // save data toggle
+ if (ep_dir) {
+ ep->data_toggle = (hrsl & HRSL_RCVTOGRD) ? 1u : 0u;
+ }else {
+ ep->data_toggle = (hrsl & HRSL_SNDTOGRD) ? 1u : 0u;
+ }
+
+ ep->state = EP_STATE_IDLE;
+ hcd_event_xfer_complete(ep->daddr, ep_addr, ep->xferred_len, result, in_isr);
+
+ // Find next pending endpoint
+ max3421_ep_t * next_ep = find_next_pending_ep(ep);
+ if (next_ep) {
+ xact_generic(rhport, next_ep, true, in_isr);
+ }else {
+ // no more pending
+ atomic_flag_clear(&_hcd_data.busy);
+ }
+}
+
+static void handle_xfer_done(uint8_t rhport, bool in_isr) {
+ const uint8_t hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
+ const uint8_t hresult = hrsl & HRSL_RESULT_MASK;
+ const uint8_t ep_num = _hcd_data.hxfr_bm.ep_num;
+ const uint8_t hxfr_type = _hcd_data.hxfr & 0xf0;
+ const uint8_t ep_dir = ((hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN)) ? 0 : 1;
+
+ max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, ep_dir);
+ TU_VERIFY(ep, );
+
+ xfer_result_t xfer_result;
+ switch(hresult) {
+ case HRSL_NAK:
+ if (ep->state == EP_STATE_ABORTING) {
+ ep->state = EP_STATE_IDLE;
+ } else {
+ if (ep_num == 0) {
+ // control endpoint -> retry immediately and return
+ hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+ return;
+ }
+ if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
+ ep->state++;
+ }
+ }
+
+ max3421_ep_t * next_ep = find_next_pending_ep(ep);
+ if (ep == next_ep) {
+ // this endpoint is only one pending -> retry immediately
+ hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+ } else if (next_ep) {
+ // switch to next pending endpoint
+ xact_generic(rhport, next_ep, true, in_isr);
+ } else {
+ // no more pending in this frame -> clear busy
+ atomic_flag_clear(&_hcd_data.busy);
+ }
+ return;
+
+ case HRSL_BAD_REQ:
+ // occurred when initialized without any pending transfer. Skip for now
+ return;
+
+ case HRSL_SUCCESS:
+ xfer_result = XFER_RESULT_SUCCESS;
+ break;
+
+ case HRSL_STALL:
+ xfer_result = XFER_RESULT_STALLED;
+ break;
+
+ default:
+ TU_LOG3("HRSL: %02X\r\n", hrsl);
+ xfer_result = XFER_RESULT_FAILED;
+ break;
+ }
+
+ if (xfer_result != XFER_RESULT_SUCCESS) {
+ xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
+ return;
+ }
+
+ if (ep_dir) {
+ // IN transfer: fifo data is already received in RCVDAV IRQ
+
+ // mark control handshake as complete
+ if (hxfr_type & HXFR_HS) {
+ ep->state = EP_STATE_COMPLETE;
+ }
+
+ // short packet or all bytes transferred
+ if (ep->state == EP_STATE_COMPLETE) {
+ xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
+ }else {
+ hxfr_write(rhport, _hcd_data.hxfr, in_isr); // more to transfer
+ }
+ } else {
+ // SETUP or OUT transfer
+
+ // clear sndfifo owner since data is sent
+ _hcd_data.sndfifo_owner.daddr = 0xff;
+ _hcd_data.sndfifo_owner.hxfr = 0xff;
+
+ uint8_t xact_len;
+
+ if (hxfr_type & HXFR_SETUP) {
+ xact_len = 8;
+ } else if (hxfr_type & HXFR_HS) {
+ xact_len = 0;
+ } else {
+ xact_len = _hcd_data.sndbc;
+ }
+
+ ep->xferred_len += xact_len;
+ ep->buf += xact_len;
+
+ if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
+ xfer_complete_isr(rhport, ep, xfer_result, hrsl, in_isr);
+ } else {
+ xact_out(rhport, ep, false, in_isr); // more to transfer
+ }
+ }
+}
+
+#if CFG_TUSB_DEBUG >= 3
+void print_hirq(uint8_t hirq) {
+ TU_LOG3_HEX(hirq);
+
+ if (hirq & HIRQ_HXFRDN_IRQ) TU_LOG3(" HXFRDN");
+ if (hirq & HIRQ_FRAME_IRQ) TU_LOG3(" FRAME");
+ if (hirq & HIRQ_CONDET_IRQ) TU_LOG3(" CONDET");
+ if (hirq & HIRQ_SUSDN_IRQ) TU_LOG3(" SUSDN");
+ if (hirq & HIRQ_SNDBAV_IRQ) TU_LOG3(" SNDBAV");
+ if (hirq & HIRQ_RCVDAV_IRQ) TU_LOG3(" RCVDAV");
+ if (hirq & HIRQ_RWU_IRQ) TU_LOG3(" RWU");
+ if (hirq & HIRQ_BUSEVENT_IRQ) TU_LOG3(" BUSEVENT");
+
+ TU_LOG3("\r\n");
+}
+#else
+ #define print_hirq(hirq)
+#endif
+
+// Interrupt handler
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
+ if (!hirq) return;
+// print_hirq(hirq);
+
+ if (hirq & HIRQ_FRAME_IRQ) {
+ _hcd_data.frame_count++;
+
+ // reset all endpoints nak counter, retry with 1st pending ep.
+ max3421_ep_t* ep_retry = NULL;
+ for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+ max3421_ep_t* ep = &_hcd_data.ep[i];
+ if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) {
+ ep->state = EP_STATE_ATTEMPT_1;
+
+ if (ep_retry == NULL) {
+ ep_retry = ep;
+ }
+ }
+ }
+
+ // start usb transfer if not busy
+ if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
+ xact_generic(rhport, ep_retry, true, in_isr);
+ }
+ }
+
+ if (hirq & HIRQ_CONDET_IRQ) {
+ handle_connect_irq(rhport, in_isr);
+ }
+
+ // queue more transfer in handle_xfer_done() can cause hirq to be set again while external IRQ may not catch and/or
+ // not call this handler again. So we need to loop until all IRQ are cleared
+ while (hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ)) {
+ if (hirq & HIRQ_RCVDAV_IRQ) {
+ const uint8_t ep_num = _hcd_data.hxfr_bm.ep_num;
+ max3421_ep_t* ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
+ uint8_t xact_len = 0;
+
+ // RCVDAV_IRQ can trigger 2 times (dual buffered)
+ while (hirq & HIRQ_RCVDAV_IRQ) {
+ const uint8_t rcvbc = reg_read(rhport, RCVBC_ADDR, in_isr);
+ xact_len = (uint8_t) tu_min16(rcvbc, ep->total_len - ep->xferred_len);
+ if (xact_len) {
+ hwfifo_receive(rhport, ep->buf, xact_len, in_isr);
+ ep->buf += xact_len;
+ ep->xferred_len += xact_len;
+ }
+
+ // ack RCVDVAV IRQ
+ hirq_write(rhport, HIRQ_RCVDAV_IRQ, in_isr);
+ hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
+ }
+
+ if (xact_len < ep->packet_size || ep->xferred_len >= ep->total_len) {
+ ep->state = EP_STATE_COMPLETE;
+ }
+ }
+
+ if (hirq & HIRQ_HXFRDN_IRQ) {
+ hirq_write(rhport, HIRQ_HXFRDN_IRQ, in_isr);
+ handle_xfer_done(rhport, in_isr);
+ }
+
+ hirq = reg_read(rhport, HIRQ_ADDR, in_isr);
+ }
+
+ // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
+ hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ;
+ if (hirq) {
+ hirq_write(rhport, hirq, in_isr);
+ }
+}
+
+#endif
diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
index 2445593fe..34a8be3b6 100644
--- a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
+++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright 2021 Bridgetek Pte Ltd
@@ -24,23 +24,20 @@
* This file is part of the TinyUSB stack.
*/
-/*
- * Contains code adapted from Bridgetek Pte Ltd via license terms stated
+/*
+ * Contains code adapted from Bridgetek Pte Ltd via license terms stated
* in https://brtchip.com/BRTSourceCodeLicenseAgreement
*/
#include "tusb_option.h"
#if CFG_TUD_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X)
+ (CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X)
#include <stdint.h>
#include <ft900.h>
#include <registers/ft900_registers.h>
-#include "board.h"
-#include "bsp/board.h"
-
#define USBD_USE_STREAMS
#include "device/dcd.h"
@@ -50,17 +47,21 @@
//--------------------------------------------------------------------+
// Board code will determine the state of VBUS from USB host.
-extern int8_t board_ft90x_vbus(void);
+extern int8_t board_ft9xx_vbus(void);
+extern int board_uart_write(void const *buf, int len);
// Static array to store an incoming SETUP request for processing by tinyusb.
-static uint8_t _ft90x_setup_packet[8];
+CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN
+static uint8_t _ft9xx_setup_packet[8];
-struct ft90x_xfer_state
+struct ft9xx_xfer_state
{
+ volatile uint8_t ready; // OUT Transfer has been received and waiting for transfer.
volatile uint8_t valid; // Transfer is pending and total_size, remain_size, and buff_ptr are valid.
- volatile int16_t total_size; // Total transfer size in bytes for this transfer.
- volatile int16_t remain_size; // Total remaining in transfer.
- volatile uint8_t *buff_ptr; // Pointer to buffer to transmit from or receive to.
+
+ int16_t total_size; // Total transfer size in bytes for this transfer.
+ int16_t remain_size; // Total remaining in transfer.
+ uint8_t *buff_ptr; // Pointer to buffer to transmit from or receive to.
uint8_t type; // Endpoint type. Of type USBD_ENDPOINT_TYPE from endpoint descriptor.
uint8_t dir; // Endpoint direction. TUSB_DIR_OUT or TUSB_DIR_IN. For control endpoint this is the current direction.
@@ -68,32 +69,32 @@ struct ft90x_xfer_state
uint16_t size; // Max packet size for endpoint from endpoint descriptor.
};
// Endpoint description array for each endpoint.
-static struct ft90x_xfer_state ep_xfer[USBD_MAX_ENDPOINT_COUNT];
+static struct ft9xx_xfer_state ep_xfer[USBD_MAX_ENDPOINT_COUNT];
// USB speed.
static tusb_speed_t _speed;
// Interrupt handlers.
-void _ft90x_usbd_ISR(void); // Interrupt handler for USB device.
-void ft90x_usbd_pm_ISR(void); // Interrupt handler for USB device for power management (called by board).
+void _ft9xx_usbd_ISR(void); // Interrupt handler for USB device.
+void ft9xx_usbd_pm_ISR(void); // Interrupt handler for USB device for power management (called by board).
// Internal functions forward declarations.
-static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
-static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
-static void _ft90x_reset_edpts(void);
-static inline void _ft90x_phy_enable(bool en);
-static void _ft90x_usb_speed(void);
-static void _dcd_ft90x_attach(void);
-static void _dcd_ft90x_detach(void) __attribute__((unused));
-static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length);
-static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length);
+static uint16_t _ft9xx_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
+static uint16_t _ft9xx_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes);
+static void _ft9xx_reset_edpts(void);
+static inline void _ft9xx_phy_enable(bool en);
+static void _ft9xx_usb_speed(void);
+static void _dcd_ft9xx_attach(void);
+static void _dcd_ft9xx_detach(void) __attribute__((unused));
+static uint16_t _ft9xx_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length);
+static uint16_t _ft9xx_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length);
// Internal functions.
// Manage an OUT transfer from the host.
// This can be up-to the maximum packet size of the endpoint.
-// Continuation of a transfer beyond the maximum packet size is performed
+// Continuation of a transfer beyond the maximum packet size is performed
// by the interrupt handler.
-static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
+static uint16_t _ft9xx_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
{
//Note: this is called from only the interrupt handler when an OUT transfer is called.
uint16_t ep_size = ep_xfer[ep_number].size;
@@ -108,7 +109,7 @@ static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_
//;
// Send the first packet of max packet size
- xfer_bytes = _ft90x_dusb_out(ep_number, (uint8_t *)buffer, xfer_bytes);
+ xfer_bytes = _ft9xx_dusb_out(ep_number, (uint8_t *)buffer, xfer_bytes);
if (ep_number == USBD_EP_0)
{
// Set flags to indicate data ready.
@@ -124,9 +125,9 @@ static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_
// Manage an IN transfer to the host.
// This can be up-to the maximum packet size of the endpoint.
-// Continuation of a transfer beyond the maximum packet size is performed
+// Continuation of a transfer beyond the maximum packet size is performed
// by the interrupt handler.
-static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
+static uint16_t _ft9xx_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes)
{
//Note: this may be called from the interrupt handler or from normal code.
uint8_t end = 0;
@@ -154,17 +155,24 @@ static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t
}
else
{
- uint8_t sr_reg;
// If there is data to transmit then wait until the IN buffer
// for the endpoint is empty.
- do
+ // This does not apply to interrupt endpoints.
+ if (ep_xfer[ep_number].type != TUSB_XFER_INTERRUPT)
{
- sr_reg = USBD_EP_SR_REG(ep_number);
- } while (sr_reg & MASK_USBD_EPxSR_INPRDY);
-
+ uint8_t sr_reg;
+ do
+ {
+ sr_reg = USBD_EP_SR_REG(ep_number);
+ } while (sr_reg & MASK_USBD_EPxSR_INPRDY);
+ }
}
- xfer_bytes = _ft90x_dusb_in(ep_number, (uint8_t *)buffer, xfer_bytes);
+ // Do not send a ZLP for interrupt endpoints.
+ if ((ep_xfer[ep_number].type != TUSB_XFER_INTERRUPT) || (xfer_bytes > 0))
+ {
+ xfer_bytes = _ft9xx_dusb_in(ep_number, (uint8_t *)buffer, xfer_bytes);
+ }
if (ep_number == USBD_EP_0)
{
@@ -188,25 +196,25 @@ static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t
return xfer_bytes;
}
-// Reset all non-control endpoints to a default state.
+// Reset all non-control endpoints to a default state.
// Control endpoint is always enabled and ready. All others disabled.
-static void _ft90x_reset_edpts(void)
+static void _ft9xx_reset_edpts(void)
{
// Disable all endpoints and remove configuration values.
for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
{
// Clear settings.
- tu_memclr(&ep_xfer[i], sizeof(struct ft90x_xfer_state));
+ tu_memclr(&ep_xfer[i], sizeof(struct ft9xx_xfer_state));
// Disable hardware.
USBD_EP_CR_REG(i) = 0;
}
-
+
// Enable interrupts from USB device control.
USBD_REG(cmie) = MASK_USBD_CMIE_ALL;
}
// Enable or disable the USB PHY.
-static inline void _ft90x_phy_enable(bool en)
+static inline void _ft9xx_phy_enable(bool en)
{
if (en)
SYS->PMCFG_L |= MASK_SYS_PMCFG_DEV_PHY_EN;
@@ -215,7 +223,7 @@ static inline void _ft90x_phy_enable(bool en)
}
// Safely connect to the USB.
-static void _dcd_ft90x_attach(void)
+static void _dcd_ft9xx_attach(void)
{
uint8_t reg;
@@ -271,7 +279,7 @@ static void _dcd_ft90x_attach(void)
}
// Gracefully disconnect from the USB.
-static void _dcd_ft90x_detach(void)
+static void _dcd_ft9xx_detach(void)
{
// Disable device connect/disconnect/host reset detection.
SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN);
@@ -311,9 +319,9 @@ static void _dcd_ft90x_detach(void)
}
// Determine the speed of the USB to which we are connected.
-// Set the speed of the PHY accordingly.
+// Set the speed of the PHY accordingly.
// High speed can be disabled through CFG_TUSB_RHPORT0_MODE or CFG_TUD_MAX_SPEED settings.
-static void _ft90x_usb_speed(void)
+static void _ft9xx_usb_speed(void)
{
uint8_t fctrl_val;
@@ -371,16 +379,16 @@ static void _ft90x_usb_speed(void)
}
// Send a buffer to the USB IN FIFO.
-// When the macro USBD_USE_STREAMS is defined this will stream a buffer of data
+// When the macro USBD_USE_STREAMS is defined this will stream a buffer of data
// to the FIFO using the most efficient MCU streamout combination.
-// If streaming is disabled then it will send each byte of the buffer in turn
+// If streaming is disabled then it will send each byte of the buffer in turn
// to the FIFO. The is no reason to not stream.
// The total number of bytes sent to the FIFO is returned.
-static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length)
+static uint16_t _ft9xx_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length)
{
uint16_t bytes_read = 0;
uint16_t buff_size = length;
-
+
#ifdef USBD_USE_STREAMS
volatile uint8_t *data_reg;
@@ -415,7 +423,7 @@ static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_
bytes_read = buff_size;
}
#else // USBD_USE_STREAMS
-
+
bytes_read = buff_size;
while (buff_size--)
{
@@ -430,17 +438,17 @@ static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_
// Receive a buffer from the USB OUT FIFO.
// When the macro USBD_USE_STREAMS is defined this will stream from the FIFO
// to a buffer of data using the most efficient MCU streamin combination.
-// If streaming is disabled then it will receive each byte from the FIFO in turn
+// If streaming is disabled then it will receive each byte from the FIFO in turn
// to the buffer. The is no reason to not stream.
// The total number of bytes received from the FIFO is returned.
-static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length)
+static uint16_t _ft9xx_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length)
{
#ifdef USBD_USE_STREAMS
volatile uint8_t *data_reg;
#endif // USBD_USE_STREAMS
uint16_t bytes_read = 0;
uint16_t buff_size = length;
-
+
if (length > 0)
{
if (ep_number == USBD_EP_0)
@@ -509,22 +517,23 @@ static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t len
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init(uint8_t rhport)
-{
- TU_LOG2("FT90x initialisation\r\n");
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ TU_LOG2("FT9xx initialisation\r\n");
- _dcd_ft90x_attach();
+ _dcd_ft9xx_attach();
- interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft90x_usbd_ISR);
+ interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft9xx_usbd_ISR);
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
void dcd_int_enable(uint8_t rhport)
{
(void)rhport;
- TU_LOG3("FT90x int enable\r\n");
+ TU_LOG3("FT9xx int enable\r\n");
// Peripheral devices interrupt enable.
interrupt_enable_globally();
@@ -534,7 +543,7 @@ void dcd_int_enable(uint8_t rhport)
void dcd_int_disable(uint8_t rhport)
{
(void)rhport;
- TU_LOG3("FT90x int disable\r\n");
+ TU_LOG3("FT9xx int disable\r\n");
// Peripheral devices interrupt disable.
interrupt_disable_globally();
@@ -587,8 +596,8 @@ void dcd_remote_wakeup(uint8_t rhport)
SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
- // Atleast 2 ms of delay needed for RESUME Data K state.
- delayms(2);
+ // At least 2 ms of delay needed for RESUME Data K state.
+ delayms(2);
SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP;
@@ -600,20 +609,20 @@ void dcd_remote_wakeup(uint8_t rhport)
void dcd_connect(uint8_t rhport)
{
(void)rhport;
- TU_LOG2("FT90x connect\r\n");
+ TU_LOG2("FT9xx connect\r\n");
CRITICAL_SECTION_BEGIN
// Is device connected?
- if (board_ft90x_vbus())
+ if (board_ft9xx_vbus())
{
// Clear/disable address register.
USBD_REG(faddr) = 0;
- _ft90x_phy_enable(true);
+ _ft9xx_phy_enable(true);
// Determine bus speed and signal speed to tusb.
- _ft90x_usb_speed();
+ _ft9xx_usb_speed();
}
-
+
// Setup the control endpoint only.
#if CFG_TUD_ENDPOINT0_SIZE == 64
USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_64 << BIT_USBD_EP0_MAX_SIZE);
@@ -636,17 +645,17 @@ void dcd_connect(uint8_t rhport)
USBD_REG(epie) = (MASK_USBD_EPIE_EP0IE);
// Restore default endpoint state.
- _ft90x_reset_edpts();
+ _ft9xx_reset_edpts();
}
// Disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
(void)rhport;
- TU_LOG2("FT90x disconnect\r\n");
+ TU_LOG2("FT9xx disconnect\r\n");
// Disable the USB PHY.
- _ft90x_phy_enable(false);
+ _ft9xx_phy_enable(false);
}
void dcd_sof_enable(uint8_t rhport, bool en)
@@ -674,12 +683,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
uint8_t ep_reg_data = 0;
int16_t total_ram;
- TU_LOG2("FT90x endpoint open %d %c\r\n", ep_number, ep_dir?'I':'O');
+ TU_LOG2("FT9xx endpoint open %d %c\r\n", ep_number, ep_dir?'I':'O');
// Check that the requested endpoint number is allowable.
if (ep_number >= USBD_MAX_ENDPOINT_COUNT)
{
- TU_LOG1("FT90x endpoint not valid: requested %d max %d\r\n", ep_number, USBD_MAX_ENDPOINT_COUNT);
+ TU_LOG1("FT9xx endpoint not valid: requested %d max %d\r\n", ep_number, USBD_MAX_ENDPOINT_COUNT);
return false;
}
@@ -691,10 +700,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
}
if (ep_reg_size > USBD_EP_MAX_SIZE_1024)
{
- TU_LOG1("FT90x endpoint size not valid: requested %d max 1024\r\n", ep_size);
+ TU_LOG1("FT9xx endpoint size not valid: requested %d max 1024\r\n", ep_size);
return false;
}
- // Calculate actual amount of buffer RAM used by this endpoint. This may be more than the
+ // Calculate actual amount of buffer RAM used by this endpoint. This may be more than the
// requested size.
ep_buff_size = 8 << ep_reg_size;
@@ -706,21 +715,21 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
if (ep_xfer[ep_number].type != USBD_EP_TYPE_DISABLED)
{
// This could be because an endpoint has been assigned with the same number.
- // On FT90x, IN and OUT endpoints may not have the same number. e.g. There
+ // On FT9xx, IN and OUT endpoints may not have the same number. e.g. There
// cannot been an 0x81 and 0x01 endpoint.
- TU_LOG1("FT90x endpoint %d already assigned\r\n", ep_number);
+ TU_LOG1("FT9xx endpoint %d already assigned\r\n", ep_number);
return false;
}
// Check that there is enough buffer RAM to allocate to this new endpoint.
// Available buffer RAM depends on the device revision.
// The IN and OUT buffer RAM should be the same size.
- if (ep_dir == USBD_DIR_IN)
+ if (ep_dir == USBD_DIR_IN)
total_ram = USBD_RAMTOTAL_IN;
else
total_ram = USBD_RAMTOTAL_OUT;
// Work out how much has been allocated to existing endpoints.
- // The total RAM allocated shoudl alsyes be a positive number as this
+ // The total RAM allocated should always be a positive number as this
// algorithm should not let it go below zero.
for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++)
{
@@ -732,23 +741,28 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
}
}
}
- // The control endpoint is taken into account as well.
- total_ram -= ep_xfer[0].buff_size;
+
+ if (sys_check_ft900_revB())
+ {
+ // The control endpoint is taken into account as well on RevB silicon.
+ total_ram -= ep_xfer[0].buff_size;
+ }
+
// Make sure we have enough space. The corner case is having zero bytes
// free which means that total_ram must be signed as zero bytes free is
// allowable.
if (total_ram < ep_buff_size)
{
- TU_LOG1("FT90x insufficient buffer RAM for endpoint %d\r\n", ep_number);
- return false;
+ TU_LOG1("FT9xx insufficient buffer RAM for endpoint %d\r\n", ep_number);
+ return false;
}
// Set the type of this endpoint in the control register.
- if (ep_type == USBD_EP_BULK)
+ if (ep_type == TUSB_XFER_BULK)
ep_reg_data |= (USBD_EP_DIS_BULK << BIT_USBD_EP_CONTROL_DIS);
- else if (ep_type == USBD_EP_INT)
+ else if (ep_type == TUSB_XFER_INTERRUPT)
ep_reg_data |= (USBD_EP_DIS_INT << BIT_USBD_EP_CONTROL_DIS);
- else if (ep_type == USBD_EP_ISOC)
+ else if (ep_type == TUSB_XFER_ISOCHRONOUS)
ep_reg_data |= (USBD_EP_DIS_ISO << BIT_USBD_EP_CONTROL_DIS);
// Set the direction of this endpoint in the control register.
if (ep_dir == USBD_DIR_IN)
@@ -756,9 +770,9 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
// Do not perform double buffering.
//if (<double buffering flag> != USBD_DB_OFF)
//ep_reg_data |= MASK_USBD_EPxCR_DB;
- // Set the control endpoint for this endpoint.
+ // Set the control register for this endpoint.
USBD_EP_CR_REG(ep_number) = ep_reg_data;
- TU_LOG2("FT90x endpoint setting %x\r\n", ep_reg_data);
+ TU_LOG2("FT9xx endpoint setting %x\r\n", ep_reg_data);
}
else
{
@@ -766,14 +780,15 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc)
USBD_EP_CR_REG(USBD_EP_0) = (ep_reg_size << BIT_USBD_EP0_MAX_SIZE);
}
+ CRITICAL_SECTION_BEGIN
// Store the endpoint characteristics for later reference.
ep_xfer[ep_number].dir = ep_dir;
ep_xfer[ep_number].type = ep_type;
ep_xfer[ep_number].size = ep_size;
ep_xfer[ep_number].buff_size = ep_buff_size;
- CRITICAL_SECTION_BEGIN
// Clear register transaction continuation and signalling state.
+ ep_xfer[ep_number].ready = 0;
ep_xfer[ep_number].valid = 0;
ep_xfer[ep_number].buff_ptr = NULL;
ep_xfer[ep_number].total_size = 0;
@@ -788,7 +803,7 @@ void dcd_edpt_close_all(uint8_t rhport)
{
(void)rhport;
// Reset the endpoint configurations.
- _ft90x_reset_edpts();
+ _ft9xx_reset_edpts();
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
@@ -796,7 +811,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
{
(void)rhport;
uint8_t ep_number = tu_edpt_number(ep_addr);
- uint8_t dir = tu_edpt_dir(ep_addr);
+ uint8_t ep_dir = tu_edpt_dir(ep_addr);
uint16_t xfer_bytes;
bool status = false;
@@ -806,19 +821,17 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
// ep_xfer is used to tell the interrupt handler what to do.
// ep_xfer can be used at interrupt level to continue transfers.
CRITICAL_SECTION_BEGIN
+
// Transfer currently in progress.
if (ep_xfer[ep_number].valid == 0)
{
- status = true;
-
ep_xfer[ep_number].total_size = total_bytes;
ep_xfer[ep_number].remain_size = total_bytes;
ep_xfer[ep_number].buff_ptr = buffer;
- ep_xfer[ep_number].valid = 1;
-
+
if (ep_number == USBD_EP_0)
{
- ep_xfer[USBD_EP_0].dir = dir;
+ ep_xfer[USBD_EP_0].dir = ep_dir;
}
else
{
@@ -827,18 +840,53 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
USBD_REG(epie) = USBD_REG(epie) | (1 << ep_number);
}
- if (dir == TUSB_DIR_IN)
+ if (ep_dir == TUSB_DIR_IN)
{
// For IN transfers send the first packet as a starter. Interrupt handler to complete
// this if it is larger than one packet.
- xfer_bytes = _ft90x_edpt_xfer_in(ep_number, buffer, total_bytes);
+ xfer_bytes = _ft9xx_edpt_xfer_in(ep_number, buffer, total_bytes);
ep_xfer[ep_number].buff_ptr += xfer_bytes;
ep_xfer[ep_number].remain_size -= xfer_bytes;
+
+ // Tell the interrupt handler to signal dcd_event_xfer_complete on completion.
+ ep_xfer[ep_number].valid = 1;
}
+ else // (dir == TUSB_DIR_OUT)
+ {
+ // For OUT transfers on the control endpoint.
+ // The host may already have performed the first data transfer after the SETUP packet
+ // before the transfer is setup for it.
+ if (ep_xfer[ep_number].ready)
+ {
+ // We have received a data packet on the endpoint without a transfer
+ // being initialised. This can be because the host has sent this packet before
+ // a new transfer has been initiated on the endpoint.
+ // We will now stream the data from the FIFO.
+ ep_xfer[ep_number].ready = 0;
+
+ // Transfer incoming data from an OUT packet to the buffer.
+ xfer_bytes = _ft9xx_edpt_xfer_out(ep_number, buffer, total_bytes);
+
+ // Report completion of the transfer.
+ dcd_event_xfer_complete(BOARD_TUD_RHPORT, ep_number /*| TUSB_DIR_OUT_MASK */, xfer_bytes, XFER_RESULT_SUCCESS, false);
+ }
+ else
+ {
+ // Tell the interrupt handler to wait for the packet to be received and
+ // then report the transfer complete with dcd_event_xfer_complete.
+ ep_xfer[ep_number].valid = 1;
+ }
+ }
+ status = true;
}
+ else
+ {
+ // Note: should not arrive here.
+ }
+
CRITICAL_SECTION_END
-
+
return status;
}
@@ -875,7 +923,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
CRITICAL_SECTION_END
}
-// Clear stall (non-control endpoint), data toggle is also reset to DATA0
+// Clear stall (non-control endpoint), data toggle is also reset to DATA0
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t ep_number = tu_edpt_number(ep_addr);
@@ -889,6 +937,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE;
// Allow transfers to restart.
+ ep_xfer[ep_number].ready = 0;
ep_xfer[ep_number].valid = 0;
ep_xfer[ep_number].remain_size = 0;
CRITICAL_SECTION_END
@@ -897,9 +946,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
// Interrupt handling.
-void _ft90x_usbd_ISR(void)
+void _ft9xx_usbd_ISR(void)
{
- tud_int_handler(BOARD_TUD_RHPORT); // Resolves to dcd_int_handler().
+ dcd_int_handler(BOARD_TUD_RHPORT);
}
void dcd_int_handler(uint8_t rhport)
@@ -936,7 +985,7 @@ void dcd_int_handler(uint8_t rhport)
if (cmif & MASK_USBD_CMIF_RSTIRQ) //Handle Reset interrupt
{
// Reset endpoints to default state.
- _ft90x_reset_edpts();
+ _ft9xx_reset_edpts();
dcd_event_bus_reset(BOARD_TUD_RHPORT, _speed, true);
}
if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt
@@ -962,7 +1011,6 @@ void dcd_int_handler(uint8_t rhport)
{
// Clear interrupt register.
USBD_REG(epif) = MASK_USBD_EPIF_EP0IRQ;
-
// Test for an incoming SETUP request on the control endpoint.
if (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_SETUP)
{
@@ -976,16 +1024,19 @@ void dcd_int_handler(uint8_t rhport)
USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_STALL;
}
- // Host has sent a SETUP packet. Recieve this into the setup packet store.
- _ft90x_dusb_out(USBD_EP_0, (uint8_t *)_ft90x_setup_packet, sizeof(USB_device_request));
-
+ // Host has sent a SETUP packet. Receive this into the SETUP packet store.
+ _ft9xx_dusb_out(USBD_EP_0, (uint8_t *)_ft9xx_setup_packet, sizeof(USB_device_request));
+
// Send the packet to tinyusb.
- dcd_event_setup_received(BOARD_TUD_RHPORT, _ft90x_setup_packet, true);
+ dcd_event_setup_received(BOARD_TUD_RHPORT, _ft9xx_setup_packet, true);
// Clear the interrupt that signals a SETUP packet is received.
USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_SETUP);
- // Allow new transfers on the control endpoint.
+ // Any SETUP packet will clear the incoming FIFO.
+ ep_xfer[USBD_EP_0].ready = 0;
+
+ // Allow new DATA and ACK transfers on the control endpoint.
ep_xfer[USBD_EP_0].valid = 0;
return;
}
@@ -996,17 +1047,31 @@ void dcd_int_handler(uint8_t rhport)
{
xfer_bytes = (uint16_t)ep_xfer[USBD_EP_0].total_size;
- // Transfer incoming data from an OUT packet to the buffer supplied.
+ // Transfer incoming data from an OUT packet to the buffer supplied.
if (ep_xfer[USBD_EP_0].dir == TUSB_DIR_OUT)
- {
- xfer_bytes = _ft90x_edpt_xfer_out(USBD_EP_0, (uint8_t *)ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes);
+ {
+ xfer_bytes = _ft9xx_edpt_xfer_out(USBD_EP_0, ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes);
}
// Now signal completion of data packet.
- dcd_event_xfer_complete(BOARD_TUD_RHPORT, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true);
+ dcd_event_xfer_complete(BOARD_TUD_RHPORT, USBD_EP_0 | (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0),
+ xfer_bytes, XFER_RESULT_SUCCESS, true);
+
+ // Incoming FIFO has been cleared.
+ ep_xfer[USBD_EP_0].ready = 0;
// Allow new transfers on the control endpoint.
ep_xfer[USBD_EP_0].valid = 0;
}
+ // No transfer is in flight for EP0.
+ else
+ {
+ // We have received a data packet on the control endpoint without a transfer
+ // being initialised. This can be because the host has sent this packet before
+ // a new transfer has been initiated on the control endpoint.
+ // We will record that there is data in the FIFO for dcd_edpt_xfer to obtain
+ // once the transfer is initiated.
+ ep_xfer[USBD_EP_0].ready = 1;
+ }
}
}
else // !(epif & MASK_USBD_EPIF_EP0IRQ)
@@ -1026,7 +1091,6 @@ void dcd_int_handler(uint8_t rhport)
if (ep_xfer[ep_number].valid)
{
xfer_bytes = 0;
- uint8_t ep_dirmask = (ep_xfer[ep_number].dir ? TUSB_DIR_IN_MASK : 0);
// Clear interrupt register for this endpoint.
USBD_REG(epif) = MASK_USBD_EPIF_IRQ(ep_number);
@@ -1034,10 +1098,15 @@ void dcd_int_handler(uint8_t rhport)
// Start or continue an OUT transfer.
if (ep_xfer[ep_number].dir == TUSB_DIR_OUT)
{
- xfer_bytes = _ft90x_edpt_xfer_out(ep_number,
- (uint8_t *)ep_xfer[ep_number].buff_ptr,
+ xfer_bytes = _ft9xx_edpt_xfer_out(ep_number,
+ ep_xfer[ep_number].buff_ptr,
(uint16_t)ep_xfer[ep_number].remain_size);
+ // Report each OUT packet received to the stack.
+ dcd_event_xfer_complete(BOARD_TUD_RHPORT,
+ ep_number /* | TUSB_DIR_OUT_MASK */,
+ xfer_bytes, XFER_RESULT_SUCCESS, true);
+
ep_xfer[ep_number].buff_ptr += xfer_bytes;
ep_xfer[ep_number].remain_size -= xfer_bytes;
}
@@ -1046,40 +1115,58 @@ void dcd_int_handler(uint8_t rhport)
{
if (ep_xfer[ep_number].remain_size > 0)
{
- xfer_bytes = _ft90x_edpt_xfer_in(ep_number,
- (uint8_t *)ep_xfer[ep_number].buff_ptr,
+ xfer_bytes = _ft9xx_edpt_xfer_in(ep_number,
+ ep_xfer[ep_number].buff_ptr,
(uint16_t)ep_xfer[ep_number].remain_size);
ep_xfer[ep_number].buff_ptr += xfer_bytes;
ep_xfer[ep_number].remain_size -= xfer_bytes;
}
+
+ if (ep_xfer[ep_number].remain_size == 0)
+ {
+ dcd_event_xfer_complete(BOARD_TUD_RHPORT,
+ ep_number | TUSB_DIR_IN_MASK,
+ ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true);
+ }
}
// When the transfer is complete...
if (ep_xfer[ep_number].remain_size == 0)
{
- // Signal tinyUSB.
- dcd_event_xfer_complete(BOARD_TUD_RHPORT, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true);
-
- // Allow new transfers on this endpoint.
+ // Finish this transfer and allow new transfers on this endpoint.
ep_xfer[ep_number].valid = 0;
// Disable the interrupt for this endpoint now it is complete.
USBD_REG(epie) = USBD_REG(epie) & (~(1 << ep_number));
}
+
+ ep_xfer[ep_number].ready = 0;
+ }
+ // No OUT transfer is in flight for this endpoint.
+ else
+ {
+ if (ep_xfer[ep_number].dir == TUSB_DIR_OUT)
+ {
+ // We will record that there is data in the FIFO for dcd_edpt_xfer to obtain
+ // once the transfer is initiated.
+ // Strictly this should not happen for a non-control endpoint. Interrupts
+ // are disabled when there are no transfers setup for an endpoint.
+ ep_xfer[ep_number].ready = 1;
+ }
}
}
}
}
}
-// Power management interrupt handler.
+// Power management interrupt handler.
// This handles USB device related power management interrupts only.
-void ft90x_usbd_pm_ISR(void)
+void ft9xx_usbd_pm_ISR(void)
{
uint16_t pmcfg = SYS->PMCFG_H;
- // Main interrupt handler is responible for
+ // Main interrupt handler is responible for
if (pmcfg & MASK_SYS_PMCFG_DEV_CONN_DEV)
{
// Signal connection interrupt
diff --git a/src/portable/mentor/musb/musb_msp432e.h b/src/portable/chipidea/ci_fs/ci_fs_kinetis.h
index fce21de88..31e14a546 100644
--- a/src/portable/mentor/musb/musb_msp432e.h
+++ b/src/portable/chipidea/ci_fs/ci_fs_kinetis.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 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
@@ -24,17 +24,26 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MUSB_MSP432E_H_
-#define _TUSB_MUSB_MSP432E_H_
+#ifndef _CI_FS_KINETIS_H
+#define _CI_FS_KINETIS_H
-#ifdef __cplusplus
- extern "C" {
-#endif
+#include "fsl_device_registers.h"
-#include "msp.h"
+//static const ci_fs_controller_t _ci_controller[] = {
+// {.reg_base = USB0_BASE, .irqnum = USB0_IRQn}
+//};
-#ifdef __cplusplus
- }
-#endif
+#define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
+#define CI_REG CI_FS_REG(0)
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USB0_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USB0_IRQn);
+}
#endif
diff --git a/src/portable/mentor/musb/musb_tm4c.h b/src/portable/chipidea/ci_fs/ci_fs_mcx.h
index 65a1751b0..4b93a03a7 100644
--- a/src/portable/mentor/musb/musb_tm4c.h
+++ b/src/portable/chipidea/ci_fs/ci_fs_mcx.h
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 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
@@ -24,22 +24,33 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _TUSB_MUSB_TM4C_H_
-#define _TUSB_MUSB_TM4C_H_
+#ifndef _CI_FS_MCX_H
+#define _CI_FS_MCX_H
-#ifdef __cplusplus
- extern "C" {
-#endif
+#include "fsl_device_registers.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_MCXN9
+ #define CI_FS_REG(_port) ((ci_fs_regs_t*) USBFS0_BASE)
+ #define CIFS_IRQN USB0_FS_IRQn
+
+#elif CFG_TUSB_MCU == OPT_MCU_MCXA15
+ #define CI_FS_REG(_port) ((ci_fs_regs_t*) USB0_BASE)
+ #define CIFS_IRQN USB0_IRQn
-#if CFG_TUSB_MCU == OPT_MCU_TM4C123
- #include "TM4C123.h"
-//#elif CFG_TUSB_MCU == OPT_MCU_TM4C129
#else
- #error "Unsupported MCUs"
+ #error "MCU is not supported"
#endif
-#ifdef __cplusplus
- }
-#endif
+#define CI_REG CI_FS_REG(0)
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(CIFS_IRQN);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(CIFS_IRQN);
+}
#endif
diff --git a/src/portable/chipidea/ci_fs/ci_fs_type.h b/src/portable/chipidea/ci_fs/ci_fs_type.h
new file mode 100644
index 000000000..5a5e53fb0
--- /dev/null
+++ b/src/portable/chipidea/ci_fs/ci_fs_type.h
@@ -0,0 +1,118 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _CI_FS_TYPE_H
+#define _CI_FS_TYPE_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+// Note: some MCUs can only access these registers in 8-bit mode
+// align 4 is used to get rid of reserved fields
+#define _va32 volatile TU_ATTR_ALIGNED(4)
+
+typedef struct {
+ _va32 uint8_t PER_ID; // [00] Peripheral ID register
+ _va32 uint8_t ID_COMP; // [04] Peripheral ID complement register
+ _va32 uint8_t REV; // [08] Peripheral revision register
+ _va32 uint8_t ADD_INFO; // [0C] Peripheral additional info register
+ _va32 uint8_t OTG_ISTAT; // [10] OTG Interrupt Status Register
+ _va32 uint8_t OTG_ICTRL; // [14] OTG Interrupt Control Register
+ _va32 uint8_t OTG_STAT; // [18] OTG Status Register
+ _va32 uint8_t OTG_CTRL; // [1C] OTG Control register
+ uint32_t reserved_20[24]; // [20]
+ _va32 uint8_t INT_STAT; // [80] Interrupt status register
+ _va32 uint8_t INT_EN; // [84] Interrupt enable register
+ _va32 uint8_t ERR_STAT; // [88] Error interrupt status register
+ _va32 uint8_t ERR_ENB; // [8C] Error interrupt enable register
+ _va32 uint8_t STAT; // [90] Status register
+ _va32 uint8_t CTL; // [94] Control register
+ _va32 uint8_t ADDR; // [98] Address register
+ _va32 uint8_t BDT_PAGE1; // [9C] BDT page register 1
+ _va32 uint8_t FRM_NUML; // [A0] Frame number register
+ _va32 uint8_t FRM_NUMH; // [A4] Frame number register
+ _va32 uint8_t TOKEN; // [A8] Token register
+ _va32 uint8_t SOF_THLD; // [AC] SOF threshold register
+ _va32 uint8_t BDT_PAGE2; // [B0] BDT page register 2
+ _va32 uint8_t BDT_PAGE3; // [B4] BDT page register 3
+
+ uint32_t reserved_b8; // [B8]
+ uint32_t reserved_bc; // [BC]
+
+ struct {
+ _va32 uint8_t CTL;
+ }EP[16]; // [C0] Endpoint control register
+
+ //----- Following is only found available in NXP Kinetis
+ _va32 uint8_t USBCTRL; // [100] USB Control register,
+ _va32 uint8_t OBSERVE; // [104] USB OTG Observe register,
+ _va32 uint8_t CONTROL; // [108] USB OTG Control register,
+ _va32 uint8_t USBTRC0; // [10C] USB Transceiver Control Register 0,
+ uint32_t reserved_110; // [110]
+ _va32 uint8_t USBFRMADJUST; // [114] Frame Adjust Register,
+
+ //----- Following is only found available in NXP MCX
+ uint32_t reserved_118[3]; // [118]
+ _va32 uint8_t KEEP_ALIVE_CTRL; // [124] Keep Alive Mode Control,
+ _va32 uint8_t KEEP_ALIVE_WKCTRL; // [128] Keep Alive Mode Wakeup Control,
+ _va32 uint8_t MISCCTRL; // [12C] Miscellaneous Control,
+ _va32 uint8_t STALL_IL_DIS; // [130] Peripheral Mode Stall Disable for Endpoints[ 7..0] IN
+ _va32 uint8_t STALL_IH_DIS; // [134] Peripheral Mode Stall Disable for Endpoints[15..8] IN
+ _va32 uint8_t STALL_OL_DIS; // [138] Peripheral Mode Stall Disable for Endpoints[ 7..0] OUT
+ _va32 uint8_t STALL_OH_DIS; // [13C] Peripheral Mode Stall Disable for Endpoints[15..8] OUT
+ _va32 uint8_t CLK_RECOVER_CTRL; // [140] USB Clock Recovery Control,
+ _va32 uint8_t CLK_RECOVER_IRC_EN; // [144] FIRC Oscillator Enable,
+ uint32_t reserved_148[3]; // [148]
+ _va32 uint8_t CLK_RECOVER_INT_EN; // [154] Clock Recovery Combined Interrupt Enable,
+ uint32_t reserved_158; // [158]
+ _va32 uint8_t CLK_RECOVER_INT_STATUS; // [15C] Clock Recovery Separated Interrupt Status,
+} ci_fs_regs_t;
+
+TU_VERIFY_STATIC(sizeof(ci_fs_regs_t) == 0x160, "Size is not correct");
+
+typedef struct
+{
+ uint32_t reg_base;
+ uint32_t irqnum;
+} ci_fs_controller_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
new file mode 100644
index 000000000..11ddb683f
--- /dev/null
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -0,0 +1,568 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
+
+#include "device/dcd.h"
+#include "ci_fs_type.h"
+
+#if defined(TUP_USBIP_CHIPIDEA_FS_KINETIS)
+ #include "ci_fs_kinetis.h"
+#elif defined(TUP_USBIP_CHIPIDEA_FS_MCX)
+ #include "ci_fs_mcx.h"
+#else
+ #error "MCU is not supported"
+#endif
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+enum {
+ TOK_PID_OUT = 0x1u,
+ TOK_PID_IN = 0x9u,
+ TOK_PID_SETUP = 0xDu,
+};
+
+typedef struct TU_ATTR_PACKED
+{
+ union {
+ uint32_t head;
+ struct {
+ union {
+ struct {
+ uint16_t : 2;
+ __IO uint16_t tok_pid : 4;
+ uint16_t data : 1;
+ __IO uint16_t own : 1;
+ uint16_t : 8;
+ };
+ struct {
+ uint16_t : 2;
+ uint16_t bdt_stall : 1;
+ uint16_t dts : 1;
+ uint16_t ninc : 1;
+ uint16_t keep : 1;
+ uint16_t : 10;
+ };
+ };
+ __IO uint16_t bc : 10;
+ uint16_t : 6;
+ };
+ };
+ uint8_t *addr;
+}buffer_descriptor_t;
+
+TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
+
+typedef struct TU_ATTR_PACKED
+{
+ union {
+ uint32_t state;
+ struct {
+ uint32_t max_packet_size :11;
+ uint32_t : 5;
+ uint32_t odd : 1;
+ uint32_t :15;
+ };
+ };
+ uint16_t length;
+ uint16_t remaining;
+}endpoint_state_t;
+
+TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
+
+typedef struct
+{
+ union {
+ /* [#EP][OUT,IN][EVEN,ODD] */
+ buffer_descriptor_t bdt[16][2][2];
+ uint16_t bda[512];
+ };
+ TU_ATTR_ALIGNED(4) union {
+ endpoint_state_t endpoint[16][2];
+ endpoint_state_t endpoint_unified[16 * 2];
+ };
+ uint8_t setup_packet[8];
+ uint8_t addr;
+}dcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// BDT(Buffer Descriptor Table) must be 256-byte aligned
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
+
+TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
+
+static void prepare_next_setup_packet(uint8_t rhport)
+{
+ const unsigned out_odd = _dcd.endpoint[0][0].odd;
+ const unsigned in_odd = _dcd.endpoint[0][1].odd;
+ TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
+
+ _dcd.bdt[0][0][out_odd].data = 0;
+ _dcd.bdt[0][0][out_odd ^ 1].data = 1;
+ _dcd.bdt[0][1][in_odd].data = 1;
+ _dcd.bdt[0][1][in_odd ^ 1].data = 0;
+ dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
+ _dcd.setup_packet, sizeof(_dcd.setup_packet));
+}
+
+static void process_stall(uint8_t rhport)
+{
+ for (int i = 0; i < 16; ++i) {
+ uint32_t const ep_ctl = CI_REG->EP[i].CTL;
+
+ if (ep_ctl & USB_ENDPT_EPSTALL_MASK) {
+ // prepare next setup if endpoint0
+ if ( i == 0 ) prepare_next_setup_packet(rhport);
+
+ // clear stall bit
+ CI_REG->EP[i].CTL = ep_ctl & ~USB_ENDPT_EPSTALL_MASK;
+ }
+ }
+}
+
+static void process_tokdne(uint8_t rhport)
+{
+ const unsigned s = CI_REG->STAT;
+ CI_REG->INT_STAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
+
+ uint8_t const epnum = (s >> USB_STAT_ENDP_SHIFT);
+ uint8_t const dir = (s & USB_STAT_TX_MASK) >> USB_STAT_TX_SHIFT;
+ unsigned const odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
+
+ buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
+ endpoint_state_t *ep = &_dcd.endpoint_unified[s >> 3];
+
+ /* fetch pid before discarded by the next steps */
+ const unsigned pid = bd->tok_pid;
+
+ /* reset values for a next transfer */
+ bd->bdt_stall = 0;
+ bd->dts = 1;
+ bd->ninc = 0;
+ bd->keep = 0;
+ /* update the odd variable to prepare for the next transfer */
+ ep->odd = odd ^ 1;
+ if (pid == TOK_PID_SETUP) {
+ dcd_event_setup_received(rhport, bd->addr, true);
+ CI_REG->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+ return;
+ }
+
+ const unsigned bc = bd->bc;
+ const unsigned remaining = ep->remaining - bc;
+ if (remaining && bc == ep->max_packet_size) {
+ /* continue the transferring consecutive data */
+ ep->remaining = remaining;
+ const int next_remaining = remaining - ep->max_packet_size;
+ if (next_remaining > 0) {
+ /* prepare to the after next transfer */
+ bd->addr += ep->max_packet_size * 2;
+ bd->bc = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
+ __DSB();
+ bd->own = 1; /* the own bit must set after addr */
+ }
+ return;
+ }
+ const unsigned length = ep->length;
+ dcd_event_xfer_complete(rhport,
+ tu_edpt_addr(epnum, dir),
+ length - remaining, XFER_RESULT_SUCCESS, true);
+ if (0 == epnum && 0 == length) {
+ /* After completion a ZLP of control transfer,
+ * it prepares for the next steup transfer. */
+ if (_dcd.addr) {
+ /* When the transfer was the SetAddress,
+ * the device address should be updated here. */
+ CI_REG->ADDR = _dcd.addr;
+ _dcd.addr = 0;
+ }
+ prepare_next_setup_packet(rhport);
+ }
+}
+
+static void process_bus_reset(uint8_t rhport)
+{
+ CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+ CI_REG->CTL |= USB_CTL_ODDRST_MASK;
+ CI_REG->ADDR = 0;
+ CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK | USB_INTEN_SLEEPEN_MASK |
+ USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
+
+ CI_REG->EP[0].CTL = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
+ for (unsigned i = 1; i < 16; ++i) {
+ CI_REG->EP[i].CTL = 0;
+ }
+ buffer_descriptor_t *bd = _dcd.bdt[0][0];
+ for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
+ bd->head = 0;
+ }
+ const endpoint_state_t ep0 = {
+ .max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
+ .odd = 0,
+ .length = 0,
+ .remaining = 0,
+ };
+ _dcd.endpoint[0][0] = ep0;
+ _dcd.endpoint[0][1] = ep0;
+ tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
+ _dcd.addr = 0;
+ prepare_next_setup_packet(rhport);
+ CI_REG->CTL &= ~USB_CTL_ODDRST_MASK;
+ dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+}
+
+static void process_bus_sleep(uint8_t rhport)
+{
+ // Enable resume & disable suspend interrupt
+ const unsigned inten = CI_REG->INT_EN;
+
+ CI_REG->INT_EN = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
+ CI_REG->USBTRC0 |= USB_USBTRC0_USBRESMEN_MASK;
+ CI_REG->USBCTRL |= USB_USBCTRL_SUSP_MASK;
+
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+}
+
+static void process_bus_resume(uint8_t rhport)
+{
+ // Enable suspend & disable resume interrupt
+ const unsigned inten = CI_REG->INT_EN;
+
+ CI_REG->USBCTRL &= ~USB_USBCTRL_SUSP_MASK; // will also clear USB_USBTRC0_USB_RESUME_INT_MASK
+ CI_REG->USBTRC0 &= ~USB_USBTRC0_USBRESMEN_MASK;
+ CI_REG->INT_EN = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
+
+ // save crystal-less setting (if available)
+ #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
+ uint32_t clk_recover_irc_en = CI_REG->CLK_RECOVER_IRC_EN;
+ uint32_t clk_recover_ctrl = CI_REG->CLK_RECOVER_CTRL;
+ #endif
+
+ CI_REG->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+ while (CI_REG->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+
+ // restore crystal-less setting (if available)
+ #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
+ CI_REG->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
+ CI_REG->CLK_RECOVER_CTRL |= clk_recover_ctrl;
+ #endif
+
+ tu_memclr(&_dcd, sizeof(_dcd));
+ CI_REG->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
+ CI_REG->BDT_PAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
+ CI_REG->BDT_PAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
+ CI_REG->BDT_PAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
+
+ CI_REG->INT_EN = USB_INTEN_USBRSTEN_MASK;
+
+ dcd_connect(rhport);
+ // NVIC_ClearPendingIRQ(CIFS_IRQN);
+ return true;
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+ _dcd.addr = dev_addr & 0x7F;
+ /* Response with status first before 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;
+
+ CI_REG->CTL |= USB_CTL_RESUME_MASK;
+
+ unsigned cnt = SystemCoreClock / 1000;
+ while (cnt--) __NOP();
+
+ CI_REG->CTL &= ~USB_CTL_RESUME_MASK;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ (void) rhport;
+ CI_REG->USBCTRL = 0;
+ CI_REG->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
+ CI_REG->CTL |= USB_CTL_USBENSOFEN_MASK;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ (void) rhport;
+ CI_REG->CTL = 0;
+ CI_REG->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en)
+{
+ (void) rhport;
+ (void) en;
+
+ // TODO implement later
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void) rhport;
+
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned xfer = ep_desc->bmAttributes.xfer;
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ const unsigned odd = ep->odd;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+
+ /* No support for control transfer */
+ TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
+
+ ep->max_packet_size = tu_edpt_packet_size(ep_desc);
+ unsigned val = USB_ENDPT_EPCTLDIS_MASK;
+ val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
+ val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+ CI_REG->EP[epn].CTL |= val;
+
+ if (xfer != TUSB_XFER_ISOCHRONOUS) {
+ bd[odd].dts = 1;
+ bd[odd].data = 0;
+ bd[odd ^ 1].dts = 1;
+ bd[odd ^ 1].data = 1;
+ }
+
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport)
+{
+ dcd_int_disable(rhport);
+
+ for (unsigned i = 1; i < 16; ++i) {
+ CI_REG->EP[i].CTL = 0;
+ }
+
+ dcd_int_enable(rhport);
+
+ buffer_descriptor_t *bd = _dcd.bdt[1][0];
+ for (unsigned i = 2; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
+ bd->head = 0;
+ }
+
+ endpoint_state_t *ep = &_dcd.endpoint[1][0];
+ for (unsigned i = 2; i < sizeof(_dcd.endpoint)/sizeof(*ep); ++i, ++ep) {
+ /* Clear except the odd */
+ ep->max_packet_size = 0;
+ ep->length = 0;
+ ep->remaining = 0;
+ }
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+ const unsigned msk = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+
+ dcd_int_disable(rhport);
+
+ CI_REG->EP[epn].CTL &= ~msk;
+ ep->max_packet_size = 0;
+ ep->length = 0;
+ ep->remaining = 0;
+ bd[0].head = 0;
+ bd[1].head = 0;
+
+ dcd_int_enable(rhport);
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ endpoint_state_t *ep = &_dcd.endpoint[epn][dir];
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
+ TU_ASSERT(0 == bd->own);
+
+ dcd_int_disable(rhport);
+
+ ep->length = total_bytes;
+ ep->remaining = total_bytes;
+
+ const unsigned mps = ep->max_packet_size;
+ if (total_bytes > mps) {
+ buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
+ /* When total_bytes is greater than the max packet size,
+ * it prepares to the next transfer to avoid NAK in advance. */
+ next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps;
+ next->addr = buffer + mps;
+ next->own = 1;
+ }
+ bd->bc = total_bytes >= mps ? mps: total_bytes;
+ bd->addr = buffer;
+ __DSB();
+ bd->own = 1; /* This bit must be set last */
+
+ dcd_int_enable(rhport);
+
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ (void) rhport;
+ const unsigned epn = tu_edpt_number(ep_addr);
+
+ if (0 == epn) {
+ CI_REG->EP[epn].CTL |= USB_ENDPT_EPSTALL_MASK;
+ } else {
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned odd = _dcd.endpoint[epn][dir].odd;
+ buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][odd];
+ TU_ASSERT(0 == bd->own,);
+
+ dcd_int_disable(rhport);
+
+ bd->bdt_stall = 1;
+ __DSB();
+ bd->own = 1; /* This bit must be set last */
+
+ dcd_int_enable(rhport);
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ TU_VERIFY(epn,);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned odd = _dcd.endpoint[epn][dir].odd;
+ buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+ TU_VERIFY(bd[odd].own,);
+
+ dcd_int_disable(rhport);
+
+ bd[odd].own = 0;
+ __DSB();
+
+ // clear stall
+ bd[odd].bdt_stall = 0;
+
+ // Reset data toggle
+ bd[odd ].data = 0;
+ bd[odd ^ 1].data = 1;
+
+ // We already cleared this in ISR, but just clear it here to be safe
+ const uint32_t ep_ctl = CI_REG->EP[epn].CTL;
+ if (ep_ctl & USB_ENDPT_EPSTALL_MASK) {
+ CI_REG->EP[epn].CTL = ep_ctl & ~USB_ENDPT_EPSTALL_MASK;
+ }
+
+ dcd_int_enable(rhport);
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+ uint32_t is = CI_REG->INT_STAT;
+ uint32_t msk = CI_REG->INT_EN;
+
+ // clear non-enabled interrupts
+ CI_REG->INT_STAT = is & ~msk;
+ is &= msk;
+
+ if (is & USB_ISTAT_ERROR_MASK) {
+ /* TODO: */
+ uint32_t es = CI_REG->ERR_STAT;
+ CI_REG->ERR_STAT = es;
+ CI_REG->INT_STAT = is; /* discard any pending events */
+ }
+
+ if (is & USB_ISTAT_USBRST_MASK) {
+ CI_REG->INT_STAT = is; /* discard any pending events */
+ process_bus_reset(rhport);
+ }
+
+ if (is & USB_ISTAT_SLEEP_MASK) {
+ // TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
+
+ // Note Host usually has extra delay after bus reset (without SOF), which could falsely
+ // detected as Sleep event. Though usbd has debouncing logic so we are good
+ CI_REG->INT_STAT = USB_ISTAT_SLEEP_MASK;
+ process_bus_sleep(rhport);
+ }
+
+#if 0 // ISTAT_RESUME never trigger, probably for host mode ?
+ if (is & USB_ISTAT_RESUME_MASK) {
+ // TU_LOG2("ISTAT Resume: "); TU_LOG2_HEX(is);
+ KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
+ process_bus_resume(rhport);
+ }
+#endif
+
+ if (CI_REG->USBTRC0 & USB_USBTRC0_USB_RESUME_INT_MASK) {
+ // TU_LOG2("USBTRC0 Resume: "); TU_LOG2_HEX(is); TU_LOG2_HEX(KHCI->USBTRC0);
+ process_bus_resume(rhport);
+ }
+
+ if (is & USB_ISTAT_SOFTOK_MASK) {
+ CI_REG->INT_STAT = USB_ISTAT_SOFTOK_MASK;
+ dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ }
+
+ if (is & USB_ISTAT_STALL_MASK) {
+ CI_REG->INT_STAT = USB_ISTAT_STALL_MASK;
+ process_stall(rhport);
+ }
+
+ if (is & USB_ISTAT_TOKDNE_MASK) {
+ process_tokdne(rhport);
+ }
+}
+
+#endif
diff --git a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
index 2de0d9cb4..75d1d55b8 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
@@ -37,22 +37,82 @@
#define USB2_BASE USB_OTG2_BASE
#endif
+// RT1040 calls its only USB USB_OTG (no 1)
+#if defined(MIMXRT1042_SERIES)
+#define USB_OTG1_IRQn USB_OTG_IRQn
+#endif
+
static const ci_hs_controller_t _ci_controller[] =
{
// RT1010 and RT1020 only has 1 USB controller
#if FSL_FEATURE_SOC_USBHS_COUNT == 1
- { .reg_base = USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 }
+ { .reg_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
#else
- { .reg_base = USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 },
- { .reg_base = USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 }
+ { .reg_base = USB1_BASE, .irqnum = USB_OTG1_IRQn},
+ { .reg_base = USB2_BASE, .irqnum = USB_OTG2_IRQn}
#endif
};
-#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
-#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
+#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
+
+//------------- DCD -------------//
+#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
+#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
+
+//------------- HCD -------------//
+#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum)
+#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum)
+
+//------------- DCache -------------//
+#if CFG_TUD_MEM_DCACHE_ENABLE || CFG_TUH_MEM_DCACHE_ENABLE
+#if __CORTEX_M == 7
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) {
+ size = (size & ~(CFG_TUD_MEM_DCACHE_LINE_SIZE-1)) + CFG_TUD_MEM_DCACHE_LINE_SIZE;
+ }
+ return size;
+}
-#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
-#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
+TU_ATTR_ALWAYS_INLINE static inline bool imxrt_is_cache_mem(uintptr_t addr) {
+ return !(0x20000000 <= addr && addr < 0x20100000);
+}
+TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (imxrt_is_cache_mem(addr32)) {
+ TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (imxrt_is_cache_mem(addr32)) {
+ // Invalidating does not push cached changes back to RAM so we need to be
+ // *very* careful when we do it. If we're not aligned, then we risk resetting
+ // values back to their RAM state.
+ TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ const uintptr_t addr32 = (uintptr_t) addr;
+ if (imxrt_is_cache_mem(addr32)) {
+ TU_ASSERT(tu_is_aligned32(addr32));
+ data_size = round_up_to_cache_line_size(data_size);
+ SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size);
+ }
+ return true;
+}
+
+#elif __CORTEX_M == 4
+#error "Secondary M4 core's cache controller is not supported yet."
+#endif
+#endif
#endif
diff --git a/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h b/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
index 8c2e7dfa6..178eec419 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
@@ -27,15 +27,26 @@
#ifndef _CI_HS_LPC18_43_H_
#define _CI_HS_LPC18_43_H_
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
// LPCOpen for 18xx & 43xx
#include "chip.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
static const ci_hs_controller_t _ci_controller[] =
{
- { .reg_base = LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
- { .reg_base = LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
+ { .reg_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
+ { .reg_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
};
+#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
+
#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
diff --git a/src/common/tusb_timeout.h b/src/portable/chipidea/ci_hs/ci_hs_mcx.h
index ce53955f0..f940f4a9d 100644
--- a/src/common/tusb_timeout.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_mcx.h
@@ -1,7 +1,7 @@
-/*
+/*
* The MIT License (MIT)
*
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,57 +24,29 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup Group_Common Common Files
- * \defgroup Group_TimeoutTimer timeout timer
- * @{ */
+#ifndef _CI_HS_MCX_H_
+#define _CI_HS_MCX_H_
-#ifndef _TUSB_TIMEOUT_H_
-#define _TUSB_TIMEOUT_H_
+#include "fsl_device_registers.h"
-#include <stdbool.h>
-#include <stdint.h>
+// NOTE: MCX N9 has 2 different USB Controller
+// - USB0 is KHCI FullSpeed
+// - USB1 is ChipIdea HighSpeed, therefore rhport = 1 is actually index 0
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
- uint32_t start;
- uint32_t interval;
-}tu_timeout_t;
-
-#if 0
+static const ci_hs_controller_t _ci_controller[] = {
+ {.reg_base = USBHS1__USBC_BASE, .irqnum = USB1_HS_IRQn}
+};
-extern uint32_t tusb_hal_millis(void);
-
-static inline void tu_timeout_set(tu_timeout_t* tt, uint32_t msec)
-{
- tt->interval = msec;
- tt->start = tusb_hal_millis();
+TU_ATTR_ALWAYS_INLINE static inline ci_hs_regs_t* CI_HS_REG(uint8_t port) {
+ (void) port;
+ return ((ci_hs_regs_t*) _ci_controller[0].reg_base);
}
-static inline bool tu_timeout_expired(tu_timeout_t* tt)
-{
- return ( tusb_hal_millis() - tt->start ) >= tt->interval;
-}
+#define CI_DCD_INT_ENABLE(_p) do { (void) _p; NVIC_EnableIRQ (_ci_controller[0].irqnum); } while (0)
+#define CI_DCD_INT_DISABLE(_p) do { (void) _p; NVIC_DisableIRQ(_ci_controller[0].irqnum); } while (0)
-// For used with periodic event to prevent drift
-static inline void tu_timeout_reset(tu_timeout_t* tt)
-{
- tt->start += tt->interval;
-}
+#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum)
+#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum)
-static inline void tu_timeout_restart(tu_timeout_t* tt)
-{
- tt->start = tusb_hal_millis();
-}
-
-#endif
-#ifdef __cplusplus
- }
#endif
-
-#endif /* _TUSB_TIMEOUT_H_ */
-
-/** @} */
diff --git a/src/portable/chipidea/ci_hs/ci_hs_type.h b/src/portable/chipidea/ci_hs/ci_hs_type.h
index 728a86b86..2f3aa3694 100644
--- a/src/portable/chipidea/ci_hs/ci_hs_type.h
+++ b/src/portable/chipidea/ci_hs/ci_hs_type.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021, Ha Thach (tinyusb.org)
@@ -31,13 +31,21 @@
extern "C" {
#endif
+// DCCPARAMS
+enum {
+ DCCPARAMS_DEN_MASK = 0x1Fu, ///< DEN bit 4:0
+};
+
// USBCMD
enum {
USBCMD_RUN_STOP = TU_BIT(0),
USBCMD_RESET = TU_BIT(1),
USBCMD_SETUP_TRIPWIRE = TU_BIT(13),
- USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
-// Interrupt Threshold bit 23:16
+ USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14), // This bit is used as a semaphore to ensure the to proper addition of a
+ // new dTD to an active (primed) endpoint’s linked list. This bit is set and
+ // cleared by software during the process of adding a new dTD
+
+ USBCMD_INTR_THRESHOLD_MASK = 0x00FF0000u, // Interrupt Threshold bit 23:16
};
// PORTSC1
@@ -72,6 +80,7 @@ enum {
// USBMode
enum {
+ USBMOD_CM_MASK = TU_BIT(0) | TU_BIT(1),
USBMODE_CM_DEVICE = 2,
USBMODE_CM_HOST = 3,
@@ -134,7 +143,6 @@ typedef struct
{
uint32_t reg_base;
uint32_t irqnum;
- uint8_t ep_count; // Max bi-directional Endpoints
}ci_hs_controller_t;
#ifdef __cplusplus
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index c7cc3e0e8..a716dc24c 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -28,16 +28,33 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_HS)
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
#include "device/dcd.h"
#include "ci_hs_type.h"
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
#include "ci_hs_imxrt.h"
+
+#if CFG_TUD_MEM_DCACHE_ENABLE
+bool dcd_dcache_clean(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean(addr, data_size);
+}
+
+bool dcd_dcache_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_invalidate(addr, data_size);
+}
+
+bool dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
#include "ci_hs_lpc18_43.h"
+
+#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
+ // MCX N9 only port 1 use this controller
+ #include "ci_hs_mcx.h"
+
#else
#error "Unsupported MCUs"
#endif
@@ -46,15 +63,6 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
-
-#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1
- #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr
-#else
- #define CleanInvalidateDCache_by_Addr(_addr, _dsize)
-#endif
-
-
// ENDPTCTRL
enum {
ENDPTCTRL_STALL = TU_BIT(0),
@@ -155,10 +163,20 @@ typedef struct {
dcd_qtd_t qtd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
}dcd_data_t;
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(2048)
static dcd_data_t _dcd_data;
//--------------------------------------------------------------------+
+// Prototypes and Helper Functions
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE
+static inline uint8_t ci_ep_count(ci_hs_regs_t const* dcd_reg)
+{
+ return dcd_reg->DCCPARAMS & DCCPARAMS_DEN_MASK;
+}
+
+//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
@@ -172,7 +190,8 @@ static void bus_reset(uint8_t rhport)
// endpoint type of the unused direction must be changed from the control type to any other
// type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
// for the data PID tracking on the active endpoint.
- for( uint8_t i=1; i < _ci_controller[rhport].ep_count; i++)
+ uint8_t const ep_count = ci_ep_count(dcd_reg);
+ for( uint8_t i=1; i < ep_count; i++)
{
dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
}
@@ -199,34 +218,46 @@ static void bus_reset(uint8_t rhport)
_dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
_dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
+
+ dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
tu_memclr(&_dcd_data, sizeof(dcd_data_t));
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ TU_ASSERT(ci_ep_count(dcd_reg) <= TUP_DCD_ENDPOINT_MAX);
+
// Reset controller
dcd_reg->USBCMD |= USBCMD_RESET;
while( dcd_reg->USBCMD & USBCMD_RESET ) {}
// Set mode to device, must be set immediately after reset
- dcd_reg->USBMODE = USBMODE_CM_DEVICE;
+ uint32_t usbmode = dcd_reg->USBMODE & ~USBMOD_CM_MASK;
+ usbmode |= USBMODE_CM_DEVICE;
+ dcd_reg->USBMODE = usbmode;
+
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
#if !TUD_OPT_HIGH_SPEED
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
#endif
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+ dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
dcd_reg->USBSTS = dcd_reg->USBSTS;
dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
- dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
- dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
+ uint32_t usbcmd = dcd_reg->USBCMD;
+ usbcmd &= ~USBCMD_INTR_THRESHOLD_MASK; // Interrupt Threshold Interval = 0
+ usbcmd |= USBCMD_RUN_STOP; // run
+
+ dcd_reg->USBCMD = usbcmd;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -268,10 +299,12 @@ void dcd_disconnect(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
{
- (void) rhport;
- (void) en;
-
- // TODO implement later
+ ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+ if (en) {
+ dcd_reg->USBINTR |= INTR_SOF;
+ } else {
+ dcd_reg->USBINTR &= ~INTR_SOF;
+ }
}
//--------------------------------------------------------------------+
@@ -280,9 +313,7 @@ void dcd_sof_enable(uint8_t rhport, bool en)
static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
{
- // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
- // address to 32-byte boundaries. Buffer must be word aligned
- CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
+ dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes);
tu_memclr(p_qtd, sizeof(dcd_qtd_t));
@@ -339,8 +370,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
+ ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+
// Must not exceed max endpoint number
- TU_ASSERT( epnum < _ci_controller[rhport].ep_count );
+ TU_ASSERT(epnum < ci_ep_count(dcd_reg));
//------------- Prepare Queue Head -------------//
dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
@@ -355,11 +388,9 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+ dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
// Enable EP Control
- ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
-
uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
if ( dir == TUSB_DIR_OUT )
@@ -378,7 +409,8 @@ void dcd_edpt_close_all (uint8_t rhport)
ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
// Disable all non-control endpoints
- for( uint8_t epnum=1; epnum < _ci_controller[rhport].ep_count; epnum++)
+ uint8_t const ep_count = ci_ep_count(dcd_reg);
+ for (uint8_t epnum = 1; epnum < ep_count; epnum++)
{
_dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
_dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
@@ -416,7 +448,7 @@ static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
// flush cache
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+ dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
if ( epnum == 0 )
{
@@ -447,7 +479,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
return true;
}
+#if !CFG_TUD_MEM_DCACHE_ENABLE
// fifo has to be aligned to 4k boundary
+// It's incompatible with dcache enabled transfer, since neither address nor size is aligned to cache line
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
@@ -493,8 +527,6 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
page++;
}
}
-
- CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
}
else
{
@@ -509,6 +541,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
return true;
}
+#endif
//--------------------------------------------------------------------+
// ISR
@@ -541,7 +574,7 @@ static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir
tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
}
}
-
+
// only number of bytes in the IOC qtd
dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
}
@@ -607,20 +640,11 @@ void dcd_int_handler(uint8_t rhport)
if (int_status & INTR_USB)
{
// Make sure we read the latest version of _dcd_data.
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
+ dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t));
uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
- if (dcd_reg->ENDPTSETUPSTAT)
- {
- //------------- Set up Received -------------//
- // 23.10.10.2 Operational model for setup transfers
- dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
-
- dcd_event_setup_received(rhport, (uint8_t*)(uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
- }
-
// 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
// nothing to do, we will submit xfer as error to usbd
// if (int_status & INTR_ERROR) { }
@@ -633,11 +657,21 @@ void dcd_int_handler(uint8_t rhport)
if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
}
}
+
+ // Set up Received
+ // 23.10.10.2 Operational model for setup transfers
+ // Must be after normal transfer complete since it is possible to have both previous control status + new setup
+ // in the same frame and we should handle previous status first.
+ if (dcd_reg->ENDPTSETUPSTAT) {
+ dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
+ dcd_event_setup_received(rhport, (uint8_t *) (uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
+ }
}
if (int_status & INTR_SOF)
{
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ const uint32_t frame = dcd_reg->FRINDEX;
+ dcd_event_sof(rhport, frame, true);
}
}
diff --git a/src/portable/chipidea/ci_hs/hcd_ci_hs.c b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
index d0396daea..c4c342a70 100644
--- a/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/hcd_ci_hs.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -28,8 +28,7 @@
// Chipidea Highspeed USB IP implement EHCI for host functionality
-#if CFG_TUH_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_EHCI)
//--------------------------------------------------------------------+
// INCLUDE
@@ -39,55 +38,74 @@
#include "portable/ehci/ehci_api.h"
#include "ci_hs_type.h"
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
- #include "ci_hs_imxrt.h"
+#if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX
+
+#include "ci_hs_imxrt.h"
+
+#if CFG_TUH_MEM_DCACHE_ENABLE
+bool hcd_dcache_clean(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean(addr, data_size);
+}
+
+bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_invalidate(addr, data_size);
+}
+
+bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) {
+ return imxrt_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
- #include "ci_hs_lpc18_43.h"
+
+#include "ci_hs_lpc18_43.h"
+
#else
- #error "Unsupported MCUs"
+#error "Unsupported MCUs"
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-#define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base)
-
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport)
-{
- ci_hs_regs_t* hcd_reg = CI_HS_REG(rhport);
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ ci_hs_regs_t *hcd_reg = CI_HS_REG(rhport);
// Reset controller
hcd_reg->USBCMD |= USBCMD_RESET;
- while( hcd_reg->USBCMD & USBCMD_RESET ) {}
+ while ( hcd_reg->USBCMD & USBCMD_RESET ) {}
// Set mode to device, must be set immediately after reset
#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
// LPC18XX/43XX need to set VBUS Power Select to HIGH
// RHPORT1 is fullspeed only (need external PHY for Highspeed)
hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
- if (rhport == 1) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ if (rhport == 1) {
+ hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
+ }
#else
hcd_reg->USBMODE = USBMODE_CM_HOST;
#endif
// FIXME force full speed, still have issue with Highspeed enumeration
+ // probably due to physical connection bouncing when plug/unplug
+ // 1. Have issue when plug/unplug devices, maybe the port is not reset properly
+ // 2. Also does not seems to detect disconnection
hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
}
-void hcd_int_enable(uint8_t rhport)
-{
+void hcd_int_enable(uint8_t rhport) {
CI_HCD_INT_ENABLE(rhport);
}
-void hcd_int_disable(uint8_t rhport)
-{
+void hcd_int_disable(uint8_t rhport) {
CI_HCD_INT_DISABLE(rhport);
}
diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c
index 0bb8b6e41..56ecb7575 100644
--- a/src/portable/dialog/da146xx/dcd_da146xx.c
+++ b/src/portable/dialog/da146xx/dcd_da146xx.c
@@ -243,7 +243,7 @@ static struct
// Converts xfer pointer to epnum (0,1,2,3) regardless of xfer direction
#define XFER_EPNUM(xfer) ((xfer - &_dcd.xfer_status[0][0]) >> 1)
-// Converts xfer pinter to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number)
+// Converts xfer pointer to EPx_REGS pointer (returns same pointer for IN and OUT with same endpoint number)
#define XFER_REGS(xfer) ep_regs[XFER_EPNUM(xfer)]
// Converts epnum (0,1,2,3) to EPx_REGS pointer
#define EPNUM_REGS(epnum) ep_regs[epnum]
@@ -651,7 +651,7 @@ static void handle_epx_tx_ev(xfer_ctl_t *xfer)
}
if (txs & USB_USB_TXS1_REG_USB_TX_URUN_Msk)
{
- TU_LOG1("EP %d FIFO underrun\n", epnum);
+ TU_LOG1("EP %d FIFO underrun\r\n", epnum);
}
// Start next or repeated packet.
start_tx_packet(xfer);
@@ -804,15 +804,16 @@ static void handle_ep0_nak(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
_dcd.init_called = true;
- if (_dcd.vbus_present)
- {
+ if (_dcd.vbus_present) {
dcd_connect(rhport);
}
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -895,6 +896,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
+void tusb_vbus_changed(bool present);
void tusb_vbus_changed(bool present)
{
if (present && !_dcd.vbus_present)
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index 76ba2a921..01bbf62bf 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -48,8 +48,8 @@
#ifdef TUP_USBIP_CHIPIDEA_HS
// NXP Transdimension: 8 elements
#define FRAMELIST_SIZE_BIT_VALUE 7u
- #define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_POS_FRAMELIST_SIZE) | \
- ((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB))
+ #define FRAMELIST_SIZE_USBCMD_VALUE (((FRAMELIST_SIZE_BIT_VALUE & 3) << EHCI_USBCMD_FRAMELIST_SIZE_SHIFT) | \
+ ((FRAMELIST_SIZE_BIT_VALUE >> 2) << EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT))
#else
// STD EHCI: 256 elements
#define FRAMELIST_SIZE_BIT_VALUE 2u
@@ -58,7 +58,8 @@
#define FRAMELIST_SIZE (1024 >> FRAMELIST_SIZE_BIT_VALUE)
-#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
+// Total queue head pool. TODO should be user configurable and more optimize memory usage in the future
+#define QHD_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB)
#define QTD_MAX QHD_MAX
typedef struct
@@ -79,62 +80,97 @@ typedef struct
ehci_qhd_t qhd_pool[QHD_MAX];
ehci_qtd_t qtd_pool[QTD_MAX] TU_ATTR_ALIGNED(32);
- ehci_registers_t* regs;
+ ehci_registers_t* regs; // operational register
+ ehci_cap_registers_t* cap_regs; // capability register
volatile uint32_t uframe_number;
}ehci_data_t;
// Periodic frame list must be 4K alignment
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
+CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
//--------------------------------------------------------------------+
-// PROTOTYPE
+// Debug
//--------------------------------------------------------------------+
-static inline ehci_link_t* get_period_head(uint8_t rhport, uint32_t interval_ms)
-{
- (void) rhport;
- return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
-}
+#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
+static inline void print_portsc(ehci_registers_t* regs) {
+ TU_LOG_HEX(EHCI_DBG, regs->portsc);
+ TU_LOG(EHCI_DBG, " Connect Status : %u\r\n", regs->portsc_bm.current_connect_status);
+ TU_LOG(EHCI_DBG, " Connect Change : %u\r\n", regs->portsc_bm.connect_status_change);
+ TU_LOG(EHCI_DBG, " Enabled : %u\r\n", regs->portsc_bm.port_enabled);
+ TU_LOG(EHCI_DBG, " Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
-static inline ehci_qhd_t* qhd_control(uint8_t dev_addr)
-{
- return &ehci_data.control[dev_addr].qhd;
+ TU_LOG(EHCI_DBG, " OverCurr Change: %u\r\n", regs->portsc_bm.over_current_change);
+ TU_LOG(EHCI_DBG, " Force Resume : %u\r\n", regs->portsc_bm.force_port_resume);
+ TU_LOG(EHCI_DBG, " Suspend : %u\r\n", regs->portsc_bm.suspend);
+ TU_LOG(EHCI_DBG, " Reset : %u\r\n", regs->portsc_bm.port_reset);
+ TU_LOG(EHCI_DBG, " Power : %u\r\n", regs->portsc_bm.port_power);
}
-static inline ehci_qhd_t* qhd_async_head(uint8_t rhport)
-{
- (void) rhport;
- // control qhd of dev0 is used as async head
- return qhd_control(0);
+static inline void print_intr(uint32_t intr) {
+ TU_LOG_HEX(EHCI_DBG, intr);
+ TU_LOG(EHCI_DBG, " USB Interrupt : %u\r\n", (intr & EHCI_INT_MASK_USB) ? 1 : 0);
+ TU_LOG(EHCI_DBG, " USB Error : %u\r\n", (intr & EHCI_INT_MASK_ERROR) ? 1 : 0);
+ TU_LOG(EHCI_DBG, " Port Change Detect : %u\r\n", (intr & EHCI_INT_MASK_PORT_CHANGE) ? 1 : 0);
+ TU_LOG(EHCI_DBG, " Frame List Rollover: %u\r\n", (intr & EHCI_INT_MASK_FRAMELIST_ROLLOVER) ? 1 : 0);
+ TU_LOG(EHCI_DBG, " Host System Error : %u\r\n", (intr & EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR) ? 1 : 0);
+ TU_LOG(EHCI_DBG, " Async Advance : %u\r\n", (intr & EHCI_INT_MASK_ASYNC_ADVANCE) ? 1 : 0);
+// TU_LOG(EHCI_DBG, " Interrupt on Async: %u\r\n", (intr & EHCI_INT_MASK_NXP_ASYNC));
+// TU_LOG(EHCI_DBG, " Periodic Schedule : %u\r\n", (intr & EHCI_INT_MASK_NXP_PERIODIC));
}
-static inline ehci_qtd_t* qtd_control(uint8_t dev_addr)
-{
- return &ehci_data.control[dev_addr].qtd;
-}
+#else
+#define print_portsc(_reg)
+#endif
+//--------------------------------------------------------------------+
+// PROTOTYPE
+//--------------------------------------------------------------------+
-static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
-static inline ehci_qhd_t* qhd_find_free (void);
-static inline ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
-
-// determine if a queue head has bus-related error
-static inline bool qhd_has_xact_error (ehci_qhd_t * p_qhd)
-{
- return (p_qhd->qtd_overlay.buffer_err || p_qhd->qtd_overlay.babble_err || p_qhd->qtd_overlay.xact_err);
- //p_qhd->qtd_overlay.non_hs_period_missed_uframe || p_qhd->qtd_overlay.pingstate_err TODO split transaction error
-}
+// weak dcache for non-cacheable MCU
+TU_ATTR_WEAK bool hcd_dcache_clean(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
+TU_ATTR_WEAK bool hcd_dcache_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
+TU_ATTR_WEAK bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { (void) addr; (void) data_size; return true; }
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr);
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_next (ehci_qhd_t const * p_qhd);
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_find_free (void);
+static ehci_qhd_t* qhd_get_from_addr (uint8_t dev_addr, uint8_t ep_addr);
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd);
+static void qhd_remove_qtd(ehci_qhd_t *qhd);
+
+TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr);
+TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_find_free (void);
+static void qtd_init (ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes);
-static inline ehci_qtd_t* qtd_find_free (void);
-static inline ehci_qtd_t* qtd_next (ehci_qtd_t const * p_qtd);
-static inline void qtd_insert_to_qhd (ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new);
-static inline void qtd_remove_1st_from_qhd (ehci_qhd_t *p_qhd);
-static void qtd_init (ehci_qtd_t* p_qtd, void const* buffer, uint16_t total_bytes);
+TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms);
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport);
+TU_ATTR_ALWAYS_INLINE static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
+TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next (ehci_link_t const *p_link);
+static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr);
+
+static void ehci_disable_schedule(ehci_registers_t* regs, bool is_period) {
+ // maybe have a timeout for status
+ if (is_period) {
+ regs->command_bm.periodic_enable = 0;
+ while(regs->status_bm.periodic_status) {}
+ } else {
+ regs->command_bm.async_enable = 0;
+ while(regs->status_bm.async_status) {} // should have a timeout
+ }
+}
-static inline void list_insert (ehci_link_t *current, ehci_link_t *new, uint8_t new_type);
-static inline ehci_link_t* list_next (ehci_link_t *p_link_pointer);
+static void ehci_enable_schedule(ehci_registers_t* regs, bool is_period) {
+ // maybe have a timeout for status
+ if (is_period) {
+ regs->command_bm.periodic_enable = 1;
+ while ( 0 == regs->status_bm.periodic_status ) {}
+ } else {
+ regs->command_bm.async_enable = 1;
+ while( 0 == regs->status_bm.async_status ) {}
+ }
+}
//--------------------------------------------------------------------+
// HCD API
@@ -152,11 +188,16 @@ void hcd_port_reset(uint8_t rhport)
ehci_registers_t* regs = ehci_data.regs;
-// regs->portsc_bm.port_enabled = 0; // disable port before reset
-// regs->portsc_bm.port_reset = 1;
+ // skip if already in reset
+ if (regs->portsc_bm.port_reset) {
+ return;
+ }
- uint32_t portsc = regs->portsc;
+ // mask out Write-1-to-Clear bits
+ uint32_t portsc = regs->portsc & ~EHCI_PORTSC_MASK_W1C;
+ // EHCI Table 2-16 PortSC
+ // when software writes Port Reset bit to a one, it must also write a zero to the Port Enable bit.
portsc &= ~(EHCI_PORTSC_MASK_PORT_EANBLED);
portsc |= EHCI_PORTSC_MASK_PORT_RESET;
@@ -166,11 +207,18 @@ void hcd_port_reset(uint8_t rhport)
void hcd_port_reset_end(uint8_t rhport)
{
(void) rhport;
-
-#if 0
ehci_registers_t* regs = ehci_data.regs;
- regs->portsc_bm.port_reset = 0;
-#endif
+
+ // skip if reset is already complete
+ if (!regs->portsc_bm.port_reset) {
+ return;
+ }
+
+ // 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;
}
bool hcd_port_connect_status(uint8_t rhport)
@@ -185,138 +233,126 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
return (tusb_speed_t) ehci_data.regs->portsc_bm.nxp_port_speed; // NXP specific port speed
}
-static void list_remove_qhd_by_addr(ehci_link_t* list_head, uint8_t dev_addr)
-{
- for(ehci_link_t* prev = list_head;
- !prev->terminate && (tu_align32(prev->address) != (uint32_t) list_head);
- prev = list_next(prev) )
- {
- // TODO check type for ISO iTD and siTD
- // TODO Suppress cast-align warning
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wcast-align"
- ehci_qhd_t* qhd = (ehci_qhd_t*) list_next(prev);
- #pragma GCC diagnostic pop
- if ( qhd->dev_addr == dev_addr )
- {
- // TODO deactive all TD, wait for QHD to inactive before removal
- prev->address = qhd->next.address;
-
- // EHCI 4.8.2 link the removed qhd to async head (which always reachable by Host Controller)
- qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
-
- if ( qhd->int_smask )
- {
- // period list queue element is guarantee to be free in the next frame (1 ms)
- qhd->used = 0;
- }else
- {
- // async list use async advance handshake
- // mark as removing, will completely re-usable when async advance isr occurs
- qhd->removing = 1;
- }
- }
- }
-}
-
// Close all opened endpoint belong to this device
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+void hcd_device_close(uint8_t rhport, uint8_t daddr)
{
// skip dev0
- if (dev_addr == 0) return;
+ if (daddr == 0) {
+ return;
+ }
// Remove from async list
- list_remove_qhd_by_addr( (ehci_link_t*) qhd_async_head(rhport), dev_addr );
+ list_remove_qhd_by_daddr((ehci_link_t *) list_get_async_head(rhport), daddr);
// Remove from all interval period list
- for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++)
- {
- list_remove_qhd_by_addr( (ehci_link_t*) &ehci_data.period_head_arr[i], dev_addr);
+ for(uint8_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++) {
+ list_remove_qhd_by_daddr((ehci_link_t *) &ehci_data.period_head_arr[i], daddr);
}
// Async doorbell (EHCI 4.8.2 for operational details)
ehci_data.regs->command_bm.async_adv_doorbell = 1;
}
-bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
-{
- (void) capability_reg; // not used yet
-
- tu_memclr(&ehci_data, sizeof(ehci_data_t));
-
- ehci_data.regs = (ehci_registers_t* ) operatial_reg;
-
- ehci_registers_t* regs = ehci_data.regs;
-
- //------------- CTRLDSSEGMENT Register (skip) -------------//
- //------------- USB INT Register -------------//
- regs->inten = 0; // 1. disable all the interrupt
- regs->status = EHCI_INT_MASK_ALL; // 2. clear all status
-
- regs->inten = EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE | EHCI_INT_MASK_ASYNC_ADVANCE |
- EHCI_INT_MASK_NXP_PERIODIC | EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_FRAMELIST_ROLLOVER;
-
- //------------- Asynchronous List -------------//
- ehci_qhd_t * const async_head = qhd_async_head(rhport);
- tu_memclr(async_head, sizeof(ehci_qhd_t));
-
- async_head->next.address = (uint32_t) async_head; // circular list, next is itself
- async_head->next.type = EHCI_QTYPE_QHD;
- async_head->head_list_flag = 1;
- async_head->qtd_overlay.halted = 1; // inactive most of time
- async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified
-
- regs->async_list_addr = (uint32_t) async_head;
+static void init_periodic_list(uint8_t rhport) {
+ (void) rhport;
- //------------- Periodic List -------------//
// Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
- for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ )
- {
+ for ( uint32_t i = 0; i < TU_ARRAY_SIZE(ehci_data.period_head_arr); i++ ) {
ehci_data.period_head_arr[i].int_smask = 1; // queue head in period list must have smask non-zero
ehci_data.period_head_arr[i].qtd_overlay.halted = 1; // dummy node, always inactive
}
- ehci_link_t * const framelist = ehci_data.period_framelist;
- ehci_link_t * const period_1ms = get_period_head(rhport, 1u);
-
+ // TODO EHCI_FRAMELIST_SIZE with other size than 8
// all links --> period_head_arr[0] (1ms)
// 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
// 1, 5 --> period_head_arr[2] (4ms)
// 3 --> period_head_arr[3] (8ms)
- // TODO EHCI_FRAMELIST_SIZE with other size than 8
- for(uint32_t i=0; i<FRAMELIST_SIZE; i++)
- {
- framelist[i].address = (uint32_t) period_1ms;
- framelist[i].type = EHCI_QTYPE_QHD;
+ ehci_link_t * const framelist = ehci_data.period_framelist;
+ ehci_link_t * const head_1ms = (ehci_link_t *) &ehci_data.period_head_arr[0];
+ ehci_link_t * const head_2ms = (ehci_link_t *) &ehci_data.period_head_arr[1];
+ ehci_link_t * const head_4ms = (ehci_link_t *) &ehci_data.period_head_arr[2];
+ ehci_link_t * const head_8ms = (ehci_link_t *) &ehci_data.period_head_arr[3];
+
+ for (uint32_t i = 0; i < FRAMELIST_SIZE; i++) {
+ framelist[i].address = (uint32_t) head_1ms;
+ framelist[i].type = EHCI_QTYPE_QHD;
}
- for(uint32_t i=0; i<FRAMELIST_SIZE; i+=2)
- {
- list_insert(framelist + i, get_period_head(rhport, 2u), EHCI_QTYPE_QHD);
+ for (uint32_t i = 0; i < FRAMELIST_SIZE; i += 2) {
+ list_insert(framelist + i, head_2ms, EHCI_QTYPE_QHD);
}
- for(uint32_t i=1; i<FRAMELIST_SIZE; i+=4)
- {
- list_insert(framelist + i, get_period_head(rhport, 4u), EHCI_QTYPE_QHD);
+ for (uint32_t i = 1; i < FRAMELIST_SIZE; i += 4) {
+ list_insert(framelist + i, head_4ms, EHCI_QTYPE_QHD);
}
- list_insert(framelist+3, get_period_head(rhport, 8u), EHCI_QTYPE_QHD);
+ list_insert(framelist + 3, head_8ms, EHCI_QTYPE_QHD);
+
+ head_1ms->terminate = 1;
+}
+
+bool ehci_init(uint8_t rhport, uint32_t capability_reg, uint32_t operatial_reg)
+{
+ tu_memclr(&ehci_data, sizeof(ehci_data_t));
+
+ ehci_data.regs = (ehci_registers_t*) operatial_reg;
+ ehci_data.cap_regs = (ehci_cap_registers_t*) capability_reg;
- period_1ms->terminate = 1;
+ ehci_registers_t* regs = ehci_data.regs;
- regs->periodic_list_base = (uint32_t) framelist;
+ // EHCI 4.1 Host Controller Initialization
+
+ //------------- CTRLDSSEGMENT Register (skip) -------------//
+
+ //------------- USB INT Register -------------//
+
+ // disable all the interrupt
+ regs->inten = 0;
+
+ // clear all status except port change since device maybe connected before this driver is initialized
+ regs->status = (EHCI_INT_MASK_ALL & ~EHCI_INT_MASK_PORT_CHANGE);
+
+ // Enable interrupts
+ regs->inten = EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE |
+ EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_FRAMELIST_ROLLOVER;
+
+ //------------- Asynchronous List -------------//
+ ehci_qhd_t * const async_head = list_get_async_head(rhport);
+ tu_memclr(async_head, sizeof(ehci_qhd_t));
+
+ async_head->next.address = (uint32_t) async_head; // circular list, next is itself
+ async_head->next.type = EHCI_QTYPE_QHD;
+ async_head->head_list_flag = 1;
+ async_head->qtd_overlay.halted = 1; // inactive most of time
+ async_head->qtd_overlay.next.terminate = 1; // TODO removed if verified
+
+ regs->async_list_addr = (uint32_t) async_head;
+
+ //------------- Periodic List -------------//
+ init_periodic_list(rhport);
+ regs->periodic_list_base = (uint32_t) ehci_data.period_framelist;
+
+ hcd_dcache_clean(&ehci_data, sizeof(ehci_data_t));
//------------- TT Control (NXP only) -------------//
regs->nxp_tt_control = 0;
//------------- USB CMD Register -------------//
- regs->command |= TU_BIT(EHCI_USBCMD_POS_RUN_STOP) | TU_BIT(EHCI_USBCMD_POS_ASYNC_ENABLE) |
- TU_BIT(EHCI_USBCMD_POS_PERIOD_ENABLE) | // TODO enable period list only there is int/iso endpoint
+ regs->command |= EHCI_USBCMD_RUN_STOP | EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE | EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE |
FRAMELIST_SIZE_USBCMD_VALUE;
//------------- ConfigFlag Register (skip) -------------//
- regs->portsc_bm.port_power = 1; // enable port power
+
+ // enable port power bit in portsc. The function of this bit depends on the value of the Port
+ // Power Control (PPC) field in the HCSPARAMS register.
+ if (ehci_data.cap_regs->hcsparams_bm.port_power_control) {
+ // 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_POWER;
+
+ regs->portsc = portsc;
+ }
return true;
}
@@ -347,15 +383,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
TU_ASSERT (ep_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
//------------- Prepare Queue Head -------------//
- ehci_qhd_t * p_qhd;
-
- if ( ep_desc->bEndpointAddress == 0 )
- {
- p_qhd = qhd_control(dev_addr);
- }else
- {
- p_qhd = qhd_find_free();
- }
+ ehci_qhd_t *p_qhd = (ep_desc->bEndpointAddress == 0) ? qhd_control(dev_addr) : qhd_find_free();
TU_ASSERT(p_qhd);
qhd_init(p_qhd, dev_addr, ep_desc);
@@ -370,11 +398,11 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
{
case TUSB_XFER_CONTROL:
case TUSB_XFER_BULK:
- list_head = (ehci_link_t*) qhd_async_head(rhport);
+ list_head = (ehci_link_t*) list_get_async_head(rhport);
break;
case TUSB_XFER_INTERRUPT:
- list_head = get_period_head(rhport, p_qhd->interval_ms);
+ list_head = list_get_period_head(rhport, p_qhd->interval_ms);
break;
case TUSB_XFER_ISOCHRONOUS:
@@ -383,12 +411,13 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
default: break;
}
-
TU_ASSERT(list_head);
- // TODO might need to disable async/period list
list_insert(list_head, (ehci_link_t*) p_qhd, EHCI_QTYPE_QHD);
+ hcd_dcache_clean(p_qhd, sizeof(ehci_qhd_t));
+ hcd_dcache_clean(list_head, sizeof(ehci_qhd_t));
+
return true;
}
@@ -400,16 +429,17 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
ehci_qtd_t* td = &ehci_data.control[dev_addr].qtd;
qtd_init(td, setup_packet, 8);
- td->pid = EHCI_PID_SETUP;
- td->int_on_complete = 1;
- td->next.terminate = 1;
+ td->pid = EHCI_PID_SETUP;
+
+ hcd_dcache_clean(setup_packet, 8);
- // sw region
- qhd->p_qtd_list_head = td;
- qhd->p_qtd_list_tail = td;
+ // Control endpoint never be stalled. Skip reset Data Toggle since it is fixed per stage
+ if (qhd->qtd_overlay.halted) {
+ qhd->qtd_overlay.halted = false;
+ }
- // attach TD
- qhd->qtd_overlay.next.address = (uint32_t) td;
+ // attach TD to QHD -> start transferring
+ qhd_attach_qtd(qhd, td);
return true;
}
@@ -421,51 +451,84 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
- if ( epnum == 0 )
- {
- ehci_qhd_t* qhd = qhd_control(dev_addr);
- ehci_qtd_t* qtd = qtd_control(dev_addr);
+ ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ ehci_qtd_t* qtd;
+ if (epnum == 0) {
+ // Control endpoint never be stalled. Skip reset Data Toggle since it is fixed per stage
+ if (qhd->qtd_overlay.halted) {
+ qhd->qtd_overlay.halted = false;
+ }
+
+ qtd = qtd_control(dev_addr);
qtd_init(qtd, buffer, buflen);
- // first first data toggle is always 1 (data & setup stage)
+ // first data toggle is always 1 (data & setup stage)
qtd->data_toggle = 1;
qtd->pid = dir ? EHCI_PID_IN : EHCI_PID_OUT;
- qtd->int_on_complete = 1;
- qtd->next.terminate = 1;
+ } else {
+ // skip if endpoint is halted
+ TU_VERIFY(!qhd->qtd_overlay.halted);
- // sw region
- qhd->p_qtd_list_head = qtd;
- qhd->p_qtd_list_tail = qtd;
+ qtd = qtd_find_free();
+ TU_ASSERT(qtd);
- // attach TD
- qhd->qtd_overlay.next.address = (uint32_t) qtd;
- }else
- {
- ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
- ehci_qtd_t *p_qtd = qtd_find_free();
- TU_ASSERT(p_qtd);
+ qtd_init(qtd, buffer, buflen);
+ qtd->pid = qhd->pid;
+ }
+
+ // IN transfer: invalidate buffer, OUT transfer: clean buffer
+ if (dir) {
+ hcd_dcache_invalidate(buffer, buflen);
+ }else {
+ hcd_dcache_clean(buffer, buflen);
+ }
+
+ // attach TD to QHD -> start transferring
+ qhd_attach_qtd(qhd, qtd);
+
+ return true;
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+
+ // TODO ISO not supported yet
+ ehci_qhd_t* qhd = qhd_get_from_addr(dev_addr, ep_addr);
+ ehci_qtd_t * volatile qtd = qhd->attached_qtd;
+ TU_VERIFY(qtd != NULL); // no queued transfer
- qtd_init(p_qtd, buffer, buflen);
- p_qtd->pid = p_qhd->pid;
+ hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t));
+ TU_VERIFY(qtd->active); // transfer is already complete
- // Insert TD to QH
- qtd_insert_to_qhd(p_qhd, p_qtd);
+ // HC is still processing, disable HC list schedule before making changes
+ bool const is_period = (qhd->interval_ms > 0);
- p_qhd->p_qtd_list_tail->int_on_complete = 1;
+ ehci_disable_schedule(ehci_data.regs, is_period);
- // attach head QTD to QHD start transferring
- p_qhd->qtd_overlay.next.address = (uint32_t) p_qhd->p_qtd_list_head;
+ // check active bit again just in case HC has just processed the TD
+ bool const still_active = qtd->active;
+ if (still_active) {
+ // remove TD from QH overlay
+ qhd->qtd_overlay.next.terminate = 1;
+ hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
+
+ // remove TD from QH software list
+ qhd_remove_qtd(qhd);
}
- return true;
+ ehci_enable_schedule(ehci_data.regs, is_period);
+
+ return still_active; // true if removed an active transfer
}
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
- ehci_qhd_t *p_qhd = qhd_get_from_addr(dev_addr, ep_addr);
- p_qhd->qtd_overlay.halted = 0;
- // TODO reset data toggle ?
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+ (void) rhport;
+ ehci_qhd_t *qhd = qhd_get_from_addr(daddr, ep_addr);
+ qhd->qtd_overlay.halted = 0;
+ qhd->qtd_overlay.data_toggle = 0;
+ hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t));
+
return true;
}
@@ -476,343 +539,283 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
// async_advance is handshake between usb stack & ehci controller.
// This isr mean it is safe to modify previously removed queue head from async list.
// In tinyusb, queue head is only removed when device is unplugged.
-static void async_advance_isr(uint8_t rhport)
+TU_ATTR_ALWAYS_INLINE static inline
+void async_advance_isr(uint8_t rhport)
{
(void) rhport;
- ehci_qhd_t* qhd_pool = ehci_data.qhd_pool;
- for(uint32_t i = 0; i < QHD_MAX; i++)
- {
- if ( qhd_pool[i].removing )
- {
+ ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
+ for (uint32_t i = 0; i < QHD_MAX; i++) {
+ if (qhd_pool[i].removing) {
qhd_pool[i].removing = 0;
- qhd_pool[i].used = 0;
+ qhd_pool[i].used = 0;
}
}
}
-static void port_connect_status_change_isr(uint8_t rhport)
-{
+TU_ATTR_ALWAYS_INLINE static inline
+void port_connect_status_change_isr(uint8_t rhport) {
// NOTE There is an sequence plug->unplug->…..-> plug if device is powering with pre-plugged device
- if (ehci_data.regs->portsc_bm.current_connect_status)
- {
+ if ( ehci_data.regs->portsc_bm.current_connect_status ) {
hcd_port_reset(rhport);
hcd_event_device_attach(rhport, true);
- }else // device unplugged
+ } else // device unplugged
{
hcd_event_device_remove(rhport, true);
}
}
-static void qhd_xfer_complete_isr(ehci_qhd_t * p_qhd)
-{
- // free all TDs from the head td to the first active TD
- while(p_qhd->p_qtd_list_head != NULL && !p_qhd->p_qtd_list_head->active)
- {
- ehci_qtd_t * volatile qtd = (ehci_qtd_t * volatile) p_qhd->p_qtd_list_head;
- bool const is_ioc = (qtd->int_on_complete != 0);
- uint8_t const ep_addr = tu_edpt_addr(p_qhd->ep_number, qtd->pid == EHCI_PID_IN ? 1 : 0);
-
- p_qhd->total_xferred_bytes += qtd->expected_bytes - qtd->total_bytes;
-
- // TD need to be freed and removed from qhd, before invoking callback
- qtd->used = 0; // free QTD
- qtd_remove_1st_from_qhd(p_qhd);
+// Check queue head for potential transfer complete (successful or error)
+TU_ATTR_ALWAYS_INLINE static inline
+void qhd_xfer_complete_isr(ehci_qhd_t * qhd) {
+ hcd_dcache_invalidate(qhd, sizeof(ehci_qhd_t)); // HC may have updated the overlay
+ volatile ehci_qtd_t *qtd_overlay = &qhd->qtd_overlay;
- if (is_ioc)
- {
- hcd_event_xfer_complete(p_qhd->dev_addr, ep_addr, p_qhd->total_xferred_bytes, XFER_RESULT_SUCCESS, true);
- p_qhd->total_xferred_bytes = 0;
- }
- }
-}
+ // process non-active (completed) QHD with attached (scheduled) TD
+ if ( !qtd_overlay->active && qhd->attached_qtd != NULL ) {
+ xfer_result_t xfer_result;
-static void async_list_xfer_complete_isr(ehci_qhd_t * const async_head)
-{
- ehci_qhd_t *p_qhd = async_head;
- do
- {
- if ( !p_qhd->qtd_overlay.halted ) // halted or error is processed in error isr
- {
- qhd_xfer_complete_isr(p_qhd);
+ if ( qtd_overlay->halted ) {
+ if (qtd_overlay->xact_err || qtd_overlay->err_count == 0 || qtd_overlay->buffer_err || qtd_overlay->babble_err) {
+ // Error count = 0 often occurs when device disconnected, or other bus-related error
+ // clear halted bit if not caused by STALL to allow more transfer
+ xfer_result = XFER_RESULT_FAILED;
+ qtd_overlay->halted = false;
+ TU_LOG3(" QHD xfer err count: %d\r\n", qtd_overlay->err_count);
+ // TU_BREAKPOINT(); // TODO skip unplugged device
+ }else {
+ // no error bits are set, endpoint is halted due to STALL
+ xfer_result = XFER_RESULT_STALLED;
+ }
+ } else {
+ xfer_result = XFER_RESULT_SUCCESS;
}
- p_qhd = qhd_next(p_qhd);
- }while(p_qhd != async_head); // async list traversal, stop if loop around
-}
-static void period_list_xfer_complete_isr(uint8_t hostid, uint32_t interval_ms)
-{
- uint16_t max_loop = 0;
- uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u);
- ehci_link_t next_item = * get_period_head(hostid, interval_ms);
+ ehci_qtd_t * volatile qtd = qhd->attached_qtd;
+ hcd_dcache_invalidate(qtd, sizeof(ehci_qtd_t)); // HC may have written back TD
- // TODO abstract max loop guard for period
- while( !next_item.terminate &&
- !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) &&
- max_loop < (QHD_MAX + EHCI_MAX_ITD + EHCI_MAX_SITD)*CFG_TUH_DEVICE_MAX)
- {
- switch ( next_item.type )
- {
- case EHCI_QTYPE_QHD:
- {
- ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address);
- if ( !p_qhd_int->qtd_overlay.halted )
- {
- qhd_xfer_complete_isr(p_qhd_int);
- }
- }
- break;
+ uint8_t const dir = (qtd->pid == EHCI_PID_IN) ? 1 : 0;
+ uint32_t const xferred_bytes = qtd->expected_bytes - qtd->total_bytes;
- case EHCI_QTYPE_ITD: // TODO support hs/fs ISO
- case EHCI_QTYPE_SITD:
- case EHCI_QTYPE_FSTN:
-
- default: break;
+ // invalidate dcache if IN transfer with data
+ if (dir == 1 && qhd->attached_buffer != 0 && xferred_bytes > 0) {
+ hcd_dcache_invalidate((void*) qhd->attached_buffer, xferred_bytes);
}
- next_item = *list_next(&next_item);
- max_loop++;
+ // remove and free TD before invoking callback
+ qhd_remove_qtd(qhd);
+
+ // notify usbh
+ uint8_t const ep_addr = tu_edpt_addr(qhd->ep_number, dir);
+ hcd_event_xfer_complete(qhd->dev_addr, ep_addr, xferred_bytes, xfer_result, true);
}
}
-static void qhd_xfer_error_isr(ehci_qhd_t * p_qhd)
+TU_ATTR_ALWAYS_INLINE static inline
+void proccess_async_xfer_isr(ehci_qhd_t * const list_head)
{
- if ( (p_qhd->dev_addr != 0 && p_qhd->qtd_overlay.halted) || // addr0 cannot be protocol STALL
- qhd_has_xact_error(p_qhd) )
- {
- // current qhd has error in transaction
- xfer_result_t error_event;
-
- // no error bits are set, endpoint is halted due to STALL
- error_event = qhd_has_xact_error(p_qhd) ? XFER_RESULT_FAILED : XFER_RESULT_STALLED;
+ ehci_qhd_t *qhd = list_head;
- p_qhd->total_xferred_bytes += p_qhd->p_qtd_list_head->expected_bytes - p_qhd->p_qtd_list_head->total_bytes;
-
-// if ( XFER_RESULT_FAILED == error_event ) TU_BREAKPOINT(); // TODO skip unplugged device
-
- p_qhd->p_qtd_list_head->used = 0; // free QTD
- qtd_remove_1st_from_qhd(p_qhd);
-
- if ( 0 == p_qhd->ep_number )
- {
- // control cannot be halted --> clear all qtd list
- p_qhd->p_qtd_list_head = NULL;
- p_qhd->p_qtd_list_tail = NULL;
-
- p_qhd->qtd_overlay.next.terminate = 1;
- p_qhd->qtd_overlay.alternate.terminate = 1;
- p_qhd->qtd_overlay.halted = 0;
-
- ehci_qtd_t *p_setup = qtd_control(p_qhd->dev_addr);
- p_setup->used = 0;
- }
-
- // call USBH callback
- hcd_event_xfer_complete(p_qhd->dev_addr, tu_edpt_addr(p_qhd->ep_number, p_qhd->pid == EHCI_PID_IN ? 1 : 0), p_qhd->total_xferred_bytes, error_event, true);
-
- p_qhd->total_xferred_bytes = 0;
- }
+ do {
+ qhd_xfer_complete_isr(qhd);
+ qhd = qhd_next(qhd);
+ } while ( qhd != list_head ); // async list traversal, stop if loop around
}
-static void xfer_error_isr(uint8_t hostid)
+TU_ATTR_ALWAYS_INLINE static inline
+void process_period_xfer_isr(uint8_t rhport, uint32_t interval_ms)
{
- //------------- async list -------------//
- ehci_qhd_t * const async_head = qhd_async_head(hostid);
- ehci_qhd_t *p_qhd = async_head;
- do
- {
- qhd_xfer_error_isr( p_qhd );
- p_qhd = qhd_next(p_qhd);
- }while(p_qhd != async_head); // async list traversal, stop if loop around
+ uint32_t const period_1ms_addr = (uint32_t) list_get_period_head(rhport, 1u);
+ ehci_link_t next_link = *list_get_period_head(rhport, interval_ms);
- //------------- TODO refractor period list -------------//
- uint32_t const period_1ms_addr = (uint32_t) get_period_head(hostid, 1u);
- for (uint32_t interval_ms=1; interval_ms <= FRAMELIST_SIZE; interval_ms *= 2)
- {
- ehci_link_t next_item = * get_period_head(hostid, interval_ms);
+ while (!next_link.terminate) {
+ if (interval_ms > 1 && period_1ms_addr == tu_align32(next_link.address)) {
+ // 1ms period list is end of list for all larger interval
+ break;
+ }
- // TODO abstract max loop guard for period
- while( !next_item.terminate &&
- !(interval_ms > 1 && period_1ms_addr == tu_align32(next_item.address)) )
- {
- switch ( next_item.type )
- {
- case EHCI_QTYPE_QHD:
- {
- ehci_qhd_t *p_qhd_int = (ehci_qhd_t *) tu_align32(next_item.address);
- qhd_xfer_error_isr(p_qhd_int);
- }
- break;
+ uintptr_t const entry_addr = tu_align32(next_link.address);
- // TODO support hs/fs ISO
- case EHCI_QTYPE_ITD:
- case EHCI_QTYPE_SITD:
- case EHCI_QTYPE_FSTN:
- default: break;
+ switch (next_link.type) {
+ case EHCI_QTYPE_QHD: {
+ ehci_qhd_t *qhd = (ehci_qhd_t *) entry_addr;
+ qhd_xfer_complete_isr(qhd);
}
+ break;
- next_item = *list_next(&next_item);
+ // TODO support hs/fs ISO
+ case EHCI_QTYPE_ITD:
+ case EHCI_QTYPE_SITD:
+ case EHCI_QTYPE_FSTN:
+ default:
+ break;
}
- }
-}
-
-#if CFG_TUSB_DEBUG >= EHCI_DBG
-static inline void print_portsc(ehci_registers_t* regs)
-{
- TU_LOG_HEX(EHCI_DBG, regs->portsc);
- TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status);
- TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change);
- TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled);
- TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
-
- TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset);
- TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power);
+ next_link = *list_next(&next_link);
+ }
}
-#else
-
-#define print_portsc(_reg)
-
-#endif
-
//------------- Host Controller Driver's Interrupt Handler -------------//
-void hcd_int_handler(uint8_t rhport)
-{
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) in_isr;
ehci_registers_t* regs = ehci_data.regs;
+ uint32_t const int_status = regs->status;
- uint32_t int_status = regs->status;
- int_status &= regs->inten;
-
- regs->status = int_status; // Acknowledge handled interrupt
-
- if (int_status == 0) return;
+ if (int_status & EHCI_INT_MASK_HC_HALTED) {
+ // something seriously wrong, maybe forget to flush/invalidate cache
+ TU_BREAKPOINT();
+ TU_LOG1(" HC halted\r\n");
+ return;
+ }
- if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER)
- {
+ if (int_status & EHCI_INT_MASK_FRAMELIST_ROLLOVER) {
ehci_data.uframe_number += (FRAMELIST_SIZE << 3);
+ regs->status = EHCI_INT_MASK_FRAMELIST_ROLLOVER; // Acknowledge
}
- if (int_status & EHCI_INT_MASK_PORT_CHANGE)
- {
- uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
- print_portsc(regs);
+ if (int_status & EHCI_INT_MASK_PORT_CHANGE) {
+ // Including: Force port resume, over-current change, enable/disable change and connect status change.
+ uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_W1C;
+ // print_portsc(regs);
- if (regs->portsc_bm.connect_status_change)
- {
+ if (regs->portsc_bm.connect_status_change) {
port_connect_status_change_isr(rhport);
}
regs->portsc |= port_status; // Acknowledge change bits in portsc
+ regs->status = EHCI_INT_MASK_PORT_CHANGE; // Acknowledge
}
- if (int_status & EHCI_INT_MASK_ERROR)
- {
- xfer_error_isr(rhport);
- }
-
- //------------- some QTD/SITD/ITD with IOC set is completed -------------//
- if (int_status & EHCI_INT_MASK_NXP_ASYNC)
- {
- async_list_xfer_complete_isr( qhd_async_head(rhport) );
- }
+ // A USB transfer is completed (OK or error)
+ uint32_t const usb_int = int_status & (EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR);
+ if (usb_int) {
+ proccess_async_xfer_isr(list_get_async_head(rhport));
- if (int_status & EHCI_INT_MASK_NXP_PERIODIC)
- {
- for (uint32_t i=1; i <= FRAMELIST_SIZE; i *= 2)
- {
- period_list_xfer_complete_isr( rhport, i );
+ for ( uint32_t i = 1; i <= FRAMELIST_SIZE; i *= 2 ) {
+ process_period_xfer_isr(rhport, i);
}
+
+ regs->status = usb_int; // Acknowledge
}
//------------- There is some removed async previously -------------//
- if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) // need to place after EHCI_INT_MASK_NXP_ASYNC
- {
+ // need to place after EHCI_INT_MASK_NXP_ASYNC
+ if (int_status & EHCI_INT_MASK_ASYNC_ADVANCE) {
async_advance_isr(rhport);
+ regs->status = EHCI_INT_MASK_ASYNC_ADVANCE; // Acknowledge
}
}
//--------------------------------------------------------------------+
-// HELPER
+// List Managing Helper
//--------------------------------------------------------------------+
+// Get head of periodic list
+TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_get_period_head(uint8_t rhport, uint32_t interval_ms) {
+ (void) rhport;
+ return (ehci_link_t*) &ehci_data.period_head_arr[ tu_log2( tu_min32(FRAMELIST_SIZE, interval_ms) ) ];
+}
-//------------- queue head helper -------------//
-static inline ehci_qhd_t* qhd_find_free (void)
-{
- for (uint32_t i=0; i<QHD_MAX; i++)
- {
- if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
- }
+// Get head of async list
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* list_get_async_head(uint8_t rhport) {
+ (void) rhport;
+ return qhd_control(0); // control qhd of dev0 is used as async head
+}
- return NULL;
+TU_ATTR_ALWAYS_INLINE static inline ehci_link_t* list_next(ehci_link_t const *p_link) {
+ return (ehci_link_t*) tu_align32(p_link->address);
}
-static inline ehci_qhd_t* qhd_next(ehci_qhd_t const * p_qhd)
+TU_ATTR_ALWAYS_INLINE static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
{
- return (ehci_qhd_t*) tu_align32(p_qhd->next.address);
+ new->address = current->address;
+ current->address = ((uint32_t) new) | (new_type << 1);
}
-static inline ehci_qhd_t* qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr)
-{
- ehci_qhd_t* qhd_pool = ehci_data.qhd_pool;
+// Remove all queue head belong to this device address
+static void list_remove_qhd_by_daddr(ehci_link_t* list_head, uint8_t dev_addr) {
+ ehci_link_t* prev = list_head;
- for(uint32_t i=0; i<QHD_MAX; i++)
- {
- if ( (qhd_pool[i].dev_addr == dev_addr) &&
- ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) )
- {
- return &qhd_pool[i];
+ while (prev && !prev->terminate) {
+ ehci_qhd_t* qhd = (ehci_qhd_t*) (uintptr_t) list_next(prev);
+
+ // done if loop back to head
+ if ( (uintptr_t) qhd == (uintptr_t) list_head) {
+ break;
+ }
+
+ if ( qhd->dev_addr == dev_addr ) {
+ // TODO deactivate all TD, wait for QHD to inactive before removal
+ prev->address = qhd->next.address;
+
+ // EHCI 4.8.2 link the removed qhd's next to async head (which always reachable by Host Controller)
+ qhd->next.address = ((uint32_t) list_head) | (EHCI_QTYPE_QHD << 1);
+
+ if ( qhd->int_smask )
+ {
+ // period list queue element is guarantee to be free in the next frame (1 ms)
+ qhd->used = 0;
+ }else
+ {
+ // async list use async advance handshake
+ // mark as removing, will completely re-usable when async advance isr occurs
+ qhd->removing = 1;
+ }
+
+ hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
+ hcd_dcache_clean(prev, sizeof(ehci_qhd_t));
+ }else {
+ prev = list_next(prev);
}
}
+}
- return NULL;
+
+//--------------------------------------------------------------------+
+// Queue Header helper
+//--------------------------------------------------------------------+
+
+// Get queue head for control transfer (always available)
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t* qhd_control(uint8_t dev_addr) {
+ return &ehci_data.control[dev_addr].qhd;
}
-//------------- TD helper -------------//
-static inline ehci_qtd_t* qtd_find_free(void)
-{
- for (uint32_t i=0; i<QTD_MAX; i++)
- {
- if ( !ehci_data.qtd_pool[i].used ) return &ehci_data.qtd_pool[i];
+// Find a free queue head
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_find_free(void) {
+ for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
+ if ( !ehci_data.qhd_pool[i].used ) return &ehci_data.qhd_pool[i];
}
-
return NULL;
}
-static inline ehci_qtd_t* qtd_next(ehci_qtd_t const * p_qtd )
-{
- return (ehci_qtd_t*) tu_align32(p_qtd->next.address);
+// Next queue head link
+TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_next(ehci_qhd_t const *p_qhd) {
+ return (ehci_qhd_t *) tu_align32(p_qhd->next.address);
}
-static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd)
-{
- if (p_qhd->p_qtd_list_head == p_qhd->p_qtd_list_tail) // last TD --> make it NULL
- {
- p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = NULL;
- }else
- {
- p_qhd->p_qtd_list_head = qtd_next( p_qhd->p_qtd_list_head );
+// Get queue head from device + endpoint address
+static ehci_qhd_t *qhd_get_from_addr(uint8_t dev_addr, uint8_t ep_addr) {
+ if ( 0 == tu_edpt_number(ep_addr) ) {
+ return qhd_control(dev_addr);
}
-}
-static inline void qtd_insert_to_qhd(ehci_qhd_t *p_qhd, ehci_qtd_t *p_qtd_new)
-{
- if (p_qhd->p_qtd_list_head == NULL) // empty list
- {
- p_qhd->p_qtd_list_head = p_qhd->p_qtd_list_tail = p_qtd_new;
- }else
- {
- p_qhd->p_qtd_list_tail->next.address = (uint32_t) p_qtd_new;
- p_qhd->p_qtd_list_tail = p_qtd_new;
+ ehci_qhd_t *qhd_pool = ehci_data.qhd_pool;
+
+ for ( uint32_t i = 0; i < QHD_MAX; i++ ) {
+ if ( (qhd_pool[i].dev_addr == dev_addr) &&
+ ep_addr == tu_edpt_addr(qhd_pool[i].ep_number, qhd_pool[i].pid) ) {
+ return &qhd_pool[i];
+ }
}
+
+ return NULL;
}
+// Init queue head with endpoint descriptor
static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
// address 0 is used as async head, which always on the list --> cannot be cleared (ehci halted otherwise)
- if (dev_addr != 0)
- {
+ if (dev_addr != 0) {
tu_memclr(p_qhd, sizeof(ehci_qhd_t));
}
@@ -839,7 +842,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
if (TUSB_SPEED_HIGH == p_qhd->ep_speed)
{
TU_ASSERT( interval <= 16, );
- if ( interval < 4) // sub milisecond interval
+ if ( interval < 4) // sub millisecond interval
{
p_qhd->interval_ms = 0;
p_qhd->int_smask = (interval == 1) ? TU_BIN8(11111111) :
@@ -862,58 +865,84 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, tusb_desc_endpoint_t c
p_qhd->int_smask = p_qhd->fl_int_cmask = 0;
}
- p_qhd->fl_hub_addr = devtree_info.hub_addr;
- p_qhd->fl_hub_port = devtree_info.hub_port;
- p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
+ p_qhd->fl_hub_addr = devtree_info.hub_addr;
+ p_qhd->fl_hub_port = devtree_info.hub_port;
+ p_qhd->mult = 1; // TODO not use high bandwidth/park mode yet
//------------- HCD Management Data -------------//
- p_qhd->used = 1;
- p_qhd->removing = 0;
- p_qhd->p_qtd_list_head = NULL;
- p_qhd->p_qtd_list_tail = NULL;
+ 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
//------------- active, but no TD list -------------//
p_qhd->qtd_overlay.halted = 0;
p_qhd->qtd_overlay.next.terminate = 1;
p_qhd->qtd_overlay.alternate.terminate = 1;
+
if (TUSB_XFER_BULK == xfer_type && p_qhd->ep_speed == TUSB_SPEED_HIGH && p_qhd->pid == EHCI_PID_OUT)
{
p_qhd->qtd_overlay.ping_err = 1; // do PING for Highspeed Bulk OUT, EHCI section 4.11
}
}
-static void qtd_init(ehci_qtd_t* p_qtd, void const* buffer, uint16_t total_bytes)
-{
- tu_memclr(p_qtd, sizeof(ehci_qtd_t));
+// Attach a TD to queue head
+static void qhd_attach_qtd(ehci_qhd_t *qhd, ehci_qtd_t *qtd) {
+ qhd->attached_qtd = qtd;
+ qhd->attached_buffer = qtd->buffer[0];
+
+ // clean and invalidate cache before physically write
+ hcd_dcache_clean_invalidate(qtd, sizeof(ehci_qtd_t));
- p_qtd->used = 1;
+ qhd->qtd_overlay.next.address = (uint32_t) qtd;
+ hcd_dcache_clean_invalidate(qhd, sizeof(ehci_qhd_t));
+}
- p_qtd->next.terminate = 1; // init to null
- p_qtd->alternate.terminate = 1; // not used, always set to terminated
- p_qtd->active = 1;
- p_qtd->err_count = 3; // TODO 3 consecutive errors tolerance
- p_qtd->data_toggle = 0;
- p_qtd->total_bytes = total_bytes;
- p_qtd->expected_bytes = total_bytes;
+// Remove an attached TD from queue head
+static void qhd_remove_qtd(ehci_qhd_t *qhd) {
+ ehci_qtd_t * volatile qtd = qhd->attached_qtd;
- p_qtd->buffer[0] = (uint32_t) buffer;
- for(uint8_t i=1; i<5; i++)
- {
- p_qtd->buffer[i] |= tu_align4k( p_qtd->buffer[i-1] ) + 4096;
- }
+ qhd->attached_qtd = NULL;
+ qhd->attached_buffer = 0;
+ hcd_dcache_clean(qhd, sizeof(ehci_qhd_t));
+
+ qtd->used = 0; // free QTD
+ hcd_dcache_clean(qtd, sizeof(ehci_qtd_t));
}
-//------------- List Managing Helper -------------//
-static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type)
-{
- new->address = current->address;
- current->address = ((uint32_t) new) | (new_type << 1);
+//--------------------------------------------------------------------+
+// Queue TD helper
+//--------------------------------------------------------------------+
+
+// Get TD for control transfer (always available)
+TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t* qtd_control(uint8_t dev_addr) {
+ return &ehci_data.control[dev_addr].qtd;
}
-static inline ehci_link_t* list_next(ehci_link_t *p_link_pointer)
-{
- return (ehci_link_t*) tu_align32(p_link_pointer->address);
+TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t *qtd_find_free(void) {
+ for (uint32_t i = 0; i < QTD_MAX; i++) {
+ if (!ehci_data.qtd_pool[i].used) return &ehci_data.qtd_pool[i];
+ }
+ return NULL;
+}
+
+static void qtd_init(ehci_qtd_t* qtd, void const* buffer, uint16_t total_bytes) {
+ tu_memclr(qtd, sizeof(ehci_qtd_t));
+ qtd->used = 1;
+
+ qtd->next.terminate = 1; // init to null
+ qtd->alternate.terminate = 1; // not used, always set to terminated
+ qtd->active = 1;
+ qtd->err_count = 3; // TODO 3 consecutive errors tolerance
+ qtd->data_toggle = 0;
+ qtd->int_on_complete = 1;
+ qtd->total_bytes = total_bytes;
+ qtd->expected_bytes = total_bytes;
+
+ qtd->buffer[0] = (uint32_t) buffer;
+ for(uint8_t i=1; i<5; i++) {
+ qtd->buffer[i] |= tu_align4k(qtd->buffer[i - 1] ) + 4096;
+ }
}
#endif
diff --git a/src/portable/ehci/ehci.h b/src/portable/ehci/ehci.h
index ff9ae12e7..457adc1d3 100644
--- a/src/portable/ehci/ehci.h
+++ b/src/portable/ehci/ehci.h
@@ -81,6 +81,8 @@ typedef union {
};
}ehci_link_t;
+TU_VERIFY_STATIC( sizeof(ehci_link_t) == 4, "size is not correct" );
+
/// Queue Element Transfer Descriptor
/// Qtd is used to declare overlay in ehci_qhd_t -> cannot be declared with TU_ATTR_ALIGNED(32)
typedef struct
@@ -114,7 +116,7 @@ typedef struct
volatile uint32_t current_page : 3 ; ///< Index into the qTD buffer pointer list
uint32_t int_on_complete : 1 ; ///< Interrupt on complete
volatile uint32_t total_bytes : 15 ; ///< Transfer bytes, decreased during transaction
- volatile uint32_t data_toggle : 1 ; ///< Data Toogle bit
+ volatile uint32_t data_toggle : 1 ; ///< Data Toggle bit
/// Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
@@ -160,13 +162,14 @@ typedef struct TU_ATTR_ALIGNED(32)
uint8_t used;
uint8_t removing; // removed from asyn list, waiting for async advance
uint8_t pid;
- uint8_t interval_ms; // polling interval in frames (or milisecond)
+ uint8_t interval_ms; // polling interval in frames (or millisecond)
- uint16_t total_xferred_bytes; // number of bytes xferred until a qtd with ioc bit set
- uint8_t reserved2[2];
+ uint8_t TU_RESERVED[4];
- ehci_qtd_t * volatile p_qtd_list_head; // head of the scheduled TD list
- ehci_qtd_t * volatile p_qtd_list_tail; // tail of the scheduled TD list
+ // Attached TD management, note usbh will only queue 1 TD per QHD.
+ // buffer for dcache invalidate since td's buffer is modified by HC and finding initial buffer address is not trivial
+ uint32_t attached_buffer;
+ ehci_qtd_t * volatile attached_qtd;
} ehci_qhd_t;
TU_VERIFY_STATIC( sizeof(ehci_qhd_t) == 64, "size is not correct" );
@@ -225,7 +228,7 @@ typedef struct TU_ATTR_ALIGNED(32)
uint16_t reserved ; ///< reserved
// Word 3: siTD Transfer Status and Control
- // Status [7:0] TODO indentical to qTD Token'status --> refractor later
+ // Status [7:0] TODO identical to qTD Token'status --> refactor later
volatile uint32_t : 1 ; // reserved
volatile uint32_t split_state : 1 ;
volatile uint32_t missed_uframe : 1 ;
@@ -245,14 +248,6 @@ typedef struct TU_ATTR_ALIGNED(32)
/// Word 4-5: Buffer Pointer List
uint32_t buffer[2]; // buffer[1] TP: Transaction Position - T-Count: Transaction Count
-// union{
-// uint32_t BufferPointer1;
-// struct {
-// volatile uint32_t TCount : 3;
-// volatile uint32_t TPosition : 2;
-// };
-// };
-
/*---------- Word 6 ----------*/
ehci_link_t back;
@@ -267,53 +262,63 @@ TU_VERIFY_STATIC( sizeof(ehci_sitd_t) == 32, "size is not correct" );
//--------------------------------------------------------------------+
// EHCI Operational Register
//--------------------------------------------------------------------+
-enum ehci_interrupt_mask_{
+enum {
+ // Bit 0-5 has maskable in interrupt enabled register
EHCI_INT_MASK_USB = TU_BIT(0),
EHCI_INT_MASK_ERROR = TU_BIT(1),
EHCI_INT_MASK_PORT_CHANGE = TU_BIT(2),
-
EHCI_INT_MASK_FRAMELIST_ROLLOVER = TU_BIT(3),
EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR = TU_BIT(4),
EHCI_INT_MASK_ASYNC_ADVANCE = TU_BIT(5),
+
EHCI_INT_MASK_NXP_SOF = TU_BIT(7),
- EHCI_INT_MASK_NXP_ASYNC = TU_BIT(18),
- EHCI_INT_MASK_NXP_PERIODIC = TU_BIT(19),
+ EHCI_INT_MASK_HC_HALTED = TU_BIT(12),
+ EHCI_INT_MASK_RECLAIMATION = TU_BIT(13),
+ EHCI_INT_MASK_PERIODIC_SCHED_STATUS = TU_BIT(14),
+ EHCI_INT_MASK_ASYNC_SCHED_STATUS = TU_BIT(15),
EHCI_INT_MASK_ALL =
EHCI_INT_MASK_USB | EHCI_INT_MASK_ERROR | EHCI_INT_MASK_PORT_CHANGE |
EHCI_INT_MASK_FRAMELIST_ROLLOVER | EHCI_INT_MASK_PCI_HOST_SYSTEM_ERROR |
- EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_SOF |
- EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC
+ EHCI_INT_MASK_ASYNC_ADVANCE | EHCI_INT_MASK_NXP_SOF
};
-enum ehci_usbcmd_pos_ {
- EHCI_USBCMD_POS_RUN_STOP = 0,
- EHCI_USBCMD_POS_FRAMELIST_SIZE = 2,
- EHCI_USBCMD_POS_PERIOD_ENABLE = 4,
- EHCI_USBCMD_POS_ASYNC_ENABLE = 5,
- EHCI_USBCMD_POS_NXP_FRAMELIST_SIZE_MSB = 15,
- EHCI_USBCMD_POS_INTERRUPT_THRESHOLD = 16
+enum {
+ EHCI_USBCMD_FRAMELIST_SIZE_SHIFT = 2, // [2..3]
+ EHCI_USBCMD_CHIPIDEA_FRAMELIST_SIZE_MSB_SHIFT = 15,
+ EHCI_USBCMD_INTERRUPT_THRESHOLD_SHIFT = 16
};
-enum ehci_portsc_change_mask_{
+enum {
+ EHCI_USBCMD_RUN_STOP = TU_BIT(0), // [0..0] 1 = Run, 0 = Stop
+ EHCI_USBCMD_HCRESET = TU_BIT(1), // [1..1] SW write 1 to reset HC, clear by HC when complete
+ EHCI_USBCMD_PERIOD_SCHEDULE_ENABLE = TU_BIT(4), // [4..4] Enable periodic schedule
+ EHCI_USBCMD_ASYNC_SCHEDULE_ENABLE = TU_BIT(5), // [5..5] Enable async schedule
+ EHCI_USBCMD_INTR_ON_ASYNC_ADVANCE_DOORBELL = TU_BIT(6), // [6..6] Tell HC to interrupt next time it advances async list. Clear by HC
+};
+
+enum {
EHCI_PORTSC_MASK_CURRENT_CONNECT_STATUS = TU_BIT(0),
EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE = TU_BIT(1),
EHCI_PORTSC_MASK_PORT_EANBLED = TU_BIT(2),
- EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE = TU_BIT(3),
+ EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE = TU_BIT(3),
EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE = TU_BIT(5),
+ EHCI_PORTSC_MASK_FORCE_RESUME = TU_BIT(6),
+ EHCI_PORTSC_MASK_PORT_SUSPEND = TU_BIT(7),
EHCI_PORTSC_MASK_PORT_RESET = TU_BIT(8),
+ EHCI_PORTSC_MASK_PORT_POWER = TU_BIT(12),
- EHCI_PORTSC_MASK_ALL =
- EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
- EHCI_PORTSC_MASK_PORT_ENABLE_CHAGNE |
- EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
+ EHCI_PORTSC_MASK_W1C =
+ EHCI_PORTSC_MASK_CONNECT_STATUS_CHANGE |
+ EHCI_PORTSC_MASK_PORT_ENABLE_CHANGE |
+ EHCI_PORTSC_MASK_OVER_CURRENT_CHANGE
};
typedef volatile struct
{
union {
- uint32_t command;
+ uint32_t command; // 0x00
struct {
uint32_t run_stop : 1 ; ///< 1=Run. 0=Stop
@@ -333,7 +338,7 @@ typedef volatile struct
};
union {
- uint32_t status;
+ uint32_t status; // 0x04
struct {
uint32_t usb : 1 ; ///< qTD with IOC is retired
@@ -350,14 +355,14 @@ typedef volatile struct
uint32_t periodic_status : 1 ; ///< Periodic schedule status
uint32_t async_status : 1 ; ///< Async schedule status
uint32_t : 2 ;
- uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected andthe packet is on the asynchronous schedule.
- uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set andthe TD was from the periodic schedule.
+ uint32_t nxp_int_async : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the asynchronous schedule. This bit is also set by the Host when a short packet is detected and the packet is on the asynchronous schedule.
+ uint32_t nxp_int_period : 1 ; ///< NXP customized: This bit is set by the Host Controller when the cause of an interrupt is a completion of a USB transaction where the Transfer Descriptor (TD) has an interrupt on complete (IOC) bit set and the TD was from the periodic schedule.
uint32_t : 12 ;
}status_bm;
};
union{
- uint32_t inten;
+ uint32_t inten; // 0x08
struct {
uint32_t usb : 1 ;
@@ -375,43 +380,87 @@ typedef volatile struct
}inten_bm;
};
- uint32_t frame_index ; ///< Micro frame counter
- uint32_t ctrl_ds_seg ; ///< Control Data Structure Segment
- uint32_t periodic_list_base ; ///< Beginning address of perodic frame list
- uint32_t async_list_addr ; ///< Address of next async QHD to be executed
+ uint32_t frame_index ; ///< 0x0C Micro frame counter
+ uint32_t ctrl_ds_seg ; ///< 0x10 Control Data Structure Segment
+ uint32_t periodic_list_base ; ///< 0x14 Beginning address of perodic frame list
+ uint32_t async_list_addr ; ///< 0x18 Address of next async QHD to be executed
uint32_t nxp_tt_control ; ///< nxp embedded transaction translator (reserved by EHCI specs)
uint32_t reserved[8] ;
- uint32_t config_flag ; ///< not used by NXP
+ uint32_t config_flag ; ///< 0x40 not used by NXP
union {
- uint32_t portsc ; ///< port status and control
- struct {
- uint32_t current_connect_status : 1; ///< 0: No device, 1: Device is present on port
- uint32_t connect_status_change : 1; ///< Change in Current Connect Status
- uint32_t port_enabled : 1; ///< Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
- uint32_t port_enable_change : 1; ///< Port Enabled has changed
- uint32_t over_current_active : 1; ///< Port has an over-current condition
- uint32_t over_current_change : 1; ///< Change to Over-current Active
- uint32_t force_port_resume : 1; ///< Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
- uint32_t suspend : 1; ///< Port in suspend state
- uint32_t port_reset : 1; ///< 1=Port is in Reset. 0=Port is not in Reset
- uint32_t nxp_highspeed_status : 1; ///< NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
- uint32_t line_status : 2; ///< D+/D- state: 00: SE0, 10: J-state, 01: K-state
- uint32_t port_power : 1; ///< 0= power off, 1= power on
- uint32_t port_owner : 1; ///< not used by NXP
- uint32_t port_indicator_control : 2; ///< 00b: off, 01b: Amber, 10b: green, 11b: undefined
- uint32_t port_test_control : 4; ///< Port test mode, not used by tinyusb
- uint32_t wake_on_connect_enable : 1; ///< Enables device connects as wake-up events
- uint32_t wake_on_disconnect_enable : 1; ///< Enables device disconnects as wake-up events
- uint32_t wake_on_over_current_enable : 1; ///< Enables over-current conditions as wake-up events
- uint32_t nxp_phy_clock_disable : 1; ///< NXP customized: the PHY can be put into Low Power Suspend – Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
- uint32_t nxp_port_force_fullspeed : 1; ///< NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
- uint32_t TU_RESERVED : 1;
- uint32_t nxp_port_speed : 2; ///< NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
+ // mixed with RW and R/WC bits, care should be taken when writing to this register
+ uint32_t portsc ; ///< 0x44 port status and control
+ const struct {
+ uint32_t current_connect_status : 1; ///< 00: 0: No device, 1: Device is present on port
+ uint32_t connect_status_change : 1; ///< 01: [R/WC] Change in Current Connect Status
+ uint32_t port_enabled : 1; ///< 02: Ports can only be enabled by HC as a part of the reset and enable. SW can write 0 to disable
+ uint32_t port_enable_change : 1; ///< 03: [R/WC] Port Enabled has changed
+ uint32_t over_current_active : 1; ///< 04: Port has an over-current condition
+ uint32_t over_current_change : 1; ///< 05: [R/WC] Change to Over-current Active
+ uint32_t force_port_resume : 1; ///< 06: Resume detected/driven on port. This functionality defined for manipulating this bit depends on the value of the Suspend bit.
+ uint32_t suspend : 1; ///< 07: Port in suspend state
+ uint32_t port_reset : 1; ///< 08: 1=Port is in Reset. 0=Port is not in Reset
+ uint32_t nxp_highspeed_status : 1; ///< 09: NXP customized: 0=connected to the port is not in High-speed mode, 1=connected to the port is in High-speed mode
+ uint32_t line_status : 2; ///< 10-11: D+/D- state: 00: SE0, 10: J-state, 01: K-state
+ uint32_t port_power : 1; ///< 12: 0= power off, 1= power on
+ uint32_t port_owner : 1; ///< 13: not used by NXP
+ uint32_t port_indicator_control : 2; ///< 14-15: 00b: off, 01b: Amber, 10b: green, 11b: undefined
+ uint32_t port_test_control : 4; ///< 16-19: Port test mode, not used by tinyusb
+ uint32_t wake_on_connect_enable : 1; ///< 20: Enables device connects as wake-up events
+ uint32_t wake_on_disconnect_enable : 1; ///< 21: Enables device disconnects as wake-up events
+ uint32_t wake_on_over_current_enable : 1; ///< 22: Enables over-current conditions as wake-up events
+ uint32_t nxp_phy_clock_disable : 1; ///< 23: NXP customized: the PHY can be put into Low Power Suspend – Clock Disable when the downstream device has been put into suspend mode or when no downstream device is connected. Low power suspend is completely under the control of software. 0: enable PHY clock, 1: disable PHY clock
+ uint32_t nxp_port_force_fullspeed : 1; ///< 24: NXP customized: Writing this bit to a 1 will force the port to only connect at Full Speed. It disables the chirp sequence that allowsthe port to identify itself as High Speed. This is useful for testing FS configurations with a HS host, hub or device.
+ uint32_t TU_RESERVED : 1; ///< 25
+ uint32_t nxp_port_speed : 2; ///< 26-27: NXP customized: This register field indicates the speed atwhich the port is operating. For HS mode operation in the host controllerand HS/FS operation in the device controller the port routing steers data to the Protocol engine. For FS and LS mode operation in the host controller, the port routing steers data to the Protocol Engine w/ Embedded Transaction Translator. 0x0: Fullspeed, 0x1: Lowspeed, 0x2: Highspeed
uint32_t TU_RESERVED : 4;
}portsc_bm;
};
-}ehci_registers_t;
+} ehci_registers_t;
+
+//--------------------------------------------------------------------+
+// Capability Registers
+//--------------------------------------------------------------------+
+typedef volatile struct {
+ uint8_t caplength; // 0x00
+ uint8_t TU_RESERVED; // 0x01
+ uint16_t hciversion; // 0x02
+
+ union {
+ uint32_t hcsparams; // 0x04
+ struct {
+ uint32_t num_ports : 4; // [00:03]
+ uint32_t port_power_control : 1; // [04]
+ uint32_t TU_RESERVED : 2; // [05:06]
+ uint32_t port_route_rule : 1; // [07]
+ uint32_t n_pcc : 4; // [08:11] Number of Ports per Companion Controller
+ uint32_t n_cc : 4; // [12:15] Number of Companion Controllers
+ uint32_t port_ind : 1; // [16] Port Indicators
+ uint32_t TU_RESERVED : 3; // [17:19]
+ uint32_t n_ptt : 4; // [20:23] ChipIdea: Number of Ports per Transaction Translator
+ uint32_t n_tt : 4; // [24:27] ChipIdea: Number of Transaction Translators
+ uint32_t TU_RESERVED : 4; // [28:31]
+ } hcsparams_bm;
+ };
+
+ union {
+ uint32_t hccparams; // 0x08
+ struct {
+ uint32_t addr_64bit : 1; // [00] 64-bit Addressing Capability
+ uint32_t programmable_frame_list_flag : 1; // [01] Programmable Frame List Flag
+ uint32_t async_park_cap : 1; // [02] Asynchronous Schedule Park Capability
+ uint32_t TU_RESERVED : 1; // [03]
+ uint32_t iso_schedule_threshold : 4; // [4:7] Isochronous Scheduling Threshold
+ uint32_t eecp : 8; // [8:15] EHCI Extended Capabilities Pointer
+ uint32_t TU_RESERVED : 16;// [16:31]
+ } hccparams_bm;
+ };
+
+ uint32_t hcsp_portroute; // 0x0C HCSP Port Route Register
+} ehci_cap_registers_t;
+
+TU_VERIFY_STATIC(sizeof(ehci_cap_registers_t) == 16, "size is not correct");
#ifdef __cplusplus
}
diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c
index 0b75af627..1b6aae026 100644
--- a/src/portable/espressif/esp32sx/dcd_esp32sx.c
+++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c
@@ -31,16 +31,27 @@
#if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
// Espressif
-#include "freertos/xtensa_api.h"
+#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)
@@ -161,8 +172,8 @@ static void enum_done_processing(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
@@ -196,9 +207,11 @@ void dcd_init(uint8_t rhport)
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)
@@ -741,7 +754,7 @@ static void handle_epin_ints(void)
// XFER Timeout
if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
- // Clear interrupt or enpoint will hang.
+ // Clear interrupt or endpoint will hang.
USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
// Maybe retry?
}
@@ -874,4 +887,3 @@ void dcd_int_disable (uint8_t rhport)
}
#endif // #if OPT_MCU_ESP32S2 || OPT_MCU_ESP32S3
-
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c
index 0ba53d4d0..4fce08dd9 100644
--- a/src/portable/mentor/musb/dcd_musb.c
+++ b/src/portable/mentor/musb/dcd_musb.c
@@ -1,7 +1,8 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Koji KITAYAMA
+ * Copyright (c) 2024, Brent Kowal (Analog Devices, Inc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,8 +27,10 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && \
- TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_MUSB)
+
+#define MUSB_DEBUG 2
+#define MUSB_REGS(rhport) ((musb_regs_t*) MUSB_BASES[rhport])
#if __GNUC__ > 8 && defined(__ARM_FEATURE_UNALIGNED)
/* GCC warns that an address may be unaligned, even though
@@ -35,48 +38,30 @@
_Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
#endif
+#include "musb_type.h"
#include "device/dcd.h"
-#if TU_CHECK_MCU(OPT_MCU_MSP432E4)
- #include "musb_msp432e.h"
-
-#elif TU_CHECK_MCU(OPT_MCU_TM4C123, OPT_MCU_TM4C129)
- #include "musb_tm4c.h"
-
- // HACK generalize later
- #include "musb_type.h"
- #define FIFO0_WORD FIFO0
- #define FIFO1_WORD FIFO1
-
+// Following symbols must be defined by port header
+// - musb_dcd_int_enable/disable/clear/get_enable
+// - musb_dcd_int_handler_enter/exit
+#if defined(TUP_USBIP_MUSB_TI)
+ #include "musb_ti.h"
+#elif defined(TUP_USBIP_MUSB_ADI)
+ #include "musb_max32.h"
#else
- #error "Unsupported MCUs"
+ #error "Unsupported MCU"
#endif
/*------------------------------------------------------------------
* MACRO TYPEDEF CONSTANT ENUM DECLARATION
*------------------------------------------------------------------*/
-#define REQUEST_TYPE_INVALID (0xFFu)
-typedef struct {
- uint_fast16_t beg; /* offset of including first element */
- uint_fast16_t end; /* offset of excluding the last element */
-} free_block_t;
-
-typedef struct TU_ATTR_PACKED {
- uint16_t TXMAXP;
- uint8_t TXCSRL;
- uint8_t TXCSRH;
- uint16_t RXMAXP;
- uint8_t RXCSRL;
- uint8_t RXCSRH;
- uint16_t RXCOUNT;
- uint16_t RESERVED[3];
-} hw_endpoint_t;
+#define REQUEST_TYPE_INVALID (0xFFu)
typedef union {
- uint8_t u8;
- uint16_t u16;
- uint32_t u32;
+ volatile uint8_t u8;
+ volatile uint16_t u16;
+ volatile uint32_t u32;
} hw_fifo_t;
typedef struct TU_ATTR_PACKED
@@ -92,134 +77,97 @@ typedef struct
uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
int8_t status_out;
pipe_state_t pipe0;
- pipe_state_t pipe[2][7]; /* pipe[direction][endpoint number - 1] */
+ pipe_state_t pipe[2][TUP_DCD_ENDPOINT_MAX-1]; /* pipe[direction][endpoint number - 1] */
uint16_t pipe_buf_is_fifo[2]; /* Bitmap. Each bit means whether 1:TU_FIFO or 0:POD. */
} dcd_data_t;
-/*------------------------------------------------------------------
- * INTERNAL OBJECT & FUNCTION DECLARATION
- *------------------------------------------------------------------*/
static dcd_data_t _dcd;
+//--------------------------------------------------------------------
+// HW FIFO Helper
+// Note: Index register is already set by caller
+//--------------------------------------------------------------------
-static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
-{
- free_block_t *cur = beg;
- for (; cur < end && ((addr < cur->beg) || (cur->end <= addr)); ++cur) ;
- return cur;
-}
+#if MUSB_CFG_DYNAMIC_FIFO
-static inline int update_free_block_list(free_block_t *blks, unsigned num, uint_fast16_t addr, uint_fast16_t size)
-{
- free_block_t *p = find_containing_block(blks, blks + num, addr);
- TU_ASSERT(p != blks + num, -2);
- if (p->beg == addr) {
- /* Shrink block */
- p->beg = addr + size;
- if (p->beg != p->end) return 0;
- /* remove block */
- free_block_t *end = blks + num;
- while (p + 1 < end) {
- *p = *(p + 1);
- ++p;
- }
- return -1;
- } else {
- /* Split into 2 blocks */
- free_block_t tmp = {
- .beg = addr + size,
- .end = p->end
- };
- p->end = addr;
- if (p->beg == p->end) {
- if (tmp.beg != tmp.end) {
- *p = tmp;
- return 0;
- }
- /* remove block */
- free_block_t *end = blks + num;
- while (p + 1 < end) {
- *p = *(p + 1);
- ++p;
- }
- return -1;
- }
- if (tmp.beg == tmp.end) return 0;
- blks[num] = tmp;
- return 1;
+// musb is configured to use dynamic FIFO sizing.
+// FF Size is encodded: 1 << (fifo_size[3:0] + 3) = 8 << fifo_size[3:0]
+// FF Address is 8*ff_addr[12:0]
+// First 64 bytes are reserved for EP0
+static uint32_t alloced_fifo_bytes;
+
+// ffsize is log2(mps) - 3 (round up)
+TU_ATTR_ALWAYS_INLINE static inline uint8_t hwfifo_byte2size(uint16_t nbytes) {
+ uint8_t ffsize = 28 - tu_min8(28, __builtin_clz(nbytes));
+ if ((8u << ffsize) < nbytes) {
+ ++ffsize;
}
+ return ffsize;
}
-static inline unsigned free_block_size(free_block_t const *blk)
-{
- return blk->end - blk->beg;
+TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigned epnum, unsigned is_rx) {
+ (void) epnum;
+ musb->fifo_size[is_rx] = 0;
+ musb->fifo_addr[is_rx] = 0;
}
-#if 0
-static inline void print_block_list(free_block_t const *blk, unsigned num)
-{
- TU_LOG1("*************\n");
- for (unsigned i = 0; i < num; ++i) {
- TU_LOG1(" Blk%u %u %u\n", i, blk->beg, blk->end);
- ++blk;
+TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps,
+ bool double_packet) {
+ (void) epnum;
+ uint8_t ffsize = hwfifo_byte2size(mps);
+ mps = 8 << ffsize; // round up to the next power of 2
+
+ if (double_packet) {
+ ffsize |= MUSB_FIFOSZ_DOUBLE_PACKET;
+ mps <<= 1;
}
+
+ TU_ASSERT(alloced_fifo_bytes + mps <= MUSB_CFG_DYNAMIC_FIFO_SIZE);
+ musb->fifo_addr[is_rx] = alloced_fifo_bytes / 8;
+ musb->fifo_size[is_rx] = ffsize;
+
+ alloced_fifo_bytes += mps;
+ return true;
}
+
#else
-#define print_block_list(a,b)
-#endif
-static unsigned find_free_memory(uint_fast16_t size_in_log2_minus3)
-{
- free_block_t free_blocks[2 * (TUP_DCD_ENDPOINT_MAX - 1)];
- unsigned num_blocks = 1;
+TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigned epnum, unsigned is_rx) {
+ (void) musb; (void) epnum; (void) is_rx;
+ // nothing to do for static FIFO
+}
- /* Initialize free memory block list */
- free_blocks[0].beg = 64 / 8;
- free_blocks[0].end = (4 << 10) / 8; /* 4KiB / 8 bytes */
- for (int i = 1; i < TUP_DCD_ENDPOINT_MAX; ++i) {
- uint_fast16_t addr;
- int num;
- USB0->EPIDX = i;
- addr = USB0->TXFIFOADD;
- if (addr) {
- unsigned sz = USB0->TXFIFOSZ;
- unsigned sft = (sz & USB_TXFIFOSZ_SIZE_M) + ((sz & USB_TXFIFOSZ_DPB) ? 1: 0);
- num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
- TU_ASSERT(-2 < num, 0);
- num_blocks += num;
- print_block_list(free_blocks, num_blocks);
- }
- addr = USB0->RXFIFOADD;
- if (addr) {
- unsigned sz = USB0->RXFIFOSZ;
- unsigned sft = (sz & USB_RXFIFOSZ_SIZE_M) + ((sz & USB_RXFIFOSZ_DPB) ? 1: 0);
- num = update_free_block_list(free_blocks, num_blocks, addr, 1 << sft);
- TU_ASSERT(-2 < num, 0);
- num_blocks += num;
- print_block_list(free_blocks, num_blocks);
+TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps,
+ bool double_packet) {
+ (void) epnum; (void) mps;
+ if (!double_packet) {
+ #if defined(TUP_USBIP_MUSB_ADI)
+ musb->indexed_csr.maxp_csr[is_rx].csrh |= MUSB_CSRH_DISABLE_DOUBLE_PACKET(is_rx);
+ #else
+ if (is_rx) {
+ musb->rx_doulbe_packet_disable |= 1u << epnum;
+ } else {
+ musb->tx_double_packet_disable |= 1u << epnum;
}
+ #endif
}
- print_block_list(free_blocks, num_blocks);
- /* Find the best fit memory block */
- uint_fast16_t size_in_8byte_unit = 1 << size_in_log2_minus3;
- free_block_t const *min = NULL;
- uint_fast16_t min_sz = 0xFFFFu;
- free_block_t const *end = &free_blocks[num_blocks];
- for (free_block_t const *cur = &free_blocks[0]; cur < end; ++cur) {
- uint_fast16_t sz = free_block_size(cur);
- if (sz < size_in_8byte_unit) continue;
- if (size_in_8byte_unit == sz) return cur->beg;
- if (sz < min_sz) min = cur;
- }
- TU_ASSERT(min, 0);
- return min->beg;
+ return true;
}
-static inline volatile hw_endpoint_t* edpt_regs(unsigned epnum_minus1)
-{
- volatile hw_endpoint_t *regs = (volatile hw_endpoint_t*)((uintptr_t)&USB0->TXMAXP1);
- return regs + epnum_minus1;
+#endif
+
+// Flush FIFO and clear data toggle
+TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigned epnum, unsigned is_rx, bool clear_dtog) {
+ (void) epnum;
+ const uint8_t csrl_dtog = clear_dtog ? MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx) : 0;
+ musb_ep_maxp_csr_t* maxp_csr = &musb->indexed_csr.maxp_csr[is_rx];
+ // may need to flush twice for double packet
+ for (unsigned i=0; i<2; i++) {
+ if (maxp_csr->csrl & MUSB_CSRL_PACKET_READY(is_rx)) {
+ maxp_csr->csrl = MUSB_CSRL_FLUSH_FIFO(is_rx) | csrl_dtog;
+ }
+ }
}
static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
@@ -284,11 +232,14 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne
ops[dir].tu_fifo_advance(f, total_len - rem);
}
-static void process_setup_packet(uint8_t rhport)
-{
+static void process_setup_packet(uint8_t rhport) {
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+
+ // Read setup packet
uint32_t *p = (void*)&_dcd.setup_packet;
- p[0] = USB0->FIFO0_WORD;
- p[1] = USB0->FIFO0_WORD;
+ volatile uint32_t *fifo_ptr = &musb_regs->fifo[0];
+ p[0] = *fifo_ptr;
+ p[1] = *fifo_ptr;
_dcd.pipe0.buf = NULL;
_dcd.pipe0.length = 0;
@@ -299,12 +250,16 @@ static void process_setup_packet(uint8_t rhport)
_dcd.remaining_ctrl = len;
const unsigned dir_in = tu_edpt_dir(_dcd.setup_packet.bmRequestType);
/* Clear RX FIFO and reverse the transaction direction */
- if (len && dir_in) USB0->CSRL0 = USB_CSRL0_RXRDYC;
+ if (len && dir_in) {
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC;
+ }
}
-static bool handle_xfer_in(uint_fast8_t ep_addr)
+static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr)
{
- unsigned epnum_minus1 = tu_edpt_number(ep_addr) - 1;
+ unsigned epnum = tu_edpt_number(ep_addr);
+ unsigned epnum_minus1 = epnum - 1;
pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
const unsigned rem = pipe->remaining;
@@ -313,44 +268,49 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
return true;
}
- volatile hw_endpoint_t *regs = edpt_regs(epnum_minus1);
- const unsigned mps = regs->TXMAXP;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
+ const unsigned mps = ep_csr->tx_maxp;
const unsigned len = TU_MIN(mps, rem);
void *buf = pipe->buf;
- // TU_LOG1(" %p mps %d len %d rem %d\n", buf, mps, len, rem);
+ volatile void *fifo_ptr = &musb_regs->fifo[epnum];
+ // TU_LOG1(" %p mps %d len %d rem %d\r\n", buf, mps, len, rem);
if (len) {
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff(buf, &USB0->FIFO1_WORD + epnum_minus1, len, TUSB_DIR_IN);
+ pipe_read_write_packet_ff(buf, fifo_ptr, len, TUSB_DIR_IN);
} else {
- pipe_write_packet(buf, &USB0->FIFO1_WORD + epnum_minus1, len);
+ pipe_write_packet(buf, fifo_ptr, len);
pipe->buf = buf + len;
}
pipe->remaining = rem - len;
}
- regs->TXCSRL = USB_TXCSRL1_TXRDY;
- // TU_LOG1(" TXCSRL%d = %x %d\n", epnum_minus1 + 1, regs->TXCSRL, rem - len);
+ ep_csr->tx_csrl = MUSB_TXCSRL1_TXRDY;
+ // TU_LOG1(" TXCSRL%d = %x %d\r\n", epnum, ep_csr->tx_csrl, rem - len);
return false;
}
-static bool handle_xfer_out(uint_fast8_t ep_addr)
+static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr)
{
- unsigned epnum_minus1 = tu_edpt_number(ep_addr) - 1;
+ unsigned epnum = tu_edpt_number(ep_addr);
+ unsigned epnum_minus1 = epnum - 1;
pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
- volatile hw_endpoint_t *regs = edpt_regs(epnum_minus1);
- // TU_LOG1(" RXCSRL%d = %x\n", epnum_minus1 + 1, regs->RXCSRL);
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
+ // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, ep_csr->rx_csrl);
- TU_ASSERT(regs->RXCSRL & USB_RXCSRL1_RXRDY);
+ TU_ASSERT(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY);
- const unsigned mps = regs->RXMAXP;
+ const unsigned mps = ep_csr->rx_maxp;
const unsigned rem = pipe->remaining;
- const unsigned vld = regs->RXCOUNT;
+ const unsigned vld = ep_csr->rx_count;
const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
void *buf = pipe->buf;
+ volatile void *fifo_ptr = &musb_regs->fifo[epnum];
if (len) {
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff(buf, &USB0->FIFO1_WORD + epnum_minus1, len, TUSB_DIR_OUT);
+ pipe_read_write_packet_ff(buf, fifo_ptr, len, TUSB_DIR_OUT);
} else {
- pipe_read_packet(buf, &USB0->FIFO1_WORD + epnum_minus1, len);
+ pipe_read_packet(buf, fifo_ptr, len);
pipe->buf = buf + len;
}
pipe->remaining = rem - len;
@@ -359,15 +319,14 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
pipe->buf = NULL;
return NULL != buf;
}
- regs->RXCSRL = 0; /* Clear RXRDY bit */
+ ep_csr->rx_csrl = 0; /* Clear RXRDY bit */
return false;
}
static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
- (void)rhport;
-
- unsigned epnum_minus1 = tu_edpt_number(ep_addr) - 1;
+ unsigned epnum = tu_edpt_number(ep_addr);
+ unsigned epnum_minus1 = epnum - 1;
unsigned dir_in = tu_edpt_dir(ep_addr);
pipe_state_t *pipe = &_dcd.pipe[dir_in][epnum_minus1];
@@ -376,10 +335,11 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16
pipe->remaining = total_bytes;
if (dir_in) {
- handle_xfer_in(ep_addr);
+ handle_xfer_in(rhport, ep_addr);
} else {
- volatile hw_endpoint_t *regs = edpt_regs(epnum_minus1);
- if (regs->RXCSRL & USB_RXCSRL1_RXRDY) regs->RXCSRL = 0;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum);
+ if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) ep_csr->rx_csrl = 0;
}
return true;
}
@@ -388,7 +348,8 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
{
(void)rhport;
TU_ASSERT(total_bytes <= 64); /* Current implementation supports for only up to 64 bytes. */
-
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
const unsigned req = _dcd.setup_packet.bmRequestType;
TU_ASSERT(req != REQUEST_TYPE_INVALID || total_bytes == 0);
@@ -399,14 +360,14 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
* may have already finished and received the next setup packet
* without calling this function, so we have no choice but to
* invoke the callback function of status packet here. */
- // TU_LOG1(" STATUS OUT USB0->CSRL0 = %x\n", USB0->CSRL0);
+ // TU_LOG1(" STATUS OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
_dcd.status_out = 0;
if (req == REQUEST_TYPE_INVALID) {
dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
} else {
/* The next setup packet has already been received, it aborts
* invoking callback function to avoid confusing TUSB stack. */
- TU_LOG1("Drop CONTROL_STAGE_ACK\n");
+ TU_LOG1("Drop CONTROL_STAGE_ACK\r\n");
}
return true;
}
@@ -415,8 +376,9 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
TU_ASSERT(total_bytes <= _dcd.remaining_ctrl);
const unsigned rem = _dcd.remaining_ctrl;
const unsigned len = TU_MIN(TU_MIN(rem, 64), total_bytes);
+ volatile void *fifo_ptr = &musb_regs->fifo[0];
if (dir_in) {
- pipe_write_packet(buffer, &USB0->FIFO0_WORD, len);
+ pipe_write_packet(buffer, fifo_ptr, len);
_dcd.pipe0.buf = buffer + len;
_dcd.pipe0.length = len;
@@ -427,45 +389,48 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
_dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; /* Change to STATUS/SETUP stage */
_dcd.status_out = 1;
/* Flush TX FIFO and reverse the transaction direction. */
- USB0->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_DATAEND;
+ ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND;
} else {
- USB0->CSRL0 = USB_CSRL0_TXRDY; /* Flush TX FIFO to return ACK. */
+ ep_csr->csr0l = MUSB_CSRL0_TXRDY; /* Flush TX FIFO to return ACK. */
}
- // TU_LOG1(" IN USB0->CSRL0 = %x\n", USB0->CSRL0);
+ // TU_LOG1(" IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
} else {
- // TU_LOG1(" OUT USB0->CSRL0 = %x\n", USB0->CSRL0);
+ // TU_LOG1(" OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
_dcd.pipe0.buf = buffer;
_dcd.pipe0.length = len;
_dcd.pipe0.remaining = len;
- USB0->CSRL0 = USB_CSRL0_RXRDYC; /* Clear RX FIFO to return ACK. */
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC; /* Clear RX FIFO to return ACK. */
}
} else if (dir_in) {
- // TU_LOG1(" STATUS IN USB0->CSRL0 = %x\n", USB0->CSRL0);
+ // TU_LOG1(" STATUS IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l);
_dcd.pipe0.buf = NULL;
_dcd.pipe0.length = 0;
_dcd.pipe0.remaining = 0;
/* Clear RX FIFO and reverse the transaction direction */
- USB0->CSRL0 = USB_CSRL0_RXRDYC | USB_CSRL0_DATAEND;
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND;
}
return true;
}
static void process_ep0(uint8_t rhport)
{
- uint_fast8_t csrl = USB0->CSRL0;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
+ uint_fast8_t csrl = ep_csr->csr0l;
- // TU_LOG1(" EP0 USB0->CSRL0 = %x\n", csrl);
+ // TU_LOG1(" EP0 ep_csr->csr0l = %x\r\n", csrl);
+ // 21.1.5: endpoint 0 service routine as peripheral
- if (csrl & USB_CSRL0_STALLED) {
+ if (csrl & MUSB_CSRL0_STALLED) {
/* Returned STALL packet to HOST. */
- USB0->CSRL0 = 0; /* Clear STALL */
+ ep_csr->csr0l = 0; /* Clear STALL */
return;
}
unsigned req = _dcd.setup_packet.bmRequestType;
- if (csrl & USB_CSRL0_SETEND) {
- TU_LOG1(" ABORT by the next packets\n");
- USB0->CSRL0 = USB_CSRL0_SETENDC;
+ if (csrl & MUSB_CSRL0_SETEND) {
+ TU_LOG1(" ABORT by the next packets\r\n");
+ ep_csr->csr0l = MUSB_CSRL0_SETENDC;
if (req != REQUEST_TYPE_INVALID && _dcd.pipe0.buf) {
/* DATA stage was aborted by receiving STATUS or SETUP packet. */
_dcd.pipe0.buf = NULL;
@@ -476,23 +441,24 @@ static void process_ep0(uint8_t rhport)
XFER_RESULT_SUCCESS, true);
}
req = REQUEST_TYPE_INVALID;
- if (!(csrl & USB_CSRL0_RXRDY)) return; /* Received SETUP packet */
+ if (!(csrl & MUSB_CSRL0_RXRDY)) return; /* Received SETUP packet */
}
- if (csrl & USB_CSRL0_RXRDY) {
+ if (csrl & MUSB_CSRL0_RXRDY) {
/* Received SETUP or DATA OUT packet */
if (req == REQUEST_TYPE_INVALID) {
/* SETUP */
- TU_ASSERT(sizeof(tusb_control_request_t) == USB0->COUNT0,);
+ TU_ASSERT(sizeof(tusb_control_request_t) == ep_csr->count0,);
process_setup_packet(rhport);
return;
}
if (_dcd.pipe0.buf) {
/* DATA OUT */
- const unsigned vld = USB0->COUNT0;
+ const unsigned vld = ep_csr->count0;
const unsigned rem = _dcd.pipe0.remaining;
const unsigned len = TU_MIN(TU_MIN(rem, 64), vld);
- pipe_read_packet(_dcd.pipe0.buf, &USB0->FIFO0_WORD, len);
+ volatile void *fifo_ptr = &musb_regs->fifo[0];
+ pipe_read_packet(_dcd.pipe0.buf, fifo_ptr, len);
_dcd.pipe0.remaining = rem - len;
_dcd.remaining_ctrl -= len;
@@ -512,7 +478,7 @@ static void process_ep0(uint8_t rhport)
/* STATUS IN */
if (*(const uint16_t*)(uintptr_t)&_dcd.setup_packet == 0x0500) {
/* The address must be changed on completion of the control transfer. */
- USB0->FADDR = (uint8_t)_dcd.setup_packet.wValue;
+ musb_regs->faddr = (uint8_t)_dcd.setup_packet.wValue;
}
_dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
dcd_event_xfer_complete(rhport,
@@ -534,118 +500,149 @@ static void process_ep0(uint8_t rhport)
static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr)
{
bool completed;
- const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned epn_minus1 = tu_edpt_number(ep_addr) - 1;
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned epn_minus1 = epn - 1;
- volatile hw_endpoint_t *regs = edpt_regs(epn_minus1);
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn);
if (dir_in) {
- // TU_LOG1(" TXCSRL%d = %x\n", epn_minus1 + 1, regs->TXCSRL);
- if (regs->TXCSRL & USB_TXCSRL1_STALLED) {
- regs->TXCSRL &= ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_UNDRN);
+ // TU_LOG1(" TX CSRL%d = %x\r\n", epn, ep_csr->tx_csrl);
+ if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) {
+ ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN);
return;
}
- completed = handle_xfer_in(ep_addr);
+ completed = handle_xfer_in(rhport, ep_addr);
} else {
- // TU_LOG1(" RXCSRL%d = %x\n", epn_minus1 + 1, regs->RXCSRL);
- if (regs->RXCSRL & USB_RXCSRL1_STALLED) {
- regs->RXCSRL &= ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_OVER);
+ // TU_LOG1(" RX CSRL%d = %x\r\n", epn, ep_csr->rx_csrl);
+ if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) {
+ ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER);
return;
}
- completed = handle_xfer_out(ep_addr);
+ completed = handle_xfer_out(rhport, ep_addr);
}
if (completed) {
- pipe_state_t *pipe = &_dcd.pipe[dir_in][tu_edpt_number(ep_addr) - 1];
+ pipe_state_t *pipe = &_dcd.pipe[dir_in][epn_minus1];
dcd_event_xfer_complete(rhport, ep_addr,
pipe->length - pipe->remaining,
XFER_RESULT_SUCCESS, true);
}
}
-static void process_bus_reset(uint8_t rhport)
-{
- /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF),
- * a control transfer state is SETUP or STATUS stage. */
+// Upon BUS RESET is detected, hardware havs already done:
+// faddr = 0, index = 0, flushes all ep fifos, clears all ep csr, enabled all ep interrupts
+static void process_bus_reset(uint8_t rhport) {
+ musb_regs_t* musb = MUSB_REGS(rhport);
+
+#if MUSB_CFG_DYNAMIC_FIFO
+ alloced_fifo_bytes = CFG_TUD_ENDPOINT0_SIZE;
+#endif
+
+ /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF), a control transfer state is SETUP or STATUS stage. */
_dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
_dcd.status_out = 0;
/* When pipe0.buf has not NULL, DATA stage works in progress. */
_dcd.pipe0.buf = NULL;
- USB0->TXIE = 1; /* Enable only EP0 */
- USB0->RXIE = 0;
+ musb->intr_txen = 1; /* Enable only EP0 */
+ musb->intr_rxen = 0;
/* Clear FIFO settings */
for (unsigned i = 1; i < TUP_DCD_ENDPOINT_MAX; ++i) {
- USB0->EPIDX = i;
- USB0->TXFIFOSZ = 0;
- USB0->TXFIFOADD = 0;
- USB0->RXFIFOSZ = 0;
- USB0->RXFIFOADD = 0;
+ musb->index = i;
+ hwfifo_reset(musb, i, 0);
+ hwfifo_reset(musb, i, 1);
}
- dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+ dcd_event_bus_reset(rhport, (musb->power & MUSB_POWER_HSMODE) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL, true);
}
/*------------------------------------------------------------------
* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
- (void)rhport;
- USB0->IE |= USB_IE_SUSPND;
- NVIC_ClearPendingIRQ(USB0_IRQn);
+#if CFG_TUSB_DEBUG >= MUSB_DEBUG
+void print_musb_info(musb_regs_t* musb_regs) {
+ // print version, epinfo, raminfo, config_data0, fifo_size
+ TU_LOG1("musb version = %u.%u\r\n", musb_regs->hwvers_bit.major, musb_regs->hwvers_bit.minor);
+ TU_LOG1("Number of endpoints: %u TX, %u RX\r\n", musb_regs->epinfo_bit.tx_ep_num, musb_regs->epinfo_bit.rx_ep_num);
+ TU_LOG1("RAM Info: %u DMA Channel, %u RAM address width\r\n", musb_regs->raminfo_bit.dma_channel, musb_regs->raminfo_bit.ram_bits);
+
+ musb_regs->index = 0;
+ TU_LOG1("config_data0 = 0x%x\r\n", musb_regs->indexed_csr.config_data0);
+#if MUSB_CFG_DYNAMIC_FIFO
+ TU_LOG1("Dynamic FIFO configuration\r\n");
+#else
+ for (uint8_t i=1; i <= musb_regs->epinfo_bit.tx_ep_num; i++) {
+ musb_regs->index = i;
+ TU_LOG1("FIFO %u Size: TX %u RX %u\r\n", i, musb_regs->indexed_csr.fifo_size_bit.tx, musb_regs->indexed_csr.fifo_size_bit.rx);
+ }
+#endif
+}
+#endif
+
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+
+#if CFG_TUSB_DEBUG >= MUSB_DEBUG
+ print_musb_info(musb_regs);
+#endif
+
+ musb_regs->intr_usben |= MUSB_IE_SUSPND;
+ musb_dcd_int_clear(rhport);
+ musb_dcd_phy_init(rhport);
dcd_connect(rhport);
+ return true;
}
-void dcd_int_enable(uint8_t rhport)
-{
- (void)rhport;
- NVIC_EnableIRQ(USB0_IRQn);
+void dcd_int_enable(uint8_t rhport) {
+ musb_dcd_int_enable(rhport);
}
-void dcd_int_disable(uint8_t rhport)
-{
- (void)rhport;
- NVIC_DisableIRQ(USB0_IRQn);
+void dcd_int_disable(uint8_t rhport) {
+ musb_dcd_int_disable(rhport);
}
// Receive Set Address request, mcu port must also include status IN response
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
- (void)rhport;
(void)dev_addr;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0);
+
_dcd.pipe0.buf = NULL;
_dcd.pipe0.length = 0;
_dcd.pipe0.remaining = 0;
/* Clear RX FIFO to return ACK. */
- USB0->CSRL0 = USB_CSRL0_RXRDYC | USB_CSRL0_DATAEND;
+ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND;
}
// Wake up host
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void)rhport;
- USB0->POWER |= USB_POWER_RESUME;
+void dcd_remote_wakeup(uint8_t rhport) {
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_regs->power |= MUSB_POWER_RESUME;
unsigned cnt = SystemCoreClock / 1000;
while (cnt--) __NOP();
- USB0->POWER &= ~USB_POWER_RESUME;
+ musb_regs->power &= ~MUSB_POWER_RESUME;
}
// Connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
- (void)rhport;
- USB0->POWER |= USB_POWER_SOFTCONN;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_regs->power |= TUD_OPT_HIGH_SPEED ? MUSB_POWER_HSENAB : 0;
+ musb_regs->power |= MUSB_POWER_SOFTCONN;
}
// Disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
- (void)rhport;
- USB0->POWER &= ~USB_POWER_SOFTCONN;
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_regs->power &= ~MUSB_POWER_SOFTCONN;
}
void dcd_sof_enable(uint8_t rhport, bool en)
@@ -659,129 +656,119 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
+// static void edpt_setup(musb_regs_t* musb, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_size){
+// const unsigned epn = tu_edpt_number(ep_addr);
+// const unsigned dir_in = tu_edpt_dir(ep_addr);
+// }
// Configure endpoint's registers according to descriptor
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
- (void) rhport;
-
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
const unsigned ep_addr = ep_desc->bEndpointAddress;
const unsigned epn = tu_edpt_number(ep_addr);
const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned xfer = ep_desc->bmAttributes.xfer;
const unsigned mps = tu_edpt_packet_size(ep_desc);
- TU_ASSERT(epn < TUP_DCD_ENDPOINT_MAX);
-
pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1];
pipe->buf = NULL;
pipe->length = 0;
pipe->remaining = 0;
- volatile hw_endpoint_t *regs = edpt_regs(epn - 1);
+ musb_regs_t* musb = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
+ const uint8_t is_rx = 1 - dir_in;
+ musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx];
+
+ maxp_csr->maxp = mps;
+ maxp_csr->csrh = 0;
+#if MUSB_CFG_SHARED_FIFO
if (dir_in) {
- regs->TXMAXP = mps;
- regs->TXCSRH = (xfer == TUSB_XFER_ISOCHRONOUS) ? USB_TXCSRH1_ISO : 0;
- if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
- regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
- else
- regs->TXCSRL = USB_TXCSRL1_CLRDT;
- USB0->TXIE |= TU_BIT(epn);
- } else {
- regs->RXMAXP = mps;
- regs->RXCSRH = (xfer == TUSB_XFER_ISOCHRONOUS) ? USB_RXCSRH1_ISO : 0;
- if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
- regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
- else
- regs->RXCSRL = USB_RXCSRL1_CLRDT;
- USB0->RXIE |= TU_BIT(epn);
+ maxp_csr->csrh |= MUSB_CSRH_TX_MODE;
}
+#endif
- /* Setup FIFO */
- int size_in_log2_minus3 = 28 - TU_MIN(28, __CLZ((uint32_t)mps));
- if ((8u << size_in_log2_minus3) < mps) ++size_in_log2_minus3;
- unsigned addr = find_free_memory(size_in_log2_minus3);
- TU_ASSERT(addr);
+ hwfifo_flush(musb, epn, is_rx, true);
- USB0->EPIDX = epn;
- if (dir_in) {
- USB0->TXFIFOADD = addr;
- USB0->TXFIFOSZ = size_in_log2_minus3;
- } else {
- USB0->RXFIFOADD = addr;
- USB0->RXFIFOSZ = size_in_log2_minus3;
- }
+ TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, false));
+ musb->intren_ep[is_rx] |= TU_BIT(epn);
return true;
}
-void dcd_edpt_close_all(uint8_t rhport)
-{
- (void) rhport;
- volatile hw_endpoint_t *regs = (volatile hw_endpoint_t *)(uintptr_t)&USB0->TXMAXP1;
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- USB0->TXIE = 1; /* Enable only EP0 */
- USB0->RXIE = 0;
- for (unsigned i = 1; i < TUP_DCD_ENDPOINT_MAX; ++i) {
- regs->TXMAXP = 0;
- regs->TXCSRH = 0;
- if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
- regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
- else
- regs->TXCSRL = USB_TXCSRL1_CLRDT;
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ musb_regs_t* musb = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
+ const uint8_t is_rx = 1 - dir_in;
+ ep_csr->maxp_csr[is_rx].csrh = 0;
+ return hwfifo_config(musb, epn, is_rx, largest_packet_size, true);
+}
- regs->RXMAXP = 0;
- regs->RXCSRH = 0;
- if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
- regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
- else
- regs->RXCSRL = USB_RXCSRL1_CLRDT;
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc ) {
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned mps = tu_edpt_packet_size(ep_desc);
- USB0->EPIDX = i;
- USB0->TXFIFOSZ = 0;
- USB0->TXFIFOADD = 0;
- USB0->RXFIFOSZ = 0;
- USB0->RXFIFOADD = 0;
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
+
+ pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1];
+ pipe->buf = NULL;
+ pipe->length = 0;
+ pipe->remaining = 0;
+
+ musb_regs_t* musb = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn);
+ const uint8_t is_rx = 1 - dir_in;
+ musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx];
+
+ maxp_csr->maxp = mps;
+ maxp_csr->csrh |= MUSB_CSRH_ISO;
+#if MUSB_CFG_SHARED_FIFO
+ if (dir_in) {
+ maxp_csr->csrh |= MUSB_CSRH_TX_MODE;
}
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+#endif
+
+ hwfifo_flush(musb, epn, is_rx, true);
+
+#if MUSB_CFG_DYNAMIC_FIFO
+ // fifo space is already allocated, keep the address and just change packet size
+ musb->fifo_size[is_rx] = hwfifo_byte2size(mps) | MUSB_FIFOSZ_DOUBLE_PACKET;
+#endif
+
+ musb->intren_ep[is_rx] |= TU_BIT(epn);
+
+ if (ie) musb_dcd_int_enable(rhport);
+
+ return true;
}
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+void dcd_edpt_close_all(uint8_t rhport)
{
- (void)rhport;
- unsigned const epn = tu_edpt_number(ep_addr);
- unsigned const dir_in = tu_edpt_dir(ep_addr);
+ musb_regs_t* musb = MUSB_REGS(rhport);
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
- hw_endpoint_t volatile *regs = edpt_regs(epn - 1);
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- if (dir_in) {
- USB0->TXIE &= ~TU_BIT(epn);
- regs->TXMAXP = 0;
- regs->TXCSRH = 0;
- if (regs->TXCSRL & USB_TXCSRL1_TXRDY)
- regs->TXCSRL = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
- else
- regs->TXCSRL = USB_TXCSRL1_CLRDT;
+ musb->intr_txen = 1; /* Enable only EP0 */
+ musb->intr_rxen = 0;
+ for (unsigned i = 1; i < TUP_DCD_ENDPOINT_MAX; ++i) {
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb, i);
+ for (unsigned d = 0; d < 2; d++) {
+ musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[d];
+ hwfifo_flush(musb, i, d, true);
+ hwfifo_reset(musb, i, d);
+ maxp_csr->maxp = 0;
+ maxp_csr->csrh = 0;
+ }
+ }
- USB0->EPIDX = epn;
- USB0->TXFIFOSZ = 0;
- USB0->TXFIFOADD = 0;
- } else {
- USB0->RXIE &= ~TU_BIT(epn);
- regs->RXMAXP = 0;
- regs->RXCSRH = 0;
- if (regs->RXCSRL & USB_RXCSRL1_RXRDY)
- regs->RXCSRL = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
- else
- regs->RXCSRL = USB_RXCSRL1_CLRDT;
+#if MUSB_CFG_DYNAMIC_FIFO
+ alloced_fifo_bytes = CFG_TUD_ENDPOINT0_SIZE;
+#endif
- USB0->EPIDX = epn;
- USB0->RXFIFOSZ = 0;
- USB0->RXFIFOADD = 0;
- }
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ if (ie) musb_dcd_int_enable(rhport);
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
@@ -789,120 +776,129 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
{
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\n", ep_addr, total_bytes);
+ // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
+
if (epnum) {
_dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] &= ~TU_BIT(epnum - 1);
ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes);
- } else
+ } else {
ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes);
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ }
+
+ if (ie) musb_dcd_int_enable(rhport);
return ret;
}
-// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
+// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack
+// - optional, however, must be listed in usbd.c
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\n", ep_addr, total_bytes);
+ // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
TU_ASSERT(epnum);
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
_dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] |= TU_BIT(epnum - 1);
ret = edpt_n_xfer(rhport, ep_addr, (uint8_t*)ff, total_bytes);
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ if (ie) musb_dcd_int_enable(rhport);
return ret;
}
// Stall endpoint
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- (void)rhport;
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
+
unsigned const epn = tu_edpt_number(ep_addr);
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn);
+
if (0 == epn) {
if (!ep_addr) { /* Ignore EP80 */
_dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID;
_dcd.pipe0.buf = NULL;
- USB0->CSRL0 = USB_CSRL0_STALL;
+ ep_csr->csr0l = MUSB_CSRL0_STALL;
}
} else {
- volatile hw_endpoint_t *regs = edpt_regs(epn - 1);
- if (tu_edpt_dir(ep_addr)) { /* IN */
- regs->TXCSRL = USB_TXCSRL1_STALL;
- } else { /* OUT */
- TU_ASSERT(!(regs->RXCSRL & USB_RXCSRL1_RXRDY),);
- regs->RXCSRL = USB_RXCSRL1_STALL;
- }
+ const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr);
+ ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_SEND_STALL(is_rx);
}
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+
+ if (ie) musb_dcd_int_enable(rhport);
}
// clear stall, data toggle is also reset to DATA0
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
+ unsigned const ie = musb_dcd_get_int_enable(rhport);
+ musb_dcd_int_disable(rhport);
+
unsigned const epn = tu_edpt_number(ep_addr);
- hw_endpoint_t volatile *regs = edpt_regs(epn - 1);
- unsigned const ie = NVIC_GetEnableIRQ(USB0_IRQn);
- NVIC_DisableIRQ(USB0_IRQn);
- if (tu_edpt_dir(ep_addr)) { /* IN */
- regs->TXCSRL = USB_TXCSRL1_CLRDT;
- } else { /* OUT */
- regs->RXCSRL = USB_RXCSRL1_CLRDT;
- }
- if (ie) NVIC_EnableIRQ(USB0_IRQn);
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn);
+ const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr);
+
+ ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx);
+
+ if (ie) musb_dcd_int_enable(rhport);
}
/*-------------------------------------------------------------------
* ISR
*-------------------------------------------------------------------*/
-void dcd_int_handler(uint8_t rhport)
-{
- uint_fast8_t is, txis, rxis;
+void dcd_int_handler(uint8_t rhport) {
+ musb_regs_t* musb_regs = MUSB_REGS(rhport);
+ const uint8_t saved_index = musb_regs->index; // save endpoint index
+
+ //Part specific ISR setup/entry
+ musb_dcd_int_handler_enter(rhport);
- is = USB0->IS; /* read and clear interrupt status */
- txis = USB0->TXIS; /* read and clear interrupt status */
- rxis = USB0->RXIS; /* read and clear interrupt status */
- // TU_LOG1("D%2x T%2x R%2x\n", is, txis, rxis);
+ uint_fast8_t intr_usb = musb_regs->intr_usb; // a read will clear this interrupt status
+ uint_fast8_t intr_tx = musb_regs->intr_tx; // a read will clear this interrupt status
+ uint_fast8_t intr_rx = musb_regs->intr_rx; // a read will clear this interrupt status
+ // TU_LOG1("D%2x T%2x R%2x\r\n", is, txis, rxis);
- is &= USB0->IE; /* Clear disabled interrupts */
- if (is & USB_IS_DISCON) {
+ intr_usb &= musb_regs->intr_usben; /* Clear disabled interrupts */
+ if (intr_usb & MUSB_IS_DISCON) {
}
- if (is & USB_IS_SOF) {
+ if (intr_usb & MUSB_IS_SOF) {
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
- if (is & USB_IS_RESET) {
+ if (intr_usb & MUSB_IS_RESET) {
process_bus_reset(rhport);
}
- if (is & USB_IS_RESUME) {
+ if (intr_usb & MUSB_IS_RESUME) {
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
- if (is & USB_IS_SUSPEND) {
+ if (intr_usb & MUSB_IS_SUSPEND) {
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
- txis &= USB0->TXIE; /* Clear disabled interrupts */
- if (txis & USB_TXIE_EP0) {
+ intr_tx &= musb_regs->intr_txen; /* Clear disabled interrupts */
+ if (intr_tx & TU_BIT(0)) {
process_ep0(rhport);
- txis &= ~TU_BIT(0);
+ intr_tx &= ~TU_BIT(0);
}
- while (txis) {
- unsigned const num = __builtin_ctz(txis);
+ while (intr_tx) {
+ unsigned const num = __builtin_ctz(intr_tx);
process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_IN));
- txis &= ~TU_BIT(num);
+ intr_tx &= ~TU_BIT(num);
}
- rxis &= USB0->RXIE; /* Clear disabled interrupts */
- while (rxis) {
- unsigned const num = __builtin_ctz(rxis);
+
+ intr_rx &= musb_regs->intr_rxen; /* Clear disabled interrupts */
+ while (intr_rx) {
+ unsigned const num = __builtin_ctz(intr_rx);
process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_OUT));
- rxis &= ~TU_BIT(num);
+ intr_rx &= ~TU_BIT(num);
}
+
+ musb_regs->index = saved_index; // restore endpoint index
}
#endif
diff --git a/src/portable/mentor/musb/hcd_musb.c b/src/portable/mentor/musb/hcd_musb.c
index 85e18e3aa..1c0740193 100644
--- a/src/portable/mentor/musb/hcd_musb.c
+++ b/src/portable/mentor/musb/hcd_musb.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Koji KITAYAMA
@@ -37,16 +37,10 @@ _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"");
#include "host/hcd.h"
-#if TU_CHECK_MCU(OPT_MCU_MSP432E4)
- #include "musb_msp432e.h"
-
-#elif TU_CHECK_MCU(OPT_MCU_TM4C123, OPT_MCU_TM4C129)
- #include "musb_tm4c.h"
-
- // HACK generalize later
- #include "musb_type.h"
- #define FIFO0_WORD FIFO0
+#include "musb_type.h"
+#if TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
+ #include "musb_ti.h"
#else
#error "Unsupported MCUs"
#endif
@@ -418,7 +412,7 @@ static void process_ep0(uint8_t rhport)
(void)rhport;
uint_fast8_t csrl = USB0->CSRL0;
- // TU_LOG1(" EP0 CSRL = %x\n", csrl);
+ // TU_LOG1(" EP0 CSRL = %x\r\n", csrl);
unsigned const dev_addr = USB0->TXFUNCADDR0;
unsigned const req = _hcd.bmRequestType;
@@ -508,7 +502,7 @@ static void process_pipe_tx(uint8_t rhport, uint_fast8_t pipenum)
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
unsigned const csrl = regs->TXCSRL;
- // TU_LOG1(" TXCSRL%d = %x\n", pipenum, csrl);
+ // TU_LOG1(" TXCSRL%d = %x\r\n", pipenum, csrl);
if (csrl & (USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) {
if (csrl & USB_TXCSRL1_TXRDY)
regs->TXCSRL = (csrl & ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_ERROR)) | USB_TXCSRL1_FLUSH;
@@ -537,7 +531,7 @@ static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
volatile hw_endpoint_t *regs = edpt_regs(pipenum - 1);
unsigned const csrl = regs->RXCSRL;
- // TU_LOG1(" RXCSRL%d = %x\n", pipenum, csrl);
+ // TU_LOG1(" RXCSRL%d = %x\r\n", pipenum, csrl);
if (csrl & (USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) {
if (csrl & USB_RXCSRL1_RXRDY)
regs->RXCSRL = (csrl & ~(USB_RXCSRL1_STALLED | USB_RXCSRL1_ERROR)) | USB_RXCSRL1_FLUSH;
@@ -562,9 +556,9 @@ static void process_pipe_rx(uint8_t rhport, uint_fast8_t pipenum)
* Host API
*------------------------------------------------------------------*/
-bool hcd_init(uint8_t rhport)
-{
- (void)rhport;
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
NVIC_ClearPendingIRQ(USB0_IRQn);
_hcd.bmRequestType = REQUEST_TYPE_INVALID;
@@ -588,7 +582,7 @@ void hcd_int_disable(uint8_t rhport)
uint32_t hcd_frame_number(uint8_t rhport)
{
(void)rhport;
- /* The device must be reset at least once after connection
+ /* The device must be reset at least once after connection
* in order to start the frame counter. */
if (_hcd.need_reset) hcd_port_reset(rhport);
return USB0->FRAME;
@@ -822,9 +816,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
return ret;
}
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ // TODO not implemented yet
+ return false;
+}
+
// clear stall, data toggle is also reset to DATA0
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
unsigned const pipenum = find_pipe(dev_addr, ep_addr);
if (!pipenum) return false;
hw_endpoint_t volatile *regs = edpt_regs(pipenum - 1);
@@ -839,14 +841,16 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
/*-------------------------------------------------------------------
* ISR
*-------------------------------------------------------------------*/
-void hcd_int_handler(uint8_t rhport)
+void hcd_int_handler(uint8_t rhport, bool in_isr)
{
+ (void) in_isr;
+
uint_fast8_t is, txis, rxis;
is = USB0->IS; /* read and clear interrupt status */
txis = USB0->TXIS; /* read and clear interrupt status */
rxis = USB0->RXIS; /* read and clear interrupt status */
- // TU_LOG1("D%2x T%2x R%2x\n", is, txis, rxis);
+ // TU_LOG1("D%2x T%2x R%2x\r\n", is, txis, rxis);
is &= USB0->IE; /* Clear disabled interrupts */
if (is & USB_IS_RESUME) {
diff --git a/src/portable/mentor/musb/musb_max32.h b/src/portable/mentor/musb/musb_max32.h
new file mode 100644
index 000000000..35849b5f8
--- /dev/null
+++ b/src/portable/mentor/musb/musb_max32.h
@@ -0,0 +1,150 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024, Brent Kowal (Analog Devices, Inc)
+ *
+ * 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_MUSB_MAX32_H_
+#define TUSB_MUSB_MAX32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mxc_device.h"
+#include "usbhs_regs.h"
+
+#define MUSB_CFG_SHARED_FIFO 1 // shared FIFO for TX and RX endpoints
+#define MUSB_CFG_DYNAMIC_FIFO 0 // dynamic EP FIFO sizing
+
+const uintptr_t MUSB_BASES[] = { MXC_BASE_USBHS };
+
+#if CFG_TUD_ENABLED
+#define USBHS_M31_CLOCK_RECOVERY
+
+// Mapping of IRQ numbers to port. Currently just 1.
+static const IRQn_Type musb_irqs[] = {
+ USB_IRQn
+};
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_enable(uint8_t rhport) {
+ NVIC_EnableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline unsigned musb_dcd_get_int_enable(uint8_t rhport) {
+ #ifdef NVIC_GetEnableIRQ // only defined in CMSIS 5
+ return NVIC_GetEnableIRQ(musb_irqs[rhport]);
+ #else
+ uint32_t IRQn = (uint32_t) musb_irqs[rhport];
+ return ((NVIC->ISER[IRQn >> 5UL] & (1UL << (IRQn & 0x1FUL))) != 0UL) ? 1UL : 0UL;
+ #endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_clear(uint8_t rhport) {
+ NVIC_ClearPendingIRQ(musb_irqs[rhport]);
+}
+
+static inline void musb_dcd_int_handler_enter(uint8_t rhport) {
+ mxc_usbhs_regs_t* hs_phy = MXC_USBHS;
+ uint32_t mxm_int, mxm_int_en, mxm_is;
+
+ //Handle PHY specific events
+ mxm_int = hs_phy->mxm_int;
+ mxm_int_en = hs_phy->mxm_int_en;
+ mxm_is = mxm_int & mxm_int_en;
+ hs_phy->mxm_int = mxm_is;
+
+ if (mxm_is & MXC_F_USBHS_MXM_INT_NOVBUS) {
+ dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
+ }
+}
+
+static inline void musb_dcd_phy_init(uint8_t rhport) {
+ (void) rhport;
+ mxc_usbhs_regs_t* hs_phy = MXC_USBHS;
+
+ // Interrupt for VBUS disconnect
+ hs_phy->mxm_int_en |= MXC_F_USBHS_MXM_INT_EN_NOVBUS;
+
+ musb_dcd_int_clear(rhport);
+
+ // Unsuspend the MAC
+ hs_phy->mxm_suspend = 0;
+
+ // Configure PHY
+ hs_phy->m31_phy_xcfgi_31_0 = (0x1 << 3) | (0x1 << 11);
+ hs_phy->m31_phy_xcfgi_63_32 = 0;
+ hs_phy->m31_phy_xcfgi_95_64 = 0x1 << (72 - 64);
+ hs_phy->m31_phy_xcfgi_127_96 = 0;
+
+ #ifdef USBHS_M31_CLOCK_RECOVERY
+ hs_phy->m31_phy_noncry_rstb = 1;
+ hs_phy->m31_phy_noncry_en = 1;
+ hs_phy->m31_phy_outclksel = 0;
+ hs_phy->m31_phy_coreclkin = 0;
+ hs_phy->m31_phy_xtlsel = 2; /* Select 25 MHz clock */
+ #else
+ hs_phy->m31_phy_noncry_rstb = 0;
+ hs_phy->m31_phy_noncry_en = 0;
+ hs_phy->m31_phy_outclksel = 1;
+ hs_phy->m31_phy_coreclkin = 1;
+ hs_phy->m31_phy_xtlsel = 3; /* Select 30 MHz clock */
+ #endif
+ hs_phy->m31_phy_pll_en = 1;
+ hs_phy->m31_phy_oscouten = 1;
+
+ /* Reset PHY */
+ hs_phy->m31_phy_ponrst = 0;
+ hs_phy->m31_phy_ponrst = 1;
+}
+
+// static inline void musb_dcd_setup_fifo(uint8_t rhport, unsigned epnum, unsigned dir_in, unsigned mps) {
+// (void) mps;
+//
+// //Most likely the caller has already grabbed the right register block. But
+// //as a precaution save and restore the register bank anyways
+// unsigned saved_index = musb_periph_inst[rhport]->index;
+//
+// musb_periph_inst[rhport]->index = epnum;
+//
+// //Disable double buffering
+// if (dir_in) {
+// musb_periph_inst[rhport]->incsru |= (MXC_F_USBHS_INCSRU_DPKTBUFDIS | MXC_F_USBHS_INCSRU_MODE);
+// } else {
+// musb_periph_inst[rhport]->outcsru |= (MXC_F_USBHS_OUTCSRU_DPKTBUFDIS);
+// }
+//
+// musb_periph_inst[rhport]->index = saved_index;
+// }
+
+#endif // CFG_TUD_ENABLED
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TUSB_MUSB_MAX32_H_
diff --git a/src/portable/mentor/musb/musb_ti.h b/src/portable/mentor/musb/musb_ti.h
new file mode 100644
index 000000000..d17e836ee
--- /dev/null
+++ b/src/portable/mentor/musb/musb_ti.h
@@ -0,0 +1,91 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024, Brent Kowal (Analog Devices, Inc)
+ *
+ * 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_MUSB_TI_H_
+#define TUSB_MUSB_TI_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_TM4C123
+ #include "TM4C123.h"
+ #define FIFO0_WORD FIFO0
+ #define FIFO1_WORD FIFO1
+//#elif CFG_TUSB_MCU == OPT_MCU_TM4C129
+#elif CFG_TUSB_MCU == OPT_MCU_MSP432E4
+ #include "msp.h"
+#else
+ #error "Unsupported MCUs"
+#endif
+
+#define MUSB_CFG_SHARED_FIFO 0
+#define MUSB_CFG_DYNAMIC_FIFO 1
+#define MUSB_CFG_DYNAMIC_FIFO_SIZE 4096
+
+const uintptr_t MUSB_BASES[] = { USB0_BASE };
+
+// Header supports both device and host modes. Only include what's necessary
+#if CFG_TUD_ENABLED
+
+// Mapping of IRQ numbers to port. Currently just 1.
+static const IRQn_Type musb_irqs[] = {
+ USB0_IRQn
+};
+
+static inline void musb_dcd_phy_init(uint8_t rhport){
+ (void)rhport;
+ //Nothing to do for this part
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_enable(uint8_t rhport) {
+ NVIC_EnableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline unsigned musb_dcd_get_int_enable(uint8_t rhport) {
+ return NVIC_GetEnableIRQ(musb_irqs[rhport]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void musb_dcd_int_clear(uint8_t rhport) {
+ NVIC_ClearPendingIRQ(musb_irqs[rhport]);
+}
+
+static inline void musb_dcd_int_handler_enter(uint8_t rhport) {
+ (void)rhport;
+ //Nothing to do for this part
+}
+
+#endif // CFG_TUD_ENABLED
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // TUSB_MUSB_TI_H_
diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h
index 8f83305a5..4e448c0ed 100644
--- a/src/portable/mentor/musb/musb_type.h
+++ b/src/portable/mentor/musb/musb_type.h
@@ -1,3 +1,29 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
/******************************************************************************
*
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
@@ -32,2590 +58,696 @@
*
*******************************************************************************/
-#ifndef _TUSB_MUSB_TYPE_H_
-#define _TUSB_MUSB_TYPE_H_
+#ifndef TUSB_MUSB_TYPE_H_
+#define TUSB_MUSB_TYPE_H_
+
+#include "stdint.h"
#ifdef __cplusplus
extern "C" {
#endif
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FADDR register.
-//
-//*****************************************************************************
-#define USB_FADDR_M 0x0000007F // Function Address
-#define USB_FADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_POWER register.
-//
-//*****************************************************************************
-#define USB_POWER_ISOUP 0x00000080 // Isochronous Update
-#define USB_POWER_SOFTCONN 0x00000040 // Soft Connect/Disconnect
-#define USB_POWER_HSENAB 0x00000020 // High Speed Enable
-#define USB_POWER_HSMODE 0x00000010 // High Speed Enable
-#define USB_POWER_RESET 0x00000008 // RESET Signaling
-#define USB_POWER_RESUME 0x00000004 // RESUME Signaling
-#define USB_POWER_SUSPEND 0x00000002 // SUSPEND Mode
-#define USB_POWER_PWRDNPHY 0x00000001 // Power Down PHY
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXIS register.
-//
-//*****************************************************************************
-#define USB_TXIS_EP7 0x00000080 // TX Endpoint 7 Interrupt
-#define USB_TXIS_EP6 0x00000040 // TX Endpoint 6 Interrupt
-#define USB_TXIS_EP5 0x00000020 // TX Endpoint 5 Interrupt
-#define USB_TXIS_EP4 0x00000010 // TX Endpoint 4 Interrupt
-#define USB_TXIS_EP3 0x00000008 // TX Endpoint 3 Interrupt
-#define USB_TXIS_EP2 0x00000004 // TX Endpoint 2 Interrupt
-#define USB_TXIS_EP1 0x00000002 // TX Endpoint 1 Interrupt
-#define USB_TXIS_EP0 0x00000001 // TX and RX Endpoint 0 Interrupt
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXIS register.
-//
-//*****************************************************************************
-#define USB_RXIS_EP7 0x00000080 // RX Endpoint 7 Interrupt
-#define USB_RXIS_EP6 0x00000040 // RX Endpoint 6 Interrupt
-#define USB_RXIS_EP5 0x00000020 // RX Endpoint 5 Interrupt
-#define USB_RXIS_EP4 0x00000010 // RX Endpoint 4 Interrupt
-#define USB_RXIS_EP3 0x00000008 // RX Endpoint 3 Interrupt
-#define USB_RXIS_EP2 0x00000004 // RX Endpoint 2 Interrupt
-#define USB_RXIS_EP1 0x00000002 // RX Endpoint 1 Interrupt
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXIE register.
-//
-//*****************************************************************************
-#define USB_TXIE_EP7 0x00000080 // TX Endpoint 7 Interrupt Enable
-#define USB_TXIE_EP6 0x00000040 // TX Endpoint 6 Interrupt Enable
-#define USB_TXIE_EP5 0x00000020 // TX Endpoint 5 Interrupt Enable
-#define USB_TXIE_EP4 0x00000010 // TX Endpoint 4 Interrupt Enable
-#define USB_TXIE_EP3 0x00000008 // TX Endpoint 3 Interrupt Enable
-#define USB_TXIE_EP2 0x00000004 // TX Endpoint 2 Interrupt Enable
-#define USB_TXIE_EP1 0x00000002 // TX Endpoint 1 Interrupt Enable
-#define USB_TXIE_EP0 0x00000001 // TX and RX Endpoint 0 Interrupt
- // Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXIE register.
-//
-//*****************************************************************************
-#define USB_RXIE_EP7 0x00000080 // RX Endpoint 7 Interrupt Enable
-#define USB_RXIE_EP6 0x00000040 // RX Endpoint 6 Interrupt Enable
-#define USB_RXIE_EP5 0x00000020 // RX Endpoint 5 Interrupt Enable
-#define USB_RXIE_EP4 0x00000010 // RX Endpoint 4 Interrupt Enable
-#define USB_RXIE_EP3 0x00000008 // RX Endpoint 3 Interrupt Enable
-#define USB_RXIE_EP2 0x00000004 // RX Endpoint 2 Interrupt Enable
-#define USB_RXIE_EP1 0x00000002 // RX Endpoint 1 Interrupt Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_IS register.
-//
-//*****************************************************************************
-#define USB_IS_VBUSERR 0x00000080 // VBUS Error (OTG only)
-#define USB_IS_SESREQ 0x00000040 // SESSION REQUEST (OTG only)
-#define USB_IS_DISCON 0x00000020 // Session Disconnect (OTG only)
-#define USB_IS_CONN 0x00000010 // Session Connect
-#define USB_IS_SOF 0x00000008 // Start of Frame
-#define USB_IS_BABBLE 0x00000004 // Babble Detected
-#define USB_IS_RESET 0x00000004 // RESET Signaling Detected
-#define USB_IS_RESUME 0x00000002 // RESUME Signaling Detected
-#define USB_IS_SUSPEND 0x00000001 // SUSPEND Signaling Detected
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_IE register.
-//
-//*****************************************************************************
-#define USB_IE_VBUSERR 0x00000080 // Enable VBUS Error Interrupt (OTG
- // only)
-#define USB_IE_SESREQ 0x00000040 // Enable Session Request (OTG
- // only)
-#define USB_IE_DISCON 0x00000020 // Enable Disconnect Interrupt
-#define USB_IE_CONN 0x00000010 // Enable Connect Interrupt
-#define USB_IE_SOF 0x00000008 // Enable Start-of-Frame Interrupt
-#define USB_IE_BABBLE 0x00000004 // Enable Babble Interrupt
-#define USB_IE_RESET 0x00000004 // Enable RESET Interrupt
-#define USB_IE_RESUME 0x00000002 // Enable RESUME Interrupt
-#define USB_IE_SUSPND 0x00000001 // Enable SUSPEND Interrupt
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FRAME register.
-//
-//*****************************************************************************
-#define USB_FRAME_M 0x000007FF // Frame Number
-#define USB_FRAME_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPIDX register.
-//
-//*****************************************************************************
-#define USB_EPIDX_EPIDX_M 0x0000000F // Endpoint Index
-#define USB_EPIDX_EPIDX_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TEST register.
-//
-//*****************************************************************************
-#define USB_TEST_FORCEH 0x00000080 // Force Host Mode
-#define USB_TEST_FIFOACC 0x00000040 // FIFO Access
-#define USB_TEST_FORCEFS 0x00000020 // Force Full-Speed Mode
-#define USB_TEST_FORCEHS 0x00000010 // Force High-Speed Mode
-#define USB_TEST_TESTPKT 0x00000008 // Test Packet Mode Enable
-#define USB_TEST_TESTK 0x00000004 // Test_K Mode Enable
-#define USB_TEST_TESTJ 0x00000002 // Test_J Mode Enable
-#define USB_TEST_TESTSE0NAK 0x00000001 // Test_SE0_NAK Test Mode Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO0 register.
-//
-//*****************************************************************************
-#define USB_FIFO0_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO0_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO1 register.
-//
-//*****************************************************************************
-#define USB_FIFO1_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO1_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO2 register.
-//
-//*****************************************************************************
-#define USB_FIFO2_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO2_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO3 register.
-//
-//*****************************************************************************
-#define USB_FIFO3_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO3_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO4 register.
-//
-//*****************************************************************************
-#define USB_FIFO4_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO4_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO5 register.
-//
-//*****************************************************************************
-#define USB_FIFO5_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO5_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO6 register.
-//
-//*****************************************************************************
-#define USB_FIFO6_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO6_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FIFO7 register.
-//
-//*****************************************************************************
-#define USB_FIFO7_EPDATA_M 0xFFFFFFFF // Endpoint Data
-#define USB_FIFO7_EPDATA_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DEVCTL register.
-//
-//*****************************************************************************
-#define USB_DEVCTL_DEV 0x00000080 // Device Mode (OTG only)
-#define USB_DEVCTL_FSDEV 0x00000040 // Full-Speed Device Detected
-#define USB_DEVCTL_LSDEV 0x00000020 // Low-Speed Device Detected
-#define USB_DEVCTL_VBUS_M 0x00000018 // VBUS Level (OTG only)
-#define USB_DEVCTL_VBUS_NONE 0x00000000 // Below SessionEnd
-#define USB_DEVCTL_VBUS_SEND 0x00000008 // Above SessionEnd, below AValid
-#define USB_DEVCTL_VBUS_AVALID 0x00000010 // Above AValid, below VBUSValid
-#define USB_DEVCTL_VBUS_VALID 0x00000018 // Above VBUSValid
-#define USB_DEVCTL_HOST 0x00000004 // Host Mode
-#define USB_DEVCTL_HOSTREQ 0x00000002 // Host Request (OTG only)
-#define USB_DEVCTL_SESSION 0x00000001 // Session Start/End (OTG only)
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_CCONF register.
-//
-//*****************************************************************************
-#define USB_CCONF_TXEDMA 0x00000002 // TX Early DMA Enable
-#define USB_CCONF_RXEDMA 0x00000001 // TX Early DMA Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFIFOSZ register.
-//
-//*****************************************************************************
-#define USB_TXFIFOSZ_DPB 0x00000010 // Double Packet Buffer Support
-#define USB_TXFIFOSZ_SIZE_M 0x0000000F // Max Packet Size
-#define USB_TXFIFOSZ_SIZE_8 0x00000000 // 8
-#define USB_TXFIFOSZ_SIZE_16 0x00000001 // 16
-#define USB_TXFIFOSZ_SIZE_32 0x00000002 // 32
-#define USB_TXFIFOSZ_SIZE_64 0x00000003 // 64
-#define USB_TXFIFOSZ_SIZE_128 0x00000004 // 128
-#define USB_TXFIFOSZ_SIZE_256 0x00000005 // 256
-#define USB_TXFIFOSZ_SIZE_512 0x00000006 // 512
-#define USB_TXFIFOSZ_SIZE_1024 0x00000007 // 1024
-#define USB_TXFIFOSZ_SIZE_2048 0x00000008 // 2048
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFIFOSZ register.
-//
-//*****************************************************************************
-#define USB_RXFIFOSZ_DPB 0x00000010 // Double Packet Buffer Support
-#define USB_RXFIFOSZ_SIZE_M 0x0000000F // Max Packet Size
-#define USB_RXFIFOSZ_SIZE_8 0x00000000 // 8
-#define USB_RXFIFOSZ_SIZE_16 0x00000001 // 16
-#define USB_RXFIFOSZ_SIZE_32 0x00000002 // 32
-#define USB_RXFIFOSZ_SIZE_64 0x00000003 // 64
-#define USB_RXFIFOSZ_SIZE_128 0x00000004 // 128
-#define USB_RXFIFOSZ_SIZE_256 0x00000005 // 256
-#define USB_RXFIFOSZ_SIZE_512 0x00000006 // 512
-#define USB_RXFIFOSZ_SIZE_1024 0x00000007 // 1024
-#define USB_RXFIFOSZ_SIZE_2048 0x00000008 // 2048
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFIFOADD
-// register.
-//
-//*****************************************************************************
-#define USB_TXFIFOADD_ADDR_M 0x000001FF // Transmit/Receive Start Address
-#define USB_TXFIFOADD_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFIFOADD
-// register.
-//
-//*****************************************************************************
-#define USB_RXFIFOADD_ADDR_M 0x000001FF // Transmit/Receive Start Address
-#define USB_RXFIFOADD_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_ULPIVBUSCTL
-// register.
-//
-//*****************************************************************************
-#define USB_ULPIVBUSCTL_USEEXTVBUSIND \
- 0x00000002 // Use External VBUS Indicator
-#define USB_ULPIVBUSCTL_USEEXTVBUS \
- 0x00000001 // Use External VBUS
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_ULPIREGDATA
-// register.
-//
-//*****************************************************************************
-#define USB_ULPIREGDATA_REGDATA_M \
- 0x000000FF // Register Data
-#define USB_ULPIREGDATA_REGDATA_S \
- 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_ULPIREGADDR
-// register.
-//
-//*****************************************************************************
-#define USB_ULPIREGADDR_ADDR_M 0x000000FF // Register Address
-#define USB_ULPIREGADDR_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_ULPIREGCTL
-// register.
-//
-//*****************************************************************************
-#define USB_ULPIREGCTL_RDWR 0x00000004 // Read/Write Control
-#define USB_ULPIREGCTL_REGCMPLT 0x00000002 // Register Access Complete
-#define USB_ULPIREGCTL_REGACC 0x00000001 // Initiate Register Access
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPINFO register.
-//
-//*****************************************************************************
-#define USB_EPINFO_RXEP_M 0x000000F0 // RX Endpoints
-#define USB_EPINFO_TXEP_M 0x0000000F // TX Endpoints
-#define USB_EPINFO_RXEP_S 4
-#define USB_EPINFO_TXEP_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RAMINFO register.
-//
-//*****************************************************************************
-#define USB_RAMINFO_DMACHAN_M 0x000000F0 // DMA Channels
-#define USB_RAMINFO_RAMBITS_M 0x0000000F // RAM Address Bus Width
-#define USB_RAMINFO_DMACHAN_S 4
-#define USB_RAMINFO_RAMBITS_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_CONTIM register.
-//
-//*****************************************************************************
-#define USB_CONTIM_WTCON_M 0x000000F0 // Connect Wait
-#define USB_CONTIM_WTID_M 0x0000000F // Wait ID
-#define USB_CONTIM_WTCON_S 4
-#define USB_CONTIM_WTID_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_VPLEN register.
-//
-//*****************************************************************************
-#define USB_VPLEN_VPLEN_M 0x000000FF // VBUS Pulse Length
-#define USB_VPLEN_VPLEN_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_HSEOF register.
-//
-//*****************************************************************************
-#define USB_HSEOF_HSEOFG_M 0x000000FF // HIgh-Speed End-of-Frame Gap
-#define USB_HSEOF_HSEOFG_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_FSEOF register.
-//
-//*****************************************************************************
-#define USB_FSEOF_FSEOFG_M 0x000000FF // Full-Speed End-of-Frame Gap
-#define USB_FSEOF_FSEOFG_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LSEOF register.
-//
-//*****************************************************************************
-#define USB_LSEOF_LSEOFG_M 0x000000FF // Low-Speed End-of-Frame Gap
-#define USB_LSEOF_LSEOFG_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR0
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR0_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR0_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR0
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR0_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR0_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT0
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT0_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT0_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR1
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR1_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR1_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR1
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR1_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR1_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT1
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT1_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT1_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR1
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR1_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR1_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR1
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR1_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR1_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT1
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT1_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT1_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR2
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR2_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR2_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR2
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR2_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR2_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT2
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT2_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT2_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR2
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR2_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR2_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR2
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR2_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR2_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT2
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT2_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT2_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR3
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR3_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR3_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR3
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR3_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR3_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT3
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT3_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT3_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR3
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR3_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR3_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR3
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR3_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR3_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT3
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT3_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT3_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR4
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR4_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR4_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR4
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR4_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR4_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT4
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT4_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT4_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR4
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR4_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR4_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR4
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR4_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR4_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT4
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT4_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT4_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR5
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR5_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR5_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR5
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR5_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR5_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT5
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT5_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT5_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR5
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR5_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR5_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR5
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR5_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR5_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT5
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT5_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT5_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR6
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR6_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR6_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR6
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR6_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR6_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT6
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT6_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT6_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR6
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR6_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR6_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR6
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR6_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR6_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT6
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT6_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT6_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXFUNCADDR7
-// register.
-//
-//*****************************************************************************
-#define USB_TXFUNCADDR7_ADDR_M 0x0000007F // Device Address
-#define USB_TXFUNCADDR7_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBADDR7
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBADDR7_ADDR_M 0x0000007F // Hub Address
-#define USB_TXHUBADDR7_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXHUBPORT7
-// register.
-//
-//*****************************************************************************
-#define USB_TXHUBPORT7_PORT_M 0x0000007F // Hub Port
-#define USB_TXHUBPORT7_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXFUNCADDR7
-// register.
-//
-//*****************************************************************************
-#define USB_RXFUNCADDR7_ADDR_M 0x0000007F // Device Address
-#define USB_RXFUNCADDR7_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBADDR7
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBADDR7_ADDR_M 0x0000007F // Hub Address
-#define USB_RXHUBADDR7_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXHUBPORT7
-// register.
-//
-//*****************************************************************************
-#define USB_RXHUBPORT7_PORT_M 0x0000007F // Hub Port
-#define USB_RXHUBPORT7_PORT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_CSRL0 register.
-//
-//*****************************************************************************
-#define USB_CSRL0_NAKTO 0x00000080 // NAK Timeout
-#define USB_CSRL0_SETENDC 0x00000080 // Setup End Clear
-#define USB_CSRL0_STATUS 0x00000040 // STATUS Packet
-#define USB_CSRL0_RXRDYC 0x00000040 // RXRDY Clear
-#define USB_CSRL0_REQPKT 0x00000020 // Request Packet
-#define USB_CSRL0_STALL 0x00000020 // Send Stall
-#define USB_CSRL0_SETEND 0x00000010 // Setup End
-#define USB_CSRL0_ERROR 0x00000010 // Error
-#define USB_CSRL0_DATAEND 0x00000008 // Data End
-#define USB_CSRL0_SETUP 0x00000008 // Setup Packet
-#define USB_CSRL0_STALLED 0x00000004 // Endpoint Stalled
-#define USB_CSRL0_TXRDY 0x00000002 // Transmit Packet Ready
-#define USB_CSRL0_RXRDY 0x00000001 // Receive Packet Ready
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_CSRH0 register.
-//
-//*****************************************************************************
-#define USB_CSRH0_DISPING 0x00000008 // PING Disable
-#define USB_CSRH0_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_CSRH0_DT 0x00000002 // Data Toggle
-#define USB_CSRH0_FLUSH 0x00000001 // Flush FIFO
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_COUNT0 register.
-//
-//*****************************************************************************
-#define USB_COUNT0_COUNT_M 0x0000007F // FIFO Count
-#define USB_COUNT0_COUNT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TYPE0 register.
-//
-//*****************************************************************************
-#define USB_TYPE0_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TYPE0_SPEED_HIGH 0x00000040 // High
-#define USB_TYPE0_SPEED_FULL 0x00000080 // Full
-#define USB_TYPE0_SPEED_LOW 0x000000C0 // Low
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_NAKLMT register.
-//
-//*****************************************************************************
-#define USB_NAKLMT_NAKLMT_M 0x0000001F // EP0 NAK Limit
-#define USB_NAKLMT_NAKLMT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXMAXP1 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP1_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP1_MAXLOAD_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL1 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL1_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL1_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL1_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL1_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL1_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL1_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL1_ERROR 0x00000004 // Error
-#define USB_TXCSRL1_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL1_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL1_TXRDY 0x00000001 // Transmit Packet Ready
+#ifndef __IO
+ #define __IO volatile
+#endif
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRH1 register.
-//
-//*****************************************************************************
-#define USB_TXCSRH1_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH1_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH1_MODE 0x00000020 // Mode
-#define USB_TXCSRH1_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH1_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH1_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH1_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH1_DT 0x00000001 // Data Toggle
+#ifndef __I
+ #define __I volatile const
+#endif
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXMAXP1 register.
-//
-//*****************************************************************************
-#define USB_RXMAXP1_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP1_MAXLOAD_S 0
+#ifndef __O
+ #define __O volatile
+#endif
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRL1 register.
-//
-//*****************************************************************************
-#define USB_RXCSRL1_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL1_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL1_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL1_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL1_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL1_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL1_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL1_OVER 0x00000004 // Overrun
-#define USB_RXCSRL1_ERROR 0x00000004 // Error
-#define USB_RXCSRL1_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL1_RXRDY 0x00000001 // Receive Packet Ready
+#ifndef __R
+ #define __R volatile const
+#endif
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRH1 register.
-//
-//*****************************************************************************
-#define USB_RXCSRH1_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH1_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH1_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH1_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH1_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH1_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH1_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH1_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH1_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH1_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
+typedef struct TU_ATTR_PACKED {
+ __IO uint16_t maxp; // 0x00, 0x04: MAXP
+ __IO uint8_t csrl; // 0x02, 0x06: CSRL
+ __IO uint8_t csrh; // 0x03, 0x07: CSRH
+}musb_ep_maxp_csr_t;
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCOUNT1 register.
-//
-//*****************************************************************************
-#define USB_RXCOUNT1_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT1_COUNT_S 0
+// 0: TX (device IN, host OUT)
+// 1: RX (device OUT, host IN)
+typedef struct TU_ATTR_PACKED {
+ union {
+ struct {
+ __IO uint16_t tx_maxp; // 0x00: TXMAXP
+ union {
+ __IO uint8_t csr0l; // 0x02: CSR0
+ __IO uint8_t tx_csrl; // 0x02: TX CSRL
+ };
+ union {
+ __IO uint8_t csr0h; // 0x03: CSR0H
+ __IO uint8_t tx_csrh; // 0x03: TX CSRH
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXTYPE1 register.
-//
-//*****************************************************************************
-#define USB_TXTYPE1_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE1_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE1_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE1_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE1_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE1_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE1_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE1_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE1_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE1_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE1_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE1_TEP_S 0
+ __IO uint16_t rx_maxp; // 0x04: RX MAXP
+ __IO uint8_t rx_csrl; // 0x06: RX CSRL
+ __IO uint8_t rx_csrh; // 0x07: RX CSRH
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL1
-// register.
-//
-//*****************************************************************************
-#define USB_TXINTERVAL1_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL1_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL1_TXPOLL_S \
- 0
-#define USB_TXINTERVAL1_NAKLMT_S \
- 0
+ musb_ep_maxp_csr_t maxp_csr[2];
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXTYPE1 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE1_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE1_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE1_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE1_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE1_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE1_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE1_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE1_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE1_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE1_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE1_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE1_TEP_S 0
+ union {
+ __IO uint16_t count0; // 0x08: COUNT0
+ __IO uint16_t rx_count; // 0x08: RX COUNT
+ };
+ union {
+ __IO uint8_t type0; // 0x0A: TYPE0 (host only)
+ __IO uint8_t tx_type; // 0x0A: TX TYPE
+ };
+ __IO uint8_t tx_interval; // 0x0B: TX INTERVAL
+ __IO uint8_t rx_type; // 0x0C: RX TYPE
+ __IO uint8_t rx_interval; // 0x0D: RX INTERVAL
+ __IO uint8_t reserved_0x0e; // 0x0E: Reserved
+ union {
+ __IO uint8_t config_data0; // 0x0F: CONFIG DATA
+ struct {
+ __IO uint8_t utmi_data_width : 1; // [0] UTMI Data Width
+ __IO uint8_t softconn_en : 1; // [1] Soft Connect Enable
+ __IO uint8_t dynamic_fifo : 1; // [2] Dynamic FIFO Sizing
+ __IO uint8_t hb_tx_en : 1; // [3] High Bandwidth TX ISO Enable
+ __IO uint8_t hb_rx_en : 1; // [4] High Bandwidth RX ISO Enable
+ __IO uint8_t big_endian : 1; // [5] Big Endian
+ __IO uint8_t mp_tx_en : 1; // [6] Auto splitting BULK TX Enable
+ __IO uint8_t mp_rx_en : 1; // [7] Auto amalgamation BULK RX Enable
+ } config_data0_bit;
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL1
-// register.
-//
-//*****************************************************************************
-#define USB_RXINTERVAL1_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL1_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL1_TXPOLL_S \
- 0
-#define USB_RXINTERVAL1_NAKLMT_S \
- 0
+ __IO uint8_t fifo_size; // 0x0F: FIFO_SIZE
+ struct {
+ __IO uint8_t tx : 4; // [3:0] TX FIFO Size
+ __IO uint8_t rx : 4; // [7:4] RX FIFO Size
+ }fifo_size_bit;
+ };
+} musb_ep_csr_t;
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXMAXP2 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP2_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP2_MAXLOAD_S 0
+TU_VERIFY_STATIC(sizeof(musb_ep_csr_t) == 16, "size is not correct");
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL2 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL2_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL2_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL2_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL2_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL2_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL2_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL2_ERROR 0x00000004 // Error
-#define USB_TXCSRL2_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL2_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL2_TXRDY 0x00000001 // Transmit Packet Ready
+typedef struct TU_ATTR_PACKED {
+ //------------- Common -------------//
+ __IO uint8_t faddr; // 0x00: FADDR
+ union {
+ __IO uint8_t power; // 0x01: POWER
+ struct {
+ __IO uint8_t suspend_mode_en : 1; // [0] SUSPEND Mode Enable
+ __IO uint8_t suspend_mode : 1; // [1] SUSPEND Mode
+ __IO uint8_t resume_mode : 1; // [2] RESUME
+ __IO uint8_t reset : 1; // [3] RESET
+ __IO uint8_t highspeed_mode : 1; // [4] High Speed Mode
+ __IO uint8_t highspeed_en : 1; // [5] High Speed Enable
+ __IO uint8_t soft_conn : 1; // [6] Soft Connect/Disconnect
+ __IO uint8_t iso_update : 1; // [7] Isochronous Update
+ } power_bit;
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRH2 register.
-//
-//*****************************************************************************
-#define USB_TXCSRH2_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH2_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH2_MODE 0x00000020 // Mode
-#define USB_TXCSRH2_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH2_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH2_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH2_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH2_DT 0x00000001 // Data Toggle
+ union {
+ struct {
+ __IO uint16_t intr_tx; // 0x02: INTR_TX
+ __IO uint16_t intr_rx; // 0x04: INTR_RX
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXMAXP2 register.
-//
-//*****************************************************************************
-#define USB_RXMAXP2_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP2_MAXLOAD_S 0
+ __IO uint16_t intr_ep[2]; // 0x02-0x05: INTR_EP0-1
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRL2 register.
-//
-//*****************************************************************************
-#define USB_RXCSRL2_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL2_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL2_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL2_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL2_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL2_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL2_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL2_ERROR 0x00000004 // Error
-#define USB_RXCSRL2_OVER 0x00000004 // Overrun
-#define USB_RXCSRL2_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL2_RXRDY 0x00000001 // Receive Packet Ready
+ union {
+ struct {
+ __IO uint16_t intr_txen; // 0x06: INTR_TXEN
+ __IO uint16_t intr_rxen; // 0x08: INTR_RXEN
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRH2 register.
-//
-//*****************************************************************************
-#define USB_RXCSRH2_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH2_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH2_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH2_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH2_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH2_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH2_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH2_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH2_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH2_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
+ __IO uint16_t intren_ep[2]; // 0x06-0x09: INTREN_EP0-1
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCOUNT2 register.
-//
-//*****************************************************************************
-#define USB_RXCOUNT2_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT2_COUNT_S 0
+ __IO uint8_t intr_usb; // 0x0A: INTRUSB
+ __IO uint8_t intr_usben; // 0x0B: INTRUSBEN
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXTYPE2 register.
-//
-//*****************************************************************************
-#define USB_TXTYPE2_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE2_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE2_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE2_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE2_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE2_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE2_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE2_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE2_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE2_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE2_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE2_TEP_S 0
+ __IO uint16_t frame; // 0x0C: FRAME
+ __IO uint8_t index; // 0x0E: INDEX
+ __IO uint8_t testmode; // 0x0F: TESTMODE
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL2
-// register.
-//
-//*****************************************************************************
-#define USB_TXINTERVAL2_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL2_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL2_NAKLMT_S \
- 0
-#define USB_TXINTERVAL2_TXPOLL_S \
- 0
+ //------------- Endpoint CSR (indexed) -------------//
+ musb_ep_csr_t indexed_csr; // 0x10-0x1F: Indexed CSR 0-15
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXTYPE2 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE2_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE2_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE2_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE2_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE2_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE2_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE2_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE2_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE2_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE2_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE2_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE2_TEP_S 0
+ //------------- FIFOs -------------//
+ __IO uint32_t fifo[16]; // 0x20-0x5C: FIFO 0-15
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL2
-// register.
-//
-//*****************************************************************************
-#define USB_RXINTERVAL2_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL2_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL2_TXPOLL_S \
- 0
-#define USB_RXINTERVAL2_NAKLMT_S \
- 0
+ // Common (2)
+ __IO uint8_t devctl; // 0x60: DEVCTL
+ __IO uint8_t misc; // 0x61: MISC
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXMAXP3 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP3_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP3_MAXLOAD_S 0
+ //------------- Dynammic FIFO (indexed) -------------//
+ union {
+ struct {
+ __IO uint8_t txfifo_sz; // 0x62: TXFIFO_SZ
+ __IO uint8_t rxfifo_sz; // 0x63: RXFIFO_SZ
+ };
+ __IO uint8_t fifo_size[2];
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL3 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL3_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL3_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL3_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL3_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL3_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL3_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL3_ERROR 0x00000004 // Error
-#define USB_TXCSRL3_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL3_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL3_TXRDY 0x00000001 // Transmit Packet Ready
+ union {
+ struct {
+ __IO uint16_t txfifo_addr; // 0x64: TXFIFO_ADDR
+ __IO uint16_t rxfifo_addr; // 0x66: RXFIFO_ADDR
+ };
+ __IO uint16_t fifo_addr[2];
+ };
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRH3 register.
-//
-//*****************************************************************************
-#define USB_TXCSRH3_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH3_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH3_MODE 0x00000020 // Mode
-#define USB_TXCSRH3_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH3_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH3_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH3_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH3_DT 0x00000001 // Data Toggle
+ //------------- Additional Control and Configuration -------------//
+ union {
+ __O uint32_t vcontrol; // 0x68: PHY VCONTROL
+ __IO uint32_t vstatus; // 0x68: PHY VSTATUS
+ };
+ union {
+ __IO uint16_t hwvers; // 0x6C: HWVERS
+ struct {
+ __IO uint16_t minor : 10; // [9:0] Minor
+ __IO uint16_t major : 5; // [14:10] Major
+ __IO uint16_t rc : 1; // [15] Release Candidate
+ } hwvers_bit;
+ };
+ __R uint16_t rsv_0x6e_0x77[5]; // 0x6E-0x77: Reserved
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXMAXP3 register.
-//
-//*****************************************************************************
-#define USB_RXMAXP3_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP3_MAXLOAD_S 0
+ //------------- Additional Configuration -------------//
+ union {
+ __IO uint8_t epinfo; // 0x78: EPINFO
+ struct {
+ __IO uint8_t tx_ep_num : 4; // [3:0] TX Endpoints
+ __IO uint8_t rx_ep_num : 4; // [7:4] RX Endpoints
+ } epinfo_bit;
+ };
+ union {
+ __IO uint8_t raminfo; // 0x79: RAMINFO
+ struct {
+ __IO uint8_t ram_bits : 4; // [3:0] RAM Address Bus Width
+ __IO uint8_t dma_channel : 4; // [7:4] DMA Channels
+ }raminfo_bit;
+ };
+ union {
+ __IO uint8_t link_info; // 0x7A: LINK_INFO
+ __IO uint8_t adi_softreset; // 0x7A: AnalogDevice SOFTRESET
+ };
+ __IO uint8_t vplen; // 0x7B: VPLEN
+ __IO uint8_t hs_eof1; // 0x7C: HS_EOF1
+ __IO uint8_t fs_eof1; // 0x7D: FS_EOF1
+ __IO uint8_t ls_eof1; // 0x7E: LS_EOF1
+ __IO uint8_t soft_rst; // 0x7F: SOFT_RST
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRL3 register.
-//
-//*****************************************************************************
-#define USB_RXCSRL3_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL3_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL3_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL3_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL3_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL3_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL3_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL3_ERROR 0x00000004 // Error
-#define USB_RXCSRL3_OVER 0x00000004 // Overrun
-#define USB_RXCSRL3_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL3_RXRDY 0x00000001 // Receive Packet Ready
+ //------------- Target Endpoints (multipoint option) -------------//
+ __IO uint16_t ctuch; // 0x80: CTUCH
+ __IO uint16_t cthsrtn; // 0x82: CTHSRTN
+ __R uint32_t rsv_0x84_0xff[31]; // 0x84-0xFF: Reserved
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRH3 register.
-//
-//*****************************************************************************
-#define USB_RXCSRH3_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH3_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH3_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH3_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH3_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH3_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH3_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH3_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH3_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH3_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
+ //------------- Non-Indexed Endpoint CSRs -------------//
+ // TI tm4c can access this directly, but should use indexed_csr for portability
+ musb_ep_csr_t abs_csr[16]; // 0x100-0x1FF: EP0-15 CSR
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCOUNT3 register.
-//
-//*****************************************************************************
-#define USB_RXCOUNT3_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT3_COUNT_S 0
+ //------------- DMA -------------//
+ __IO uint8_t dma_intr; // 0x200: DMA_INTR
+ __R uint8_t rsv_0x201_0x203[3]; // 0x201-0x203: Reserved
+ struct {
+ __IO uint16_t cntl; // 0x204: DMA_CNTL
+ __IO uint16_t rsv_0x206; // 0x206: Reserved
+ __IO uint32_t addr; // 0x208: DMA_ADDR
+ __IO uint32_t count; // 0x20C: DMA_COUNT
+ __IO uint32_t rsv_0x210; // 0x210: Reserved
+ }dma[8];
+ __R uint32_t rsv_0x284_0x2FF[31]; // 0x284-0x2FF: Reserved
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXTYPE3 register.
-//
-//*****************************************************************************
-#define USB_TXTYPE3_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE3_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE3_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE3_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE3_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE3_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE3_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE3_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE3_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE3_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE3_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE3_TEP_S 0
+ //------------- Extended -------------//
+ __R uint32_t rsv_0x300; // 0x300: Reserved
+ struct {
+ __IO uint16_t count; // 0x304: REQ_PACKET_COUNT
+ __R uint16_t rsv_0x306; // 0x306: Reserved
+ }req_packet[15];
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL3
-// register.
-//
-//*****************************************************************************
-#define USB_TXINTERVAL3_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL3_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL3_TXPOLL_S \
- 0
-#define USB_TXINTERVAL3_NAKLMT_S \
- 0
+ __IO uint16_t rx_doulbe_packet_disable; // 0x340: RX_DOUBLE_PACKET_DISABLE
+ __IO uint16_t tx_double_packet_disable; // 0x342: TX_DOUBLE_PACKET_DISABLE
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXTYPE3 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE3_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE3_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE3_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE3_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE3_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE3_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE3_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE3_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE3_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE3_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE3_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE3_TEP_S 0
+ __IO uint16_t chirp_timeout; // 0x344: CHIRP_TIMEOUT
+ __IO uint16_t hs_to_utm; // 0x346: HS_TO_UTM delay
+ __IO uint16_t hs_timeout_adder; // 0x348: HS_TIMEOUT_ADDER
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL3
-// register.
-//
-//*****************************************************************************
-#define USB_RXINTERVAL3_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL3_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL3_TXPOLL_S \
- 0
-#define USB_RXINTERVAL3_NAKLMT_S \
- 0
+ __R uint8_t rsv_34A_34f[6]; // 0x34A-0x34F: Reserved
+} musb_regs_t;
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXMAXP4 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP4_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP4_MAXLOAD_S 0
+TU_VERIFY_STATIC(sizeof(musb_regs_t) == 0x350, "size is not correct");
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL4 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL4_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL4_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL4_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL4_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL4_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL4_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL4_ERROR 0x00000004 // Error
-#define USB_TXCSRL4_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL4_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL4_TXRDY 0x00000001 // Transmit Packet Ready
+//--------------------------------------------------------------------+
+// Helper
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_regs, unsigned epnum) {
+ musb_regs->index = epnum;
+ return &musb_regs->indexed_csr;
+}
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRH4 register.
-//
-//*****************************************************************************
-#define USB_TXCSRH4_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH4_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH4_MODE 0x00000020 // Mode
-#define USB_TXCSRH4_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH4_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH4_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH4_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH4_DT 0x00000001 // Data Toggle
+//--------------------------------------------------------------------+
+// Register Bit Field
+//--------------------------------------------------------------------+
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXMAXP4 register.
-//
-//*****************************************************************************
-#define USB_RXMAXP4_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP4_MAXLOAD_S 0
+// 0x01: Power
+#define MUSB_POWER_ISOUP 0x0080 // Isochronous Update
+#define MUSB_POWER_SOFTCONN 0x0040 // Soft Connect/Disconnect
+#define MUSB_POWER_HSENAB 0x0020 // High Speed Enable
+#define MUSB_POWER_HSMODE 0x0010 // High Speed Enable
+#define MUSB_POWER_RESET 0x0008 // RESET Signaling
+#define MUSB_POWER_RESUME 0x0004 // RESUME Signaling
+#define MUSB_POWER_SUSPEND 0x0002 // SUSPEND Mode
+#define MUSB_POWER_PWRDNPHY 0x0001 // Power Down PHY
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRL4 register.
-//
-//*****************************************************************************
-#define USB_RXCSRL4_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL4_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL4_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL4_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL4_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL4_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL4_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL4_OVER 0x00000004 // Overrun
-#define USB_RXCSRL4_ERROR 0x00000004 // Error
-#define USB_RXCSRL4_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL4_RXRDY 0x00000001 // Receive Packet Ready
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRH4 register.
-//
-//*****************************************************************************
-#define USB_RXCSRH4_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH4_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH4_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH4_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH4_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH4_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH4_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH4_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH4_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH4_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCOUNT4 register.
-//
-//*****************************************************************************
-#define USB_RXCOUNT4_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT4_COUNT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXTYPE4 register.
-//
-//*****************************************************************************
-#define USB_TXTYPE4_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE4_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE4_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE4_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE4_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE4_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE4_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE4_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE4_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE4_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE4_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE4_TEP_S 0
+// Interrupt TX/RX Status and Enable: each bit is for an endpoint
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL4
-// register.
-//
-//*****************************************************************************
-#define USB_TXINTERVAL4_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL4_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL4_NAKLMT_S \
- 0
-#define USB_TXINTERVAL4_TXPOLL_S \
- 0
+// 0x6c: HWVERS
+#define MUSB_HWVERS_RC_SHIFT 15
+#define MUSB_HWVERS_RC_MASK 0x8000
+#define MUSB_HWVERS_MAJOR_SHIFT 10
+#define MUSB_HWVERS_MAJOR_MASK 0x7C00
+#define MUSB_HWVERS_MINOR_SHIFT 0
+#define MUSB_HWVERS_MINOR_MASK 0x03FF
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXTYPE4 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE4_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE4_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE4_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE4_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE4_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE4_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE4_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE4_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE4_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE4_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE4_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE4_TEP_S 0
+// 0x12, 0x16: TX/RX CSRL
+#define MUSB_CSRL_PACKET_READY(_rx) (1u << 0)
+#define MUSB_CSRL_FLUSH_FIFO(_rx) (1u << ((_rx) ? 4 : 3))
+#define MUSB_CSRL_SEND_STALL(_rx) (1u << ((_rx) ? 5 : 4))
+#define MUSB_CSRL_STALLED(_rx) (1u << ((_rx) ? 6 : 5))
+#define MUSB_CSRL_CLEAR_DATA_TOGGLE(_rx) (1u << ((_rx) ? 7 : 6))
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL4
-// register.
-//
-//*****************************************************************************
-#define USB_RXINTERVAL4_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL4_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL4_NAKLMT_S \
- 0
-#define USB_RXINTERVAL4_TXPOLL_S \
- 0
+// 0x13, 0x17: TX/RX CSRH
+#define MUSB_CSRH_DISABLE_DOUBLE_PACKET(_rx) (1u << 1)
+#define MUSB_CSRH_TX_MODE (1u << 5) // 1 = TX, 0 = RX. only relevant for SHARED FIFO
+#define MUSB_CSRH_ISO (1u << 6)
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXMAXP5 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP5_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP5_MAXLOAD_S 0
+// 0x62, 0x63: TXFIFO_SZ, RXFIFO_SZ
+#define MUSB_FIFOSZ_DOUBLE_PACKET (1u << 4)
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL5 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL5_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL5_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL5_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL5_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL5_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL5_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL5_ERROR 0x00000004 // Error
-#define USB_TXCSRL5_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL5_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL5_TXRDY 0x00000001 // Transmit Packet Ready
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXCSRH5 register.
+// The following are defines for the bit fields in the MUSB_O_IS register.
//
//*****************************************************************************
-#define USB_TXCSRH5_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH5_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH5_MODE 0x00000020 // Mode
-#define USB_TXCSRH5_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH5_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH5_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH5_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH5_DT 0x00000001 // Data Toggle
+#define MUSB_IS_VBUSERR 0x0080 // VBUS Error (OTG only)
+#define MUSB_IS_SESREQ 0x0040 // SESSION REQUEST (OTG only)
+#define MUSB_IS_DISCON 0x0020 // Session Disconnect (OTG only)
+#define MUSB_IS_CONN 0x0010 // Session Connect
+#define MUSB_IS_SOF 0x0008 // Start of Frame
+#define MUSB_IS_BABBLE 0x0004 // Babble Detected
+#define MUSB_IS_RESET 0x0004 // RESET Signaling Detected
+#define MUSB_IS_RESUME 0x0002 // RESUME Signaling Detected
+#define MUSB_IS_SUSPEND 0x0001 // SUSPEND Signaling Detected
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXMAXP5 register.
+// The following are defines for the bit fields in the MUSB_O_IE register.
//
//*****************************************************************************
-#define USB_RXMAXP5_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP5_MAXLOAD_S 0
+#define MUSB_IE_VBUSERR 0x0080 // Enable VBUS Error Interrupt (OTG only)
+#define MUSB_IE_SESREQ 0x0040 // Enable Session Request (OTG only)
+#define MUSB_IE_DISCON 0x0020 // Enable Disconnect Interrupt
+#define MUSB_IE_CONN 0x0010 // Enable Connect Interrupt
+#define MUSB_IE_SOF 0x0008 // Enable Start-of-Frame Interrupt
+#define MUSB_IE_BABBLE 0x0004 // Enable Babble Interrupt
+#define MUSB_IE_RESET 0x0004 // Enable RESET Interrupt
+#define MUSB_IE_RESUME 0x0002 // Enable RESUME Interrupt
+#define MUSB_IE_SUSPND 0x0001 // Enable SUSPEND Interrupt
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCSRL5 register.
+// The following are defines for the bit fields in the MUSB_O_FRAME register.
//
//*****************************************************************************
-#define USB_RXCSRL5_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL5_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL5_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL5_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL5_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL5_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL5_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL5_ERROR 0x00000004 // Error
-#define USB_RXCSRL5_OVER 0x00000004 // Overrun
-#define USB_RXCSRL5_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL5_RXRDY 0x00000001 // Receive Packet Ready
+#define MUSB_FRAME_M 0x07FF // Frame Number
+#define MUSB_FRAME_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCSRH5 register.
+// The following are defines for the bit fields in the MUSB_O_TEST register.
//
//*****************************************************************************
-#define USB_RXCSRH5_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH5_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH5_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH5_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH5_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH5_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH5_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH5_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH5_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH5_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
+#define MUSB_TEST_FORCEH 0x0080 // Force Host Mode
+#define MUSB_TEST_FIFOACC 0x0040 // FIFO Access
+#define MUSB_TEST_FORCEFS 0x0020 // Force Full-Speed Mode
+#define MUSB_TEST_FORCEHS 0x0010 // Force High-Speed Mode
+#define MUSB_TEST_TESTPKT 0x0008 // Test Packet Mode Enable
+#define MUSB_TEST_TESTK 0x0004 // Test_K Mode Enable
+#define MUSB_TEST_TESTJ 0x0002 // Test_J Mode Enable
+#define MUSB_TEST_TESTSE0NAK 0x0001 // Test_SE0_NAK Test Mode Enable
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCOUNT5 register.
+// The following are defines for the bit fields in the MUSB_O_DEVCTL register.
//
//*****************************************************************************
-#define USB_RXCOUNT5_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT5_COUNT_S 0
+#define MUSB_DEVCTL_DEV 0x0080 // Device Mode (OTG only)
+#define MUSB_DEVCTL_FSDEV 0x0040 // Full-Speed Device Detected
+#define MUSB_DEVCTL_LSDEV 0x0020 // Low-Speed Device Detected
+#define MUSB_DEVCTL_VBUS_M 0x0018 // VBUS Level (OTG only)
+#define MUSB_DEVCTL_VBUS_NONE 0x0000 // Below SessionEnd
+#define MUSB_DEVCTL_VBUS_SEND 0x0008 // Above SessionEnd, below AValid
+#define MUSB_DEVCTL_VBUS_AVALID 0x0010 // Above AValid, below VBUSValid
+#define MUSB_DEVCTL_VBUS_VALID 0x0018 // Above VBUSValid
+#define MUSB_DEVCTL_HOST 0x0004 // Host Mode
+#define MUSB_DEVCTL_HOSTREQ 0x0002 // Host Request (OTG only)
+#define MUSB_DEVCTL_SESSION 0x0001 // Session Start/End (OTG only)
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXTYPE5 register.
+// The following are defines for the bit fields in the MUSB_O_CCONF register.
//
//*****************************************************************************
-#define USB_TXTYPE5_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE5_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE5_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE5_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE5_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE5_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE5_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE5_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE5_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE5_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE5_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE5_TEP_S 0
+#define MUSB_CCONF_TXEDMA 0x0002 // TX Early DMA Enable
+#define MUSB_CCONF_RXEDMA 0x0001 // TX Early DMA Enable
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL5
+// The following are defines for the bit fields in the MUSB_O_ULPIVBUSCTL
// register.
//
//*****************************************************************************
-#define USB_TXINTERVAL5_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL5_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL5_NAKLMT_S \
- 0
-#define USB_TXINTERVAL5_TXPOLL_S \
- 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXTYPE5 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE5_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE5_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE5_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE5_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE5_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE5_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE5_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE5_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE5_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE5_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE5_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE5_TEP_S 0
+#define MUSB_ULPIVBUSCTL_USEEXTVBUSIND 0x0002 // Use External VBUS Indicator
+#define MUSB_ULPIVBUSCTL_USEEXTVBUS 0x0001 // Use External VBUS
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL5
+// The following are defines for the bit fields in the MUSB_O_ULPIREGDATA
// register.
//
//*****************************************************************************
-#define USB_RXINTERVAL5_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL5_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL5_TXPOLL_S \
- 0
-#define USB_RXINTERVAL5_NAKLMT_S \
- 0
-
+#define MUSB_ULPIREGDATA_REGDATA_M 0x00FF // Register Data
+#define MUSB_ULPIREGDATA_REGDATA_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXMAXP6 register.
-//
-//*****************************************************************************
-#define USB_TXMAXP6_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP6_MAXLOAD_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRL6 register.
-//
-//*****************************************************************************
-#define USB_TXCSRL6_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL6_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL6_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL6_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL6_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL6_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL6_ERROR 0x00000004 // Error
-#define USB_TXCSRL6_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL6_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL6_TXRDY 0x00000001 // Transmit Packet Ready
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXCSRH6 register.
-//
-//*****************************************************************************
-#define USB_TXCSRH6_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH6_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH6_MODE 0x00000020 // Mode
-#define USB_TXCSRH6_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH6_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH6_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH6_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH6_DT 0x00000001 // Data Toggle
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXMAXP6 register.
-//
-//*****************************************************************************
-#define USB_RXMAXP6_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP6_MAXLOAD_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRL6 register.
-//
-//*****************************************************************************
-#define USB_RXCSRL6_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL6_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL6_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL6_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL6_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL6_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL6_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL6_ERROR 0x00000004 // Error
-#define USB_RXCSRL6_OVER 0x00000004 // Overrun
-#define USB_RXCSRL6_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL6_RXRDY 0x00000001 // Receive Packet Ready
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCSRH6 register.
-//
-//*****************************************************************************
-#define USB_RXCSRH6_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH6_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH6_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH6_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH6_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH6_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH6_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH6_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH6_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH6_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXCOUNT6 register.
-//
-//*****************************************************************************
-#define USB_RXCOUNT6_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT6_COUNT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXTYPE6 register.
-//
-//*****************************************************************************
-#define USB_TXTYPE6_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE6_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE6_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE6_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE6_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE6_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE6_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE6_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE6_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE6_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE6_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE6_TEP_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL6
+// The following are defines for the bit fields in the MUSB_O_ULPIREGADDR
// register.
//
//*****************************************************************************
-#define USB_TXINTERVAL6_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL6_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL6_TXPOLL_S \
- 0
-#define USB_TXINTERVAL6_NAKLMT_S \
- 0
+#define MUSB_ULPIREGADDR_ADDR_M 0x00FF // Register Address
+#define MUSB_ULPIREGADDR_ADDR_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXTYPE6 register.
-//
-//*****************************************************************************
-#define USB_RXTYPE6_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE6_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE6_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE6_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE6_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE6_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE6_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE6_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE6_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE6_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE6_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE6_TEP_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL6
+// The following are defines for the bit fields in the MUSB_O_ULPIREGCTL
// register.
//
//*****************************************************************************
-#define USB_RXINTERVAL6_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL6_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL6_NAKLMT_S \
- 0
-#define USB_RXINTERVAL6_TXPOLL_S \
- 0
+#define MUSB_ULPIREGCTL_RDWR 0x0004 // Read/Write Control
+#define MUSB_ULPIREGCTL_REGCMPLT 0x0002 // Register Access Complete
+#define MUSB_ULPIREGCTL_REGACC 0x0001 // Initiate Register Access
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXMAXP7 register.
+// The following are defines for the bit fields in the MUSB_O_EPINFO register.
//
//*****************************************************************************
-#define USB_TXMAXP7_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_TXMAXP7_MAXLOAD_S 0
+#define MUSB_EPINFO_RXEP_M 0x00F0 // RX Endpoints
+#define MUSB_EPINFO_TXEP_M 0x000F // TX Endpoints
+#define MUSB_EPINFO_RXEP_S 4
+#define MUSB_EPINFO_TXEP_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXCSRL7 register.
+// The following are defines for the bit fields in the MUSB_O_RAMINFO register.
//
//*****************************************************************************
-#define USB_TXCSRL7_NAKTO 0x00000080 // NAK Timeout
-#define USB_TXCSRL7_CLRDT 0x00000040 // Clear Data Toggle
-#define USB_TXCSRL7_STALLED 0x00000020 // Endpoint Stalled
-#define USB_TXCSRL7_STALL 0x00000010 // Send STALL
-#define USB_TXCSRL7_SETUP 0x00000010 // Setup Packet
-#define USB_TXCSRL7_FLUSH 0x00000008 // Flush FIFO
-#define USB_TXCSRL7_ERROR 0x00000004 // Error
-#define USB_TXCSRL7_UNDRN 0x00000004 // Underrun
-#define USB_TXCSRL7_FIFONE 0x00000002 // FIFO Not Empty
-#define USB_TXCSRL7_TXRDY 0x00000001 // Transmit Packet Ready
+#define MUSB_RAMINFO_DMACHAN_M 0x00F0 // DMA Channels
+#define MUSB_RAMINFO_RAMBITS_M 0x000F // RAM Address Bus Width
+#define MUSB_RAMINFO_DMACHAN_S 4
+#define MUSB_RAMINFO_RAMBITS_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXCSRH7 register.
+// The following are defines for the bit fields in the MUSB_O_CONTIM register.
//
//*****************************************************************************
-#define USB_TXCSRH7_AUTOSET 0x00000080 // Auto Set
-#define USB_TXCSRH7_ISO 0x00000040 // Isochronous Transfers
-#define USB_TXCSRH7_MODE 0x00000020 // Mode
-#define USB_TXCSRH7_DMAEN 0x00000010 // DMA Request Enable
-#define USB_TXCSRH7_FDT 0x00000008 // Force Data Toggle
-#define USB_TXCSRH7_DMAMOD 0x00000004 // DMA Request Mode
-#define USB_TXCSRH7_DTWE 0x00000002 // Data Toggle Write Enable
-#define USB_TXCSRH7_DT 0x00000001 // Data Toggle
+#define MUSB_CONTIM_WTCON_M 0x00F0 // Connect Wait
+#define MUSB_CONTIM_WTID_M 0x000F // Wait ID
+#define MUSB_CONTIM_WTCON_S 4
+#define MUSB_CONTIM_WTID_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXMAXP7 register.
+// The following are defines for the bit fields in the MUSB_O_VPLEN register.
//
//*****************************************************************************
-#define USB_RXMAXP7_MAXLOAD_M 0x000007FF // Maximum Payload
-#define USB_RXMAXP7_MAXLOAD_S 0
+#define MUSB_VPLEN_VPLEN_M 0x00FF // VBUS Pulse Length
+#define MUSB_VPLEN_VPLEN_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCSRL7 register.
+// The following are defines for the bit fields in the MUSB_O_HSEOF register.
//
//*****************************************************************************
-#define USB_RXCSRL7_CLRDT 0x00000080 // Clear Data Toggle
-#define USB_RXCSRL7_STALLED 0x00000040 // Endpoint Stalled
-#define USB_RXCSRL7_REQPKT 0x00000020 // Request Packet
-#define USB_RXCSRL7_STALL 0x00000020 // Send STALL
-#define USB_RXCSRL7_FLUSH 0x00000010 // Flush FIFO
-#define USB_RXCSRL7_DATAERR 0x00000008 // Data Error
-#define USB_RXCSRL7_NAKTO 0x00000008 // NAK Timeout
-#define USB_RXCSRL7_ERROR 0x00000004 // Error
-#define USB_RXCSRL7_OVER 0x00000004 // Overrun
-#define USB_RXCSRL7_FULL 0x00000002 // FIFO Full
-#define USB_RXCSRL7_RXRDY 0x00000001 // Receive Packet Ready
+#define MUSB_HSEOF_HSEOFG_M 0x00FF // HIgh-Speed End-of-Frame Gap
+#define MUSB_HSEOF_HSEOFG_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCSRH7 register.
+// The following are defines for the bit fields in the MUSB_O_FSEOF register.
//
//*****************************************************************************
-#define USB_RXCSRH7_AUTOCL 0x00000080 // Auto Clear
-#define USB_RXCSRH7_ISO 0x00000040 // Isochronous Transfers
-#define USB_RXCSRH7_AUTORQ 0x00000040 // Auto Request
-#define USB_RXCSRH7_DMAEN 0x00000020 // DMA Request Enable
-#define USB_RXCSRH7_PIDERR 0x00000010 // PID Error
-#define USB_RXCSRH7_DISNYET 0x00000010 // Disable NYET
-#define USB_RXCSRH7_DMAMOD 0x00000008 // DMA Request Mode
-#define USB_RXCSRH7_DTWE 0x00000004 // Data Toggle Write Enable
-#define USB_RXCSRH7_DT 0x00000002 // Data Toggle
-#define USB_RXCSRH7_INCOMPRX 0x00000001 // Incomplete RX Transmission
- // Status
+#define MUSB_FSEOF_FSEOFG_M 0x00FF // Full-Speed End-of-Frame Gap
+#define MUSB_FSEOF_FSEOFG_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXCOUNT7 register.
+// The following are defines for the bit fields in the MUSB_O_LSEOF register.
//
//*****************************************************************************
-#define USB_RXCOUNT7_COUNT_M 0x00001FFF // Receive Packet Count
-#define USB_RXCOUNT7_COUNT_S 0
+#define MUSB_LSEOF_LSEOFG_M 0x00FF // Low-Speed End-of-Frame Gap
+#define MUSB_LSEOF_LSEOFG_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXTYPE7 register.
+// The following are defines for the bit fields in the MUSB_O_CSRL0 register.
//
//*****************************************************************************
-#define USB_TXTYPE7_SPEED_M 0x000000C0 // Operating Speed
-#define USB_TXTYPE7_SPEED_DFLT 0x00000000 // Default
-#define USB_TXTYPE7_SPEED_HIGH 0x00000040 // High
-#define USB_TXTYPE7_SPEED_FULL 0x00000080 // Full
-#define USB_TXTYPE7_SPEED_LOW 0x000000C0 // Low
-#define USB_TXTYPE7_PROTO_M 0x00000030 // Protocol
-#define USB_TXTYPE7_PROTO_CTRL 0x00000000 // Control
-#define USB_TXTYPE7_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_TXTYPE7_PROTO_BULK 0x00000020 // Bulk
-#define USB_TXTYPE7_PROTO_INT 0x00000030 // Interrupt
-#define USB_TXTYPE7_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_TXTYPE7_TEP_S 0
+#define MUSB_CSRL0_NAKTO 0x0080 // NAK Timeout
+#define MUSB_CSRL0_SETENDC 0x0080 // Setup End Clear
+#define MUSB_CSRL0_STATUS 0x0040 // STATUS Packet
+#define MUSB_CSRL0_RXRDYC 0x0040 // RXRDY Clear
+#define MUSB_CSRL0_REQPKT 0x0020 // Request Packet
+#define MUSB_CSRL0_STALL 0x0020 // Send Stall
+#define MUSB_CSRL0_SETEND 0x0010 // Setup End
+#define MUSB_CSRL0_ERROR 0x0010 // Error
+#define MUSB_CSRL0_DATAEND 0x0008 // Data End
+#define MUSB_CSRL0_SETUP 0x0008 // Setup Packet
+#define MUSB_CSRL0_STALLED 0x0004 // Endpoint Stalled
+#define MUSB_CSRL0_TXRDY 0x0002 // Transmit Packet Ready
+#define MUSB_CSRL0_RXRDY 0x0001 // Receive Packet Ready
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXINTERVAL7
-// register.
+// The following are defines for the bit fields in the MUSB_O_CSRH0 register.
//
//*****************************************************************************
-#define USB_TXINTERVAL7_TXPOLL_M \
- 0x000000FF // TX Polling
-#define USB_TXINTERVAL7_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_TXINTERVAL7_NAKLMT_S \
- 0
-#define USB_TXINTERVAL7_TXPOLL_S \
- 0
+#define MUSB_CSRH0_DISPING 0x0008 // PING Disable
+#define MUSB_CSRH0_DTWE 0x0004 // Data Toggle Write Enable
+#define MUSB_CSRH0_DT 0x0002 // Data Toggle
+#define MUSB_CSRH0_FLUSH 0x0001 // Flush FIFO
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXTYPE7 register.
+// The following are defines for the bit fields in the MUSB_O_TYPE0 register.
//
//*****************************************************************************
-#define USB_RXTYPE7_SPEED_M 0x000000C0 // Operating Speed
-#define USB_RXTYPE7_SPEED_DFLT 0x00000000 // Default
-#define USB_RXTYPE7_SPEED_HIGH 0x00000040 // High
-#define USB_RXTYPE7_SPEED_FULL 0x00000080 // Full
-#define USB_RXTYPE7_SPEED_LOW 0x000000C0 // Low
-#define USB_RXTYPE7_PROTO_M 0x00000030 // Protocol
-#define USB_RXTYPE7_PROTO_CTRL 0x00000000 // Control
-#define USB_RXTYPE7_PROTO_ISOC 0x00000010 // Isochronous
-#define USB_RXTYPE7_PROTO_BULK 0x00000020 // Bulk
-#define USB_RXTYPE7_PROTO_INT 0x00000030 // Interrupt
-#define USB_RXTYPE7_TEP_M 0x0000000F // Target Endpoint Number
-#define USB_RXTYPE7_TEP_S 0
+#define MUSB_TYPE0_SPEED_M 0x00C0 // Operating Speed
+#define MUSB_TYPE0_SPEED_HIGH 0x0040 // High
+#define MUSB_TYPE0_SPEED_FULL 0x0080 // Full
+#define MUSB_TYPE0_SPEED_LOW 0x00C0 // Low
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXINTERVAL7
-// register.
-//
-//*****************************************************************************
-#define USB_RXINTERVAL7_TXPOLL_M \
- 0x000000FF // RX Polling
-#define USB_RXINTERVAL7_NAKLMT_M \
- 0x000000FF // NAK Limit
-#define USB_RXINTERVAL7_NAKLMT_S \
- 0
-#define USB_RXINTERVAL7_TXPOLL_S \
- 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAINTR register.
-//
-//*****************************************************************************
-#define USB_DMAINTR_CH7 0x00000080 // Channel 7 DMA Interrupt
-#define USB_DMAINTR_CH6 0x00000040 // Channel 6 DMA Interrupt
-#define USB_DMAINTR_CH5 0x00000020 // Channel 5 DMA Interrupt
-#define USB_DMAINTR_CH4 0x00000010 // Channel 4 DMA Interrupt
-#define USB_DMAINTR_CH3 0x00000008 // Channel 3 DMA Interrupt
-#define USB_DMAINTR_CH2 0x00000004 // Channel 2 DMA Interrupt
-#define USB_DMAINTR_CH1 0x00000002 // Channel 1 DMA Interrupt
-#define USB_DMAINTR_CH0 0x00000001 // Channel 0 DMA Interrupt
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACTL0 register.
-//
-//*****************************************************************************
-#define USB_DMACTL0_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL0_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL0_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL0_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL0_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL0_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL0_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL0_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL0_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL0_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL0_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL0_EP_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAADDR0 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR0_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR0_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT0
-// register.
-//
-//*****************************************************************************
-#define USB_DMACOUNT0_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT0_COUNT_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACTL1 register.
-//
-//*****************************************************************************
-#define USB_DMACTL1_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL1_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL1_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL1_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL1_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL1_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL1_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL1_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL1_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL1_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL1_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL1_EP_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAADDR1 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR1_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR1_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT1
-// register.
-//
-//*****************************************************************************
-#define USB_DMACOUNT1_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT1_COUNT_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACTL2 register.
-//
-//*****************************************************************************
-#define USB_DMACTL2_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL2_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL2_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL2_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL2_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL2_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL2_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL2_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL2_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL2_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL2_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL2_EP_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAADDR2 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR2_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR2_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT2
-// register.
-//
-//*****************************************************************************
-#define USB_DMACOUNT2_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT2_COUNT_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACTL3 register.
+// The following are defines for the bit fields in the MUSB_O_NAKLMT register.
//
//*****************************************************************************
-#define USB_DMACTL3_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL3_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL3_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL3_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL3_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL3_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL3_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL3_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL3_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL3_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL3_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL3_EP_S 4
+#define MUSB_NAKLMT_NAKLMT_M 0x001F // EP0 NAK Limit
+#define MUSB_NAKLMT_NAKLMT_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMAADDR3 register.
+// The following are defines for the bit fields in the MUSB_O_TXCSRL1 register.
//
//*****************************************************************************
-#define USB_DMAADDR3_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR3_ADDR_S 2
+#define MUSB_TXCSRL1_NAKTO 0x0080 // NAK Timeout
+#define MUSB_TXCSRL1_CLRDT 0x0040 // Clear Data Toggle
+#define MUSB_TXCSRL1_STALLED 0x0020 // Endpoint Stalled
+#define MUSB_TXCSRL1_STALL 0x0010 // Send STALL
+#define MUSB_TXCSRL1_SETUP 0x0010 // Setup Packet
+#define MUSB_TXCSRL1_FLUSH 0x0008 // Flush FIFO
+#define MUSB_TXCSRL1_ERROR 0x0004 // Error
+#define MUSB_TXCSRL1_UNDRN 0x0004 // Underrun
+#define MUSB_TXCSRL1_FIFONE 0x0002 // FIFO Not Empty
+#define MUSB_TXCSRL1_TXRDY 0x0001 // Transmit Packet Ready
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACOUNT3
-// register.
+// The following are defines for the bit fields in the MUSB_O_TXCSRH1 register.
//
//*****************************************************************************
-#define USB_DMACOUNT3_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT3_COUNT_S 2
+#define MUSB_TXCSRH1_AUTOSET 0x0080 // Auto Set
+#define MUSB_TXCSRH1_ISO 0x0040 // Isochronous Transfers
+#define MUSB_TXCSRH1_MODE 0x0020 // Mode
+#define MUSB_TXCSRH1_DMAEN 0x0010 // DMA Request Enable
+#define MUSB_TXCSRH1_FDT 0x0008 // Force Data Toggle
+#define MUSB_TXCSRH1_DMAMOD 0x0004 // DMA Request Mode
+#define MUSB_TXCSRH1_DTWE 0x0002 // Data Toggle Write Enable
+#define MUSB_TXCSRH1_DT 0x0001 // Data Toggle
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACTL4 register.
+// The following are defines for the bit fields in the MUSB_O_RXCSRL1 register.
//
//*****************************************************************************
-#define USB_DMACTL4_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL4_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL4_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL4_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL4_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL4_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL4_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL4_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL4_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL4_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL4_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL4_EP_S 4
+#define MUSB_RXCSRL1_CLRDT 0x0080 // Clear Data Toggle
+#define MUSB_RXCSRL1_STALLED 0x0040 // Endpoint Stalled
+#define MUSB_RXCSRL1_STALL 0x0020 // Send STALL
+#define MUSB_RXCSRL1_REQPKT 0x0020 // Request Packet
+#define MUSB_RXCSRL1_FLUSH 0x0010 // Flush FIFO
+#define MUSB_RXCSRL1_DATAERR 0x0008 // Data Error
+#define MUSB_RXCSRL1_NAKTO 0x0008 // NAK Timeout
+#define MUSB_RXCSRL1_OVER 0x0004 // Overrun
+#define MUSB_RXCSRL1_ERROR 0x0004 // Error
+#define MUSB_RXCSRL1_FULL 0x0002 // FIFO Full
+#define MUSB_RXCSRL1_RXRDY 0x0001 // Receive Packet Ready
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMAADDR4 register.
+// The following are defines for the bit fields in the MUSB_O_RXCSRH1 register.
//
//*****************************************************************************
-#define USB_DMAADDR4_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR4_ADDR_S 2
+#define MUSB_RXCSRH1_AUTOCL 0x0080 // Auto Clear
+#define MUSB_RXCSRH1_AUTORQ 0x0040 // Auto Request
+#define MUSB_RXCSRH1_ISO 0x0040 // Isochronous Transfers
+#define MUSB_RXCSRH1_DMAEN 0x0020 // DMA Request Enable
+#define MUSB_RXCSRH1_DISNYET 0x0010 // Disable NYET
+#define MUSB_RXCSRH1_PIDERR 0x0010 // PID Error
+#define MUSB_RXCSRH1_DMAMOD 0x0008 // DMA Request Mode
+#define MUSB_RXCSRH1_DTWE 0x0004 // Data Toggle Write Enable
+#define MUSB_RXCSRH1_DT 0x0002 // Data Toggle
+#define MUSB_RXCSRH1_INCOMPRX 0x0001 // Incomplete RX Transmission Status
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACOUNT4
-// register.
+// The following are defines for the bit fields in the MUSB_O_TXTYPE1 register.
//
//*****************************************************************************
-#define USB_DMACOUNT4_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT4_COUNT_S 2
+#define MUSB_TXTYPE1_SPEED_M 0x00C0 // Operating Speed
+#define MUSB_TXTYPE1_SPEED_DFLT 0x0000 // Default
+#define MUSB_TXTYPE1_SPEED_HIGH 0x0040 // High
+#define MUSB_TXTYPE1_SPEED_FULL 0x0080 // Full
+#define MUSB_TXTYPE1_SPEED_LOW 0x00C0 // Low
+#define MUSB_TXTYPE1_PROTO_M 0x0030 // Protocol
+#define MUSB_TXTYPE1_PROTO_CTRL 0x0000 // Control
+#define MUSB_TXTYPE1_PROTO_ISOC 0x0010 // Isochronous
+#define MUSB_TXTYPE1_PROTO_BULK 0x0020 // Bulk
+#define MUSB_TXTYPE1_PROTO_INT 0x0030 // Interrupt
+#define MUSB_TXTYPE1_TEP_M 0x000F // Target Endpoint Number
+#define MUSB_TXTYPE1_TEP_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACTL5 register.
-//
-//*****************************************************************************
-#define USB_DMACTL5_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL5_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL5_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL5_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL5_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL5_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL5_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL5_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL5_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL5_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL5_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL5_EP_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAADDR5 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR5_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR5_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT5
+// The following are defines for the bit fields in the MUSB_O_TXINTERVAL1
// register.
//
//*****************************************************************************
-#define USB_DMACOUNT5_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT5_COUNT_S 2
+#define MUSB_TXINTERVAL1_NAKLMT_M 0x00FF // NAK Limit
+#define MUSB_TXINTERVAL1_TXPOLL_M 0x00FF // TX Polling
+#define MUSB_TXINTERVAL1_TXPOLL_S 0
+#define MUSB_TXINTERVAL1_NAKLMT_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACTL6 register.
+// The following are defines for the bit fields in the MUSB_O_RXTYPE1 register.
//
//*****************************************************************************
-#define USB_DMACTL6_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL6_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL6_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL6_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
- // length
-#define USB_DMACTL6_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
- // unspecified length
-#define USB_DMACTL6_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL6_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL6_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL6_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL6_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL6_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL6_EP_S 4
+#define MUSB_RXTYPE1_SPEED_M 0x00C0 // Operating Speed
+#define MUSB_RXTYPE1_SPEED_DFLT 0x0000 // Default
+#define MUSB_RXTYPE1_SPEED_HIGH 0x0040 // High
+#define MUSB_RXTYPE1_SPEED_FULL 0x0080 // Full
+#define MUSB_RXTYPE1_SPEED_LOW 0x00C0 // Low
+#define MUSB_RXTYPE1_PROTO_M 0x0030 // Protocol
+#define MUSB_RXTYPE1_PROTO_CTRL 0x0000 // Control
+#define MUSB_RXTYPE1_PROTO_ISOC 0x0010 // Isochronous
+#define MUSB_RXTYPE1_PROTO_BULK 0x0020 // Bulk
+#define MUSB_RXTYPE1_PROTO_INT 0x0030 // Interrupt
+#define MUSB_RXTYPE1_TEP_M 0x000F // Target Endpoint Number
+#define MUSB_RXTYPE1_TEP_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMAADDR6 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR6_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR6_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT6
+// The following are defines for the bit fields in the MUSB_O_RXINTERVAL1
// register.
//
//*****************************************************************************
-#define USB_DMACOUNT6_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT6_COUNT_S 2
+#define MUSB_RXINTERVAL1_TXPOLL_M 0x00FF // RX Polling
+#define MUSB_RXINTERVAL1_NAKLMT_M 0x00FF // NAK Limit
+#define MUSB_RXINTERVAL1_TXPOLL_S 0
+#define MUSB_RXINTERVAL1_NAKLMT_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_DMACTL7 register.
+// The following are defines for the bit fields in the MUSB_O_DMACTL0 register.
//
//*****************************************************************************
-#define USB_DMACTL7_BRSTM_M 0x00000600 // Burst Mode
-#define USB_DMACTL7_BRSTM_ANY 0x00000000 // Bursts of unspecified length
-#define USB_DMACTL7_BRSTM_INC4 0x00000200 // INCR4 or unspecified length
-#define USB_DMACTL7_BRSTM_INC8 0x00000400 // INCR8, INCR4 or unspecified
+#define MUSB_DMACTL0_BRSTM_M 0x0600 // Burst Mode
+#define MUSB_DMACTL0_BRSTM_ANY 0x0000 // Bursts of unspecified length
+#define MUSB_DMACTL0_BRSTM_INC4 0x0200 // INCR4 or unspecified length
+#define MUSB_DMACTL0_BRSTM_INC8 0x0400 // INCR8, INCR4 or unspecified
// length
-#define USB_DMACTL7_BRSTM_INC16 0x00000600 // INCR16, INCR8, INCR4 or
+#define MUSB_DMACTL0_BRSTM_INC16 0x0600 // INCR16, INCR8, INCR4 or
// unspecified length
-#define USB_DMACTL7_ERR 0x00000100 // Bus Error Bit
-#define USB_DMACTL7_EP_M 0x000000F0 // Endpoint number
-#define USB_DMACTL7_IE 0x00000008 // DMA Interrupt Enable
-#define USB_DMACTL7_MODE 0x00000004 // DMA Transfer Mode
-#define USB_DMACTL7_DIR 0x00000002 // DMA Direction
-#define USB_DMACTL7_ENABLE 0x00000001 // DMA Transfer Enable
-#define USB_DMACTL7_EP_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMAADDR7 register.
-//
-//*****************************************************************************
-#define USB_DMAADDR7_ADDR_M 0xFFFFFFFC // DMA Address
-#define USB_DMAADDR7_ADDR_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DMACOUNT7
-// register.
-//
-//*****************************************************************************
-#define USB_DMACOUNT7_COUNT_M 0xFFFFFFFC // DMA Count
-#define USB_DMACOUNT7_COUNT_S 2
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT1
-// register.
-//
-//*****************************************************************************
-#define USB_RQPKTCOUNT1_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT1_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT2
-// register.
-//
-//*****************************************************************************
-#define USB_RQPKTCOUNT2_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT2_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT3
-// register.
-//
-//*****************************************************************************
-#define USB_RQPKTCOUNT3_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT3_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT4
-// register.
-//
-//*****************************************************************************
-#define USB_RQPKTCOUNT4_COUNT_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT4_COUNT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT5
-// register.
-//
-//*****************************************************************************
-#define USB_RQPKTCOUNT5_COUNT_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT5_COUNT_S 0
+#define MUSB_DMACTL0_ERR 0x0100 // Bus Error Bit
+#define MUSB_DMACTL0_EP_M 0x00F0 // Endpoint number
+#define MUSB_DMACTL0_IE 0x0008 // DMA Interrupt Enable
+#define MUSB_DMACTL0_MODE 0x0004 // DMA Transfer Mode
+#define MUSB_DMACTL0_DIR 0x0002 // DMA Direction
+#define MUSB_DMACTL0_ENABLE 0x0001 // DMA Transfer Enable
+#define MUSB_DMACTL0_EP_S 4
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT6
-// register.
+// The following are defines for the bit fields in the MUSB_O_DMAADDR0 register.
//
//*****************************************************************************
-#define USB_RQPKTCOUNT6_COUNT_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT6_COUNT_S 0
+#define MUSB_DMAADDR0_ADDR_M 0xFFFFFFFC // DMA Address
+#define MUSB_DMAADDR0_ADDR_S 2
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RQPKTCOUNT7
+// The following are defines for the bit fields in the MUSB_O_DMACOUNT0
// register.
//
//*****************************************************************************
-#define USB_RQPKTCOUNT7_COUNT_M 0x0000FFFF // Block Transfer Packet Count
-#define USB_RQPKTCOUNT7_COUNT_S 0
+#define MUSB_DMACOUNT0_COUNT_M 0xFFFFFFFC // DMA Count
+#define MUSB_DMACOUNT0_COUNT_S 2
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_RXDPKTBUFDIS
-// register.
+// The following are defines for the bit fields in the MUSB_O_CTO register.
//
//*****************************************************************************
-#define USB_RXDPKTBUFDIS_EP7 0x00000080 // EP7 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP6 0x00000040 // EP6 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP5 0x00000020 // EP5 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP4 0x00000010 // EP4 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP3 0x00000008 // EP3 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP2 0x00000004 // EP2 RX Double-Packet Buffer
- // Disable
-#define USB_RXDPKTBUFDIS_EP1 0x00000002 // EP1 RX Double-Packet Buffer
- // Disable
+#define MUSB_CTO_CCTV_M 0xFFFF // Configurable Chirp Timeout Value
+#define MUSB_CTO_CCTV_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_TXDPKTBUFDIS
-// register.
+// The following are defines for the bit fields in the MUSB_O_HHSRTN register.
//
//*****************************************************************************
-#define USB_TXDPKTBUFDIS_EP7 0x00000080 // EP7 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP6 0x00000040 // EP6 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP5 0x00000020 // EP5 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP4 0x00000010 // EP4 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP3 0x00000008 // EP3 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP2 0x00000004 // EP2 TX Double-Packet Buffer
- // Disable
-#define USB_TXDPKTBUFDIS_EP1 0x00000002 // EP1 TX Double-Packet Buffer
- // Disable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_CTO register.
-//
-//*****************************************************************************
-#define USB_CTO_CCTV_M 0x0000FFFF // Configurable Chirp Timeout Value
-#define USB_CTO_CCTV_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_HHSRTN register.
-//
-//*****************************************************************************
-#define USB_HHSRTN_HHSRTN_M 0x0000FFFF // HIgh Speed to UTM Operating
+#define MUSB_HHSRTN_HHSRTN_M 0xFFFF // HIgh Speed to UTM Operating
// Delay
-#define USB_HHSRTN_HHSRTN_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_HSBT register.
-//
-//*****************************************************************************
-#define USB_HSBT_HSBT_M 0x0000000F // High Speed Timeout Adder
-#define USB_HSBT_HSBT_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LPMATTR register.
-//
-//*****************************************************************************
-#define USB_LPMATTR_ENDPT_M 0x0000F000 // Endpoint
-#define USB_LPMATTR_RMTWAK 0x00000100 // Remote Wake
-#define USB_LPMATTR_HIRD_M 0x000000F0 // Host Initiated Resume Duration
-#define USB_LPMATTR_LS_M 0x0000000F // Link State
-#define USB_LPMATTR_LS_L1 0x00000001 // Sleep State (L1)
-#define USB_LPMATTR_ENDPT_S 12
-#define USB_LPMATTR_HIRD_S 4
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LPMCNTRL register.
-//
-//*****************************************************************************
-#define USB_LPMCNTRL_NAK 0x00000010 // LPM NAK
-#define USB_LPMCNTRL_EN_M 0x0000000C // LPM Enable
-#define USB_LPMCNTRL_EN_NONE 0x00000000 // LPM and Extended transactions
- // are not supported. In this case,
- // the USB does not respond to LPM
- // transactions and LPM
- // transactions cause a timeout
-#define USB_LPMCNTRL_EN_EXT 0x00000004 // LPM is not supported but
- // extended transactions are
- // supported. In this case, the USB
- // does respond to an LPM
- // transaction with a STALL
-#define USB_LPMCNTRL_EN_LPMEXT 0x0000000C // The USB supports LPM extended
- // transactions. In this case, the
- // USB responds with a NYET or an
- // ACK as determined by the value
- // of TXLPM and other conditions
-#define USB_LPMCNTRL_RES 0x00000002 // LPM Resume
-#define USB_LPMCNTRL_TXLPM 0x00000001 // Transmit LPM Transaction Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LPMIM register.
-//
-//*****************************************************************************
-#define USB_LPMIM_ERR 0x00000020 // LPM Error Interrupt Mask
-#define USB_LPMIM_RES 0x00000010 // LPM Resume Interrupt Mask
-#define USB_LPMIM_NC 0x00000008 // LPM NC Interrupt Mask
-#define USB_LPMIM_ACK 0x00000004 // LPM ACK Interrupt Mask
-#define USB_LPMIM_NY 0x00000002 // LPM NY Interrupt Mask
-#define USB_LPMIM_STALL 0x00000001 // LPM STALL Interrupt Mask
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LPMRIS register.
-//
-//*****************************************************************************
-#define USB_LPMRIS_ERR 0x00000020 // LPM Interrupt Status
-#define USB_LPMRIS_RES 0x00000010 // LPM Resume Interrupt Status
-#define USB_LPMRIS_NC 0x00000008 // LPM NC Interrupt Status
-#define USB_LPMRIS_ACK 0x00000004 // LPM ACK Interrupt Status
-#define USB_LPMRIS_NY 0x00000002 // LPM NY Interrupt Status
-#define USB_LPMRIS_LPMST 0x00000001 // LPM STALL Interrupt Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_LPMFADDR register.
-//
-//*****************************************************************************
-#define USB_LPMFADDR_ADDR_M 0x0000007F // LPM Function Address
-#define USB_LPMFADDR_ADDR_S 0
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPC register.
-//
-//*****************************************************************************
-#define USB_EPC_PFLTACT_M 0x00000300 // Power Fault Action
-#define USB_EPC_PFLTACT_UNCHG 0x00000000 // Unchanged
-#define USB_EPC_PFLTACT_TRIS 0x00000100 // Tristate
-#define USB_EPC_PFLTACT_LOW 0x00000200 // Low
-#define USB_EPC_PFLTACT_HIGH 0x00000300 // High
-#define USB_EPC_PFLTAEN 0x00000040 // Power Fault Action Enable
-#define USB_EPC_PFLTSEN_HIGH 0x00000020 // Power Fault Sense
-#define USB_EPC_PFLTEN 0x00000010 // Power Fault Input Enable
-#define USB_EPC_EPENDE 0x00000004 // EPEN Drive Enable
-#define USB_EPC_EPEN_M 0x00000003 // External Power Supply Enable
- // Configuration
-#define USB_EPC_EPEN_LOW 0x00000000 // Power Enable Active Low
-#define USB_EPC_EPEN_HIGH 0x00000001 // Power Enable Active High
-#define USB_EPC_EPEN_VBLOW 0x00000002 // Power Enable High if VBUS Low
- // (OTG only)
-#define USB_EPC_EPEN_VBHIGH 0x00000003 // Power Enable High if VBUS High
- // (OTG only)
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPCRIS register.
-//
-//*****************************************************************************
-#define USB_EPCRIS_PF 0x00000001 // USB Power Fault Interrupt Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPCIM register.
-//
-//*****************************************************************************
-#define USB_EPCIM_PF 0x00000001 // USB Power Fault Interrupt Mask
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_EPCISC register.
-//
-//*****************************************************************************
-#define USB_EPCISC_PF 0x00000001 // USB Power Fault Interrupt Status
- // and Clear
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DRRIS register.
-//
-//*****************************************************************************
-#define USB_DRRIS_RESUME 0x00000001 // RESUME Interrupt Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DRIM register.
-//
-//*****************************************************************************
-#define USB_DRIM_RESUME 0x00000001 // RESUME Interrupt Mask
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_DRISC register.
-//
-//*****************************************************************************
-#define USB_DRISC_RESUME 0x00000001 // RESUME Interrupt Status and
- // Clear
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_GPCS register.
-//
-//*****************************************************************************
-#define USB_GPCS_DEVMOD_M 0x00000007 // Device Mode
-#define USB_GPCS_DEVMOD_OTG 0x00000000 // Use USB0VBUS and USB0ID pin
-#define USB_GPCS_DEVMOD_HOST 0x00000002 // Force USB0VBUS and USB0ID low
-#define USB_GPCS_DEVMOD_DEV 0x00000003 // Force USB0VBUS and USB0ID high
-#define USB_GPCS_DEVMOD_HOSTVBUS \
- 0x00000004 // Use USB0VBUS and force USB0ID
- // low
-#define USB_GPCS_DEVMOD_DEVVBUS 0x00000005 // Use USB0VBUS and force USB0ID
- // high
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_VDC register.
-//
-//*****************************************************************************
-#define USB_VDC_VBDEN 0x00000001 // VBUS Droop Enable
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_VDCRIS register.
-//
-//*****************************************************************************
-#define USB_VDCRIS_VD 0x00000001 // VBUS Droop Raw Interrupt Status
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_VDCIM register.
-//
-//*****************************************************************************
-#define USB_VDCIM_VD 0x00000001 // VBUS Droop Interrupt Mask
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_VDCISC register.
-//
-//*****************************************************************************
-#define USB_VDCISC_VD 0x00000001 // VBUS Droop Interrupt Status and
- // Clear
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_PP register.
-//
-//*****************************************************************************
-#define USB_PP_ECNT_M 0x0000FF00 // Endpoint Count
-#define USB_PP_USB_M 0x000000C0 // USB Capability
-#define USB_PP_USB_DEVICE 0x00000040 // DEVICE
-#define USB_PP_USB_HOSTDEVICE 0x00000080 // HOST
-#define USB_PP_USB_OTG 0x000000C0 // OTG
-#define USB_PP_ULPI 0x00000020 // ULPI Present
-#define USB_PP_PHY 0x00000010 // PHY Present
-#define USB_PP_TYPE_M 0x0000000F // Controller Type
-#define USB_PP_TYPE_0 0x00000000 // The first-generation USB
- // controller
-#define USB_PP_TYPE_1 0x00000001 // The second-generation USB
- // controller revision
-#define USB_PP_ECNT_S 8
-
-//*****************************************************************************
-//
-// The following are defines for the bit fields in the USB_O_PC register.
-//
-//*****************************************************************************
-#define USB_PC_ULPIEN 0x00010000 // ULPI Enable
+#define MUSB_HHSRTN_HHSRTN_S 0
//*****************************************************************************
//
-// The following are defines for the bit fields in the USB_O_CC register.
+// The following are defines for the bit fields in the MUSB_O_HSBT register.
//
//*****************************************************************************
-#define USB_CC_CLKEN 0x00000200 // USB Clock Enable
-#define USB_CC_CSD 0x00000100 // Clock Source/Direction
-#define USB_CC_CLKDIV_M 0x0000000F // PLL Clock Divisor
-#define USB_CC_CLKDIV_S 0
+#define MUSB_HSBT_HSBT_M 0x000F // High Speed Timeout Adder
+#define MUSB_HSBT_HSBT_S 0
#ifdef __cplusplus
}
diff --git a/src/portable/microchip/pic/README.md b/src/portable/microchip/pic/README.md
new file mode 100644
index 000000000..7ba6a4a96
--- /dev/null
+++ b/src/portable/microchip/pic/README.md
@@ -0,0 +1,51 @@
+# Microchip PIC Chipidea FS Driver
+
+This driver adds support for Microchip PIC microcontrollers with full-speed Chipidea USB peripheral to the TinyUSB stack. It supports the following families:
+
+- PIC32MX (untested)
+- PIC32MM
+- PIC32MK (untested)
+- PIC24FJ
+- PIC24EP (untested)
+- dsPIC33EP (untested)
+
+Currently only the device mode is supported.
+
+
+## Important Notes
+
+### Handling of shared VBUS & GPIO pin
+
+Some PICs have the USB VBUS pin bonded with a GPIO pin in the chip package. This driver does **NOT** handle the potential conflict between the VBUS and GPIO functionalities.
+
+Developers must ensure that the GPIO pin is tristated when the VBUS pin is managed by the USB peripheral in order to prevent damaging the chip.
+
+This design choice allows developers the flexibility to use the GPIO functionality for controlling VBUS in device mode if desired.
+
+
+## TODO
+
+### Handle USB remote wakeup timing correctly
+
+The Chipidea FS IP doesn't handle the RESUME signal automatically and it must be managed in software. It needs to be asserted for exactly 10ms, and this is impossible to do without per-device support due to BSP differences. For now, a simple for-based loop is used.
+
+### 8-bit PIC support
+
+The 8-bit PICs also uses the Chipidea FS IP. Technically it's possible to support them as well.
+
+Possible difficulties:
+- Memory size constraints (1KB/8KB ballpark)
+- A third BDT layout (now we have two)
+- Different compiler-specific directives
+- Compiler bugs if you use SDCC
+
+
+## Author
+[ReimuNotMoe](https://github.com/ReimuNotMoe) at SudoMaker, Ltd.
+
+
+## Credits
+
+This driver is based on:
+- Microchip's USB driver (usb_device.c)
+- TinyUSB's NXP KHCI driver (dcd_khci.c)
diff --git a/src/portable/microchip/pic/dcd_pic.c b/src/portable/microchip/pic/dcd_pic.c
index 6cf0e6285..b4a698199 100644
--- a/src/portable/microchip/pic/dcd_pic.c
+++ b/src/portable/microchip/pic/dcd_pic.c
@@ -1,8 +1,11 @@
-/*
+/*
* The MIT License (MIT)
*
- * Copyright (c) 2020 Koji Kitayama
- * Copyright (c) 2022 Reimu NotMoe <[email protected]>
+ * Copyright (c) 2022-2024 SudoMaker, Ltd.
+ * Author: Mike Yang (Reimu NotMoe) <[email protected]>
+ *
+ * Based on usb_device.c - Copyright (c) 2015 Microchip Technology Inc.
+ * Based on dcd_khci.c - Copyright (c) 2020 Koji Kitayama
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -189,7 +192,7 @@ typedef struct
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// BDT(Buffer Descriptor Table) must be 256-byte aligned
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) volatile static dcd_data_t _dcd;
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) volatile static dcd_data_t _dcd;
#if TU_PIC_INT_SIZE == 4
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
@@ -313,12 +316,23 @@ static void prepare_next_setup_packet(uint8_t rhport)
{
const unsigned out_odd = _dcd.endpoint[0][0].odd;
const unsigned in_odd = _dcd.endpoint[0][1].odd;
- TU_ASSERT(0 == _dcd.bdt[0][0][out_odd].own, );
+
+ // Abandon any previous control transfers that might have been using EP0.
+ // Ordinarily, nothing actually needs abandoning, since the previous control
+ // transfer would have completed successfully prior to the host sending the
+ // next SETUP packet. However, in a timeout error case, or after an EP0
+ // STALL event, one or more UOWN bits might still be set. If so, we should
+ // clear the UOWN bits, so the EP0 IN/OUT endpoints are in a known inactive
+ // state, ready for re-arming by the `dcd_edpt_xfer' function that will be
+ // called next.
_dcd.bdt[0][0][out_odd].data = 0;
+ _dcd.bdt[0][0][out_odd].own = 0;
_dcd.bdt[0][0][out_odd ^ 1].data = 1;
_dcd.bdt[0][1][in_odd].data = 1;
+ _dcd.bdt[0][1][in_odd].own = 0;
_dcd.bdt[0][1][in_odd ^ 1].data = 0;
+ _dcd.bdt[0][1][in_odd ^ 1].own = 0;
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
_dcd.setup_packet, sizeof(_dcd.setup_packet));
}
@@ -470,23 +484,22 @@ static void process_bus_resume(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
intr_disable(rhport);
intr_clear(rhport);
-#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
- TRISBbits.TRISB6 = 1;
-#endif
-
tu_memclr(&_dcd, sizeof(_dcd));
#if TU_PIC_INT_SIZE == 4
+ // The USBBUSY bit is present on PIC32s and we're required to check it
+ // prior to powering on the USB peripheral (see DS61126F page 27)
+ while (U1PWRCbits.USBBUSY);
U1PWRCSET = _U1PWRC_USBPWR_MASK;
#else
U1PWRCbits.USBPWR = 1;
#endif
-
+
#if TU_PIC_INT_SIZE == 4
uint32_t bdt_phys = KVA_TO_PA((uintptr_t)_dcd.bdt);
@@ -502,6 +515,20 @@ void dcd_init(uint8_t rhport)
U1IE = _U1IE_URSTIE_MASK;
dcd_connect(rhport);
+ return true;
+}
+
+bool dcd_deinit(uint8_t rhport)
+{
+ U1CON = 0;
+ U1IE = 0;
+ U1OTGIE = 0;
+#if TU_PIC_INT_SIZE == 4
+ U1PWRCCLR = _U1PWRC_USUSPEND_MASK | _U1PWRC_USBPWR_MASK;
+#else
+ U1PWRC &= ~(_U1PWRC_USUSPEND_MASK | _U1PWRC_USBPWR_MASK);
+#endif
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -528,8 +555,25 @@ void dcd_remote_wakeup(uint8_t rhport)
#else
U1CONbits.RESUME = 1;
#endif
- unsigned cnt = 25000000 / 1000;
+
+ // FIXME: Assert RESUME signal correctly, requires device-specific handling
+ // For now we use a hardcoded cycle-based delay which attempts to delay 10ms
+ // at the most common CPU frequencies. On PIC32s we assume the loop body
+ // takes 3 cycles. On 16-bit PICs we assume the XC16 compiler is in use and
+ // use its `__delay_ms' function.
+
+#if CFG_TUSB_MCU == OPT_MCU_PIC32MM
+ uint32_t cnt = 24000000 / 1000 / 3;
+ while (cnt--) asm volatile("nop");
+#elif CFG_TUSB_MCU == OPT_MCU_PIC32MX
+ uint32_t cnt = 40000000 / 1000 / 3;
while (cnt--) asm volatile("nop");
+#elif CFG_TUSB_MCU == OPT_MCU_PIC32MK
+ uint32_t cnt = 120000000 / 1000 / 3;
+ while (cnt--) asm volatile("nop");
+#else
+ __delay_ms(10);
+#endif
#if TU_PIC_INT_SIZE == 4
U1CONCLR = _U1CON_RESUME_MASK;
@@ -737,8 +781,11 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
void dcd_int_handler(uint8_t rhport)
{
- uint32_t is = U1IR;
- uint32_t msk = U1IE;
+ uint32_t is, msk;
+
+ // Part 1 - "USB interrupts"
+ is = U1IR;
+ msk = U1IE;
U1IR = is & ~msk;
is &= msk;
@@ -746,7 +793,7 @@ void dcd_int_handler(uint8_t rhport)
if (is & _U1IR_UERRIF_MASK) {
uint32_t es = U1EIR;
U1EIR = es;
- U1IR = is; /* discard any pending events */
+ U1IR = is; /* discard any pending events */
}
if (is & _U1IR_URSTIF_MASK) {
@@ -757,29 +804,66 @@ void dcd_int_handler(uint8_t rhport)
if (is & _U1IR_IDLEIF_MASK) {
// Note Host usually has extra delay after bus reset (without SOF), which could falsely
// detected as Sleep event. Though usbd has debouncing logic so we are good
+
+ /*
+ * NOTE: Do not clear U1OTGIRbits.ACTVIF here!
+ * Reason:
+ * ACTVIF is only generated once an IDLEIF has been generated.
+ * This is a 1:1 ratio interrupt generation.
+ * For every IDLEIF, there will be only one ACTVIF regardless of
+ * the number of subsequent bus transitions.
+ *
+ * If the ACTIF is cleared here, a problem could occur when:
+ * [ IDLE ][bus activity ->
+ * <--- 3 ms -----> ^
+ * ^ ACTVIF=1
+ * IDLEIF=1
+ * # # # # (#=Program polling flags)
+ * ^
+ * This polling loop will see both
+ * IDLEIF=1 and ACTVIF=1.
+ * However, the program services IDLEIF first
+ * because ACTIVIE=0.
+ * If this routine clears the only ACTIVIF,
+ * then it can never get out of the suspend
+ * mode.
+ */
+ U1OTGIESET = _U1OTGIE_ACTVIE_MASK;
U1IR = _U1IR_IDLEIF_MASK;
process_bus_sleep(rhport);
}
- if (is & _U1IR_RESUMEIF_MASK) {
- U1IR = _U1IR_RESUMEIF_MASK;
- process_bus_resume(rhport);
- }
-
if (is & _U1IR_SOFIF_MASK) {
U1IR = _U1IR_SOFIF_MASK;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
if (is & _U1IR_STALLIF_MASK) {
- U1IR = _U1IR_STALLIF_MASK;
process_stall(rhport);
+ U1IR = _U1IR_STALLIF_MASK;
}
if (is & _U1IR_TRNIF_MASK) {
process_tokdne(rhport);
}
+ // Part 2 - "USB OTG interrupts"
+ is = U1OTGIR;
+ msk = U1OTGIE;
+
+ U1OTGIR = is & ~msk;
+ is &= msk;
+
+ if (is & _U1OTGIR_ACTVIF_MASK) {
+#if TU_PIC_INT_SIZE == 4
+ U1OTGIECLR = _U1OTGIE_ACTVIE_MASK;
+#else
+ U1OTGIE &= ~_U1OTGIE_ACTVIE_MASK;
+#endif
+ U1OTGIR = _U1OTGIR_ACTVIF_MASK;
+ process_bus_resume(rhport);
+ }
+
intr_clear(rhport);
}
diff --git a/src/portable/microchip/pic32mz/dcd_pic32mz.c b/src/portable/microchip/pic32mz/dcd_pic32mz.c
index 9ad755670..8709baf67 100644
--- a/src/portable/microchip/pic32mz/dcd_pic32mz.c
+++ b/src/portable/microchip/pic32mz/dcd_pic32mz.c
@@ -120,8 +120,8 @@ static ep0_stage_t ep0_get_stage(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
// Disable endpoint interrupts for now
USB_REGS->INTRRXEbits.w = 0;
USB_REGS->INTRTXEbits.w = 0;
@@ -129,6 +129,7 @@ void dcd_init(uint8_t rhport)
USB_REGS->INTRUSBEbits.w = 7;
dcd_connect(rhport);
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/microchip/pic32mz/usbhs_registers.h b/src/portable/microchip/pic32mz/usbhs_registers.h
index 757e3f083..03fe78bbd 100644
--- a/src/portable/microchip/pic32mz/usbhs_registers.h
+++ b/src/portable/microchip/pic32mz/usbhs_registers.h
@@ -21,16 +21,16 @@
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*******************************************************************************/
/*******************************************************************************
- USBHS Peripheral Library Register Defintions
+ USBHS Peripheral Library Register Definitions
File Name:
usbhs_registers.h
Summary:
- USBHS PLIB Register Defintions
+ USBHS PLIB Register Definitions
Description:
- This file contains the constants and defintions which are required by the
+ This file contains the constants and definitions which are required by the
the USBHS library.
*******************************************************************************/
@@ -50,16 +50,16 @@
#define USBHS_REG_INTRRX 0x004
#define USBHS_REG_INTRTXE 0x006
#define USBHS_REG_INTRRXE 0x008
-#define USBHS_REG_INTRUSB 0x00A
-#define USBHS_REG_INTRUSBE 0x00B
+#define USBHS_REG_INTRUSB 0x00A
+#define USBHS_REG_INTRUSBE 0x00B
#define USBHS_REG_FRAME 0x00C
#define USBHS_REG_INDEX 0x00E
#define USBHS_REG_TESTMODE 0x00F
/*******************************************************
- * Endpoint Control Status Registers (CSR). These values
+ * Endpoint Control Status Registers (CSR). These values
* should be added to either the 0x10 to access the
- * register through Indexed CSR. To access the actual
+ * register through Indexed CSR. To access the actual
* CSR, see ahead in this header file.
******************************************************/
@@ -99,20 +99,20 @@
#define USBHS_EP_DEVICE_RX_SEND_STALL 0x20
/* FADDR - Device Function Address */
-typedef union
+typedef union
{
- struct __attribute__((packed))
+ struct __attribute__((packed))
{
unsigned FUNC:7;
unsigned :1;
};
- uint8_t w;
+ uint8_t w;
} __USBHS_FADDR_t;
/* POWER - Control Resume and Suspend signalling */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -126,14 +126,14 @@ typedef union
unsigned ISOUPD:1;
};
struct
- {
+ {
uint8_t w;
};
} __USBHS_POWER_t;
/* INTRTXE - Transmit endpoint interrupt enable */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -155,7 +155,7 @@ typedef union
} __USBHS_INTRTXE_t;
/* INTRRXE - Receive endpoint interrupt enable */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -198,7 +198,7 @@ typedef union
} __USBHS_INTRUSBE_t;
/* FRAME - Frame number */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -213,7 +213,7 @@ typedef union
} __USBHS_FRAME_t;
/* INDEX - Endpoint index */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -228,7 +228,7 @@ typedef union
} __USBHS_INDEX_t;
/* TESTMODE - Test mode register */
-typedef union
+typedef union
{
struct __attribute__((packed))
{
@@ -248,7 +248,7 @@ typedef union
} __USBHS_TESTMODE_t;
-/* COUNT0 - Indicates the amount of data received in endpoint 0 */
+/* COUNT0 - Indicates the amount of data received in endpoint 0 */
typedef union
{
struct __attribute__((packed))
@@ -627,7 +627,7 @@ typedef union
};
uint16_t w;
-} __USBHS_TXMAXP_t;
+} __USBHS_TXMAXP_t;
/* TXFIFOSZ - Size of the transmit endpoint FIFO */
typedef struct __attribute__((packed))
@@ -781,7 +781,7 @@ typedef union
} __USBHS_DMACNTL_t;
-/* Endpoint Control and Status Register Set */
+/* Endpoint Control and Status Register Set */
typedef struct __attribute__((packed))
{
volatile __USBHS_TXMAXP_t TXMAXPbits;
@@ -906,7 +906,7 @@ typedef struct __attribute__((aligned(4),packed))
volatile __USBHS_TXFIFOADD_t TXFIFOADDbits;
volatile __USBHS_RXFIFOADD_t RXFIFOADDbits;
-
+
volatile uint32_t VCONTROL;
volatile uint16_t HWVERS;
volatile uint8_t padding1[10];
@@ -923,7 +923,7 @@ typedef struct __attribute__((aligned(4),packed))
volatile __USBHS_TARGET_ADDR_t TADDR[16];
volatile __USBHS_EPCSR_t EPCSR[16];
volatile uint32_t DMA_INTR;
- volatile __USBHS_DMA_CHANNEL_t DMA_CHANNEL[8];
+ volatile __USBHS_DMA_CHANNEL_t DMA_CHANNEL[8];
volatile uint32_t RQPKTXOUNT[16];
} usbhs_registers_t;
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 976d3dfd0..357aa1549 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -78,9 +78,9 @@ static void bus_reset(void)
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// Reset to get in a clean state.
USB->DEVICE.CTRLA.bit.SWRST = true;
@@ -102,6 +102,8 @@ void dcd_init (uint8_t rhport)
USB->DEVICE.INTFLAG.reg |= USB->DEVICE.INTFLAG.reg; // clear pending
USB->DEVICE.INTENSET.reg = /* USB_DEVICE_INTENSET_SOF | */ USB_DEVICE_INTENSET_EORST;
+
+ return true;
}
#if CFG_TUSB_MCU == OPT_MCU_SAMD51 || CFG_TUSB_MCU == OPT_MCU_SAME5X
@@ -183,9 +185,12 @@ void dcd_connect(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
{
(void) rhport;
- (void) en;
- // TODO implement later
+ if (en) {
+ USB->DEVICE.INTENSET.bit.SOF = 1;
+ } else {
+ USB->DEVICE.INTENCLR.bit.SOF = 1;
+ }
}
/*------------------------------------------------------------------*/
@@ -330,7 +335,7 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
//--------------------------------------------------------------------+
// Interrupt Handler
//--------------------------------------------------------------------+
-void maybe_transfer_complete(void) {
+static void maybe_transfer_complete(void) {
uint32_t epints = USB->DEVICE.EPINTSMRY.reg;
for (uint8_t epnum = 0; epnum < USB_EPT_NUM; epnum++) {
@@ -374,7 +379,9 @@ void dcd_int_handler (uint8_t rhport)
if ( int_status & USB_DEVICE_INTFLAG_SOF )
{
USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
- dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ const uint32_t frame = USB->DEVICE.FNUM.bit.FNUM;
+ dcd_event_sof(0, frame, true);
+ //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
// SAMD doesn't distinguish between Suspend and Disconnect state.
diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c
index 814e680fb..a5c768839 100644
--- a/src/portable/microchip/samg/dcd_samg.c
+++ b/src/portable/microchip/samg/dcd_samg.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
@@ -52,37 +52,31 @@ typedef struct
// Endpoint 0-5, each can only be either OUT or In
xfer_desc_t _dcd_xfer[EP_COUNT];
-void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize) {
xfer->epsize = epsize;
}
-void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes) {
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->actual_len = 0;
}
-void xfer_end(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_end(xfer_desc_t* xfer) {
xfer->buffer = NULL;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = 0;
xfer->actual_len = 0;
}
-uint16_t xfer_packet_len(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t xfer_packet_len(xfer_desc_t* xfer) {
// also cover zero-length packet
return tu_min16(xfer->total_len - xfer->actual_len, xfer->epsize);
}
-void xfer_packet_done(xfer_desc_t* xfer)
-{
+TU_ATTR_ALWAYS_INLINE static inline void xfer_packet_done(xfer_desc_t* xfer) {
uint16_t const xact_len = xfer_packet_len(xfer);
-
xfer->buffer += xact_len;
xfer->actual_len += xact_len;
}
@@ -90,19 +84,15 @@ void xfer_packet_done(xfer_desc_t* xfer)
//------------- Transaction helpers -------------//
// Write data to EP FIFO, return number of written bytes
-static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_write(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
UDP->UDP_FDR[epnum] = (uint32_t) buffer[i];
}
}
// Read data from EP FIFO
-static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
-{
- for(uint16_t i=0; i<xact_len; i++)
- {
+static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len) {
+ for(uint16_t i=0; i<xact_len; i++) {
buffer[i] = (uint8_t) UDP->UDP_FDR[epnum];
}
}
@@ -112,24 +102,24 @@ static void xact_ep_read(uint8_t epnum, uint8_t* buffer, uint16_t xact_len)
#define CSR_NO_EFFECT_1_ALL (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 | UDP_CSR_STALLSENT | UDP_CSR_RXSETUP | UDP_CSR_TXCOMP)
// Per Specs: CSR need synchronization each write
-static inline void csr_write(uint8_t epnum, uint32_t value)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_write(uint8_t epnum, uint32_t value) {
uint32_t const csr = value;
UDP->UDP_CSR[epnum] = csr;
volatile uint32_t nop_count;
- for (nop_count = 0; nop_count < 20; nop_count ++) __NOP();
+ for (nop_count = 0; nop_count < 20; nop_count ++) {
+ __NOP();
+ }
}
// Per Specs: CSR need synchronization each write
-static inline void csr_set(uint8_t epnum, uint32_t mask)
+TU_ATTR_ALWAYS_INLINE static inline void csr_set(uint8_t epnum, uint32_t mask)
{
csr_write(epnum, UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL | mask);
}
// Per Specs: CSR need synchronization each write
-static inline void csr_clear(uint8_t epnum, uint32_t mask)
-{
+TU_ATTR_ALWAYS_INLINE static inline void csr_clear(uint8_t epnum, uint32_t mask) {
csr_write(epnum, (UDP->UDP_CSR[epnum] | CSR_NO_EFFECT_1_ALL) & ~mask);
}
@@ -155,10 +145,13 @@ static void bus_reset(void)
}
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
+
tu_memclr(_dcd_xfer, sizeof(_dcd_xfer));
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
@@ -277,6 +270,11 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
return true;
}
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport; (void) ep_addr;
+ // TODO implement dcd_edpt_close()
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/microchip/samx7x/common_usb_regs.h b/src/portable/microchip/samx7x/common_usb_regs.h
index d232f0bcb..db4a81e0e 100644
--- a/src/portable/microchip/samx7x/common_usb_regs.h
+++ b/src/portable/microchip/samx7x/common_usb_regs.h
@@ -2007,7 +2007,7 @@
/** \brief DEVDMA hardware registers */
typedef struct
-{
+{
__IO uint32_t DEVDMANXTDSC; /**< (DEVDMA Offset: 0x00) Device DMA Channel Next Descriptor Address Register */
__IO uint32_t DEVDMAADDRESS; /**< (DEVDMA Offset: 0x04) Device DMA Channel Address Register */
__IO uint32_t DEVDMACONTROL; /**< (DEVDMA Offset: 0x08) Device DMA Channel Control Register */
@@ -2016,7 +2016,7 @@ typedef struct
/** \brief HSTDMA hardware registers */
typedef struct
-{
+{
__IO uint32_t HSTDMANXTDSC; /**< (HSTDMA Offset: 0x00) Host DMA Channel Next Descriptor Address Register */
__IO uint32_t HSTDMAADDRESS; /**< (HSTDMA Offset: 0x04) Host DMA Channel Address Register */
__IO uint32_t HSTDMACONTROL; /**< (HSTDMA Offset: 0x08) Host DMA Channel Control Register */
@@ -2025,7 +2025,7 @@ typedef struct
/** \brief USBHS hardware registers */
typedef struct
-{
+{
__IO uint32_t DEVCTRL; /**< (USBHS Offset: 0x00) Device General Control Register */
__I uint32_t DEVISR; /**< (USBHS Offset: 0x04) Device Global Interrupt Status Register */
__O uint32_t DEVICR; /**< (USBHS Offset: 0x08) Device Global Interrupt Clear Register */
diff --git a/src/portable/microchip/samx7x/dcd_samx7x.c b/src/portable/microchip/samx7x/dcd_samx7x.c
index 7507c0f69..8aec1568d 100644
--- a/src/portable/microchip/samx7x/dcd_samx7x.c
+++ b/src/portable/microchip/samx7x/dcd_samx7x.c
@@ -42,7 +42,7 @@
# define USE_SOF 0
#endif
-// Dual bank can imporve performance, but need 2 times bigger packet buffer
+// Dual bank can improve performance, but need 2 times bigger packet buffer
// As SAM7x has only 4KB packet buffer, use with caution !
// Enable in FS mode as packets are smaller
#ifndef USE_DUAL_BANK
@@ -77,7 +77,7 @@ static tusb_speed_t get_speed(void);
static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix);
// DMA descriptors shouldn't be placed in ITCM !
-CFG_TUSB_MEM_SECTION static dma_desc_t dma_desc[6];
+CFG_TUD_MEM_SECTION static dma_desc_t dma_desc[6];
static xfer_ctl_t xfer_status[EP_MAX];
@@ -104,9 +104,10 @@ TU_ATTR_ALWAYS_INLINE static inline void CleanInValidateCache(uint32_t *addr, in
//------------------------------------------------------------------
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
dcd_connect(rhport);
+ return true;
}
// Enable device interrupt
@@ -241,7 +242,7 @@ static void dcd_ep_handler(uint8_t ep_ix)
if (int_status & DEVEPTISR_RXOUTI)
{
uint8_t *ptr = EP_GET_FIFO_PTR(0,8);
-
+
if (count && xfer->total_len)
{
uint16_t remain = xfer->total_len - xfer->queued_len;
@@ -252,7 +253,7 @@ static void dcd_ep_handler(uint8_t ep_ix)
if (xfer->buffer)
{
memcpy(xfer->buffer + xfer->queued_len, ptr, count);
- } else
+ } else
{
tu_fifo_write_n(xfer->fifo, ptr, count);
}
@@ -281,7 +282,7 @@ static void dcd_ep_handler(uint8_t ep_ix)
{
// TX not complete
dcd_transmit_packet(xfer, 0);
- } else
+ } else
{
// TX complete
dcd_event_xfer_complete(0, 0x80 + 0, xfer->total_len, XFER_RESULT_SUCCESS, true);
@@ -292,7 +293,7 @@ static void dcd_ep_handler(uint8_t ep_ix)
}
}
}
- } else
+ } else
{
if (int_status & DEVEPTISR_RXOUTI)
{
@@ -333,7 +334,7 @@ static void dcd_ep_handler(uint8_t ep_ix)
{
// TX not complete
dcd_transmit_packet(xfer, ep_ix);
- } else
+ } else
{
// TX complete
dcd_event_xfer_complete(0, 0x80 + ep_ix, xfer->total_len, XFER_RESULT_SUCCESS, true);
@@ -359,7 +360,7 @@ static void dcd_dma_handler(uint8_t ep_ix)
if(USB_REG->DEVEPTCFG[ep_ix] & DEVEPTCFG_EPDIR)
{
dcd_event_xfer_complete(0, 0x80 + ep_ix, count, XFER_RESULT_SUCCESS, true);
- } else
+ } else
{
dcd_event_xfer_complete(0, ep_ix, count, XFER_RESULT_SUCCESS, true);
}
@@ -507,12 +508,12 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
// Enable Endpoint 0 Interrupts
USB_REG->DEVIER = DEVIER_PEP_0;
return true;
- } else
+ } else
{
// Endpoint configuration is not successful
return false;
}
- } else
+ } else
{
// Enable the endpoint
USB_REG->DEVEPT |= ((0x01 << epnum) << DEVEPT_EPEN0_Pos);
@@ -544,7 +545,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
{
USB_REG->DEVIER = ((0x01 << epnum) << DEVIER_PEP_0_Pos);
return true;
- } else
+ } else
{
// Endpoint configuration is not successful
return false;
@@ -583,7 +584,7 @@ static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix)
{
memcpy(ptr, xfer->buffer + xfer->queued_len, len);
}
- else
+ else
{
tu_fifo_read_n(xfer->fifo, ptr, len);
}
@@ -595,7 +596,7 @@ static void dcd_transmit_packet(xfer_ctl_t * xfer, uint8_t ep_ix)
{
// Control endpoint: clear the interrupt flag to send the data
USB_REG->DEVEPTICR[0] = DEVEPTICR_TXINIC;
- } else
+ } else
{
// Other endpoint types: clear the FIFO control flag to send the data
USB_REG->DEVEPTIDR[ep_ix] = DEVEPTIDR_FIFOCONC;
@@ -616,7 +617,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->fifo = NULL;
-
+
if (EP_DMA_SUPPORT(epnum) && total_bytes != 0)
{
// Force the CPU to flush the buffer. We increase the size by 32 because the call aligns the
@@ -644,16 +645,16 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
__set_PRIMASK(irq_state);
- // Here a ZLP has been recieved
+ // Here a ZLP has been received
// and the DMA transfer must be not started.
// It is the end of transfer
return false;
- } else
+ } else
{
if (dir == TUSB_DIR_OUT)
{
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_RXOUTES;
- } else
+ } else
{
dcd_transmit_packet(xfer,epnum);
}
@@ -701,20 +702,20 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
// Clean invalidate cache of linear part
CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_lin, 4), info.len_lin + 31);
-
+
USB_REG->DEVDMA[epnum - 1].DEVDMAADDRESS = (uint32_t)info.ptr_lin;
if (info.len_wrap)
{
// Clean invalidate cache of wrapped part
CleanInValidateCache((uint32_t*) tu_align((uint32_t) info.ptr_wrap, 4), info.len_wrap + 31);
-
+
dma_desc[epnum - 1].next_desc = 0;
dma_desc[epnum - 1].buff_addr = (uint32_t)info.ptr_wrap;
dma_desc[epnum - 1].chnl_ctrl =
udd_dma_ctrl_wrap | (info.len_wrap << DEVDMACONTROL_BUFF_LENGTH_Pos);
// Clean cache of wrapped DMA descriptor
CleanInValidateCache((uint32_t*)&dma_desc[epnum - 1], sizeof(dma_desc_t));
-
+
udd_dma_ctrl_lin |= DEVDMASTATUS_DESC_LDST;
USB_REG->DEVDMA[epnum - 1].DEVDMANXTDSC = (uint32_t)&dma_desc[epnum - 1];
} else {
@@ -734,7 +735,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
}
__set_PRIMASK(irq_state);
- // Here a ZLP has been recieved
+ // Here a ZLP has been received
// and the DMA transfer must be not started.
// It is the end of transfer
return false;
@@ -743,7 +744,7 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
if (dir == TUSB_DIR_OUT)
{
USB_REG->DEVEPTIER[epnum] = DEVEPTIER_RXOUTES;
- } else
+ } else
{
dcd_transmit_packet(xfer,epnum);
}
diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
index 39b09db68..1ce3da27e 100644
--- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -110,7 +110,7 @@ typedef struct
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// BDT(Buffer Descriptor Table) must be 256-byte aligned
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
@@ -245,9 +245,9 @@ static void process_bus_active(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
tu_memclr(&_dcd, sizeof(_dcd));
USB_OTG_FS->BDT_PAGE_01 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
@@ -256,6 +256,7 @@ void dcd_init(uint8_t rhport)
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB_FS_IRQn);
+ return true;
}
#define USB_DEVICE_INTERRUPT_PRIORITY (3U)
void dcd_int_enable(uint8_t rhport)
@@ -283,7 +284,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
/* Response with status first before changing device address */
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
+
+#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
extern u32 SystemCoreClock;
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
void dcd_remote_wakeup(uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index ca37d799f..9e5f5117f 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -29,10 +29,23 @@
#if CFG_TUD_ENABLED && CFG_TUSB_MCU == OPT_MCU_NRF5X
#include <stdatomic.h>
+
+// Suppress warning caused by nrfx driver
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#pragma GCC diagnostic ignored "-Wcast-align"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
#include "nrf.h"
-#include "nrf_clock.h"
-#include "nrf_power.h"
-#include "nrfx_usbd_errata.h"
+#include "nrfx_clock.h"
+#include "nrf_erratas.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
#include "device/dcd.h"
// TODO remove later
@@ -43,28 +56,46 @@
#include "mcu/mcu.h"
#endif
+/* Try to detect nrfx version if not configured with CFG_TUD_NRF_NRFX_VERSION
+ * nrfx v1 and v2 are concurrently developed. There is no NRFX_VERSION only MDK VERSION which is as follows:
+ * - v3.0.0: 8.53.1 (conflict with v2.11.0), v3.1.0: 8.55.0 ...
+ * - v2.11.0: 8.53.1, v2.6.0: 8.44.1, v2.5.0: 8.40.2, v2.4.0: 8.37.0, v2.3.0: 8.35.0, v2.2.0: 8.32.1, v2.1.0: 8.30.2, v2.0.0: 8.29.0
+ * - v1.9.0: 8.40.3, v1.8.6: 8.35.0 (conflict with v2.3.0), v1.8.5: 8.32.3, v1.8.4: 8.32.1 (conflict with v2.2.0),
+ * v1.8.2: 8.32.1 (conflict with v2.2.0), v1.8.1: 8.27.1
+ * Therefore the check for v1 would be:
+ * - MDK < 8.29.0 (v2.0), MDK == 8.32.3, 8.40.3
+ * - in case of conflict User of those version must upgrade to other 1.x version or set CFG_TUD_NRF_NRFX_VERSION
+*/
+#ifndef CFG_TUD_NRF_NRFX_VERSION
+ #define _MDK_VERSION (10000*MDK_MAJOR_VERSION + 100*MDK_MINOR_VERSION + MDK_MICRO_VERSION)
+
+ #if _MDK_VERSION < 82900 || _MDK_VERSION == 83203 || _MDK_VERSION == 84003
+ // nrfx <= 1.8.1, or 1.8.5 or 1.9.0
+ #define CFG_TUD_NRF_NRFX_VERSION 1
+ #else
+ #define CFG_TUD_NRF_NRFX_VERSION 2
+ #endif
+#endif
+
/*------------------------------------------------------------------*/
/* MACRO TYPEDEF CONSTANT ENUM
*------------------------------------------------------------------*/
-enum
-{
+enum {
// Max allowed by USB specs
- MAX_PACKET_SIZE = 64,
+ MAX_PACKET_SIZE = 64,
// Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT
EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) |
USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
};
-enum
-{
- EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
+enum {
+ EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
EP_CBI_COUNT = 8 // Control Bulk Interrupt endpoints count
};
// Transfer Descriptor
-typedef struct
-{
+typedef struct {
uint8_t* buffer;
uint16_t total_len;
volatile uint16_t actual_len;
@@ -82,120 +113,95 @@ typedef struct
} xfer_td_t;
// Data for managing dcd
-static struct
-{
+static struct {
// All 8 endpoints including control IN & OUT (offset 1)
// +1 for ISO endpoints
xfer_td_t xfer[EP_CBI_COUNT + 1][2];
// nRF can only carry one DMA at a time, this is used to guard the access to EasyDMA
- atomic_bool dma_running;
-}_dcd;
+ atomic_flag dma_running;
+
+ // Track whether sof has been manually enabled
+ bool sof_enabled;
+} _dcd;
/*------------------------------------------------------------------*/
/* Control / Bulk / Interrupt (CBI) Transfer
*------------------------------------------------------------------*/
-// NVIC_GetEnableIRQ is only available in CMSIS v5
-#ifndef NVIC_GetEnableIRQ
-static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
-{
- if ((int32_t)(IRQn) >= 0)
- {
- return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
- }
- else
- {
- return(0U);
- }
-}
-#endif
-
// check if we are in ISR
-TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) {
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
}
// helper to start DMA
-static void start_dma(volatile uint32_t* reg_startep)
-{
+static void start_dma(volatile uint32_t* reg_startep) {
(*reg_startep) = 1;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
// TASKS_EP0STATUS, TASKS_EP0RCVOUT seem to need EasyDMA to be available
// However these don't trigger any DMA transfer and got ENDED event subsequently
// Therefore dma_pending is corrected right away
- if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) )
- {
+ if ((reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT)) {
atomic_flag_clear(&_dcd.dma_running);
}
}
-static void edpt_dma_start(volatile uint32_t* reg_startep)
-{
- if ( atomic_flag_test_and_set(&_dcd.dma_running) )
- {
- usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
- }else
- {
+static void edpt_dma_start(volatile uint32_t* reg_startep) {
+ if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+ usbd_defer_func((osal_task_func_t)(uintptr_t ) edpt_dma_start, (void*) (uintptr_t) reg_startep, is_in_isr());
+ } else {
start_dma(reg_startep);
}
}
// DMA is complete
-static void edpt_dma_end(void)
-{
- TU_ASSERT(_dcd.dma_running, );
+static void edpt_dma_end(void) {
atomic_flag_clear(&_dcd.dma_running);
}
// helper getting td
-static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
-{
+static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) {
return &_dcd.xfer[epnum][dir];
}
static void xact_out_dma(uint8_t epnum);
+
// Function wraps xact_out_dma which wants uint8_t while usbd_defer_func wants void (*)(void *)
-static void xact_out_dma_wrapper(void *epnum)
-{
- xact_out_dma((uint8_t)((uintptr_t)epnum));
+static void xact_out_dma_wrapper(void* epnum) {
+ xact_out_dma((uint8_t) ((uintptr_t) epnum));
}
// Start DMA to move data from Endpoint -> RAM
-static void xact_out_dma(uint8_t epnum)
-{
+static void xact_out_dma(uint8_t epnum) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint32_t xact_len;
// DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock,
- // If already running deffer call regardless if it was called from ISR or task,
- if ( atomic_flag_test_and_set(&_dcd.dma_running) )
- {
- usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr());
+ // If already running defer call regardless if it was called from ISR or task,
+ if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+ usbd_defer_func((osal_task_func_t) xact_out_dma_wrapper, (void*) (uint32_t) epnum, is_in_isr());
return;
}
- if (epnum == EP_ISO_NUM)
- {
+ if (epnum == EP_ISO_NUM) {
xact_len = NRF_USBD->SIZE.ISOOUT;
// If ZERO bit is set, ignore ISOOUT length
- if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk)
- {
+ if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) {
xact_len = 0;
atomic_flag_clear(&_dcd.dma_running);
- }
- else
- {
- // Trigger DMA move data from Endpoint -> SRAM
- NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
- NRF_USBD->ISOOUT.MAXCNT = xact_len;
+ } else {
+ if (xfer->started) {
+ // Trigger DMA move data from Endpoint -> SRAM
+ NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->ISOOUT.MAXCNT = xact_len;
- start_dma(&NRF_USBD->TASKS_STARTISOOUT);
+ start_dma(&NRF_USBD->TASKS_STARTISOOUT);
+ } else {
+ atomic_flag_clear(&_dcd.dma_running);
+ }
}
- }
- else
- {
+ } else {
// limit xact len to remaining length
xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
@@ -209,14 +215,13 @@ static void xact_out_dma(uint8_t epnum)
// Prepare for a CBI transaction IN, call at the start
// it start DMA to transfer data from RAM -> Endpoint
-static void xact_in_dma(uint8_t epnum)
-{
+static void xact_in_dma(uint8_t epnum) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
// Each transaction is up to Max Packet Size
uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
- NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
+ NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
@@ -225,26 +230,24 @@ static void xact_in_dma(uint8_t epnum)
//--------------------------------------------------------------------+
// Controller API
//--------------------------------------------------------------------+
-void dcd_init (uint8_t rhport)
-{
- TU_LOG1("dcd init\r\n");
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
+ TU_LOG2("dcd init\r\n");
+ return true;
}
-void dcd_int_enable(uint8_t rhport)
-{
+void dcd_int_enable(uint8_t rhport) {
(void) rhport;
NVIC_EnableIRQ(USBD_IRQn);
}
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
(void) rhport;
NVIC_DisableIRQ(USBD_IRQn);
}
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
(void) rhport;
(void) dev_addr;
// Set Address is automatically update by hw controller, nothing to do
@@ -259,8 +262,7 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk;
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
+void dcd_remote_wakeup(uint8_t rhport) {
(void) rhport;
// Bring controller out of low power mode
@@ -269,8 +271,7 @@ void dcd_remote_wakeup(uint8_t rhport)
}
// disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
(void) rhport;
NRF_USBD->USBPULLUP = 0;
@@ -280,53 +281,51 @@ void dcd_disconnect(uint8_t rhport)
}
// connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
(void) rhport;
NRF_USBD->USBPULLUP = 1;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
- (void) en;
-
- // TODO implement later
+ if (en) {
+ _dcd.sof_enabled = true;
+ NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
+ } else {
+ _dcd.sof_enabled = false;
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ }
}
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
-{
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
(void) rhport;
uint8_t const ep_addr = desc_edpt->bEndpointAddress;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const epnum = tu_edpt_number(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
_dcd.xfer[epnum][dir].mps = tu_edpt_packet_size(desc_edpt);
- if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
- {
- if (dir == TUSB_DIR_OUT)
- {
+ if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN |= TU_BIT(epnum);
// Write any value to SIZE register will allow nRF to ACK/accept data
NRF_USBD->SIZE.EPOUT[epnum] = 0;
- }else
- {
+ } else {
NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
- NRF_USBD->EPINEN |= TU_BIT(epnum);
+ NRF_USBD->EPINEN |= TU_BIT(epnum);
}
- }
- else
- {
+ // clear stall and reset DataToggle
+ NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+ NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
+ } else {
TU_ASSERT(epnum == EP_ISO_NUM);
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
// SPLIT ISO buffer when ISO IN endpoint is already opened.
if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
@@ -339,9 +338,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
- }
- else
- {
+ } else {
NRF_USBD->EVENTS_ENDISOIN = 0;
// SPLIT ISO buffer when ISO OUT endpoint is already opened.
@@ -352,43 +349,38 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
// Enable SOF and ISOIN interrupts, and ISOIN endpoint.
NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
- NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
+ NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
}
}
- // clear stall and reset DataToggle
- NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
- NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
-
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
+void dcd_edpt_close_all(uint8_t rhport) {
// disable interrupt to prevent race condition
dcd_int_disable(rhport);
// disable all non-control (bulk + interrupt) endpoints
- for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
- {
+ for (uint8_t ep = 1; ep < EP_CBI_COUNT; ep++) {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
NRF_USBD->TASKS_STARTEPIN[ep] = 0;
NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
- tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
+ tu_memclr(_dcd.xfer[ep], 2 * sizeof(xfer_td_t));
}
// disable both ISO
NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
- NRF_USBD->TASKS_STARTISOIN = 0;
+ NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
- tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
+ tu_memclr(_dcd.xfer[EP_ISO_NUM], 2 * sizeof(xfer_td_t));
// de-activate all non-control
NRF_USBD->EPOUTEN = 1UL;
@@ -397,107 +389,89 @@ void dcd_edpt_close_all (uint8_t rhport)
dcd_int_enable(rhport);
}
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- if (epnum != EP_ISO_NUM)
- {
+ if (epnum != EP_ISO_NUM) {
// CBI
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
- }
- else
- {
+ } else {
NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
NRF_USBD->EPINEN &= ~TU_BIT(epnum);
}
- }
- else
- {
+ } else {
_dcd.xfer[EP_ISO_NUM][dir].mps = 0;
// ISO
- if (dir == TUSB_DIR_OUT)
- {
+ if (dir == TUSB_DIR_OUT) {
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
NRF_USBD->EVENTS_ENDISOOUT = 0;
- }
- else
- {
+ } else {
NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
}
// One of the ISO endpoints closed, no need to split buffers any more.
NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
// When both ISO endpoint are close there is no need for SOF any more.
- if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+ if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0)
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
}
_dcd.xfer[epnum][dir].started = false;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+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);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
TU_ASSERT(!xfer->started);
- xfer->buffer = buffer;
- xfer->total_len = total_bytes;
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
xfer->actual_len = 0;
// Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
- if ( control_status )
- {
- // Status Phase also requires EasyDMA has to be available as well !!!!
- edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
-
+ if (control_status) {
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
- }
- else if ( dir == TUSB_DIR_OUT )
- {
+
+ // Status Phase also requires EasyDMA has to be available as well !!!!
+ edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
+ } else if (dir == TUSB_DIR_OUT) {
xfer->started = true;
- if ( epnum == 0 )
- {
+ if (epnum == 0) {
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
- }else
- {
+ } else {
// started just set, it could start DMA transfer if interrupt was trigger after this line
// code only needs to start transfer (from Endpoint to RAM) when data_received was set
// before started was set. If started is NOT set but data_received is, it means that
// current transfer was already finished and next data is already present in endpoint and
// can be consumed by future transfer
- __ISB(); __DSB();
- if ( xfer->data_received && xfer->started )
- {
+ __ISB();
+ __DSB();
+ if (xfer->data_received && xfer->started) {
// Data is already received previously
// start DMA to copy to SRAM
xfer->data_received = false;
xact_out_dma(epnum);
- }
- else
- {
+ } else {
// nRF auto accept next Bulk/Interrupt OUT packet
// nothing to do
}
}
- }
- else
- {
+ } else {
// Start DMA to copy data from RAM -> Endpoint
xact_in_dma(epnum);
}
@@ -505,42 +479,37 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_td_t* xfer = get_td(epnum, dir);
- if ( epnum == 0 )
- {
+ if (epnum == 0) {
NRF_USBD->TASKS_EP0STALL = 1;
- }else if (epnum != EP_ISO_NUM)
- {
+ } else if (epnum != EP_ISO_NUM) {
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
// Note: nRF can auto ACK packet OUT before get stalled.
// There maybe data in endpoint fifo already, we need to pull it out
- if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
- {
+ if ((dir == TUSB_DIR_OUT) && xfer->data_received) {
xfer->data_received = false;
xact_out_dma(epnum);
}
}
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- if ( epnum != 0 && epnum != EP_ISO_NUM )
- {
+ if (epnum != 0 && epnum != EP_ISO_NUM) {
// reset data toggle to DATA0
// First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from
// VALUE, or write it again with VALUE=Data0 or Data1
@@ -553,26 +522,25 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
// Write any value to SIZE register will allow nRF to ACK/accept data
if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
}
/*------------------------------------------------------------------*/
/* Interrupt Handler
*------------------------------------------------------------------*/
-void bus_reset(void)
-{
+static void bus_reset(void) {
// 6.35.6 USB controller automatically disabled all endpoints (except control)
NRF_USBD->EPOUTEN = 1UL;
NRF_USBD->EPINEN = 1UL;
- for(int i=0; i<8; i++)
- {
+ for (int i = 0; i < 8; i++) {
NRF_USBD->TASKS_STARTEPIN[i] = 0;
NRF_USBD->TASKS_STARTEPOUT[i] = 0;
}
- NRF_USBD->TASKS_STARTISOIN = 0;
+ NRF_USBD->TASKS_STARTISOIN = 0;
NRF_USBD->TASKS_STARTISOOUT = 0;
// Clear USB Event Interrupt
@@ -582,43 +550,40 @@ void bus_reset(void)
// Reset interrupt
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
- USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+ USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk |
+ USBD_INTEN_ENDEPOUT0_Msk;
tu_varclr(&_dcd);
_dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
_dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
}
-void dcd_int_handler(uint8_t rhport)
-{
+void dcd_int_handler(uint8_t rhport) {
(void) rhport;
- uint32_t const inten = NRF_USBD->INTEN;
+ uint32_t const inten = NRF_USBD->INTEN;
uint32_t int_status = 0;
volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET;
- for(uint8_t i=0; i<USBD_INTEN_EPDATA_Pos+1; i++)
- {
- if ( tu_bit_test(inten, i) && regevt[i] )
- {
+ for (uint8_t i = 0; i < USBD_INTEN_EPDATA_Pos + 1; i++) {
+ if (tu_bit_test(inten, i) && regevt[i]) {
int_status |= TU_BIT(i);
// event clear
regevt[i] = 0;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
}
- if ( int_status & USBD_INTEN_USBRESET_Msk )
- {
+ if (int_status & USBD_INTEN_USBRESET_Msk) {
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
}
// ISOIN: Data was moved to endpoint buffer, client will be notified in SOF
- if ( int_status & USBD_INTEN_ENDISOIN_Msk )
- {
+ if (int_status & USBD_INTEN_ENDISOIN_Msk) {
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
xfer->actual_len = NRF_USBD->ISOIN.AMOUNT;
@@ -627,50 +592,53 @@ void dcd_int_handler(uint8_t rhport)
xfer->iso_in_transfer_ready = true;
}
- if ( int_status & USBD_INTEN_SOF_Msk )
- {
+ if (int_status & USBD_INTEN_SOF_Msk) {
bool iso_enabled = false;
// ISOOUT: Transfer data gathered in previous frame from buffer to RAM
- if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
- {
+ if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) {
iso_enabled = true;
- xact_out_dma(EP_ISO_NUM);
+ // Transfer from endpoint to RAM only if data is not corrupted
+ if ((int_status & USBD_INTEN_USBEVENT_Msk) == 0 ||
+ (NRF_USBD->EVENTCAUSE & USBD_EVENTCAUSE_ISOOUTCRC_Msk) == 0) {
+ xact_out_dma(EP_ISO_NUM);
+ }
}
// ISOIN: Notify client that data was transferred
- if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
- {
+ if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) {
iso_enabled = true;
xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
- if ( xfer->iso_in_transfer_ready )
- {
+ if (xfer->iso_in_transfer_ready) {
xfer->iso_in_transfer_ready = false;
dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
}
- if ( !iso_enabled )
- {
- // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
- // so we disable it now
- NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
+ if (!iso_enabled && !_dcd.sof_enabled) {
+ // SOF interrupt not manually enabled and ISO endpoint is not used,
+ // SOF is only enabled one-time for remote wakeup so we disable it now
+
+ NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
}
- dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ const uint32_t frame = NRF_USBD->FRAMECNTR;
+ dcd_event_sof(0, frame, true);
+ //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
}
- if ( int_status & USBD_INTEN_USBEVENT_Msk )
- {
- TU_LOG(2, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
+ if (int_status & USBD_INTEN_USBEVENT_Msk) {
+ TU_LOG(3, "EVENTCAUSE = 0x%04" PRIX32 "\r\n", NRF_USBD->EVENTCAUSE);
- enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk };
+ enum {
+ EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk |
+ USBD_EVENTCAUSE_ISOOUTCRC_Msk
+ };
uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
- if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk) {
// Put controller into low power mode
// Leave HFXO disable to application, since it may be used by other peripherals
NRF_USBD->LOWPOWER = 1;
@@ -678,8 +646,7 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
- if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk) {
// USB is out of low power mode, and wakeup is allowed
// Initiate RESUME signal
NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
@@ -691,34 +658,29 @@ void dcd_int_handler(uint8_t rhport)
NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
}
- if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
- {
+ if (evt_cause & USBD_EVENTCAUSE_RESUME_Msk) {
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
}
// Setup tokens are specific to the Control endpoint.
- if ( int_status & USBD_INTEN_EP0SETUP_Msk )
- {
- uint8_t const setup[8] =
- {
- NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
- NRF_USBD->WINDEXL , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
+ if (int_status & USBD_INTEN_EP0SETUP_Msk) {
+ uint8_t const setup[8] = {
+ NRF_USBD->BMREQUESTTYPE, NRF_USBD->BREQUEST, NRF_USBD->WVALUEL, NRF_USBD->WVALUEH,
+ NRF_USBD->WINDEXL, NRF_USBD->WINDEXH, NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
};
// nrf5x hw auto handle set address, there is no need to inform usb stack
- tusb_control_request_t const * request = (tusb_control_request_t const *) setup;
+ tusb_control_request_t const* request = (tusb_control_request_t const*) setup;
- if ( !(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
- TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
- TUSB_REQ_SET_ADDRESS == request->bRequest) )
- {
+ if (!(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
+ TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+ TUSB_REQ_SET_ADDRESS == request->bRequest)) {
dcd_event_setup_received(0, setup, true);
}
}
- if ( int_status & EDPT_END_ALL_MASK )
- {
+ if (int_status & EDPT_END_ALL_MASK) {
// DMA complete move data from SRAM <-> Endpoint
// Must before endpoint transfer handling
edpt_dma_end();
@@ -760,30 +722,24 @@ void dcd_int_handler(uint8_t rhport)
* len if Host decides to sent fewer bytes, it this case transaction is also
* complete and next transfer is not initiated here like for CBI.
*/
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT+1; epnum++)
- {
- if ( tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos+epnum))
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT + 1; epnum++) {
+ if (tu_bit_test(int_status, USBD_INTEN_ENDEPOUT0_Pos + epnum)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
uint16_t const xact_len = NRF_USBD->EPOUT[epnum].AMOUNT;
- xfer->buffer += xact_len;
+ xfer->buffer += xact_len;
xfer->actual_len += xact_len;
// Transfer complete if transaction len < Max Packet Size or total len is transferred
- if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
- {
- if ( epnum == 0 )
- {
+ if ((epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len)) {
+ if (epnum == 0) {
// Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
- }else
- {
+ } else {
// nRF auto accept next Bulk/Interrupt OUT packet
// nothing to do
}
- }else
- {
+ } else {
TU_ASSERT(xfer->started,);
xfer->total_len = xfer->actual_len;
xfer->started = false;
@@ -797,11 +753,11 @@ void dcd_int_handler(uint8_t rhport)
}
// Endpoint <-> Host ( In & OUT )
- if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) )
- {
+ if (int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk)) {
uint32_t data_status = NRF_USBD->EPDATASTATUS;
NRF_USBD->EPDATASTATUS = data_status;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
// EP0DATADONE is set with either Control Out on IN Data
// Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN.
@@ -810,22 +766,18 @@ void dcd_int_handler(uint8_t rhport)
bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
// CBI In: Endpoint -> Host (transaction complete)
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
- {
- if ( tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in) )
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) {
+ if (tu_bit_test(data_status, epnum) || (epnum == 0 && is_control_in)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
uint8_t const xact_len = NRF_USBD->EPIN[epnum].AMOUNT;
- xfer->buffer += xact_len;
+ xfer->buffer += xact_len;
xfer->actual_len += xact_len;
- if ( xfer->actual_len < xfer->total_len )
- {
+ if (xfer->actual_len < xfer->total_len) {
// Start DMA to copy next data packet
xact_in_dma(epnum);
- } else
- {
+ } else {
// CBI IN complete
dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
}
@@ -833,17 +785,13 @@ void dcd_int_handler(uint8_t rhport)
}
// CBI OUT: Host -> Endpoint
- for(uint8_t epnum=0; epnum<EP_CBI_COUNT; epnum++)
- {
- if ( tu_bit_test(data_status, 16+epnum) || (epnum == 0 && is_control_out) )
- {
+ for (uint8_t epnum = 0; epnum < EP_CBI_COUNT; epnum++) {
+ if (tu_bit_test(data_status, 16 + epnum) || (epnum == 0 && is_control_out)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
- if ( xfer->started && xfer->actual_len < xfer->total_len )
- {
+ if (xfer->started && xfer->actual_len < xfer->total_len) {
xact_out_dma(epnum);
- }else
- {
+ } else {
// Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet
// Mark this endpoint with data received
xfer->data_received = true;
@@ -867,76 +815,80 @@ void dcd_int_handler(uint8_t rhport)
#define SD_MAGIC_NUMBER 0x51B1E5DB
#endif
-static inline bool is_sd_existed(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_existed(void) {
return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER;
}
// check if SD is existed and enabled
-static inline bool is_sd_enabled(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_enabled(void) {
if ( !is_sd_existed() ) return false;
-
uint8_t sd_en = false;
(void) sd_softdevice_is_enabled(&sd_en);
return sd_en;
}
#endif
-static bool hfclk_running(void)
-{
+static bool hfclk_running(void) {
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
uint32_t is_running = 0;
(void) sd_clock_hfclk_is_running(&is_running);
return (is_running ? true : false);
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#else
return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#endif
}
-static void hfclk_enable(void)
-{
+static void hfclk_enable(void) {
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_request();
return;
#else
// already running, nothing to do
- if ( hfclk_running() ) return;
+ if (hfclk_running()) return;
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
(void)sd_clock_hfclk_request();
return;
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
+ nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART);
+#else
nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED);
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
#endif
+#endif
}
-static void hfclk_disable(void)
-{
+static void hfclk_disable(void) {
#if CFG_TUSB_OS == OPT_OS_MYNEWT
usb_clock_release();
return;
#else
#ifdef SOFTDEVICE_PRESENT
- if ( is_sd_enabled() )
- {
+ if ( is_sd_enabled() ) {
(void)sd_clock_hfclk_release();
return;
}
#endif
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+ nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
+#else
nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
#endif
+#endif
}
// Power & Clock Peripheral on nRF5x to manage USB
@@ -949,8 +901,8 @@ static void hfclk_disable(void)
// Therefore this function must be called to handle USB power event by
// - nrfx_power_usbevt_init() : if Softdevice is not used or enabled
// - SoftDevice SOC event : if SD is used and enabled
-void tusb_hal_nrf_power_event (uint32_t event)
-{
+void tusb_hal_nrf_power_event(uint32_t event);
+void tusb_hal_nrf_power_event(uint32_t event) {
// Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h
enum {
USB_EVT_DETECTED = 0,
@@ -958,51 +910,42 @@ void tusb_hal_nrf_power_event (uint32_t event)
USB_EVT_READY = 2
};
-#if CFG_TUSB_DEBUG >= 2
- const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
- TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
+#if CFG_TUSB_DEBUG >= 3
+ const char* const power_evt_str[] = {"Detected", "Removed", "Ready"};
+ TU_LOG(3, "Power USB event: %s\r\n", power_evt_str[event]);
#endif
- switch ( event )
- {
+ switch (event) {
case USB_EVT_DETECTED:
- if ( !NRF_USBD->ENABLE )
- {
+ if (!NRF_USBD->ENABLE) {
// Prepare for receiving READY event: disable interrupt since we will blocking wait
NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
#ifdef NRF52_SERIES // NRF53 does not need this errata
// ERRATA 171, 187, 166
- if ( nrfx_usbd_errata_187() )
- {
+ if (nrf52_errata_187()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_171() )
- {
+ if (nrf52_errata_171()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
}
// CRITICAL_REGION_EXIT();
}
@@ -1010,64 +953,58 @@ void tusb_hal_nrf_power_event (uint32_t event)
// Enable the peripheral (will cause Ready event)
NRF_USBD->ENABLE = 1;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
// Enable HFCLK
hfclk_enable();
}
- break;
+ break;
case USB_EVT_READY:
// Skip if pull-up is enabled and HCLK is already running.
// Application probably call this more than necessary.
- if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
+ if (NRF_USBD->USBPULLUP && hfclk_running()) break;
// Waiting for USBD peripheral enabled
- while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
+ while (!(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE)) {}
NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
#ifdef NRF52_SERIES
- if ( nrfx_usbd_errata_171() )
- {
+ if (nrf52_errata_171()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_187() )
- {
+ if (nrf52_errata_187()) {
// CRITICAL_REGION_ENTER();
- if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
- {
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
- *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
- }
- else
- {
- *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
+ if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
+ *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+ } else {
+ *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
}
// CRITICAL_REGION_EXIT();
}
- if ( nrfx_usbd_errata_166() )
- {
- *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3;
- *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40;
+ if (nrf52_errata_166()) {
+ *((volatile uint32_t*) (NRF_USBD_BASE + 0x800)) = 0x7E3;
+ *((volatile uint32_t*) (NRF_USBD_BASE + 0x804)) = 0x40;
- __ISB(); __DSB();
+ __ISB();
+ __DSB();
}
#endif
@@ -1079,30 +1016,31 @@ void tusb_hal_nrf_power_event (uint32_t event)
// Enable interrupt, priorities should be set by application
NVIC_ClearPendingIRQ(USBD_IRQn);
+
// Don't enable USBD interrupt yet, if dcd_init() did not finish yet
// Interrupt will be enabled by tud_init(), when USB stack is ready
// to handle interrupts.
- if (tud_inited())
- {
+ if (tud_inited()) {
NVIC_EnableIRQ(USBD_IRQn);
}
// Wait for HFCLK
- while ( !hfclk_running() ) { }
+ while (!hfclk_running()) {}
// Enable pull up
NRF_USBD->USBPULLUP = 1;
- __ISB(); __DSB(); // for sync
- break;
+ __ISB();
+ __DSB(); // for sync
+ break;
case USB_EVT_REMOVED:
- if ( NRF_USBD->ENABLE )
- {
+ if (NRF_USBD->ENABLE) {
// Abort all transfers
// Disable pull up
NRF_USBD->USBPULLUP = 0;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
// Disable Interrupt
NVIC_DisableIRQ(USBD_IRQn);
@@ -1111,15 +1049,17 @@ void tusb_hal_nrf_power_event (uint32_t event)
NRF_USBD->INTENCLR = NRF_USBD->INTEN;
NRF_USBD->ENABLE = 0;
- __ISB(); __DSB(); // for sync
+ __ISB();
+ __DSB(); // for sync
hfclk_disable();
dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
}
- break;
+ break;
- default: break;
+ default:
+ break;
}
}
diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c
index 2fdc05f36..b0b6fe857 100644
--- a/src/portable/nuvoton/nuc120/dcd_nuc120.c
+++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c
@@ -27,9 +27,9 @@
/*
Theory of operation:
- The NUC100/NUC120 USBD peripheral has six "EP"s, but each is simplex,
- so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
- implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
+ The NUC100/NUC120 USBD peripheral has six "EP"s, but each is simplex,
+ so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
+ implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
EP0_IN and EP0_OUT respectively. This leaves up to four for user usage.
*/
@@ -201,9 +201,9 @@ static const uint32_t enabled_irqs = USBD_INTSTS_FLDET_STS_Msk | USBD_INTSTS_BUS
NUC100/NUC120 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
USBD->ATTR = 0x7D0;
@@ -215,6 +215,8 @@ void dcd_init(uint8_t rhport)
USBD->INTSTS = enabled_irqs;
USBD->INTEN = enabled_irqs;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c
index a56b5f8e8..f4af97ca7 100644
--- a/src/portable/nuvoton/nuc121/dcd_nuc121.c
+++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c
@@ -27,9 +27,9 @@
/*
Theory of operation:
- The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex,
- so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
- implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
+ The NUC121/NUC125/NUC126 USBD peripheral has eight "EP"s, but each is simplex,
+ so two collectively (peripheral nomenclature of "EP0" and "EP1") are needed to
+ implement USB EP0. PERIPH_EP0 and PERIPH_EP1 are used by this driver for
EP0_IN and EP0_OUT respectively. This leaves up to six for user usage.
*/
@@ -209,9 +209,9 @@ enum {
NUC121/NUC125/NUC126 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
#ifdef SUPPORT_LPM
USBD->ATTR = 0x7D0 | USBD_LPMACK;
@@ -227,6 +227,8 @@ void dcd_init(uint8_t rhport)
USBD->INTSTS = ENABLED_IRQS;
USBD->INTEN = ENABLED_IRQS;
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c
index 886720e33..1c98a0a49 100644
--- a/src/portable/nuvoton/nuc505/dcd_nuc505.c
+++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c
@@ -27,9 +27,9 @@
/*
Theory of operation:
- The NUC505 USBD peripheral has twelve "EP"s, where each is simplex, in addition
+ The NUC505 USBD peripheral has twelve "EP"s, where each is simplex, in addition
to dedicated support for the control endpoint (EP0). The non-user endpoints
- are referred to as "user" EPs in this code, and follow the datasheet
+ are referred to as "user" EPs in this code, and follow the datasheet
nomenclature of EPA through EPL.
*/
@@ -181,7 +181,7 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
ep->EPINTEN = USBD_EPINTEN_TXPKIEN_Msk;
}
- /* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
+ /* provided buffers are thankfully 32-bit aligned, allowing most data to be transferred as 32-bit */
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
@@ -279,9 +279,9 @@ static const uint32_t enabled_irqs = USBD_GINTEN_USBIEN_Msk | \
NUC505 TinyUSB API driver implementation
*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
/* configure interrupts in their initial state; BUSINTEN and CEPINTEN will be subsequently and dynamically re-written as needed */
USBD->GINTEN = enabled_irqs;
@@ -291,6 +291,8 @@ void dcd_init(uint8_t rhport)
bus_reset();
usb_attach();
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -389,7 +391,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
while (total_bytes < USBD->CEPRXCNT);
for (int count = 0; count < total_bytes; count++)
*buffer++ = USBD->CEPDAT_BYTE;
-
+
dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, true);
}
}
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index 13eb105cd..3d5e195a9 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Koji Kitayama
@@ -26,12 +26,14 @@
#include "tusb_option.h"
-#if CFG_TUD_ENABLED && ( \
- ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \
- )
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
-#include "fsl_device_registers.h"
-#define KHCI USB0
+#ifdef TUP_USBIP_CHIPIDEA_FS_KINETIS
+ #include "fsl_device_registers.h"
+ #define KHCI USB0
+#else
+ #error "MCU is not supported"
+#endif
#include "device/dcd.h"
@@ -112,7 +114,7 @@ typedef struct
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// BDT(Buffer Descriptor Table) must be 256-byte aligned
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
@@ -263,13 +265,25 @@ static void process_bus_resume(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Device API
*------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
+
+ // save crystal-less setting (if available)
+ #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
+ uint32_t clk_recover_irc_en = KHCI->CLK_RECOVER_IRC_EN;
+ uint32_t clk_recover_ctrl = KHCI->CLK_RECOVER_CTRL;
+ #endif
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+ // restore crystal-less setting (if available)
+ #if defined(FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED) && FSL_FEATURE_USB_KHCI_IRC48M_MODULE_CLOCK_ENABLED == 1
+ KHCI->CLK_RECOVER_IRC_EN = clk_recover_irc_en;
+ KHCI->CLK_RECOVER_CTRL |= clk_recover_ctrl;
+ #endif
+
tu_memclr(&_dcd, sizeof(_dcd));
KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >> 8);
@@ -280,6 +294,8 @@ void dcd_init(uint8_t rhport)
dcd_connect(rhport);
NVIC_ClearPendingIRQ(USB0_IRQn);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -296,7 +312,7 @@ void dcd_int_disable(uint8_t rhport)
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
- _dcd.addr = dev_addr & 0x7F;
+ _dcd.addr = dev_addr & 0x7F;
/* Response with status first before changing device address */
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
@@ -526,9 +542,9 @@ void dcd_int_handler(uint8_t rhport)
}
if (is & USB_ISTAT_SLEEP_MASK) {
- // TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
+ // TU_LOG3("Suspend: "); TU_LOG2_HEX(is);
- // Note Host usually has extra delay after bus reset (without SOF), which could falsely
+ // Note Host usually has extra delay after bus reset (without SOF), which could falsely
// detected as Sleep event. Though usbd has debouncing logic so we are good
KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
process_bus_sleep(rhport);
@@ -549,7 +565,7 @@ void dcd_int_handler(uint8_t rhport)
if (is & USB_ISTAT_SOFTOK_MASK) {
KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ dcd_event_sof(rhport, tu_u16(KHCI->FRMNUMH, KHCI->FRMNUML), true);
}
if (is & USB_ISTAT_STALL_MASK) {
diff --git a/src/portable/nxp/khci/hcd_khci.c b/src/portable/nxp/khci/hcd_khci.c
index 0f5fa6275..056dbf40b 100644
--- a/src/portable/nxp/khci/hcd_khci.c
+++ b/src/portable/nxp/khci/hcd_khci.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Koji Kitayama
@@ -26,12 +26,14 @@
#include "tusb_option.h"
-#if CFG_TUH_ENABLED && ( \
- ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX ) || ( CFG_TUSB_MCU == OPT_MCU_K32L2BXX ) \
- )
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_CHIPIDEA_FS)
-#include "fsl_device_registers.h"
-#define KHCI USB0
+#ifdef TUP_USBIP_CHIPIDEA_FS_KINETIS
+ #include "fsl_device_registers.h"
+ #define KHCI USB0
+#else
+ #error "MCU is not supported"
+#endif
#include "host/hcd.h"
@@ -135,10 +137,10 @@ typedef struct
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// BDT(Buffer Descriptor Table) must be 256-byte aligned
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd;
-//CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024];
+CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(512) static hcd_data_t _hcd;
+//CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t _rx_buf[1024];
-int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
+static int find_pipe(uint8_t dev_addr, uint8_t ep_addr)
{
/* Find the target pipe */
int num;
@@ -159,7 +161,7 @@ static int prepare_packets(int pipenum)
buffer_descriptor_t *bd = _hcd.bdt[dir_tx];
TU_ASSERT(0 == bd[odd].own, -1);
- // TU_LOG1(" %p dir %d odd %d data %d\n", &bd[odd], dir_tx, odd, pipe->data);
+ // TU_LOG1(" %p dir %d odd %d data %d\r\n", &bd[odd], dir_tx, odd, pipe->data);
ep->pipenum = pipenum;
@@ -249,7 +251,7 @@ static bool resume_transfer(int pipenum)
flags |= USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPCTLDIS_MASK | USB_ENDPT_RETRYDIS_MASK;
break;
}
- // TU_LOG1(" resume pipenum %d flags %x\n", pipenum, flags);
+ // TU_LOG1(" resume pipenum %d flags %x\r\n", pipenum, flags);
KHCI->ENDPOINT[0].ENDPT = flags;
KHCI->ADDR = (KHCI->ADDR & USB_ADDR_LSEN_MASK) | pipe->dev_addr;
@@ -300,7 +302,7 @@ static void process_tokdne(uint8_t rhport)
int pipenum = ep->pipenum;
int next_pipenum;
- // TU_LOG1("TOKDNE %x PID %x pipe %d\n", s, pid, pipenum);
+ // TU_LOG1("TOKDNE %x PID %x pipe %d\r\n", s, pid, pipenum);
xfer_result_t result;
switch (pid) {
@@ -366,10 +368,9 @@ static void process_bus_reset(uint8_t rhport)
/*------------------------------------------------------------------*/
/* Host API
*------------------------------------------------------------------*/
-bool hcd_init(uint8_t rhport)
-{
- (void)rhport;
-
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
@@ -414,7 +415,7 @@ void hcd_int_disable(uint8_t rhport)
uint32_t hcd_frame_number(uint8_t rhport)
{
(void)rhport;
- /* The device must be reset at least once after connection
+ /* The device must be reset at least once after connection
* in order to start the frame counter. */
if (_hcd.need_reset) hcd_port_reset(rhport);
uint32_t frmnum = KHCI->FRMNUML;
@@ -445,6 +446,10 @@ void hcd_port_reset(uint8_t rhport)
_hcd.need_reset = false;
}
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+}
+
tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
(void)rhport;
@@ -477,7 +482,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
(void)rhport;
- // TU_LOG1("SETUP %u\n", dev_addr);
+ // TU_LOG1("SETUP %u\r\n", dev_addr);
TU_ASSERT(0 == (_hcd.in_progress & TU_BIT(0)));
int pipenum = find_pipe(dev_addr, 0);
@@ -508,7 +513,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
{
(void)rhport;
uint8_t const ep_addr = ep_desc->bEndpointAddress;
- // TU_LOG1("O %u %x\n", dev_addr, ep_addr);
+ // TU_LOG1("O %u %x\r\n", dev_addr, ep_addr);
/* Find a free pipe */
pipe_state_t *p = &_hcd.pipe[0];
pipe_state_t *end = &_hcd.pipe[CFG_TUH_ENDPOINT_MAX * 2];
@@ -541,7 +546,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
(void)rhport;
- // TU_LOG1("X %u %x %x %d\n", dev_addr, ep_addr, (uintptr_t)buffer, buflen);
+ // TU_LOG1("X %u %x %x %d\r\n", dev_addr, ep_addr, (uintptr_t)buffer, buflen);
int pipenum = find_pipe(dev_addr, ep_addr);
TU_ASSERT(0 <= pipenum);
@@ -560,8 +565,16 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ // TODO not implemented yet
+ return false;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
if (!tu_edpt_number(ep_addr)) return true;
int num = find_pipe(dev_addr, ep_addr);
if (num < 0) return false;
@@ -573,12 +586,13 @@ bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
/*--------------------------------------------------------------------+
* ISR
*--------------------------------------------------------------------+*/
-void hcd_int_handler(uint8_t rhport)
+void hcd_int_handler(uint8_t rhport, bool in_isr)
{
+ (void) in_isr;
uint32_t is = KHCI->ISTAT;
uint32_t msk = KHCI->INTEN;
- // TU_LOG1("S %lx\n", is);
+ // TU_LOG1("S %lx\r\n", is);
/* clear disabled interrupts */
KHCI->ISTAT = (is & ~msk & ~USB_ISTAT_TOKDNE_MASK) | USB_ISTAT_SOFTOK_MASK;
@@ -587,7 +601,7 @@ void hcd_int_handler(uint8_t rhport)
if (is & USB_ISTAT_ERROR_MASK) {
unsigned err = KHCI->ERRSTAT;
if (err) {
- TU_LOG1(" ERR %x\n", err);
+ TU_LOG1(" ERR %x\r\n", err);
KHCI->ERRSTAT = err;
} else {
KHCI->INTEN &= ~USB_ISTAT_ERROR_MASK;
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
index 0894a5eeb..855c59cd1 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -92,7 +92,7 @@ typedef struct
} dcd_data_t;
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(128) static dcd_data_t _dcd;
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(128) static dcd_data_t _dcd;
//--------------------------------------------------------------------+
@@ -167,9 +167,9 @@ static void bus_reset(void)
tu_memclr(&_dcd, sizeof(dcd_data_t));
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
//------------- user manual 11.13 usb device controller initialization -------------//
// step 6 : set up control endpoint
@@ -186,6 +186,8 @@ void dcd_init(uint8_t rhport)
// Clear pending IRQ
NVIC_ClearPendingIRQ(USB_IRQn);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -335,6 +337,11 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport; (void) ep_addr;
+ // TODO implement dcd_edpt_close()
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.h b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
index 07daa32e4..654b80866 100644
--- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
+++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
index ad9ed59b4..090d1ba69 100644
--- a/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+++ b/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019, Ha Thach (tinyusb.org)
@@ -30,6 +30,7 @@
(CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX)
#include "chip.h"
+#include "host/hcd.h"
void hcd_int_enable(uint8_t rhport)
{
@@ -44,4 +45,3 @@ void hcd_int_disable(uint8_t rhport)
}
#endif
-
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index 9b490c48c..7d5cfb5b0 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -34,20 +34,26 @@
* - LPC54114
* - LPC55s69
*/
-#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_LPC11UXX || \
- CFG_TUSB_MCU == OPT_MCU_LPC13XX || \
- CFG_TUSB_MCU == OPT_MCU_LPC15XX || \
- CFG_TUSB_MCU == OPT_MCU_LPC51UXX || \
- CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
- CFG_TUSB_MCU == OPT_MCU_LPC55XX)
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_IP3511)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
-#if CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13XX || CFG_TUSB_MCU == OPT_MCU_LPC15XX
+#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
// LPCOpen
+ #ifdef __GNUC__
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
+ #pragma GCC diagnostic ignored "-Wstrict-prototypes"
+ #endif
+
#include "chip.h"
+
+ #ifdef __GNUC__
+ #pragma GCC diagnostic pop
+ #endif
+
#else
// SDK
#include "fsl_device_registers.h"
@@ -80,7 +86,7 @@ typedef struct {
enum {
NBYTES_ISO_FS_MAX = 1023, // FS ISO
NBYTES_ISO_HS_MAX = 1024, // HS ISO
- NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt
+ NBYTES_CBI_FS_MAX = 64, // FS control/bulk/interrupt. TODO some FS can do burst with higher size e.g 1024. Need to test
NBYTES_CBI_HS_MAX = 32767 // can be up to all 15-bit, but only tested with 4096
};
@@ -90,26 +96,36 @@ enum {
};
enum {
- CMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
- CMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
- CMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
- CMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), // reflect the soft-connect only, does not reflect the actual attached state
- CMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
- // 23-22 is link speed (only available for HighSpeed port)
- CMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
- CMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
- CMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
- CMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
+ DEVCMDSTAT_DEVICE_ADDR_MASK = TU_BIT(7 )-1,
+ DEVCMDSTAT_DEVICE_ENABLE_MASK = TU_BIT(7 ),
+ DEVCMDSTAT_SETUP_RECEIVED_MASK = TU_BIT(8 ),
+ DEVCMDSTAT_DEVICE_CONNECT_MASK = TU_BIT(16), // reflect the soft-connect only, does not reflect the actual attached state
+ DEVCMDSTAT_DEVICE_SUSPEND_MASK = TU_BIT(17),
+ // 23-22 is link speed (only available for HighSpeed port)
+ DEVCMDSTAT_CONNECT_CHANGE_MASK = TU_BIT(24),
+ DEVCMDSTAT_SUSPEND_CHANGE_MASK = TU_BIT(25),
+ DEVCMDSTAT_RESET_CHANGE_MASK = TU_BIT(26),
+ DEVCMDSTAT_VBUS_DEBOUNCED_MASK = TU_BIT(28),
};
enum {
- CMDSTAT_SPEED_SHIFT = 22
+ DEVCMDSTAT_SPEED_SHIFT = 22
};
//--------------------------------------------------------------------+
// Endpoint Command/Status List
//--------------------------------------------------------------------+
+// EP Command/Status field definition
+enum {
+ EPCS_TYPE = TU_BIT(26),
+ EPCS_RF_TV = TU_BIT(27),
+ EPCS_TOGGLE_RESET = TU_BIT(28),
+ EPCS_STALL = TU_BIT(29),
+ EPCS_DISABLED = TU_BIT(30),
+ EPCS_ACTIVE = TU_BIT(31),
+};
+
// Endpoint Command/Status
typedef union TU_ATTR_PACKED
{
@@ -133,13 +149,13 @@ typedef union TU_ATTR_PACKED
volatile struct {
uint32_t TU_RESERVED : 26;
- uint32_t is_iso : 1 ;
- uint32_t toggle_mode : 1 ;
+ uint32_t type : 1 ;
+ uint32_t rf_tv : 1 ; // rate feedback or toggle value
uint32_t toggle_reset : 1 ;
uint32_t stall : 1 ;
uint32_t disable : 1 ;
uint32_t active : 1 ;
- };
+ } cmd_sts;
}ep_cmd_sts_t;
TU_VERIFY_STATIC( sizeof(ep_cmd_sts_t) == 4, "size is not correct" );
@@ -176,11 +192,12 @@ typedef struct
// EP list must be 256-byte aligned
// Some MCU controller may require this variable to be placed in specific SRAM region.
// For example: LPC55s69 port1 Highspeed must be USB_RAM (0x40100000)
-// Use CFG_TUSB_MEM_SECTION to place it accordingly.
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
+// Use CFG_TUD_MEM_SECTION to place it accordingly.
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(256) static dcd_data_t _dcd;
// Dummy buffer to fix ZLPs overwriting the buffer (probably an USB/DMA controller bug)
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
+// TODO find way to save memory
+CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
//--------------------------------------------------------------------+
// Multiple Controllers
@@ -188,59 +205,73 @@ CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(64) static uint8_t dummy[8];
typedef struct
{
- dcd_registers_t* regs; // registers
- const tusb_speed_t max_speed; // max link speed
- const IRQn_Type irqnum; // IRQ number
- const uint8_t ep_pairs; // Max bi-directional Endpoints
+ dcd_registers_t* regs; // registers
+ const bool is_highspeed; // max link speed
+ const IRQn_Type irqnum; // IRQ number
+ const uint8_t ep_pairs; // Max bi-directional Endpoints
}dcd_controller_t;
#ifdef INCLUDE_FSL_DEVICE_REGISTERS
-static const dcd_controller_t _dcd_controller[] =
-{
- { .regs = (dcd_registers_t*) USB0_BASE , .max_speed = TUSB_SPEED_FULL, .irqnum = USB0_IRQn, .ep_pairs = FSL_FEATURE_USB_EP_NUM },
+static const dcd_controller_t _dcd_controller[] = {
+ { .regs = (dcd_registers_t*) USB0_BASE , .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = FSL_FEATURE_USB_EP_NUM },
#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
- { .regs = (dcd_registers_t*) USBHSD_BASE, .max_speed = TUSB_SPEED_HIGH, .irqnum = USB1_IRQn, .ep_pairs = FSL_FEATURE_USBHSD_EP_NUM }
+ { .regs = (dcd_registers_t*) USBHSD_BASE, .is_highspeed = true, .irqnum = USB1_IRQn, .ep_pairs = FSL_FEATURE_USBHSD_EP_NUM }
#endif
};
#else
-static const dcd_controller_t _dcd_controller[] =
-{
- { .regs = (dcd_registers_t*) LPC_USB0_BASE, .max_speed = TUSB_SPEED_FULL, .irqnum = USB0_IRQn, .ep_pairs = 5 },
+static const dcd_controller_t _dcd_controller[] = {
+ { .regs = (dcd_registers_t*) LPC_USB0_BASE, .is_highspeed = false, .irqnum = USB0_IRQn, .ep_pairs = 5 },
};
#endif
+#if defined(FSL_FEATURE_SOC_USBHSD_COUNT) && FSL_FEATURE_SOC_USBHSD_COUNT
+ #define IP3511_HAS_HIGHSPEED
+#endif
+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-static inline uint16_t get_buf_offset(void const * buffer)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint16_t get_buf_offset(void const * buffer) {
uint32_t addr = (uint32_t) buffer;
TU_ASSERT( (addr & 0x3f) == 0, 0 );
return ( (addr >> 6) & 0xFFFFUL ) ;
}
-static inline uint8_t ep_addr2id(uint8_t ep_addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline uint8_t ep_addr2id(uint8_t ep_addr) {
return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0);
}
+TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_highspeed) {
+ return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
+}
+
+TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
+ return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline ep_cmd_sts_t* get_ep_cs(uint8_t ep_id) {
+ return _dcd.ep[ep_id];
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool rhport_is_highspeed(uint8_t rhport) {
+ return _dcd_controller[rhport].is_highspeed;
+}
+
//--------------------------------------------------------------------+
// CONTROLLER API
//--------------------------------------------------------------------+
-static void prepare_setup_packet(uint8_t rhport)
-{
- if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
- {
- _dcd.ep[0][1].buffer_fs.offset = get_buf_offset(_dcd.setup_packet);
- }else
- {
- _dcd.ep[0][1].buffer_hs.offset = get_buf_offset(_dcd.setup_packet);
+static void prepare_setup_packet(uint8_t rhport) {
+ uint16_t const buf_offset = get_buf_offset(_dcd.setup_packet);
+ if ( _dcd_controller[rhport].is_highspeed ) {
+ _dcd.ep[0][1].buffer_hs.offset = buf_offset;
+ } else {
+ _dcd.ep[0][1].buffer_fs.offset = buf_offset;
}
}
@@ -258,8 +289,8 @@ static void edpt_reset_all(uint8_t rhport)
}
prepare_setup_packet(rhport);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
edpt_reset_all(rhport);
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@@ -268,10 +299,12 @@ void dcd_init(uint8_t rhport)
dcd_reg->DATABUFSTART = tu_align((uint32_t) &_dcd, TU_BIT(22)); // 22-bit alignment
dcd_reg->INTSTAT |= dcd_reg->INTSTAT; // clear all pending interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK;
- dcd_reg->DEVCMDSTAT |= CMDSTAT_DEVICE_ENABLE_MASK | CMDSTAT_DEVICE_CONNECT_MASK |
- CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
+ dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_DEVICE_ENABLE_MASK | DEVCMDSTAT_DEVICE_CONNECT_MASK |
+ DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
NVIC_ClearPendingIRQ(_dcd_controller[rhport].irqnum);
+
+ return true;
}
void dcd_int_enable(uint8_t rhport)
@@ -291,7 +324,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
// Response with status first before changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
- dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_ADDR_MASK;
+ dcd_reg->DEVCMDSTAT &= ~DEVCMDSTAT_DEVICE_ADDR_MASK;
dcd_reg->DEVCMDSTAT |= dev_addr;
}
@@ -303,13 +336,13 @@ void dcd_remote_wakeup(uint8_t rhport)
void dcd_connect(uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->DEVCMDSTAT |= CMDSTAT_DEVICE_CONNECT_MASK;
+ dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_DEVICE_CONNECT_MASK;
}
void dcd_disconnect(uint8_t rhport)
{
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->DEVCMDSTAT &= ~CMDSTAT_DEVICE_CONNECT_MASK;
+ dcd_reg->DEVCMDSTAT &= ~DEVCMDSTAT_DEVICE_CONNECT_MASK;
}
void dcd_sof_enable(uint8_t rhport, bool en)
@@ -329,7 +362,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
// TODO cannot able to STALL Control OUT endpoint !!!!! FIXME try some walk-around
uint8_t const ep_id = ep_addr2id(ep_addr);
- _dcd.ep[ep_id][0].stall = 1;
+ _dcd.ep[ep_id][0].cmd_sts.stall = 1;
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
@@ -338,21 +371,41 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
uint8_t const ep_id = ep_addr2id(ep_addr);
- _dcd.ep[ep_id][0].stall = 0;
- _dcd.ep[ep_id][0].toggle_reset = 1;
- _dcd.ep[ep_id][0].toggle_mode = 0;
+ _dcd.ep[ep_id][0].cmd_sts.stall = 0;
+ _dcd.ep[ep_id][0].cmd_sts.toggle_reset = 1;
+ _dcd.ep[ep_id][0].cmd_sts.rf_tv = 0;
}
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
{
//------------- Prepare Queue Head -------------//
uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
+ ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
// Check if endpoint is available
- TU_ASSERT( _dcd.ep[ep_id][0].disable && _dcd.ep[ep_id][1].disable );
+ TU_ASSERT( ep_cs[0].cmd_sts.disable && ep_cs[1].cmd_sts.disable );
edpt_reset(rhport, ep_id);
- _dcd.ep[ep_id][0].is_iso = (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
+
+ switch (p_endpoint_desc->bmAttributes.xfer) {
+ case TUSB_XFER_ISOCHRONOUS:
+ ep_cs[0].cmd_sts.type = 1;
+ break;
+
+ case TUSB_XFER_INTERRUPT:
+ // What is interrupt endpoint in rate feedback mode ?
+ if ( rhport_is_highspeed(rhport) ) {
+ ep_cs[0].cmd_sts.type = 1;
+ ep_cs[0].cmd_sts.rf_tv = 1;
+ }
+ break;
+
+ case TUSB_XFER_BULK:
+ // nothing to do both type and rf_tv are 0
+ break;
+
+ default: break;
+ }
// Enable EP interrupt
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@@ -365,56 +418,64 @@ void dcd_edpt_close_all (uint8_t rhport)
{
for (uint8_t ep_id = 0; ep_id < 2*_dcd_controller[rhport].ep_pairs; ++ep_id)
{
- _dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
- _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
+ _dcd.ep[ep_id][0].cmd_sts.active = _dcd.ep[ep_id][0].cmd_sts.active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
+ _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
}
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
-
+
uint8_t ep_id = ep_addr2id(ep_addr);
- _dcd.ep[ep_id][0].active = _dcd.ep[ep_id][0].active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
- _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
+ _dcd.ep[ep_id][0].cmd_sts.active = _dcd.ep[ep_id][0].cmd_sts.active = 0; // TODO proper way is to EPSKIP then wait ep[][].active then write ep[][].disable (see table 778 in LPC55S69 Use Manual)
+ _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
-static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes)
-{
+static void prepare_ep_xfer(uint8_t rhport, uint8_t ep_id, uint16_t buf_offset, uint16_t total_bytes) {
uint16_t nbytes;
+ ep_cmd_sts_t* ep_cs = get_ep_cs(ep_id);
- if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL )
- {
- nbytes = tu_min16(total_bytes, _dcd.ep[ep_id][0].is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX);
- _dcd.ep[ep_id][0].buffer_fs.offset = buf_offset;
- _dcd.ep[ep_id][0].buffer_fs.nbytes = nbytes;
- }else
- {
- nbytes = tu_min16(total_bytes, NBYTES_CBI_HS_MAX);
- _dcd.ep[ep_id][0].buffer_hs.offset = buf_offset;
- _dcd.ep[ep_id][0].buffer_hs.nbytes = nbytes;
+ const bool is_iso = ep_is_iso(ep_cs, _dcd_controller[rhport].is_highspeed);
+
+ if ( rhport_is_highspeed(rhport) ) {
+ nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_HS_MAX : NBYTES_CBI_HS_MAX);
+ #if TU_CHECK_MCU(OPT_MCU_LPC54)
+ // LPC54 Errata USB.1: In USB high-speed device mode, the NBytes field does not decrement after BULK OUT transfer.
+ // Suggested Work-around: Program the NByte to the max packet size (512)
+ // Actual Work-around: round up NByte to multiple of 4.
+ // Note: this can cause buffer overflowed and corrupt data if host send more data than total_bytes
+ if ( (ep_id > 1) && (ep_id & 0x01) == 0 && ep_is_bulk(ep_cs) ) {
+ if ( nbytes & 0x03 ) {
+ nbytes = tu_align4(nbytes) + 4;
+ }
+ }
+ #endif
+
+ ep_cs[0].buffer_hs.offset = buf_offset;
+ ep_cs[0].buffer_hs.nbytes = nbytes;
+ }else {
+ nbytes = tu_min16(total_bytes, is_iso ? NBYTES_ISO_FS_MAX : NBYTES_CBI_FS_MAX);
+ ep_cs[0].buffer_fs.offset = buf_offset;
+ ep_cs[0].buffer_fs.nbytes = nbytes;
}
_dcd.dma[ep_id].nbytes = nbytes;
-
- _dcd.ep[ep_id][0].active = 1;
+ ep_cs[0].cmd_sts.active = 1;
}
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
uint8_t const ep_id = ep_addr2id(ep_addr);
+ if (!buffer || total_bytes == 0) {
+ // Although having no data, ZLPs can cause buffer overwritten to zeroes. Probably due to USB/DMA controller side
+ // effect/bug. Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
+ buffer = (uint8_t *) (uint32_t) dummy;
+ }
+
tu_memclr(&_dcd.dma[ep_id], sizeof(xfer_dma_t));
_dcd.dma[ep_id].total_bytes = total_bytes;
- if (!buffer)
- {
- // Although having no data, ZLPs can cause buffer overwritten to zeroes.
- // Probably due to USB/DMA controller side effect/bug.
- // Assigned buffer offset to (valid) dummy to prevent overwriting to DATABUFSTART
- buffer = (uint8_t*)(uint32_t)dummy;
- }
-
prepare_ep_xfer(rhport, ep_id, get_buf_offset(buffer), total_bytes);
return true;
@@ -426,12 +487,12 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
static void bus_reset(uint8_t rhport)
{
tu_memclr(&_dcd, sizeof(dcd_data_t));
- edpt_reset_all(rhport);
+ edpt_reset_all(rhport);
// disable all endpoints as specified by LPC55S69 UM Table 778
for(uint8_t ep_id = 0; ep_id < 2*MAX_EP_PAIRS; ep_id++)
{
- _dcd.ep[ep_id][0].disable = _dcd.ep[ep_id][1].disable = 1;
+ _dcd.ep[ep_id][0].cmd_sts.disable = _dcd.ep[ep_id][1].cmd_sts.disable = 1;
}
dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
@@ -441,50 +502,49 @@ static void bus_reset(uint8_t rhport)
dcd_reg->EPSKIP = 0xFFFFFFFF;
dcd_reg->INTSTAT = dcd_reg->INTSTAT; // clear all pending interrupt
- dcd_reg->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
+ dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_SETUP_RECEIVED_MASK; // clear setup received interrupt
dcd_reg->INTEN = INT_DEVICE_STATUS_MASK | TU_BIT(0) | TU_BIT(1); // enable device status & control endpoints
}
-static void process_xfer_isr(uint8_t rhport, uint32_t int_status)
-{
+static void process_xfer_isr(uint8_t rhport, uint32_t int_status) {
uint8_t const max_ep = 2*_dcd_controller[rhport].ep_pairs;
- for(uint8_t ep_id = 0; ep_id < max_ep; ep_id++ )
- {
- if ( tu_bit_test(int_status, ep_id) )
- {
+ for(uint8_t ep_id = 0; ep_id < max_ep; ep_id++ ) {
+ if ( tu_bit_test(int_status, ep_id) ) {
ep_cmd_sts_t * ep_cs = &_dcd.ep[ep_id][0];
xfer_dma_t* xfer_dma = &_dcd.dma[ep_id];
- if ( ep_id == 0 || ep_id == 1)
- {
+ if ( ep_id <= 1 ) {
// For control endpoint, we need to manually clear Active bit
- ep_cs->active = 0;
+ ep_cs->cmd_sts.active = 0;
}
uint16_t buf_offset;
uint16_t buf_nbytes;
- if (_dcd_controller[rhport].max_speed == TUSB_SPEED_FULL)
- {
- buf_offset = ep_cs->buffer_fs.offset;
- buf_nbytes = ep_cs->buffer_fs.nbytes;
- }else
- {
+ if ( rhport_is_highspeed(rhport) ) {
buf_offset = ep_cs->buffer_hs.offset;
buf_nbytes = ep_cs->buffer_hs.nbytes;
+
+ #if TU_CHECK_MCU(OPT_MCU_LPC54)
+ // LPC54 Errata USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer
+ // There is no work-around. For EP in transfer, the NByte value can be ignored after a packet is transmitted.
+ if ( (ep_id > 1) && (ep_id & 0x01) == 1 && ep_is_bulk(ep_cs) ) {
+ buf_nbytes = 0;
+ }
+ #endif
+ } else {
+ buf_offset = ep_cs->buffer_fs.offset;
+ buf_nbytes = ep_cs->buffer_fs.nbytes;
}
xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes;
- if ( (buf_nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) )
- {
+ if ( (buf_nbytes == 0) && (xfer_dma->total_bytes > xfer_dma->xferred_bytes) ) {
// There is more data to transfer
// buff_offset has been already increased by hw to correct value for next transfer
prepare_ep_xfer(rhport, ep_id, buf_offset, xfer_dma->total_bytes - xfer_dma->xferred_bytes);
- }
- else
- {
+ } else {
// for detecting ZLP
xfer_dma->total_bytes = xfer_dma->xferred_bytes;
@@ -511,19 +571,16 @@ void dcd_int_handler(uint8_t rhport)
//------------- Device Status -------------//
if ( int_status & INT_DEVICE_STATUS_MASK )
{
- dcd_reg->DEVCMDSTAT |= CMDSTAT_RESET_CHANGE_MASK | CMDSTAT_CONNECT_CHANGE_MASK | CMDSTAT_SUSPEND_CHANGE_MASK;
+ dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_RESET_CHANGE_MASK | DEVCMDSTAT_CONNECT_CHANGE_MASK | DEVCMDSTAT_SUSPEND_CHANGE_MASK;
- if ( cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
+ if ( cmd_stat & DEVCMDSTAT_RESET_CHANGE_MASK) // bus reset
{
bus_reset(rhport);
tusb_speed_t speed = TUSB_SPEED_FULL;
-
- if (_dcd_controller[rhport].max_speed == TUSB_SPEED_HIGH)
- {
+ if ( _dcd_controller[rhport].is_highspeed ) {
// 0 : reserved, 1 : full, 2 : high, 3: super
- if ( 2 == ((cmd_stat >> CMDSTAT_SPEED_SHIFT) & 0x3UL) )
- {
+ if ( 2 == ((cmd_stat >> DEVCMDSTAT_SPEED_SHIFT) & 0x3UL) ) {
speed= TUSB_SPEED_HIGH;
}
}
@@ -531,35 +588,35 @@ void dcd_int_handler(uint8_t rhport)
dcd_event_bus_reset(rhport, speed, true);
}
- if (cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)
+ if (cmd_stat & DEVCMDSTAT_CONNECT_CHANGE_MASK)
{
// device disconnect
- if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
+ if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{
// debouncing as this can be set when device is powering
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
}
- if (cmd_stat & CMDSTAT_SUSPEND_CHANGE_MASK)
+ if (cmd_stat & DEVCMDSTAT_SUSPEND_CHANGE_MASK)
{
// suspend signal, bus idle for more than 3ms
// Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
- if (cmd_stat & CMDSTAT_DEVICE_ADDR_MASK)
+ if (cmd_stat & DEVCMDSTAT_DEVICE_ADDR_MASK)
{
- dcd_event_bus_signal(rhport, (cmd_stat & CMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true);
+ dcd_event_bus_signal(rhport, (cmd_stat & DEVCMDSTAT_DEVICE_SUSPEND_MASK) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME, true);
}
}
}
// Setup Receive
- if ( tu_bit_test(int_status, 0) && (cmd_stat & CMDSTAT_SETUP_RECEIVED_MASK) )
+ if ( tu_bit_test(int_status, 0) && (cmd_stat & DEVCMDSTAT_SETUP_RECEIVED_MASK) )
{
// Follow UM flowchart to clear Active & Stall on both Control IN/OUT endpoints
- _dcd.ep[0][0].active = _dcd.ep[1][0].active = 0;
- _dcd.ep[0][0].stall = _dcd.ep[1][0].stall = 0;
+ _dcd.ep[0][0].cmd_sts.active = _dcd.ep[1][0].cmd_sts.active = 0;
+ _dcd.ep[0][0].cmd_sts.stall = _dcd.ep[1][0].cmd_sts.stall = 0;
- dcd_reg->DEVCMDSTAT |= CMDSTAT_SETUP_RECEIVED_MASK;
+ dcd_reg->DEVCMDSTAT |= DEVCMDSTAT_SETUP_RECEIVED_MASK;
dcd_event_setup_received(rhport, _dcd.setup_packet, true);
@@ -575,4 +632,3 @@ void dcd_int_handler(uint8_t rhport)
}
#endif
-
diff --git a/src/portable/nxp/transdimension/common_transdimension.h b/src/portable/nxp/transdimension/common_transdimension.h
deleted file mode 100644
index 69074de41..000000000
--- a/src/portable/nxp/transdimension/common_transdimension.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2021, Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef COMMON_TRANSDIMENSION_H_
-#define COMMON_TRANSDIMENSION_H_
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-// USBCMD
-enum {
- USBCMD_RUN_STOP = TU_BIT(0),
- USBCMD_RESET = TU_BIT(1),
- USBCMD_SETUP_TRIPWIRE = TU_BIT(13),
- USBCMD_ADD_QTD_TRIPWIRE = TU_BIT(14) ///< This bit is used as a semaphore to ensure the to proper addition of a new dTD to an active (primed) endpoint’s linked list. This bit is set and cleared by software during the process of adding a new dTD
-// Interrupt Threshold bit 23:16
-};
-
-// PORTSC1
-#define PORTSC1_PORT_SPEED_POS 26
-
-enum {
- PORTSC1_CURRENT_CONNECT_STATUS = TU_BIT(0),
- PORTSC1_FORCE_PORT_RESUME = TU_BIT(6),
- PORTSC1_SUSPEND = TU_BIT(7),
- PORTSC1_FORCE_FULL_SPEED = TU_BIT(24),
- PORTSC1_PORT_SPEED = TU_BIT(26) | TU_BIT(27)
-};
-
-// OTGSC
-enum {
- OTGSC_VBUS_DISCHARGE = TU_BIT(0),
- OTGSC_VBUS_CHARGE = TU_BIT(1),
-// OTGSC_HWASSIST_AUTORESET = TU_BIT(2),
- OTGSC_OTG_TERMINATION = TU_BIT(3), ///< Must set to 1 when OTG go to device mode
- OTGSC_DATA_PULSING = TU_BIT(4),
- OTGSC_ID_PULLUP = TU_BIT(5),
-// OTGSC_HWASSIT_DATA_PULSE = TU_BIT(6),
-// OTGSC_HWASSIT_BDIS_ACONN = TU_BIT(7),
- OTGSC_ID = TU_BIT(8), ///< 0 = A device, 1 = B Device
- OTGSC_A_VBUS_VALID = TU_BIT(9),
- OTGSC_A_SESSION_VALID = TU_BIT(10),
- OTGSC_B_SESSION_VALID = TU_BIT(11),
- OTGSC_B_SESSION_END = TU_BIT(12),
- OTGSC_1MS_TOGGLE = TU_BIT(13),
- OTGSC_DATA_BUS_PULSING_STATUS = TU_BIT(14),
-};
-
-// USBMode
-enum {
- USBMODE_CM_DEVICE = 2,
- USBMODE_CM_HOST = 3,
-
- USBMODE_SLOM = TU_BIT(3),
- USBMODE_SDIS = TU_BIT(4),
-
- USBMODE_VBUS_POWER_SELECT = TU_BIT(5), // Need to be enabled for LPC18XX/43XX in host mode
-};
-
-// Device Registers
-typedef struct
-{
- //------------- ID + HW Parameter Registers-------------//
- __I uint32_t TU_RESERVED[64]; ///< For iMX RT10xx, but not used by LPC18XX/LPC43XX
-
- //------------- Capability Registers-------------//
- __I uint8_t CAPLENGTH; ///< Capability Registers Length
- __I uint8_t TU_RESERVED[1];
- __I uint16_t HCIVERSION; ///< Host Controller Interface Version
-
- __I uint32_t HCSPARAMS; ///< Host Controller Structural Parameters
- __I uint32_t HCCPARAMS; ///< Host Controller Capability Parameters
- __I uint32_t TU_RESERVED[5];
-
- __I uint16_t DCIVERSION; ///< Device Controller Interface Version
- __I uint8_t TU_RESERVED[2];
-
- __I uint32_t DCCPARAMS; ///< Device Controller Capability Parameters
- __I uint32_t TU_RESERVED[6];
-
- //------------- Operational Registers -------------//
- __IO uint32_t USBCMD; ///< USB Command Register
- __IO uint32_t USBSTS; ///< USB Status Register
- __IO uint32_t USBINTR; ///< Interrupt Enable Register
- __IO uint32_t FRINDEX; ///< USB Frame Index
- __I uint32_t TU_RESERVED;
- __IO uint32_t DEVICEADDR; ///< Device Address
- __IO uint32_t ENDPTLISTADDR; ///< Endpoint List Address
- __I uint32_t TU_RESERVED;
- __IO uint32_t BURSTSIZE; ///< Programmable Burst Size
- __IO uint32_t TXFILLTUNING; ///< TX FIFO Fill Tuning
- uint32_t TU_RESERVED[4];
- __IO uint32_t ENDPTNAK; ///< Endpoint NAK
- __IO uint32_t ENDPTNAKEN; ///< Endpoint NAK Enable
- __I uint32_t TU_RESERVED;
- __IO uint32_t PORTSC1; ///< Port Status & Control
- __I uint32_t TU_RESERVED[7];
- __IO uint32_t OTGSC; ///< On-The-Go Status & control
- __IO uint32_t USBMODE; ///< USB Device Mode
- __IO uint32_t ENDPTSETUPSTAT; ///< Endpoint Setup Status
- __IO uint32_t ENDPTPRIME; ///< Endpoint Prime
- __IO uint32_t ENDPTFLUSH; ///< Endpoint Flush
- __I uint32_t ENDPTSTAT; ///< Endpoint Status
- __IO uint32_t ENDPTCOMPLETE; ///< Endpoint Complete
- __IO uint32_t ENDPTCTRL[8]; ///< Endpoint Control 0 - 7
-} dcd_registers_t, hcd_registers_t;
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* COMMON_TRANSDIMENSION_H_ */
diff --git a/src/portable/nxp/transdimension/dcd_transdimension.c b/src/portable/nxp/transdimension/dcd_transdimension.c
deleted file mode 100644
index 1f27a6872..000000000
--- a/src/portable/nxp/transdimension/dcd_transdimension.c
+++ /dev/null
@@ -1,672 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if CFG_TUD_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
-
-#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
- #include "fsl_device_registers.h"
- #define INCLUDE_FSL_DEVICE_REGISTERS
-#else
- // LPCOpen for 18xx & 43xx
- #include "chip.h"
-#endif
-
-#include "common/tusb_common.h"
-#include "device/dcd.h"
-#include "common_transdimension.h"
-
-#if defined(__CORTEX_M) && __CORTEX_M == 7 && __DCACHE_PRESENT == 1
- #define CleanInvalidateDCache_by_Addr SCB_CleanInvalidateDCache_by_Addr
-#else
- #define CleanInvalidateDCache_by_Addr(_addr, _dsize)
-#endif
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-
-// ENDPTCTRL
-enum {
- ENDPTCTRL_STALL = TU_BIT(0),
- ENDPTCTRL_TOGGLE_INHIBIT = TU_BIT(5), // used for test only
- ENDPTCTRL_TOGGLE_RESET = TU_BIT(6),
- ENDPTCTRL_ENABLE = TU_BIT(7)
-};
-
-enum {
- ENDPTCTRL_TYPE_POS = 2, // Endpoint type is 2-bit field
-};
-
-// USBSTS, USBINTR
-enum {
- INTR_USB = TU_BIT(0),
- INTR_ERROR = TU_BIT(1),
- INTR_PORT_CHANGE = TU_BIT(2),
- INTR_RESET = TU_BIT(6),
- INTR_SOF = TU_BIT(7),
- INTR_SUSPEND = TU_BIT(8),
- INTR_NAK = TU_BIT(16)
-};
-
-// Queue Transfer Descriptor
-typedef struct
-{
- // Word 0: Next QTD Pointer
- uint32_t next; ///< Next link pointer This field contains the physical memory address of the next dTD to be processed
-
- // Word 1: qTQ Token
- uint32_t : 3 ;
- volatile uint32_t xact_err : 1 ;
- uint32_t : 1 ;
- volatile uint32_t buffer_err : 1 ;
- volatile uint32_t halted : 1 ;
- volatile uint32_t active : 1 ;
- uint32_t : 2 ;
- uint32_t iso_mult_override : 2 ; ///< This field can be used for transmit ISOs to override the MULT field in the dQH. This field must be zero for all packet types that are not transmit-ISO.
- uint32_t : 3 ;
- uint32_t int_on_complete : 1 ;
- volatile uint32_t total_bytes : 15 ;
- uint32_t : 1 ;
-
- // Word 2-6: Buffer Page Pointer List, Each element in the list is a 4K page aligned, physical memory address. The lower 12 bits in each pointer are reserved (except for the first one) as each memory pointer must reference the start of a 4K page
- uint32_t buffer[5]; ///< buffer1 has frame_n for TODO Isochronous
-
- //--------------------------------------------------------------------+
- // TD is 32 bytes aligned but occupies only 28 bytes
- // Therefore there are 4 bytes padding that we can use.
- //--------------------------------------------------------------------+
- uint16_t expected_bytes;
- uint8_t reserved[2];
-} dcd_qtd_t;
-
-TU_VERIFY_STATIC( sizeof(dcd_qtd_t) == 32, "size is not correct");
-
-// Queue Head
-typedef struct
-{
- // Word 0: Capabilities and Characteristics
- uint32_t : 15 ; ///< Number of packets executed per transaction descriptor 00 - Execute N transactions as demonstrated by the USB variable length protocol where N is computed using Max_packet_length and the Total_bytes field in the dTD. 01 - Execute one transaction 10 - Execute two transactions 11 - Execute three transactions Remark: Non-isochronous endpoints must set MULT = 00. Remark: Isochronous endpoints must set MULT = 01, 10, or 11 as needed.
- uint32_t int_on_setup : 1 ; ///< Interrupt on setup This bit is used on control type endpoints to indicate if USBINT is set in response to a setup being received.
- uint32_t max_packet_size : 11 ; ///< Endpoint's wMaxPacketSize
- uint32_t : 2 ;
- uint32_t zero_length_termination : 1 ; ///< This bit is used for non-isochronous endpoints to indicate when a zero-length packet is received to terminate transfers in case the total transfer length is “multiple”. 0 - Enable zero-length packet to terminate transfers equal to a multiple of Max_packet_length (default). 1 - Disable zero-length packet on transfers that are equal in length to a multiple Max_packet_length.
- uint32_t iso_mult : 2 ; ///<
-
- // Word 1: Current qTD Pointer
- volatile uint32_t qtd_addr;
-
- // Word 2-9: Transfer Overlay
- volatile dcd_qtd_t qtd_overlay;
-
- // Word 10-11: Setup request (control OUT only)
- volatile tusb_control_request_t setup_request;
-
- //--------------------------------------------------------------------+
- // QHD is 64 bytes aligned but occupies only 48 bytes
- // Therefore there are 16 bytes padding that we can use.
- //--------------------------------------------------------------------+
- tu_fifo_t * ff;
- uint8_t reserved[12];
-} dcd_qhd_t;
-
-TU_VERIFY_STATIC( sizeof(dcd_qhd_t) == 64, "size is not correct");
-
-//--------------------------------------------------------------------+
-// Variables
-//--------------------------------------------------------------------+
-
-typedef struct
-{
- dcd_registers_t* regs; // registers
- const IRQn_Type irqnum; // IRQ number
- const uint8_t ep_count; // Max bi-directional Endpoints
-}dcd_controller_t;
-
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
- static const dcd_controller_t _dcd_controller[] =
- {
- // RT1010 and RT1020 only has 1 USB controller
- #if FSL_FEATURE_SOC_USBHS_COUNT == 1
- { .regs = (dcd_registers_t*) USB_BASE , .irqnum = USB_OTG1_IRQn, .ep_count = 8 }
- #else
- { .regs = (dcd_registers_t*) USB1_BASE, .irqnum = USB_OTG1_IRQn, .ep_count = 8 },
- { .regs = (dcd_registers_t*) USB2_BASE, .irqnum = USB_OTG2_IRQn, .ep_count = 8 }
- #endif
- };
-
-#else
- static const dcd_controller_t _dcd_controller[] =
- {
- { .regs = (dcd_registers_t*) LPC_USB0_BASE, .irqnum = USB0_IRQn, .ep_count = 6 },
- { .regs = (dcd_registers_t*) LPC_USB1_BASE, .irqnum = USB1_IRQn, .ep_count = 4 }
- };
-#endif
-
-#define QTD_NEXT_INVALID 0x01
-
-typedef struct {
- // Must be at 2K alignment
- // Each endpoint with direction (IN/OUT) occupies a queue head
- // for portability, TinyUSB only queue 1 TD for each Qhd
- dcd_qhd_t qhd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(64);
- dcd_qtd_t qtd[TUP_DCD_ENDPOINT_MAX][2] TU_ATTR_ALIGNED(32);
-}dcd_data_t;
-
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(2048)
-static dcd_data_t _dcd_data;
-
-//--------------------------------------------------------------------+
-// Controller API
-//--------------------------------------------------------------------+
-
-/// follows LPC43xx User Manual 23.10.3
-static void bus_reset(uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- // The reset value for all endpoint types is the control endpoint. If one endpoint
- // direction is enabled and the paired endpoint of opposite direction is disabled, then the
- // endpoint type of the unused direction must be changed from the control type to any other
- // type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
- // for the data PID tracking on the active endpoint.
- for( uint8_t i=1; i < _dcd_controller[rhport].ep_count; i++)
- {
- dcd_reg->ENDPTCTRL[i] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
- }
-
- //------------- Clear All Registers -------------//
- dcd_reg->ENDPTNAK = dcd_reg->ENDPTNAK;
- dcd_reg->ENDPTNAKEN = 0;
- dcd_reg->USBSTS = dcd_reg->USBSTS;
- dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
- dcd_reg->ENDPTCOMPLETE = dcd_reg->ENDPTCOMPLETE;
-
- while (dcd_reg->ENDPTPRIME) {}
- dcd_reg->ENDPTFLUSH = 0xFFFFFFFF;
- while (dcd_reg->ENDPTFLUSH) {}
-
- // read reset bit in portsc
-
- //------------- Queue Head & Queue TD -------------//
- tu_memclr(&_dcd_data, sizeof(dcd_data_t));
-
- //------------- Set up Control Endpoints (0 OUT, 1 IN) -------------//
- _dcd_data.qhd[0][0].zero_length_termination = _dcd_data.qhd[0][1].zero_length_termination = 1;
- _dcd_data.qhd[0][0].max_packet_size = _dcd_data.qhd[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
- _dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID;
-
- _dcd_data.qhd[0][0].int_on_setup = 1; // OUT only
-}
-
-void dcd_init(uint8_t rhport)
-{
- tu_memclr(&_dcd_data, sizeof(dcd_data_t));
-
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- // Reset controller
- dcd_reg->USBCMD |= USBCMD_RESET;
- while( dcd_reg->USBCMD & USBCMD_RESET ) {}
-
- // Set mode to device, must be set immediately after reset
- dcd_reg->USBMODE = USBMODE_CM_DEVICE;
- dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
-
-#if !TUD_OPT_HIGH_SPEED
- dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
-#endif
-
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
-
- dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment
- dcd_reg->USBSTS = dcd_reg->USBSTS;
- dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND;
-
- dcd_reg->USBCMD &= ~0x00FF0000; // Interrupt Threshold Interval = 0
- dcd_reg->USBCMD |= USBCMD_RUN_STOP; // Connect
-}
-
-void dcd_int_enable(uint8_t rhport)
-{
- NVIC_EnableIRQ(_dcd_controller[rhport].irqnum);
-}
-
-void dcd_int_disable(uint8_t rhport)
-{
- NVIC_DisableIRQ(_dcd_controller[rhport].irqnum);
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- // Response with status first before changing device address
- dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
-
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->DEVICEADDR = (dev_addr << 25) | TU_BIT(24);
-}
-
-void dcd_remote_wakeup(uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->PORTSC1 |= PORTSC1_FORCE_PORT_RESUME;
-}
-
-void dcd_connect(uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->USBCMD |= USBCMD_RUN_STOP;
-}
-
-void dcd_disconnect(uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->USBCMD &= ~USBCMD_RUN_STOP;
-}
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- // TODO implement later
-}
-
-//--------------------------------------------------------------------+
-// HELPER
-//--------------------------------------------------------------------+
-
-static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes)
-{
- // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the
- // address to 32-byte boundaries. Buffer must be word aligned
- CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31);
-
- tu_memclr(p_qtd, sizeof(dcd_qtd_t));
-
- p_qtd->next = QTD_NEXT_INVALID;
- p_qtd->active = 1;
- p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
- p_qtd->int_on_complete = true;
-
- if (data_ptr != NULL)
- {
- p_qtd->buffer[0] = (uint32_t) data_ptr;
-
- uint32_t const bufend = p_qtd->buffer[0] + total_bytes;
- for(uint8_t i=1; i<5; i++)
- {
- uint32_t const next_page = tu_align4k( p_qtd->buffer[i-1] ) + 4096;
- if ( bufend <= next_page ) break;
-
- p_qtd->buffer[i] = next_page;
-
- // TODO page[1] FRAME_N for ISO transfer
- }
- }
-}
-
-//--------------------------------------------------------------------+
-// DCD Endpoint Port
-//--------------------------------------------------------------------+
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
-
- // flush to abort any primed buffer
- dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- // data toggle also need to be reset
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_reg->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << ( dir ? 16 : 0 );
- dcd_reg->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << ( dir ? 16 : 0));
-}
-
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
- uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
-
- // Must not exceed max endpoint number
- TU_ASSERT( epnum < _dcd_controller[rhport].ep_count );
-
- //------------- Prepare Queue Head -------------//
- dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
- tu_memclr(p_qhd, sizeof(dcd_qhd_t));
-
- p_qhd->zero_length_termination = 1;
- p_qhd->max_packet_size = tu_edpt_packet_size(p_endpoint_desc);
- if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
- {
- p_qhd->iso_mult = 1;
- }
-
- p_qhd->qtd_overlay.next = QTD_NEXT_INVALID;
-
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
-
- // Enable EP Control
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET;
-
- if ( dir == TUSB_DIR_OUT )
- {
- dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0xFFFF0000u) | epctrl;
- }else
- {
- dcd_reg->ENDPTCTRL[epnum] = (dcd_reg->ENDPTCTRL[epnum] & 0x0000FFFFu) | (epctrl << 16);
- }
-
- return true;
-}
-
-void dcd_edpt_close_all (uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- // Disable all non-control endpoints
- for( uint8_t epnum=1; epnum < _dcd_controller[rhport].ep_count; epnum++)
- {
- _dcd_data.qhd[epnum][TUSB_DIR_OUT].qtd_overlay.halted = 1;
- _dcd_data.qhd[epnum][TUSB_DIR_IN ].qtd_overlay.halted = 1;
-
- dcd_reg->ENDPTFLUSH = TU_BIT(epnum) | TU_BIT(epnum+16);
- dcd_reg->ENDPTCTRL[epnum] = (TUSB_XFER_BULK << ENDPTCTRL_TYPE_POS) | (TUSB_XFER_BULK << (16+ENDPTCTRL_TYPE_POS));
- }
-}
-
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- _dcd_data.qhd[epnum][dir].qtd_overlay.halted = 1;
-
- // Flush EP
- uint32_t const flush_mask = TU_BIT(epnum + (dir ? 16 : 0));
- dcd_reg->ENDPTFLUSH = flush_mask;
- while(dcd_reg->ENDPTFLUSH & flush_mask);
-
- // Clear EP enable
- dcd_reg->ENDPTCTRL[epnum] &=~(ENDPTCTRL_ENABLE << (dir ? 16 : 0));
-}
-
-static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
-
- p_qhd->qtd_overlay.halted = false; // clear any previous error
- p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd
-
- // flush cache
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
-
- if ( epnum == 0 )
- {
- // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism
- // wait until ENDPTSETUPSTAT before priming data/status in response TODO add time out
- while(dcd_reg->ENDPTSETUPSTAT & TU_BIT(0)) {}
- }
-
- // start transfer
- dcd_reg->ENDPTPRIME = TU_BIT(epnum + (dir ? 16 : 0));
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- dcd_qhd_t* p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t* p_qtd = &_dcd_data.qtd[epnum][dir];
-
- // Prepare qtd
- qtd_init(p_qtd, buffer, total_bytes);
-
- // Start qhd transfer
- p_qhd->ff = NULL;
- qhd_start_xfer(rhport, epnum, dir);
-
- return true;
-}
-
-// fifo has to be aligned to 4k boundary
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
-
- tu_fifo_buffer_info_t fifo_info;
-
- if (dir)
- {
- tu_fifo_get_read_info(ff, &fifo_info);
- } else
- {
- tu_fifo_get_write_info(ff, &fifo_info);
- }
-
- if ( fifo_info.len_lin >= total_bytes )
- {
- // Linear length is enough for this transfer
- qtd_init(p_qtd, fifo_info.ptr_lin, total_bytes);
- }
- else
- {
- // linear part is not enough
-
- // prepare TD up to linear length
- qtd_init(p_qtd, fifo_info.ptr_lin, fifo_info.len_lin);
-
- if ( !tu_offset4k((uint32_t) fifo_info.ptr_wrap) && !tu_offset4k(tu_fifo_depth(ff)) )
- {
- // If buffer is aligned to 4K & buffer size is multiple of 4K
- // We can make use of buffer page array to also combine the linear + wrapped length
- p_qtd->total_bytes = p_qtd->expected_bytes = total_bytes;
-
- for(uint8_t i = 1, page = 0; i < 5; i++)
- {
- // pick up buffer array where linear ends
- if (p_qtd->buffer[i] == 0)
- {
- p_qtd->buffer[i] = (uint32_t) fifo_info.ptr_wrap + 4096 * page;
- page++;
- }
- }
-
- CleanInvalidateDCache_by_Addr((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31);
- }
- else
- {
- // TODO we may need to carry the wrapped length after the linear part complete
- // for now only transfer up to linear part
- }
- }
-
- // Start qhd transfer
- p_qhd->ff = ff;
- qhd_start_xfer(rhport, epnum, dir);
-
- return true;
-}
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-
-static void process_edpt_complete_isr(uint8_t rhport, uint8_t epnum, uint8_t dir)
-{
- dcd_qhd_t * p_qhd = &_dcd_data.qhd[epnum][dir];
- dcd_qtd_t * p_qtd = &_dcd_data.qtd[epnum][dir];
-
- uint8_t result = p_qtd->halted ? XFER_RESULT_STALLED :
- ( p_qtd->xact_err || p_qtd->buffer_err ) ? XFER_RESULT_FAILED : XFER_RESULT_SUCCESS;
-
- if ( result != XFER_RESULT_SUCCESS )
- {
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
- // flush to abort error buffer
- dcd_reg->ENDPTFLUSH = TU_BIT(epnum + (dir ? 16 : 0));
- }
-
- uint16_t const xferred_bytes = p_qtd->expected_bytes - p_qtd->total_bytes;
-
- if (p_qhd->ff)
- {
- if (dir == TUSB_DIR_IN)
- {
- tu_fifo_advance_read_pointer(p_qhd->ff, xferred_bytes);
- } else
- {
- tu_fifo_advance_write_pointer(p_qhd->ff, xferred_bytes);
- }
- }
-
- // only number of bytes in the IOC qtd
- dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, dir), xferred_bytes, result, true);
-}
-
-void dcd_int_handler(uint8_t rhport)
-{
- dcd_registers_t* dcd_reg = _dcd_controller[rhport].regs;
-
- uint32_t const int_enable = dcd_reg->USBINTR;
- uint32_t const int_status = dcd_reg->USBSTS & int_enable;
- dcd_reg->USBSTS = int_status; // Acknowledge handled interrupt
-
- // disabled interrupt sources
- if (int_status == 0) return;
-
- // Set if the port controller enters the full or high-speed operational state.
- // either from Bus Reset or Suspended state
- if (int_status & INTR_PORT_CHANGE)
- {
- // TU_LOG2("PortChange %08lx\r\n", dcd_reg->PORTSC1);
-
- // Reset interrupt is not enabled, we manually check if Port Change is due
- // to connection / disconnection
- if ( dcd_reg->USBSTS & INTR_RESET )
- {
- dcd_reg->USBSTS = INTR_RESET;
-
- if (dcd_reg->PORTSC1 & PORTSC1_CURRENT_CONNECT_STATUS)
- {
- uint32_t const speed = (dcd_reg->PORTSC1 & PORTSC1_PORT_SPEED) >> PORTSC1_PORT_SPEED_POS;
- bus_reset(rhport);
- dcd_event_bus_reset(rhport, (tusb_speed_t) speed, true);
- }else
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
- }
- }
- else
- {
- // Triggered by resuming from suspended state
- if ( !(dcd_reg->PORTSC1 & PORTSC1_SUSPEND) )
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
- }
- }
- }
-
- if (int_status & INTR_SUSPEND)
- {
- // TU_LOG2("Suspend %08lx\r\n", dcd_reg->PORTSC1);
-
- if (dcd_reg->PORTSC1 & PORTSC1_SUSPEND)
- {
- // Note: Host may delay more than 3 ms before and/or after bus reset before doing enumeration.
- // Skip suspend event if we are not addressed
- if ((dcd_reg->DEVICEADDR >> 25) & 0x0f)
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
- }
- }
- }
-
- if (int_status & INTR_USB)
- {
- // Make sure we read the latest version of _dcd_data.
- CleanInvalidateDCache_by_Addr((uint32_t*) &_dcd_data, sizeof(dcd_data_t));
-
- uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE;
- dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge
-
- if (dcd_reg->ENDPTSETUPSTAT)
- {
- //------------- Set up Received -------------//
- // 23.10.10.2 Operational model for setup transfers
- dcd_reg->ENDPTSETUPSTAT = dcd_reg->ENDPTSETUPSTAT;
-
- dcd_event_setup_received(rhport, (uint8_t*)(uintptr_t) &_dcd_data.qhd[0][0].setup_request, true);
- }
-
- // 23.10.12.3 Failed QTD also get ENDPTCOMPLETE set
- // nothing to do, we will submit xfer as error to usbd
- // if (int_status & INTR_ERROR) { }
-
- if ( edpt_complete )
- {
- for(uint8_t epnum = 0; epnum < TUP_DCD_ENDPOINT_MAX; epnum++)
- {
- if ( tu_bit_test(edpt_complete, epnum) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_OUT);
- if ( tu_bit_test(edpt_complete, epnum+16) ) process_edpt_complete_isr(rhport, epnum, TUSB_DIR_IN);
- }
- }
- }
-
- if (int_status & INTR_SOF)
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
- }
-}
-
-#endif
diff --git a/src/portable/nxp/transdimension/hcd_transdimension.c b/src/portable/nxp/transdimension/hcd_transdimension.c
deleted file mode 100644
index 392764ff6..000000000
--- a/src/portable/nxp/transdimension/hcd_transdimension.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-// NXP Trans-Dimension USB IP implement EHCI for host functionality
-
-#if CFG_TUH_ENABLED && \
- (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT)
-
-#warning "transdimenion is renamed to chipidea (portable/chipidea/ci_hs) to match other opensource naming convention such as linux. This file will be removed in the future, please update your makefile accordingly"
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
- #include "fsl_device_registers.h"
-#else
- // LPCOpen for 18xx & 43xx
- #include "chip.h"
-#endif
-
-#include "common/tusb_common.h"
-#include "common_transdimension.h"
-#include "portable/ehci/ehci_api.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-
-// TODO can be merged with dcd_controller_t
-typedef struct
-{
- uint32_t regs_base; // registers base
- const IRQn_Type irqnum; // IRQ number
-}hcd_controller_t;
-
-#if CFG_TUSB_MCU == OPT_MCU_MIMXRT
- static const hcd_controller_t _hcd_controller[] =
- {
- // RT1010 and RT1020 only has 1 USB controller
- #if FSL_FEATURE_SOC_USBHS_COUNT == 1
- { .regs_base = USB_BASE , .irqnum = USB_OTG1_IRQn }
- #else
- { .regs_base = USB1_BASE, .irqnum = USB_OTG1_IRQn },
- { .regs_base = USB2_BASE, .irqnum = USB_OTG2_IRQn }
- #endif
- };
-
-#else
- static const hcd_controller_t _hcd_controller[] =
- {
- { .regs_base = LPC_USB0_BASE, .irqnum = USB0_IRQn },
- { .regs_base = LPC_USB1_BASE, .irqnum = USB1_IRQn }
- };
-#endif
-
-//--------------------------------------------------------------------+
-// Controller API
-//--------------------------------------------------------------------+
-
-bool hcd_init(uint8_t rhport)
-{
- hcd_registers_t* hcd_reg = (hcd_registers_t*) _hcd_controller[rhport].regs_base;
-
- // Reset controller
- hcd_reg->USBCMD |= USBCMD_RESET;
- while( hcd_reg->USBCMD & USBCMD_RESET ) {}
-
- // Set mode to device, must be set immediately after reset
-#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX
- // LPC18XX/43XX need to set VBUS Power Select to HIGH
- // RHPORT1 is fullspeed only (need external PHY for Highspeed)
- hcd_reg->USBMODE = USBMODE_CM_HOST | USBMODE_VBUS_POWER_SELECT;
- if (rhport == 1) hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
-#else
- hcd_reg->USBMODE = USBMODE_CM_HOST;
-#endif
-
- // FIXME force full speed, still have issue with Highspeed enumeration
- hcd_reg->PORTSC1 |= PORTSC1_FORCE_FULL_SPEED;
-
- return ehci_init(rhport, (uint32_t) &hcd_reg->CAPLENGTH, (uint32_t) &hcd_reg->USBCMD);
-}
-
-void hcd_int_enable(uint8_t rhport)
-{
- NVIC_EnableIRQ(_hcd_controller[rhport].irqnum);
-}
-
-void hcd_int_disable(uint8_t rhport)
-{
- NVIC_DisableIRQ(_hcd_controller[rhport].irqnum);
-}
-
-#endif
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index 3e523ebc2..ce35eab70 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -28,6 +28,10 @@
#if CFG_TUH_ENABLED && defined(TUP_USBIP_OHCI)
+#ifndef TUP_OHCI_RHPORTS
+#error OHCI is enabled, but TUP_OHCI_RHPORTS is not defined.
+#endif
+
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
@@ -141,7 +145,7 @@ enum {
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data;
+CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(256) static ohci_data_t ohci_data;
static ohci_ed_t * const p_ed_head[] =
{
@@ -153,34 +157,72 @@ static ohci_ed_t * const p_ed_head[] =
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed);
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr);
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd);
//--------------------------------------------------------------------+
// USBH-HCD API
//--------------------------------------------------------------------+
-// Initialization according to 5.1.1.4
-bool hcd_init(uint8_t rhport)
+
+// If your system requires separation of virtual and physical memory, implement
+// 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;
+}
+
+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;
+}
+
+// Initialization according to 5.1.1.4
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
//------------- Data Structure init -------------//
tu_memclr(&ohci_data, sizeof(ohci_data_t));
for(uint8_t i=0; i<32; i++)
- { // assign all interrupt pointes to period head ed
- ohci_data.hcca.interrupt_table[i] = (uint32_t) &ohci_data.period_head_ed;
+ { // assign all interrupt pointers to period head ed
+ ohci_data.hcca.interrupt_table[i] = (uint32_t) _phys_addr(&ohci_data.period_head_ed);
}
ohci_data.control[0].ed.skip = 1;
ohci_data.bulk_head_ed.skip = 1;
ohci_data.period_head_ed.skip = 1;
+ //If OHCI hardware is in SMM mode, gain ownership (Ref OHCI spec 5.1.1.3.3)
+ if (OHCI_REG->control_bit.interrupt_routing == 1)
+ {
+ OHCI_REG->command_status_bit.ownership_change_request = 1;
+ while (OHCI_REG->control_bit.interrupt_routing == 1) {}
+ }
+
+ //If OHCI hardware has come from warm-boot, signal resume (Ref OHCI spec 5.1.1.3.4)
+ else if (OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_RESET &&
+ OHCI_REG->control_bit.hc_functional_state != OHCI_CONTROL_FUNCSTATE_OPERATIONAL)
+ {
+ //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
+ }
+
// reset controller
OHCI_REG->command_status_bit.controller_reset = 1;
while( OHCI_REG->command_status_bit.controller_reset ) {} // should not take longer than 10 us
//------------- init ohci registers -------------//
- OHCI_REG->control_head_ed = (uint32_t) &ohci_data.control[0].ed;
- OHCI_REG->bulk_head_ed = (uint32_t) &ohci_data.bulk_head_ed;
- OHCI_REG->hcca = (uint32_t) &ohci_data.hcca;
+ OHCI_REG->control_head_ed = (uint32_t) _phys_addr(&ohci_data.control[0].ed);
+ OHCI_REG->bulk_head_ed = (uint32_t) _phys_addr(&ohci_data.bulk_head_ed);
+ OHCI_REG->hcca = (uint32_t) _phys_addr(&ohci_data.hcca);
OHCI_REG->interrupt_disable = OHCI_REG->interrupt_enable; // disable all interrupts
OHCI_REG->interrupt_status = OHCI_REG->interrupt_status; // clear current set bits
@@ -188,15 +230,21 @@ bool hcd_init(uint8_t rhport)
OHCI_INT_UNRECOVERABLE_ERROR_MASK | OHCI_INT_FRAME_OVERFLOW_MASK | OHCI_INT_RHPORT_STATUS_CHANGE_MASK |
OHCI_INT_MASTER_ENABLE_MASK;
- OHCI_REG->control |= OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
+ OHCI_REG->control = OHCI_CONTROL_CONTROL_BULK_RATIO | OHCI_CONTROL_LIST_CONTROL_ENABLE_MASK |
OHCI_CONTROL_LIST_BULK_ENABLE_MASK | OHCI_CONTROL_LIST_PERIODIC_ENABLE_MASK; // TODO Isochronous
OHCI_REG->frame_interval = (OHCI_FMINTERVAL_FSMPS << 16) | OHCI_FMINTERVAL_FI;
+ OHCI_REG->frame_interval ^= (1 << 31); //Must toggle when frame_interval is updated.
OHCI_REG->periodic_start = (OHCI_FMINTERVAL_FI * 9) / 10; // Periodic start is 90% of frame interval
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
+
return true;
}
@@ -212,8 +260,7 @@ uint32_t hcd_frame_number(uint8_t rhport)
//--------------------------------------------------------------------+
void hcd_port_reset(uint8_t hostid)
{
- (void) hostid;
- OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
+ OHCI_REG->rhport_status[hostid] = RHPORT_PORT_RESET_STATUS_MASK;
}
void hcd_port_reset_end(uint8_t rhport)
@@ -223,14 +270,12 @@ void hcd_port_reset_end(uint8_t rhport)
bool hcd_port_connect_status(uint8_t hostid)
{
- (void) hostid;
- return OHCI_REG->rhport_status_bit[0].current_connect_status;
+ return OHCI_REG->rhport_status_bit[hostid].current_connect_status;
}
tusb_speed_t hcd_port_speed_get(uint8_t hostid)
{
- (void) hostid;
- return OHCI_REG->rhport_status_bit[0].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
+ return OHCI_REG->rhport_status_bit[hostid].low_speed_device_attached ? TUSB_SPEED_LOW : TUSB_SPEED_FULL;
}
// endpoints are tied to an address, which only reclaim after a long delay when enumerating
@@ -297,19 +342,24 @@ static void ed_init(ohci_ed_t *p_ed, uint8_t dev_addr, uint16_t ep_size, uint8_t
p_ed->is_interrupt_xfer = (xfer_type == TUSB_XFER_INTERRUPT ? 1 : 0);
}
-static void gtd_init(ohci_gtd_t* p_td, uint8_t* data_ptr, uint16_t total_bytes)
-{
+static void gtd_init(ohci_gtd_t *p_td, uint8_t *data_ptr, uint16_t total_bytes) {
tu_memclr(p_td, sizeof(ohci_gtd_t));
- p_td->used = 1;
- p_td->expected_bytes = total_bytes;
+ p_td->used = 1;
+ gtd_get_extra_data(p_td)->expected_bytes = total_bytes;
+
+ p_td->buffer_rounding = 1; // less than queued length is not a error
+ p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
+ p_td->condition_code = OHCI_CCODE_NOT_ACCESSED;
- p_td->buffer_rounding = 1; // less than queued length is not a error
- p_td->delay_interrupt = OHCI_INT_ON_COMPLETE_NO;
- p_td->condition_code = OHCI_CCODE_NOT_ACCESSED;
+ uint8_t *cbp = (uint8_t *) _phys_addr(data_ptr);
- p_td->current_buffer_pointer = data_ptr;
- p_td->buffer_end = total_bytes ? (data_ptr + total_bytes-1) : data_ptr;
+ p_td->current_buffer_pointer = cbp;
+ if ( total_bytes ) {
+ p_td->buffer_end = _phys_addr(data_ptr + total_bytes - 1);
+ } else {
+ p_td->buffer_end = cbp;
+ }
}
static ohci_ed_t * ed_from_addr(uint8_t dev_addr, uint8_t ep_addr)
@@ -345,7 +395,7 @@ static ohci_ed_t * ed_find_free(void)
static void ed_list_insert(ohci_ed_t * p_pre, ohci_ed_t * p_ed)
{
p_ed->next = p_pre->next;
- p_pre->next = (uint32_t) p_ed;
+ p_pre->next = (uint32_t) _phys_addr(p_ed);
}
static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
@@ -354,20 +404,24 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr)
while( p_prev->next )
{
- ohci_ed_t* ed = (ohci_ed_t*) p_prev->next;
+ ohci_ed_t* ed = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
if (ed->dev_addr == dev_addr)
{
- // unlink ed
+ // Prevent Host Controller from processing this ED while we remove it
+ ed->skip = 1;
+
+ // unlink ed, will also move up p_prev
p_prev->next = ed->next;
// point the removed ED's next pointer to list head to make sure HC can always safely move away from this ED
- ed->next = (uint32_t) p_head;
+ ed->next = (uint32_t) _phys_addr(p_head);
ed->used = 0;
+ ed->skip = 0;
+ }else
+ {
+ p_prev = (ohci_ed_t*) _virt_addr((void *)p_prev->next);
}
-
- // check next valid since we could remove it
- if (p_prev->next) p_prev = (ohci_ed_t*) p_prev->next;
}
}
@@ -386,11 +440,11 @@ static void td_insert_to_ed(ohci_ed_t* p_ed, ohci_gtd_t * p_gtd)
// tail is always NULL
if ( tu_align16(p_ed->td_head.address) == 0 )
{ // TD queue is empty --> head = TD
- p_ed->td_head.address |= (uint32_t) p_gtd;
+ p_ed->td_head.address |= (uint32_t) _phys_addr(p_gtd);
}
else
{ // TODO currently only support queue up to 2 TD each endpoint at a time
- ((ohci_gtd_t*) tu_align16(p_ed->td_head.address))->next = (uint32_t) p_gtd;
+ ((ohci_gtd_t*) tu_align16((uint32_t)_virt_addr((void *)p_ed->td_head.address)))->next = (uint32_t) _phys_addr(p_gtd);
}
}
@@ -439,14 +493,14 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
ohci_ed_t* ed = &ohci_data.control[dev_addr].ed;
ohci_gtd_t *qtd = &ohci_data.control[dev_addr].gtd;
- gtd_init(qtd, (uint8_t*) setup_packet, 8);
+ gtd_init(qtd, (uint8_t*)(uintptr_t) setup_packet, 8);
qtd->index = dev_addr;
qtd->pid = PID_SETUP;
qtd->data_toggle = GTD_DT_DATA0;
- qtd->delay_interrupt = 0;
+ qtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
//------------- Attach TDs list to Control Endpoint -------------//
- ed->td_head.address = (uint32_t) qtd;
+ ed->td_head.address = (uint32_t) _phys_addr(qtd);
OHCI_REG->command_status_bit.control_list_filled = 1;
@@ -470,9 +524,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
gtd->index = dev_addr;
gtd->pid = dir ? PID_IN : PID_OUT;
gtd->data_toggle = GTD_DT_DATA1; // Both Data and Ack stage start with DATA1
- gtd->delay_interrupt = 0;
+ gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
- ed->td_head.address = (uint32_t) gtd;
+ ed->td_head.address = (uint32_t) _phys_addr(gtd);
OHCI_REG->command_status_bit.control_list_filled = 1;
}else
@@ -484,7 +538,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
gtd_init(gtd, buffer, buflen);
gtd->index = ed-ohci_data.ed_pool;
- gtd->delay_interrupt = 0;
+ gtd->delay_interrupt = OHCI_INT_ON_COMPLETE_YES;
td_insert_to_ed(ed, gtd);
@@ -495,8 +549,16 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *
return true;
}
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ // TODO not implemented yet
+ return false;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
ohci_ed_t * const p_ed = ed_from_addr(dev_addr, ep_addr);
p_ed->is_stalled = 0;
@@ -520,16 +582,17 @@ static ohci_td_item_t* list_reverse(ohci_td_item_t* td_head)
while(td_head != NULL)
{
+ td_head = _virt_addr(td_head);
uint32_t next = td_head->next;
// make current's item become reverse's first item
td_head->next = (uint32_t) td_reverse_head;
- td_reverse_head = td_head;
+ td_reverse_head = _phys_addr(td_head);
td_head = (ohci_td_item_t*) next; // advance to next item
}
- return td_reverse_head;
+ return _virt_addr(td_reverse_head);
}
static inline bool gtd_is_control(ohci_gtd_t const * const p_qtd)
@@ -548,6 +611,15 @@ static inline ohci_ed_t* gtd_get_ed(ohci_gtd_t const * const p_qtd)
}
}
+static gtd_extra_data_t *gtd_get_extra_data(ohci_gtd_t const * const gtd) {
+ if ( gtd_is_control(gtd) ) {
+ uint8_t idx = ((uintptr_t)gtd - (uintptr_t)&ohci_data.control->gtd) / sizeof(ohci_data.control[0]);
+ return &ohci_data.gtd_extra_control[idx];
+ }else {
+ return &ohci_data.gtd_extra[gtd - ohci_data.gtd_pool];
+ }
+}
+
static inline uint32_t gtd_xfer_byte_left(uint32_t buffer_end, uint32_t current_buffer)
{
// 5.2.9 OHCI sample code
@@ -565,6 +637,7 @@ static void done_queue_isr(uint8_t hostid)
// done head is written in reversed order of completion --> need to reverse the done queue first
ohci_td_item_t* td_head = list_reverse ( (ohci_td_item_t*) tu_align16(ohci_data.hcca.done_head) );
+ ohci_data.hcca.done_head = 0;
while( td_head != NULL )
{
@@ -578,8 +651,7 @@ static void done_queue_isr(uint8_t hostid)
if ( (qtd->delay_interrupt == OHCI_INT_ON_COMPLETE_YES) || (event != XFER_RESULT_SUCCESS) )
{
ohci_ed_t * const ed = gtd_get_ed(qtd);
-
- uint32_t const xferred_bytes = qtd->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
+ uint32_t const xferred_bytes = gtd_get_extra_data(qtd)->expected_bytes - gtd_xfer_byte_left((uint32_t) qtd->buffer_end, (uint32_t) qtd->current_buffer_pointer);
// NOTE Assuming the current list is BULK and there is no other EDs in the list has queued TDs.
// When there is a error resulting this ED is halted, and this EP still has other queued TD
@@ -588,7 +660,7 @@ static void done_queue_isr(uint8_t hostid)
// --> HC will not process Control list (due to service ratio when Bulk list not empty)
// To walk-around this, the halted ED will have TailP = HeadP (empty list condition), when clearing halt
// the TailP must be set back to NULL for processing remaining TDs
- if ((event != XFER_RESULT_SUCCESS))
+ if (event != XFER_RESULT_SUCCESS)
{
ed->td_tail &= 0x0Ful;
ed->td_tail |= tu_align16(ed->td_head.address); // mark halted EP as empty queue
@@ -600,17 +672,21 @@ static void done_queue_isr(uint8_t hostid)
hcd_event_xfer_complete(ed->dev_addr, tu_edpt_addr(ed->ep_number, dir), xferred_bytes, event, true);
}
- td_head = (ohci_td_item_t*) td_head->next;
+ td_head = (ohci_td_item_t*) _virt_addr((void *)td_head->next);
}
}
-void hcd_int_handler(uint8_t hostid)
-{
+void hcd_int_handler(uint8_t hostid, bool in_isr) {
+ (void) in_isr;
+
uint32_t const int_en = OHCI_REG->interrupt_enable;
uint32_t const int_status = OHCI_REG->interrupt_status & int_en;
if (int_status == 0) return;
+ // Disable MIE as per OHCI spec 5.3
+ OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK;
+
// Frame number overflow
if ( int_status & OHCI_INT_FRAME_OVERFLOW_MASK )
{
@@ -620,29 +696,30 @@ void hcd_int_handler(uint8_t hostid)
//------------- RootHub status -------------//
if ( int_status & OHCI_INT_RHPORT_STATUS_CHANGE_MASK )
{
- uint32_t const rhport_status = OHCI_REG->rhport_status[0] & RHPORT_ALL_CHANGE_MASK;
-
- // TODO dual port is not yet supported
- if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
+ for (int i = 0; i < TUP_OHCI_RHPORTS; i++)
{
- // TODO check if remote wake-up
- if ( OHCI_REG->rhport_status_bit[0].current_connect_status )
+ uint32_t const rhport_status = OHCI_REG->rhport_status[i] & RHPORT_ALL_CHANGE_MASK;
+ if ( rhport_status & RHPORT_CONNECT_STATUS_CHANGE_MASK )
{
- // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
- OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
- hcd_event_device_attach(hostid, true);
- }else
- {
- hcd_event_device_remove(hostid, true);
+ // TODO check if remote wake-up
+ if ( OHCI_REG->rhport_status_bit[i].current_connect_status )
+ {
+ // TODO reset port immediately, without this controller will got 2-3 (debouncing connection status change)
+ OHCI_REG->rhport_status[i] = RHPORT_PORT_RESET_STATUS_MASK;
+ hcd_event_device_attach(i, true);
+ }else
+ {
+ hcd_event_device_remove(i, true);
+ }
}
- }
- if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
- {
+ if ( rhport_status & RHPORT_PORT_SUSPEND_CHANGE_MASK)
+ {
- }
+ }
- OHCI_REG->rhport_status[0] = rhport_status; // acknowledge all interrupt
+ OHCI_REG->rhport_status[i] = rhport_status; // acknowledge all interrupt
+ }
}
//------------- Transfer Complete -------------//
@@ -652,6 +729,8 @@ void hcd_int_handler(uint8_t hostid)
}
OHCI_REG->interrupt_status = int_status; // Acknowledge handled interrupt
+
+ OHCI_REG->interrupt_enable = OHCI_INT_MASTER_ENABLE_MASK; // Enable MIE
}
//--------------------------------------------------------------------+
// HELPER
@@ -659,4 +738,3 @@ void hcd_int_handler(uint8_t hostid)
#endif
-
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index 9fc954c8f..94bad5df7 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -34,7 +34,7 @@
//--------------------------------------------------------------------+
// OHCI CONFIGURATION & CONSTANTS
//--------------------------------------------------------------------+
-#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enalbed
+#define HOST_HCD_XFER_INTERRUPT // TODO interrupt is used widely, should always be enabled
#define OHCI_PERIODIC_LIST (defined HOST_HCD_XFER_INTERRUPT || defined HOST_HCD_XFER_ISOCHRONOUS)
// TODO merge OHCI with EHCI
@@ -45,6 +45,9 @@ enum {
#define ED_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX)
#define GTD_MAX ED_MAX
+// tinyUSB's OHCI implementation caps number of EDs to 8 bits
+TU_VERIFY_STATIC (ED_MAX <= 256, "Reduce CFG_TUH_DEVICE_MAX or CFG_TUH_ENDPOINT_MAX");
+
//--------------------------------------------------------------------+
// OHCI Data Structure
//--------------------------------------------------------------------+
@@ -58,7 +61,9 @@ typedef struct {
TU_VERIFY_STATIC( sizeof(ohci_hcca_t) == 256, "size is not correct" );
-typedef struct {
+// common link item for gtd and itd for list travel
+// use as pointer only
+typedef struct TU_ATTR_ALIGNED(16) {
uint32_t reserved[2];
volatile uint32_t next;
uint32_t reserved2;
@@ -68,9 +73,8 @@ typedef struct TU_ATTR_ALIGNED(16)
{
// Word 0
uint32_t used : 1;
- uint32_t index : 4; // endpoint index the td belongs to, or device address in case of control xfer
- uint32_t expected_bytes : 13; // TODO available for hcd
-
+ uint32_t index : 8; // endpoint index the gtd belongs to, or device address in case of control xfer
+ uint32_t : 9; // can be used
uint32_t buffer_rounding : 1;
uint32_t pid : 2;
uint32_t delay_interrupt : 3;
@@ -79,7 +83,7 @@ typedef struct TU_ATTR_ALIGNED(16)
volatile uint32_t condition_code : 4;
// Word 1
- volatile uint8_t* current_buffer_pointer;
+ uint8_t* volatile current_buffer_pointer;
// Word 2 : next TD
volatile uint32_t next;
@@ -150,9 +154,12 @@ typedef struct TU_ATTR_ALIGNED(32)
TU_VERIFY_STATIC( sizeof(ochi_itd_t) == 32, "size is not correct" );
+typedef struct {
+ uint16_t expected_bytes; // up to 8192 bytes so max is 13 bits
+} gtd_extra_data_t;
+
// structure with member alignment required from large to small
-typedef struct TU_ATTR_ALIGNED(256)
-{
+typedef struct TU_ATTR_ALIGNED(256) {
ohci_hcca_t hcca;
ohci_ed_t bulk_head_ed; // static bulk head (dummy)
@@ -162,14 +169,17 @@ typedef struct TU_ATTR_ALIGNED(256)
struct {
ohci_ed_t ed;
ohci_gtd_t gtd;
- }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
+ } control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
// ochi_itd_t itd[OHCI_MAX_ITD]; // itd requires alignment of 32
ohci_ed_t ed_pool[ED_MAX];
ohci_gtd_t gtd_pool[GTD_MAX];
- volatile uint16_t frame_number_hi;
+ // extra data needed by TDs that can't fit in the TD struct
+ gtd_extra_data_t gtd_extra_control[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1];
+ gtd_extra_data_t gtd_extra[GTD_MAX];
+ volatile uint16_t frame_number_hi;
} ohci_data_t;
//--------------------------------------------------------------------+
@@ -230,8 +240,27 @@ typedef volatile struct
uint32_t periodic_start;
uint32_t lowspeed_threshold;
- uint32_t rh_descriptorA;
- uint32_t rh_descriptorB;
+ union {
+ uint32_t rh_descriptorA;
+ struct {
+ uint32_t number_downstream_ports : 8;
+ uint32_t power_switching_mode : 1;
+ uint32_t no_power_switching : 1;
+ uint32_t device_type : 1;
+ uint32_t overcurrent_protection_mode : 1;
+ uint32_t no_over_current_protection : 1;
+ uint32_t reserved : 11;
+ uint32_t power_on_to_good_time : 8;
+ } rh_descriptorA_bit;
+ };
+
+ union {
+ uint32_t rh_descriptorB;
+ struct {
+ uint32_t device_removable : 16;
+ uint32_t port_power_control_mask : 16;
+ } rh_descriptorB_bit;
+ };
union {
uint32_t rh_status;
@@ -248,7 +277,7 @@ typedef volatile struct
};
union {
- uint32_t rhport_status[2]; // TODO NXP OHCI controller only has 2 ports
+ uint32_t rhport_status[TUP_OHCI_RHPORTS];
struct {
uint32_t current_connect_status : 1;
uint32_t port_enable_status : 1;
@@ -265,11 +294,11 @@ typedef volatile struct
uint32_t port_over_current_indicator_change : 1;
uint32_t port_reset_status_change : 1;
uint32_t TU_RESERVED : 11;
- }rhport_status_bit[2];
+ }rhport_status_bit[TUP_OHCI_RHPORTS];
};
}ohci_registers_t;
-TU_VERIFY_STATIC( sizeof(ohci_registers_t) == 0x5c, "size is not correct");
+TU_VERIFY_STATIC( sizeof(ohci_registers_t) == (0x54 + (4 * TUP_OHCI_RHPORTS)), "size is not correct");
#ifdef __cplusplus
}
diff --git a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
index 1bc5594d8..60afbd435 100644
--- a/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
@@ -50,12 +50,13 @@ static usb_descriptor_buffers_t desc;
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
-
+ (void) rh_init;
static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
usb_device = pio_usb_device_init(&config, &desc);
+
+ return true;
}
// Enable device interrupt
diff --git a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
index 58b153ac3..6422afff1 100644
--- a/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
+++ b/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
@@ -48,17 +48,16 @@ static pio_usb_configuration_t pio_host_cfg = PIO_USB_DEFAULT_CONFIG;
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
-bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param)
-{
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void *cfg_param) {
(void) rhport;
TU_VERIFY(cfg_id == TUH_CFGID_RPI_PIO_USB_CONFIGURATION);
memcpy(&pio_host_cfg, cfg_param, sizeof(pio_usb_configuration_t));
return true;
}
-bool hcd_init(uint8_t rhport)
-{
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
// To run USB SOF interrupt in core1, call this init in core1
pio_usb_host_init(&pio_host_cfg);
@@ -66,20 +65,17 @@ bool hcd_init(uint8_t rhport)
return true;
}
-void hcd_port_reset(uint8_t rhport)
-{
+void hcd_port_reset(uint8_t rhport) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
pio_usb_host_port_reset_start(pio_rhport);
}
-void hcd_port_reset_end(uint8_t rhport)
-{
+void hcd_port_reset_end(uint8_t rhport) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
pio_usb_host_port_reset_end(pio_rhport);
}
-bool hcd_port_connect_status(uint8_t rhport)
-{
+bool hcd_port_connect_status(uint8_t rhport) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
root_port_t *root = PIO_USB_ROOT_PORT(pio_rhport);
@@ -88,33 +84,28 @@ bool hcd_port_connect_status(uint8_t rhport)
return line_state != PORT_PIN_SE0;
}
-tusb_speed_t hcd_port_speed_get(uint8_t rhport)
-{
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
// TODO determine link speed
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return PIO_USB_ROOT_PORT(pio_rhport)->is_fullspeed ? TUSB_SPEED_FULL : TUSB_SPEED_LOW;
}
// Close all opened endpoint belong to this device
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
-{
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
pio_usb_host_close_device(pio_rhport, dev_addr);
}
-uint32_t hcd_frame_number(uint8_t rhport)
-{
+uint32_t hcd_frame_number(uint8_t rhport) {
(void) rhport;
- return 0;
+ return pio_usb_host_get_frame_number();
}
-void hcd_int_enable(uint8_t rhport)
-{
+void hcd_int_enable(uint8_t rhport) {
(void) rhport;
}
-void hcd_int_disable(uint8_t rhport)
-{
+void hcd_int_disable(uint8_t rhport) {
(void) rhport;
}
@@ -122,24 +113,26 @@ void hcd_int_disable(uint8_t rhport)
// Endpoint API
//--------------------------------------------------------------------+
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep)
-{
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *desc_ep) {
hcd_devtree_info_t dev_tree;
hcd_devtree_get_info(dev_addr, &dev_tree);
bool const need_pre = (dev_tree.hub_addr && dev_tree.speed == TUSB_SPEED_LOW);
uint8_t const pio_rhport = RHPORT_PIO(rhport);
- return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const*) desc_ep, need_pre);
+ return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const *) desc_ep, need_pre);
}
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
-{
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen);
}
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
-{
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ uint8_t const pio_rhport = RHPORT_PIO(rhport);
+ return pio_usb_host_endpoint_abort_transfer(pio_rhport, dev_addr, ep_addr);
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_send_setup(pio_rhport, dev_addr, setup_packet);
}
@@ -150,34 +143,32 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet
// // so if any transfer is active on epx, we are busy. Interrupt endpoints have their own
// // EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint
// // on that device
-// pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr);
+// pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\r\n", dev_addr, ep_addr);
// struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
// assert(ep);
// bool busy = ep->active;
-// pico_trace("busy == %d\n", busy);
+// pico_trace("busy == %d\r\n", busy);
// return busy;
//}
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
(void) dev_addr;
(void) ep_addr;
return true;
}
-static void __no_inline_not_in_flash_func(handle_endpoint_irq)(root_port_t* rport, xfer_result_t result, volatile uint32_t* ep_reg)
-{
+static void __no_inline_not_in_flash_func(handle_endpoint_irq)(root_port_t *rport, xfer_result_t result,
+ volatile uint32_t *ep_reg) {
(void) rport;
const uint32_t ep_all = *ep_reg;
- for(uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++)
- {
+ for ( uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++ ) {
uint32_t const mask = (1u << ep_idx);
- if (ep_all & mask)
- {
- endpoint_t* ep = PIO_USB_ENDPOINT(ep_idx);
+ if ( ep_all & mask ) {
+ endpoint_t * ep = PIO_USB_ENDPOINT(ep_idx);
hcd_event_xfer_complete(ep->dev_addr, ep->ep_num, ep->actual_len, result, true);
}
}
@@ -187,35 +178,29 @@ static void __no_inline_not_in_flash_func(handle_endpoint_irq)(root_port_t* rpor
}
// IRQ Handler
-void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id)
-{
+void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id) {
uint8_t const tu_rhport = root_id + 1;
- root_port_t* rport = PIO_USB_ROOT_PORT(root_id);
+ root_port_t *rport = PIO_USB_ROOT_PORT(root_id);
uint32_t const ints = rport->ints;
- if ( ints & PIO_USB_INTS_CONNECT_BITS )
- {
- hcd_event_device_attach(tu_rhport, true);
+ if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS ) {
+ handle_endpoint_irq(rport, XFER_RESULT_SUCCESS, &rport->ep_complete);
}
- if ( ints & PIO_USB_INTS_DISCONNECT_BITS )
- {
- hcd_event_device_remove(tu_rhport, true);
+ if ( ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS ) {
+ handle_endpoint_irq(rport, XFER_RESULT_STALLED, &rport->ep_stalled);
}
- if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS )
- {
- handle_endpoint_irq(rport, XFER_RESULT_SUCCESS, &rport->ep_complete);
+ if ( ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS ) {
+ handle_endpoint_irq(rport, XFER_RESULT_FAILED, &rport->ep_error);
}
- if ( ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS )
- {
- handle_endpoint_irq(rport, XFER_RESULT_STALLED, &rport->ep_stalled);
+ if ( ints & PIO_USB_INTS_CONNECT_BITS ) {
+ hcd_event_device_attach(tu_rhport, true);
}
- if ( ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS )
- {
- handle_endpoint_irq(rport, XFER_RESULT_FAILED, &rport->ep_error);
+ if ( ints & PIO_USB_INTS_DISCONNECT_BITS ) {
+ hcd_event_device_remove(tu_rhport, true);
}
// clear all
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 4952d29b1..af08b549d 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -29,6 +29,7 @@
#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB
#include "pico.h"
+#include "hardware/sync.h"
#include "rp2040_usb.h"
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
@@ -45,12 +46,8 @@
/*------------------------------------------------------------------*/
/* Low level controller
*------------------------------------------------------------------*/
-
-#define usb_hw_set hw_set_alias(usb_hw)
-#define usb_hw_clear hw_clear_alias(usb_hw)
-
// Init these in dcd_init
-static uint8_t *next_buffer_ptr;
+static uint8_t* next_buffer_ptr;
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
@@ -58,79 +55,43 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
static bool _sof_enable = false;
-TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
-{
+TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
return &hw_endpoints[num][dir];
}
-static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
-{
+TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) {
uint8_t num = tu_edpt_number(ep_addr);
tusb_dir_t dir = tu_edpt_dir(ep_addr);
return hw_endpoint_get_by_num(num, dir);
}
-static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type)
-{
- // size must be multiple of 64
- uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u;
+// Allocate from the USB buffer space (max 3840 bytes)
+static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) {
+ // round up size to multiple of 64
+ size = tu_round_up(ep->wMaxPacketSize, 64);
// double buffered Bulk endpoint
- if ( transfer_type == TUSB_XFER_BULK )
- {
+ if (ep->transfer_type == TUSB_XFER_BULK) {
size *= 2u;
}
+ // assign buffer
ep->hw_data_buf = next_buffer_ptr;
next_buffer_ptr += size;
- assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0);
- uint dpram_offset = hw_data_offset(ep->hw_data_buf);
- hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
-
- pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
-
- // Fill in endpoint control register with buffer offset
- uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
-
- *ep->endpoint_control = reg;
+ hard_assert(next_buffer_ptr < usb_dpram->epx_data + sizeof(usb_dpram->epx_data));
+ pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf);
}
-static void _hw_endpoint_close(struct hw_endpoint *ep)
-{
- // Clear hardware registers and then zero the struct
- // Clears endpoint enable
- *ep->endpoint_control = 0;
- // Clears buffer available, etc
- *ep->buffer_control = 0;
- // Clear any endpoint state
- memset(ep, 0, sizeof(struct hw_endpoint));
-
- // Reclaim buffer space if all endpoints are closed
- bool reclaim_buffers = true;
- for ( uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++ )
- {
- if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL)
- {
- reclaim_buffers = false;
- break;
- }
- }
- if (reclaim_buffers)
- {
- next_buffer_ptr = &usb_dpram->epx_data[0];
- }
-}
-
-static void hw_endpoint_close(uint8_t ep_addr)
-{
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
- _hw_endpoint_close(ep);
+// Enable endpoint
+TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_enable(struct hw_endpoint* ep) {
+ uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->hw_data_buf);
+ *ep->endpoint_control = reg;
}
-static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
-{
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+// main processing for dcd_edpt_iso_activate
+static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
const uint8_t num = tu_edpt_number(ep_addr);
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
@@ -145,206 +106,232 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
ep->transfer_type = transfer_type;
// Every endpoint has a buffer control register in dpram
- if ( dir == TUSB_DIR_IN )
- {
+ if (dir == TUSB_DIR_IN) {
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
- }
- else
- {
+ } else {
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
}
// Clear existing buffer control state
*ep->buffer_control = 0;
- if ( num == 0 )
- {
+ if (num == 0) {
// EP0 has no endpoint control register because the buffer offsets are fixed
ep->endpoint_control = NULL;
// Buffer offset is fixed (also double buffered)
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
- }
- else
- {
+ } else {
// Set the endpoint control register (starts at EP1, hence num-1)
- if ( dir == TUSB_DIR_IN )
- {
+ if (dir == TUSB_DIR_IN) {
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
- }
- else
- {
+ } else {
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
}
+ }
+}
- // alloc a buffer and fill in endpoint control register
- _hw_endpoint_alloc(ep, transfer_type);
+// Init, allocate buffer and enable endpoint
+static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_init(ep_addr, wMaxPacketSize, transfer_type);
+ const uint8_t num = tu_edpt_number(ep_addr);
+ if (num != 0) {
+ // EP0 is already enabled
+ hw_endpoint_alloc(ep, ep->wMaxPacketSize);
+ hw_endpoint_enable(ep);
}
}
-static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
-{
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
- hw_endpoint_xfer_start(ep, buffer, total_bytes);
+static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_xfer_start(ep, buffer, total_bytes);
}
-static void __tusb_irq_path_func(hw_handle_buff_status)(void)
-{
- uint32_t remaining_buffers = usb_hw->buf_status;
- pico_trace("buf_status = 0x%08x\n", remaining_buffers);
- uint bit = 1u;
- for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
- {
- if (remaining_buffers & bit)
- {
- // clear this in advance
- usb_hw_clear->buf_status = bit;
+static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
+ uint32_t remaining_buffers = usb_hw->buf_status;
+ pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
+ uint bit = 1u;
+ for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
+ if (remaining_buffers & bit) {
+ // clear this in advance
+ usb_hw_clear->buf_status = bit;
- // IN transfer for even i, OUT transfer for odd i
- struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u));
+ // IN transfer for even i, OUT transfer for odd i
+ struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
- // Continue xfer
- bool done = hw_endpoint_xfer_continue(ep);
- if (done)
- {
- // Notify
- dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
- hw_endpoint_reset_transfer(ep);
- }
- remaining_buffers &= ~bit;
- }
- bit <<= 1u;
+ // Continue xfer
+ bool done = hw_endpoint_xfer_continue(ep);
+ if (done) {
+ // Notify
+ dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
+ hw_endpoint_reset_transfer(ep);
+ }
+ remaining_buffers &= ~bit;
}
+ bit <<= 1u;
+ }
}
-TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
-{
- // If we have finished this transfer on EP0 set pid back to 1 for next
- // setup transfer. Also clear a stall in case
- uint8_t addrs[] = {0x0, 0x80};
- for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++)
- {
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
- ep->next_pid = 1u;
+TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
+ // If we have finished this transfer on EP0 set pid back to 1 for next
+ // setup transfer. Also clear a stall in case
+ for (uint8_t dir = 0; dir < 2; dir++) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
+ if (ep->active) {
+ // Abort any pending transfer from a prior control transfer per USB specs
+ // Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
+ // Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
+ uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS);
+ if (rp2040_chip_version() >= 2) {
+ usb_hw_set->abort = abort_mask;
+ while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
+ }
+
+ _hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
+ hw_endpoint_reset_transfer(ep);
+
+ if (rp2040_chip_version() >= 2) {
+ usb_hw_clear->abort_done = abort_mask;
+ usb_hw_clear->abort = abort_mask;
+ }
}
+ ep->next_pid = 1u;
+ }
}
-static void __tusb_irq_path_func(reset_non_control_endpoints)(void)
-{
+static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
// Disable all non-control
- for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ )
- {
+ for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) {
usb_dpram->ep_ctrl[i].in = 0;
usb_dpram->ep_ctrl[i].out = 0;
}
// clear non-control hw endpoints
- tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t));
+ tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
// reclaim buffer space
next_buffer_ptr = &usb_dpram->epx_data[0];
}
-static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
-{
- uint32_t const status = usb_hw->ints;
- uint32_t handled = 0;
+static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
+ uint32_t const status = usb_hw->ints;
+ uint32_t handled = 0;
- if (status & USB_INTF_DEV_SOF_BITS)
- {
- handled |= USB_INTF_DEV_SOF_BITS;
+ if (status & USB_INTF_DEV_SOF_BITS) {
+ bool keep_sof_alive = false;
- // disable SOF interrupt if it is used for RESUME in remote wakeup
- if (!_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
+ handled |= USB_INTF_DEV_SOF_BITS;
- dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
- }
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ // Errata 15 workaround for Device Bulk-In endpoint
+ e15_last_sof = time_us_32();
- // xfer events are handled before setup req. So if a transfer completes immediately
- // before closing the EP, the events will be delivered in same order.
- if (status & USB_INTS_BUFF_STATUS_BITS)
- {
- handled |= USB_INTS_BUFF_STATUS_BITS;
- hw_handle_buff_status();
- }
+ for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
- if (status & USB_INTS_SETUP_REQ_BITS)
- {
- handled |= USB_INTS_SETUP_REQ_BITS;
- uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
+ // Active Bulk IN endpoint requires SOF
+ if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
+ keep_sof_alive = true;
- // reset pid to both 1 (data and ack)
- reset_ep0_pid();
+ hw_endpoint_lock_update(ep, 1);
+
+ // Deferred enable?
+ if (ep->pending) {
+ ep->pending = 0;
+ hw_endpoint_start_next_buffer(ep);
+ }
- // Pass setup packet to tiny usb
- dcd_event_setup_received(0, setup, true);
- usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
+ hw_endpoint_lock_update(ep, -1);
+ }
}
+#endif
-#if FORCE_VBUS_DETECT == 0
- // Since we force VBUS detect On, device will always think it is connected and
- // couldn't distinguish between disconnect and suspend
- if (status & USB_INTS_DEV_CONN_DIS_BITS)
- {
- handled |= USB_INTS_DEV_CONN_DIS_BITS;
+ // disable SOF interrupt if it is used for RESUME in remote wakeup
+ if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
- if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
- {
- // Connected: nothing to do
- }else
- {
- // Disconnected
- dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
- }
+ dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
+ }
+
+ // xfer events are handled before setup req. So if a transfer completes immediately
+ // before closing the EP, the events will be delivered in same order.
+ if (status & USB_INTS_BUFF_STATUS_BITS) {
+ handled |= USB_INTS_BUFF_STATUS_BITS;
+ hw_handle_buff_status();
+ }
+
+ if (status & USB_INTS_SETUP_REQ_BITS) {
+ handled |= USB_INTS_SETUP_REQ_BITS;
+ uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
+
+ // reset pid to both 1 (data and ack)
+ reset_ep0();
- usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
+ // Pass setup packet to tiny usb
+ dcd_event_setup_received(0, setup, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
+ }
+
+#if FORCE_VBUS_DETECT == 0
+ // Since we force VBUS detect On, device will always think it is connected and
+ // couldn't distinguish between disconnect and suspend
+ if (status & USB_INTS_DEV_CONN_DIS_BITS)
+ {
+ handled |= USB_INTS_DEV_CONN_DIS_BITS;
+
+ if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
+ {
+ // Connected: nothing to do
+ }else
+ {
+ // Disconnected
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
}
+
+ usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
+ }
#endif
- // SE0 for 2.5 us or more (will last at least 10ms)
- if (status & USB_INTS_BUS_RESET_BITS)
- {
- pico_trace("BUS RESET\n");
+ // SE0 for 2.5 us or more (will last at least 10ms)
+ if (status & USB_INTS_BUS_RESET_BITS) {
+ pico_trace("BUS RESET\r\n");
- handled |= USB_INTS_BUS_RESET_BITS;
+ handled |= USB_INTS_BUS_RESET_BITS;
- usb_hw->dev_addr_ctrl = 0;
- reset_non_control_endpoints();
- dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
- usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
+ usb_hw->dev_addr_ctrl = 0;
+ reset_non_control_endpoints();
+ dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
- // Only run enumeration walk-around if pull up is enabled
- if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix();
+ // Only run enumeration workaround if pull up is enabled
+ if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix();
#endif
- }
+ }
- /* Note from pico datasheet 4.1.2.6.4 (v1.2)
- * If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
- * the device is first connected but the bus is idle. The bus can be idle for a few ms before
- * the host begins sending start of frame packets. You will also see a suspend interrupt
- * when the device is disconnected if you do not have a VBUS detect circuit connected. This is
- * because without VBUS detection, it is impossible to tell the difference between
- * being disconnected and suspended.
- */
- if (status & USB_INTS_DEV_SUSPEND_BITS)
- {
- handled |= USB_INTS_DEV_SUSPEND_BITS;
- dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
- usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
- }
+ /* Note from pico datasheet 4.1.2.6.4 (v1.2)
+ * If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
+ * the device is first connected but the bus is idle. The bus can be idle for a few ms before
+ * the host begins sending start of frame packets. You will also see a suspend interrupt
+ * when the device is disconnected if you do not have a VBUS detect circuit connected. This is
+ * because without VBUS detection, it is impossible to tell the difference between
+ * being disconnected and suspended.
+ */
+ if (status & USB_INTS_DEV_SUSPEND_BITS) {
+ handled |= USB_INTS_DEV_SUSPEND_BITS;
+ dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
+ }
- if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS)
- {
- handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
- dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
- usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
- }
+ if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) {
+ handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
+ dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
+ usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
+ }
- if (status ^ handled)
- {
- panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
- }
+ if (status ^ handled) {
+ panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+ }
}
#define USB_INTS_ERROR_BITS ( \
@@ -358,10 +345,17 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
+// older SDK
+#ifndef PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY
+#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
+#endif
+
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
assert(rhport == 0);
+ TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
+
// Reset hardware to default state
rp2040_usb_init();
@@ -373,9 +367,9 @@ void dcd_init (uint8_t rhport)
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
// Init control endpoints
- tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t));
- hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL);
- hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL);
+ tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t));
+ hw_endpoint_open(0x0, 64, TUSB_XFER_CONTROL);
+ hw_endpoint_open(0x80, 64, TUSB_XFER_CONTROL);
// Init non-control endpoints
reset_non_control_endpoints();
@@ -388,27 +382,38 @@ void dcd_init (uint8_t rhport)
// for the global interrupt enable...
// Note: Force VBUS detect cause disconnection not detectable
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
- usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
- USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
- (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
+ usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
+ USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
+ (FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
dcd_connect(rhport);
+ return true;
}
-void dcd_int_enable(__unused uint8_t rhport)
-{
- assert(rhport == 0);
- irq_set_enabled(USBCTRL_IRQ, true);
+bool dcd_deinit(uint8_t rhport) {
+ (void) rhport;
+
+ reset_non_control_endpoints();
+ irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq);
+
+ // reset usb hardware into initial state
+ reset_block(RESETS_RESET_USBCTRL_BITS);
+ unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+
+ return true;
}
-void dcd_int_disable(__unused uint8_t rhport)
-{
- assert(rhport == 0);
- irq_set_enabled(USBCTRL_IRQ, false);
+void dcd_int_enable(__unused uint8_t rhport) {
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, true);
}
-void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
-{
+void dcd_int_disable(__unused uint8_t rhport) {
+ assert(rhport == 0);
+ irq_set_enabled(USBCTRL_IRQ, false);
+}
+
+void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
assert(rhport == 0);
// Can't set device address in hardware until status xfer has complete
@@ -416,8 +421,7 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
hw_endpoint_xfer(0x80, NULL, 0);
}
-void dcd_remote_wakeup(__unused uint8_t rhport)
-{
+void dcd_remote_wakeup(__unused uint8_t rhport) {
pico_info("dcd_remote_wakeup %d\n", rhport);
assert(rhport == 0);
@@ -428,96 +432,113 @@ void dcd_remote_wakeup(__unused uint8_t rhport)
}
// disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(__unused uint8_t rhport)
-{
+void dcd_disconnect(__unused uint8_t rhport) {
(void) rhport;
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
// connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(__unused uint8_t rhport)
-{
+void dcd_connect(__unused uint8_t rhport) {
(void) rhport;
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
_sof_enable = en;
- if (en)
- {
+ if (en) {
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
- }else
- {
+ }
+#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ else {
+ // Don't clear immediately if the SOF workaround is in use.
+ // The SOF handler will conditionally disable the interrupt.
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
}
+#endif
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
(void) rhport;
- if ( request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
- request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS )
- {
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
}
}
-bool dcd_edpt_open (__unused uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
-{
- assert(rhport == 0);
- hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
- return true;
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+ (void) rhport;
+ const uint8_t xfer_type = desc_edpt->bmAttributes.xfer;
+ TU_VERIFY(xfer_type != TUSB_XFER_ISOCHRONOUS);
+ hw_endpoint_open(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), xfer_type);
+ return true;
+}
+
+// New API: Allocate packet buffer used by ISO endpoints
+// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ hw_endpoint_init(ep_addr, largest_packet_size, TUSB_XFER_ISOCHRONOUS);
+ hw_endpoint_alloc(ep, largest_packet_size);
+ return true;
+}
+
+// New API: Configure and enable an ISO endpoint according to descriptor
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
+ (void) rhport;
+ const uint8_t ep_addr = ep_desc->bEndpointAddress;
+ // Fill in endpoint control register with buffer offset
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
+ TU_ASSERT(ep->hw_data_buf != NULL); // must be inited and buffer allocated
+ ep->wMaxPacketSize = ep_desc->wMaxPacketSize;
+
+ hw_endpoint_enable(ep);
+ return true;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
+void dcd_edpt_close_all(uint8_t rhport) {
(void) rhport;
// may need to use EP Abort
reset_non_control_endpoints();
}
-bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
- assert(rhport == 0);
- hw_endpoint_xfer(ep_addr, buffer, total_bytes);
- return true;
+bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ assert(rhport == 0);
+ hw_endpoint_xfer(ep_addr, buffer, total_bytes);
+ return true;
}
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
- if ( tu_edpt_number(ep_addr) == 0 )
- {
+ if (tu_edpt_number(ep_addr) == 0) {
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
- usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
+ usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS
+ : USB_EP_STALL_ARM_EP0_OUT_BITS;
}
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
// stall and clear current pending buffer
// may need to use EP_ABORT
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
}
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
- if (tu_edpt_number(ep_addr))
- {
- struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+ if (tu_edpt_number(ep_addr)) {
+ struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
// clear stall also reset toggle to DATA0, ready for next transfer
ep->next_pid = 0;
@@ -525,16 +546,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
}
}
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- pico_trace("dcd_edpt_close %02x\n", ep_addr);
- hw_endpoint_close(ep_addr);
-}
-
-void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport)
-{
+void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
(void) rhport;
dcd_rp2040_irq();
}
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 10237d1f9..2c0a3fd49 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -27,7 +27,7 @@
#include "tusb_option.h"
-#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB
+#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB && !CFG_TUH_MAX3421
#include "pico.h"
#include "rp2040_usb.h"
@@ -56,9 +56,6 @@ static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
static struct hw_endpoint ep_pool[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
#define epx (ep_pool[0])
-#define usb_hw_set hw_set_alias(usb_hw)
-#define usb_hw_clear hw_clear_alias(usb_hw)
-
// Flags we set by default in sie_ctrl (we add other bits on top)
enum {
SIE_CTRL_BASE = USB_SIE_CTRL_SOF_EN_BITS | USB_SIE_CTRL_KEEP_ALIVE_EN_BITS |
@@ -81,89 +78,90 @@ static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
TU_ATTR_ALWAYS_INLINE static inline uint8_t dev_speed(void)
{
- return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
+ return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
}
-static bool need_pre(uint8_t dev_addr)
+TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr)
{
- // If this device is different to the speed of the root device
- // (i.e. is a low speed device on a full speed hub) then need pre
- return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
+ // If this device is different to the speed of the root device
+ // (i.e. is a low speed device on a full speed hub) then need pre
+ return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
}
static void __tusb_irq_path_func(hw_xfer_complete)(struct hw_endpoint *ep, xfer_result_t xfer_result)
{
- // Mark transfer as done before we tell the tinyusb stack
- uint8_t dev_addr = ep->dev_addr;
- uint8_t ep_addr = ep->ep_addr;
- uint xferred_len = ep->xferred_len;
- hw_endpoint_reset_transfer(ep);
- hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true);
+ // Mark transfer as done before we tell the tinyusb stack
+ uint8_t dev_addr = ep->dev_addr;
+ uint8_t ep_addr = ep->ep_addr;
+ uint xferred_len = ep->xferred_len;
+ hw_endpoint_reset_transfer(ep);
+ hcd_event_xfer_complete(dev_addr, ep_addr, xferred_len, xfer_result, true);
}
static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_endpoint *ep)
{
- usb_hw_clear->buf_status = bit;
- // EP may have been stalled?
- assert(ep->active);
- bool done = hw_endpoint_xfer_continue(ep);
- if (done)
- {
- hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
- }
+ usb_hw_clear->buf_status = bit;
+ // EP may have been stalled?
+ assert(ep->active);
+ bool done = hw_endpoint_xfer_continue(ep);
+ if ( done )
+ {
+ hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+ }
}
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
{
- uint32_t remaining_buffers = usb_hw->buf_status;
- pico_trace("buf_status 0x%08x\n", remaining_buffers);
+ uint32_t remaining_buffers = usb_hw->buf_status;
+ pico_trace("buf_status 0x%08lx\n", remaining_buffers);
- // Check EPX first
- uint bit = 0b1;
- if (remaining_buffers & bit)
- {
- remaining_buffers &= ~bit;
- struct hw_endpoint *ep = &epx;
-
- uint32_t ep_ctrl = *ep->endpoint_control;
- if (ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS)
- {
- TU_LOG(3, "Double Buffered: ");
- }else
- {
- TU_LOG(3, "Single Buffered: ");
- }
- TU_LOG_HEX(3, ep_ctrl);
+ // Check EPX first
+ uint bit = 0b1;
+ if ( remaining_buffers & bit )
+ {
+ remaining_buffers &= ~bit;
+ struct hw_endpoint * ep = &epx;
- _handle_buff_status_bit(bit, ep);
+ uint32_t ep_ctrl = *ep->endpoint_control;
+ if ( ep_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS )
+ {
+ TU_LOG(3, "Double Buffered: ");
}
-
- // Check "interrupt" (asynchronous) endpoints for both IN and OUT
- for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++)
+ else
{
- // EPX is bit 0 & 1
- // IEP1 IN is bit 2
- // IEP1 OUT is bit 3
- // IEP2 IN is bit 4
- // IEP2 OUT is bit 5
- // IEP3 IN is bit 6
- // IEP3 OUT is bit 7
- // etc
- for(uint j = 0; j < 2; j++)
- {
- bit = 1 << (i*2+j);
- if (remaining_buffers & bit)
- {
- remaining_buffers &= ~bit;
- _handle_buff_status_bit(bit, &ep_pool[i]);
- }
- }
+ TU_LOG(3, "Single Buffered: ");
}
+ TU_LOG_HEX(3, ep_ctrl);
- if (remaining_buffers)
+ _handle_buff_status_bit(bit, ep);
+ }
+
+ // Check "interrupt" (asynchronous) endpoints for both IN and OUT
+ for ( uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++ )
+ {
+ // EPX is bit 0 & 1
+ // IEP1 IN is bit 2
+ // IEP1 OUT is bit 3
+ // IEP2 IN is bit 4
+ // IEP2 OUT is bit 5
+ // IEP3 IN is bit 6
+ // IEP3 OUT is bit 7
+ // etc
+ for ( uint j = 0; j < 2; j++ )
{
- panic("Unhandled buffer %d\n", remaining_buffers);
+ bit = 1 << (i * 2 + j);
+ if ( remaining_buffers & bit )
+ {
+ remaining_buffers &= ~bit;
+ _handle_buff_status_bit(bit, &ep_pool[i]);
+ }
}
+ }
+
+ if ( remaining_buffers )
+ {
+ panic("Unhandled buffer %d\n", remaining_buffers);
+ }
}
static void __tusb_irq_path_func(hw_trans_complete)(void)
@@ -186,198 +184,200 @@ static void __tusb_irq_path_func(hw_trans_complete)(void)
static void __tusb_irq_path_func(hcd_rp2040_irq)(void)
{
- uint32_t status = usb_hw->ints;
- uint32_t handled = 0;
-
- if (status & USB_INTS_HOST_CONN_DIS_BITS)
- {
- handled |= USB_INTS_HOST_CONN_DIS_BITS;
-
- if (dev_speed())
- {
- hcd_event_device_attach(RHPORT_NATIVE, true);
- }
- else
- {
- hcd_event_device_remove(RHPORT_NATIVE, true);
- }
+ uint32_t status = usb_hw->ints;
+ uint32_t handled = 0;
- // Clear speed change interrupt
- usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
- }
+ if ( status & USB_INTS_HOST_CONN_DIS_BITS )
+ {
+ handled |= USB_INTS_HOST_CONN_DIS_BITS;
- if (status & USB_INTS_STALL_BITS)
+ if ( dev_speed() )
{
- // We have rx'd a stall from the device
- // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS
- // AND TRANS_COMPLETE as the stall is an alternative response
- // to one of those events
- pico_trace("Stall REC\n");
- handled |= USB_INTS_STALL_BITS;
- usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
- hw_xfer_complete(&epx, XFER_RESULT_STALLED);
+ hcd_event_device_attach(RHPORT_NATIVE, true);
}
-
- if (status & USB_INTS_BUFF_STATUS_BITS)
+ else
{
- handled |= USB_INTS_BUFF_STATUS_BITS;
- TU_LOG(2, "Buffer complete\n");
- hw_handle_buff_status();
+ hcd_event_device_remove(RHPORT_NATIVE, true);
}
- if (status & USB_INTS_TRANS_COMPLETE_BITS)
- {
- handled |= USB_INTS_TRANS_COMPLETE_BITS;
- usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
- TU_LOG(2, "Transfer complete\n");
- hw_trans_complete();
- }
+ // Clear speed change interrupt
+ usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
+ }
- if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS)
- {
- handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
- usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
- }
+ if ( status & USB_INTS_STALL_BITS )
+ {
+ // We have rx'd a stall from the device
+ // NOTE THIS SHOULD HAVE PRIORITY OVER BUFF_STATUS
+ // AND TRANS_COMPLETE as the stall is an alternative response
+ // to one of those events
+ pico_trace("Stall REC\n");
+ handled |= USB_INTS_STALL_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
+ hw_xfer_complete(&epx, XFER_RESULT_STALLED);
+ }
- if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
- {
- usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
- TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n", tu_u32_low16(*epx.buffer_control), tu_u32_high16(*epx.buffer_control));
- panic("Data Seq Error \n");
- }
+ if ( status & USB_INTS_BUFF_STATUS_BITS )
+ {
+ handled |= USB_INTS_BUFF_STATUS_BITS;
+ TU_LOG(2, "Buffer complete\r\n");
+ hw_handle_buff_status();
+ }
- if (status ^ handled)
- {
- panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
- }
+ if ( status & USB_INTS_TRANS_COMPLETE_BITS )
+ {
+ handled |= USB_INTS_TRANS_COMPLETE_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
+ TU_LOG(2, "Transfer complete\r\n");
+ hw_trans_complete();
+ }
+
+ if ( status & USB_INTS_ERROR_RX_TIMEOUT_BITS )
+ {
+ handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
+ usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
+ }
+
+ if ( status & USB_INTS_ERROR_DATA_SEQ_BITS )
+ {
+ usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
+ TU_LOG(3, " Seq Error: [0] = 0x%04u [1] = 0x%04x\r\n",
+ tu_u32_low16(*epx.buffer_control),
+ tu_u32_high16(*epx.buffer_control));
+ panic("Data Seq Error \n");
+ }
+
+ if ( status ^ handled )
+ {
+ panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+ }
}
-void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport)
-{
+void __tusb_irq_path_func(hcd_int_handler)(uint8_t rhport, bool in_isr) {
(void) rhport;
+ (void) in_isr;
hcd_rp2040_irq();
}
static struct hw_endpoint *_next_free_interrupt_ep(void)
{
- struct hw_endpoint *ep = NULL;
- for (uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++)
+ struct hw_endpoint * ep = NULL;
+ for ( uint i = 1; i < TU_ARRAY_SIZE(ep_pool); i++ )
+ {
+ ep = &ep_pool[i];
+ if ( !ep->configured )
{
- ep = &ep_pool[i];
- if (!ep->configured)
- {
- // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
- ep->interrupt_num = (uint8_t) (i - 1);
- return ep;
- }
+ // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
+ ep->interrupt_num = (uint8_t) (i - 1);
+ return ep;
}
- return ep;
+ }
+ return ep;
}
static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
{
- struct hw_endpoint *ep = NULL;
+ struct hw_endpoint * ep = NULL;
- if (transfer_type != TUSB_XFER_CONTROL)
- {
- // Note: even though datasheet name these "Interrupt" endpoints. These are actually
- // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation)
- ep = _next_free_interrupt_ep();
- pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num);
- assert(ep);
- ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
- ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
- // 0 for epx (double buffered): TODO increase to 1024 for ISO
- // 2x64 for intep0
- // 3x64 for intep1
- // etc
- ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
- }
- else
- {
- ep = &epx;
- ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
- ep->endpoint_control = &usbh_dpram->epx_ctrl;
- ep->hw_data_buf = &usbh_dpram->epx_data[0];
- }
+ if ( transfer_type != TUSB_XFER_CONTROL )
+ {
+ // Note: even though datasheet name these "Interrupt" endpoints. These are actually
+ // "Asynchronous" endpoints and can be used for other type such as: Bulk (ISO need confirmation)
+ ep = _next_free_interrupt_ep();
+ pico_info("Allocate %s ep %d\n", tu_edpt_type_str(transfer_type), ep->interrupt_num);
+ assert(ep);
+ ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
+ ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
+ // 0 for epx (double buffered): TODO increase to 1024 for ISO
+ // 2x64 for intep0
+ // 3x64 for intep1
+ // etc
+ ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 2)];
+ }
+ else
+ {
+ ep = &epx;
+ ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
+ ep->endpoint_control = &usbh_dpram->epx_ctrl;
+ ep->hw_data_buf = &usbh_dpram->epx_data[0];
+ }
- return ep;
+ return ep;
}
static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
{
- // Already has data buffer, endpoint control, and buffer control allocated at this point
- assert(ep->endpoint_control);
- assert(ep->buffer_control);
- assert(ep->hw_data_buf);
+ // Already has data buffer, endpoint control, and buffer control allocated at this point
+ assert(ep->endpoint_control);
+ assert(ep->buffer_control);
+ assert(ep->hw_data_buf);
- uint8_t const num = tu_edpt_number(ep_addr);
- tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
- ep->ep_addr = ep_addr;
- ep->dev_addr = dev_addr;
+ ep->ep_addr = ep_addr;
+ ep->dev_addr = dev_addr;
- // For host, IN to host == RX, anything else rx == false
- ep->rx = (dir == TUSB_DIR_IN);
+ // For host, IN to host == RX, anything else rx == false
+ ep->rx = (dir == TUSB_DIR_IN);
- // Response to a setup packet on EP0 starts with pid of 1
- ep->next_pid = (num == 0 ? 1u : 0u);
- ep->wMaxPacketSize = wMaxPacketSize;
- ep->transfer_type = transfer_type;
+ // Response to a setup packet on EP0 starts with pid of 1
+ ep->next_pid = (num == 0 ? 1u : 0u);
+ ep->wMaxPacketSize = wMaxPacketSize;
+ ep->transfer_type = transfer_type;
- pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type);
- pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf);
- uint dpram_offset = hw_data_offset(ep->hw_data_buf);
- // Bits 0-5 should be 0
- assert(!(dpram_offset & 0b111111));
+ pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type);
+ pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf);
+ uint dpram_offset = hw_data_offset(ep->hw_data_buf);
+ // Bits 0-5 should be 0
+ assert(!(dpram_offset & 0b111111));
+
+ // Fill in endpoint control register with buffer offset
+ uint32_t ep_reg = EP_CTRL_ENABLE_BITS
+ | EP_CTRL_INTERRUPT_PER_BUFFER
+ | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
+ | dpram_offset;
+ if ( bmInterval )
+ {
+ ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
+ }
+ *ep->endpoint_control = ep_reg;
+ pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg);
+ ep->configured = true;
+
+ if ( ep != &epx )
+ {
+ // Endpoint has its own addr_endp and interrupt bits to be setup!
+ // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with:
+ // - device address
+ // - endpoint number / direction
+ // - preamble
+ uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB));
- // Fill in endpoint control register with buffer offset
- uint32_t ep_reg = EP_CTRL_ENABLE_BITS
- | EP_CTRL_INTERRUPT_PER_BUFFER
- | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
- | dpram_offset;
- if (bmInterval)
+ if ( dir == TUSB_DIR_OUT )
{
- ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
+ reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS;
}
- *ep->endpoint_control = ep_reg;
- pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
- ep->configured = true;
- if (ep != &epx)
+ if ( need_pre(dev_addr) )
{
- // Endpoint has its own addr_endp and interrupt bits to be setup!
- // This is an interrupt/async endpoint. so need to set up ADDR_ENDP register with:
- // - device address
- // - endpoint number / direction
- // - preamble
- uint32_t reg = (uint32_t) (dev_addr | (num << USB_ADDR_ENDP1_ENDPOINT_LSB));
-
- if (dir == TUSB_DIR_OUT)
- {
- reg |= USB_ADDR_ENDP1_INTEP_DIR_BITS;
- }
-
- if (need_pre(dev_addr))
- {
- reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
- }
- usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
+ reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
+ }
+ usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
- // Finally, enable interrupt that endpoint
- usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
+ // Finally, enable interrupt that endpoint
+ usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
- // If it's an interrupt endpoint we need to set up the buffer control
- // register
- }
+ // If it's an interrupt endpoint we need to set up the buffer control
+ // register
+ }
}
//--------------------------------------------------------------------+
// HCD API
//--------------------------------------------------------------------+
-bool hcd_init(uint8_t rhport)
-{
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
pico_trace("hcd_init %d\n", rhport);
assert(rhport == 0);
@@ -409,6 +409,16 @@ bool hcd_init(uint8_t rhport)
return true;
}
+bool hcd_deinit(uint8_t rhport) {
+ (void) rhport;
+
+ irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq);
+ reset_block(RESETS_RESET_USBCTRL_BITS);
+ unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+
+ return true;
+}
+
void hcd_port_reset(uint8_t rhport)
{
(void) rhport;
@@ -434,16 +444,17 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport)
{
(void) rhport;
assert(rhport == 0);
+
// TODO: Should enumval this register
- switch (dev_speed())
+ switch ( dev_speed() )
{
- case 1:
- return TUSB_SPEED_LOW;
- case 2:
- return TUSB_SPEED_FULL;
- default:
- panic("Invalid speed\n");
- return TUSB_SPEED_INVALID;
+ case 1:
+ return TUSB_SPEED_LOW;
+ case 2:
+ return TUSB_SPEED_FULL;
+ default:
+ panic("Invalid speed\n");
+ // return TUSB_SPEED_INVALID;
}
}
@@ -476,8 +487,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
uint32_t hcd_frame_number(uint8_t rhport)
{
- (void) rhport;
- return usb_hw->sof_rd;
+ (void) rhport;
+ return usb_hw->sof_rd;
}
void hcd_int_enable(uint8_t rhport)
@@ -501,117 +512,134 @@ void hcd_int_disable(uint8_t rhport)
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
- (void) rhport;
+ (void) rhport;
- pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
+ pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
- // Allocated differently based on if it's an interrupt endpoint or not
- struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
- TU_ASSERT(ep);
+ // Allocated differently based on if it's an interrupt endpoint or not
+ struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
+ TU_ASSERT(ep);
- _hw_endpoint_init(ep,
- dev_addr,
- ep_desc->bEndpointAddress,
- tu_edpt_packet_size(ep_desc),
- ep_desc->bmAttributes.xfer,
- ep_desc->bInterval);
+ _hw_endpoint_init(ep,
+ dev_addr,
+ ep_desc->bEndpointAddress,
+ tu_edpt_packet_size(ep_desc),
+ ep_desc->bmAttributes.xfer,
+ ep_desc->bInterval);
- return true;
+ return true;
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
- (void) rhport;
+ (void) rhport;
- pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
-
- uint8_t const ep_num = tu_edpt_number(ep_addr);
- tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
+ pico_trace("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
- // Get appropriate ep. Either EPX or interrupt endpoint
- struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
- TU_ASSERT(ep);
+ // Get appropriate ep. Either EPX or interrupt endpoint
+ struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
- // EP should be inactive
- assert(!ep->active);
+ TU_ASSERT(ep);
- // Control endpoint can change direction 0x00 <-> 0x80
- if ( ep_addr != ep->ep_addr )
- {
- assert(ep_num == 0);
+ // EP should be inactive
+ assert(!ep->active);
- // Direction has flipped on endpoint control so re init it but with same properties
- _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
- }
+ // Control endpoint can change direction 0x00 <-> 0x80
+ if ( ep_addr != ep->ep_addr )
+ {
+ assert(ep_num == 0);
- // If a normal transfer (non-interrupt) then initiate using
- // sie ctrl registers. Otherwise interrupt ep registers should
- // already be configured
- if (ep == &epx) {
- hw_endpoint_xfer_start(ep, buffer, buflen);
+ // Direction has flipped on endpoint control so re init it but with same properties
+ _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
+ }
- // That has set up buffer control, endpoint control etc
- // for host we have to initiate the transfer
- usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB));
+ // If a normal transfer (non-interrupt) then initiate using
+ // sie ctrl registers. Otherwise interrupt ep registers should
+ // already be configured
+ if ( ep == &epx )
+ {
+ hw_endpoint_xfer_start(ep, buffer, buflen);
- uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE |
- (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS);
- // Set pre if we are a low speed device on full speed hub
- flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
+ // That has set up buffer control, endpoint control etc
+ // for host we have to initiate the transfer
+ usb_hw->dev_addr_ctrl = (uint32_t) (dev_addr | (ep_num << USB_ADDR_ENDP_ENDPOINT_LSB));
- usb_hw->sie_ctrl = flags;
- }else
- {
- hw_endpoint_xfer_start(ep, buffer, buflen);
- }
+ uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | SIE_CTRL_BASE |
+ (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) |
+ (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
+ // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit
+ // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access".
+ // We write everything except the START_TRANS bit first, then wait some cycles.
+ usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS;
+ busy_wait_at_least_cycles(12);
+ usb_hw->sie_ctrl = flags;
+ }else
+ {
+ hw_endpoint_xfer_start(ep, buffer, buflen);
+ }
- return true;
+ return true;
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ // TODO not implemented yet
+ return false;
}
bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
{
- (void) rhport;
+ (void) rhport;
- // Copy data into setup packet buffer
- for(uint8_t i=0; i<8; i++)
- {
- usbh_dpram->setup_packet[i] = setup_packet[i];
- }
+ // Copy data into setup packet buffer
+ for ( uint8_t i = 0; i < 8; i++ )
+ {
+ usbh_dpram->setup_packet[i] = setup_packet[i];
+ }
- // Configure EP0 struct with setup info for the trans complete
- struct hw_endpoint *ep = _hw_endpoint_allocate(0);
- TU_ASSERT(ep);
+ // Configure EP0 struct with setup info for the trans complete
+ struct hw_endpoint * ep = _hw_endpoint_allocate(0);
+ TU_ASSERT(ep);
- // EPX should be inactive
- assert(!ep->active);
+ // EPX should be inactive
+ assert(!ep->active);
- // EP0 out
- _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
- assert(ep->configured);
+ // EP0 out
+ _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
+ assert(ep->configured);
- ep->remaining_len = 8;
- ep->active = true;
+ ep->remaining_len = 8;
+ ep->active = true;
- // Set device address
- usb_hw->dev_addr_ctrl = dev_addr;
+ // Set device address
+ usb_hw->dev_addr_ctrl = dev_addr;
- // Set pre if we are a low speed device on full speed hub
- uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS |
- (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
+ // Set pre if we are a low speed device on full speed hub
+ uint32_t const flags = SIE_CTRL_BASE | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS |
+ (need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- usb_hw->sie_ctrl = flags;
+ // START_TRANS bit on SIE_CTRL seems to exhibit the same behavior as the AVAILABLE bit
+ // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access".
+ // We write everything except the START_TRANS bit first, then wait some cycles.
+ usb_hw->sie_ctrl = flags & ~USB_SIE_CTRL_START_TRANS_BITS;
+ busy_wait_at_least_cycles(12);
+ usb_hw->sie_ctrl = flags;
- return true;
+ return true;
}
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
- (void) dev_addr;
- (void) ep_addr;
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
- panic("hcd_clear_stall");
- return true;
+ panic("hcd_clear_stall");
+ // return true;
}
#endif
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 25c013bd2..43f48da39 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -32,88 +32,97 @@
#include <stdlib.h>
#include "rp2040_usb.h"
-// Direction strings for debug
-const char *ep_dir_string[] = {
- "out",
- "in",
-};
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTOTYPE
+//--------------------------------------------------------------------+
+static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep);
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
- // todo add critsec as necessary to prevent issues between worker and IRQ...
- // note that this is perhaps as simple as disabling IRQs because it would make
- // sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
-}
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+ static bool e15_is_bulkin_ep(struct hw_endpoint* ep);
+ static bool e15_is_critical_frame_period(struct hw_endpoint* ep);
+#else
+ #define e15_is_bulkin_ep(x) (false)
+ #define e15_is_critical_frame_period(x) (false)
+#endif
-static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
-static void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
+// if usb hardware is in host mode
+TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
+ return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
+}
//--------------------------------------------------------------------+
-//
+// Implementation
//--------------------------------------------------------------------+
+// Provide own byte by byte memcpy as not all copies are aligned
+static void unaligned_memcpy(void *dst, const void *src, size_t n) {
+ uint8_t *dst_byte = (uint8_t*)dst;
+ const uint8_t *src_byte = (const uint8_t*)src;
+ while (n--) {
+ *dst_byte++ = *src_byte++;
+ }
+}
-void rp2040_usb_init(void)
-{
+void rp2040_usb_init(void) {
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+#ifdef __GNUC__
// Clear any previous state just in case
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#if __GNUC__ > 6
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
- memset(usb_hw, 0, sizeof(*usb_hw));
+#endif
memset(usb_dpram, 0, sizeof(*usb_dpram));
+#ifdef __GNUC__
#pragma GCC diagnostic pop
+#endif
// Mux the controller to the onboard usb phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
+
+ TU_LOG2_INT(sizeof(hw_endpoint_t));
}
-void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep)
-{
+void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) {
ep->active = false;
ep->remaining_len = 0;
ep->xferred_len = 0;
ep->user_buf = 0;
}
-void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
- uint32_t value = 0;
- if (and_mask) {
- value = *ep->buffer_control & and_mask;
- }
- if (or_mask) {
- value |= or_mask;
- if (or_mask & USB_BUF_CTRL_AVAIL) {
- if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
- panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
- }
- *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
- // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
- // Don't need delay in host mode as host is in charge
-#if !CFG_TUH_ENABLED
- __asm volatile (
- "b 1f\n"
- "1: b 1f\n"
- "1: b 1f\n"
- "1: b 1f\n"
- "1: b 1f\n"
- "1: b 1f\n"
- "1:\n"
- : : : "memory");
-#endif
- }
+void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask,
+ uint32_t or_mask) {
+ uint32_t value = 0;
+
+ if (and_mask) {
+ value = *ep->buffer_control & and_mask;
+ }
+
+ if (or_mask) {
+ value |= or_mask;
+ if (or_mask & USB_BUF_CTRL_AVAIL) {
+ if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
+ panic("ep %02X was already available", ep->ep_addr);
+ }
+ *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
+ // 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control
+ // Don't need delay in host mode as host is in charge
+ if ( !is_host_mode()) {
+ busy_wait_at_least_cycles(12);
+ }
}
- *ep->buffer_control = value;
+ }
+
+ *ep->buffer_control = value;
}
// prepare buffer, return buffer control
-static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
-{
+static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
- ep->remaining_len = (uint16_t)(ep->remaining_len - buflen);
+ ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
@@ -121,10 +130,9 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
ep->next_pid ^= 1u;
- if ( !ep->rx )
- {
+ if (!ep->rx) {
// Copy data from user buffer to hw buffer
- memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen);
+ unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
ep->user_buf += buflen;
// Mark as full
@@ -134,8 +142,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
// Is this the last buffer? Only really matters for host mode. Will trigger
// the trans complete irq but also stop it polling. We only really care about
// trans complete for setup packets being sent
- if (ep->remaining_len == 0)
- {
+ if (ep->remaining_len == 0) {
buf_ctrl |= USB_BUF_CTRL_LAST;
}
@@ -145,20 +152,22 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
}
// Prepare buffer control register value
-static void __tusb_irq_path_func(_hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
-{
+void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
uint32_t ep_ctrl = *ep->endpoint_control;
// always compute and start with buffer 0
uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL;
- // For now: skip double buffered for Device mode, OUT endpoint since
+ // For now: skip double buffered for OUT endpoint in Device mode, since
// host could send < 64 bytes and cause short packet on buffer0
- // NOTE this could happen to Host mode IN endpoint
- bool const force_single = !(usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) && !tu_edpt_dir(ep->ep_addr);
+ // NOTE: this could happen to Host mode IN endpoint
+ // Also, Host mode "interrupt" endpoint hardware is only single buffered,
+ // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
+ bool const is_host = is_host_mode();
+ bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
+ (is_host && tu_edpt_number(ep->ep_addr) != 0);
- if(ep->remaining_len && !force_single)
- {
+ if (ep->remaining_len && !force_single) {
// Use buffer 1 (double buffered) if there is still data
// TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
@@ -167,8 +176,7 @@ static void __tusb_irq_path_func(_hw_endpoint_start_next_buffer)(struct hw_endpo
// Set endpoint control double buffered bit if needed
ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
- }else
- {
+ } else {
// Single buffered since 1 is enough
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
@@ -183,59 +191,60 @@ static void __tusb_irq_path_func(_hw_endpoint_start_next_buffer)(struct hw_endpo
_hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
}
-void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
-{
- _hw_endpoint_lock_update(ep, 1);
+void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) {
+ hw_endpoint_lock_update(ep, 1);
- if ( ep->active )
- {
+ if (ep->active) {
// TODO: Is this acceptable for interrupt packets?
- TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\n", tu_edpt_number(ep->ep_addr),
- ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
-
+ TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr);
hw_endpoint_reset_transfer(ep);
}
// Fill in info now that we're kicking off the hw
ep->remaining_len = total_len;
- ep->xferred_len = 0;
- ep->active = true;
- ep->user_buf = buffer;
+ ep->xferred_len = 0;
+ ep->active = true;
+ ep->user_buf = buffer;
+
+ if (e15_is_bulkin_ep(ep)) {
+ usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
+ }
+
+ if (e15_is_critical_frame_period(ep)) {
+ ep->pending = 1;
+ } else {
+ hw_endpoint_start_next_buffer(ep);
+ }
- _hw_endpoint_start_next_buffer(ep);
- _hw_endpoint_lock_update(ep, -1);
+ hw_endpoint_lock_update(ep, -1);
}
// sync endpoint buffer and return transferred bytes
-static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
-{
+static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
- if (buf_id) buf_ctrl = buf_ctrl >> 16;
+ if (buf_id) buf_ctrl = buf_ctrl >> 16;
uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
- if ( !ep->rx )
- {
+ if (!ep->rx) {
// We are continuing a transfer here. If we are TX, we have successfully
// sent some data can increase the length we have sent
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
- ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes);
- }else
- {
+ ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
+ } else {
// If we have received some data, so can increase the length
// we have received AFTER we have copied it to the user buffer at the appropriate offset
assert(buf_ctrl & USB_BUF_CTRL_FULL);
- memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes);
- ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes);
+ unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
+ ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
ep->user_buf += xferred_bytes;
}
// Short packet
- if (xferred_bytes < ep->wMaxPacketSize)
- {
- pico_trace(" Short packet on buffer %d with %u bytes\n", buf_id, xferred_bytes);
+ if (xferred_bytes < ep->wMaxPacketSize) {
+ pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes);
// Reduce total length as this is last packet
ep->remaining_len = 0;
}
@@ -243,8 +252,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uin
return xferred_bytes;
}
-static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep)
-{
+static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) {
// Update hw endpoint struct with info from hardware
// after a buff status interrupt
@@ -255,14 +263,11 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
// sync buffer 1 if double buffered
- if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS )
- {
- if (buf0_bytes == ep->wMaxPacketSize)
- {
+ if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
+ if (buf0_bytes == ep->wMaxPacketSize) {
// sync buffer 1 if not short packet
sync_ep_buffer(ep, 1);
- }else
- {
+ } else {
// short packet on buffer 0
// TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
// At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from
@@ -294,13 +299,12 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
}
// Returns true if transfer is complete
-bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
-{
- _hw_endpoint_lock_update(ep, 1);
+bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
+ hw_endpoint_lock_update(ep, 1);
+
// Part way through a transfer
- if (!ep->active)
- {
- panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ if (!ep->active) {
+ panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
}
// Update EP struct from hardware state
@@ -308,22 +312,71 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
// Now we have synced our state with the hardware. Is there more data to transfer?
// If we are done then notify tinyusb
- if (ep->remaining_len == 0)
- {
- pico_trace("Completed transfer of %d bytes on ep %d %s\n",
- ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
+ if (ep->remaining_len == 0) {
+ pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr);
// Notify caller we are done so it can notify the tinyusb stack
- _hw_endpoint_lock_update(ep, -1);
+ hw_endpoint_lock_update(ep, -1);
return true;
- }
- else
- {
- _hw_endpoint_start_next_buffer(ep);
+ } else {
+ if (e15_is_critical_frame_period(ep)) {
+ ep->pending = 1;
+ } else {
+ hw_endpoint_start_next_buffer(ep);
+ }
}
- _hw_endpoint_lock_update(ep, -1);
+ hw_endpoint_lock_update(ep, -1);
// More work to do
return false;
}
+//--------------------------------------------------------------------+
+// Errata 15
+//--------------------------------------------------------------------+
+
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+
+/* Don't mark IN buffers as available during the last 200us of a full-speed
+ frame. This avoids a situation seen with the USB2.0 hub on a Raspberry
+ Pi 4 where a late IN token before the next full-speed SOF can cause port
+ babble and a corrupt ACK packet. The nature of the data corruption has a
+ chance to cause device lockup.
+
+ Use the next SOF to mark delayed buffers as available. This reduces
+ available Bulk IN bandwidth by approximately 20%, and requires that the
+ SOF interrupt is enabled while these transfers are ongoing.
+
+ Inherit the top-level enable from the corresponding Pico-SDK flag.
+ Applications that will not use the device in a situation where it could
+ be plugged into a Pi 4 or Pi 400 (for example, when directly connected
+ to a commodity hub or other host) can turn off the flag in the SDK.
+*/
+
+volatile uint32_t e15_last_sof = 0;
+
+// check if Errata 15 is needed for this endpoint i.e device bulk-in
+static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) {
+ return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN &&
+ ep->transfer_type == TUSB_XFER_BULK);
+}
+
+// check if we need to apply Errata 15 workaround : i.e
+// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame
+static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) {
+ TU_VERIFY(e15_is_bulkin_ep(ep));
+
+ /* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point.
+ * The device state machine cannot recover from receiving an incorrect PID
+ * when it is expecting an ACK.
+ */
+ uint32_t delta = time_us_32() - e15_last_sof;
+ if (delta < 800 || delta > 998) {
+ return false;
+ }
+ TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(),
+ e15_last_sof);
+ return true;
+}
+
+#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
#endif
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h
index c72dae64c..d4d29a816 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.h
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h
@@ -11,11 +11,21 @@
#include "hardware/structs/usb.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
+#include "hardware/timer.h"
#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
#endif
+#if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
+#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX
+#endif
+
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+#undef PICO_RP2040_USB_FAST_IRQ
+#define PICO_RP2040_USB_FAST_IRQ 1
+#endif
+
#ifndef PICO_RP2040_USB_FAST_IRQ
#define PICO_RP2040_USB_FAST_IRQ 0
#endif
@@ -26,6 +36,9 @@
#define __tusb_irq_path_func(x) x
#endif
+#define usb_hw_set ((usb_hw_t *) hw_set_alias_untyped(usb_hw))
+#define usb_hw_clear ((usb_hw_t *) hw_clear_alias_untyped(usb_hw))
+
#define pico_info(...) TU_LOG(2, __VA_ARGS__)
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)
@@ -34,11 +47,11 @@ typedef struct hw_endpoint
{
// Is this a valid struct
bool configured;
-
+
// Transfer direction (i.e. IN is rx for host but tx for device)
// allows us to common up transfer functions
bool rx;
-
+
uint8_t ep_addr;
uint8_t next_pid;
@@ -51,20 +64,25 @@ typedef struct hw_endpoint
// Buffer pointer in usb dpram
uint8_t *hw_data_buf;
+ // User buffer in main memory
+ uint8_t *user_buf;
+
// Current transfer information
- bool active;
uint16_t remaining_len;
uint16_t xferred_len;
- // User buffer in main memory
- uint8_t *user_buf;
-
// Data needed from EP descriptor
uint16_t wMaxPacketSize;
+ // Endpoint is in use
+ bool active;
+
// Interrupt, bulk, etc
uint8_t transfer_type;
-
+
+ // Transfer scheduled but not active
+ uint8_t pending;
+
#if CFG_TUH_ENABLED
// Only needed for host
uint8_t dev_addr;
@@ -72,36 +90,52 @@ typedef struct hw_endpoint
// If interrupt endpoint
uint8_t interrupt_num;
#endif
+
} hw_endpoint_t;
+#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
+extern volatile uint32_t e15_last_sof;
+#endif
+
void rp2040_usb_init(void);
void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
+void hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
+
+TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
+ // todo add critsec as necessary to prevent issues between worker and IRQ...
+ // note that this is perhaps as simple as disabling IRQs because it would make
+ // sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
+}
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
-TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) {
- return *ep->buffer_control;
+TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep)
+{
+ return *ep->buffer_control;
}
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) {
- return _hw_endpoint_buffer_control_update32(ep, 0, value);
+TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value)
+{
+ _hw_endpoint_buffer_control_update32(ep, 0, value);
}
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) {
- return _hw_endpoint_buffer_control_update32(ep, ~value, value);
+TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value)
+{
+ _hw_endpoint_buffer_control_update32(ep, ~value, value);
}
-TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) {
- return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
+TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value)
+{
+ _hw_endpoint_buffer_control_update32(ep, ~value, 0);
}
-static inline uintptr_t hw_data_offset(uint8_t *buf)
+static inline uintptr_t hw_data_offset (uint8_t *buf)
{
- // Remove usb base from buffer pointer
- return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
+ // Remove usb base from buffer pointer
+ return (uintptr_t) buf ^ (uintptr_t) usb_dpram;
}
extern const char *ep_dir_string[];
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
new file mode 100644
index 000000000..ecd28973c
--- /dev/null
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -0,0 +1,1032 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ * Portions copyrighted (c) 2021 Roland Winistoerfer
+ *
+ * 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 && defined(TUP_USBIP_RUSB2)
+
+#include "device/dcd.h"
+#include "rusb2_type.h"
+
+#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
+ #include "rusb2_rx.h"
+#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
+ #include "rusb2_ra.h"
+ #if defined(RENESAS_CORTEX_M23)
+ #define D0FIFO CFIFO
+ #define D0FIFOSEL CFIFOSEL
+ #define D0FIFOSEL_b CFIFOSEL_b
+ #define D1FIFOSEL CFIFOSEL
+ #define D1FIFOSEL_b CFIFOSEL_b
+ #define D0FIFOCTR CFIFOCTR
+ #define D0FIFOCTR_b CFIFOCTR_b
+ #endif
+
+#else
+ #error "Unsupported MCU"
+#endif
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM
+//--------------------------------------------------------------------+
+enum {
+ PIPE_COUNT = 10,
+};
+
+typedef struct {
+ void *buf; /* the start address of a transfer data buffer */
+ uint16_t length; /* the number of bytes in the buffer */
+ uint16_t remaining; /* the number of bytes remaining in the buffer */
+
+ uint8_t ep; /* an assigned endpoint address */
+ uint8_t ff; /* `buf` is TU_FUFO or POD */
+} pipe_state_t;
+
+typedef struct
+{
+ pipe_state_t pipe[PIPE_COUNT];
+ uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
+ // Track whether sof has been manually enabled
+ bool sof_enabled;
+} dcd_data_t;
+
+static dcd_data_t _dcd;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+
+// Transfer conditions specifiable for each pipe for most MCUs
+// - Pipe 0: Control transfer with 64-byte single buffer
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+ #if defined(BSP_MCU_GROUP_RA2A1)
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 0, 0 }, // Isochronous not supported
+ { 4, 5 }, // Bulk
+ { 6, 7 }, // Interrupt
+ };
+ #else
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 1, 2 }, // Isochronous
+ { 1, 5 }, // Bulk
+ { 6, 9 }, // Interrupt
+ };
+ #endif
+
+ // find backward since only pipe 1, 2 support ISO
+ const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+ const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
+
+ for (int i = idx_last; i >= idx_first; i--) {
+ if (0 == _dcd.pipe[i].ep) return i;
+ }
+
+ return 0;
+}
+
+static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) {
+ if (num) {
+ return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]);
+ } else {
+ return (volatile uint16_t*)&(rusb->DCPCTR);
+ }
+}
+
+static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) {
+ volatile reg_pipetre_t* tre = NULL;
+ if ((1 <= num) && (num <= 5)) {
+ tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E);
+ }
+ return tre;
+}
+
+static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
+ rusb2_reg_t *rusb = RUSB2_REG(rhport);
+ const unsigned epn = tu_edpt_number(ep_addr);
+
+ if (epn) {
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+ return get_pipectr(rusb, num);
+ } else {
+ return get_pipectr(rusb, 0);
+ }
+}
+
+static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) {
+ return rusb->DCPMAXP_b.MXPS;
+}
+
+static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) {
+ rusb->PIPESEL = num;
+ return rusb->PIPEMAXP;
+}
+
+static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) {
+ while ( rusb->D0FIFOSEL_b.CURPIPE != num ) {}
+ while ( !rusb->D0FIFOCTR_b.FRDY ) {}
+}
+
+//--------------------------------------------------------------------+
+// Pipe FIFO
+//--------------------------------------------------------------------+
+
+// Write data buffer --> hw fifo
+static void pipe_write_packet(rusb2_reg_t * rusb, void *buf, volatile void *fifo, unsigned len)
+{
+ (void) rusb;
+
+ volatile uint16_t *ff16;
+ volatile uint8_t *ff8;
+
+ // Highspeed FIFO is 32-bit
+ if ( rusb2_is_highspeed_reg(rusb) ) {
+ // TODO 32-bit access for better performance
+ ff16 = (volatile uint16_t*) ((uintptr_t) fifo+2);
+ ff8 = (volatile uint8_t *) ((uintptr_t) fifo+3);
+ }else {
+ ff16 = (volatile uint16_t*) fifo;
+ ff8 = ((volatile uint8_t*) fifo);
+ }
+
+ uint8_t const* buf8 = (uint8_t const*) buf;
+
+ while (len >= 2) {
+ *ff16 = tu_unaligned_read16(buf8);
+ buf8 += 2;
+ len -= 2;
+ }
+
+ if (len > 0) {
+ *ff8 = *buf8;
+ ++buf8;
+ }
+}
+
+// Read data buffer <-- hw fifo
+static void pipe_read_packet(rusb2_reg_t * rusb, void *buf, volatile void *fifo, unsigned len)
+{
+ (void) rusb;
+
+ // TODO 16/32-bit access for better performance
+
+ uint8_t *p = (uint8_t*)buf;
+ volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
+ while (len--) *p++ = *reg;
+}
+
+// Write data sw fifo --> hw fifo
+static void pipe_write_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void *fifo, uint16_t total_len) {
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_read_info(f, &info);
+
+ uint16_t count = tu_min16(total_len, info.len_lin);
+ pipe_write_packet(rusb, info.ptr_lin, fifo, count);
+
+ uint16_t rem = total_len - count;
+ if (rem) {
+ rem = tu_min16(rem, info.len_wrap);
+ pipe_write_packet(rusb, info.ptr_wrap, fifo, rem);
+ count += rem;
+ }
+
+ tu_fifo_advance_read_pointer(f, count);
+}
+
+// Read data sw fifo <-- hw fifo
+static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void *fifo, uint16_t total_len) {
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_write_info(f, &info);
+
+ uint16_t count = tu_min16(total_len, info.len_lin);
+ pipe_read_packet(rusb, info.ptr_lin, fifo, count);
+
+ uint16_t rem = total_len - count;
+ if (rem) {
+ rem = tu_min16(rem, info.len_wrap);
+ pipe_read_packet(rusb, info.ptr_wrap, fifo, rem);
+ count += rem;
+ }
+
+ tu_fifo_advance_write_pointer(f, count);
+}
+
+//--------------------------------------------------------------------+
+// Pipe Transfer
+//--------------------------------------------------------------------+
+
+static bool pipe0_xfer_in(rusb2_reg_t* rusb)
+{
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ const unsigned rem = pipe->remaining;
+
+ if (!rem) {
+ pipe->buf = NULL;
+ return true;
+ }
+
+ const uint16_t mps = edpt0_max_packet_size(rusb);
+ const uint16_t len = tu_min16(mps, rem);
+ void *buf = pipe->buf;
+
+ if (len) {
+ if (pipe->ff) {
+ pipe_write_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->CFIFO, len);
+ } else {
+ pipe_write_packet(rusb, buf, (volatile void*)&rusb->CFIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ }
+
+ if (len < mps) {
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BVAL_Msk;
+ }
+
+ pipe->remaining = rem - len;
+ return false;
+}
+
+static bool pipe0_xfer_out(rusb2_reg_t* rusb)
+{
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ const unsigned rem = pipe->remaining;
+
+ const uint16_t mps = edpt0_max_packet_size(rusb);
+ const uint16_t vld = rusb->CFIFOCTR_b.DTLN;
+ const uint16_t len = tu_min16(tu_min16(rem, mps), vld);
+ void *buf = pipe->buf;
+
+ if (len) {
+ if (pipe->ff) {
+ pipe_read_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->CFIFO, len);
+ } else {
+ pipe_read_packet(rusb, buf, (volatile void*)&rusb->CFIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ }
+
+ if (len < mps) {
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BCLR_Msk;
+ }
+
+ pipe->remaining = rem - len;
+ if ((len < mps) || (rem == len)) {
+ pipe->buf = NULL;
+ return true;
+ }
+
+ return false;
+}
+
+static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
+{
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ const unsigned rem = pipe->remaining;
+
+ if (!rem) {
+ pipe->buf = NULL;
+ return true;
+ }
+
+ rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_16BIT | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
+ const uint16_t mps = edpt_max_packet_size(rusb, num);
+ pipe_wait_for_ready(rusb, num);
+ const uint16_t len = tu_min16(rem, mps);
+ void *buf = pipe->buf;
+
+ if (len) {
+ if (pipe->ff) {
+ pipe_write_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->D0FIFO, len);
+ } else {
+ pipe_write_packet(rusb, buf, (volatile void*)&rusb->D0FIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ }
+
+ if (len < mps) {
+ rusb->D0FIFOCTR = RUSB2_CFIFOCTR_BVAL_Msk;
+ }
+
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) {} /* if CURPIPE bits changes, check written value */
+
+ pipe->remaining = rem - len;
+
+ return false;
+}
+
+static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
+{
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ const uint16_t rem = pipe->remaining;
+
+ rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_8BIT;
+ const uint16_t mps = edpt_max_packet_size(rusb, num);
+ pipe_wait_for_ready(rusb, num);
+
+ const uint16_t vld = rusb->D0FIFOCTR_b.DTLN;
+ const uint16_t len = tu_min16(tu_min16(rem, mps), vld);
+ void *buf = pipe->buf;
+
+ if (len) {
+ if (pipe->ff) {
+ pipe_read_packet_ff(rusb, (tu_fifo_t*)buf, (volatile void*)&rusb->D0FIFO, len);
+ } else {
+ pipe_read_packet(rusb, buf, (volatile void*)&rusb->D0FIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ }
+
+ if (len < mps) {
+ rusb->D0FIFOCTR = RUSB2_CFIFOCTR_BCLR_Msk;
+ }
+
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) {} /* if CURPIPE bits changes, check written value */
+
+ pipe->remaining = rem - len;
+ if ((len < mps) || (rem == len)) {
+ pipe->buf = NULL;
+ return NULL != buf;
+ }
+
+ return false;
+}
+
+static void process_setup_packet(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ if (0 == (rusb->INTSTS0 & RUSB2_INTSTS0_VALID_Msk)) return;
+
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BCLR_Msk;
+ uint16_t setup_packet[4] = {
+ tu_htole16(rusb->USBREQ),
+ tu_htole16(rusb->USBVAL),
+ tu_htole16(rusb->USBINDX),
+ tu_htole16(rusb->USBLENG)
+ };
+
+ rusb->INTSTS0 = ~((uint16_t) RUSB2_INTSTS0_VALID_Msk);
+ dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet[0], true);
+}
+
+static void process_status_completion(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ uint8_t ep_addr;
+ /* Check the data stage direction */
+ if (rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) {
+ /* IN transfer. */
+ ep_addr = tu_edpt_addr(0, TUSB_DIR_IN);
+ } else {
+ /* OUT transfer. */
+ ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
+ }
+
+ dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
+}
+
+static bool process_pipe0_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
+{
+ /* configure fifo direction and access unit settings */
+ if ( ep_addr ) {
+ /* IN, 2 bytes */
+ rusb->CFIFOSEL = RUSB2_CFIFOSEL_ISEL_WRITE | RUSB2_FIFOSEL_MBW_16BIT |
+ (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
+ while ( !(rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) ) {}
+ } else {
+ /* OUT, a byte */
+ rusb->CFIFOSEL = RUSB2_FIFOSEL_MBW_8BIT;
+ while ( rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE ) {}
+ }
+
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ pipe->ff = buffer_type;
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
+
+ if ( total_bytes ) {
+ pipe->buf = buffer;
+ if ( ep_addr ) {
+ /* IN */
+ TU_ASSERT(rusb->DCPCTR_b.BSTS && (rusb->USBREQ & 0x80));
+ pipe0_xfer_in(rusb);
+ }
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_BUF;
+ } else {
+ /* ZLP */
+ pipe->buf = NULL;
+ rusb->DCPCTR = RUSB2_DCPCTR_CCPL_Msk | RUSB2_PIPE_CTR_PID_BUF;
+ }
+
+ return true;
+}
+
+static bool process_pipe_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+
+ TU_ASSERT(num);
+
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ pipe->ff = buffer_type;
+ pipe->buf = buffer;
+ pipe->length = total_bytes;
+ pipe->remaining = total_bytes;
+
+ if (dir) {
+ /* IN */
+ if (total_bytes) {
+ pipe_xfer_in(rusb, num);
+ } else {
+ /* ZLP */
+ rusb->D0FIFOSEL = num;
+ pipe_wait_for_ready(rusb, num);
+ rusb->D0FIFOCTR = RUSB2_CFIFOCTR_BVAL_Msk;
+ rusb->D0FIFOSEL = 0;
+ /* if CURPIPE bits changes, check written value */
+ while (rusb->D0FIFOSEL_b.CURPIPE) {}
+ }
+ } else {
+ // OUT
+ volatile reg_pipetre_t *pt = get_pipetre(rusb, num);
+
+ if (pt) {
+ const uint16_t mps = edpt_max_packet_size(rusb, num);
+ volatile uint16_t *ctr = get_pipectr(rusb, num);
+
+ if (*ctr & 0x3) *ctr = RUSB2_PIPE_CTR_PID_NAK;
+
+ pt->TRE = TU_BIT(8);
+ pt->TRN = (total_bytes + mps - 1) / mps;
+ pt->TRENB = 1;
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+ }
+
+ // TU_LOG2("X %x %d %d\r\n", ep_addr, total_bytes, buffer_type);
+ return true;
+}
+
+static bool process_edpt_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ if (0 == epn) {
+ return process_pipe0_xfer(rusb, buffer_type, ep_addr, buffer, total_bytes);
+ } else {
+ return process_pipe_xfer(rusb, buffer_type, ep_addr, buffer, total_bytes);
+ }
+}
+
+static void process_pipe0_bemp(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ bool completed = pipe0_xfer_in(rusb);
+ if (completed) {
+ pipe_state_t *pipe = &_dcd.pipe[0];
+ dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
+ pipe->length, XFER_RESULT_SUCCESS, true);
+ }
+}
+
+static void process_pipe_brdy(uint8_t rhport, unsigned num)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ pipe_state_t *pipe = &_dcd.pipe[num];
+ const unsigned dir = tu_edpt_dir(pipe->ep);
+ bool completed;
+
+ if (dir) {
+ /* IN */
+ completed = pipe_xfer_in(rusb, num);
+ } else {
+ // OUT
+ if (num) {
+ completed = pipe_xfer_out(rusb, num);
+ } else {
+ completed = pipe0_xfer_out(rusb);
+ }
+ }
+ if (completed) {
+ dcd_event_xfer_complete(rhport, pipe->ep,
+ pipe->length - pipe->remaining,
+ XFER_RESULT_SUCCESS, true);
+ // TU_LOG1("C %d %d\r\n", num, pipe->length - pipe->remaining);
+ }
+}
+
+static void process_bus_reset(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ rusb->BEMPENB = 1;
+ rusb->BRDYENB = 1;
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BCLR_Msk;
+
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) {} /* if CURPIPE bits changes, check written value */
+
+ rusb->D1FIFOSEL = 0;
+ while (rusb->D1FIFOSEL_b.CURPIPE) {} /* if CURPIPE bits changes, check written value */
+
+ volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_CTR[0]));
+ volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_TR[0].E));
+
+ for (int i = 1; i <= 5; ++i) {
+ rusb->PIPESEL = i;
+ rusb->PIPECFG = 0;
+ *ctr = RUSB2_PIPE_CTR_ACLRM_Msk;
+ *ctr = 0;
+ ++ctr;
+ *tre = TU_BIT(8);
+ tre += 2;
+ }
+
+ for (int i = 6; i <= 9; ++i) {
+ rusb->PIPESEL = i;
+ rusb->PIPECFG = 0;
+ *ctr = RUSB2_PIPE_CTR_ACLRM_Msk;
+ *ctr = 0;
+ ++ctr;
+ }
+ tu_varclr(&_dcd);
+
+ TU_LOG3("Bus reset, RHST = %u\r\n", rusb->DVSTCTR0_b.RHST);
+ tusb_speed_t speed;
+ switch(rusb->DVSTCTR0 & RUSB2_DVSTCTR0_RHST_Msk) {
+ case RUSB2_DVSTCTR0_RHST_LS:
+ speed = TUSB_SPEED_LOW;
+ break;
+
+ case RUSB2_DVSTCTR0_RHST_FS:
+ speed = TUSB_SPEED_FULL;
+ break;
+
+ case RUSB2_DVSTCTR0_RHST_HS:
+ speed = TUSB_SPEED_HIGH;
+ break;
+
+ default:
+ TU_ASSERT(false, );
+ }
+
+ dcd_event_bus_reset(rhport, speed, true);
+}
+
+static void process_set_address(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ const uint16_t addr = rusb->USBADDR_b.USBADDR;
+ if (!addr) return;
+
+ const tusb_control_request_t setup_packet = {
+#if defined(__CCRX__)
+ .bmRequestType = { 0 }, /* Note: CCRX needs the braces over this struct member */
+#else
+ .bmRequestType = 0,
+#endif
+ .bRequest = TUSB_REQ_SET_ADDRESS,
+ .wValue = addr,
+ .wIndex = 0,
+ .wLength = 0,
+ };
+
+ dcd_event_setup_received(rhport, (const uint8_t *) &setup_packet, true);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+
+#if 0 // previously present in the rx driver before generalization
+static uint32_t disable_interrupt(void)
+{
+ uint32_t pswi;
+#if defined(__CCRX__)
+ pswi = get_psw() & 0x010000;
+ clrpsw_i();
+#else
+ pswi = __builtin_rx_mvfc(0) & 0x010000;
+ __builtin_rx_clrpsw('I');
+#endif
+ return pswi;
+}
+
+static void enable_interrupt(uint32_t pswi)
+{
+#if defined(__CCRX__)
+ set_psw(get_psw() | pswi);
+#else
+ __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
+#endif
+}
+#endif
+
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ rusb2_module_start(rhport, true);
+
+ // We disable SOF for now until needed later on.
+ // Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval)
+ _dcd.sof_enabled = false;
+
+#ifdef RUSB2_SUPPORT_HIGHSPEED
+ if ( rusb2_is_highspeed_rhport(rhport) ) {
+ rusb->SYSCFG_b.HSE = 1;
+
+ // leave CLKSEL as default (0x11) 24Mhz
+
+ // Power and reset UTMI Phy
+ uint16_t physet = (rusb->PHYSET | RUSB2_PHYSET_PLLRESET_Msk) & ~RUSB2_PHYSET_DIRPD_Msk;
+ rusb->PHYSET = physet;
+ R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MILLISECONDS);
+ rusb->PHYSET_b.PLLRESET = 0;
+
+ // set UTMI to operating mode and wait for PLL lock confirmation
+ rusb->LPSTS_b.SUSPENDM = 1;
+ while (!rusb->PLLSTA_b.PLLLOCK) {}
+
+ rusb->SYSCFG_b.DRPD = 0;
+ rusb->SYSCFG_b.USBE = 1;
+
+ // Set CPU bus wait time (fine tunne later)
+ // rusb2->BUSWAIT |= 0x0F00U;
+
+ rusb->PHYSET_b.REPSEL = 1;
+ } else
+#endif
+ {
+ rusb->SYSCFG_b.SCKE = 1;
+ while (!rusb->SYSCFG_b.SCKE) {}
+ rusb->SYSCFG_b.DRPD = 0;
+ rusb->SYSCFG_b.DCFM = 0;
+ rusb->SYSCFG_b.USBE = 1;
+
+ // MCU specific PHY init
+ rusb2_phy_init();
+
+ rusb->PHYSLEW = 0x5;
+ rusb->DPUSR0R_FS_b.FIXPHY0 = 0u; /* USB_BASE Transceiver Output fixed */
+ }
+
+ /* Setup default control pipe */
+ rusb->DCPMAXP_b.MXPS = 64;
+
+ rusb->INTSTS0 = 0;
+ rusb->INTENB0 = RUSB2_INTSTS0_VBINT_Msk | RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_BEMP_Msk |
+ RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (_dcd.sof_enabled ? RUSB2_INTSTS0_SOFR_Msk : 0) |
+ RUSB2_INTSTS0_RESM_Msk;
+ rusb->BEMPENB = 1;
+ rusb->BRDYENB = 1;
+
+ // If VBUS (detect) pin is not used, application need to call tud_connect() manually after tud_init()
+ if (rusb->INTSTS0_b.VBSTS) {
+ dcd_connect(rhport);
+ }
+
+ return true;
+}
+
+void dcd_int_enable(uint8_t rhport) {
+ rusb2_int_enable(rhport);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ rusb2_int_disable(rhport);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ (void) dev_addr;
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ rusb->DVSTCTR0_b.WKUP = 1;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ if ( rusb2_is_highspeed_rhport(rhport)) {
+ rusb->SYSCFG_b.CNEN = 1;
+ }
+ rusb->SYSCFG_b.DPRPU = 1;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ rusb->SYSCFG_b.DPRPU = 0;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ _dcd.sof_enabled = en;
+ rusb->INTENB0_b.SOFE = en ? 1: 0;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+ (void)rhport;
+
+ rusb2_reg_t * rusb = RUSB2_REG(rhport);
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned xfer = ep_desc->bmAttributes.xfer;
+
+ const unsigned mps = tu_edpt_packet_size(ep_desc);
+
+ if (xfer == TUSB_XFER_ISOCHRONOUS) {
+ // Fullspeed ISO is limit to 256 bytes
+ if ( !rusb2_is_highspeed_rhport(rhport) && mps > 256) {
+ return false;
+ }
+ }
+
+ const unsigned num = find_pipe(xfer);
+ TU_ASSERT(num);
+
+ _dcd.pipe[num].ep = ep_addr;
+ _dcd.ep[dir][epn] = num;
+
+ /* setup pipe */
+ dcd_int_disable(rhport);
+
+ if ( rusb2_is_highspeed_rhport(rhport) ) {
+ // FIXME shouldn't be after pipe selection and config, also the BUFNMB should be changed
+ // depending on the allocation scheme
+ rusb->PIPEBUF = 0x7C08;
+ }
+
+ rusb->PIPESEL = num;
+ rusb->PIPEMAXP = mps;
+ volatile uint16_t *ctr = get_pipectr(rusb, num);
+ *ctr = RUSB2_PIPE_CTR_ACLRM_Msk | RUSB2_PIPE_CTR_SQCLR_Msk;
+ *ctr = 0;
+ unsigned cfg = (dir << 4) | epn;
+
+ if (xfer == TUSB_XFER_BULK) {
+ cfg |= (RUSB2_PIPECFG_TYPE_BULK | RUSB2_PIPECFG_SHTNAK_Msk | RUSB2_PIPECFG_DBLB_Msk);
+ } else if (xfer == TUSB_XFER_INTERRUPT) {
+ cfg |= RUSB2_PIPECFG_TYPE_INT;
+ } else {
+ cfg |= (RUSB2_PIPECFG_TYPE_ISO | RUSB2_PIPECFG_DBLB_Msk);
+ }
+
+ rusb->PIPECFG = cfg;
+ rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
+ rusb->BRDYENB |= TU_BIT(num);
+
+ if (dir || (xfer != TUSB_XFER_BULK)) {
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+
+ // TU_LOG1("O %d %x %x\r\n", rusb->PIPESEL, rusb->PIPECFG, rusb->PIPEMAXP);
+ dcd_int_enable(rhport);
+
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport)
+{
+ unsigned i = TU_ARRAY_SIZE(_dcd.pipe);
+ dcd_int_disable(rhport);
+ while (--i) { /* Close all pipes except 0 */
+ const unsigned ep_addr = _dcd.pipe[i].ep;
+ if (!ep_addr) continue;
+ dcd_edpt_close(rhport, ep_addr);
+ }
+ dcd_int_enable(rhport);
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+ rusb2_reg_t * rusb = RUSB2_REG(rhport);
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir = tu_edpt_dir(ep_addr);
+ const unsigned num = _dcd.ep[dir][epn];
+
+ rusb->BRDYENB &= ~TU_BIT(num);
+ volatile uint16_t *ctr = get_pipectr(rusb, num);
+ *ctr = 0;
+ rusb->PIPESEL = num;
+ rusb->PIPECFG = 0;
+ _dcd.pipe[num].ep = 0;
+ _dcd.ep[dir][epn] = 0;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ dcd_int_disable(rhport);
+ bool r = process_edpt_xfer(rusb, 0, ep_addr, buffer, total_bytes);
+ dcd_int_enable(rhport);
+
+ return r;
+}
+
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+{
+ // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
+ TU_ASSERT(ff->item_size == 1);
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ dcd_int_disable(rhport);
+ bool r = process_edpt_xfer(rusb, 1, ep_addr, ff, total_bytes);
+ dcd_int_enable(rhport);
+
+ return r;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
+ if (!ctr) return;
+ dcd_int_disable(rhport);
+ const uint32_t pid = *ctr & 0x3;
+ *ctr = pid | RUSB2_PIPE_CTR_PID_STALL;
+ *ctr = RUSB2_PIPE_CTR_PID_STALL;
+ dcd_int_enable(rhport);
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+ rusb2_reg_t * rusb = RUSB2_REG(rhport);
+ volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
+ if (!ctr) return;
+
+ dcd_int_disable(rhport);
+ *ctr = RUSB2_PIPE_CTR_SQCLR_Msk;
+
+ if (tu_edpt_dir(ep_addr)) { /* IN */
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ } else {
+ const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)];
+ rusb->PIPESEL = num;
+ if (rusb->PIPECFG_b.TYPE != 1) {
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+ }
+ dcd_int_enable(rhport);
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+ unsigned int count = 0;
+ while ((value & 1) == 0) {
+ value >>= 1;
+ count++;
+ }
+ return count;
+}
+#endif
+
+void dcd_int_handler(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ uint16_t is0 = rusb->INTSTS0;
+
+ /* clear active bits except VALID (don't write 0 to already cleared bits according to the HW manual) */
+ rusb->INTSTS0 = ~((RUSB2_INTSTS0_CTRT_Msk | RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_SOFR_Msk |
+ RUSB2_INTSTS0_RESM_Msk | RUSB2_INTSTS0_VBINT_Msk) & is0) | RUSB2_INTSTS0_VALID_Msk;
+
+ // VBUS changes
+ if ( is0 & RUSB2_INTSTS0_VBINT_Msk ) {
+ if ( rusb->INTSTS0_b.VBSTS ) {
+ dcd_connect(rhport);
+ } else {
+ dcd_disconnect(rhport);
+ }
+ }
+
+ // Resumed
+ if ( is0 & RUSB2_INTSTS0_RESM_Msk ) {
+ dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 0;
+ }
+ }
+
+ // SOF received
+ if ( (is0 & RUSB2_INTSTS0_SOFR_Msk) && rusb->INTENB0_b.SOFE ) {
+ // USBD will exit suspended mode when SOF event is received
+ const uint32_t frame = rusb->FRMNUM_b.FRNM;
+ dcd_event_sof(rhport, frame, true);
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 0;
+ }
+ }
+
+ // Device state changes
+ if ( is0 & RUSB2_INTSTS0_DVST_Msk ) {
+ switch (is0 & RUSB2_INTSTS0_DVSQ_Msk) {
+ case RUSB2_INTSTS0_DVSQ_STATE_DEF:
+ process_bus_reset(rhport);
+ break;
+
+ case RUSB2_INTSTS0_DVSQ_STATE_ADDR:
+ process_set_address(rhport);
+ break;
+
+ case RUSB2_INTSTS0_DVSQ_STATE_SUSP0:
+ case RUSB2_INTSTS0_DVSQ_STATE_SUSP1:
+ case RUSB2_INTSTS0_DVSQ_STATE_SUSP2:
+ case RUSB2_INTSTS0_DVSQ_STATE_SUSP3:
+ dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+ if (!_dcd.sof_enabled) {
+ rusb->INTENB0_b.SOFE = 1;
+ }
+
+ default: break;
+ }
+ }
+
+// if ( is0 & RUSB2_INTSTS0_NRDY_Msk ) {
+// rusb->NRDYSTS = 0;
+// }
+
+ // Control transfer stage changes
+ if ( is0 & RUSB2_INTSTS0_CTRT_Msk ) {
+ if ( is0 & RUSB2_INTSTS0_CTSQ_CTRL_RDATA ) {
+ /* A setup packet has been received. */
+ process_setup_packet(rhport);
+ } else if ( 0 == (is0 & RUSB2_INTSTS0_CTSQ_Msk) ) {
+ /* A ZLP has been sent/received. */
+ process_status_completion(rhport);
+ }
+ }
+
+ // Buffer empty
+ if ( is0 & RUSB2_INTSTS0_BEMP_Msk ) {
+ const uint16_t s = rusb->BEMPSTS;
+ rusb->BEMPSTS = 0;
+ if ( s & 1 ) {
+ process_pipe0_bemp(rhport);
+ }
+ }
+
+ // Buffer ready
+ if ( is0 & RUSB2_INTSTS0_BRDY_Msk ) {
+ const unsigned m = rusb->BRDYENB;
+ unsigned s = rusb->BRDYSTS & m;
+ /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
+ rusb->BRDYSTS = ~s;
+ while (s) {
+ const unsigned num = __builtin_ctz(s);
+ process_pipe_brdy(rhport, num);
+ s &= ~TU_BIT(num);
+ }
+ }
+}
+
+#endif
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
new file mode 100644
index 000000000..4c81b05be
--- /dev/null
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -0,0 +1,844 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Koji Kitayama
+ * Portions copyrighted (c) 2021 Roland Winistoerfer
+ *
+ * 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_TUH_ENABLED && defined(TUP_USBIP_RUSB2)
+
+#include "host/hcd.h"
+#include "rusb2_type.h"
+
+#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
+ #include "rusb2_rx.h"
+#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
+ #include "rusb2_ra.h"
+#else
+ #error "Unsupported MCU"
+#endif
+
+#define TU_RUSB2_HCD_DBG 2
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+enum {
+ PIPE_COUNT = 10,
+};
+
+TU_ATTR_PACKED_BEGIN
+TU_ATTR_BIT_FIELD_ORDER_BEGIN
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ volatile uint16_t u8: 8;
+ volatile uint16_t : 0;
+ };
+ volatile uint16_t u16;
+} hw_fifo_t;
+
+typedef struct TU_ATTR_PACKED {
+ void *buf; /* the start address of a transfer data buffer */
+ uint16_t length; /* the number of bytes in the buffer */
+ uint16_t remaining; /* the number of bytes remaining in the buffer */
+ struct {
+ uint32_t ep : 8; /* an assigned endpoint address */
+ uint32_t dev : 8; /* an assigned device address */
+ uint32_t ff : 1; /* `buf` is TU_FUFO or POD */
+ uint32_t : 0;
+ };
+} pipe_state_t;
+
+TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
+TU_ATTR_BIT_FIELD_ORDER_END
+
+typedef struct
+{
+ bool need_reset; /* The device has not been reset after connection. */
+ pipe_state_t pipe[PIPE_COUNT];
+ uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
+ uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
+} hcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+static hcd_data_t _hcd;
+
+// TODO merged with DCD
+// Transfer conditions specifiable for each pipe for most MCUs
+// - Pipe 0: Control transfer with 64-byte single buffer
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+ const uint8_t pipe_idx_arr[4][2] = {
+ { 0, 0 }, // Control
+ { 1, 2 }, // Isochronous
+ { 1, 5 }, // Bulk
+ { 6, 9 }, // Interrupt
+ };
+
+ // find backward since only pipe 1, 2 support ISO
+ const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+ const uint8_t idx_last = pipe_idx_arr[xfer_type][1];
+
+ for (int i = idx_last; i >= idx_first; i--) {
+ if (0 == _hcd.pipe[i].ep) return i;
+ }
+
+ return 0;
+}
+
+static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
+{
+ if (num) {
+ return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]);
+ } else {
+ return (volatile uint16_t*)&(rusb->DCPCTR);
+ }
+}
+
+static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
+{
+ volatile reg_pipetre_t* tre = NULL;
+ if ((1 <= num) && (num <= 5)) {
+ tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E);
+ }
+ return tre;
+}
+
+static volatile uint16_t* addr_to_pipectr(uint8_t rhport, uint8_t dev_addr, unsigned ep_addr)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ const unsigned epn = tu_edpt_number(ep_addr);
+
+ if (epn) {
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned num = _hcd.ep[dev_addr][dir_in][epn - 1];
+ return get_pipectr(rusb, num);
+ } else {
+ return get_pipectr(rusb, 0);
+ }
+}
+
+static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb)
+{
+ return rusb->DCPMAXP_b.MXPS;
+}
+
+static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num)
+{
+ rusb->PIPESEL = num;
+ return rusb->PIPEMAXP_b.MXPS;
+}
+
+static inline void pipe_wait_for_ready(rusb2_reg_t* rusb, unsigned num)
+{
+ while (rusb->D0FIFOSEL_b.CURPIPE != num) ;
+ while (!rusb->D0FIFOCTR_b.FRDY) {}
+}
+
+static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
+{
+ // NOTE: unlike DCD, Highspeed 32-bit FIFO does not need to adjust the fifo address
+ volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
+ uintptr_t addr = (uintptr_t)buf;
+ while (len >= 2) {
+ reg->u16 = *(const uint16_t *)addr;
+ addr += 2;
+ len -= 2;
+ }
+ if (len) {
+ reg->u8 = *(const uint8_t *)addr;
+ ++addr;
+ }
+}
+
+static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
+{
+ uint8_t *p = (uint8_t*)buf;
+ volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
+ while (len--) *p++ = *reg;
+}
+
+static bool pipe0_xfer_in(rusb2_reg_t* rusb)
+{
+ pipe_state_t *pipe = &_hcd.pipe[0];
+ const unsigned rem = pipe->remaining;
+
+ const unsigned mps = edpt0_max_packet_size(rusb);
+ const unsigned vld = rusb->CFIFOCTR_b.DTLN;
+ const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
+ void *buf = pipe->buf;
+ if (len) {
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
+ pipe_read_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ if (len < mps) {
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BCLR_Msk;
+ }
+ pipe->remaining = rem - len;
+ if ((len < mps) || (rem == len)) {
+ pipe->buf = NULL;
+ return true;
+ }
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_BUF;
+ return false;
+}
+
+static bool pipe0_xfer_out(rusb2_reg_t* rusb)
+{
+ pipe_state_t *pipe = &_hcd.pipe[0];
+ const unsigned rem = pipe->remaining;
+ if (!rem) {
+ pipe->buf = NULL;
+ return true;
+ }
+ const unsigned mps = edpt0_max_packet_size(rusb);
+ const unsigned len = TU_MIN(mps, rem);
+ void *buf = pipe->buf;
+ if (len) {
+ pipe_write_packet(buf, (volatile void*)&rusb->CFIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ if (len < mps) {
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BVAL_Msk;
+ }
+ pipe->remaining = rem - len;
+ return false;
+}
+
+static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
+{
+ pipe_state_t *pipe = &_hcd.pipe[num];
+ const unsigned rem = pipe->remaining;
+
+ rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_8BIT;
+ const unsigned mps = edpt_max_packet_size(rusb, num);
+ pipe_wait_for_ready(rusb, num);
+ const unsigned vld = rusb->D0FIFOCTR_b.DTLN;
+ const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
+ void *buf = pipe->buf;
+ if (len) {
+ pipe_read_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ if (len < mps) {
+ rusb->D0FIFOCTR = RUSB2_D0FIFOCTR_BCLR_Msk;
+ }
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) ; /* if CURPIPE bits changes, check written value */
+ pipe->remaining = rem - len;
+ if ((len < mps) || (rem == len)) {
+ pipe->buf = NULL;
+ return NULL != buf;
+ }
+ return false;
+}
+
+static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num)
+{
+ pipe_state_t *pipe = &_hcd.pipe[num];
+ const unsigned rem = pipe->remaining;
+
+ if (!rem) {
+ pipe->buf = NULL;
+ return true;
+ }
+
+ rusb->D0FIFOSEL = num | RUSB2_FIFOSEL_MBW_16BIT | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
+ const unsigned mps = edpt_max_packet_size(rusb, num);
+ pipe_wait_for_ready(rusb, num);
+ const unsigned len = TU_MIN(rem, mps);
+ void *buf = pipe->buf;
+ if (len) {
+ pipe_write_packet(buf, (volatile void*)&rusb->D0FIFO, len);
+ pipe->buf = (uint8_t*)buf + len;
+ }
+ if (len < mps) {
+ rusb->D0FIFOCTR = RUSB2_D0FIFOCTR_BVAL_Msk;
+ }
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) ; /* if CURPIPE bits changes, check written value */
+ pipe->remaining = rem - len;
+ return false;
+}
+
+static bool process_pipe0_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
+{
+ (void)dev_addr;
+
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+
+ /* configure fifo direction and access unit settings */
+ if (dir_in) { /* IN, a byte */
+ rusb->CFIFOSEL = RUSB2_FIFOSEL_MBW_8BIT;
+ while (rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE) ;
+ } else { /* OUT, 2 bytes */
+ rusb->CFIFOSEL = RUSB2_CFIFOSEL_ISEL_WRITE | RUSB2_FIFOSEL_MBW_16BIT |
+ (TU_BYTE_ORDER == TU_BIG_ENDIAN ? RUSB2_FIFOSEL_BIGEND : 0);
+ while (!(rusb->CFIFOSEL & RUSB2_CFIFOSEL_ISEL_WRITE)) ;
+ }
+
+ pipe_state_t *pipe = &_hcd.pipe[0];
+ pipe->ep = ep_addr;
+ pipe->length = buflen;
+ pipe->remaining = buflen;
+ if (buflen) {
+ pipe->buf = buffer;
+ if (!dir_in) { /* OUT */
+ TU_ASSERT(rusb->DCPCTR_b.BSTS && (rusb->USBREQ & 0x80));
+ pipe0_xfer_out(rusb);
+ }
+ } else { /* ZLP */
+ pipe->buf = NULL;
+ if (!dir_in) { /* OUT */
+ rusb->CFIFOCTR = RUSB2_CFIFOCTR_BVAL_Msk;
+ }
+ if (dir_in == rusb->DCPCFG_b.DIR) {
+ TU_ASSERT(RUSB2_PIPE_CTR_PID_NAK == rusb->DCPCTR_b.PID);
+ rusb->DCPCTR_b.SQSET = 1;
+ rusb->DCPCFG_b.DIR = dir_in ^ 1;
+ }
+ }
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_BUF;
+ return true;
+}
+
+static bool process_pipe_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, void *buffer, uint16_t buflen)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned num = _hcd.ep[dev_addr - 1][dir_in][epn - 1];
+
+ TU_ASSERT(num);
+
+ pipe_state_t *pipe = &_hcd.pipe[num];
+ pipe->buf = buffer;
+ pipe->length = buflen;
+ pipe->remaining = buflen;
+ if (!dir_in) { /* OUT */
+ if (buflen) {
+ pipe_xfer_out(rusb, num);
+ } else { /* ZLP */
+ rusb->D0FIFOSEL = num;
+ pipe_wait_for_ready(rusb, num);
+ rusb->D0FIFOCTR = RUSB2_D0FIFOCTR_BVAL_Msk;
+ rusb->D0FIFOSEL = 0;
+ while (rusb->D0FIFOSEL_b.CURPIPE) {} /* if CURPIPE bits changes, check written value */
+ }
+ } else {
+ volatile uint16_t *ctr = get_pipectr(rusb, num);
+ volatile reg_pipetre_t *pt = get_pipetre(rusb, num);
+ if (pt) {
+ const unsigned mps = edpt_max_packet_size(rusb, num);
+ if (*ctr & 0x3) *ctr = RUSB2_PIPE_CTR_PID_NAK;
+ pt->TRE = TU_BIT(8);
+ pt->TRN = (buflen + mps - 1) / mps;
+ pt->TRENB = 1;
+ }
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+ return true;
+}
+
+static bool process_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
+{
+ const unsigned epn = tu_edpt_number(ep_addr);
+ if (0 == epn) {
+ return process_pipe0_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
+ } else {
+ return process_pipe_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
+ }
+}
+
+static void process_pipe0_bemp(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ bool completed = pipe0_xfer_out(rusb);
+ if (completed) {
+ pipe_state_t *pipe = &_hcd.pipe[0];
+ hcd_event_xfer_complete(pipe->dev,
+ tu_edpt_addr(0, TUSB_DIR_OUT),
+ pipe->length - pipe->remaining,
+ XFER_RESULT_SUCCESS, true);
+ }
+}
+
+static void process_pipe_nrdy(uint8_t rhport, unsigned num)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ xfer_result_t result;
+ uint16_t volatile *ctr = get_pipectr(rusb, num);
+ TU_LOG(TU_RUSB2_HCD_DBG, "NRDY %d %x\r\n", num, *ctr);
+ switch (*ctr & RUSB2_PIPE_CTR_PID_Msk) {
+ default: return;
+ case RUSB2_PIPE_CTR_PID_STALL: result = XFER_RESULT_STALLED; break;
+ case RUSB2_PIPE_CTR_PID_STALL2: result = XFER_RESULT_STALLED; break;
+ case RUSB2_PIPE_CTR_PID_NAK: result = XFER_RESULT_FAILED; break;
+ }
+ pipe_state_t *pipe = &_hcd.pipe[num];
+ hcd_event_xfer_complete(pipe->dev, pipe->ep,
+ pipe->length - pipe->remaining,
+ result, true);
+}
+
+static void process_pipe_brdy(uint8_t rhport, unsigned num)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ pipe_state_t *pipe = &_hcd.pipe[num];
+ const unsigned dir_in = tu_edpt_dir(pipe->ep);
+ bool completed;
+
+ if (dir_in) { /* IN */
+ if (num) {
+ completed = pipe_xfer_in(rusb, num);
+ } else {
+ completed = pipe0_xfer_in(rusb);
+ }
+ } else {
+ completed = pipe_xfer_out(rusb, num);
+ }
+ if (completed) {
+ hcd_event_xfer_complete(pipe->dev, pipe->ep,
+ pipe->length - pipe->remaining,
+ XFER_RESULT_SUCCESS, true);
+ TU_LOG(TU_RUSB2_HCD_DBG, "C %d %d\r\n", num, pipe->length - pipe->remaining);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* Host API
+ *------------------------------------------------------------------*/
+
+#if 0 // previously present in the rx driver before generalization
+static uint32_t disable_interrupt(void)
+{
+ uint32_t pswi;
+#if defined(__CCRX__)
+ pswi = get_psw() & 0x010000;
+ clrpsw_i();
+#else
+ pswi = __builtin_rx_mvfc(0) & 0x010000;
+ __builtin_rx_clrpsw('I');
+#endif
+ return pswi;
+}
+
+static void enable_interrupt(uint32_t pswi)
+{
+#if defined(__CCRX__)
+ set_psw(get_psw() | pswi);
+#else
+ __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
+#endif
+}
+#endif
+
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ rusb2_module_start(rhport, true);
+
+#ifdef RUSB2_SUPPORT_HIGHSPEED
+ if (rusb2_is_highspeed_rhport(rhport) ) {
+ rusb->SYSCFG_b.HSE = 1;
+ rusb->PHYSET_b.HSEB = 0;
+ rusb->PHYSET_b.DIRPD = 0;
+ R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MILLISECONDS);
+ rusb->PHYSET_b.PLLRESET = 0;
+ rusb->LPSTS_b.SUSPENDM = 1;
+ while ( !rusb->PLLSTA_b.PLLLOCK );
+ rusb->SYSCFG_b.DRPD = 1;
+ rusb->SYSCFG_b.DCFM = 1;
+ rusb->SYSCFG_b.DPRPU = 0;
+ rusb->SYSCFG_b.CNEN = 1;
+ rusb->BUSWAIT |= 0x0F00U;
+ rusb->SOFCFG_b.INTL = 1;
+ rusb->DVSTCTR0_b.VBUSEN = 1;
+ rusb->CFIFOSEL_b.MBW = 1;
+ rusb->D0FIFOSEL_b.MBW = 1;
+ rusb->D1FIFOSEL_b.MBW = 1;
+ rusb->INTSTS0 = 0;
+ for ( volatile int i = 0; i < 30000; ++i );
+ rusb->SYSCFG_b.USBE = 1;
+ } else
+#endif
+ {
+ rusb->SYSCFG_b.SCKE = 1;
+ while ( !rusb->SYSCFG_b.SCKE ) {}
+ rusb->SYSCFG_b.DCFM = 1; // Host function
+ rusb->SYSCFG_b.DPRPU = 0; // Disable D+ pull up
+ rusb->SYSCFG_b.DRPD = 1; // Enable D+/D- pull down
+
+ rusb->DVSTCTR0_b.VBUSEN = 1;
+ for ( volatile int i = 0; i < 30000; ++i ) {} // FIXME do we need to wait here? how long ?
+ //R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
+ rusb->SYSCFG_b.USBE = 1;
+
+ // MCU specific PHY init
+ rusb2_phy_init();
+
+ rusb->PHYSLEW = 0x5;
+ rusb->DPUSR0R_FS_b.FIXPHY0 = 0u; /* Transceiver Output fixed */
+ }
+
+ /* Setup default control pipe */
+ rusb->DCPCFG = RUSB2_PIPECFG_SHTNAK_Msk;
+ rusb->DCPMAXP = 64;
+ rusb->INTENB0 = RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_NRDY_Msk | RUSB2_INTSTS0_BEMP_Msk;
+ rusb->INTENB1 = RUSB2_INTSTS1_SACK_Msk | RUSB2_INTSTS1_SIGN_Msk | RUSB2_INTSTS1_ATTCH_Msk | RUSB2_INTSTS1_DTCH_Msk;
+ rusb->BEMPENB = 1;
+ rusb->NRDYENB = 1;
+ rusb->BRDYENB = 1;
+
+ return true;
+}
+
+void hcd_int_enable(uint8_t rhport) {
+ rusb2_int_enable(rhport);
+}
+
+void hcd_int_disable(uint8_t rhport) {
+ rusb2_int_disable(rhport);
+}
+
+uint32_t hcd_frame_number(uint8_t rhport)
+{
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ /* The device must be reset at least once after connection
+ * in order to start the frame counter. */
+ if (_hcd.need_reset) hcd_port_reset(rhport);
+ return rusb->FRMNUM_b.FRNM;
+}
+
+/*--------------------------------------------------------------------+
+ * Port API
+ *--------------------------------------------------------------------+*/
+bool hcd_port_connect_status(uint8_t rhport) {
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ return rusb->INTSTS1_b.ATTCH ? true : false;
+}
+
+void hcd_port_reset(uint8_t rhport) {
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
+ while (rusb->DCPCTR_b.PBUSY) {}
+
+ hcd_int_disable(rhport);
+ rusb->DVSTCTR0_b.UACT = 0;
+ if (rusb->DCPCTR_b.SUREQ) {
+ rusb->DCPCTR_b.SUREQCLR = 1;
+ }
+ hcd_int_enable(rhport);
+
+ /* Reset should be asserted 10-20ms. */
+ rusb->DVSTCTR0_b.USBRST = 1;
+ for (volatile int i = 0; i < 2400000; ++i) {}
+ rusb->DVSTCTR0_b.USBRST = 0;
+
+ rusb->DVSTCTR0_b.UACT = 1;
+ _hcd.need_reset = false;
+}
+
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ switch (rusb->DVSTCTR0_b.RHST) {
+ case RUSB2_DVSTCTR0_RHST_HS: return TUSB_SPEED_HIGH;
+ case RUSB2_DVSTCTR0_RHST_FS: return TUSB_SPEED_FULL;
+ case RUSB2_DVSTCTR0_RHST_LS: return TUSB_SPEED_LOW;
+ default: return TUSB_SPEED_INVALID;
+ }
+}
+
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ uint16_t volatile *ctr;
+
+ TU_ASSERT(dev_addr < 6,); /* USBa can only handle addresses from 0 to 5. */
+ if (!dev_addr) return;
+
+ _hcd.ctl_mps[dev_addr] = 0;
+ uint8_t *ep = &_hcd.ep[dev_addr - 1][0][0];
+
+ for (int i = 0; i < 2 * 15; ++i, ++ep) {
+ unsigned num = *ep;
+ if (!num || (dev_addr != _hcd.pipe[num].dev)) continue;
+
+ ctr = (uint16_t volatile*)&rusb->PIPE_CTR[num - 1];
+ *ctr = 0;
+ rusb->NRDYENB &= ~TU_BIT(num);
+ rusb->BRDYENB &= ~TU_BIT(num);
+ rusb->PIPESEL = num;
+ rusb->PIPECFG = 0;
+ rusb->PIPEMAXP = 0;
+
+ _hcd.pipe[num].ep = 0;
+ _hcd.pipe[num].dev = 0;
+ *ep = 0;
+ }
+}
+
+/*--------------------------------------------------------------------+
+ * Endpoints API
+ *--------------------------------------------------------------------+*/
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+ TU_ASSERT(dev_addr < 6); /* USBa can only handle addresses from 0 to 5. */
+
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ TU_LOG(TU_RUSB2_HCD_DBG, "S %d %x\r\n", dev_addr, rusb->DCPCTR);
+
+ TU_ASSERT(0 == rusb->DCPCTR_b.SUREQ);
+
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
+
+ _hcd.pipe[0].buf = NULL;
+ _hcd.pipe[0].length = 8;
+ _hcd.pipe[0].remaining = 0;
+ _hcd.pipe[0].dev = dev_addr;
+
+ while (rusb->DCPCTR_b.PBUSY) ;
+ rusb->DCPMAXP = (dev_addr << 12) | _hcd.ctl_mps[dev_addr];
+
+ /* Set direction in advance for DATA stage */
+ uint8_t const bmRequesttype = setup_packet[0];
+ rusb->DCPCFG_b.DIR = tu_edpt_dir(bmRequesttype) ? 0: 1;
+
+ uint16_t const* p = (uint16_t const*)(uintptr_t)&setup_packet[0];
+ rusb->USBREQ = tu_htole16(p[0]);
+ rusb->USBVAL = p[1];
+ rusb->USBINDX = p[2];
+ rusb->USBLENG = p[3];
+
+ rusb->DCPCTR_b.SUREQ = 1;
+ return true;
+}
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc)
+{
+ TU_ASSERT(dev_addr < 6); /* USBa can only handle addresses from 0 to 5. */
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+
+ const unsigned ep_addr = ep_desc->bEndpointAddress;
+ const unsigned epn = tu_edpt_number(ep_addr);
+ const unsigned mps = tu_edpt_packet_size(ep_desc);
+
+ if (0 == epn) {
+ rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
+ hcd_devtree_info_t devtree;
+ hcd_devtree_get_info(dev_addr, &devtree);
+ uint16_t volatile *devadd = (uint16_t volatile *)(uintptr_t) &rusb->DEVADD[0];
+ devadd += dev_addr;
+ while (rusb->DCPCTR_b.PBUSY) {}
+ rusb->DCPMAXP = (dev_addr << 12) | mps;
+ *devadd = (TUSB_SPEED_FULL == devtree.speed) ? RUSB2_DEVADD_USBSPD_FS : RUSB2_DEVADD_USBSPD_LS;
+ _hcd.ctl_mps[dev_addr] = mps;
+ return true;
+ }
+
+ const unsigned dir_in = tu_edpt_dir(ep_addr);
+ const unsigned xfer = ep_desc->bmAttributes.xfer;
+ if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) {
+ /* USBa supports up to 256 bytes */
+ return false;
+ }
+ const unsigned num = find_pipe(xfer);
+ if (!num) return false;
+
+ _hcd.pipe[num].dev = dev_addr;
+ _hcd.pipe[num].ep = ep_addr;
+ _hcd.ep[dev_addr - 1][dir_in][epn - 1] = num;
+
+ /* setup pipe */
+ hcd_int_disable(rhport);
+
+ rusb->PIPESEL = num;
+ rusb->PIPEMAXP = (dev_addr << 12) | mps;
+ volatile uint16_t *ctr = get_pipectr(rusb, num);
+ *ctr = RUSB2_PIPE_CTR_ACLRM_Msk | RUSB2_PIPE_CTR_SQCLR_Msk;
+ *ctr = 0;
+
+ unsigned cfg = ((1 ^ dir_in) << 4) | epn;
+ if (xfer == TUSB_XFER_BULK) {
+ cfg |= RUSB2_PIPECFG_TYPE_BULK | RUSB2_PIPECFG_SHTNAK_Msk | RUSB2_PIPECFG_DBLB_Msk;
+ } else if (xfer == TUSB_XFER_INTERRUPT) {
+ cfg |= RUSB2_PIPECFG_TYPE_INT;
+ } else {
+ cfg |= RUSB2_PIPECFG_TYPE_ISO | RUSB2_PIPECFG_DBLB_Msk;
+ }
+
+ rusb->PIPECFG = cfg;
+ rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
+ rusb->NRDYENB |= TU_BIT(num);
+ rusb->BRDYENB |= TU_BIT(num);
+
+ if (!dir_in) {
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+
+ hcd_int_enable(rhport);
+
+ return true;
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
+{
+ bool r;
+ hcd_int_disable(rhport);
+ TU_LOG(TU_RUSB2_HCD_DBG, "X %d %x %u\r\n", dev_addr, ep_addr, buflen);
+ r = process_edpt_xfer(rhport, dev_addr, ep_addr, buffer, buflen);
+ hcd_int_enable(rhport);
+ return r;
+}
+
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ // TODO not implemented yet
+ return false;
+}
+
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ uint16_t volatile *ctr = addr_to_pipectr(rhport, dev_addr, ep_addr);
+ TU_ASSERT(ctr);
+
+ const uint32_t pid = *ctr & 0x3;
+ if (pid & 2) {
+ *ctr = pid & 2;
+ *ctr = 0;
+ }
+ *ctr = RUSB2_PIPE_CTR_SQCLR_Msk;
+ unsigned const epn = tu_edpt_number(ep_addr);
+ if (!epn) return true;
+
+ if (!tu_edpt_dir(ep_addr)) { /* OUT */
+ *ctr = RUSB2_PIPE_CTR_PID_BUF;
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+ unsigned int count = 0;
+ while ((value & 1) == 0) {
+ value >>= 1;
+ count++;
+ }
+ return count;
+}
+#endif
+
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) in_isr;
+
+ rusb2_reg_t* rusb = RUSB2_REG(rhport);
+ unsigned is0 = rusb->INTSTS0;
+ unsigned is1 = rusb->INTSTS1;
+
+ /* clear active bits except VALID (don't write 0 to already cleared bits according to the HW manual) */
+ rusb->INTSTS1 = ~((RUSB2_INTSTS1_SACK_Msk | RUSB2_INTSTS1_SIGN_Msk | RUSB2_INTSTS1_ATTCH_Msk | RUSB2_INTSTS1_DTCH_Msk) & is1);
+ rusb->INTSTS0 = ~((RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_NRDY_Msk | RUSB2_INTSTS0_BEMP_Msk) & is0);
+
+ TU_LOG3("IS %04x %04x\r\n", is0, is1);
+ is1 &= rusb->INTENB1;
+ is0 &= rusb->INTENB0;
+
+ if (is1 & RUSB2_INTSTS1_SACK_Msk) {
+ /* Set DATA1 in advance for the next transfer. */
+ rusb->DCPCTR_b.SQSET = 1;
+ hcd_event_xfer_complete(rusb->DCPMAXP_b.DEVSEL, tu_edpt_addr(0, TUSB_DIR_OUT), 8, XFER_RESULT_SUCCESS, true);
+ }
+
+ if (is1 & RUSB2_INTSTS1_SIGN_Msk) {
+ hcd_event_xfer_complete(rusb->DCPMAXP_b.DEVSEL, tu_edpt_addr(0, TUSB_DIR_OUT), 8, XFER_RESULT_FAILED, true);
+ }
+
+ if (is1 & RUSB2_INTSTS1_ATTCH_Msk) {
+ rusb->DVSTCTR0_b.UACT = 1;
+ _hcd.need_reset = true;
+ rusb->INTENB1 = (rusb->INTENB1 & ~RUSB2_INTSTS1_ATTCH_Msk) | RUSB2_INTSTS1_DTCH_Msk;
+ hcd_event_device_attach(rhport, true);
+ }
+
+ if (is1 & RUSB2_INTSTS1_DTCH_Msk) {
+ rusb->DVSTCTR0_b.UACT = 0;
+ if (rusb->DCPCTR_b.SUREQ) {
+ rusb->DCPCTR_b.SUREQCLR = 1;
+ }
+ rusb->INTENB1 = (rusb->INTENB1 & ~RUSB2_INTSTS1_DTCH_Msk) | RUSB2_INTSTS1_ATTCH_Msk;
+ hcd_event_device_remove(rhport, true);
+ }
+
+ if (is0 & RUSB2_INTSTS0_BEMP_Msk) {
+ const unsigned s = rusb->BEMPSTS;
+ rusb->BEMPSTS = 0;
+ if (s & 1) {
+ process_pipe0_bemp(rhport);
+ }
+ }
+
+ if (is0 & RUSB2_INTSTS0_NRDY_Msk) {
+ const unsigned m = rusb->NRDYENB;
+ unsigned s = rusb->NRDYSTS & m;
+ rusb->NRDYSTS = ~s;
+ while (s) {
+ const unsigned num = __builtin_ctz(s);
+ process_pipe_nrdy(rhport, num);
+ s &= ~TU_BIT(num);
+ }
+ }
+ if (is0 & RUSB2_INTSTS0_BRDY_Msk) {
+ const unsigned m = rusb->BRDYENB;
+ unsigned s = rusb->BRDYSTS & m;
+ /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
+ rusb->BRDYSTS = ~s;
+ while (s) {
+ const unsigned num = __builtin_ctz(s);
+ process_pipe_brdy(rhport, num);
+ s &= ~TU_BIT(num);
+ }
+ }
+}
+
+#endif
diff --git a/src/portable/renesas/rusb2/rusb2_common.c b/src/portable/renesas/rusb2/rusb2_common.c
new file mode 100644
index 000000000..72e65736b
--- /dev/null
+++ b/src/portable/renesas/rusb2/rusb2_common.c
@@ -0,0 +1,62 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if defined(TUP_USBIP_RUSB2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+
+#include "rusb2_type.h"
+
+#if TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N)
+#include "rusb2_rx.h"
+
+#elif TU_CHECK_MCU(OPT_MCU_RAXXX)
+#include "rusb2_ra.h"
+
+// USBFS_INT_IRQn and USBHS_USB_INT_RESUME_IRQn are generated by FSP
+rusb2_controller_t rusb2_controller[] = {
+ { .reg_base = R_USB_FS0_BASE, .irqnum = USBFS_INT_IRQn },
+ #ifdef RUSB2_SUPPORT_HIGHSPEED
+ { .reg_base = R_USB_HS0_BASE, .irqnum = USBHS_USB_INT_RESUME_IRQn },
+ #endif
+};
+
+// Application API for setting IRQ number. May throw warnings for missing prototypes.
+void tusb_rusb2_set_irqnum(uint8_t rhport, int32_t irqnum);
+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
+
+
+#endif
diff --git a/src/portable/renesas/rusb2/rusb2_ra.h b/src/portable/renesas/rusb2/rusb2_ra.h
new file mode 100644
index 000000000..4774d2e2c
--- /dev/null
+++ b/src/portable/renesas/rusb2/rusb2_ra.h
@@ -0,0 +1,109 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Rafael Silva (@perigoso)
+ *
+ * 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 _RUSB2_RA_H_
+#define _RUSB2_RA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#pragma GCC diagnostic ignored "-Wundef"
+
+// extra push due to https://github.com/renesas/fsp/pull/278
+#pragma GCC diagnostic push
+#endif
+
+/* renesas fsp api */
+#include "bsp_api.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// IAR does not have __builtin_ctz
+#if defined(__ICCARM__)
+ #define __builtin_ctz(x) __iar_builtin_CLZ(__iar_builtin_RBIT(x))
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+typedef struct {
+ uint32_t reg_base;
+ int32_t irqnum;
+}rusb2_controller_t;
+
+#if defined(BSP_MCU_GROUP_RA6M5) || defined(BSP_MCU_GROUP_RA6M3) || (BSP_CFG_MCU_PART_SERIES == 8)
+ #define RUSB2_SUPPORT_HIGHSPEED
+ #define RUSB2_CONTROLLER_COUNT 2
+
+ #define rusb2_is_highspeed_rhport(_p) (_p == 1)
+ #define rusb2_is_highspeed_reg(_reg) (_reg == RUSB2_REG(1))
+#else
+ #define RUSB2_CONTROLLER_COUNT 1
+
+ #define rusb2_is_highspeed_rhport(_p) (false)
+ #define rusb2_is_highspeed_reg(_reg) (false)
+#endif
+
+extern rusb2_controller_t rusb2_controller[];
+#define RUSB2_REG(_p) ((rusb2_reg_t*) rusb2_controller[_p].reg_base)
+
+//--------------------------------------------------------------------+
+// RUSB2 API
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_module_start(uint8_t rhport, bool start) {
+ uint32_t const mask = 1U << (11+rhport);
+ if (start) {
+ R_MSTP->MSTPCRB &= ~mask;
+ }else {
+ R_MSTP->MSTPCRB |= mask;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_enable(uint8_t rhport) {
+ NVIC_EnableIRQ(rusb2_controller[rhport].irqnum);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_disable(uint8_t rhport) {
+ NVIC_DisableIRQ(rusb2_controller[rhport].irqnum);
+}
+
+// MCU specific PHY init
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_phy_init(void) {
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RUSB2_RA_H_ */
diff --git a/src/portable/renesas/rusb2/rusb2_rx.h b/src/portable/renesas/rusb2/rusb2_rx.h
new file mode 100644
index 000000000..7bf4be47e
--- /dev/null
+++ b/src/portable/renesas/rusb2/rusb2_rx.h
@@ -0,0 +1,94 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ * Portions copyrighted (c) 2021 Roland Winistoerfer
+ * Copyright (c) 2022 Rafael Silva (@perigoso)
+ *
+ * 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 _RUSB2_RX_H_
+#define _RUSB2_RX_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "iodefine.h"
+
+#define RUSB2_REG_BASE (0x000A0000)
+
+TU_ATTR_ALWAYS_INLINE static inline rusb2_reg_t* RUSB2_REG(uint8_t rhport) {
+ (void) rhport;
+ return (rusb2_reg_t *) RUSB2_REG_BASE;
+}
+
+
+#define rusb2_is_highspeed_rhport(_p) (false)
+#define rusb2_is_highspeed_reg(_reg) (false)
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+
+// Start/Stop MSTP TODO implement later
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_module_start(uint8_t rhport, bool start) {
+ (void) rhport;
+ (void) start;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_enable(uint8_t rhport)
+{
+ (void) rhport;
+#if (CFG_TUSB_MCU == OPT_MCU_RX72N)
+ IEN(PERIB, INTB185) = 1;
+#else
+ IEN(USB0, USBI0) = 1;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_disable(uint8_t rhport)
+{
+ (void) rhport;
+#if (CFG_TUSB_MCU == OPT_MCU_RX72N)
+ IEN(PERIB, INTB185) = 0;
+#else
+ IEN(USB0, USBI0) = 0;
+#endif
+}
+
+// MCU specific PHY init
+TU_ATTR_ALWAYS_INLINE static inline void rusb2_phy_init(void)
+{
+#if (CFG_TUSB_MCU == OPT_MCU_RX72N)
+ IR(PERIB, INTB185) = 0;
+#else
+ IR(USB0, USBI0) = 0;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RUSB2_RX_H_ */
diff --git a/src/portable/renesas/rusb2/rusb2_type.h b/src/portable/renesas/rusb2/rusb2_type.h
new file mode 100644
index 000000000..dd88f66a7
--- /dev/null
+++ b/src/portable/renesas/rusb2/rusb2_type.h
@@ -0,0 +1,1780 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Rafael Silva (@perigoso)
+ *
+ * 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_RUSB2_TYPE_H_
+#define _TUSB_RUSB2_TYPE_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// CCRX specific attribute to generate a Code that Accesses Variables in the Declared Size
+#ifdef __CCRX__
+ #define _ccrx_evenaccess __evenaccess
+#else
+ #define _ccrx_evenaccess
+#endif
+
+/*--------------------------------------------------------------------*/
+/* Register Definitions */
+/*--------------------------------------------------------------------*/
+
+/* Start of definition of packed structs (used by the CCRX toolchain) */
+TU_ATTR_PACKED_BEGIN
+TU_ATTR_BIT_FIELD_ORDER_BEGIN
+
+// TODO same as RUSB2_PIPE_TR_t
+typedef struct TU_ATTR_PACKED _ccrx_evenaccess {
+ union {
+ struct {
+ uint16_t : 8;
+ uint16_t TRCLR: 1;
+ uint16_t TRENB: 1;
+ uint16_t : 0;
+ };
+ uint16_t TRE;
+ };
+ uint16_t TRN;
+} reg_pipetre_t;
+
+typedef struct {
+ union {
+ volatile uint16_t E; /* (@ 0x00000000) Pipe Transaction Counter Enable Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 8;
+ volatile uint16_t TRCLR : 1; /* [8..8] Transaction Counter Clear */
+ volatile uint16_t TRENB : 1; /* [9..9] Transaction Counter Enable */
+ uint16_t : 6;
+ } E_b;
+ };
+
+ union {
+ volatile uint16_t N; /* (@ 0x00000002) Pipe Transaction Counter Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t TRNCNT : 16; /* [15..0] Transaction Counter */
+ } N_b;
+ };
+} RUSB2_PIPE_TR_t; /* Size = 4 (0x4) */
+
+
+/* RUSB2 Registers Structure */
+typedef struct _ccrx_evenaccess {
+ union {
+ volatile uint16_t SYSCFG; /* (@ 0x00000000) System Configuration Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t USBE : 1; /* [0..0] USB Operation Enable */
+ uint16_t : 2;
+ volatile uint16_t DMRPU : 1; /* [3..3] D- Line Resistor Control */
+ volatile uint16_t DPRPU : 1; /* [4..4] D+ Line Resistor Control */
+ volatile uint16_t DRPD : 1; /* [5..5] D+/D- Line Resistor Control */
+ volatile uint16_t DCFM : 1; /* [6..6] Controller Function Select */
+ volatile uint16_t HSE : 1; // [7..7] High-Speed Operation Enable
+ volatile uint16_t CNEN : 1; /* [8..8] CNEN Single End Receiver Enable */
+ uint16_t : 1;
+ volatile uint16_t SCKE : 1; /* [10..10] USB Clock Enable */
+ uint16_t : 5;
+ } SYSCFG_b;
+ };
+
+ union {
+ volatile uint16_t BUSWAIT; /* (@ 0x00000002) CPU Bus Wait Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t BWAIT : 4; /* [3..0] CPU Bus Access Wait Specification BWAIT waits (BWAIT+2 access cycles) */
+ uint16_t : 12;
+ } BUSWAIT_b;
+ };
+
+ union {
+ volatile const uint16_t SYSSTS0; /* (@ 0x00000004) System Configuration Status Register 0 */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t LNST : 2; /* [1..0] USB Data Line Status Monitor */
+ volatile const uint16_t IDMON : 1; /* [2..2] External ID0 Input Pin Monitor */
+ uint16_t : 2;
+ volatile const uint16_t SOFEA : 1; /* [5..5] SOF Active Monitor While Host Controller Function is Selected. */
+ volatile const uint16_t HTACT : 1; /* [6..6] USB Host Sequencer Status Monitor */
+ uint16_t : 7;
+ volatile const uint16_t OVCMON : 2; /* [15..14] External USB0_OVRCURA/ USB0_OVRCURB Input Pin Monitor */
+ } SYSSTS0_b;
+ };
+
+ union {
+ volatile const uint16_t PLLSTA; /* (@ 0x00000006) PLL Status Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t PLLLOCK : 1; /* [0..0] PLL Lock Flag */
+ uint16_t : 15;
+ } PLLSTA_b;
+ };
+
+ union {
+ volatile uint16_t DVSTCTR0; /* (@ 0x00000008) Device State Control Register 0 */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t RHST : 3; /* [2..0] USB Bus Reset Status */
+ uint16_t : 1;
+ volatile uint16_t UACT : 1; /* [4..4] USB Bus Enable */
+ volatile uint16_t RESUME : 1; /* [5..5] Resume Output */
+ volatile uint16_t USBRST : 1; /* [6..6] USB Bus Reset Output */
+ volatile uint16_t RWUPE : 1; /* [7..7] Wakeup Detection Enable */
+ volatile uint16_t WKUP : 1; /* [8..8] Wakeup Output */
+ volatile uint16_t VBUSEN : 1; /* [9..9] USB_VBUSEN Output Pin Control */
+ volatile uint16_t EXICEN : 1; /* [10..10] USB_EXICEN Output Pin Control */
+ volatile uint16_t HNPBTOA : 1; /* [11..11] Host Negotiation Protocol (HNP) */
+ uint16_t : 4;
+ } DVSTCTR0_b;
+ };
+ volatile const uint16_t RESERVED;
+
+ union {
+ volatile uint16_t TESTMODE; /* (@ 0x0000000C) USB Test Mode Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t UTST : 4; /* [3..0] Test Mode */
+ uint16_t : 12;
+ } TESTMODE_b;
+ };
+ volatile const uint16_t RESERVED1;
+ volatile const uint32_t RESERVED2;
+
+ union {
+ volatile uint32_t CFIFO; /* (@ 0x00000014) CFIFO Port Register */
+
+ struct TU_ATTR_PACKED {
+ union {
+ volatile uint16_t CFIFOL; /* (@ 0x00000014) CFIFO Port Register L */
+ volatile uint8_t CFIFOLL; /* (@ 0x00000014) CFIFO Port Register LL */
+ };
+
+ union {
+ volatile uint16_t CFIFOH; /* (@ 0x00000016) CFIFO Port Register H */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint8_t RESERVED3;
+ volatile uint8_t CFIFOHH; /* (@ 0x00000017) CFIFO Port Register HH */
+ };
+ };
+ };
+ };
+
+ union {
+ volatile uint32_t D0FIFO; /* (@ 0x00000018) D0FIFO Port Register */
+
+ struct TU_ATTR_PACKED {
+ union {
+ volatile uint16_t D0FIFOL; /* (@ 0x00000018) D0FIFO Port Register L */
+ volatile uint8_t D0FIFOLL; /* (@ 0x00000018) D0FIFO Port Register LL */
+ };
+
+ union {
+ volatile uint16_t D0FIFOH; /* (@ 0x0000001A) D0FIFO Port Register H */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint8_t RESERVED4;
+ volatile uint8_t D0FIFOHH; /* (@ 0x0000001B) D0FIFO Port Register HH */
+ };
+ };
+ };
+ };
+
+ union {
+ volatile uint32_t D1FIFO; /* (@ 0x0000001C) D1FIFO Port Register */
+
+ struct TU_ATTR_PACKED {
+ union {
+ volatile uint16_t D1FIFOL; /* (@ 0x0000001C) D1FIFO Port Register L */
+ volatile uint8_t D1FIFOLL; /* (@ 0x0000001C) D1FIFO Port Register LL */
+ };
+
+ union {
+ volatile uint16_t D1FIFOH; /* (@ 0x0000001E) D1FIFO Port Register H */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint8_t RESERVED5;
+ volatile uint8_t D1FIFOHH; /* (@ 0x0000001F) D1FIFO Port Register HH */
+ };
+ };
+ };
+ };
+
+ union {
+ volatile uint16_t CFIFOSEL; /* (@ 0x00000020) CFIFO Port Select Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t CURPIPE : 4; /* [3..0] CFIFO Port Access Pipe Specification */
+ uint16_t : 1;
+ volatile uint16_t ISEL : 1; /* [5..5] CFIFO Port Access Direction When DCP is Selected */
+ uint16_t : 2;
+ volatile uint16_t BIGEND : 1; /* [8..8] CFIFO Port Endian Control */
+ uint16_t : 1;
+ volatile uint16_t MBW : 2; /* [11..10] CFIFO Port Access Bit Width */
+ uint16_t : 2;
+ volatile uint16_t REW : 1; /* [14..14] Buffer Pointer Rewind */
+ volatile uint16_t RCNT : 1; /* [15..15] Read Count Mode */
+ } CFIFOSEL_b;
+ };
+
+ union {
+ volatile uint16_t CFIFOCTR; /* (@ 0x00000022) CFIFO Port Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t DTLN : 12; /* [11..0] Receive Data LengthIndicates the length of the receive data. */
+ uint16_t : 1;
+ volatile const uint16_t FRDY : 1; /* [13..13] FIFO Port Ready */
+ volatile uint16_t BCLR : 1; /* [14..14] CPU Buffer ClearNote: Only 0 can be read. */
+ volatile uint16_t BVAL : 1; /* [15..15] Buffer Memory Valid Flag */
+ } CFIFOCTR_b;
+ };
+ volatile const uint32_t RESERVED6;
+
+ union {
+ volatile uint16_t D0FIFOSEL; /* (@ 0x00000028) D0FIFO Port Select Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t CURPIPE : 4; /* [3..0] FIFO Port Access Pipe Specification */
+ uint16_t : 4;
+ volatile uint16_t BIGEND : 1; /* [8..8] FIFO Port Endian Control */
+ uint16_t : 1;
+ volatile uint16_t MBW : 2; /* [11..10] FIFO Port Access Bit Width */
+ volatile uint16_t DREQE : 1; /* [12..12] DMA/DTC Transfer Request Enable */
+ volatile uint16_t DCLRM : 1; /* [13..13] Auto Buffer Memory Clear Mode Accessed after Specified Pipe Data is Read */
+ volatile uint16_t REW : 1; /* [14..14] Buffer Pointer RewindNote: Only 0 can be read. */
+ volatile uint16_t RCNT : 1; /* [15..15] Read Count Mode */
+ } D0FIFOSEL_b;
+ };
+
+ union {
+ volatile uint16_t D0FIFOCTR; /* (@ 0x0000002A) D0FIFO Port Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t DTLN : 12; /* [11..0] Receive Data LengthIndicates the length of the receive data. */
+ uint16_t : 1;
+ volatile const uint16_t FRDY : 1; /* [13..13] FIFO Port Ready */
+ volatile uint16_t BCLR : 1; /* [14..14] CPU Buffer ClearNote: Only 0 can be read. */
+ volatile uint16_t BVAL : 1; /* [15..15] Buffer Memory Valid Flag */
+ } D0FIFOCTR_b;
+ };
+
+ union {
+ volatile uint16_t D1FIFOSEL; /* (@ 0x0000002C) D1FIFO Port Select Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t CURPIPE : 4; /* [3..0] FIFO Port Access Pipe Specification */
+ uint16_t : 4;
+ volatile uint16_t BIGEND : 1; /* [8..8] FIFO Port Endian Control */
+ uint16_t : 1;
+ volatile uint16_t MBW : 2; /* [11..10] FIFO Port Access Bit Width */
+ volatile uint16_t DREQE : 1; /* [12..12] DMA/DTC Transfer Request Enable */
+ volatile uint16_t DCLRM : 1; /* [13..13] Auto Buffer Memory Clear Mode Accessed after Specified Pipe Data is Read */
+ volatile uint16_t REW : 1; /* [14..14] Buffer Pointer Rewind */
+ volatile uint16_t RCNT : 1; /* [15..15] Read Count Mode */
+ } D1FIFOSEL_b;
+ };
+
+ union {
+ volatile uint16_t D1FIFOCTR; /* (@ 0x0000002E) D1FIFO Port Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t DTLN : 12; /* [11..0] Receive Data LengthIndicates the length of the receive data. */
+ uint16_t : 1;
+ volatile const uint16_t FRDY : 1; /* [13..13] FIFO Port Ready */
+ volatile uint16_t BCLR : 1; /* [14..14] CPU Buffer ClearNote: Only 0 can be read. */
+ volatile uint16_t BVAL : 1; /* [15..15] Buffer Memory Valid Flag */
+ } D1FIFOCTR_b;
+ };
+
+ union {
+ volatile uint16_t INTENB0; /* (@ 0x00000030) Interrupt Enable Register 0 */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 8;
+ volatile uint16_t BRDYE : 1; /* [8..8] Buffer Ready Interrupt Enable */
+ volatile uint16_t NRDYE : 1; /* [9..9] Buffer Not Ready Response Interrupt Enable */
+ volatile uint16_t BEMPE : 1; /* [10..10] Buffer Empty Interrupt Enable */
+ volatile uint16_t CTRE : 1; /* [11..11] Control Transfer Stage Transition Interrupt Enable */
+ volatile uint16_t DVSE : 1; /* [12..12] Device State Transition Interrupt Enable */
+ volatile uint16_t SOFE : 1; /* [13..13] Frame Number Update Interrupt Enable */
+ volatile uint16_t RSME : 1; /* [14..14] Resume Interrupt Enable */
+ volatile uint16_t VBSE : 1; /* [15..15] VBUS Interrupt Enable */
+ } INTENB0_b;
+ };
+
+ union {
+ volatile uint16_t INTENB1; /* (@ 0x00000032) Interrupt Enable Register 1 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PDDETINTE0 : 1; /* [0..0] PDDETINT0 Detection Interrupt Enable */
+ uint16_t : 3;
+ volatile uint16_t SACKE : 1; /* [4..4] Setup Transaction Normal Response Interrupt Enable */
+ volatile uint16_t SIGNE : 1; /* [5..5] Setup Transaction Error Interrupt Enable */
+ volatile uint16_t EOFERRE : 1; /* [6..6] EOF Error Detection Interrupt Enable */
+ uint16_t : 1;
+ volatile uint16_t LPMENDE : 1; /*!< [8..8] LPM Transaction End Interrupt Enable */
+ volatile uint16_t L1RSMENDE : 1; /*!< [9..9] L1 Resume End Interrupt Enable */
+ uint16_t : 1;
+ volatile uint16_t ATTCHE : 1; /* [11..11] Connection Detection Interrupt Enable */
+ volatile uint16_t DTCHE : 1; /* [12..12] Disconnection Detection Interrupt Enable */
+ uint16_t : 1;
+ volatile uint16_t BCHGE : 1; /* [14..14] USB Bus Change Interrupt Enable */
+ volatile uint16_t OVRCRE : 1; /* [15..15] Overcurrent Input Change Interrupt Enable */
+ } INTENB1_b;
+ };
+ volatile const uint16_t RESERVED7;
+
+ union {
+ volatile uint16_t BRDYENB; /* (@ 0x00000036) BRDY Interrupt Enable Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0BRDYE : 1; /* [0..0] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE1BRDYE : 1; /* [1..1] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE2BRDYE : 1; /* [2..2] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE3BRDYE : 1; /* [3..3] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE4BRDYE : 1; /* [4..4] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE5BRDYE : 1; /* [5..5] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE6BRDYE : 1; /* [6..6] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE7BRDYE : 1; /* [7..7] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE8BRDYE : 1; /* [8..8] BRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE9BRDYE : 1; /* [9..9] BRDY Interrupt Enable for PIPE */
+ uint16_t : 6;
+ } BRDYENB_b;
+ };
+
+ union {
+ volatile uint16_t NRDYENB; /* (@ 0x00000038) NRDY Interrupt Enable Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0NRDYE : 1; /* [0..0] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE1NRDYE : 1; /* [1..1] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE2NRDYE : 1; /* [2..2] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE3NRDYE : 1; /* [3..3] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE4NRDYE : 1; /* [4..4] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE5NRDYE : 1; /* [5..5] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE6NRDYE : 1; /* [6..6] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE7NRDYE : 1; /* [7..7] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE8NRDYE : 1; /* [8..8] NRDY Interrupt Enable for PIPE */
+ volatile uint16_t PIPE9NRDYE : 1; /* [9..9] NRDY Interrupt Enable for PIPE */
+ uint16_t : 6;
+ } NRDYENB_b;
+ };
+
+ union {
+ volatile uint16_t BEMPENB; /* (@ 0x0000003A) BEMP Interrupt Enable Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0BEMPE : 1; /* [0..0] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE1BEMPE : 1; /* [1..1] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE2BEMPE : 1; /* [2..2] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE3BEMPE : 1; /* [3..3] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE4BEMPE : 1; /* [4..4] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE5BEMPE : 1; /* [5..5] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE6BEMPE : 1; /* [6..6] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE7BEMPE : 1; /* [7..7] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE8BEMPE : 1; /* [8..8] BEMP Interrupt Enable for PIPE */
+ volatile uint16_t PIPE9BEMPE : 1; /* [9..9] BEMP Interrupt Enable for PIPE */
+ uint16_t : 6;
+ } BEMPENB_b;
+ };
+
+ union {
+ volatile uint16_t SOFCFG; /* (@ 0x0000003C) SOF Output Configuration Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 4;
+ volatile const uint16_t EDGESTS : 1; /* [4..4] Edge Interrupt Output Status Monitor */
+ volatile uint16_t INTL : 1; /* [5..5] Interrupt Output Sense Select */
+ volatile uint16_t BRDYM : 1; /* [6..6] BRDY Interrupt Status Clear Timing */
+ uint16_t : 1;
+ volatile uint16_t TRNENSEL : 1; /* [8..8] Transaction-Enabled Time Select */
+ uint16_t : 7;
+ } SOFCFG_b;
+ };
+
+ union {
+ volatile uint16_t PHYSET; /* (@ 0x0000003E) PHY Setting Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t DIRPD : 1; /* [0..0] Power-Down Control */
+ volatile uint16_t PLLRESET : 1; /* [1..1] PLL Reset Control */
+ uint16_t : 1;
+ volatile uint16_t CDPEN : 1; /* [3..3] Charging Downstream Port Enable */
+ volatile uint16_t CLKSEL : 2; /* [5..4] Input System Clock Frequency */
+ uint16_t : 2;
+ volatile uint16_t REPSEL : 2; /* [9..8] Terminating Resistance Adjustment Cycle */
+ uint16_t : 1;
+ volatile uint16_t REPSTART : 1; /* [11..11] Forcibly Start Terminating Resistance Adjustment */
+ uint16_t : 3;
+ volatile uint16_t HSEB : 1; /* [15..15] CL-Only Mode */
+ } PHYSET_b;
+ };
+
+ union {
+ volatile uint16_t INTSTS0; /* (@ 0x00000040) Interrupt Status Register 0 */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t CTSQ : 3; /* [2..0] Control Transfer Stage */
+ volatile uint16_t VALID : 1; /* [3..3] USB Request Reception */
+ volatile const uint16_t DVSQ : 3; /* [6..4] Device State */
+ volatile const uint16_t VBSTS : 1; /* [7..7] VBUS Input Status */
+ volatile const uint16_t BRDY : 1; /* [8..8] Buffer Ready Interrupt Status */
+ volatile const uint16_t NRDY : 1; /* [9..9] Buffer Not Ready Interrupt Status */
+ volatile const uint16_t BEMP : 1; /* [10..10] Buffer Empty Interrupt Status */
+ volatile uint16_t CTRT : 1; /* [11..11] Control Transfer Stage Transition Interrupt Status */
+ volatile uint16_t DVST : 1; /* [12..12] Device State Transition Interrupt Status */
+ volatile uint16_t SOFR : 1; /* [13..13] Frame Number Refresh Interrupt Status */
+ volatile uint16_t RESM : 1; /* [14..14] Resume Interrupt Status */
+ volatile uint16_t VBINT : 1; /* [15..15] VBUS Interrupt Status */
+ } INTSTS0_b;
+ };
+
+ union {
+ volatile uint16_t INTSTS1; /* (@ 0x00000042) Interrupt Status Register 1 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PDDETINT0 : 1; /* [0..0] PDDET0 Detection Interrupt Status */
+ uint16_t : 3;
+ volatile uint16_t SACK : 1; /* [4..4] Setup Transaction Normal Response Interrupt Status */
+ volatile uint16_t SIGN : 1; /* [5..5] Setup Transaction Error Interrupt Status */
+ volatile uint16_t EOFERR : 1; /* [6..6] EOF Error Detection Interrupt Status */
+ uint16_t : 1;
+ volatile uint16_t LPMEND : 1; /* [8..8] LPM Transaction End Interrupt Status */
+ volatile uint16_t L1RSMEND : 1; /* [9..9] L1 Resume End Interrupt Status */
+ uint16_t : 1;
+ volatile uint16_t ATTCH : 1; /* [11..11] ATTCH Interrupt Status */
+ volatile uint16_t DTCH : 1; /* [12..12] USB Disconnection Detection Interrupt Status */
+ uint16_t : 1;
+ volatile uint16_t BCHG : 1; /* [14..14] USB Bus Change Interrupt Status */
+ volatile uint16_t OVRCR : 1; /* [15..15] Overcurrent Input Change Interrupt Status */
+ } INTSTS1_b;
+ };
+ volatile const uint16_t RESERVED8;
+
+ union {
+ volatile uint16_t BRDYSTS; /* (@ 0x00000046) BRDY Interrupt Status Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0BRDY : 1; /* [0..0] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE1BRDY : 1; /* [1..1] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE2BRDY : 1; /* [2..2] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE3BRDY : 1; /* [3..3] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE4BRDY : 1; /* [4..4] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE5BRDY : 1; /* [5..5] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE6BRDY : 1; /* [6..6] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE7BRDY : 1; /* [7..7] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE8BRDY : 1; /* [8..8] BRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE9BRDY : 1; /* [9..9] BRDY Interrupt Status for PIPE */
+ uint16_t : 6;
+ } BRDYSTS_b;
+ };
+
+ union {
+ volatile uint16_t NRDYSTS; /* (@ 0x00000048) NRDY Interrupt Status Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0NRDY : 1; /* [0..0] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE1NRDY : 1; /* [1..1] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE2NRDY : 1; /* [2..2] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE3NRDY : 1; /* [3..3] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE4NRDY : 1; /* [4..4] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE5NRDY : 1; /* [5..5] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE6NRDY : 1; /* [6..6] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE7NRDY : 1; /* [7..7] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE8NRDY : 1; /* [8..8] NRDY Interrupt Status for PIPE */
+ volatile uint16_t PIPE9NRDY : 1; /* [9..9] NRDY Interrupt Status for PIPE */
+ uint16_t : 6;
+ } NRDYSTS_b;
+ };
+
+ union {
+ volatile uint16_t BEMPSTS; /* (@ 0x0000004A) BEMP Interrupt Status Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPE0BEMP : 1; /* [0..0] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE1BEMP : 1; /* [1..1] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE2BEMP : 1; /* [2..2] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE3BEMP : 1; /* [3..3] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE4BEMP : 1; /* [4..4] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE5BEMP : 1; /* [5..5] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE6BEMP : 1; /* [6..6] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE7BEMP : 1; /* [7..7] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE8BEMP : 1; /* [8..8] BEMP Interrupt Status for PIPE */
+ volatile uint16_t PIPE9BEMP : 1; /* [9..9] BEMP Interrupt Status for PIPE */
+ uint16_t : 6;
+ } BEMPSTS_b;
+ };
+
+ union {
+ volatile uint16_t FRMNUM; /* (@ 0x0000004C) Frame Number Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t FRNM : 11; /* [10..0] Frame NumberLatest frame number */
+ uint16_t : 3;
+ volatile uint16_t CRCE : 1; /* [14..14] Receive Data Error */
+ volatile uint16_t OVRN : 1; /* [15..15] Overrun/Underrun Detection Status */
+ } FRMNUM_b;
+ };
+
+ union {
+ volatile uint16_t UFRMNUM; /* (@ 0x0000004E) uFrame Number Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t UFRNM : 3; /* [2..0] MicroframeIndicate the microframe number. */
+ uint16_t : 12;
+ volatile uint16_t DVCHG : 1; /* [15..15] Device State Change */
+ } UFRMNUM_b;
+ };
+
+ union {
+ volatile uint16_t USBADDR; /* (@ 0x00000050) USB Address Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t USBADDR : 7; /* [6..0] USB Address In device controller mode */
+ uint16_t : 1;
+ volatile uint16_t STSRECOV0 : 3; /* [10..8] Status Recovery */
+ uint16_t : 5;
+ } USBADDR_b;
+ };
+ volatile const uint16_t RESERVED9;
+
+ union {
+ volatile uint16_t USBREQ; /* (@ 0x00000054) USB Request Type Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t BMREQUESTTYPE : 8; /* [7..0] Request TypeThese bits store the USB request bmRequestType value. */
+ volatile uint16_t BREQUEST : 8; /* [15..8] RequestThese bits store the USB request bRequest value. */
+ } USBREQ_b;
+ };
+
+ union {
+ volatile uint16_t USBVAL; /* (@ 0x00000056) USB Request Value Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t WVALUE : 16; /* [15..0] ValueThese bits store the USB request Value value. */
+ } USBVAL_b;
+ };
+
+ union {
+ volatile uint16_t USBINDX; /* (@ 0x00000058) USB Request Index Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t WINDEX : 16; /* [15..0] IndexThese bits store the USB request wIndex value. */
+ } USBINDX_b;
+ };
+
+ union {
+ volatile uint16_t USBLENG; /* (@ 0x0000005A) USB Request Length Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t WLENGTH : 16; /* [15..0] LengthThese bits store the USB request wLength value. */
+ } USBLENG_b;
+ };
+
+ union {
+ volatile uint16_t DCPCFG; /* (@ 0x0000005C) DCP Configuration Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 4;
+ volatile uint16_t DIR : 1; /* [4..4] Transfer Direction */
+ uint16_t : 2;
+ volatile uint16_t SHTNAK : 1; /* [7..7] Pipe Disabled at End of Transfer */
+ volatile uint16_t CNTMD : 1; /* [8..8] Continuous Transfer Mode */
+ uint16_t : 7;
+ } DCPCFG_b;
+ };
+
+ union {
+ volatile uint16_t DCPMAXP; /* (@ 0x0000005E) DCP Maximum Packet Size Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t MXPS : 7; /* [6..0] Maximum Packet Size */
+ uint16_t : 5;
+ volatile uint16_t DEVSEL : 4; /* [15..12] Device Select */
+ } DCPMAXP_b;
+ };
+
+ union {
+ volatile uint16_t DCPCTR; /* (@ 0x00000060) DCP Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PID : 2; /* [1..0] Response PID */
+ volatile uint16_t CCPL : 1; /* [2..2] Control Transfer End Enable */
+ uint16_t : 2;
+ volatile const uint16_t PBUSY : 1; /* [5..5] Pipe Busy */
+ volatile const uint16_t SQMON : 1; /* [6..6] Sequence Toggle Bit Monitor */
+ volatile uint16_t SQSET : 1; /* [7..7] Sequence Toggle Bit Set */
+ volatile uint16_t SQCLR : 1; /* [8..8] Sequence Toggle Bit Clear */
+ uint16_t : 2;
+ volatile uint16_t SUREQCLR : 1; /* [11..11] SUREQ Bit Clear */
+ volatile uint16_t CSSTS : 1; /* [12..12] Split Transaction COMPLETE SPLIT(CSPLIT) Status */
+ volatile uint16_t CSCLR : 1; /* [13..13] Split Transaction CSPLIT Status Clear */
+ volatile uint16_t SUREQ : 1; /* [14..14] Setup Token Transmission */
+ volatile const uint16_t BSTS : 1; /* [15..15] Buffer Status */
+ } DCPCTR_b;
+ };
+ volatile const uint16_t RESERVED10;
+
+ union {
+ volatile uint16_t PIPESEL; /* (@ 0x00000064) Pipe Window Select Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PIPESEL : 4; /* [3..0] Pipe Window Select */
+ uint16_t : 12;
+ } PIPESEL_b;
+ };
+ volatile const uint16_t RESERVED11;
+
+ union {
+ volatile uint16_t PIPECFG; /* (@ 0x00000068) Pipe Configuration Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t EPNUM : 4; /* [3..0] Endpoint Number */
+ volatile uint16_t DIR : 1; /* [4..4] Transfer Direction */
+ uint16_t : 2;
+ volatile uint16_t SHTNAK : 1; /* [7..7] Pipe Disabled at End of Transfer */
+ volatile uint16_t CNTMD : 1; /* [8..8] Continuous Transfer Mode */
+ volatile uint16_t DBLB : 1; /* [9..9] Double Buffer Mode */
+ volatile uint16_t BFRE : 1; /* [10..10] BRDY Interrupt Operation Specification */
+ uint16_t : 3;
+ volatile uint16_t TYPE : 2; /* [15..14] Transfer Type */
+ } PIPECFG_b;
+ };
+
+ union {
+ volatile uint16_t PIPEBUF; /*!< (@ 0x0000006A) Pipe Buffer Register */
+
+ struct {
+ volatile uint16_t BUFNMB : 8; // [7..0] Buffer NumberThese bits specify the FIFO buffer number of the selected pipe (04h to 87h)
+ uint16_t : 2;
+ volatile uint16_t BUFSIZE : 5; /*!< [14..10] Buffer Size 00h: 64 bytes 01h: 128 bytes : 1Fh: 2 Kbytes */
+ uint16_t : 1;
+ } PIPEBUF_b;
+ };
+
+ union {
+ volatile uint16_t PIPEMAXP; /* (@ 0x0000006C) Pipe Maximum Packet Size Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t MXPS : 11; /* [10..0] Maximum Packet Size */
+ uint16_t : 1;
+ volatile uint16_t DEVSEL : 4; /* [15..12] Device Select */
+ } PIPEMAXP_b;
+ };
+
+ union {
+ volatile uint16_t PIPEPERI; /* (@ 0x0000006E) Pipe Cycle Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t IITV : 3; /* [2..0] Interval Error Detection Interval */
+ uint16_t : 9;
+ volatile uint16_t IFIS : 1; /* [12..12] Isochronous IN Buffer Flush */
+ uint16_t : 3;
+ } PIPEPERI_b;
+ };
+
+ union {
+ volatile uint16_t PIPE_CTR[9]; /* (@ 0x00000070) Pipe [0..8] Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t PID : 2; /* [1..0] Response PID */
+ uint16_t : 3;
+ volatile const uint16_t PBUSY : 1; /* [5..5] Pipe Busy */
+ volatile const uint16_t SQMON : 1; /* [6..6] Sequence Toggle Bit Confirmation */
+ volatile uint16_t SQSET : 1; /* [7..7] Sequence Toggle Bit Set */
+ volatile uint16_t SQCLR : 1; /* [8..8] Sequence Toggle Bit Clear */
+ volatile uint16_t ACLRM : 1; /* [9..9] Auto Buffer Clear Mode */
+ volatile uint16_t ATREPM : 1; /* [10..10] Auto Response Mode */
+ uint16_t : 1;
+ volatile const uint16_t CSSTS : 1; /* [12..12] CSSTS Status */
+ volatile uint16_t CSCLR : 1; /* [13..13] CSPLIT Status Clear */
+ volatile const uint16_t INBUFM : 1; /* [14..14] Transmit Buffer Monitor */
+ volatile const uint16_t BSTS : 1; /* [15..15] Buffer Status */
+ } PIPE_CTR_b[9];
+ };
+ volatile const uint16_t RESERVED13;
+ volatile const uint32_t RESERVED14[3];
+ volatile RUSB2_PIPE_TR_t PIPE_TR[5]; /* (@ 0x00000090) Pipe Transaction Counter Registers */
+ volatile const uint32_t RESERVED15[3];
+
+ union {
+ volatile uint16_t USBBCCTRL0; /* (@ 0x000000B0) BC Control Register 0 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t RPDME0 : 1; /* [0..0] D- Pin Pull-Down Control */
+ volatile uint16_t IDPSRCE0 : 1; /* [1..1] D+ Pin IDPSRC Output Control */
+ volatile uint16_t IDMSINKE0 : 1; /* [2..2] D- Pin 0.6 V Input Detection (Comparator and Sink) Control */
+ volatile uint16_t VDPSRCE0 : 1; /* [3..3] D+ Pin VDPSRC (0.6 V) Output Control */
+ volatile uint16_t IDPSINKE0 : 1; /* [4..4] D+ Pin 0.6 V Input Detection (Comparator and Sink) Control */
+ volatile uint16_t VDMSRCE0 : 1; /* [5..5] D- Pin VDMSRC (0.6 V) Output Control */
+ uint16_t : 1;
+ volatile uint16_t BATCHGE0 : 1; /* [7..7] BC (Battery Charger) Function Ch0 General Enable Control */
+ volatile const uint16_t CHGDETSTS0 : 1; /* [8..8] D- Pin 0.6 V Input Detection Status */
+ volatile const uint16_t PDDETSTS0 : 1; /* [9..9] D+ Pin 0.6 V Input Detection Status */
+ uint16_t : 6;
+ } USBBCCTRL0_b;
+ };
+ volatile const uint16_t RESERVED16;
+ volatile const uint32_t RESERVED17[4];
+
+ union {
+ volatile uint16_t UCKSEL; /* (@ 0x000000C4) USB Clock Selection Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t UCKSELC : 1; /* [0..0] USB Clock Selection */
+ uint16_t : 15;
+ } UCKSEL_b;
+ };
+ volatile const uint16_t RESERVED18;
+ volatile const uint32_t RESERVED19;
+
+ union {
+ volatile uint16_t USBMC; /* (@ 0x000000CC) USB Module Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t VDDUSBE : 1; /* [0..0] USB Reference Power Supply Circuit On/Off Control */
+ uint16_t : 6;
+ volatile uint16_t VDCEN : 1; /* [7..7] USB Regulator On/Off Control */
+ uint16_t : 8;
+ } USBMC_b;
+ };
+ volatile const uint16_t RESERVED20;
+
+ union {
+ volatile uint16_t DEVADD[10]; /* (@ 0x000000D0) Device Address Configuration Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 6;
+ volatile uint16_t USBSPD : 2; /* [7..6] Transfer Speed of Communication Target Device */
+ volatile uint16_t HUBPORT : 3; /* [10..8] Communication Target Connecting Hub Port */
+ volatile uint16_t UPPHUB : 4; /* [14..11] Communication Target Connecting Hub Register */
+ uint16_t : 1;
+ } DEVADD_b[10];
+ };
+ volatile const uint32_t RESERVED21[3];
+
+ union {
+ volatile uint32_t PHYSLEW; /* (@ 0x000000F0) PHY Cross Point Adjustment Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint32_t SLEWR00 : 1; /* [0..0] Receiver Cross Point Adjustment 00 */
+ volatile uint32_t SLEWR01 : 1; /* [1..1] Receiver Cross Point Adjustment 01 */
+ volatile uint32_t SLEWF00 : 1; /* [2..2] Receiver Cross Point Adjustment 00 */
+ volatile uint32_t SLEWF01 : 1; /* [3..3] Receiver Cross Point Adjustment 01 */
+ uint32_t : 28;
+ } PHYSLEW_b;
+ };
+ volatile const uint32_t RESERVED22[3];
+
+ union {
+ volatile uint16_t LPCTRL; /* (@ 0x00000100) Low Power Control Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 7;
+ volatile uint16_t HWUPM : 1; /* [7..7] Resume Return Mode Setting */
+ uint16_t : 8;
+ } LPCTRL_b;
+ };
+
+ union {
+ volatile uint16_t LPSTS; /* (@ 0x00000102) Low Power Status Register */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 14;
+ volatile uint16_t SUSPENDM : 1; /* [14..14] UTMI SuspendM Control */
+ uint16_t : 1;
+ } LPSTS_b;
+ };
+ volatile const uint32_t RESERVED23[15];
+
+ union {
+ volatile uint16_t BCCTRL; /* (@ 0x00000140) Battery Charging Control Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t IDPSRCE : 1; /* [0..0] IDPSRC Control */
+ volatile uint16_t IDMSINKE : 1; /* [1..1] IDMSINK Control */
+ volatile uint16_t VDPSRCE : 1; /* [2..2] VDPSRC Control */
+ volatile uint16_t IDPSINKE : 1; /* [3..3] IDPSINK Control */
+ volatile uint16_t VDMSRCE : 1; /* [4..4] VDMSRC Control */
+ volatile uint16_t DCPMODE : 1; /* [5..5] DCP Mode Control */
+ uint16_t : 2;
+ volatile const uint16_t CHGDETSTS : 1; /* [8..8] CHGDET Status */
+ volatile const uint16_t PDDETSTS : 1; /* [9..9] PDDET Status */
+ uint16_t : 6;
+ } BCCTRL_b;
+ };
+ volatile const uint16_t RESERVED24;
+
+ union {
+ volatile uint16_t PL1CTRL1; /* (@ 0x00000144) Function L1 Control Register 1 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t L1RESPEN : 1; /* [0..0] L1 Response Enable */
+ volatile uint16_t L1RESPMD : 2; /* [2..1] L1 Response Mode */
+ volatile uint16_t L1NEGOMD : 1; /* [3..3] L1 Response Negotiation Control. */
+ volatile const uint16_t DVSQ : 4; /* [7..4] DVSQ Extension.DVSQ[3] is Mirror of DVSQ[2:0] in INTSTS0. */
+ volatile uint16_t HIRDTHR : 4; /* [11..8] L1 Response Negotiation Threshold Value */
+ uint16_t : 2;
+ volatile uint16_t L1EXTMD : 1; /* [14..14] PHY Control Mode at L1 Return */
+ uint16_t : 1;
+ } PL1CTRL1_b;
+ };
+
+ union {
+ volatile uint16_t PL1CTRL2; /* (@ 0x00000146) Function L1 Control Register 2 */
+
+ struct TU_ATTR_PACKED {
+ uint16_t : 8;
+ volatile uint16_t HIRDMON : 4; /* [11..8] HIRD Value Monitor */
+ volatile uint16_t RWEMON : 1; /* [12..12] RWE Value Monitor */
+ uint16_t : 3;
+ } PL1CTRL2_b;
+ };
+
+ union {
+ volatile uint16_t HL1CTRL1; /* (@ 0x00000148) Host L1 Control Register 1 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t L1REQ : 1; /* [0..0] L1 Transition Request */
+ volatile const uint16_t L1STATUS : 2; /* [2..1] L1 Request Completion Status */
+ uint16_t : 13;
+ } HL1CTRL1_b;
+ };
+
+ union {
+ volatile uint16_t HL1CTRL2; /* (@ 0x0000014A) Host L1 Control Register 2 */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t L1ADDR : 4; /* [3..0] LPM Token DeviceAddress */
+ uint16_t : 4;
+ volatile uint16_t HIRD : 4; /* [11..8] LPM Token HIRD */
+ volatile uint16_t L1RWE : 1; /* [12..12] LPM Token L1 Remote Wake Enable */
+ uint16_t : 2;
+ volatile uint16_t BESL : 1; /* [15..15] BESL & Alternate HIRD */
+ } HL1CTRL2_b;
+ };
+
+ volatile uint32_t RESERVED25_1;
+
+ union {
+ volatile uint16_t PHYTRIM1; /*!< (@ 0x00000150) PHY Timing Register 1 */
+
+ struct {
+ volatile uint16_t DRISE : 2; /*!< [1..0] FS/LS Rising-Edge Output Waveform Adjustment Function */
+ volatile uint16_t DFALL : 2; /*!< [3..2] FS/LS Falling-Edge Output Waveform Adjustment Function */
+ uint16_t : 3;
+ volatile uint16_t PCOMPENB : 1; /*!< [7..7] PVDD Start-up Detection */
+ volatile uint16_t HSIUP : 4; /*!< [11..8] HS Output Level Setting */
+ volatile uint16_t IMPOFFSET : 3; /*!< [14..12] terminating resistance offset value setting.Offset value for adjusting the terminating resistance. */
+ uint16_t : 1;
+ } PHYTRIM1_b;
+ };
+
+ union {
+ volatile uint16_t PHYTRIM2; /*!< (@ 0x00000152) PHY Timing Register 2 */
+
+ struct {
+ volatile uint16_t SQU : 4; /*!< [3..0] Squelch Detection Level */
+ uint16_t : 3;
+ volatile uint16_t HSRXENMO : 1; /*!< [7..7] HS Receive Enable Control Mode */
+ volatile uint16_t PDR : 2; /*!< [9..8] HS Output Adjustment Function */
+ uint16_t : 2;
+ volatile uint16_t DIS : 3; /*!< [14..12] Disconnect Detection Level */
+ uint16_t : 1;
+ } PHYTRIM2_b;
+ };
+ volatile uint32_t RESERVED25_2[3];
+
+ union {
+ volatile const uint32_t DPUSR0R; /* (@ 0x00000160) Deep Standby USB Transceiver Control/Pin Monitor Register */
+
+ struct TU_ATTR_PACKED {
+ uint32_t : 20;
+ volatile const uint32_t DOVCAHM : 1; /* [20..20] OVRCURA InputIndicates OVRCURA input signal on the HS side of USB port. */
+ volatile const uint32_t DOVCBHM : 1; /* [21..21] OVRCURB InputIndicates OVRCURB input signal on the HS side of USB port. */
+ uint32_t : 1;
+ volatile const uint32_t DVBSTSHM : 1; /* [23..23] VBUS InputIndicates VBUS input signal on the HS side of USB port. */
+ uint32_t : 8;
+ } DPUSR0R_b;
+ };
+
+ union {
+ volatile uint32_t DPUSR1R; /* (@ 0x00000164) Deep Standby USB Suspend/Resume Interrupt Register */
+
+ struct TU_ATTR_PACKED {
+ uint32_t : 4;
+ volatile uint32_t DOVCAHE : 1; /* [4..4] OVRCURA Interrupt Enable Clear */
+ volatile uint32_t DOVCBHE : 1; /* [5..5] OVRCURB Interrupt Enable Clear */
+ uint32_t : 1;
+ volatile uint32_t DVBSTSHE : 1; /* [7..7] VBUS Interrupt Enable/Clear */
+ uint32_t : 12;
+ volatile const uint32_t DOVCAH : 1; /* [20..20] Indication of Return from OVRCURA Interrupt Source */
+ volatile const uint32_t DOVCBH : 1; /* [21..21] Indication of Return from OVRCURB Interrupt Source */
+ uint32_t : 1;
+ volatile const uint32_t DVBSTSH : 1; /* [23..23] Indication of Return from VBUS Interrupt Source */
+ uint32_t : 8;
+ } DPUSR1R_b;
+ };
+
+ union {
+ volatile uint16_t DPUSR2R; /* (@ 0x00000168) Deep Standby USB Suspend/Resume Interrupt Register */
+
+ struct TU_ATTR_PACKED {
+ volatile const uint16_t DPINT : 1; /* [0..0] Indication of Return from DP Interrupt Source */
+ volatile const uint16_t DMINT : 1; /* [1..1] Indication of Return from DM Interrupt Source */
+ uint16_t : 2;
+ volatile const uint16_t DPVAL : 1; /* [4..4] DP InputIndicates DP input signal on the HS side of USB port. */
+ volatile const uint16_t DMVAL : 1; /* [5..5] DM InputIndicates DM input signal on the HS side of USB port. */
+ uint16_t : 2;
+ volatile uint16_t DPINTE : 1; /* [8..8] DP Interrupt Enable Clear */
+ volatile uint16_t DMINTE : 1; /* [9..9] DM Interrupt Enable Clear */
+ uint16_t : 6;
+ } DPUSR2R_b;
+ };
+
+ union {
+ volatile uint16_t DPUSRCR; /* (@ 0x0000016A) Deep Standby USB Suspend/Resume Command Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint16_t FIXPHY : 1; /* [0..0] USB Transceiver Control Fix */
+ volatile uint16_t FIXPHYPD : 1; /* [1..1] USB Transceiver Control Fix for PLL */
+ uint16_t : 14;
+ } DPUSRCR_b;
+ };
+ volatile const uint32_t RESERVED26[165];
+
+ union {
+ volatile uint32_t
+ DPUSR0R_FS; /* (@ 0x00000400) Deep Software Standby USB Transceiver Control/Pin Monitor Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint32_t SRPC0 : 1; /* [0..0] USB Single End Receiver Control */
+ volatile uint32_t RPUE0 : 1; /* [1..1] DP Pull-Up Resistor Control */
+ uint32_t : 1;
+ volatile uint32_t DRPD0 : 1; /* [3..3] D+/D- Pull-Down Resistor Control */
+ volatile uint32_t FIXPHY0 : 1; /* [4..4] USB Transceiver Output Fix */
+ uint32_t : 11;
+ volatile const uint32_t DP0 : 1; /* [16..16] USB0 D+ InputIndicates the D+ input signal of the USB. */
+ volatile const uint32_t DM0 : 1; /* [17..17] USB D-InputIndicates the D- input signal of the USB. */
+ uint32_t : 2;
+ volatile const uint32_t DOVCA0 : 1; /* [20..20] USB OVRCURA InputIndicates the OVRCURA input signal of the USB. */
+ volatile const uint32_t DOVCB0 : 1; /* [21..21] USB OVRCURB InputIndicates the OVRCURB input signal of the USB. */
+ uint32_t : 1;
+ volatile const uint32_t DVBSTS0 : 1; /* [23..23] USB VBUS InputIndicates the VBUS input signal of the USB. */
+ uint32_t : 8;
+ } DPUSR0R_FS_b;
+ };
+
+ union {
+ volatile uint32_t DPUSR1R_FS; /* (@ 0x00000404) Deep Software Standby USB Suspend/Resume Interrupt Register */
+
+ struct TU_ATTR_PACKED {
+ volatile uint32_t DPINTE0 : 1; /* [0..0] USB DP Interrupt Enable/Clear */
+ volatile uint32_t DMINTE0 : 1; /* [1..1] USB DM Interrupt Enable/Clear */
+ uint32_t : 2;
+ volatile uint32_t DOVRCRAE0 : 1; /* [4..4] USB OVRCURA Interrupt Enable/Clear */
+ volatile uint32_t DOVRCRBE0 : 1; /* [5..5] USB OVRCURB Interrupt Enable/Clear */
+ uint32_t : 1;
+ volatile uint32_t DVBSE0 : 1; /* [7..7] USB VBUS Interrupt Enable/Clear */
+ uint32_t : 8;
+ volatile const uint32_t DPINT0 : 1; /* [16..16] USB DP Interrupt Source Recovery */
+ volatile const uint32_t DMINT0 : 1; /* [17..17] USB DM Interrupt Source Recovery */
+ uint32_t : 2;
+ volatile const uint32_t DOVRCRA0 : 1; /* [20..20] USB OVRCURA Interrupt Source Recovery */
+ volatile const uint32_t DOVRCRB0 : 1; /* [21..21] USB OVRCURB Interrupt Source Recovery */
+ uint32_t : 1;
+ volatile const uint32_t DVBINT0 : 1; /* [23..23] USB VBUS Interrupt Source Recovery */
+ uint32_t : 8;
+ } DPUSR1R_FS_b;
+ };
+} rusb2_reg_t; /* Size = 1032 (0x408) */
+
+TU_ATTR_PACKED_END /* End of definition of packed structs (used by the CCRX toolchain) */
+TU_ATTR_BIT_FIELD_ORDER_END
+
+/*--------------------------------------------------------------------*/
+/* Register Bit Definitions */
+/*--------------------------------------------------------------------*/
+
+// PIPE_TR
+// E
+#define RUSB2_PIPE_TR_E_TRENB_Pos (9UL) /* TRENB (Bit 9) */
+#define RUSB2_PIPE_TR_E_TRENB_Msk (0x200UL) /* TRENB (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_TR_E_TRCLR_Pos (8UL) /* TRCLR (Bit 8) */
+#define RUSB2_PIPE_TR_E_TRCLR_Msk (0x100UL) /* TRCLR (Bitfield-Mask: 0x01) */
+
+// N
+#define RUSB2_PIPE_TR_N_TRNCNT_Pos (0UL) /* TRNCNT (Bit 0) */
+#define RUSB2_PIPE_TR_N_TRNCNT_Msk (0xffffUL) /* TRNCNT (Bitfield-Mask: 0xffff) */
+
+// Core Registers
+
+// SYSCFG
+#define RUSB2_SYSCFG_SCKE_Pos (10UL) /* SCKE (Bit 10) */
+#define RUSB2_SYSCFG_SCKE_Msk (0x400UL) /* SCKE (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_CNEN_Pos (8UL) /* CNEN (Bit 8) */
+#define RUSB2_SYSCFG_CNEN_Msk (0x100UL) /* CNEN (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_HSE_Pos (7UL) /*!< HSE (Bit 7) */
+#define RUSB2_SYSCFG_HSE_Msk (0x80UL) /*!< HSE (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_DCFM_Pos (6UL) /* DCFM (Bit 6) */
+#define RUSB2_SYSCFG_DCFM_Msk (0x40UL) /* DCFM (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_DRPD_Pos (5UL) /* DRPD (Bit 5) */
+#define RUSB2_SYSCFG_DRPD_Msk (0x20UL) /* DRPD (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_DPRPU_Pos (4UL) /* DPRPU (Bit 4) */
+#define RUSB2_SYSCFG_DPRPU_Msk (0x10UL) /* DPRPU (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_DMRPU_Pos (3UL) /* DMRPU (Bit 3) */
+#define RUSB2_SYSCFG_DMRPU_Msk (0x8UL) /* DMRPU (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSCFG_USBE_Pos (0UL) /* USBE (Bit 0) */
+#define RUSB2_SYSCFG_USBE_Msk (0x1UL) /* USBE (Bitfield-Mask: 0x01) */
+
+// BUSWAIT
+#define RUSB2_BUSWAIT_BWAIT_Pos (0UL) /* BWAIT (Bit 0) */
+#define RUSB2_BUSWAIT_BWAIT_Msk (0xfUL) /* BWAIT (Bitfield-Mask: 0x0f) */
+
+// SYSSTS0
+#define RUSB2_SYSSTS0_OVCMON_Pos (14UL) /* OVCMON (Bit 14) */
+#define RUSB2_SYSSTS0_OVCMON_Msk (0xc000UL) /* OVCMON (Bitfield-Mask: 0x03) */
+#define RUSB2_SYSSTS0_HTACT_Pos (6UL) /* HTACT (Bit 6) */
+#define RUSB2_SYSSTS0_HTACT_Msk (0x40UL) /* HTACT (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSSTS0_SOFEA_Pos (5UL) /* SOFEA (Bit 5) */
+#define RUSB2_SYSSTS0_SOFEA_Msk (0x20UL) /* SOFEA (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSSTS0_IDMON_Pos (2UL) /* IDMON (Bit 2) */
+#define RUSB2_SYSSTS0_IDMON_Msk (0x4UL) /* IDMON (Bitfield-Mask: 0x01) */
+#define RUSB2_SYSSTS0_LNST_Pos (0UL) /* LNST (Bit 0) */
+#define RUSB2_SYSSTS0_LNST_Msk (0x3UL) /* LNST (Bitfield-Mask: 0x03) */
+
+// PLLSTA
+#define RUSB2_PLLSTA_PLLLOCK_Pos (0UL) /* PLLLOCK (Bit 0) */
+#define RUSB2_PLLSTA_PLLLOCK_Msk (0x1UL) /* PLLLOCK (Bitfield-Mask: 0x01) */
+
+// DVSTCTR0
+#define RUSB2_DVSTCTR0_HNPBTOA_Pos (11UL) /* HNPBTOA (Bit 11) */
+#define RUSB2_DVSTCTR0_HNPBTOA_Msk (0x800UL) /* HNPBTOA (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_EXICEN_Pos (10UL) /* EXICEN (Bit 10) */
+#define RUSB2_DVSTCTR0_EXICEN_Msk (0x400UL) /* EXICEN (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_VBUSEN_Pos (9UL) /* VBUSEN (Bit 9) */
+#define RUSB2_DVSTCTR0_VBUSEN_Msk (0x200UL) /* VBUSEN (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_WKUP_Pos (8UL) /* WKUP (Bit 8) */
+#define RUSB2_DVSTCTR0_WKUP_Msk (0x100UL) /* WKUP (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_RWUPE_Pos (7UL) /* RWUPE (Bit 7) */
+#define RUSB2_DVSTCTR0_RWUPE_Msk (0x80UL) /* RWUPE (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_USBRST_Pos (6UL) /* USBRST (Bit 6) */
+#define RUSB2_DVSTCTR0_USBRST_Msk (0x40UL) /* USBRST (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_RESUME_Pos (5UL) /* RESUME (Bit 5) */
+#define RUSB2_DVSTCTR0_RESUME_Msk (0x20UL) /* RESUME (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_UACT_Pos (4UL) /* UACT (Bit 4) */
+#define RUSB2_DVSTCTR0_UACT_Msk (0x10UL) /* UACT (Bitfield-Mask: 0x01) */
+#define RUSB2_DVSTCTR0_RHST_Pos (0UL) /* RHST (Bit 0) */
+#define RUSB2_DVSTCTR0_RHST_Msk (0x7UL) /* RHST (Bitfield-Mask: 0x07) */
+
+// TESTMODE
+#define RUSB2_TESTMODE_UTST_Pos (0UL) /* UTST (Bit 0) */
+#define RUSB2_TESTMODE_UTST_Msk (0xfUL) /* UTST (Bitfield-Mask: 0x0f) */
+
+// CFIFOSEL
+#define RUSB2_CFIFOSEL_RCNT_Pos (15UL) /* RCNT (Bit 15) */
+#define RUSB2_CFIFOSEL_RCNT_Msk (0x8000UL) /* RCNT (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOSEL_REW_Pos (14UL) /* REW (Bit 14) */
+#define RUSB2_CFIFOSEL_REW_Msk (0x4000UL) /* REW (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOSEL_MBW_Pos (10UL) /* MBW (Bit 10) */
+#define RUSB2_CFIFOSEL_MBW_Msk (0xc00UL) /* MBW (Bitfield-Mask: 0x03) */
+#define RUSB2_CFIFOSEL_BIGEND_Pos (8UL) /* BIGEND (Bit 8) */
+#define RUSB2_CFIFOSEL_BIGEND_Msk (0x100UL) /* BIGEND (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOSEL_ISEL_Pos (5UL) /* ISEL (Bit 5) */
+#define RUSB2_CFIFOSEL_ISEL_Msk (0x20UL) /* ISEL (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOSEL_CURPIPE_Pos (0UL) /* CURPIPE (Bit 0) */
+#define RUSB2_CFIFOSEL_CURPIPE_Msk (0xfUL) /* CURPIPE (Bitfield-Mask: 0x0f) */
+
+// CFIFOCTR
+#define RUSB2_CFIFOCTR_BVAL_Pos (15UL) /* BVAL (Bit 15) */
+#define RUSB2_CFIFOCTR_BVAL_Msk (0x8000UL) /* BVAL (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOCTR_BCLR_Pos (14UL) /* BCLR (Bit 14) */
+#define RUSB2_CFIFOCTR_BCLR_Msk (0x4000UL) /* BCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOCTR_FRDY_Pos (13UL) /* FRDY (Bit 13) */
+#define RUSB2_CFIFOCTR_FRDY_Msk (0x2000UL) /* FRDY (Bitfield-Mask: 0x01) */
+#define RUSB2_CFIFOCTR_DTLN_Pos (0UL) /* DTLN (Bit 0) */
+#define RUSB2_CFIFOCTR_DTLN_Msk (0xfffUL) /* DTLN (Bitfield-Mask: 0xfff) */
+
+// D0FIFOSEL
+#define RUSB2_D0FIFOSEL_RCNT_Pos (15UL) /* RCNT (Bit 15) */
+#define RUSB2_D0FIFOSEL_RCNT_Msk (0x8000UL) /* RCNT (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOSEL_REW_Pos (14UL) /* REW (Bit 14) */
+#define RUSB2_D0FIFOSEL_REW_Msk (0x4000UL) /* REW (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOSEL_DCLRM_Pos (13UL) /* DCLRM (Bit 13) */
+#define RUSB2_D0FIFOSEL_DCLRM_Msk (0x2000UL) /* DCLRM (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOSEL_DREQE_Pos (12UL) /* DREQE (Bit 12) */
+#define RUSB2_D0FIFOSEL_DREQE_Msk (0x1000UL) /* DREQE (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOSEL_MBW_Pos (10UL) /* MBW (Bit 10) */
+#define RUSB2_D0FIFOSEL_MBW_Msk (0xc00UL) /* MBW (Bitfield-Mask: 0x03) */
+#define RUSB2_D0FIFOSEL_BIGEND_Pos (8UL) /* BIGEND (Bit 8) */
+#define RUSB2_D0FIFOSEL_BIGEND_Msk (0x100UL) /* BIGEND (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOSEL_CURPIPE_Pos (0UL) /* CURPIPE (Bit 0) */
+#define RUSB2_D0FIFOSEL_CURPIPE_Msk (0xfUL) /* CURPIPE (Bitfield-Mask: 0x0f) */
+
+// D0FIFOCTR
+#define RUSB2_D0FIFOCTR_BVAL_Pos (15UL) /* BVAL (Bit 15) */
+#define RUSB2_D0FIFOCTR_BVAL_Msk (0x8000UL) /* BVAL (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOCTR_BCLR_Pos (14UL) /* BCLR (Bit 14) */
+#define RUSB2_D0FIFOCTR_BCLR_Msk (0x4000UL) /* BCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOCTR_FRDY_Pos (13UL) /* FRDY (Bit 13) */
+#define RUSB2_D0FIFOCTR_FRDY_Msk (0x2000UL) /* FRDY (Bitfield-Mask: 0x01) */
+#define RUSB2_D0FIFOCTR_DTLN_Pos (0UL) /* DTLN (Bit 0) */
+#define RUSB2_D0FIFOCTR_DTLN_Msk (0xfffUL) /* DTLN (Bitfield-Mask: 0xfff) */
+
+// D1FIFOSEL
+#define RUSB2_D1FIFOSEL_RCNT_Pos (15UL) /* RCNT (Bit 15) */
+#define RUSB2_D1FIFOSEL_RCNT_Msk (0x8000UL) /* RCNT (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOSEL_REW_Pos (14UL) /* REW (Bit 14) */
+#define RUSB2_D1FIFOSEL_REW_Msk (0x4000UL) /* REW (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOSEL_DCLRM_Pos (13UL) /* DCLRM (Bit 13) */
+#define RUSB2_D1FIFOSEL_DCLRM_Msk (0x2000UL) /* DCLRM (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOSEL_DREQE_Pos (12UL) /* DREQE (Bit 12) */
+#define RUSB2_D1FIFOSEL_DREQE_Msk (0x1000UL) /* DREQE (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOSEL_MBW_Pos (10UL) /* MBW (Bit 10) */
+#define RUSB2_D1FIFOSEL_MBW_Msk (0xc00UL) /* MBW (Bitfield-Mask: 0x03) */
+#define RUSB2_D1FIFOSEL_BIGEND_Pos (8UL) /* BIGEND (Bit 8) */
+#define RUSB2_D1FIFOSEL_BIGEND_Msk (0x100UL) /* BIGEND (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOSEL_CURPIPE_Pos (0UL) /* CURPIPE (Bit 0) */
+#define RUSB2_D1FIFOSEL_CURPIPE_Msk (0xfUL) /* CURPIPE (Bitfield-Mask: 0x0f) */
+
+// D1FIFOCTR
+#define RUSB2_D1FIFOCTR_BVAL_Pos (15UL) /* BVAL (Bit 15) */
+#define RUSB2_D1FIFOCTR_BVAL_Msk (0x8000UL) /* BVAL (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOCTR_BCLR_Pos (14UL) /* BCLR (Bit 14) */
+#define RUSB2_D1FIFOCTR_BCLR_Msk (0x4000UL) /* BCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOCTR_FRDY_Pos (13UL) /* FRDY (Bit 13) */
+#define RUSB2_D1FIFOCTR_FRDY_Msk (0x2000UL) /* FRDY (Bitfield-Mask: 0x01) */
+#define RUSB2_D1FIFOCTR_DTLN_Pos (0UL) /* DTLN (Bit 0) */
+#define RUSB2_D1FIFOCTR_DTLN_Msk (0xfffUL) /* DTLN (Bitfield-Mask: 0xfff) */
+
+// INTENB0
+#define RUSB2_INTENB0_VBSE_Pos (15UL) /* VBSE (Bit 15) */
+#define RUSB2_INTENB0_VBSE_Msk (0x8000UL) /* VBSE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_RSME_Pos (14UL) /* RSME (Bit 14) */
+#define RUSB2_INTENB0_RSME_Msk (0x4000UL) /* RSME (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_SOFE_Pos (13UL) /* SOFE (Bit 13) */
+#define RUSB2_INTENB0_SOFE_Msk (0x2000UL) /* SOFE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_DVSE_Pos (12UL) /* DVSE (Bit 12) */
+#define RUSB2_INTENB0_DVSE_Msk (0x1000UL) /* DVSE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_CTRE_Pos (11UL) /* CTRE (Bit 11) */
+#define RUSB2_INTENB0_CTRE_Msk (0x800UL) /* CTRE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_BEMPE_Pos (10UL) /* BEMPE (Bit 10) */
+#define RUSB2_INTENB0_BEMPE_Msk (0x400UL) /* BEMPE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_NRDYE_Pos (9UL) /* NRDYE (Bit 9) */
+#define RUSB2_INTENB0_NRDYE_Msk (0x200UL) /* NRDYE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB0_BRDYE_Pos (8UL) /* BRDYE (Bit 8) */
+#define RUSB2_INTENB0_BRDYE_Msk (0x100UL) /* BRDYE (Bitfield-Mask: 0x01) */
+
+// INTENB1
+#define RUSB2_INTENB1_OVRCRE_Pos (15UL) /* OVRCRE (Bit 15) */
+#define RUSB2_INTENB1_OVRCRE_Msk (0x8000UL) /* OVRCRE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_BCHGE_Pos (14UL) /* BCHGE (Bit 14) */
+#define RUSB2_INTENB1_BCHGE_Msk (0x4000UL) /* BCHGE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_DTCHE_Pos (12UL) /* DTCHE (Bit 12) */
+#define RUSB2_INTENB1_DTCHE_Msk (0x1000UL) /* DTCHE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_ATTCHE_Pos (11UL) /* ATTCHE (Bit 11) */
+#define RUSB2_INTENB1_ATTCHE_Msk (0x800UL) /* ATTCHE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_L1RSMENDE_Pos (9UL) /*!< L1RSMENDE (Bit 9) */
+#define RUSB2_INTENB1_L1RSMENDE_Msk (0x200UL) /*!< L1RSMENDE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_LPMENDE_Pos (8UL) /*!< LPMENDE (Bit 8) */
+#define RUSB2_INTENB1_LPMENDE_Msk (0x100UL) /*!< LPMENDE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_EOFERRE_Pos (6UL) /* EOFERRE (Bit 6) */
+#define RUSB2_INTENB1_EOFERRE_Msk (0x40UL) /* EOFERRE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_SIGNE_Pos (5UL) /* SIGNE (Bit 5) */
+#define RUSB2_INTENB1_SIGNE_Msk (0x20UL) /* SIGNE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_SACKE_Pos (4UL) /* SACKE (Bit 4) */
+#define RUSB2_INTENB1_SACKE_Msk (0x10UL) /* SACKE (Bitfield-Mask: 0x01) */
+#define RUSB2_INTENB1_PDDETINTE0_Pos (0UL) /* PDDETINTE0 (Bit 0) */
+#define RUSB2_INTENB1_PDDETINTE0_Msk (0x1UL) /* PDDETINTE0 (Bitfield-Mask: 0x01) */
+
+// BRDYENB
+#define RUSB2_BRDYENB_PIPEBRDYE_Pos (0UL) /* PIPEBRDYE (Bit 0) */
+#define RUSB2_BRDYENB_PIPEBRDYE_Msk (0x1UL) /* PIPEBRDYE (Bitfield-Mask: 0x01) */
+
+// NRDYENB
+#define RUSB2_NRDYENB_PIPENRDYE_Pos (0UL) /* PIPENRDYE (Bit 0) */
+#define RUSB2_NRDYENB_PIPENRDYE_Msk (0x1UL) /* PIPENRDYE (Bitfield-Mask: 0x01) */
+
+// BEMPENB
+#define RUSB2_BEMPENB_PIPEBEMPE_Pos (0UL) /* PIPEBEMPE (Bit 0) */
+#define RUSB2_BEMPENB_PIPEBEMPE_Msk (0x1UL) /* PIPEBEMPE (Bitfield-Mask: 0x01) */
+
+// SOFCFG
+#define RUSB2_SOFCFG_TRNENSEL_Pos (8UL) /* TRNENSEL (Bit 8) */
+#define RUSB2_SOFCFG_TRNENSEL_Msk (0x100UL) /* TRNENSEL (Bitfield-Mask: 0x01) */
+#define RUSB2_SOFCFG_BRDYM_Pos (6UL) /* BRDYM (Bit 6) */
+#define RUSB2_SOFCFG_BRDYM_Msk (0x40UL) /* BRDYM (Bitfield-Mask: 0x01) */
+#define RUSB2_SOFCFG_INTL_Pos (5UL) /* INTL (Bit 5) */
+#define RUSB2_SOFCFG_INTL_Msk (0x20UL) /* INTL (Bitfield-Mask: 0x01) */
+#define RUSB2_SOFCFG_EDGESTS_Pos (4UL) /* EDGESTS (Bit 4) */
+#define RUSB2_SOFCFG_EDGESTS_Msk (0x10UL) /* EDGESTS (Bitfield-Mask: 0x01) */
+
+// PHYSET
+#define RUSB2_PHYSET_HSEB_Pos (15UL) /* HSEB (Bit 15) */
+#define RUSB2_PHYSET_HSEB_Msk (0x8000UL) /* HSEB (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSET_REPSTART_Pos (11UL) /* REPSTART (Bit 11) */
+#define RUSB2_PHYSET_REPSTART_Msk (0x800UL) /* REPSTART (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSET_REPSEL_Pos (8UL) /* REPSEL (Bit 8) */
+#define RUSB2_PHYSET_REPSEL_Msk (0x300UL) /* REPSEL (Bitfield-Mask: 0x03) */
+#define RUSB2_PHYSET_CLKSEL_Pos (4UL) /* CLKSEL (Bit 4) */
+#define RUSB2_PHYSET_CLKSEL_Msk (0x30UL) /* CLKSEL (Bitfield-Mask: 0x03) */
+#define RUSB2_PHYSET_CDPEN_Pos (3UL) /* CDPEN (Bit 3) */
+#define RUSB2_PHYSET_CDPEN_Msk (0x8UL) /* CDPEN (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSET_PLLRESET_Pos (1UL) /* PLLRESET (Bit 1) */
+#define RUSB2_PHYSET_PLLRESET_Msk (0x2UL) /* PLLRESET (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSET_DIRPD_Pos (0UL) /* DIRPD (Bit 0) */
+#define RUSB2_PHYSET_DIRPD_Msk (0x1UL) /* DIRPD (Bitfield-Mask: 0x01) */
+
+// INTSTS0
+#define RUSB2_INTSTS0_VBINT_Pos (15UL) /* VBINT (Bit 15) */
+#define RUSB2_INTSTS0_VBINT_Msk (0x8000UL) /* VBINT (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_RESM_Pos (14UL) /* RESM (Bit 14) */
+#define RUSB2_INTSTS0_RESM_Msk (0x4000UL) /* RESM (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_SOFR_Pos (13UL) /* SOFR (Bit 13) */
+#define RUSB2_INTSTS0_SOFR_Msk (0x2000UL) /* SOFR (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_DVST_Pos (12UL) /* DVST (Bit 12) */
+#define RUSB2_INTSTS0_DVST_Msk (0x1000UL) /* DVST (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_CTRT_Pos (11UL) /* CTRT (Bit 11) */
+#define RUSB2_INTSTS0_CTRT_Msk (0x800UL) /* CTRT (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_BEMP_Pos (10UL) /* BEMP (Bit 10) */
+#define RUSB2_INTSTS0_BEMP_Msk (0x400UL) /* BEMP (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_NRDY_Pos (9UL) /* NRDY (Bit 9) */
+#define RUSB2_INTSTS0_NRDY_Msk (0x200UL) /* NRDY (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_BRDY_Pos (8UL) /* BRDY (Bit 8) */
+#define RUSB2_INTSTS0_BRDY_Msk (0x100UL) /* BRDY (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_VBSTS_Pos (7UL) /* VBSTS (Bit 7) */
+#define RUSB2_INTSTS0_VBSTS_Msk (0x80UL) /* VBSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_DVSQ_Pos (4UL) /* DVSQ (Bit 4) */
+#define RUSB2_INTSTS0_DVSQ_Msk (0x70UL) /* DVSQ (Bitfield-Mask: 0x07) */
+#define RUSB2_INTSTS0_VALID_Pos (3UL) /* VALID (Bit 3) */
+#define RUSB2_INTSTS0_VALID_Msk (0x8UL) /* VALID (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS0_CTSQ_Pos (0UL) /* CTSQ (Bit 0) */
+#define RUSB2_INTSTS0_CTSQ_Msk (0x7UL) /* CTSQ (Bitfield-Mask: 0x07) */
+
+// INTSTS1
+#define RUSB2_INTSTS1_OVRCR_Pos (15UL) /* OVRCR (Bit 15) */
+#define RUSB2_INTSTS1_OVRCR_Msk (0x8000UL) /* OVRCR (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_BCHG_Pos (14UL) /* BCHG (Bit 14) */
+#define RUSB2_INTSTS1_BCHG_Msk (0x4000UL) /* BCHG (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_DTCH_Pos (12UL) /* DTCH (Bit 12) */
+#define RUSB2_INTSTS1_DTCH_Msk (0x1000UL) /* DTCH (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_ATTCH_Pos (11UL) /* ATTCH (Bit 11) */
+#define RUSB2_INTSTS1_ATTCH_Msk (0x800UL) /* ATTCH (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_L1RSMEND_Pos (9UL) /* L1RSMEND (Bit 9) */
+#define RUSB2_INTSTS1_L1RSMEND_Msk (0x200UL) /* L1RSMEND (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_LPMEND_Pos (8UL) /* LPMEND (Bit 8) */
+#define RUSB2_INTSTS1_LPMEND_Msk (0x100UL) /* LPMEND (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_EOFERR_Pos (6UL) /* EOFERR (Bit 6) */
+#define RUSB2_INTSTS1_EOFERR_Msk (0x40UL) /* EOFERR (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_SIGN_Pos (5UL) /* SIGN (Bit 5) */
+#define RUSB2_INTSTS1_SIGN_Msk (0x20UL) /* SIGN (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_SACK_Pos (4UL) /* SACK (Bit 4) */
+#define RUSB2_INTSTS1_SACK_Msk (0x10UL) /* SACK (Bitfield-Mask: 0x01) */
+#define RUSB2_INTSTS1_PDDETINT0_Pos (0UL) /* PDDETINT0 (Bit 0) */
+#define RUSB2_INTSTS1_PDDETINT0_Msk (0x1UL) /* PDDETINT0 (Bitfield-Mask: 0x01) */
+
+// BRDYSTS
+#define RUSB2_BRDYSTS_PIPEBRDY_Pos (0UL) /* PIPEBRDY (Bit 0) */
+#define RUSB2_BRDYSTS_PIPEBRDY_Msk (0x1UL) /* PIPEBRDY (Bitfield-Mask: 0x01) */
+
+// NRDYSTS
+#define RUSB2_NRDYSTS_PIPENRDY_Pos (0UL) /* PIPENRDY (Bit 0) */
+#define RUSB2_NRDYSTS_PIPENRDY_Msk (0x1UL) /* PIPENRDY (Bitfield-Mask: 0x01) */
+
+// BEMPSTS
+#define RUSB2_BEMPSTS_PIPEBEMP_Pos (0UL) /* PIPEBEMP (Bit 0) */
+#define RUSB2_BEMPSTS_PIPEBEMP_Msk (0x1UL) /* PIPEBEMP (Bitfield-Mask: 0x01) */
+
+// FRMNUM
+#define RUSB2_FRMNUM_OVRN_Pos (15UL) /* OVRN (Bit 15) */
+#define RUSB2_FRMNUM_OVRN_Msk (0x8000UL) /* OVRN (Bitfield-Mask: 0x01) */
+#define RUSB2_FRMNUM_CRCE_Pos (14UL) /* CRCE (Bit 14) */
+#define RUSB2_FRMNUM_CRCE_Msk (0x4000UL) /* CRCE (Bitfield-Mask: 0x01) */
+#define RUSB2_FRMNUM_FRNM_Pos (0UL) /* FRNM (Bit 0) */
+#define RUSB2_FRMNUM_FRNM_Msk (0x7ffUL) /* FRNM (Bitfield-Mask: 0x7ff) */
+
+// UFRMNUM
+#define RUSB2_UFRMNUM_DVCHG_Pos (15UL) /* DVCHG (Bit 15) */
+#define RUSB2_UFRMNUM_DVCHG_Msk (0x8000UL) /* DVCHG (Bitfield-Mask: 0x01) */
+#define RUSB2_UFRMNUM_UFRNM_Pos (0UL) /* UFRNM (Bit 0) */
+#define RUSB2_UFRMNUM_UFRNM_Msk (0x7UL) /* UFRNM (Bitfield-Mask: 0x07) */
+
+// USBADDR
+#define RUSB2_USBADDR_STSRECOV0_Pos (8UL) /* STSRECOV0 (Bit 8) */
+#define RUSB2_USBADDR_STSRECOV0_Msk (0x700UL) /* STSRECOV0 (Bitfield-Mask: 0x07) */
+#define RUSB2_USBADDR_USBADDR_Pos (0UL) /* USBADDR (Bit 0) */
+#define RUSB2_USBADDR_USBADDR_Msk (0x7fUL) /* USBADDR (Bitfield-Mask: 0x7f) */
+
+// USBREQ
+#define RUSB2_USBREQ_BREQUEST_Pos (8UL) /* BREQUEST (Bit 8) */
+#define RUSB2_USBREQ_BREQUEST_Msk (0xff00UL) /* BREQUEST (Bitfield-Mask: 0xff) */
+#define RUSB2_USBREQ_BMREQUESTTYPE_Pos (0UL) /* BMREQUESTTYPE (Bit 0) */
+#define RUSB2_USBREQ_BMREQUESTTYPE_Msk (0xffUL) /* BMREQUESTTYPE (Bitfield-Mask: 0xff) */
+
+// USBVAL
+#define RUSB2_USBVAL_WVALUE_Pos (0UL) /* WVALUE (Bit 0) */
+#define RUSB2_USBVAL_WVALUE_Msk (0xffffUL) /* WVALUE (Bitfield-Mask: 0xffff) */
+
+// USBINDX
+#define RUSB2_USBINDX_WINDEX_Pos (0UL) /* WINDEX (Bit 0) */
+#define RUSB2_USBINDX_WINDEX_Msk (0xffffUL) /* WINDEX (Bitfield-Mask: 0xffff) */
+
+// USBLENG
+#define RUSB2_USBLENG_WLENGTH_Pos (0UL) /* WLENGTH (Bit 0) */
+#define RUSB2_USBLENG_WLENGTH_Msk (0xffffUL) /* WLENGTH (Bitfield-Mask: 0xffff) */
+
+// DCPCFG
+#define RUSB2_DCPCFG_CNTMD_Pos (8UL) /* CNTMD (Bit 8) */
+#define RUSB2_DCPCFG_CNTMD_Msk (0x100UL) /* CNTMD (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCFG_SHTNAK_Pos (7UL) /* SHTNAK (Bit 7) */
+#define RUSB2_DCPCFG_SHTNAK_Msk (0x80UL) /* SHTNAK (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCFG_DIR_Pos (4UL) /* DIR (Bit 4) */
+#define RUSB2_DCPCFG_DIR_Msk (0x10UL) /* DIR (Bitfield-Mask: 0x01) */
+
+// DCPMAXP
+#define RUSB2_DCPMAXP_DEVSEL_Pos (12UL) /* DEVSEL (Bit 12) */
+#define RUSB2_DCPMAXP_DEVSEL_Msk (0xf000UL) /* DEVSEL (Bitfield-Mask: 0x0f) */
+#define RUSB2_DCPMAXP_MXPS_Pos (0UL) /* MXPS (Bit 0) */
+#define RUSB2_DCPMAXP_MXPS_Msk (0x7fUL) /* MXPS (Bitfield-Mask: 0x7f) */
+
+// DCPCTR
+#define RUSB2_DCPCTR_BSTS_Pos (15UL) /* BSTS (Bit 15) */
+#define RUSB2_DCPCTR_BSTS_Msk (0x8000UL) /* BSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_SUREQ_Pos (14UL) /* SUREQ (Bit 14) */
+#define RUSB2_DCPCTR_SUREQ_Msk (0x4000UL) /* SUREQ (Bitfield-Mask: 0x01) */
+#define R_USB_HS0_DCPCTR_CSCLR_Pos (13UL) /*!< CSCLR (Bit 13) */
+#define RUSB2_DCPCTR_CSCLR_Msk (0x2000UL) /*!< CSCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_CSSTS_Pos (12UL) /*!< CSSTS (Bit 12) */
+#define RUSB2_DCPCTR_CSSTS_Msk (0x1000UL) /*!< CSSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_SUREQCLR_Pos (11UL) /* SUREQCLR (Bit 11) */
+#define RUSB2_DCPCTR_SUREQCLR_Msk (0x800UL) /* SUREQCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_SQCLR_Pos (8UL) /* SQCLR (Bit 8) */
+#define RUSB2_DCPCTR_SQCLR_Msk (0x100UL) /* SQCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_SQSET_Pos (7UL) /* SQSET (Bit 7) */
+#define RUSB2_DCPCTR_SQSET_Msk (0x80UL) /* SQSET (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_SQMON_Pos (6UL) /* SQMON (Bit 6) */
+#define RUSB2_DCPCTR_SQMON_Msk (0x40UL) /* SQMON (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_PBUSY_Pos (5UL) /* PBUSY (Bit 5) */
+#define RUSB2_DCPCTR_PBUSY_Msk (0x20UL) /* PBUSY (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_CCPL_Pos (2UL) /* CCPL (Bit 2) */
+#define RUSB2_DCPCTR_CCPL_Msk (0x4UL) /* CCPL (Bitfield-Mask: 0x01) */
+#define RUSB2_DCPCTR_PID_Pos (0UL) /* PID (Bit 0) */
+#define RUSB2_DCPCTR_PID_Msk (0x3UL) /* PID (Bitfield-Mask: 0x03) */
+
+// PIPESEL
+#define RUSB2_PIPESEL_PIPESEL_Pos (0UL) /* PIPESEL (Bit 0) */
+#define RUSB2_PIPESEL_PIPESEL_Msk (0xfUL) /* PIPESEL (Bitfield-Mask: 0x0f) */
+
+// PIPECFG
+#define RUSB2_PIPECFG_TYPE_Pos (14UL) /* TYPE (Bit 14) */
+#define RUSB2_PIPECFG_TYPE_Msk (0xc000UL) /* TYPE (Bitfield-Mask: 0x03) */
+#define RUSB2_PIPECFG_BFRE_Pos (10UL) /* BFRE (Bit 10) */
+#define RUSB2_PIPECFG_BFRE_Msk (0x400UL) /* BFRE (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPECFG_DBLB_Pos (9UL) /* DBLB (Bit 9) */
+#define RUSB2_PIPECFG_DBLB_Msk (0x200UL) /* DBLB (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPECFG_CNTMD_Pos (8UL) /*!< CNTMD (Bit 8) */
+#define RUSB2_PIPECFG_CNTMD_Msk (0x100UL) /*!< CNTMD (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPECFG_SHTNAK_Pos (7UL) /* SHTNAK (Bit 7) */
+#define RUSB2_PIPECFG_SHTNAK_Msk (0x80UL) /* SHTNAK (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPECFG_DIR_Pos (4UL) /* DIR (Bit 4) */
+#define RUSB2_PIPECFG_DIR_Msk (0x10UL) /* DIR (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPECFG_EPNUM_Pos (0UL) /* EPNUM (Bit 0) */
+#define RUSB2_PIPECFG_EPNUM_Msk (0xfUL) /* EPNUM (Bitfield-Mask: 0x0f) */
+
+// PIPEBUF
+#define RUSB2_PIPEBUF_BUFSIZE_Pos (10UL) /*!< BUFSIZE (Bit 10) */
+#define RUSB2_PIPEBUF_BUFSIZE_Msk (0x7c00UL) /*!< BUFSIZE (Bitfield-Mask: 0x1f) */
+#define RUSB2_PIPEBUF_BUFNMB_Pos (0UL) /*!< BUFNMB (Bit 0) */
+#define RUSB2_PIPEBUF_BUFNMB_Msk (0xffUL) /*!< BUFNMB (Bitfield-Mask: 0xff) */
+
+// PIPEMAXP
+#define RUSB2_PIPEMAXP_DEVSEL_Pos (12UL) /* DEVSEL (Bit 12) */
+#define RUSB2_PIPEMAXP_DEVSEL_Msk (0xf000UL) /* DEVSEL (Bitfield-Mask: 0x0f) */
+#define RUSB2_PIPEMAXP_MXPS_Pos (0UL) /* MXPS (Bit 0) */
+#define RUSB2_PIPEMAXP_MXPS_Msk (0x1ffUL) /* MXPS (Bitfield-Mask: 0x1ff) */
+
+// PIPEPERI
+#define RUSB2_PIPEPERI_IFIS_Pos (12UL) /* IFIS (Bit 12) */
+#define RUSB2_PIPEPERI_IFIS_Msk (0x1000UL) /* IFIS (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPEPERI_IITV_Pos (0UL) /* IITV (Bit 0) */
+#define RUSB2_PIPEPERI_IITV_Msk (0x7UL) /* IITV (Bitfield-Mask: 0x07) */
+
+// PIPE_CTR
+#define RUSB2_PIPE_CTR_BSTS_Pos (15UL) /* BSTS (Bit 15) */
+#define RUSB2_PIPE_CTR_BSTS_Msk (0x8000UL) /* BSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_INBUFM_Pos (14UL) /* INBUFM (Bit 14) */
+#define RUSB2_PIPE_CTR_INBUFM_Msk (0x4000UL) /* INBUFM (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_CSCLR_Pos (13UL) /* CSCLR (Bit 13) */
+#define RUSB2_PIPE_CTR_CSCLR_Msk (0x2000UL) /* CSCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_CSSTS_Pos (12UL) /* CSSTS (Bit 12) */
+#define RUSB2_PIPE_CTR_CSSTS_Msk (0x1000UL) /* CSSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_ATREPM_Pos (10UL) /* ATREPM (Bit 10) */
+#define RUSB2_PIPE_CTR_ATREPM_Msk (0x400UL) /* ATREPM (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_ACLRM_Pos (9UL) /* ACLRM (Bit 9) */
+#define RUSB2_PIPE_CTR_ACLRM_Msk (0x200UL) /* ACLRM (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_SQCLR_Pos (8UL) /* SQCLR (Bit 8) */
+#define RUSB2_PIPE_CTR_SQCLR_Msk (0x100UL) /* SQCLR (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_SQSET_Pos (7UL) /* SQSET (Bit 7) */
+#define RUSB2_PIPE_CTR_SQSET_Msk (0x80UL) /* SQSET (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_SQMON_Pos (6UL) /* SQMON (Bit 6) */
+#define RUSB2_PIPE_CTR_SQMON_Msk (0x40UL) /* SQMON (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_PBUSY_Pos (5UL) /* PBUSY (Bit 5) */
+#define RUSB2_PIPE_CTR_PBUSY_Msk (0x20UL) /* PBUSY (Bitfield-Mask: 0x01) */
+#define RUSB2_PIPE_CTR_PID_Pos (0UL) /* PID (Bit 0) */
+#define RUSB2_PIPE_CTR_PID_Msk (0x3UL) /* PID (Bitfield-Mask: 0x03) */
+
+// DEVADD
+#define RUSB2_DEVADD_UPPHUB_Pos (11UL) /* UPPHUB (Bit 11) */
+#define RUSB2_DEVADD_UPPHUB_Msk (0x7800UL) /* UPPHUB (Bitfield-Mask: 0x0f) */
+#define RUSB2_DEVADD_HUBPORT_Pos (8UL) /* HUBPORT (Bit 8) */
+#define RUSB2_DEVADD_HUBPORT_Msk (0x700UL) /* HUBPORT (Bitfield-Mask: 0x07) */
+#define RUSB2_DEVADD_USBSPD_Pos (6UL) /* USBSPD (Bit 6) */
+#define RUSB2_DEVADD_USBSPD_Msk (0xc0UL) /* USBSPD (Bitfield-Mask: 0x03) */
+
+// USBBCCTRL0
+#define RUSB2_USBBCCTRL0_PDDETSTS0_Pos (9UL) /* PDDETSTS0 (Bit 9) */
+#define RUSB2_USBBCCTRL0_PDDETSTS0_Msk (0x200UL) /* PDDETSTS0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_CHGDETSTS0_Pos (8UL) /* CHGDETSTS0 (Bit 8) */
+#define RUSB2_USBBCCTRL0_CHGDETSTS0_Msk (0x100UL) /* CHGDETSTS0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_BATCHGE0_Pos (7UL) /* BATCHGE0 (Bit 7) */
+#define RUSB2_USBBCCTRL0_BATCHGE0_Msk (0x80UL) /* BATCHGE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_VDMSRCE0_Pos (5UL) /* VDMSRCE0 (Bit 5) */
+#define RUSB2_USBBCCTRL0_VDMSRCE0_Msk (0x20UL) /* VDMSRCE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_IDPSINKE0_Pos (4UL) /* IDPSINKE0 (Bit 4) */
+#define RUSB2_USBBCCTRL0_IDPSINKE0_Msk (0x10UL) /* IDPSINKE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_VDPSRCE0_Pos (3UL) /* VDPSRCE0 (Bit 3) */
+#define RUSB2_USBBCCTRL0_VDPSRCE0_Msk (0x8UL) /* VDPSRCE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_IDMSINKE0_Pos (2UL) /* IDMSINKE0 (Bit 2) */
+#define RUSB2_USBBCCTRL0_IDMSINKE0_Msk (0x4UL) /* IDMSINKE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_IDPSRCE0_Pos (1UL) /* IDPSRCE0 (Bit 1) */
+#define RUSB2_USBBCCTRL0_IDPSRCE0_Msk (0x2UL) /* IDPSRCE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_USBBCCTRL0_RPDME0_Pos (0UL) /* RPDME0 (Bit 0) */
+#define RUSB2_USBBCCTRL0_RPDME0_Msk (0x1UL) /* RPDME0 (Bitfield-Mask: 0x01) */
+
+// UCKSEL
+#define RUSB2_UCKSEL_UCKSELC_Pos (0UL) /* UCKSELC (Bit 0) */
+#define RUSB2_UCKSEL_UCKSELC_Msk (0x1UL) /* UCKSELC (Bitfield-Mask: 0x01) */
+
+// USBMC
+#define RUSB2_USBMC_VDCEN_Pos (7UL) /* VDCEN (Bit 7) */
+#define RUSB2_USBMC_VDCEN_Msk (0x80UL) /* VDCEN (Bitfield-Mask: 0x01) */
+#define RUSB2_USBMC_VDDUSBE_Pos (0UL) /* VDDUSBE (Bit 0) */
+#define RUSB2_USBMC_VDDUSBE_Msk (0x1UL) /* VDDUSBE (Bitfield-Mask: 0x01) */
+
+// PHYSLEW
+#define RUSB2_PHYSLEW_SLEWF01_Pos (3UL) /* SLEWF01 (Bit 3) */
+#define RUSB2_PHYSLEW_SLEWF01_Msk (0x8UL) /* SLEWF01 (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSLEW_SLEWF00_Pos (2UL) /* SLEWF00 (Bit 2) */
+#define RUSB2_PHYSLEW_SLEWF00_Msk (0x4UL) /* SLEWF00 (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSLEW_SLEWR01_Pos (1UL) /* SLEWR01 (Bit 1) */
+#define RUSB2_PHYSLEW_SLEWR01_Msk (0x2UL) /* SLEWR01 (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYSLEW_SLEWR00_Pos (0UL) /* SLEWR00 (Bit 0) */
+#define RUSB2_PHYSLEW_SLEWR00_Msk (0x1UL) /* SLEWR00 (Bitfield-Mask: 0x01) */
+
+// LPCTRL
+#define RUSB2_LPCTRL_HWUPM_Pos (7UL) /* HWUPM (Bit 7) */
+#define RUSB2_LPCTRL_HWUPM_Msk (0x80UL) /* HWUPM (Bitfield-Mask: 0x01) */
+
+// LPSTS
+#define RUSB2_LPSTS_SUSPENDM_Pos (14UL) /* SUSPENDM (Bit 14) */
+#define RUSB2_LPSTS_SUSPENDM_Msk (0x4000UL) /* SUSPENDM (Bitfield-Mask: 0x01) */
+
+// BCCTRL
+#define RUSB2_BCCTRL_PDDETSTS_Pos (9UL) /* PDDETSTS (Bit 9) */
+#define RUSB2_BCCTRL_PDDETSTS_Msk (0x200UL) /* PDDETSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_CHGDETSTS_Pos (8UL) /* CHGDETSTS (Bit 8) */
+#define RUSB2_BCCTRL_CHGDETSTS_Msk (0x100UL) /* CHGDETSTS (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_DCPMODE_Pos (5UL) /* DCPMODE (Bit 5) */
+#define RUSB2_BCCTRL_DCPMODE_Msk (0x20UL) /* DCPMODE (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_VDMSRCE_Pos (4UL) /* VDMSRCE (Bit 4) */
+#define RUSB2_BCCTRL_VDMSRCE_Msk (0x10UL) /* VDMSRCE (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_IDPSINKE_Pos (3UL) /* IDPSINKE (Bit 3) */
+#define RUSB2_BCCTRL_IDPSINKE_Msk (0x8UL) /* IDPSINKE (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_VDPSRCE_Pos (2UL) /* VDPSRCE (Bit 2) */
+#define RUSB2_BCCTRL_VDPSRCE_Msk (0x4UL) /* VDPSRCE (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_IDMSINKE_Pos (1UL) /* IDMSINKE (Bit 1) */
+#define RUSB2_BCCTRL_IDMSINKE_Msk (0x2UL) /* IDMSINKE (Bitfield-Mask: 0x01) */
+#define RUSB2_BCCTRL_IDPSRCE_Pos (0UL) /* IDPSRCE (Bit 0) */
+#define RUSB2_BCCTRL_IDPSRCE_Msk (0x1UL) /* IDPSRCE (Bitfield-Mask: 0x01) */
+
+// PL1CTRL1
+#define RUSB2_PL1CTRL1_L1EXTMD_Pos (14UL) /* L1EXTMD (Bit 14) */
+#define RUSB2_PL1CTRL1_L1EXTMD_Msk (0x4000UL) /* L1EXTMD (Bitfield-Mask: 0x01) */
+#define RUSB2_PL1CTRL1_HIRDTHR_Pos (8UL) /* HIRDTHR (Bit 8) */
+#define RUSB2_PL1CTRL1_HIRDTHR_Msk (0xf00UL) /* HIRDTHR (Bitfield-Mask: 0x0f) */
+#define RUSB2_PL1CTRL1_DVSQ_Pos (4UL) /* DVSQ (Bit 4) */
+#define RUSB2_PL1CTRL1_DVSQ_Msk (0xf0UL) /* DVSQ (Bitfield-Mask: 0x0f) */
+#define RUSB2_PL1CTRL1_L1NEGOMD_Pos (3UL) /* L1NEGOMD (Bit 3) */
+#define RUSB2_PL1CTRL1_L1NEGOMD_Msk (0x8UL) /* L1NEGOMD (Bitfield-Mask: 0x01) */
+#define RUSB2_PL1CTRL1_L1RESPMD_Pos (1UL) /* L1RESPMD (Bit 1) */
+#define RUSB2_PL1CTRL1_L1RESPMD_Msk (0x6UL) /* L1RESPMD (Bitfield-Mask: 0x03) */
+#define RUSB2_PL1CTRL1_L1RESPEN_Pos (0UL) /* L1RESPEN (Bit 0) */
+#define RUSB2_PL1CTRL1_L1RESPEN_Msk (0x1UL) /* L1RESPEN (Bitfield-Mask: 0x01) */
+
+// PL1CTRL2
+#define RUSB2_PL1CTRL2_RWEMON_Pos (12UL) /* RWEMON (Bit 12) */
+#define RUSB2_PL1CTRL2_RWEMON_Msk (0x1000UL) /* RWEMON (Bitfield-Mask: 0x01) */
+#define RUSB2_PL1CTRL2_HIRDMON_Pos (8UL) /* HIRDMON (Bit 8) */
+#define RUSB2_PL1CTRL2_HIRDMON_Msk (0xf00UL) /* HIRDMON (Bitfield-Mask: 0x0f) */
+
+// HL1CTRL1
+#define RUSB2_HL1CTRL1_L1STATUS_Pos (1UL) /* L1STATUS (Bit 1) */
+#define RUSB2_HL1CTRL1_L1STATUS_Msk (0x6UL) /* L1STATUS (Bitfield-Mask: 0x03) */
+#define RUSB2_HL1CTRL1_L1REQ_Pos (0UL) /* L1REQ (Bit 0) */
+#define RUSB2_HL1CTRL1_L1REQ_Msk (0x1UL) /* L1REQ (Bitfield-Mask: 0x01) */
+
+// HL1CTRL2
+#define RUSB2_HL1CTRL2_BESL_Pos (15UL) /* BESL (Bit 15) */
+#define RUSB2_HL1CTRL2_BESL_Msk (0x8000UL) /* BESL (Bitfield-Mask: 0x01) */
+#define RUSB2_HL1CTRL2_L1RWE_Pos (12UL) /* L1RWE (Bit 12) */
+#define RUSB2_HL1CTRL2_L1RWE_Msk (0x1000UL) /* L1RWE (Bitfield-Mask: 0x01) */
+#define RUSB2_HL1CTRL2_HIRD_Pos (8UL) /* HIRD (Bit 8) */
+#define RUSB2_HL1CTRL2_HIRD_Msk (0xf00UL) /* HIRD (Bitfield-Mask: 0x0f) */
+#define RUSB2_HL1CTRL2_L1ADDR_Pos (0UL) /* L1ADDR (Bit 0) */
+#define RUSB2_HL1CTRL2_L1ADDR_Msk (0xfUL) /* L1ADDR (Bitfield-Mask: 0x0f) */
+
+// PHYTRIM1
+#define RUSB2_PHYTRIM1_IMPOFFSET_Pos (12UL) /*!< IMPOFFSET (Bit 12) */
+#define RUSB2_PHYTRIM1_IMPOFFSET_Msk (0x7000UL) /*!< IMPOFFSET (Bitfield-Mask: 0x07) */
+#define RUSB2_PHYTRIM1_HSIUP_Pos (8UL) /*!< HSIUP (Bit 8) */
+#define RUSB2_PHYTRIM1_HSIUP_Msk (0xf00UL) /*!< HSIUP (Bitfield-Mask: 0x0f) */
+#define RUSB2_PHYTRIM1_PCOMPENB_Pos (7UL) /*!< PCOMPENB (Bit 7) */
+#define RUSB2_PHYTRIM1_PCOMPENB_Msk (0x80UL) /*!< PCOMPENB (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYTRIM1_DFALL_Pos (2UL) /*!< DFALL (Bit 2) */
+#define RUSB2_PHYTRIM1_DFALL_Msk (0xcUL) /*!< DFALL (Bitfield-Mask: 0x03) */
+#define RUSB2_PHYTRIM1_DRISE_Pos (0UL) /*!< DRISE (Bit 0) */
+#define RUSB2_PHYTRIM1_DRISE_Msk (0x3UL) /*!< DRISE (Bitfield-Mask: 0x03) */
+
+// PHYTRIM2
+#define RUSB2_PHYTRIM2_DIS_Pos (12UL) /*!< DIS (Bit 12) */
+#define RUSB2_PHYTRIM2_DIS_Msk (0x7000UL) /*!< DIS (Bitfield-Mask: 0x07) */
+#define RUSB2_PHYTRIM2_PDR_Pos (8UL) /*!< PDR (Bit 8) */
+#define RUSB2_PHYTRIM2_PDR_Msk (0x300UL) /*!< PDR (Bitfield-Mask: 0x03) */
+#define RUSB2_PHYTRIM2_HSRXENMO_Pos (7UL) /*!< HSRXENMO (Bit 7) */
+#define RUSB2_PHYTRIM2_HSRXENMO_Msk (0x80UL) /*!< HSRXENMO (Bitfield-Mask: 0x01) */
+#define RUSB2_PHYTRIM2_SQU_Pos (0UL) /*!< SQU (Bit 0) */
+#define RUSB2_PHYTRIM2_SQU_Msk (0xfUL) /*!< SQU (Bitfield-Mask: 0x0f) */
+
+// DPUSR0R
+#define RUSB2_DPUSR0R_DVBSTSHM_Pos (23UL) /* DVBSTSHM (Bit 23) */
+#define RUSB2_DPUSR0R_DVBSTSHM_Msk (0x800000UL) /* DVBSTSHM (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_DOVCBHM_Pos (21UL) /* DOVCBHM (Bit 21) */
+#define RUSB2_DPUSR0R_DOVCBHM_Msk (0x200000UL) /* DOVCBHM (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_DOVCAHM_Pos (20UL) /* DOVCAHM (Bit 20) */
+#define RUSB2_DPUSR0R_DOVCAHM_Msk (0x100000UL) /* DOVCAHM (Bitfield-Mask: 0x01) */
+
+// DPUSR1R
+#define RUSB2_DPUSR1R_DVBSTSH_Pos (23UL) /* DVBSTSH (Bit 23) */
+#define RUSB2_DPUSR1R_DVBSTSH_Msk (0x800000UL) /* DVBSTSH (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_DOVCBH_Pos (21UL) /* DOVCBH (Bit 21) */
+#define RUSB2_DPUSR1R_DOVCBH_Msk (0x200000UL) /* DOVCBH (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_DOVCAH_Pos (20UL) /* DOVCAH (Bit 20) */
+#define RUSB2_DPUSR1R_DOVCAH_Msk (0x100000UL) /* DOVCAH (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_DVBSTSHE_Pos (7UL) /* DVBSTSHE (Bit 7) */
+#define RUSB2_DPUSR1R_DVBSTSHE_Msk (0x80UL) /* DVBSTSHE (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_DOVCBHE_Pos (5UL) /* DOVCBHE (Bit 5) */
+#define RUSB2_DPUSR1R_DOVCBHE_Msk (0x20UL) /* DOVCBHE (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_DOVCAHE_Pos (4UL) /* DOVCAHE (Bit 4) */
+#define RUSB2_DPUSR1R_DOVCAHE_Msk (0x10UL) /* DOVCAHE (Bitfield-Mask: 0x01) */
+
+// DPUSR2R
+#define RUSB2_DPUSR2R_DMINTE_Pos (9UL) /* DMINTE (Bit 9) */
+#define RUSB2_DPUSR2R_DMINTE_Msk (0x200UL) /* DMINTE (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR2R_DPINTE_Pos (8UL) /* DPINTE (Bit 8) */
+#define RUSB2_DPUSR2R_DPINTE_Msk (0x100UL) /* DPINTE (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR2R_DMVAL_Pos (5UL) /* DMVAL (Bit 5) */
+#define RUSB2_DPUSR2R_DMVAL_Msk (0x20UL) /* DMVAL (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR2R_DPVAL_Pos (4UL) /* DPVAL (Bit 4) */
+#define RUSB2_DPUSR2R_DPVAL_Msk (0x10UL) /* DPVAL (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR2R_DMINT_Pos (1UL) /* DMINT (Bit 1) */
+#define RUSB2_DPUSR2R_DMINT_Msk (0x2UL) /* DMINT (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR2R_DPINT_Pos (0UL) /* DPINT (Bit 0) */
+#define RUSB2_DPUSR2R_DPINT_Msk (0x1UL) /* DPINT (Bitfield-Mask: 0x01) */
+
+// DPUSRCR
+#define RUSB2_DPUSRCR_FIXPHYPD_Pos (1UL) /* FIXPHYPD (Bit 1) */
+#define RUSB2_DPUSRCR_FIXPHYPD_Msk (0x2UL) /* FIXPHYPD (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSRCR_FIXPHY_Pos (0UL) /* FIXPHY (Bit 0) */
+#define RUSB2_DPUSRCR_FIXPHY_Msk (0x1UL) /* FIXPHY (Bitfield-Mask: 0x01) */
+
+// DPUSR0R_FS
+#define RUSB2_DPUSR0R_FS_DVBSTS0_Pos (23UL) /* DVBSTS0 (Bit 23) */
+#define RUSB2_DPUSR0R_FS_DVBSTS0_Msk (0x800000UL) /* DVBSTS0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_DOVCB0_Pos (21UL) /* DOVCB0 (Bit 21) */
+#define RUSB2_DPUSR0R_FS_DOVCB0_Msk (0x200000UL) /* DOVCB0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_DOVCA0_Pos (20UL) /* DOVCA0 (Bit 20) */
+#define RUSB2_DPUSR0R_FS_DOVCA0_Msk (0x100000UL) /* DOVCA0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_DM0_Pos (17UL) /* DM0 (Bit 17) */
+#define RUSB2_DPUSR0R_FS_DM0_Msk (0x20000UL) /* DM0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_DP0_Pos (16UL) /* DP0 (Bit 16) */
+#define RUSB2_DPUSR0R_FS_DP0_Msk (0x10000UL) /* DP0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_FIXPHY0_Pos (4UL) /* FIXPHY0 (Bit 4) */
+#define RUSB2_DPUSR0R_FS_FIXPHY0_Msk (0x10UL) /* FIXPHY0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_DRPD0_Pos (3UL) /* DRPD0 (Bit 3) */
+#define RUSB2_DPUSR0R_FS_DRPD0_Msk (0x8UL) /* DRPD0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_RPUE0_Pos (1UL) /* RPUE0 (Bit 1) */
+#define RUSB2_DPUSR0R_FS_RPUE0_Msk (0x2UL) /* RPUE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR0R_FS_SRPC0_Pos (0UL) /* SRPC0 (Bit 0) */
+#define RUSB2_DPUSR0R_FS_SRPC0_Msk (0x1UL) /* SRPC0 (Bitfield-Mask: 0x01) */
+
+// DPUSR1R_FS
+#define RUSB2_DPUSR1R_FS_DVBINT0_Pos (23UL) /* DVBINT0 (Bit 23) */
+#define RUSB2_DPUSR1R_FS_DVBINT0_Msk (0x800000UL) /* DVBINT0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DOVRCRB0_Pos (21UL) /* DOVRCRB0 (Bit 21) */
+#define RUSB2_DPUSR1R_FS_DOVRCRB0_Msk (0x200000UL) /* DOVRCRB0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DOVRCRA0_Pos (20UL) /* DOVRCRA0 (Bit 20) */
+#define RUSB2_DPUSR1R_FS_DOVRCRA0_Msk (0x100000UL) /* DOVRCRA0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DMINT0_Pos (17UL) /* DMINT0 (Bit 17) */
+#define RUSB2_DPUSR1R_FS_DMINT0_Msk (0x20000UL) /* DMINT0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DPINT0_Pos (16UL) /* DPINT0 (Bit 16) */
+#define RUSB2_DPUSR1R_FS_DPINT0_Msk (0x10000UL) /* DPINT0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DVBSE0_Pos (7UL) /* DVBSE0 (Bit 7) */
+#define RUSB2_DPUSR1R_FS_DVBSE0_Msk (0x80UL) /* DVBSE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DOVRCRBE0_Pos (5UL) /* DOVRCRBE0 (Bit 5) */
+#define RUSB2_DPUSR1R_FS_DOVRCRBE0_Msk (0x20UL) /* DOVRCRBE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DOVRCRAE0_Pos (4UL) /* DOVRCRAE0 (Bit 4) */
+#define RUSB2_DPUSR1R_FS_DOVRCRAE0_Msk (0x10UL) /* DOVRCRAE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DMINTE0_Pos (1UL) /* DMINTE0 (Bit 1) */
+#define RUSB2_DPUSR1R_FS_DMINTE0_Msk (0x2UL) /* DMINTE0 (Bitfield-Mask: 0x01) */
+#define RUSB2_DPUSR1R_FS_DPINTE0_Pos (0UL) /* DPINTE0 (Bit 0) */
+#define RUSB2_DPUSR1R_FS_DPINTE0_Msk (0x1UL) /* DPINTE0 (Bitfield-Mask: 0x01) */
+
+/*--------------------------------------------------------------------*/
+/* Register Bit Utils */
+/*--------------------------------------------------------------------*/
+#define RUSB2_PIPE_CTR_PID_NAK (0U << RUSB2_PIPE_CTR_PID_Pos) /* NAK response */
+#define RUSB2_PIPE_CTR_PID_BUF (1U << RUSB2_PIPE_CTR_PID_Pos) /* BUF response (depends buffer state) */
+#define RUSB2_PIPE_CTR_PID_STALL (2U << RUSB2_PIPE_CTR_PID_Pos) /* STALL response */
+#define RUSB2_PIPE_CTR_PID_STALL2 (3U << RUSB2_PIPE_CTR_PID_Pos) /* Also STALL response */
+
+#define RUSB2_DVSTCTR0_RHST_LS (1U << RUSB2_DVSTCTR0_RHST_Pos) /* Low-speed connection */
+#define RUSB2_DVSTCTR0_RHST_FS (2U << RUSB2_DVSTCTR0_RHST_Pos) /* Full-speed connection */
+#define RUSB2_DVSTCTR0_RHST_HS (3U << RUSB2_DVSTCTR0_RHST_Pos) /* Full-speed connection */
+
+#define RUSB2_DEVADD_USBSPD_LS (1U << RUSB2_DEVADD_USBSPD_Pos) /* Target Device Low-speed */
+#define RUSB2_DEVADD_USBSPD_FS (2U << RUSB2_DEVADD_USBSPD_Pos) /* Target Device Full-speed */
+
+#define RUSB2_CFIFOSEL_ISEL_WRITE (1U << RUSB2_CFIFOSEL_ISEL_Pos) /* FIFO write AKA TX*/
+
+#define RUSB2_FIFOSEL_BIGEND (1U << RUSB2_CFIFOSEL_BIGEND_Pos) /* FIFO Big Endian */
+#define RUSB2_FIFOSEL_MBW_8BIT (0U << RUSB2_CFIFOSEL_MBW_Pos) /* 8-bit width */
+#define RUSB2_FIFOSEL_MBW_16BIT (1U << RUSB2_CFIFOSEL_MBW_Pos) /* 16-bit width */
+#define RUSB2_FIFOSEL_MBW_32BIT (2U << RUSB2_CFIFOSEL_MBW_Pos) /* 32-bit width */
+
+#define RUSB2_INTSTS0_CTSQ_CTRL_RDATA (1U << RUSB2_INTSTS0_CTSQ_Pos)
+
+#define RUSB2_INTSTS0_DVSQ_STATE_DEF (1U << RUSB2_INTSTS0_DVSQ_Pos) /* Default state */
+#define RUSB2_INTSTS0_DVSQ_STATE_ADDR (2U << RUSB2_INTSTS0_DVSQ_Pos) /* Address state */
+#define RUSB2_INTSTS0_DVSQ_STATE_SUSP0 (4U << RUSB2_INTSTS0_DVSQ_Pos) /* Suspend state */
+#define RUSB2_INTSTS0_DVSQ_STATE_SUSP1 (5U << RUSB2_INTSTS0_DVSQ_Pos) /* Suspend state */
+#define RUSB2_INTSTS0_DVSQ_STATE_SUSP2 (6U << RUSB2_INTSTS0_DVSQ_Pos) /* Suspend state */
+#define RUSB2_INTSTS0_DVSQ_STATE_SUSP3 (7U << RUSB2_INTSTS0_DVSQ_Pos) /* Suspend state */
+
+#define RUSB2_PIPECFG_TYPE_BULK (1U << RUSB2_PIPECFG_TYPE_Pos)
+#define RUSB2_PIPECFG_TYPE_INT (2U << RUSB2_PIPECFG_TYPE_Pos)
+#define RUSB2_PIPECFG_TYPE_ISO (3U << RUSB2_PIPECFG_TYPE_Pos)
+
+//--------------------------------------------------------------------+
+// Static Assert
+//--------------------------------------------------------------------+
+
+TU_VERIFY_STATIC(sizeof(RUSB2_PIPE_TR_t) == 4, "incorrect size");
+TU_VERIFY_STATIC(sizeof(rusb2_reg_t) == 1032, "incorrect size");
+
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, SYSCFG ) == 0x0000, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BUSWAIT ) == 0x0002, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, SYSSTS0 ) == 0x0004, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PLLSTA ) == 0x0006, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DVSTCTR0 ) == 0x0008, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, TESTMODE ) == 0x000C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, CFIFO ) == 0x0014, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D0FIFO ) == 0x0018, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D1FIFO ) == 0x001C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, CFIFOSEL ) == 0x0020, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, CFIFOCTR ) == 0x0022, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D0FIFOSEL ) == 0x0028, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D0FIFOCTR ) == 0x002A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D1FIFOSEL ) == 0x002C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, D1FIFOCTR ) == 0x002E, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, INTENB0 ) == 0x0030, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, INTENB1 ) == 0x0032, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BRDYENB ) == 0x0036, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, NRDYENB ) == 0x0038, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BEMPENB ) == 0x003A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, SOFCFG ) == 0x003C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PHYSET ) == 0x003E, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, INTSTS0 ) == 0x0040, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, INTSTS1 ) == 0x0042, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BRDYSTS ) == 0x0046, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, NRDYSTS ) == 0x0048, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BEMPSTS ) == 0x004A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, FRMNUM ) == 0x004C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, UFRMNUM ) == 0x004E, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBADDR ) == 0x0050, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBREQ ) == 0x0054, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBVAL ) == 0x0056, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBINDX ) == 0x0058, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBLENG ) == 0x005A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DCPCFG ) == 0x005C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DCPMAXP ) == 0x005E, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DCPCTR ) == 0x0060, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPESEL ) == 0x0064, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPECFG ) == 0x0068, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPEBUF ) == 0x006A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPEMAXP ) == 0x006C, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPEPERI ) == 0x006E, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPE_CTR ) == 0x0070, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PIPE_TR ) == 0x0090, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBBCCTRL0 ) == 0x00B0, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, UCKSEL ) == 0x00C4, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, USBMC ) == 0x00CC, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DEVADD ) == 0x00D0, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PHYSLEW ) == 0x00F0, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, LPCTRL ) == 0x0100, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, LPSTS ) == 0x0102, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, BCCTRL ) == 0x0140, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PL1CTRL1 ) == 0x0144, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PL1CTRL2 ) == 0x0146, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, HL1CTRL1 ) == 0x0148, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, HL1CTRL2 ) == 0x014A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PHYTRIM1 ) == 0x0150, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, PHYTRIM2 ) == 0x0152, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR0R ) == 0x0160, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR1R ) == 0x0164, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR2R ) == 0x0168, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSRCR ) == 0x016A, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR0R_FS ) == 0x0400, "incorrect offset");
+TU_VERIFY_STATIC(offsetof(rusb2_reg_t, DPUSR1R_FS ) == 0x0404, "incorrect offset");
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_RUSB2_TYPE_H_ */
diff --git a/src/portable/renesas/usba/dcd_usba.c b/src/portable/renesas/usba/dcd_usba.c
deleted file mode 100644
index fa87c9f4d..000000000
--- a/src/portable/renesas/usba/dcd_usba.c
+++ /dev/null
@@ -1,915 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020 Koji Kitayama
- * Portions copyrighted (c) 2021 Roland Winistoerfer
- *
- * 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"
-
-// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
-// We disable SOF for now until needed later on
-#define USE_SOF 0
-
-#if CFG_TUD_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X || \
- CFG_TUSB_MCU == OPT_MCU_RX65X || \
- CFG_TUSB_MCU == OPT_MCU_RX72N )
-#include "device/dcd.h"
-#include "iodefine.h"
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-#define SYSTEM_PRCR_PRC1 (1<<1)
-#define SYSTEM_PRCR_PRKEY (0xA5u<<8)
-
-#define USB_FIFOSEL_TX ((uint16_t)(1u<<5))
-#define USB_FIFOSEL_BIGEND ((uint16_t)(1u<<8))
-#define USB_FIFOSEL_MBW_8 ((uint16_t)(0u<<10))
-#define USB_FIFOSEL_MBW_16 ((uint16_t)(1u<<10))
-#define USB_IS0_CTSQ ((uint16_t)(7u))
-#define USB_IS0_DVSQ ((uint16_t)(7u<<4))
-#define USB_IS0_VALID ((uint16_t)(1u<<3))
-#define USB_IS0_BRDY ((uint16_t)(1u<<8))
-#define USB_IS0_NRDY ((uint16_t)(1u<<9))
-#define USB_IS0_BEMP ((uint16_t)(1u<<10))
-#define USB_IS0_CTRT ((uint16_t)(1u<<11))
-#define USB_IS0_DVST ((uint16_t)(1u<<12))
-#define USB_IS0_SOFR ((uint16_t)(1u<<13))
-#define USB_IS0_RESM ((uint16_t)(1u<<14))
-#define USB_IS0_VBINT ((uint16_t)(1u<<15))
-#define USB_IS1_SACK ((uint16_t)(1u<<4))
-#define USB_IS1_SIGN ((uint16_t)(1u<<5))
-#define USB_IS1_EOFERR ((uint16_t)(1u<<6))
-#define USB_IS1_ATTCH ((uint16_t)(1u<<11))
-#define USB_IS1_DTCH ((uint16_t)(1u<<12))
-#define USB_IS1_BCHG ((uint16_t)(1u<<14))
-#define USB_IS1_OVRCR ((uint16_t)(1u<<15))
-
-#define USB_IS0_CTSQ_MSK (7u)
-#define USB_IS0_CTSQ_SETUP (1u)
-#define USB_IS0_DVSQ_DEF (1u<<4)
-#define USB_IS0_DVSQ_ADDR (2u<<4)
-#define USB_IS0_DVSQ_SUSP0 (4u<<4)
-#define USB_IS0_DVSQ_SUSP1 (5u<<4)
-#define USB_IS0_DVSQ_SUSP2 (6u<<4)
-#define USB_IS0_DVSQ_SUSP3 (7u<<4)
-
-#define USB_PIPECTR_PID_NAK (0u)
-#define USB_PIPECTR_PID_BUF (1u)
-#define USB_PIPECTR_PID_STALL (2u)
-#define USB_PIPECTR_CCPL (1u<<2)
-#define USB_PIPECTR_SQMON (1u<<6)
-#define USB_PIPECTR_SQCLR (1u<<8)
-#define USB_PIPECTR_ACLRM (1u<<9)
-#define USB_PIPECTR_INBUFM (1u<<14)
-#define USB_PIPECTR_BSTS (1u<<15)
-
-#define USB_FIFOCTR_DTLN (0x1FF)
-#define USB_FIFOCTR_FRDY (1u<<13)
-#define USB_FIFOCTR_BCLR (1u<<14)
-#define USB_FIFOCTR_BVAL (1u<<15)
-
-#define USB_PIPECFG_SHTNAK (1u<<7)
-#define USB_PIPECFG_DBLB (1u<<9)
-#define USB_PIPECFG_BULK (1u<<14)
-#define USB_PIPECFG_ISO (3u<<14)
-#define USB_PIPECFG_INT (2u<<14)
-
-#define FIFO_REQ_CLR (1u)
-#define FIFO_COMPLETE (1u<<1)
-
-// Start of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_PACKED_BEGIN
-TU_ATTR_BIT_FIELD_ORDER_BEGIN
-
-typedef struct {
- union {
- struct {
- uint16_t : 8;
- uint16_t TRCLR: 1;
- uint16_t TRENB: 1;
- uint16_t : 0;
- };
- uint16_t TRE;
- };
- uint16_t TRN;
-} reg_pipetre_t;
-
-typedef union {
- struct {
- volatile uint16_t u8: 8;
- volatile uint16_t : 0;
- };
- volatile uint16_t u16;
-} hw_fifo_t;
-
-typedef struct TU_ATTR_PACKED
-{
- void *buf; /* the start address of a transfer data buffer */
- uint16_t length; /* the number of bytes in the buffer */
- uint16_t remaining; /* the number of bytes remaining in the buffer */
- struct {
- uint32_t ep : 8; /* an assigned endpoint address */
- uint32_t ff : 1; /* `buf` is TU_FUFO or POD */
- uint32_t : 0;
- };
-} pipe_state_t;
-
-TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_BIT_FIELD_ORDER_END
-
-typedef struct
-{
- pipe_state_t pipe[10];
- uint8_t ep[2][16]; /* a lookup table for a pipe index from an endpoint address */
-} dcd_data_t;
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static dcd_data_t _dcd;
-
-static uint32_t disable_interrupt(void)
-{
- uint32_t pswi;
-#if defined(__CCRX__)
- pswi = get_psw() & 0x010000;
- clrpsw_i();
-#else
- pswi = __builtin_rx_mvfc(0) & 0x010000;
- __builtin_rx_clrpsw('I');
-#endif
- return pswi;
-}
-
-static void enable_interrupt(uint32_t pswi)
-{
-#if defined(__CCRX__)
- set_psw(get_psw() | pswi);
-#else
- __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
-#endif
-}
-
-static unsigned find_pipe(unsigned xfer)
-{
- switch (xfer) {
- case TUSB_XFER_ISOCHRONOUS:
- for (int i = 1; i <= 2; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
- case TUSB_XFER_BULK:
- for (int i = 3; i <= 5; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- for (int i = 1; i <= 1; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
- case TUSB_XFER_INTERRUPT:
- for (int i = 6; i <= 9; ++i) {
- if (0 == _dcd.pipe[i].ep) return i;
- }
- break;
- default:
- /* No support for control transfer */
- break;
- }
- return 0;
-}
-
-static volatile uint16_t* get_pipectr(unsigned num)
-{
- volatile uint16_t *ctr = NULL;
- if (num) {
- ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
- ctr += num - 1;
- } else {
- ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
- }
- return ctr;
-}
-
-static volatile reg_pipetre_t* get_pipetre(unsigned num)
-{
- volatile reg_pipetre_t* tre = NULL;
- if ((1 <= num) && (num <= 5)) {
- tre = (volatile reg_pipetre_t*)&USB0.PIPE1TRE.WORD;
- tre += num - 1;
- }
- return tre;
-}
-
-static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
-{
- (void)rhport;
- volatile uint16_t *ctr = NULL;
- const unsigned epn = tu_edpt_number(ep_addr);
- if (epn) {
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned num = _dcd.ep[dir][epn];
- if (num) {
- ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
- ctr += num - 1;
- }
- } else {
- ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
- }
- return ctr;
-}
-
-static unsigned edpt0_max_packet_size(void)
-{
- return USB0.DCPMAXP.BIT.MXPS;
-}
-
-static unsigned edpt_max_packet_size(unsigned num)
-{
- USB0.PIPESEL.WORD = num;
- return USB0.PIPEMAXP.WORD;
-}
-
-static inline void pipe_wait_for_ready(unsigned num)
-{
- while (USB0.D0FIFOSEL.BIT.CURPIPE != num) ;
- while (!USB0.D0FIFOCTR.BIT.FRDY) ;
-}
-
-static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
-{
- volatile hw_fifo_t *reg = (volatile hw_fifo_t*) fifo;
- uintptr_t addr = (uintptr_t)buf;
- while (len >= 2) {
- reg->u16 = *(const uint16_t *)addr;
- addr += 2;
- len -= 2;
- }
- if (len) {
- reg->u8 = *(const uint8_t *)addr;
- ++addr;
- }
-}
-
-static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
-{
- uint8_t *p = (uint8_t*)buf;
- volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
- while (len--) *p++ = *reg;
-}
-
-static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigned len, unsigned dir)
-{
- static const struct {
- void (*tu_fifo_get_info)(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
- void (*tu_fifo_advance)(tu_fifo_t *f, uint16_t n);
- void (*pipe_read_write)(void *buf, volatile void *fifo, unsigned len);
- } ops[] = {
- /* OUT */ {tu_fifo_get_write_info,tu_fifo_advance_write_pointer,pipe_read_packet},
- /* IN */ {tu_fifo_get_read_info, tu_fifo_advance_read_pointer, pipe_write_packet},
- };
- tu_fifo_buffer_info_t info;
- ops[dir].tu_fifo_get_info(f, &info);
- unsigned total_len = len;
- len = TU_MIN(total_len, info.len_lin);
- ops[dir].pipe_read_write(info.ptr_lin, fifo, len);
- unsigned rem = total_len - len;
- if (rem) {
- len = TU_MIN(rem, info.len_wrap);
- ops[dir].pipe_read_write(info.ptr_wrap, fifo, len);
- rem -= len;
- }
- ops[dir].tu_fifo_advance(f, total_len - rem);
-}
-
-static bool pipe0_xfer_in(void)
-{
- pipe_state_t *pipe = &_dcd.pipe[0];
- const unsigned rem = pipe->remaining;
- if (!rem) {
- pipe->buf = NULL;
- return true;
- }
- const unsigned mps = edpt0_max_packet_size();
- const unsigned len = TU_MIN(mps, rem);
- void *buf = pipe->buf;
- if (len) {
- if (pipe->ff) {
- pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.CFIFO.WORD, len, TUSB_DIR_IN);
- } else {
- pipe_write_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- }
- if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
- pipe->remaining = rem - len;
- return false;
-}
-
-static bool pipe0_xfer_out(void)
-{
- pipe_state_t *pipe = &_dcd.pipe[0];
- const unsigned rem = pipe->remaining;
-
- const unsigned mps = edpt0_max_packet_size();
- const unsigned vld = USB0.CFIFOCTR.BIT.DTLN;
- const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
- void *buf = pipe->buf;
- if (len) {
- if (pipe->ff) {
- pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.CFIFO.WORD, len, TUSB_DIR_OUT);
- } else {
- pipe_read_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- }
- if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
- pipe->remaining = rem - len;
- if ((len < mps) || (rem == len)) {
- pipe->buf = NULL;
- return true;
- }
- return false;
-}
-
-static bool pipe_xfer_in(unsigned num)
-{
- pipe_state_t *pipe = &_dcd.pipe[num];
- const unsigned rem = pipe->remaining;
-
- if (!rem) {
- pipe->buf = NULL;
- return true;
- }
-
- USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
- const unsigned mps = edpt_max_packet_size(num);
- pipe_wait_for_ready(num);
- const unsigned len = TU_MIN(rem, mps);
- void *buf = pipe->buf;
- if (len) {
- if (pipe->ff) {
- pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.D0FIFO.WORD, len, TUSB_DIR_IN);
- } else {
- pipe_write_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- }
- if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- pipe->remaining = rem - len;
- return false;
-}
-
-static bool pipe_xfer_out(unsigned num)
-{
- pipe_state_t *pipe = &_dcd.pipe[num];
- const unsigned rem = pipe->remaining;
-
- USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_8;
- const unsigned mps = edpt_max_packet_size(num);
- pipe_wait_for_ready(num);
- const unsigned vld = USB0.D0FIFOCTR.BIT.DTLN;
- const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
- void *buf = pipe->buf;
- if (len) {
- if (pipe->ff) {
- pipe_read_write_packet_ff((tu_fifo_t*)buf, (volatile void*)&USB0.D0FIFO.WORD, len, TUSB_DIR_OUT);
- } else {
- pipe_read_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- }
- if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BCLR;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- pipe->remaining = rem - len;
- if ((len < mps) || (rem == len)) {
- pipe->buf = NULL;
- return NULL != buf;
- }
- return false;
-}
-
-static void process_setup_packet(uint8_t rhport)
-{
- uint16_t setup_packet[4];
- if (0 == (USB0.INTSTS0.WORD & USB_IS0_VALID)) return;
- USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
- setup_packet[0] = tu_le16toh(USB0.USBREQ.WORD);
- setup_packet[1] = USB0.USBVAL;
- setup_packet[2] = USB0.USBINDX;
- setup_packet[3] = USB0.USBLENG;
- USB0.INTSTS0.WORD = ~USB_IS0_VALID;
- dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet[0], true);
-}
-
-static void process_status_completion(uint8_t rhport)
-{
- uint8_t ep_addr;
- /* Check the data stage direction */
- if (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) {
- /* IN transfer. */
- ep_addr = tu_edpt_addr(0, TUSB_DIR_IN);
- } else {
- /* OUT transfer. */
- ep_addr = tu_edpt_addr(0, TUSB_DIR_OUT);
- }
- dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
-}
-
-static bool process_pipe0_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
-{
- /* configure fifo direction and access unit settings */
- if (ep_addr) { /* IN, 2 bytes */
- USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
- while (!(USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX)) ;
- } else { /* OUT, a byte */
- USB0.CFIFOSEL.WORD = USB_FIFOSEL_MBW_8;
- while (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) ;
- }
-
- pipe_state_t *pipe = &_dcd.pipe[0];
- pipe->ff = buffer_type;
- pipe->length = total_bytes;
- pipe->remaining = total_bytes;
- if (total_bytes) {
- pipe->buf = buffer;
- if (ep_addr) { /* IN */
- TU_ASSERT(USB0.DCPCTR.BIT.BSTS && (USB0.USBREQ.WORD & 0x80));
- pipe0_xfer_in();
- }
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF;
- } else {
- /* ZLP */
- pipe->buf = NULL;
- USB0.DCPCTR.WORD = USB_PIPECTR_CCPL | USB_PIPECTR_PID_BUF;
- }
- return true;
-}
-
-static bool process_pipe_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
-{
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned num = _dcd.ep[dir][epn];
-
- TU_ASSERT(num);
-
- pipe_state_t *pipe = &_dcd.pipe[num];
- pipe->ff = buffer_type;
- pipe->buf = buffer;
- pipe->length = total_bytes;
- pipe->remaining = total_bytes;
- if (dir) { /* IN */
- if (total_bytes) {
- pipe_xfer_in(num);
- } else { /* ZLP */
- USB0.D0FIFOSEL.WORD = num;
- pipe_wait_for_ready(num);
- USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- }
- } else {
-#if defined(__CCRX__)
- __evenaccess volatile reg_pipetre_t *pt = get_pipetre(num);
-#else
- volatile reg_pipetre_t *pt = get_pipetre(num);
-#endif
- if (pt) {
- const unsigned mps = edpt_max_packet_size(num);
- volatile uint16_t *ctr = get_pipectr(num);
- if (*ctr & 0x3) *ctr = USB_PIPECTR_PID_NAK;
- pt->TRE = TU_BIT(8);
- pt->TRN = (total_bytes + mps - 1) / mps;
- pt->TRENB = 1;
- *ctr = USB_PIPECTR_PID_BUF;
- }
- }
- // TU_LOG1("X %x %d %d\r\n", ep_addr, total_bytes, buffer_type);
- return true;
-}
-
-static bool process_edpt_xfer(int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
-{
- const unsigned epn = tu_edpt_number(ep_addr);
- if (0 == epn) {
- return process_pipe0_xfer(buffer_type, ep_addr, buffer, total_bytes);
- } else {
- return process_pipe_xfer(buffer_type, ep_addr, buffer, total_bytes);
- }
-}
-
-static void process_pipe0_bemp(uint8_t rhport)
-{
- bool completed = pipe0_xfer_in();
- if (completed) {
- pipe_state_t *pipe = &_dcd.pipe[0];
- dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN),
- pipe->length, XFER_RESULT_SUCCESS, true);
- }
-}
-
-static void process_pipe_brdy(uint8_t rhport, unsigned num)
-{
- pipe_state_t *pipe = &_dcd.pipe[num];
- const unsigned dir = tu_edpt_dir(pipe->ep);
- bool completed;
-
- if (dir) { /* IN */
- completed = pipe_xfer_in(num);
- } else {
- if (num) {
- completed = pipe_xfer_out(num);
- } else {
- completed = pipe0_xfer_out();
- }
- }
- if (completed) {
- dcd_event_xfer_complete(rhport, pipe->ep,
- pipe->length - pipe->remaining,
- XFER_RESULT_SUCCESS, true);
- // TU_LOG1("C %d %d\r\n", num, pipe->length - pipe->remaining);
- }
-}
-
-static void process_bus_reset(uint8_t rhport)
-{
- USB0.BEMPENB.WORD = 1;
- USB0.BRDYENB.WORD = 1;
- USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- USB0.D1FIFOSEL.WORD = 0;
- while (USB0.D1FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1CTR.WORD));
- volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t)(&USB0.PIPE1TRE.WORD));
- for (int i = 1; i <= 5; ++i) {
- USB0.PIPESEL.WORD = i;
- USB0.PIPECFG.WORD = 0;
- *ctr = USB_PIPECTR_ACLRM;
- *ctr = 0;
- ++ctr;
- *tre = TU_BIT(8);
- tre += 2;
- }
- for (int i = 6; i <= 9; ++i) {
- USB0.PIPESEL.WORD = i;
- USB0.PIPECFG.WORD = 0;
- *ctr = USB_PIPECTR_ACLRM;
- *ctr = 0;
- ++ctr;
- }
- tu_varclr(&_dcd);
- dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
-}
-
-static void process_set_address(uint8_t rhport)
-{
- const uint32_t addr = USB0.USBADDR.BIT.USBADDR;
- if (!addr) return;
- const tusb_control_request_t setup_packet = {
-#if defined(__CCRX__)
- .bmRequestType = { 0 }, /* Note: CCRX needs the braces over this struct member */
-#else
- .bmRequestType = 0,
-#endif
- .bRequest = TUSB_REQ_SET_ADDRESS,
- .wValue = addr,
- .wIndex = 0,
- .wLength = 0,
- };
- dcd_event_setup_received(rhport, (const uint8_t*)&setup_packet, true);
-}
-
-/*------------------------------------------------------------------*/
-/* Device API
- *------------------------------------------------------------------*/
-void dcd_init(uint8_t rhport)
-{
- (void)rhport;
- /* Enable USB0 */
- uint32_t pswi = disable_interrupt();
- SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
- MSTP(USB0) = 0;
- SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
- enable_interrupt(pswi);
- USB0.SYSCFG.BIT.SCKE = 1;
- while (!USB0.SYSCFG.BIT.SCKE) ;
- USB0.SYSCFG.BIT.DRPD = 0;
- USB0.SYSCFG.BIT.DCFM = 0;
- USB0.SYSCFG.BIT.USBE = 1;
-
- USB.DPUSR0R.BIT.FIXPHY0 = 0u; /* USB0 Transceiver Output fixed */
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- USB0.PHYSLEW.LONG = 0x5;
- IR(PERIB, INTB185) = 0;
-#else
- IR(USB0, USBI0) = 0;
-#endif
-
- /* Setup default control pipe */
- USB0.DCPMAXP.BIT.MXPS = 64;
- USB0.INTENB0.WORD = USB_IS0_VBINT | USB_IS0_BRDY | USB_IS0_BEMP |
- USB_IS0_DVST | USB_IS0_CTRT | (USE_SOF ? USB_IS0_SOFR: 0) | USB_IS0_RESM;
- USB0.BEMPENB.WORD = 1;
- USB0.BRDYENB.WORD = 1;
-
- if (USB0.INTSTS0.BIT.VBSTS) {
- dcd_connect(rhport);
- }
-}
-
-void dcd_int_enable(uint8_t rhport)
-{
- (void)rhport;
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- IEN(PERIB, INTB185) = 1;
-#else
- IEN(USB0, USBI0) = 1;
-#endif
-}
-
-void dcd_int_disable(uint8_t rhport)
-{
- (void)rhport;
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- IEN(PERIB, INTB185) = 0;
-#else
- IEN(USB0, USBI0) = 0;
-#endif
-}
-
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void)rhport;
- (void)dev_addr;
-}
-
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void)rhport;
- USB0.DVSTCTR0.BIT.WKUP = 1;
-}
-
-void dcd_connect(uint8_t rhport)
-{
- (void)rhport;
- USB0.SYSCFG.BIT.DPRPU = 1;
-}
-
-void dcd_disconnect(uint8_t rhport)
-{
- (void)rhport;
- USB0.SYSCFG.BIT.DPRPU = 0;
-}
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- // TODO implement later
-}
-
-//--------------------------------------------------------------------+
-// Endpoint API
-//--------------------------------------------------------------------+
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
- (void)rhport;
-
- const unsigned ep_addr = ep_desc->bEndpointAddress;
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned xfer = ep_desc->bmAttributes.xfer;
-
- const unsigned mps = tu_edpt_packet_size(ep_desc);
- if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) {
- /* USBa supports up to 256 bytes */
- return false;
- }
-
- const unsigned num = find_pipe(xfer);
- if (!num) return false;
- _dcd.pipe[num].ep = ep_addr;
- _dcd.ep[dir][epn] = num;
-
- /* setup pipe */
- dcd_int_disable(rhport);
- USB0.PIPESEL.WORD = num;
- USB0.PIPEMAXP.WORD = mps;
- volatile uint16_t *ctr = get_pipectr(num);
- *ctr = USB_PIPECTR_ACLRM | USB_PIPECTR_SQCLR;
- *ctr = 0;
- unsigned cfg = (dir << 4) | epn;
- if (xfer == TUSB_XFER_BULK) {
- cfg |= (USB_PIPECFG_BULK | USB_PIPECFG_SHTNAK | USB_PIPECFG_DBLB);
- } else if (xfer == TUSB_XFER_INTERRUPT) {
- cfg |= USB_PIPECFG_INT;
- } else {
- cfg |= (USB_PIPECFG_ISO | USB_PIPECFG_DBLB);
- }
- USB0.PIPECFG.WORD = cfg;
- USB0.BRDYSTS.WORD = 0x1FFu ^ TU_BIT(num);
- USB0.BRDYENB.WORD |= TU_BIT(num);
- if (dir || (xfer != TUSB_XFER_BULK)) {
- *ctr = USB_PIPECTR_PID_BUF;
- }
- // TU_LOG1("O %d %x %x\r\n", USB0.PIPESEL.WORD, USB0.PIPECFG.WORD, USB0.PIPEMAXP.WORD);
- dcd_int_enable(rhport);
-
- return true;
-}
-
-void dcd_edpt_close_all(uint8_t rhport)
-{
- unsigned i = TU_ARRAY_SIZE(_dcd.pipe);
- dcd_int_disable(rhport);
- while (--i) { /* Close all pipes except 0 */
- const unsigned ep_addr = _dcd.pipe[i].ep;
- if (!ep_addr) continue;
- dcd_edpt_close(rhport, ep_addr);
- }
- dcd_int_enable(rhport);
-}
-
-void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
-{
- (void)rhport;
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir = tu_edpt_dir(ep_addr);
- const unsigned num = _dcd.ep[dir][epn];
-
- USB0.BRDYENB.WORD &= ~TU_BIT(num);
- volatile uint16_t *ctr = get_pipectr(num);
- *ctr = 0;
- USB0.PIPESEL.WORD = num;
- USB0.PIPECFG.WORD = 0;
- _dcd.pipe[num].ep = 0;
- _dcd.ep[dir][epn] = 0;
-}
-
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
-{
- bool r;
- dcd_int_disable(rhport);
- r = process_edpt_xfer(0, ep_addr, buffer, total_bytes);
- dcd_int_enable(rhport);
- return r;
-}
-
-bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
- // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
- TU_ASSERT(ff->item_size == 1);
- bool r;
- dcd_int_disable(rhport);
- r = process_edpt_xfer(1, ep_addr, ff, total_bytes);
- dcd_int_enable(rhport);
- return r;
-}
-
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
-{
- volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
- if (!ctr) return;
- dcd_int_disable(rhport);
- const uint32_t pid = *ctr & 0x3;
- *ctr = pid | USB_PIPECTR_PID_STALL;
- *ctr = USB_PIPECTR_PID_STALL;
- dcd_int_enable(rhport);
-}
-
-void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
-{
- volatile uint16_t *ctr = ep_addr_to_pipectr(rhport, ep_addr);
- if (!ctr) return;
- dcd_int_disable(rhport);
- *ctr = USB_PIPECTR_SQCLR;
-
- if (tu_edpt_dir(ep_addr)) { /* IN */
- *ctr = USB_PIPECTR_PID_BUF;
- } else {
- const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)];
- USB0.PIPESEL.WORD = num;
- if (USB0.PIPECFG.BIT.TYPE != 1) {
- *ctr = USB_PIPECTR_PID_BUF;
- }
- }
- dcd_int_enable(rhport);
-}
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-void dcd_int_handler(uint8_t rhport)
-{
- (void)rhport;
-
- unsigned is0 = USB0.INTSTS0.WORD;
- /* clear active bits except VALID (don't write 0 to already cleared bits according to the HW manual) */
- USB0.INTSTS0.WORD = ~((USB_IS0_CTRT | USB_IS0_DVST | USB_IS0_SOFR | USB_IS0_RESM | USB_IS0_VBINT) & is0) | USB_IS0_VALID;
- if (is0 & USB_IS0_VBINT) {
- if (USB0.INTSTS0.BIT.VBSTS) {
- dcd_connect(rhport);
- } else {
- dcd_disconnect(rhport);
- }
- }
- if (is0 & USB_IS0_RESM) {
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
-#if (0==USE_SOF)
- USB0.INTENB0.BIT.SOFE = 0;
-#endif
- }
- if ((is0 & USB_IS0_SOFR) && USB0.INTENB0.BIT.SOFE) {
- // USBD will exit suspended mode when SOF event is received
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
-#if (0==USE_SOF)
- USB0.INTENB0.BIT.SOFE = 0;
-#endif
- }
- if (is0 & USB_IS0_DVST) {
- switch (is0 & USB_IS0_DVSQ) {
- case USB_IS0_DVSQ_DEF:
- process_bus_reset(rhport);
- break;
- case USB_IS0_DVSQ_ADDR:
- process_set_address(rhport);
- break;
- case USB_IS0_DVSQ_SUSP0:
- case USB_IS0_DVSQ_SUSP1:
- case USB_IS0_DVSQ_SUSP2:
- case USB_IS0_DVSQ_SUSP3:
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
-#if (0==USE_SOF)
- USB0.INTENB0.BIT.SOFE = 1;
-#endif
- default:
- break;
- }
- }
- if (is0 & USB_IS0_CTRT) {
- if (is0 & USB_IS0_CTSQ_SETUP) {
- /* A setup packet has been received. */
- process_setup_packet(rhport);
- } else if (0 == (is0 & USB_IS0_CTSQ_MSK)) {
- /* A ZLP has been sent/received. */
- process_status_completion(rhport);
- }
- }
- if (is0 & USB_IS0_BEMP) {
- const unsigned s = USB0.BEMPSTS.WORD;
- USB0.BEMPSTS.WORD = 0;
- if (s & 1) {
- process_pipe0_bemp(rhport);
- }
- }
- if (is0 & USB_IS0_BRDY) {
- const unsigned m = USB0.BRDYENB.WORD;
- unsigned s = USB0.BRDYSTS.WORD & m;
- /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
- USB0.BRDYSTS.WORD = ~s;
- while (s) {
-#if defined(__CCRX__)
- static const int Mod37BitPosition[] = {
- -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
- 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
- 20, 8, 19, 18
- };
-
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
- const unsigned num = __builtin_ctz(s);
-#endif
- process_pipe_brdy(rhport, num);
- s &= ~TU_BIT(num);
- }
- }
-}
-
-#endif
diff --git a/src/portable/renesas/usba/hcd_usba.c b/src/portable/renesas/usba/hcd_usba.c
deleted file mode 100644
index 5246ecb94..000000000
--- a/src/portable/renesas/usba/hcd_usba.c
+++ /dev/null
@@ -1,875 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2021 Koji Kitayama
- * Portions copyrighted (c) 2021 Roland Winistoerfer
- *
- * 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_TUH_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_RX63X || \
- CFG_TUSB_MCU == OPT_MCU_RX65X || \
- CFG_TUSB_MCU == OPT_MCU_RX72N )
-#include "host/hcd.h"
-#include "iodefine.h"
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-#define SYSTEM_PRCR_PRC1 (1<<1)
-#define SYSTEM_PRCR_PRKEY (0xA5u<<8)
-
-#define USB_DVSTCTR0_LOW (1u)
-#define USB_DVSTCTR0_FULL (2u)
-
-#define USB_FIFOSEL_TX ((uint16_t)(1u<<5))
-#define USB_FIFOSEL_BIGEND ((uint16_t)(1u<<8))
-#define USB_FIFOSEL_MBW_8 ((uint16_t)(0u<<10))
-#define USB_FIFOSEL_MBW_16 ((uint16_t)(1u<<10))
-#define USB_IS0_CTSQ ((uint16_t)(7u))
-#define USB_IS0_DVSQ ((uint16_t)(7u<<4))
-#define USB_IS0_VALID ((uint16_t)(1u<<3))
-#define USB_IS0_BRDY ((uint16_t)(1u<<8))
-#define USB_IS0_NRDY ((uint16_t)(1u<<9))
-#define USB_IS0_BEMP ((uint16_t)(1u<<10))
-#define USB_IS0_CTRT ((uint16_t)(1u<<11))
-#define USB_IS0_DVST ((uint16_t)(1u<<12))
-#define USB_IS0_SOFR ((uint16_t)(1u<<13))
-#define USB_IS0_RESM ((uint16_t)(1u<<14))
-#define USB_IS0_VBINT ((uint16_t)(1u<<15))
-#define USB_IS1_SACK ((uint16_t)(1u<<4))
-#define USB_IS1_SIGN ((uint16_t)(1u<<5))
-#define USB_IS1_EOFERR ((uint16_t)(1u<<6))
-#define USB_IS1_ATTCH ((uint16_t)(1u<<11))
-#define USB_IS1_DTCH ((uint16_t)(1u<<12))
-#define USB_IS1_BCHG ((uint16_t)(1u<<14))
-#define USB_IS1_OVRCR ((uint16_t)(1u<<15))
-
-#define USB_IS0_CTSQ_MSK (7u)
-#define USB_IS0_CTSQ_SETUP (1u)
-#define USB_IS0_DVSQ_DEF (1u<<4)
-#define USB_IS0_DVSQ_ADDR (2u<<4)
-#define USB_IS0_DVSQ_SUSP0 (4u<<4)
-#define USB_IS0_DVSQ_SUSP1 (5u<<4)
-#define USB_IS0_DVSQ_SUSP2 (6u<<4)
-#define USB_IS0_DVSQ_SUSP3 (7u<<4)
-
-#define USB_PIPECTR_PID_MSK (3u)
-#define USB_PIPECTR_PID_NAK (0u)
-#define USB_PIPECTR_PID_BUF (1u)
-#define USB_PIPECTR_PID_STALL (2u)
-#define USB_PIPECTR_CCPL (1u<<2)
-#define USB_PIPECTR_SQMON (1u<<6)
-#define USB_PIPECTR_SQCLR (1u<<8)
-#define USB_PIPECTR_ACLRM (1u<<9)
-#define USB_PIPECTR_INBUFM (1u<<14)
-#define USB_PIPECTR_BSTS (1u<<15)
-
-#define USB_FIFOCTR_DTLN (0x1FF)
-#define USB_FIFOCTR_FRDY (1u<<13)
-#define USB_FIFOCTR_BCLR (1u<<14)
-#define USB_FIFOCTR_BVAL (1u<<15)
-
-#define USB_PIPECFG_SHTNAK (1u<<7)
-#define USB_PIPECFG_DBLB (1u<<9)
-#define USB_PIPECFG_BULK (1u<<14)
-#define USB_PIPECFG_ISO (3u<<14)
-#define USB_PIPECFG_INT (2u<<14)
-
-#define USB_DEVADD_LOW (1u<<6)
-#define USB_DEVADD_FULL (2u<<6)
-
-#define FIFO_REQ_CLR (1u)
-#define FIFO_COMPLETE (1u<<1)
-
-// Start of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_PACKED_BEGIN
-TU_ATTR_BIT_FIELD_ORDER_BEGIN
-
-typedef struct {
- union {
- struct {
- uint16_t : 8;
- uint16_t TRCLR: 1;
- uint16_t TRENB: 1;
- uint16_t : 0;
- };
- uint16_t TRE;
- };
- uint16_t TRN;
-} reg_pipetre_t;
-
-typedef union {
- struct {
- volatile uint16_t u8: 8;
- volatile uint16_t : 0;
- };
- volatile uint16_t u16;
-} hw_fifo_t;
-
-typedef struct TU_ATTR_PACKED
-{
- void *buf; /* the start address of a transfer data buffer */
- uint16_t length; /* the number of bytes in the buffer */
- uint16_t remaining; /* the number of bytes remaining in the buffer */
- struct {
- uint32_t ep : 8; /* an assigned endpoint address */
- uint32_t dev : 8; /* an assigned device address */
- uint32_t ff : 1; /* `buf` is TU_FUFO or POD */
- uint32_t : 0;
- };
-} pipe_state_t;
-
-TU_ATTR_PACKED_END // End of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_BIT_FIELD_ORDER_END
-
-typedef struct
-{
- bool need_reset; /* The device has not been reset after connection. */
- pipe_state_t pipe[10];
- uint8_t ep[4][2][15]; /* a lookup table for a pipe index from an endpoint address */
- uint8_t ctl_mps[5]; /* EP0 max packet size for each device */
-} hcd_data_t;
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static hcd_data_t _hcd;
-
-static uint32_t disable_interrupt(void)
-{
- uint32_t pswi;
-#if defined(__CCRX__)
- pswi = get_psw() & 0x010000;
- clrpsw_i();
-#else
- pswi = __builtin_rx_mvfc(0) & 0x010000;
- __builtin_rx_clrpsw('I');
-#endif
- return pswi;
-}
-
-static void enable_interrupt(uint32_t pswi)
-{
-#if defined(__CCRX__)
- set_psw(get_psw() | pswi);
-#else
- __builtin_rx_mvtc(0, __builtin_rx_mvfc(0) | pswi);
-#endif
-}
-
-static unsigned find_pipe(unsigned xfer)
-{
- switch (xfer) {
- case TUSB_XFER_ISOCHRONOUS:
- for (int i = 1; i <= 2; ++i) {
- if (0 == _hcd.pipe[i].ep) return i;
- }
- break;
- case TUSB_XFER_BULK:
- for (int i = 3; i <= 5; ++i) {
- if (0 == _hcd.pipe[i].ep) return i;
- }
- for (int i = 1; i <= 1; ++i) {
- if (0 == _hcd.pipe[i].ep) return i;
- }
- break;
- case TUSB_XFER_INTERRUPT:
- for (int i = 6; i <= 9; ++i) {
- if (0 == _hcd.pipe[i].ep) return i;
- }
- break;
- default:
- /* No support for control transfer */
- break;
- }
- return 0;
-}
-
-static volatile uint16_t* get_pipectr(unsigned num)
-{
- volatile uint16_t *ctr = NULL;
- if (num) {
- ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
- ctr += num - 1;
- } else {
- ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
- }
- return ctr;
-}
-
-static volatile reg_pipetre_t* get_pipetre(unsigned num)
-{
- volatile reg_pipetre_t* tre = NULL;
- if ((1 <= num) && (num <= 5)) {
- tre = (volatile reg_pipetre_t*)&USB0.PIPE1TRE.WORD;
- tre += num - 1;
- }
- return tre;
-}
-
-static volatile uint16_t* addr_to_pipectr(uint8_t dev_addr, unsigned ep_addr)
-{
- volatile uint16_t *ctr = NULL;
- const unsigned epn = tu_edpt_number(ep_addr);
- if (epn) {
- const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned num = _hcd.ep[dev_addr][dir_in][epn - 1];
- if (num) {
- ctr = (volatile uint16_t*)&USB0.PIPE1CTR.WORD;
- ctr += num - 1;
- }
- } else {
- ctr = (volatile uint16_t*)&USB0.DCPCTR.WORD;
- }
- return ctr;
-}
-
-static unsigned edpt0_max_packet_size(void)
-{
- return USB0.DCPMAXP.BIT.MXPS;
-}
-
-static unsigned edpt_max_packet_size(unsigned num)
-{
- USB0.PIPESEL.WORD = num;
- return USB0.PIPEMAXP.BIT.MXPS;
-}
-
-static inline void pipe_wait_for_ready(unsigned num)
-{
- while (USB0.D0FIFOSEL.BIT.CURPIPE != num) ;
- while (!USB0.D0FIFOCTR.BIT.FRDY) ;
-}
-
-static void pipe_write_packet(void *buf, volatile void *fifo, unsigned len)
-{
- volatile hw_fifo_t *reg = (volatile hw_fifo_t*)fifo;
- uintptr_t addr = (uintptr_t)buf;
- while (len >= 2) {
- reg->u16 = *(const uint16_t *)addr;
- addr += 2;
- len -= 2;
- }
- if (len) {
- reg->u8 = *(const uint8_t *)addr;
- ++addr;
- }
-}
-
-static void pipe_read_packet(void *buf, volatile void *fifo, unsigned len)
-{
- uint8_t *p = (uint8_t*)buf;
- volatile uint8_t *reg = (volatile uint8_t*)fifo; /* byte access is always at base register address */
- while (len--) *p++ = *reg;
-}
-
-static bool pipe0_xfer_in(void)
-{
- pipe_state_t *pipe = &_hcd.pipe[0];
- const unsigned rem = pipe->remaining;
-
- const unsigned mps = edpt0_max_packet_size();
- const unsigned vld = USB0.CFIFOCTR.BIT.DTLN;
- const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
- void *buf = pipe->buf;
- if (len) {
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_NAK;
- pipe_read_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BCLR;
- pipe->remaining = rem - len;
- if ((len < mps) || (rem == len)) {
- pipe->buf = NULL;
- return true;
- }
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF;
- return false;
-}
-
-static bool pipe0_xfer_out(void)
-{
- pipe_state_t *pipe = &_hcd.pipe[0];
- const unsigned rem = pipe->remaining;
- if (!rem) {
- pipe->buf = NULL;
- return true;
- }
- const unsigned mps = edpt0_max_packet_size();
- const unsigned len = TU_MIN(mps, rem);
- void *buf = pipe->buf;
- if (len) {
- pipe_write_packet(buf, (volatile void*)&USB0.CFIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- if (len < mps) USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
- pipe->remaining = rem - len;
- return false;
-}
-
-static bool pipe_xfer_in(unsigned num)
-{
- pipe_state_t *pipe = &_hcd.pipe[num];
- const unsigned rem = pipe->remaining;
-
- USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_8;
- const unsigned mps = edpt_max_packet_size(num);
- pipe_wait_for_ready(num);
- const unsigned vld = USB0.D0FIFOCTR.BIT.DTLN;
- const unsigned len = TU_MIN(TU_MIN(rem, mps), vld);
- void *buf = pipe->buf;
- if (len) {
- pipe_read_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BCLR;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- pipe->remaining = rem - len;
- if ((len < mps) || (rem == len)) {
- pipe->buf = NULL;
- return NULL != buf;
- }
- return false;
-}
-
-static bool pipe_xfer_out(unsigned num)
-{
- pipe_state_t *pipe = &_hcd.pipe[num];
- const unsigned rem = pipe->remaining;
-
- if (!rem) {
- pipe->buf = NULL;
- return true;
- }
-
- USB0.D0FIFOSEL.WORD = num | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
- const unsigned mps = edpt_max_packet_size(num);
- pipe_wait_for_ready(num);
- const unsigned len = TU_MIN(rem, mps);
- void *buf = pipe->buf;
- if (len) {
- pipe_write_packet(buf, (volatile void*)&USB0.D0FIFO.WORD, len);
- pipe->buf = (uint8_t*)buf + len;
- }
- if (len < mps) USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- pipe->remaining = rem - len;
- return false;
-}
-
-static bool process_pipe0_xfer(uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
-{
- (void)dev_addr;
- const unsigned dir_in = tu_edpt_dir(ep_addr);
-
- /* configure fifo direction and access unit settings */
- if (dir_in) { /* IN, a byte */
- USB0.CFIFOSEL.WORD = USB_FIFOSEL_MBW_8;
- while (USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX) ;
- } else { /* OUT, 2 bytes */
- USB0.CFIFOSEL.WORD = USB_FIFOSEL_TX | USB_FIFOSEL_MBW_16 | (TU_BYTE_ORDER == TU_BIG_ENDIAN ? USB_FIFOSEL_BIGEND : 0);
- while (!(USB0.CFIFOSEL.WORD & USB_FIFOSEL_TX)) ;
- }
-
- pipe_state_t *pipe = &_hcd.pipe[0];
- pipe->ep = ep_addr;
- pipe->length = buflen;
- pipe->remaining = buflen;
- if (buflen) {
- pipe->buf = buffer;
- if (!dir_in) { /* OUT */
- TU_ASSERT(USB0.DCPCTR.BIT.BSTS && (USB0.USBREQ.WORD & 0x80));
- pipe0_xfer_out();
- }
- } else { /* ZLP */
- pipe->buf = NULL;
- if (!dir_in) { /* OUT */
- USB0.CFIFOCTR.WORD = USB_FIFOCTR_BVAL;
- }
- if (dir_in == USB0.DCPCFG.BIT.DIR) {
- TU_ASSERT(USB_PIPECTR_PID_NAK == USB0.DCPCTR.BIT.PID);
- USB0.DCPCTR.BIT.SQSET = 1;
- USB0.DCPCFG.BIT.DIR = dir_in ^ 1;
- }
- }
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_BUF;
- return true;
-}
-
-static bool process_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
-{
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned num = _hcd.ep[dev_addr - 1][dir_in][epn - 1];
-
- TU_ASSERT(num);
-
- pipe_state_t *pipe = &_hcd.pipe[num];
- pipe->buf = buffer;
- pipe->length = buflen;
- pipe->remaining = buflen;
- if (!dir_in) { /* OUT */
- if (buflen) {
- pipe_xfer_out(num);
- } else { /* ZLP */
- USB0.D0FIFOSEL.WORD = num;
- pipe_wait_for_ready(num);
- USB0.D0FIFOCTR.WORD = USB_FIFOCTR_BVAL;
- USB0.D0FIFOSEL.WORD = 0;
- while (USB0.D0FIFOSEL.BIT.CURPIPE) ; /* if CURPIPE bits changes, check written value */
- }
- } else {
- volatile uint16_t *ctr = get_pipectr(num);
- volatile reg_pipetre_t *pt = get_pipetre(num);
- if (pt) {
- const unsigned mps = edpt_max_packet_size(num);
- if (*ctr & 0x3) *ctr = USB_PIPECTR_PID_NAK;
- pt->TRE = TU_BIT(8);
- pt->TRN = (buflen + mps - 1) / mps;
- pt->TRENB = 1;
- }
- *ctr = USB_PIPECTR_PID_BUF;
- }
- return true;
-}
-
-static bool process_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, void* buffer, uint16_t buflen)
-{
- const unsigned epn = tu_edpt_number(ep_addr);
- if (0 == epn) {
- return process_pipe0_xfer(dev_addr, ep_addr, buffer, buflen);
- } else {
- return process_pipe_xfer(dev_addr, ep_addr, buffer, buflen);
- }
-}
-
-static void process_pipe0_bemp(uint8_t rhport)
-{
- (void)rhport;
- bool completed = pipe0_xfer_out();
- if (completed) {
- pipe_state_t *pipe = &_hcd.pipe[0];
- hcd_event_xfer_complete(pipe->dev,
- tu_edpt_addr(0, TUSB_DIR_OUT),
- pipe->length - pipe->remaining,
- XFER_RESULT_SUCCESS, true);
- }
-}
-
-static void process_pipe_nrdy(uint8_t rhport, unsigned num)
-{
- (void)rhport;
- unsigned result;
- uint16_t volatile *ctr = get_pipectr(num);
- // TU_LOG1("NRDY %d %x\n", num, *ctr);
- switch (*ctr & USB_PIPECTR_PID_MSK) {
- default: return;
- case USB_PIPECTR_PID_STALL: result = XFER_RESULT_STALLED; break;
- case USB_PIPECTR_PID_NAK: result = XFER_RESULT_FAILED; break;
- }
- pipe_state_t *pipe = &_hcd.pipe[num];
- hcd_event_xfer_complete(pipe->dev, pipe->ep,
- pipe->length - pipe->remaining,
- result, true);
-}
-
-static void process_pipe_brdy(uint8_t rhport, unsigned num)
-{
- (void)rhport;
- pipe_state_t *pipe = &_hcd.pipe[num];
- const unsigned dir_in = tu_edpt_dir(pipe->ep);
- bool completed;
-
- if (dir_in) { /* IN */
- if (num) {
- completed = pipe_xfer_in(num);
- } else {
- completed = pipe0_xfer_in();
- }
- } else {
- completed = pipe_xfer_out(num);
- }
- if (completed) {
- hcd_event_xfer_complete(pipe->dev, pipe->ep,
- pipe->length - pipe->remaining,
- XFER_RESULT_SUCCESS, true);
- // TU_LOG1("C %d %d\r\n", num, pipe->length - pipe->remaining);
- }
-}
-
-
-/*------------------------------------------------------------------*/
-/* Host API
- *------------------------------------------------------------------*/
-bool hcd_init(uint8_t rhport)
-{
- (void)rhport;
- /* Enable USB0 */
- uint32_t pswi = disable_interrupt();
- SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
- MSTP(USB0) = 0;
- SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
- enable_interrupt(pswi);
- USB0.SYSCFG.BIT.SCKE = 1;
- while (!USB0.SYSCFG.BIT.SCKE) ;
- USB0.SYSCFG.BIT.DPRPU = 0;
- USB0.SYSCFG.BIT.DRPD = 0;
- USB0.SYSCFG.BIT.DCFM = 1;
-
- USB0.DVSTCTR0.BIT.VBUSEN = 1;
-
- USB0.SYSCFG.BIT.DRPD = 1;
- for (volatile int i = 0; i < 30000; ++i) ;
- USB0.SYSCFG.BIT.USBE = 1;
-
- USB.DPUSR0R.BIT.FIXPHY0 = 0u; /* USB0 Transceiver Output fixed */
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- USB0.PHYSLEW.LONG = 0x5;
- IR(PERIB, INTB185) = 0;
-#else
- IR(USB0, USBI0) = 0;
-#endif
-
- /* Setup default control pipe */
- USB0.DCPCFG.WORD = USB_PIPECFG_SHTNAK;
- USB0.DCPMAXP.WORD = 64;
- USB0.INTENB0.WORD = USB_IS0_BRDY | USB_IS0_NRDY | USB_IS0_BEMP;
- USB0.INTENB1.WORD = USB_IS1_SACK | USB_IS1_SIGN |
- USB_IS1_ATTCH | USB_IS1_DTCH;
- USB0.BEMPENB.WORD = 1;
- USB0.NRDYENB.WORD = 1;
- USB0.BRDYENB.WORD = 1;
- return true;
-}
-
-void hcd_int_enable(uint8_t rhport)
-{
- (void)rhport;
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- IEN(PERIB, INTB185) = 1;
-#else
- IEN(USB0, USBI0) = 1;
-#endif
-}
-
-void hcd_int_disable(uint8_t rhport)
-{
- (void)rhport;
-#if ( CFG_TUSB_MCU == OPT_MCU_RX72N )
- IEN(PERIB, INTB185) = 0;
-#else
- IEN(USB0, USBI0) = 0;
-#endif
-}
-
-uint32_t hcd_frame_number(uint8_t rhport)
-{
- (void)rhport;
- /* The device must be reset at least once after connection
- * in order to start the frame counter. */
- if (_hcd.need_reset) hcd_port_reset(rhport);
- return USB0.FRMNUM.BIT.FRNM;
-}
-
-/*--------------------------------------------------------------------+
- * Port API
- *--------------------------------------------------------------------+*/
-bool hcd_port_connect_status(uint8_t rhport)
-{
- (void)rhport;
- return USB0.INTSTS1.BIT.ATTCH ? true: false;
-}
-
-void hcd_port_reset(uint8_t rhport)
-{
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_NAK;
- while (USB0.DCPCTR.BIT.PBUSY) ;
- hcd_int_disable(rhport);
- USB0.DVSTCTR0.BIT.UACT = 0;
- if (USB0.DCPCTR.BIT.SUREQ)
- USB0.DCPCTR.BIT.SUREQCLR = 1;
- hcd_int_enable(rhport);
- /* Reset should be asserted 10-20ms. */
- USB0.DVSTCTR0.BIT.USBRST = 1;
- for (volatile int i = 0; i < 2400000; ++i) ;
- USB0.DVSTCTR0.BIT.USBRST = 0;
- USB0.DVSTCTR0.BIT.UACT = 1;
- _hcd.need_reset = false;
-}
-
-void hcd_port_reset_end(uint8_t rhport)
-{
- (void) rhport;
-}
-
-tusb_speed_t hcd_port_speed_get(uint8_t rhport)
-{
- (void)rhport;
- switch (USB0.DVSTCTR0.BIT.RHST) {
- default: return TUSB_SPEED_INVALID;
- case USB_DVSTCTR0_FULL: return TUSB_SPEED_FULL;
- case USB_DVSTCTR0_LOW: return TUSB_SPEED_LOW;
- }
-}
-
-void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
-{
- (void)rhport;
- uint16_t volatile *ctr;
- TU_ASSERT(dev_addr < 6,); /* USBa can only handle addresses from 0 to 5. */
- if (!dev_addr) return;
- _hcd.ctl_mps[dev_addr] = 0;
- uint8_t *ep = &_hcd.ep[dev_addr - 1][0][0];
- for (int i = 0; i < 2 * 15; ++i, ++ep) {
- unsigned num = *ep;
- if (!num || dev_addr != _hcd.pipe[num].dev) continue;
-
- ctr = (uint16_t volatile*)&USB0.PIPE1CTR.WORD + num - 1;
- *ctr = 0;
- USB0.NRDYENB.WORD &= ~TU_BIT(num);
- USB0.BRDYENB.WORD &= ~TU_BIT(num);
- USB0.PIPESEL.WORD = num;
- USB0.PIPECFG.WORD = 0;
- USB0.PIPEMAXP.WORD = 0;
-
- _hcd.pipe[num].ep = 0;
- _hcd.pipe[num].dev = 0;
- *ep = 0;
- }
-}
-
-/*--------------------------------------------------------------------+
- * Endpoints API
- *--------------------------------------------------------------------+*/
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
-{
- (void)rhport;
- // TU_LOG1("S %d %x\n", dev_addr, USB0.DCPCTR.WORD);
-
- TU_ASSERT(dev_addr < 6); /* USBa can only handle addresses from 0 to 5. */
- TU_ASSERT(0 == USB0.DCPCTR.BIT.SUREQ);
-
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_NAK;
-
- _hcd.pipe[0].buf = NULL;
- _hcd.pipe[0].length = 8;
- _hcd.pipe[0].remaining = 0;
- _hcd.pipe[0].dev = dev_addr;
-
- while (USB0.DCPCTR.BIT.PBUSY) ;
- USB0.DCPMAXP.WORD = (dev_addr << 12) | _hcd.ctl_mps[dev_addr];
-
- /* Set direction in advance for DATA stage */
- uint8_t const bmRequesttype = setup_packet[0];
- USB0.DCPCFG.BIT.DIR = tu_edpt_dir(bmRequesttype) ? 0: 1;
-
- uint16_t const* p = (uint16_t const*)(uintptr_t)&setup_packet[0];
- USB0.USBREQ.WORD = tu_htole16(p[0]);
- USB0.USBVAL = p[1];
- USB0.USBINDX = p[2];
- USB0.USBLENG = p[3];
-
- USB0.DCPCTR.BIT.SUREQ = 1;
- return true;
-}
-
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
-{
- (void)rhport;
- TU_ASSERT(dev_addr < 6); /* USBa can only handle addresses from 0 to 5. */
-
- const unsigned ep_addr = ep_desc->bEndpointAddress;
- const unsigned epn = tu_edpt_number(ep_addr);
- const unsigned mps = tu_edpt_packet_size(ep_desc);
- if (0 == epn) {
- USB0.DCPCTR.WORD = USB_PIPECTR_PID_NAK;
- hcd_devtree_info_t devtree;
- hcd_devtree_get_info(dev_addr, &devtree);
- uint16_t volatile *devadd = (uint16_t volatile *)(uintptr_t)&USB0.DEVADD0.WORD;
- devadd += dev_addr;
- while (USB0.DCPCTR.BIT.PBUSY) ;
- USB0.DCPMAXP.WORD = (dev_addr << 12) | mps;
- *devadd = (TUSB_SPEED_FULL == devtree.speed) ? USB_DEVADD_FULL : USB_DEVADD_LOW;
- _hcd.ctl_mps[dev_addr] = mps;
- return true;
- }
-
- const unsigned dir_in = tu_edpt_dir(ep_addr);
- const unsigned xfer = ep_desc->bmAttributes.xfer;
- if (xfer == TUSB_XFER_ISOCHRONOUS && mps > 256) {
- /* USBa supports up to 256 bytes */
- return false;
- }
- const unsigned num = find_pipe(xfer);
- if (!num) return false;
- _hcd.pipe[num].dev = dev_addr;
- _hcd.pipe[num].ep = ep_addr;
- _hcd.ep[dev_addr - 1][dir_in][epn - 1] = num;
-
- /* setup pipe */
- hcd_int_disable(rhport);
- USB0.PIPESEL.WORD = num;
- USB0.PIPEMAXP.WORD = (dev_addr << 12) | mps;
- volatile uint16_t *ctr = get_pipectr(num);
- *ctr = USB_PIPECTR_ACLRM | USB_PIPECTR_SQCLR;
- *ctr = 0;
- unsigned cfg = ((1 ^ dir_in) << 4) | epn;
- if (xfer == TUSB_XFER_BULK) {
- cfg |= USB_PIPECFG_BULK | USB_PIPECFG_SHTNAK | USB_PIPECFG_DBLB;
- } else if (xfer == TUSB_XFER_INTERRUPT) {
- cfg |= USB_PIPECFG_INT;
- } else {
- cfg |= USB_PIPECFG_ISO | USB_PIPECFG_DBLB;
- }
- USB0.PIPECFG.WORD = cfg;
- USB0.BRDYSTS.WORD = 0x1FFu ^ TU_BIT(num);
- USB0.NRDYENB.WORD |= TU_BIT(num);
- USB0.BRDYENB.WORD |= TU_BIT(num);
- if (!dir_in) {
- *ctr = USB_PIPECTR_PID_BUF;
- }
- hcd_int_enable(rhport);
-
- return true;
-}
-
-bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen)
-{
- bool r;
- hcd_int_disable(rhport);
- // TU_LOG1("X %d %x %u\n", dev_addr, ep_addr, buflen);
- r = process_edpt_xfer(dev_addr, ep_addr, buffer, buflen);
- hcd_int_enable(rhport);
- return r;
-}
-
-bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
-{
- uint16_t volatile *ctr = addr_to_pipectr(dev_addr, ep_addr);
- TU_ASSERT(ctr);
-
- const uint32_t pid = *ctr & 0x3;
- if (pid & 2) {
- *ctr = pid & 2;
- *ctr = 0;
- }
- *ctr = USB_PIPECTR_SQCLR;
- unsigned const epn = tu_edpt_number(ep_addr);
- if (!epn) return true;
-
- if (!tu_edpt_dir(ep_addr)) { /* OUT */
- *ctr = USB_PIPECTR_PID_BUF;
- }
- return true;
-}
-
-//--------------------------------------------------------------------+
-// ISR
-//--------------------------------------------------------------------+
-void hcd_int_handler(uint8_t rhport)
-{
- (void)rhport;
-#if defined(__CCRX__)
- static const int Mod37BitPosition[] = {
- -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
- 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
- 20, 8, 19, 18};
-#endif
-
- unsigned is1 = USB0.INTSTS1.WORD;
- unsigned is0 = USB0.INTSTS0.WORD;
- /* clear active bits except VALID (don't write 0 to already cleared bits according to the HW manual) */
- USB0.INTSTS1.WORD = ~((USB_IS1_SACK | USB_IS1_SIGN | USB_IS1_ATTCH | USB_IS1_DTCH) & is1);
- USB0.INTSTS0.WORD = ~((USB_IS0_BRDY | USB_IS0_NRDY | USB_IS0_BEMP) & is0);
- // TU_LOG1("IS %04x %04x\n", is0, is1);
- is1 &= USB0.INTENB1.WORD;
- is0 &= USB0.INTENB0.WORD;
-
- if (is1 & USB_IS1_SACK) {
- /* Set DATA1 in advance for the next transfer. */
- USB0.DCPCTR.BIT.SQSET = 1;
- hcd_event_xfer_complete(USB0.DCPMAXP.BIT.DEVSEL,
- tu_edpt_addr(0, TUSB_DIR_OUT),
- 8, XFER_RESULT_SUCCESS, true);
- }
- if (is1 & USB_IS1_SIGN) {
- hcd_event_xfer_complete(USB0.DCPMAXP.BIT.DEVSEL,
- tu_edpt_addr(0, TUSB_DIR_OUT),
- 8, XFER_RESULT_FAILED, true);
- }
- if (is1 & USB_IS1_ATTCH) {
- USB0.DVSTCTR0.BIT.UACT = 1;
- _hcd.need_reset = true;
- USB0.INTENB1.WORD = (USB0.INTENB1.WORD & ~USB_IS1_ATTCH) | USB_IS1_DTCH;
- hcd_event_device_attach(rhport, true);
- }
- if (is1 & USB_IS1_DTCH) {
- USB0.DVSTCTR0.BIT.UACT = 0;
- if (USB0.DCPCTR.BIT.SUREQ)
- USB0.DCPCTR.BIT.SUREQCLR = 1;
- USB0.INTENB1.WORD = (USB0.INTENB1.WORD & ~USB_IS1_DTCH) | USB_IS1_ATTCH;
- hcd_event_device_remove(rhport, true);
- }
-
- if (is0 & USB_IS0_BEMP) {
- const unsigned s = USB0.BEMPSTS.WORD;
- USB0.BEMPSTS.WORD = 0;
- if (s & 1) {
- process_pipe0_bemp(rhport);
- }
- }
- if (is0 & USB_IS0_NRDY) {
- const unsigned m = USB0.NRDYENB.WORD;
- unsigned s = USB0.NRDYSTS.WORD & m;
- USB0.NRDYSTS.WORD = ~s;
- while (s) {
-#if defined(__CCRX__)
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
- const unsigned num = __builtin_ctz(s);
-#endif
- process_pipe_nrdy(rhport, num);
- s &= ~TU_BIT(num);
- }
- }
- if (is0 & USB_IS0_BRDY) {
- const unsigned m = USB0.BRDYENB.WORD;
- unsigned s = USB0.BRDYSTS.WORD & m;
- /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
- USB0.BRDYSTS.WORD = ~s;
- while (s) {
-#if defined(__CCRX__)
- const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
- const unsigned num = __builtin_ctz(s);
-#endif
- process_pipe_brdy(rhport, num);
- s &= ~TU_BIT(num);
- }
- }
-}
-
-#endif
diff --git a/src/portable/sony/cxd56/dcd_cxd56.c b/src/portable/sony/cxd56/dcd_cxd56.c
index 6677891a5..b16509c6f 100644
--- a/src/portable/sony/cxd56/dcd_cxd56.c
+++ b/src/portable/sony/cxd56/dcd_cxd56.c
@@ -102,17 +102,25 @@ static int _dcd_bind(FAR struct usbdevclass_driver_s *driver, FAR struct usbdev_
usbdev = dev;
usbdcd_driver.ep[0] = dev->ep0;
+ #ifdef EP_ALLOCREQ
+ // SDK v2
usbdcd_driver.req[0] = EP_ALLOCREQ(usbdcd_driver.ep[0]);
- if (usbdcd_driver.req[0] != NULL)
- {
+ if (usbdcd_driver.req[0] != NULL) {
usbdcd_driver.req[0]->len = 64;
usbdcd_driver.req[0]->buf = EP_ALLOCBUFFER(usbdcd_driver.ep[0], 64);
- if (!usbdcd_driver.req[0]->buf)
- {
+ if (!usbdcd_driver.req[0]->buf) {
EP_FREEREQ(usbdcd_driver.ep[0], usbdcd_driver.req[0]);
usbdcd_driver.req[0] = NULL;
+ return ENOMEM;
}
}
+ #else
+ // SDK v3
+ usbdcd_driver.req[0] = usbdev_allocreq(usbdcd_driver.ep[0], 64);
+ if (usbdcd_driver.req[0] == NULL) {
+ return ENOMEM;
+ }
+ #endif
usbdcd_driver.req[0]->callback = usbdcd_ep0incomplete;
@@ -193,9 +201,9 @@ static void _dcd_resume(FAR struct usbdevclass_driver_s *driver, FAR struct usbd
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
usbdcd_driver.usbdevclass_driver.speed = USB_SPEED_HIGH;
usbdcd_driver.usbdevclass_driver.ops = &g_driverops;
@@ -203,6 +211,8 @@ void dcd_init(uint8_t rhport)
usbdcd_driver.setup_queue = osal_queue_create(&_setup_queue_def);
usbdev_register(&usbdcd_driver.usbdevclass_driver);
+
+ return true;
}
// Enable device interrupt
@@ -295,13 +305,19 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc)
}
usbdcd_driver.req[epnum] = NULL;
+
+ #ifdef EP_ALLOCREQ
+ // sdk v2
usbdcd_driver.req[epnum] = EP_ALLOCREQ(usbdcd_driver.ep[epnum]);
- if (usbdcd_driver.req[epnum] != NULL)
- {
+ if (usbdcd_driver.req[epnum] != NULL) {
usbdcd_driver.req[epnum]->len = ep_mps;
}
- else
- {
+ #else
+ // sdk v3
+ usbdcd_driver.req[epnum] = usbdev_allocreq(usbdcd_driver.ep[epnum], ep_mps);
+ #endif
+
+ if(usbdcd_driver.req[epnum] == NULL) {
return false;
}
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index c88a8a3a2..ef58957bb 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -1,11 +1,12 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Nathan Conrad
*
* Portions:
- * Copyright (c) 2016 STMicroelectronics
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2022 Simon Küppers (skuep)
+ * Copyright (c) 2022 HiFiPhile
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,13 +36,21 @@
* It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This
* covers:
*
- * F04x, F072, F078, 070x6/B 1024 byte buffer
+ * F04x, F072, F078, F070x6/B 1024 byte buffer
* F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?)
* F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up
* F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up
+ * C0 2048 byte buffer; 32-bit bus; host mode
+ * G0 2048 byte buffer; 32-bit bus; host mode
+ * G4 1024 byte buffer
+ * H5 2048 byte buffer; 32-bit bus; host mode
* L0x2, L0x3 1024 byte buffer
* L1 512 byte buffer
* L4x2, L4x3 1024 byte buffer
+ * L5 1024 byte buffer
+ * U0 1024 byte buffer; 32-bit bus
+ * U535, U545 2048 byte buffer; 32-bit bus; host mode
+ * WB35, WB55 1024 byte buffer
*
* To use this driver, you must:
* - If you are using a device with crystal-less USB, set up the clock recovery system (CRS)
@@ -64,10 +73,6 @@
* - STALL handled, but not tested.
* - Does it work? No clue.
* - All EP BTABLE buffers are created based on max packet size of first EP opened with that address.
- * - No isochronous endpoints
- * - Endpoint index is the ID of the endpoint
- * - This means that priority is given to endpoints with lower ID numbers
- * - Code is mixing up EP IX with EP ID. Everywhere.
* - Packet buffer memory is copied in the interrupt.
* - This is better for performance, but means interrupts are disabled for longer
* - DMA may be the best choice, but it could also be pushed to the USBD task.
@@ -103,569 +108,361 @@
#include "tusb_option.h"
-#if defined(STM32F102x6) || defined(STM32F102xB) || \
- defined(STM32F103x6) || defined(STM32F103xB) || \
- defined(STM32F103xE) || defined(STM32F103xG)
-#define STM32F1_FSDEV
-#endif
-
-#if defined(STM32L412xx) || defined(STM32L422xx) || \
- defined(STM32L432xx) || defined(STM32L433xx) || \
- defined(STM32L442xx) || defined(STM32L443xx) || \
- defined(STM32L452xx) || defined(STM32L462xx)
-#define STM32L4_FSDEV
-#endif
-
-#if CFG_TUD_ENABLED && \
- ( TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F3, OPT_MCU_STM32L0, OPT_MCU_STM32L1, OPT_MCU_STM32G4, OPT_MCU_STM32WB) || \
- (TU_CHECK_MCU(OPT_MCU_STM32F1) && defined(STM32F1_FSDEV)) || \
- (TU_CHECK_MCU(OPT_MCU_STM32L4) && defined(STM32L4_FSDEV)) \
- )
-
-// In order to reduce the dependance on HAL, we undefine this.
-// Some definitions are copied to our private include file.
-#undef USE_HAL_DRIVER
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \
+ !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0)
#include "device/dcd.h"
-#include "portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h"
-
-
-/*****************************************************
- * Configuration
- *****************************************************/
-
-// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM
-// (8u here would mean 8 IN and 8 OUT)
-#ifndef MAX_EP_COUNT
-# define MAX_EP_COUNT 8U
-#endif
-// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
-// Both of these MUST be a multiple of 2, and are in byte units.
-#ifndef DCD_STM32_BTABLE_BASE
-# define DCD_STM32_BTABLE_BASE 0U
-#endif
-
-#ifndef DCD_STM32_BTABLE_LENGTH
-# define DCD_STM32_BTABLE_LENGTH (PMA_LENGTH - DCD_STM32_BTABLE_BASE)
-#endif
-
-// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
-// We disable SOF for now until needed later on
-#ifndef USE_SOF
-# define USE_SOF 0
+#if defined(TUP_USBIP_FSDEV_STM32)
+ #include "fsdev_stm32.h"
+#elif defined(TUP_USBIP_FSDEV_CH32)
+ #include "fsdev_ch32.h"
+#else
+ #error "Unknown USB IP"
#endif
-/***************************************************
- * Checks, structs, defines, function definitions, etc.
- */
-
-TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_LENGTH))<=(PMA_LENGTH),
- "BTABLE does not fit in PMA RAM");
+#include "fsdev_type.h"
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
// One of these for every EP IN & OUT, uses a bit of RAM....
-typedef struct
-{
- uint8_t * buffer;
- // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
+typedef struct {
+ uint8_t *buffer;
+ tu_fifo_t *ff;
uint16_t total_len;
uint16_t queued_len;
- uint16_t pma_ptr;
- uint8_t max_packet_size;
- uint8_t pma_alloc_size;
+ uint16_t max_packet_size;
+ uint8_t ep_idx; // index for USB_EPnR register
+ bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask
} xfer_ctl_t;
-static xfer_ctl_t xfer_status[MAX_EP_COUNT][2];
-
-static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t epnum, uint32_t dir)
-{
- return &xfer_status[epnum][dir];
-}
-
-static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
+// EP allocator
+typedef struct {
+ uint8_t ep_num;
+ uint8_t ep_type;
+ bool allocated[2];
+} ep_alloc_t;
+static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2];
+static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT];
static uint8_t remoteWakeCountdown; // When wake is requested
+//--------------------------------------------------------------------+
+// Prototypes
+//--------------------------------------------------------------------+
+
// into the stack.
-static void dcd_handle_bus_reset(void);
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix);
-static void dcd_ep_ctr_handler(void);
+static void handle_bus_reset(uint8_t rhport);
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix);
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir);
-// PMA allocation/access
-static uint8_t open_ep_count;
+// PMA allocation/access
static uint16_t ep_buf_ptr; ///< Points to first free memory location
-static void dcd_pma_alloc_reset(void);
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length);
-static void dcd_pma_free(uint8_t ep_addr);
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes);
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes);
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf);
+static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes);
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes);
-//static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
-//static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes);
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes);
-// Using a function due to better type checks
-// This seems better than having to do type casts everywhere else
-static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
- *reg = (uint16_t)(*reg & ~mask);
-}
+static void edpt0_open(uint8_t rhport);
-// Bits in ISTR are cleared upon writing 0
-static inline void clear_istr_bits(uint16_t mask) {
- USB->ISTR = ~mask;
+TU_ATTR_ALWAYS_INLINE static inline void edpt0_prepare_setup(void) {
+ btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8);
}
-void dcd_init (uint8_t rhport)
-{
- /* Clocks should already be enabled */
- /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */
+//--------------------------------------------------------------------+
+// Inline helper
+//--------------------------------------------------------------------+
- /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL.
- * Here, the RM is followed. */
+TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint8_t dir) {
+ return &xfer_status[epnum][dir];
+}
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
- asm("NOP");
- }
- // Perform USB peripheral reset
- USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ // Follow the RM mentions to use a special ordering of PDWN and FRES
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
}
- reg16_clear_bits(&USB->CNTR, USB_CNTR_PDWN);// Remove powerdown
- // Wait startup time, for F042 and F070, this is <= 1 us.
- for(uint32_t i = 0; i<200; i++) // should be a few us
- {
+
+ // Perform USB peripheral reset
+ FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
asm("NOP");
}
- USB->CNTR = 0; // Enable USB
-
- USB->BTABLE = DCD_STM32_BTABLE_BASE;
- USB->ISTR = 0; // Clear pending interrupts
+ FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
- // Reset endpoints to disabled
- for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
- {
- // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED.
- pcd_set_endpoint(USB,i,0u);
+ // Wait startup time, for F042 and F070, this is <= 1 us.
+ for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
+ asm("NOP");
}
+ FSDEV_REG->CNTR = 0; // Enable USB
- USB->CNTR |= USB_CNTR_RESETM | (USE_SOF ? USB_CNTR_SOFM : 0) | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
- dcd_handle_bus_reset();
-
- // Enable pull-up if supported
- if ( dcd_connect ) dcd_connect(rhport);
-}
-
-// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
-#if defined(USB_BCDR_DPPU)
-
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- USB->BCDR &= ~(USB_BCDR_DPPU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- USB->BCDR |= USB_BCDR_DPPU;
-}
-
-#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
-}
+#if !defined(FSDEV_BUS_32BIT)
+ // BTABLE register does not exist any more on 32-bit bus devices
+ FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE;
#endif
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
- (void) rhport;
- (void) en;
-
- // TODO implement later
-}
-
-// Enable device interrupt
-void dcd_int_enable (uint8_t rhport)
-{
- (void)rhport;
- // Member here forces write to RAM before allowing ISR to execute
- __DSB();
- __ISB();
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
- CFG_TUSB_MCU == OPT_MCU_STM32L4
- NVIC_EnableIRQ(USB_IRQn);
+ FSDEV_REG->ISTR = 0; // Clear pending interrupts
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- NVIC_EnableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
- // shared USB/CAN IRQs to separate CAN and USB IRQs.
- // This dynamically checks if this remap is active to enable the right IRQs.
- #ifdef SYSCFG_CFGR1_USB_IT_RMP
- if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
- {
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
- NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
- }
- else
- #endif
- {
- NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
+ // Reset endpoints to disabled
+ for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED.
+ ep_write(i, 0u, false);
}
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
- NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
- NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
- NVIC_EnableIRQ(USBWakeUp_IRQn);
+ FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM |
+ USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_PMAOVRM;
+ handle_bus_reset(rhport);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- NVIC_EnableIRQ(USB_HP_IRQn);
- NVIC_EnableIRQ(USB_LP_IRQn);
+ // Enable pull-up if supported
+ dcd_connect(rhport);
-#else
- #error Unknown arch in USB driver
-#endif
+ return true;
}
-// Disable device interrupt
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void)rhport;
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
- CFG_TUSB_MCU == OPT_MCU_STM32L4
- NVIC_DisableIRQ(USB_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- NVIC_DisableIRQ(USB_LP_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
- // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
- // shared USB/CAN IRQs to separate CAN and USB IRQs.
- // This dynamically checks if this remap is active to disable the right IRQs.
- #ifdef SYSCFG_CFGR1_USB_IT_RMP
- if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
- {
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
- NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
- }
- else
- #endif
- {
- NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
- NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
+ if (en) {
+ FSDEV_REG->CNTR |= USB_CNTR_SOFM;
+ } else {
+ FSDEV_REG->CNTR &= ~USB_CNTR_SOFM;
}
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
- NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
- NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
- NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- NVIC_DisableIRQ(USB_HP_IRQn);
- NVIC_DisableIRQ(USB_LP_IRQn);
-
-#else
- #error Unknown arch in USB driver
-#endif
-
- // CMSIS has a membar after disabling interrupts
}
// Receive Set Address request, mcu port must also include status IN response
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
- (void) rhport;
- (void) dev_addr;
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void)dev_addr;
// Respond with status
- dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+ dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0);
// DCD can only set address after status for this request is complete.
// do it at dcd_edpt0_status_complete()
}
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void) rhport;
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void)rhport;
- USB->CNTR |= (uint16_t) USB_CNTR_RESUME;
+ FSDEV_REG->CNTR |= USB_CNTR_RESUME;
remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms.
}
-static const tusb_desc_endpoint_t ep0OUT_desc =
-{
- .bLength = sizeof(tusb_desc_endpoint_t),
- .bDescriptorType = TUSB_DESC_ENDPOINT,
-
- .bEndpointAddress = 0x00,
- .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
- .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE,
- .bInterval = 0
-};
+static void handle_bus_reset(uint8_t rhport) {
+ FSDEV_REG->DADDR = 0u; // disable USB Function
-static const tusb_desc_endpoint_t ep0IN_desc =
-{
- .bLength = sizeof(tusb_desc_endpoint_t),
- .bDescriptorType = TUSB_DESC_ENDPOINT,
-
- .bEndpointAddress = 0x80,
- .bmAttributes = { .xfer = TUSB_XFER_CONTROL },
- .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE,
- .bInterval = 0
-};
-
-static void dcd_handle_bus_reset(void)
-{
- //__IO uint16_t * const epreg = &(EPREG(0));
- USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag
-
- // Clear all EPREG (or maybe this is automatic? I'm not sure)
- for(uint32_t i=0; i<STFSDEV_EP_COUNT; i++)
- {
- pcd_set_endpoint(USB,i,0u);
+ for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ // Clear EP allocation status
+ ep_alloc_status[i].ep_num = 0xFF;
+ ep_alloc_status[i].ep_type = 0xFF;
+ ep_alloc_status[i].allocated[0] = false;
+ ep_alloc_status[i].allocated[1] = false;
}
- dcd_pma_alloc_reset();
- dcd_edpt_open (0, &ep0OUT_desc);
- dcd_edpt_open (0, &ep0IN_desc);
+ // Reset PMA allocation
+ ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT;
+
+ edpt0_open(rhport); // open control endpoint (both IN & OUT)
- USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero.
+ FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function
}
// Handle CTR interrupt for the TX/IN direction
-//
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_tx_handler(uint32_t wIstr)
-{
- uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
- uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
+static void handle_ctr_tx(uint32_t ep_id) {
+ uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
- // Verify the CTR_TX bit is set. This was in the ST Micro code,
- // but I'm not sure it's actually necessary?
- if((wEPRegVal & USB_EP_CTR_TX) == 0U)
- {
- return;
- }
+ uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN);
- /* clear int flag */
- pcd_clear_tx_ep_ctr(USB, EPindex);
-
- xfer_ctl_t * xfer = xfer_ctl_ptr(EPindex,TUSB_DIR_IN);
- if((xfer->total_len != xfer->queued_len)) /* TX not complete */
- {
- dcd_transmit_packet(xfer, EPindex);
+ if (ep_is_iso(ep_reg)) {
+ // Ignore spurious interrupts that we don't schedule
+ // host can send IN token while there is no data to send, since ISO does not have NAK
+ // this will result to zero length packet --> trigger interrupt (which cannot be masked)
+ if (!xfer->iso_in_sending) {
+ return;
+ }
+ xfer->iso_in_sending = false;
+ uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1;
+ btable_set_count(ep_id, buf_id, 0);
}
- else /* TX Complete */
- {
- dcd_event_xfer_complete(0, (uint8_t)(0x80 + EPindex), xfer->total_len, XFER_RESULT_SUCCESS, true);
+
+ if (xfer->total_len != xfer->queued_len) {
+ dcd_transmit_packet(xfer, ep_id);
+ } else {
+ dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true);
}
}
-// Handle CTR interrupt for the RX/OUT direction
-//
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
-{
- uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
- uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
- uint32_t count = pcd_get_ep_rx_cnt(USB,EPindex);
+static void handle_ctr_setup(uint32_t ep_id) {
+ uint16_t rx_count = btable_get_count(ep_id, BTABLE_BUF_RX);
+ uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
+ uint8_t setup_packet[8] TU_ATTR_ALIGNED(4);
- xfer_ctl_t *xfer = xfer_ctl_ptr(EPindex,TUSB_DIR_OUT);
+ dcd_read_packet_memory(setup_packet, rx_addr, rx_count);
- // Verify the CTR_RX bit is set. This was in the ST Micro code,
- // but I'm not sure it's actually necessary?
- if((wEPRegVal & USB_EP_CTR_RX) == 0U)
- {
- return;
- }
-
- if((EPindex == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */
- {
- // The setup_received function uses memcpy, so this must first copy the setup data into
- // user memory, to allow for the 32-bit access that memcpy performs.
- uint8_t userMemBuf[8];
- /* Get SETUP Packet*/
- if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again.
- {
- // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
- pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
- pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
- dcd_read_packet_memory(userMemBuf, *pcd_ep_rx_address_ptr(USB,EPindex), 8);
- dcd_event_setup_received(0, (uint8_t*)userMemBuf, true);
- }
- }
- else
- {
- // Clear RX CTR interrupt flag
- if(EPindex != 0u)
- {
- pcd_clear_rx_ep_ctr(USB, EPindex);
- }
+ // Clear CTR RX if another setup packet arrived before this, it will be discarded
+ ep_write_clear_ctr(ep_id, TUSB_DIR_OUT);
- if (count != 0U)
- {
-#if 0 // TODO support dcd_edpt_xfer_fifo API
- if (xfer->ff)
- {
- dcd_read_packet_memory_ff(xfer->ff, *pcd_ep_rx_address_ptr(USB,EPindex), count);
- }
- else
-#endif
- {
- dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), *pcd_ep_rx_address_ptr(USB,EPindex), count);
- }
+ // Setup packet should always be 8 bytes. If not, we probably missed the packet
+ if (rx_count == 8) {
+ dcd_event_setup_received(0, (uint8_t*) setup_packet, true);
+ // Hardware should reset EP0 RX/TX to NAK and both toggle to 1
+ } else {
+ // Missed setup packet !!!
+ TU_BREAKPOINT();
+ edpt0_prepare_setup();
+ }
+}
- xfer->queued_len = (uint16_t)(xfer->queued_len + count);
- }
+// Handle CTR interrupt for the RX/OUT direction
+static void handle_ctr_rx(uint32_t ep_id) {
+ uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
+ uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
+ bool const is_iso = ep_is_iso(ep_reg);
+ xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
- if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
- {
- /* RX COMPLETE */
- dcd_event_xfer_complete(0, EPindex, xfer->queued_len, XFER_RESULT_SUCCESS, true);
- // Though the host could still send, we don't know.
- // Does the bulk pipe need to be reset to valid to allow for a ZLP?
- }
- else
- {
- uint32_t remaining = (uint32_t)xfer->total_len - (uint32_t)xfer->queued_len;
- if(remaining >= xfer->max_packet_size) {
- pcd_set_ep_rx_cnt(USB, EPindex,xfer->max_packet_size);
- } else {
- pcd_set_ep_rx_cnt(USB, EPindex,remaining);
- }
- pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID);
- }
+ uint8_t buf_id;
+ if (is_iso) {
+ buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered
+ } else {
+ buf_id = BTABLE_BUF_RX;
}
+ uint16_t const rx_count = btable_get_count(ep_id, buf_id);
+ uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id);
- // For EP0, prepare to receive another SETUP packet.
- // Clear CTR last so that a new packet does not overwrite the packing being read.
- // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared)
- if(EPindex == 0u)
- {
- // Always be prepared for a status packet...
- pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE);
- pcd_clear_rx_ep_ctr(USB, EPindex);
+ if (xfer->ff) {
+ dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count);
+ } else {
+ dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count);
}
-}
+ xfer->queued_len += rx_count;
-static void dcd_ep_ctr_handler(void)
-{
- uint32_t wIstr;
+ if ((rx_count < xfer->max_packet_size) || (xfer->queued_len >= xfer->total_len)) {
+ // all bytes received or short packet
- /* stay in loop while pending interrupts */
- while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U)
- {
+ // For ch32v203: reset rx bufsize to mps to prevent race condition to cause PMAOVR (occurs with msc write10)
+ btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, xfer->max_packet_size);
- if ((wIstr & USB_ISTR_DIR) == 0U) /* TX/IN */
- {
- dcd_ep_ctr_tx_handler(wIstr);
- }
- else /* RX/OUT*/
- {
- dcd_ep_ctr_rx_handler(wIstr);
+ dcd_event_xfer_complete(0, ep_num, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+
+ // ch32 seems to unconditionally accept ZLP on EP0 OUT, which can incorrectly use queued_len of previous
+ // transfer. So reset total_len and queued_len to 0.
+ xfer->total_len = xfer->queued_len = 0;
+ } else {
+ // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always
+ if (!is_iso) {
+ uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt);
}
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_OUT); // will change RX Status, reserved other toggle bits
+ ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_VALID);
+ ep_write(ep_id, ep_reg, false);
}
}
void dcd_int_handler(uint8_t rhport) {
+ uint32_t int_status = FSDEV_REG->ISTR;
- (void) rhport;
-
- uint32_t int_status = USB->ISTR;
- //const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
- // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
- // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
-
- // The ST driver loops here on the CTR bit, but that loop has been moved into the
- // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't
- // be triggered repeatedly.
+ /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
+ if (int_status & USB_ISTR_SOF) {
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
+ dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true);
+ }
- if(int_status & USB_ISTR_RESET) {
+ if (int_status & USB_ISTR_RESET) {
// USBRST is start of reset.
- clear_istr_bits(USB_ISTR_RESET);
- dcd_handle_bus_reset();
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
+ handle_bus_reset(rhport);
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
return; // Don't do the rest of the things here; perhaps they've been cleared?
}
- if (int_status & USB_ISTR_CTR)
- {
- /* servicing of the endpoint correct transfer interrupt */
- /* clear of the CTR flag into the sub */
- dcd_ep_ctr_handler();
- }
+ if (int_status & USB_ISTR_WKUP) {
+ FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE;
+ FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP;
- if (int_status & USB_ISTR_WKUP)
- {
- reg16_clear_bits(&USB->CNTR, USB_CNTR_LPMODE);
- reg16_clear_bits(&USB->CNTR, USB_CNTR_FSUSP);
- clear_istr_bits(USB_ISTR_WKUP);
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
}
- if (int_status & USB_ISTR_SUSP)
- {
+ if (int_status & USB_ISTR_SUSP) {
/* Suspend is asserted for both suspend and unplug events. without Vbus monitoring,
* these events cannot be differentiated, so we only trigger suspend. */
/* Force low-power mode in the macrocell */
- USB->CNTR |= USB_CNTR_FSUSP;
- USB->CNTR |= USB_CNTR_LPMODE;
+ FSDEV_REG->CNTR |= USB_CNTR_FSUSP;
+ FSDEV_REG->CNTR |= USB_CNTR_LPMODE;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
- clear_istr_bits(USB_ISTR_SUSP);
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
}
-#if USE_SOF
- if(int_status & USB_ISTR_SOF) {
- clear_istr_bits(USB_ISTR_SOF);
- dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+ if (int_status & USB_ISTR_ESOF) {
+ if (remoteWakeCountdown == 1u) {
+ FSDEV_REG->CNTR &= ~USB_CNTR_RESUME;
+ }
+ if (remoteWakeCountdown > 0u) {
+ remoteWakeCountdown--;
+ }
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
}
-#endif
- if(int_status & USB_ISTR_ESOF) {
- if(remoteWakeCountdown == 1u)
- {
- USB->CNTR &= (uint16_t)(~USB_CNTR_RESUME);
+ // loop to handle all pending CTR interrupts
+ while (FSDEV_REG->ISTR & USB_ISTR_CTR) {
+ // skip DIR bit, and use CTR TX/RX instead, since there is chance we have both TX/RX completed in one interrupt
+ uint32_t const ep_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID;
+ uint32_t const ep_reg = ep_read(ep_id);
+
+ if (ep_reg & USB_EP_CTR_RX) {
+ #ifdef FSDEV_BUS_32BIT
+ /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
+ * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf
+ * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers
+ * Description:
+ * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses
+ * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct.
+ * Workaround:
+ * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
+ * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
+ * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code
+ * also takes time, so we'll wait 60 cycles (count = 20).
+ * - Since Low Speed mode is not supported/popular, we will ignore it for now.
+ *
+ * Note: this errata may also apply to G0, U5, H5 etc.
+ */
+ volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver
+ while (cycle_count > 0U) {
+ cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
+ }
+ #endif
+
+ if (ep_reg & USB_EP_SETUP) {
+ handle_ctr_setup(ep_id); // CTR will be clear after copied setup packet
+ } else {
+ ep_write_clear_ctr(ep_id, TUSB_DIR_OUT);
+ handle_ctr_rx(ep_id);
+ }
}
- if(remoteWakeCountdown > 0u)
- {
- remoteWakeCountdown--;
+
+ if (ep_reg & USB_EP_CTR_TX) {
+ ep_write_clear_ctr(ep_id, TUSB_DIR_IN);
+ handle_ctr_tx(ep_id);
}
- clear_istr_bits(USB_ISTR_ESOF);
+ }
+
+ if (int_status & USB_ISTR_PMAOVR) {
+ TU_BREAKPOINT();
+ FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_PMAOVR;
}
}
@@ -675,498 +472,508 @@ void dcd_int_handler(uint8_t rhport) {
// Invoked when a control transfer's status stage is complete.
// May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
+ (void)rhport;
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
- request->bRequest == TUSB_REQ_SET_ADDRESS )
- {
- uint8_t const dev_addr = (uint8_t) request->wValue;
-
- // Setting new address after the whole request is complete
- reg16_clear_bits(&USB->DADDR, USB_DADDR_ADD);
- USB->DADDR = (uint16_t)(USB->DADDR | dev_addr); // leave the enable bit set
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ uint8_t const dev_addr = (uint8_t)request->wValue;
+ FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr);
}
-}
-static void dcd_pma_alloc_reset(void)
-{
- ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each)
- //TU_LOG2("dcd_pma_alloc_reset()\r\n");
- for(uint32_t i=0; i<MAX_EP_COUNT; i++)
- {
- xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_alloc_size = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_alloc_size = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_ptr = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_ptr = 0U;
- }
+ edpt0_prepare_setup();
}
/***
* Allocate a section of PMA
- *
- * If the EP number has already been allocated, and the new allocation
- * is larger than the old allocation, then this will fail with a TU_ASSERT.
- * (This is done to simplify the code. More complicated algorithms could be used)
- *
+ * In case of double buffering, high 16bit is the address of 2nd buffer
* During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
*/
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, size_t length)
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t* epXferCtl = xfer_ctl_ptr(epnum,dir);
+ uint8_t blsize, num_block;
+ uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block);
+ (void) blsize;
+ (void) num_block;
+
+ uint32_t addr = ep_buf_ptr;
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
- if(epXferCtl->pma_alloc_size != 0U)
- {
- //TU_LOG2("dcd_pma_alloc(%x,%x)=%x (cached)\r\n",ep_addr,length,epXferCtl->pma_ptr);
- // Previously allocated
- TU_ASSERT(length <= epXferCtl->pma_alloc_size, 0xFFFF); // Verify no larger than previous alloc
- return epXferCtl->pma_ptr;
+ if (dbuf) {
+ addr |= ((uint32_t)ep_buf_ptr) << 16;
+ ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
}
-
- uint16_t addr = ep_buf_ptr;
- ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
-
- // Verify no overflow
- TU_ASSERT(ep_buf_ptr <= PMA_LENGTH, 0xFFFF);
-
- epXferCtl->pma_ptr = addr;
- epXferCtl->pma_alloc_size = length;
- //TU_LOG2("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
+
+ // Verify packet buffer is not overflowed
+ TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
return addr;
}
/***
- * Free a block of PMA space
+ * Allocate hardware endpoint
*/
-static void dcd_pma_free(uint8_t ep_addr)
+static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- // Presently, this should never be called for EP0 IN/OUT
- TU_ASSERT(open_ep_count > 2, /**/);
- TU_ASSERT(xfer_ctl_ptr(epnum,dir)->max_packet_size != 0, /**/);
- open_ep_count--;
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- // If count is 2, only EP0 should be open, so allocations can be mostly reset.
+ for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
+ // Check if already allocated
+ if (ep_alloc_status[i].allocated[dir] &&
+ ep_alloc_status[i].ep_type == ep_type &&
+ ep_alloc_status[i].ep_num == epnum) {
+ return i;
+ }
- if(open_ep_count == 2)
- {
- ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT + 2*CFG_TUD_ENDPOINT0_SIZE; // 8 bytes per endpoint (two TX and two RX words, each), and EP0
+ // If EP of current direction is not allocated
+ // Except for ISO endpoint, both direction should be free
+ if (!ep_alloc_status[i].allocated[dir] &&
+ (ep_type != TUSB_XFER_ISOCHRONOUS || !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
+ if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) {
+ ep_alloc_status[i].ep_num = epnum;
+ ep_alloc_status[i].ep_type = ep_type;
+ ep_alloc_status[i].allocated[dir] = true;
- // Skip EP0
- for(uint32_t i=1; i<MAX_EP_COUNT; i++)
- {
- xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_alloc_size = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_alloc_size = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_OUT)->pma_ptr = 0U;
- xfer_ctl_ptr(i,TUSB_DIR_IN)->pma_ptr = 0U;
+ return i;
+ }
+ }
}
}
+
+ // Allocation failed
+ TU_ASSERT(0);
}
-// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers,
-// so I'm using the #define from HAL here, instead.
+void edpt0_open(uint8_t rhport) {
+ (void) rhport;
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
+ dcd_ep_alloc(0x0, TUSB_XFER_CONTROL);
+ dcd_ep_alloc(0x80, TUSB_XFER_CONTROL);
+
+ xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][0].ep_idx = 0;
+
+ xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][1].ep_idx = 0;
+
+ uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+ uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+
+ btable_set_addr(0, BTABLE_BUF_RX, pma_addr0);
+ btable_set_addr(0, BTABLE_BUF_TX, pma_addr1);
+
+ uint32_t ep_reg = ep_read(0) & ~USB_EPREG_MASK; // only get toggle bits
+ ep_reg |= USB_EP_CONTROL;
+ ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_NAK);
+ ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
+ // no need to explicitly set DTOG bits since we aren't masked DTOG bit
+
+ edpt0_prepare_setup(); // prepare for setup packet
+ ep_write(0, ep_reg, false);
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
(void)rhport;
- uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- const uint16_t epMaxPktSize = tu_edpt_packet_size(p_endpoint_desc);
- uint16_t pma_addr;
- uint32_t wType;
-
- // Isochronous not supported (yet), and some other driver assumptions.
- TU_ASSERT(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
- TU_ASSERT(epnum < MAX_EP_COUNT);
+ uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ const uint16_t packet_size = tu_edpt_packet_size(desc_ep);
+ uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer);
+ TU_ASSERT(ep_idx < FSDEV_EP_COUNT);
+
+ uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK;
+ ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_TX | USB_EP_CTR_RX;
// Set type
- switch(p_endpoint_desc->bmAttributes.xfer) {
- case TUSB_XFER_CONTROL:
- wType = USB_EP_CONTROL;
- break;
-#if (0)
- case TUSB_XFER_ISOCHRONOUS: // FIXME: Not yet supported
- wType = USB_EP_ISOCHRONOUS;
- break;
-#endif
+ switch (desc_ep->bmAttributes.xfer) {
+ case TUSB_XFER_BULK:
+ ep_reg |= USB_EP_BULK;
+ break;
+ case TUSB_XFER_INTERRUPT:
+ ep_reg |= USB_EP_INTERRUPT;
+ break;
- case TUSB_XFER_BULK:
- wType = USB_EP_CONTROL;
- break;
+ default:
+ // Note: ISO endpoint should use alloc / active functions
+ TU_ASSERT(false);
+ }
+
+ /* Create a packet memory buffer area. */
+ uint16_t pma_addr = dcd_pma_alloc(packet_size, false);
+ btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
+
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer->max_packet_size = packet_size;
+ xfer->ep_idx = ep_idx;
- case TUSB_XFER_INTERRUPT:
- wType = USB_EP_INTERRUPT;
- break;
+ ep_change_status(&ep_reg, dir, EP_STAT_NAK);
+ ep_change_dtog(&ep_reg, dir, 0);
- default:
- TU_ASSERT(false);
+ // reserve other direction toggle bits
+ if (dir == TUSB_DIR_IN) {
+ ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX);
+ } else {
+ ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX);
}
- pcd_set_eptype(USB, epnum, wType);
- pcd_set_ep_address(USB, epnum, epnum);
- // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
- // or being double-buffered (bulk endpoints)
- pcd_clear_ep_kind(USB,0);
+ ep_write(ep_idx, ep_reg, true);
- pma_addr = dcd_pma_alloc(p_endpoint_desc->bEndpointAddress, epMaxPktSize);
+ return true;
+}
- if(dir == TUSB_DIR_IN)
- {
- *pcd_ep_tx_address_ptr(USB, epnum) = pma_addr;
- pcd_set_ep_tx_cnt(USB, epnum, epMaxPktSize);
- pcd_clear_tx_dtog(USB, epnum);
- pcd_set_ep_tx_status(USB,epnum,USB_EP_TX_NAK);
- }
- else
- {
- *pcd_ep_rx_address_ptr(USB, epnum) = pma_addr;
- pcd_set_ep_rx_cnt(USB, epnum, epMaxPktSize);
- pcd_clear_rx_dtog(USB, epnum);
- pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_NAK);
+void dcd_edpt_close_all(uint8_t rhport) {
+ dcd_int_disable(rhport);
+
+ for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) {
+ // Reset endpoint
+ ep_write(i, 0, false);
+ // Clear EP allocation status
+ ep_alloc_status[i].ep_num = 0xFF;
+ ep_alloc_status[i].ep_type = 0xFF;
+ ep_alloc_status[i].allocated[0] = false;
+ ep_alloc_status[i].allocated[1] = false;
}
- xfer_ctl_ptr(epnum, dir)->max_packet_size = epMaxPktSize;
+ dcd_int_enable(rhport);
- return true;
+ // Reset PMA allocation
+ ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
- (void) rhport;
- // TODO implement dcd_edpt_close_all()
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void)rhport;
+
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ 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
+ uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
+ uint16_t pma_addr2 = pma_addr >> 16;
+#else
+ uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false);
+ uint16_t pma_addr2 = pma_addr;
+#endif
+
+ btable_set_addr(ep_idx, 0, pma_addr);
+ btable_set_addr(ep_idx, 1, pma_addr2);
+
+ xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
+ xfer->ep_idx = ep_idx;
+
+ return true;
}
-/**
- * Close an endpoint.
- *
- * This function may be called with interrupts enabled or disabled.
- *
- * This also clears transfers in progress, should there be any.
- */
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
(void)rhport;
- uint32_t const epnum = tu_edpt_number(ep_addr);
- uint32_t const dir = tu_edpt_dir(ep_addr);
-
- if(dir == TUSB_DIR_IN)
- {
- pcd_set_ep_tx_status(USB,epnum,USB_EP_TX_DIS);
- }
- else
- {
- pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_DIS);
- }
+ uint8_t const ep_addr = desc_ep->bEndpointAddress;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
+
+ uint8_t const ep_idx = xfer->ep_idx;
+
+ xfer->max_packet_size = tu_edpt_packet_size(desc_ep);
- dcd_pma_free(ep_addr);
+ 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;
+ 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);
+
+ ep_write(ep_idx, ep_reg, true);
+
+ return true;
}
// Currently, single-buffered, and only 64 bytes at a time (max)
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
+ uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+ uint32_t ep_reg = ep_read(ep_ix) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
-{
- uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len);
+ bool const is_iso = ep_is_iso(ep_reg);
- if(len > xfer->max_packet_size) // max packet size for FS transfer
- {
- len = xfer->max_packet_size;
+ uint8_t buf_id;
+ if (is_iso) {
+ buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0;
+ } else {
+ buf_id = BTABLE_BUF_TX;
}
- uint16_t oldAddr = *pcd_ep_tx_address_ptr(USB,ep_ix);
+ uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id);
-#if 0 // TODO support dcd_edpt_xfer_fifo API
- if (xfer->ff)
- {
- dcd_write_packet_memory_ff(xfer->ff, oldAddr, len);
+ if (xfer->ff) {
+ dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len);
+ } else {
+ dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len);
}
- else
-#endif
- {
- dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
- }
- xfer->queued_len = (uint16_t)(xfer->queued_len + len);
+ xfer->queued_len += len;
- pcd_set_ep_tx_cnt(USB,ep_ix,len);
- pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID);
+ btable_set_count(ep_ix, buf_id, len);
+ ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_VALID);
+
+ if (is_iso) {
+ xfer->iso_in_sending = true;
+ }
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN); // only change TX Status, reserve other toggle bits
+ ep_write(ep_ix, ep_reg, true);
}
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
(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_ptr(ep_num, dir);
+ uint8_t const ep_idx = xfer->ep_idx;
- xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
+ if (dir == TUSB_DIR_IN) {
+ dcd_transmit_packet(xfer, ep_idx);
+ } else {
+ uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir);
- xfer->buffer = buffer;
- // xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
- xfer->total_len = total_bytes;
- xfer->queued_len = 0;
+ uint16_t cnt = tu_min16(xfer->total_len, xfer->max_packet_size);
- if ( dir == TUSB_DIR_OUT )
- {
- // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid
- // buffer for the control endpoint.
- if (epnum == 0 && buffer == NULL)
- {
- xfer->buffer = (uint8_t*)_setup_packet;
- }
- if(total_bytes > xfer->max_packet_size)
- {
- pcd_set_ep_rx_cnt(USB,epnum,xfer->max_packet_size);
+ if (ep_is_iso(ep_reg)) {
+ btable_set_rx_bufsize(ep_idx, 0, cnt);
+ btable_set_rx_bufsize(ep_idx, 1, cnt);
} else {
- pcd_set_ep_rx_cnt(USB,epnum,total_bytes);
+ btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt);
}
- pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
- }
- else // IN
- {
- dcd_transmit_packet(xfer,epnum);
+
+ ep_change_status(&ep_reg, dir, EP_STAT_VALID);
+ ep_write(ep_idx, ep_reg, true);
}
+
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;
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+
+ return edpt_xfer(rhport, ep_num, dir);
+}
- xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) {
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
xfer->buffer = NULL;
- // xfer->ff = ff; // TODO support dcd_edpt_xfer_fifo API
+ xfer->ff = ff;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
- if ( dir == TUSB_DIR_OUT )
- {
- if(total_bytes > xfer->max_packet_size)
- {
- pcd_set_ep_rx_cnt(USB,epnum,xfer->max_packet_size);
- } else {
- pcd_set_ep_rx_cnt(USB,epnum,total_bytes);
- }
- pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
- }
- else // IN
- {
- dcd_transmit_packet(xfer,epnum);
- }
- return true;
+ return edpt_xfer(rhport, ep_num, dir);
}
-#endif
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ uint8_t const ep_idx = xfer->ep_idx;
- if (ep_addr & 0x80)
- { // IN
- pcd_set_ep_tx_status(USB, ep_addr & 0x7F, USB_EP_TX_STALL);
- }
- else
- { // OUT
- pcd_set_ep_rx_status(USB, ep_addr, USB_EP_RX_STALL);
- }
+ uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir);
+ ep_change_status(&ep_reg, dir, EP_STAT_STALL);
+
+ ep_write(ep_idx, ep_reg, true);
}
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
- if (ep_addr & 0x80)
- { // IN
- ep_addr &= 0x7F;
-
- pcd_set_ep_tx_status(USB,ep_addr, USB_EP_TX_NAK);
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+ xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+ uint8_t const ep_idx = xfer->ep_idx;
- /* Reset to DATA0 if clearing stall condition. */
- pcd_clear_tx_dtog(USB,ep_addr);
- }
- else
- { // OUT
- /* Reset to DATA0 if clearing stall condition. */
- pcd_clear_rx_dtog(USB,ep_addr);
+ uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits
+ ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir);
- pcd_set_ep_rx_status(USB,ep_addr, USB_EP_RX_NAK);
+ if (!ep_is_iso(ep_reg)) {
+ ep_change_status(&ep_reg, dir, EP_STAT_NAK);
}
+ ep_change_dtog(&ep_reg, dir, 0); // Reset to DATA0
+ ep_write(ep_idx, ep_reg, true);
}
-// Packet buffer access can only be 8- or 16-bit.
-/**
- * @brief Copy a buffer from user memory area to packet memory area (PMA).
- * This uses byte-access for user memory (so support non-aligned buffers)
- * and 16-bit access for packet memory.
- * @param dst, byte address in PMA; must be 16-bit aligned
- * @param src pointer to user memory area.
- * @param wPMABufAddr address into PMA.
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes)
-{
- uint32_t n = ((uint32_t)wNBytes + 1U) >> 1U;
- uint32_t i;
- uint16_t temp1, temp2;
- const uint8_t * srcVal;
+//--------------------------------------------------------------------+
+// PMA read/write
+//--------------------------------------------------------------------+
- // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
- // it volatile so that it won't do that.
- __IO uint16_t *pdwVal;
+// Write to packet memory area (PMA) from user memory
+// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
+// - Uses unaligned for RAM (since M0 cannot access unaligned address)
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) {
+ if (nbytes == 0) return true;
+ uint32_t n_write = nbytes / FSDEV_BUS_SIZE;
- srcVal = src;
- pdwVal = &pma[PMA_STRIDE*(dst>>1)];
+ fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(dst);
+ const uint8_t *src8 = src;
- for (i = n; i != 0; i--)
- {
- temp1 = (uint16_t) *srcVal;
- srcVal++;
- temp2 = temp1 | ((uint16_t)((uint16_t) ((*srcVal) << 8U))) ;
- *pdwVal = temp2;
- pdwVal += PMA_STRIDE;
- srcVal++;
+ while (n_write--) {
+ pma_buf->value = fsdevbus_unaligned_read(src8);
+ src8 += FSDEV_BUS_SIZE;
+ pma_buf++;
}
- return true;
-}
-#if 0 // TODO support dcd_edpt_xfer_fifo API
-/**
- * @brief Copy from FIFO to packet memory area (PMA).
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
+ // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit
+ uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1);
+ if (odd) {
+ fsdev_bus_t temp = 0;
+ for(uint16_t i = 0; i < odd; i++) {
+ temp |= *src8++ << (i * 8);
+ }
+ pma_buf->value = temp;
+ }
-// THIS FUNCTION IS UNTESTED
+ return true;
+}
-static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes)
-{
- // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
- // Check for first linear part
- void * src;
- uint16_t len = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes); // We want to read from the FIFO - THIS FUNCTION CHANGED!!!
- TU_VERIFY(len && dcd_write_packet_memory(dst, src, len)); // and write it into the PMA
- tu_fifo_advance_read_pointer(ff, len);
+// Read from packet memory area (PMA) to user memory.
+// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
+// - Uses unaligned for RAM (since M0 cannot access unaligned address)
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) {
+ if (nbytes == 0) return true;
+ uint32_t n_read = nbytes / FSDEV_BUS_SIZE;
- // Check for wrapped part
- if (len < wNBytes)
- {
- // Get remaining wrapped length
- uint16_t len2 = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes - len);
- TU_VERIFY(len2);
+ fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(src);
+ uint8_t *dst8 = (uint8_t *)dst;
- // Update destination pointer
- dst += len;
+ while (n_read--) {
+ fsdevbus_unaligned_write(dst8, (fsdev_bus_t ) pma_buf->value);
+ dst8 += FSDEV_BUS_SIZE;
+ pma_buf++;
+ }
- // Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
- if (len % 2) // If len is uneven there is a byte left to copy
- {
- // Since PMA can accessed only 16 bit-wise we copy the last byte again
- tu_fifo_backward_read_pointer(ff, 1); // Move one byte back and copy two bytes for the PMA
- tu_fifo_read_n(ff, (void *) &pma[PMA_STRIDE*(dst>>1)], 2); // Since EP FIFOs must be of item size 1 this is safe to do
- dst++;
- len2--;
+ // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit
+ uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1);
+ if (odd) {
+ fsdev_bus_t temp = pma_buf->value;
+ while (odd--) {
+ *dst8++ = (uint8_t) (temp & 0xfful);
+ temp >>= 8;
}
-
- TU_VERIFY(dcd_write_packet_memory(dst, src, len2));
- tu_fifo_advance_write_pointer(ff, len2);
}
return true;
}
-#endif
-/**
- * @brief Copy a buffer from packet memory area (PMA) to user memory area.
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes)
-{
- uint32_t n = (uint32_t)wNBytes >> 1U;
- uint32_t i;
- // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
- // it volatile so that it won't do that.
- __IO const uint16_t *pdwVal;
- uint32_t temp;
+// Write to PMA from FIFO
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) {
+ if (wNBytes == 0) return true;
- pdwVal = &pma[PMA_STRIDE*(src>>1)];
- uint8_t *dstVal = (uint8_t*)dst;
+ // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_read_info(ff, &info);
- for (i = n; i != 0U; i--)
- {
- temp = *pdwVal;
- pdwVal += PMA_STRIDE;
- *dstVal++ = ((temp >> 0) & 0xFF);
- *dstVal++ = ((temp >> 8) & 0xFF);
- }
+ uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin);
+ uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap);
+ uint16_t const cnt_total = cnt_lin + cnt_wrap;
+
+ // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
+ // last lin byte will be combined with wrapped part To ensure PMA is always access aligned
+ uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1);
+ uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1);
+ uint8_t const *src8 = (uint8_t const*) info.ptr_lin;
+
+ // write even linear part
+ dcd_write_packet_memory(dst, src8, lin_even);
+ dst += lin_even;
+ src8 += lin_even;
- if (wNBytes % 2)
- {
- temp = *pdwVal;
- pdwVal += PMA_STRIDE;
- *dstVal++ = ((temp >> 0) & 0xFF);
+ if (lin_odd == 0) {
+ src8 = (uint8_t const*) info.ptr_wrap;
+ } else {
+ // Combine last linear bytes + first wrapped bytes to form fsdev bus width data
+ fsdev_bus_t temp = 0;
+ uint16_t i;
+ for(i = 0; i < lin_odd; i++) {
+ temp |= *src8++ << (i * 8);
+ }
+
+ src8 = (uint8_t const*) info.ptr_wrap;
+ for(; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) {
+ temp |= *src8++ << (i * 8);
+ }
+
+ dcd_write_packet_memory(dst, &temp, FSDEV_BUS_SIZE);
+ dst += FSDEV_BUS_SIZE;
}
+
+ // write the rest of the wrapped part
+ dcd_write_packet_memory(dst, src8, cnt_wrap);
+
+ tu_fifo_advance_read_pointer(ff, cnt_total);
return true;
}
-#if 0 // TODO support dcd_edpt_xfer_fifo API
-/**
- * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
- * Uses byte-access of system memory and 16-bit access of packet memory
- * @param wNBytes no. of bytes to be copied.
- * @retval None
- */
+// Read from PMA to FIFO
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) {
+ if (wNBytes == 0) return true;
-// THIS FUNCTION IS UNTESTED
-
-static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes)
-{
// Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
// Check for first linear part
- void * dst;
- uint16_t len = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes); // THIS FUNCTION CHANGED!!!!
- TU_VERIFY(len && dcd_read_packet_memory(dst, src, len));
- tu_fifo_advance_write_pointer(ff, len);
+ tu_fifo_buffer_info_t info;
+ tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO
- // Check for wrapped part
- if (len < wNBytes)
- {
- // Get remaining wrapped length
- uint16_t len2 = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes - len);
- TU_VERIFY(len2);
+ uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin);
+ uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap);
+ uint16_t cnt_total = cnt_lin + cnt_wrap;
- // Update source pointer
- src += len;
+ // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
+ // last lin byte will be combined with wrapped part To ensure PMA is always access aligned
- // Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
- if (len % 2) // If len is uneven there is a byte left to copy
- {
- uint32_t temp = pma[PMA_STRIDE*(src>>1)];
- *((uint8_t *)dst++) = ((temp >> 8) & 0xFF);
- src++;
- len2--;
+ uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1);
+ uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1);
+ uint8_t *dst8 = (uint8_t *) info.ptr_lin;
+
+ // read even linear part
+ dcd_read_packet_memory(dst8, src, lin_even);
+ dst8 += lin_even;
+ src += lin_even;
+
+ if (lin_odd == 0) {
+ dst8 = (uint8_t *) info.ptr_wrap;
+ } else {
+ // Combine last linear bytes + first wrapped bytes to form fsdev bus width data
+ fsdev_bus_t temp;
+ dcd_read_packet_memory(&temp, src, FSDEV_BUS_SIZE);
+ src += FSDEV_BUS_SIZE;
+
+ uint16_t i;
+ for (i = 0; i < lin_odd; i++) {
+ *dst8++ = (uint8_t) (temp & 0xfful);
+ temp >>= 8;
}
- TU_VERIFY(dcd_read_packet_memory(dst, src, len2));
- tu_fifo_advance_write_pointer(ff, len2);
+ dst8 = (uint8_t *) info.ptr_wrap;
+ for (; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) {
+ *dst8++ = (uint8_t) (temp & 0xfful);
+ temp >>= 8;
+ }
}
+ // read the rest of the wrapped part
+ dcd_read_packet_memory(dst8, src, cnt_wrap);
+
+ tu_fifo_advance_write_pointer(ff, cnt_total);
return true;
}
#endif
-
-#endif
-
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
deleted file mode 100644
index 920d12c1b..000000000
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev_pvt_st.h
+++ /dev/null
@@ -1,426 +0,0 @@
-/**
- ******************************************************************************
- * @file dcd_stm32f0_pvt_st.h
- * @brief DCD utilities from ST code
- ******************************************************************************
- * @attention
- *
- * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
- * <h2><center>&copy; parts COPYRIGHT(c) N Conrad</center></h2>
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of STMicroelectronics nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- **********/
-
-// This file contains source copied from ST's HAL, and thus should have their copyright statement.
-
-// PMA_LENGTH is PMA buffer size in bytes.
-// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
-// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
-
-#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-
-#if defined(STM32F042x6) || \
- defined(STM32F070x6) || defined(STM32F070xB) || \
- defined(STM32F072xB) || \
- defined(STM32F078xx)
- #include "stm32f0xx.h"
- #define PMA_LENGTH (1024u)
- // F0x2 models are crystal-less
- // All have internal D+ pull-up
- // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
- // PMA dedicated to USB (no sharing with CAN)
-
-#elif defined(STM32F1_FSDEV)
- #include "stm32f1xx.h"
- #define PMA_LENGTH (512u)
- // NO internal Pull-ups
- // *B, and *C: 2 x 16 bits/word
-
- // F1 names this differently from the rest
- #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
-
-#elif defined(STM32F302xB) || defined(STM32F302xC) || \
- defined(STM32F303xB) || defined(STM32F303xC) || \
- defined(STM32F373xC)
- #include "stm32f3xx.h"
- #define PMA_LENGTH (512u)
- // NO internal Pull-ups
- // *B, and *C: 1 x 16 bits/word
- // PMA dedicated to USB (no sharing with CAN)
-
-#elif defined(STM32F302x6) || defined(STM32F302x8) || \
- defined(STM32F302xD) || defined(STM32F302xE) || \
- defined(STM32F303xD) || defined(STM32F303xE)
- #include "stm32f3xx.h"
- #define PMA_LENGTH (1024u)
- // 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.
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
- #include "stm32l0xx.h"
- #define PMA_LENGTH (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
- #include "stm32l1xx.h"
- #define PMA_LENGTH (512u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
- #include "stm32g4xx.h"
- #define PMA_LENGTH (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
- #include "stm32wbxx.h"
- #define PMA_LENGTH (1024u)
- /* ST provided header has incorrect value */
- #undef USB_PMAADDR
- #define USB_PMAADDR USB1_PMAADDR
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
- #include "stm32l4xx.h"
- #define PMA_LENGTH (1024u)
-
-#else
- #error You are using an untested or unimplemented STM32 variant. Please update the driver.
- // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4
-#endif
-
-// For purposes of accessing the packet
-#if ((PMA_LENGTH) == 512u)
- #define PMA_STRIDE (2u)
-#elif ((PMA_LENGTH) == 1024u)
- #define PMA_STRIDE (1u)
-#endif
-
-// And for type-safety create a new macro for the volatile address of PMAADDR
-// The compiler should warn us if we cast it to a non-volatile type?
-// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
-static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
-
-// prototypes
-static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum);
-static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum);
-static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wRegValue);
-
-
-/* SetENDPOINT */
-static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wRegValue)
-{
- __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpNum*2u);
- *reg = (uint16_t)wRegValue;
-}
-
-/* GetENDPOINT */
-static inline uint16_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpNum) {
- __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpNum*2u);
- return *reg;
-}
-
-static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wType)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= (uint32_t)USB_EP_T_MASK;
- regVal |= wType;
- regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-
-static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EP_T_FIELD;
- return regVal;
-}
-/**
- * @brief Clears bit CTR_RX / CTR_TX in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @retval None
- */
-static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPREG_MASK;
- regVal &= ~USB_EP_CTR_RX;
- regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0)
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPREG_MASK;
- regVal &= ~USB_EP_CTR_TX;
- regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
- pcd_set_endpoint(USBx, bEpNum,regVal);
-}
-/**
- * @brief gets counter of the tx buffer.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @retval Counter value
- */
-static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpNum);
- return *regPtr & 0x3ffU;
-}
-
-static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpNum);
- return *regPtr & 0x3ffU;
-}
-
-/**
- * @brief Sets counter of rx buffer with no. of blocks.
- * @param dwReg Register
- * @param wCount Counter.
- * @param wNBlocks no. of Blocks.
- * @retval None
- */
-
-static inline void pcd_set_ep_cnt_rx_reg(__O uint16_t * pdwReg, size_t wCount) {
- uint32_t wNBlocks;
- if(wCount > 62u)
- {
- wNBlocks = wCount >> 5u;
- if((wCount & 0x1fU) == 0u)
- {
- wNBlocks--;
- }
- wNBlocks = wNBlocks << 10u;
- wNBlocks |= 0x8000u; // Mark block size as 32byte
- *pdwReg = (uint16_t)wNBlocks;
- }
- else
- {
- wNBlocks = wCount >> 1u;
- if((wCount & 0x1U) != 0u)
- {
- wNBlocks++;
- }
- *pdwReg = (uint16_t)((wNBlocks) << 10u);
- }
-}
-
-
-/**
- * @brief Sets address in an endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @param bAddr Address.
- * @retval None
- */
-static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t bAddr)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPREG_MASK;
- regVal |= bAddr;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpNum,regVal);
-}
-
-static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x)
-{
- size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
- total_word_offset *= PMA_STRIDE;
- return &(pma[total_word_offset]);
-}
-
-// Pointers to the PMA table entries (using the ARM address space)
-static inline __IO uint16_t* pcd_ep_tx_address_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 0u);
-}
-static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 1u);
-}
-
-static inline __IO uint16_t* pcd_ep_rx_address_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 2u);
-}
-
-static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- return pcd_btable_word_ptr(USBx,(bEpNum)*4u + 3u);
-}
-
-static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wCount)
-{
- *pcd_ep_tx_cnt_ptr(USBx, bEpNum) = (uint16_t)wCount;
-}
-
-static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wCount)
-{
- __IO uint16_t *pdwReg = pcd_ep_rx_cnt_ptr((USBx),(bEpNum));
- pcd_set_ep_cnt_rx_reg(pdwReg, wCount);
-}
-
-/**
- * @brief sets the status for tx transfer (bits STAT_TX[1:0]).
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @param wState new state
- * @retval None
- */
-static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wState)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPTX_DTOGMASK;
-
- /* toggle first bit ? */
- if((USB_EPTX_DTOG1 & (wState))!= 0U)
- {
- regVal ^= USB_EPTX_DTOG1;
- }
- /* toggle second bit ? */
- if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U)
- {
- regVal ^= USB_EPTX_DTOG2;
- }
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-} /* pcd_set_ep_tx_status */
-
-/**
- * @brief sets the status for rx transfer (bits STAT_TX[1:0])
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @param wState new state
- * @retval None
- */
-
-static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpNum, uint32_t wState)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPRX_DTOGMASK;
-
- /* toggle first bit ? */
- if((USB_EPRX_DTOG1 & wState)!= 0U)
- {
- regVal ^= USB_EPRX_DTOG1;
- }
- /* toggle second bit ? */
- if((USB_EPRX_DTOG2 & wState)!= 0U)
- {
- regVal ^= USB_EPRX_DTOG2;
- }
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-} /* pcd_set_ep_rx_status */
-
-static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- return (regVal & USB_EPRX_STAT) >> (12u);
-} /* pcd_get_ep_rx_status */
-
-
-/**
- * @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @retval None
- */
-static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-
-static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-
-/**
- * @brief Clears DTOG_RX / DTOG_TX bit in the endpoint register.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @retval None
- */
-
-static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- if((regVal & USB_EP_DTOG_RX) != 0)
- {
- pcd_rx_dtog(USBx,bEpNum);
- }
-}
-
-static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- if((regVal & USB_EP_DTOG_TX) != 0)
- {
- pcd_tx_dtog(USBx,bEpNum);
- }
-}
-
-/**
- * @brief set & clear EP_KIND bit.
- * @param USBx USB peripheral instance register address.
- * @param bEpNum Endpoint Number.
- * @retval None
- */
-
-static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal |= USB_EP_KIND;
- regVal &= USB_EPREG_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpNum)
-{
- uint32_t regVal = pcd_get_endpoint(USBx, bEpNum);
- regVal &= USB_EPKIND_MASK;
- regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
- pcd_set_endpoint(USBx, bEpNum, regVal);
-}
-
-// This checks if the device has "LPM"
-#if defined(USB_ISTR_L1REQ)
-#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
-#else
-#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
-#endif
-
-#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 )
-
-// Number of endpoints in hardware
-#define STFSDEV_EP_COUNT (8u)
-
-#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */
diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h
new file mode 100644
index 000000000..518197c47
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h
@@ -0,0 +1,206 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+/** <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
+ * All rights reserved.</center></h2>
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef TUSB_FSDEV_CH32_H
+#define TUSB_FSDEV_CH32_H
+
+#include "common/tusb_compiler.h"
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+ #include <ch32v20x.h>
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL)
+#define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL)
+
+/**************************** 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) */
+#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */
+#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */
+#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */
+#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */
+#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */
+#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */
+#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */
+#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */
+
+/* Legacy defines */
+#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR
+
+#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */
+#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/
+#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */
+#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */
+#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */
+#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */
+#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */
+#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */
+
+/* Legacy defines */
+#define USB_CLR_PMAOVRM USB_CLR_PMAOVR
+
+/************************* CNTR control register bits definitions ***********/
+#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */
+#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */
+#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */
+#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */
+#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */
+#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */
+#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */
+#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */
+#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */
+#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */
+#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */
+#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */
+#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */
+
+/* Legacy defines */
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR
+#define USB_CNTR_LP_MODE USB_CNTR_LPMODE
+
+/******************** FNR Frame Number Register bit definitions ************/
+#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */
+#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */
+#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */
+#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */
+#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */
+
+/******************** DADDR Device ADDRess bit definitions ****************/
+#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */
+#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */
+
+/****************************** Endpoint register *************************/
+#define USB_EP0R USB_BASE /*!< endpoint 0 register address */
+#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */
+#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */
+#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */
+#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */
+#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */
+#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */
+#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */
+/* bit positions */
+#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */
+#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */
+#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */
+#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */
+#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */
+#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */
+#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */
+#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */
+#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */
+#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */
+
+/* EndPoint REGister MASK (no toggle fields) */
+#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+ /*!< EP_TYPE[1:0] EndPoint TYPE */
+#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */
+#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */
+#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */
+#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */
+#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */
+#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK)
+
+#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */
+ /*!< STAT_TX[1:0] STATus for TX transfer */
+#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */
+#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */
+#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */
+#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */
+#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */
+#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */
+#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK)
+ /*!< STAT_RX[1:0] STATus for RX transfer */
+#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */
+#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */
+#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */
+#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */
+#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32V20X
+static const IRQn_Type fsdev_irq[] = {
+ USB_HP_CAN1_TX_IRQn,
+ USB_LP_CAN1_RX0_IRQn,
+ USBWakeUp_IRQn
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+#else
+ #error "Unsupported MCU"
+#endif
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+ for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN;
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN;
+}
+
+#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
new file mode 100644
index 000000000..ccf31e035
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -0,0 +1,372 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) N Conrad
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_FSDEV_STM32_H
+#define TUSB_FSDEV_STM32_H
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32F0
+ #include "stm32f0xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ #define FSDEV_REG_BASE USB_BASE
+ // F0x2 models are crystal-less
+ // All have internal D+ pull-up
+ // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ #include "stm32f1xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 2 x 16 bits/word
+
+ // F1 names this differently from the rest
+ #define USB_CNTR_LPMODE USB_CNTR_LP_MODE
+
+#elif defined(STM32F302xB) || defined(STM32F302xC) || \
+ defined(STM32F303xB) || defined(STM32F303xC) || \
+ defined(STM32F373xC)
+ #include "stm32f3xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+ // NO internal Pull-ups
+ // *B, and *C: 1 x 16 bits/word
+ // PMA dedicated to USB (no sharing with CAN)
+
+#elif defined(STM32F302x6) || defined(STM32F302x8) || \
+ defined(STM32F302xD) || defined(STM32F302xE) || \
+ defined(STM32F303xD) || defined(STM32F303xE)
+ #include "stm32f3xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ // 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.
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
+ #include "stm32l0xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
+ #include "stm32l1xx.h"
+ #define FSDEV_PMA_SIZE (512u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
+ #include "stm32g4xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #include "stm32g0xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ #include "stm32c0xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+ #define USB_EP_CTR_RX USB_CHEP_VTRX
+ #define USB_EP_CTR_TX USB_CHEP_VTTX
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
+ #include "stm32h5xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+ #include "stm32wbxx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+ /* 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)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+ #include "stm32l5xx.h"
+ #define FSDEV_PMA_SIZE (1024u)
+
+ #ifndef USB_PMAADDR
+ #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
+ #endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ #include "stm32u5xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U0
+ #include "stm32u0xx.h"
+ #define FSDEV_PMA_SIZE (2048u)
+ #define USB USB_DRD_FS
+
+ #define USB_EP_CTR_RX USB_EP_VTRX
+ #define USB_EP_CTR_TX USB_EP_VTTX
+ #define USB_EP_T_FIELD USB_CHEP_UTYPE
+ #define USB_EPREG_MASK USB_CHEP_REG_MASK
+ #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+ #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+ #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+ #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+ #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+ #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+ #define USB_EPRX_STAT USB_CH_RX_VALID
+ #define USB_EPKIND_MASK USB_EP_KIND_MASK
+ #define USB_CNTR_FRES USB_CNTR_USBRST
+ #define USB_CNTR_RESUME USB_CNTR_L2RES
+ #define USB_ISTR_EP_ID USB_ISTR_IDN
+ #define USB_EPADDR_FIELD USB_CHEP_ADDR
+ #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+ #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#else
+ #error You are using an untested or unimplemented STM32 variant. Please update the driver.
+ // This includes U0
+#endif
+
+//--------------------------------------------------------------------+
+// Register and PMA Base Address
+//--------------------------------------------------------------------+
+#ifndef FSDEV_REG_BASE
+#if defined(USB_BASE)
+ #define FSDEV_REG_BASE USB_BASE
+#elif defined(USB_DRD_BASE)
+ #define FSDEV_REG_BASE USB_DRD_BASE
+#elif defined(USB_DRD_FS_BASE)
+ #define FSDEV_REG_BASE USB_DRD_FS_BASE
+#else
+ #error "FSDEV_REG_BASE not defined"
+#endif
+#endif
+
+#ifndef FSDEV_PMA_BASE
+#if defined(USB_PMAADDR)
+ #define FSDEV_PMA_BASE USB_PMAADDR
+#elif defined(USB_DRD_PMAADDR)
+ #define FSDEV_PMA_BASE USB_DRD_PMAADDR
+#else
+ #error "FSDEV_PMA_BASE not defined"
+#endif
+#endif
+
+// This checks if the device has "LPM"
+#if defined(USB_ISTR_L1REQ)
+#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
+#else
+#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
+#endif
+
+#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 )
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn)
+ #define USBWakeUp_IRQn USB_FS_WKUP_IRQn
+#endif
+
+static const IRQn_Type fsdev_irq[] = {
+ #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4)
+ USB_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+ USB_HP_CAN1_TX_IRQn,
+ USB_LP_CAN1_RX0_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32F3
+ // USB remap handles dcd functions
+ USB_HP_CAN_TX_IRQn,
+ USB_LP_CAN_RX0_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+ #ifdef STM32G0B0xx
+ USB_IRQn,
+ #else
+ USB_UCPD1_2_IRQn,
+ #endif
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32C0
+ USB_DRD_FS_IRQn,
+ #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
+ USB_HP_IRQn,
+ USB_LP_IRQn,
+ USBWakeUp_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32H5
+ USB_DRD_FS_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+ USB_FS_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+ USB_HP_IRQn,
+ USB_LP_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+ USB_IRQn,
+ #elif CFG_TUSB_MCU == OPT_MCU_STM32U0
+ USB_DRD_FS_IRQn,
+ #else
+ #error Unknown arch in USB driver
+ #endif
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+
+void dcd_int_enable(uint8_t rhport) {
+ (void)rhport;
+
+ // forces write to RAM before allowing ISR to execute
+ __DSB(); __ISB();
+
+ #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+ NVIC_EnableIRQ(USB_HP_IRQn);
+ NVIC_EnableIRQ(USB_LP_IRQn);
+ NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+ } else
+ #endif
+ {
+ for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_EnableIRQ(fsdev_irq[i]);
+ }
+ }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void)rhport;
+
+ #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+ // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+ // shared USB/CAN IRQs to separate CAN and USB IRQs.
+ // This dynamically checks if this remap is active to enable the right IRQs.
+ if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+ NVIC_DisableIRQ(USB_HP_IRQn);
+ NVIC_DisableIRQ(USB_LP_IRQn);
+ NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+ } else
+ #endif
+ {
+ for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+ NVIC_DisableIRQ(fsdev_irq[i]);
+ }
+ }
+
+ // CMSIS has a membar after disabling interrupts
+}
+
+// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
+#if defined(USB_BCDR_DPPU)
+
+void dcd_disconnect(uint8_t rhport) {
+ (void)rhport;
+ USB->BCDR &= ~(USB_BCDR_DPPU);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void)rhport;
+ USB->BCDR |= USB_BCDR_DPPU;
+}
+
+#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
+
+void dcd_disconnect(uint8_t rhport) {
+ (void)rhport;
+ SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void)rhport;
+ SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
+}
+#endif
+
+
+#endif /* TUSB_FSDEV_STM32_H */
diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h
new file mode 100644
index 000000000..cf36576bb
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_type.h
@@ -0,0 +1,307 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) N Conrad
+ * Copyright(c) 2024, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_FSDEV_TYPE_H
+#define TUSB_FSDEV_TYPE_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include "stdint.h"
+
+// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
+// Both of these MUST be a multiple of 2, and are in byte units.
+#ifndef FSDEV_BTABLE_BASE
+#define FSDEV_BTABLE_BASE 0U
+#endif
+
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes");
+
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
+// - 512-byte devices, access with a stride of two words (use every other 16-bit address)
+// - 1024-byte devices, access with a stride of one word (use every 16-bit address)
+// - 2048-byte devices, access with 32-bit address
+
+// For purposes of accessing the packet
+#if FSDEV_PMA_SIZE == 512
+ // 1x16 bit / word access scheme
+ #define FSDEV_PMA_STRIDE 2
+ #define pma_access_scheme TU_ATTR_ALIGNED(4)
+#elif FSDEV_PMA_SIZE == 1024
+ // 2x16 bit / word access scheme
+ #define FSDEV_PMA_STRIDE 1
+ #define pma_access_scheme
+#elif FSDEV_PMA_SIZE == 2048
+ // 32 bit access scheme
+ #define FSDEV_BUS_32BIT
+ #define FSDEV_PMA_STRIDE 1
+ #define pma_access_scheme
+#endif
+
+// The fsdev_bus_t type can be used for both register and PMA access necessities
+#ifdef FSDEV_BUS_32BIT
+ typedef uint32_t fsdev_bus_t;
+ #define fsdevbus_unaligned_read(_addr) tu_unaligned_read32(_addr)
+ #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write32(_addr, _value)
+#else
+ typedef uint16_t fsdev_bus_t;
+ #define fsdevbus_unaligned_read(_addr) tu_unaligned_read16(_addr)
+ #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write16(_addr, _value)
+#endif
+
+enum {
+ FSDEV_BUS_SIZE = sizeof(fsdev_bus_t),
+};
+
+//--------------------------------------------------------------------+
+// BTable Typedef
+//--------------------------------------------------------------------+
+enum {
+ BTABLE_BUF_TX = 0,
+ BTABLE_BUF_RX = 1
+};
+
+// hardware limit endpoint
+#define FSDEV_EP_COUNT 8
+
+// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either
+// 16-bit or 32-bit depending on FSDEV_BUS_32BIT.
+// 0: TX (IN), 1: RX (OUT)
+typedef union {
+ // data is strictly 16-bit access (address could be 32-bit aligned)
+ struct {
+ volatile pma_access_scheme uint16_t addr;
+ volatile pma_access_scheme uint16_t count;
+ } ep16[FSDEV_EP_COUNT][2];
+
+ // strictly 32-bit access
+ struct {
+ volatile uint32_t count_addr;
+ } ep32[FSDEV_EP_COUNT][2];
+} fsdev_btable_t;
+
+TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct");
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");
+
+#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(FSDEV_BTABLE_BASE)))
+
+typedef struct {
+ volatile pma_access_scheme fsdev_bus_t value;
+} fsdev_pma_buf_t;
+
+#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(_addr)))
+
+//--------------------------------------------------------------------+
+// Registers Typedef
+//--------------------------------------------------------------------+
+
+// volatile 32-bit aligned
+#define _va32 volatile TU_ATTR_ALIGNED(4)
+
+typedef struct {
+ struct {
+ _va32 fsdev_bus_t reg;
+ }ep[FSDEV_EP_COUNT];
+
+ _va32 uint32_t RESERVED7[8]; // Reserved
+ _va32 fsdev_bus_t CNTR; // 40: Control register
+ _va32 fsdev_bus_t ISTR; // 44: Interrupt status register
+ _va32 fsdev_bus_t FNR; // 48: Frame number register
+ _va32 fsdev_bus_t DADDR; // 4C: Device address register
+ _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only)
+ _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only)
+ _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only)
+} fsdev_regs_t;
+
+TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset");
+TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct");
+
+#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE)
+
+
+#ifndef USB_EPTX_STAT
+#define USB_EPTX_STAT 0x0030U
+#endif
+
+#ifndef USB_EPRX_STAT
+#define USB_EPRX_STAT 0x3000U
+#endif
+
+#ifndef USB_EPTX_STAT_Pos
+#define USB_EPTX_STAT_Pos 4u
+#endif
+
+#ifndef USB_EP_DTOG_TX_Pos
+#define USB_EP_DTOG_TX_Pos 6u
+#endif
+
+#ifndef USB_EP_CTR_TX_Pos
+#define USB_EP_CTR_TX_Pos 7u
+#endif
+
+typedef enum {
+ EP_STAT_DISABLED = 0,
+ EP_STAT_STALL = 1,
+ EP_STAT_NAK = 2,
+ EP_STAT_VALID = 3
+}ep_stat_t;
+
+#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+
+//--------------------------------------------------------------------+
+// Endpoint Helper
+// - CTR is write 0 to clear
+// - DTOG and STAT are write 1 to toggle
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) {
+ return FSDEV_REG->ep[ep_id].reg;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) {
+ if (need_exclusive) {
+ dcd_int_disable(0);
+ }
+
+ FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value;
+
+ if (need_exclusive) {
+ dcd_int_enable(0);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb_dir_t dir) {
+ uint32_t reg = FSDEV_REG->ep[ep_id].reg;
+ reg |= USB_EP_CTR_TX | USB_EP_CTR_RX;
+ reg &= USB_EPREG_MASK;
+ reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+ ep_write(ep_id, reg, false);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) {
+ *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) {
+ *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) {
+ return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS;
+}
+
+//--------------------------------------------------------------------+
+// BTable Helper
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) {
+#ifdef FSDEV_BUS_32BIT
+ return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu;
+#else
+ return FSDEV_BTABLE->ep16[ep_id][buf_id].addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) {
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, uint8_t buf_id) {
+ uint16_t count;
+#ifdef FSDEV_BUS_32BIT
+ count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16);
+#else
+ count = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+#endif
+ return count & 0x3FFU;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) {
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+ cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
+ FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt;
+#endif
+}
+
+/* Aligned buffer size according to hardware */
+TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) {
+ /* The STM32 full speed USB peripheral supports only a limited set of
+ * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
+ uint16_t block_in_bytes;
+ if (size > 62) {
+ block_in_bytes = 32;
+ *blsize = 1;
+ *num_block = tu_div_ceil(size, 32);
+ } else {
+ block_in_bytes = 2;
+ *blsize = 0;
+ *num_block = tu_div_ceil(size, 2);
+ }
+
+ return (*num_block) * block_in_bytes;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) {
+ uint8_t blsize, num_block;
+ (void) pma_align_buffer_size(wCount, &blsize, &num_block);
+
+ /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
+ uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10);
+ if (bl_nb == 0) {
+ // zlp but 0 is invalid value, set blsize to 1 (32 bytes)
+ // Note: lower value can cause PMAOVR on setup with ch32v203
+ bl_nb = 1 << 15;
+ }
+
+#ifdef FSDEV_BUS_32BIT
+ uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+ count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu);
+ FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+ FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb;
+#endif
+
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/st/synopsys/dcd_synopsys.c b/src/portable/st/synopsys/dcd_synopsys.c
deleted file mode 100644
index 2fc3adb4f..000000000
--- a/src/portable/st/synopsys/dcd_synopsys.c
+++ /dev/null
@@ -1,1240 +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)
- * Copyright (c) 2020 Jan Duempelmann
- * Copyright (c) 2020 Reinhard Panhuber
- *
- * 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"
-
-// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
-// We disable SOF for now until needed later on
-#define USE_SOF 0
-
-#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
- defined (STM32F107xB) || defined (STM32F107xC)
-#define STM32F1_SYNOPSYS
-#endif
-
-#if defined (STM32L475xx) || defined (STM32L476xx) || \
- defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
- defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
- defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
-#define STM32L4_SYNOPSYS
-#endif
-
-#if CFG_TUD_ENABLED && \
- ( (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
- CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
- CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
- CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
- (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS) || \
- CFG_TUSB_MCU == OPT_MCU_GD32VF103 ) \
- )
-
-// EP_MAX : Max number of bi-directional endpoints including EP0
-// EP_FIFO_SIZE : Size of dedicated USB SRAM
-#if CFG_TUSB_MCU == OPT_MCU_STM32F1
-#include "stm32f1xx.h"
-#define EP_MAX_FS 4
-#define EP_FIFO_SIZE_FS 1280
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F2
-#include "stm32f2xx.h"
-#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
-#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F4
-#include "stm32f4xx.h"
-#define EP_MAX_FS USB_OTG_FS_MAX_IN_ENDPOINTS
-#define EP_FIFO_SIZE_FS USB_OTG_FS_TOTAL_FIFO_SIZE
-#define EP_MAX_HS USB_OTG_HS_MAX_IN_ENDPOINTS
-#define EP_FIFO_SIZE_HS USB_OTG_HS_TOTAL_FIFO_SIZE
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H7
-#include "stm32h7xx.h"
-#define EP_MAX_FS 9
-#define EP_FIFO_SIZE_FS 4096
-#define EP_MAX_HS 9
-#define EP_FIFO_SIZE_HS 4096
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
-#include "stm32f7xx.h"
-#define EP_MAX_FS 6
-#define EP_FIFO_SIZE_FS 1280
-#define EP_MAX_HS 9
-#define EP_FIFO_SIZE_HS 4096
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
-#include "stm32l4xx.h"
-#define EP_MAX_FS 6
-#define EP_FIFO_SIZE_FS 1280
-
-#elif CFG_TUSB_MCU == OPT_MCU_GD32VF103
-#include "synopsys_common.h"
-
-// for remote wakeup delay
-#define __NOP() __asm volatile ("nop")
-
-// These numbers are the same for the whole GD32VF103 family.
-#define OTG_FS_IRQn 86
-#define EP_MAX_FS 4
-#define EP_FIFO_SIZE_FS 1280
-
-// The GD32VF103 is a RISC-V MCU, which implements the ECLIC Core-Local
-// Interrupt Controller by Nuclei. It is nearly API compatible to the
-// NVIC used by ARM MCUs.
-#define ECLIC_INTERRUPT_ENABLE_BASE 0xD2001001UL
-
-#define NVIC_EnableIRQ __eclic_enable_interrupt
-#define NVIC_DisableIRQ __eclic_disable_interrupt
-
-static inline void __eclic_enable_interrupt (uint32_t irq) {
- *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 1;
-}
-
-static inline void __eclic_disable_interrupt (uint32_t irq){
- *(volatile uint8_t*)(ECLIC_INTERRUPT_ENABLE_BASE + (irq * 4)) = 0;
-}
-
-#else
-#error "Unsupported MCUs"
-#endif
-
-#include "device/dcd.h"
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM
-//--------------------------------------------------------------------+
-
-// On STM32 we associate Port0 to OTG_FS, and Port1 to OTG_HS
-#if TUD_OPT_RHPORT == 0
-#define EP_MAX EP_MAX_FS
-#define EP_FIFO_SIZE EP_FIFO_SIZE_FS
-#define RHPORT_REGS_BASE USB_OTG_FS_PERIPH_BASE
-#define RHPORT_IRQn OTG_FS_IRQn
-
-#else
-#define EP_MAX EP_MAX_HS
-#define EP_FIFO_SIZE EP_FIFO_SIZE_HS
-#define RHPORT_REGS_BASE USB_OTG_HS_PERIPH_BASE
-#define RHPORT_IRQn OTG_HS_IRQn
-
-#endif
-
-#define GLOBAL_BASE(_port) ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE)
-#define DEVICE_BASE(_port) (USB_OTG_DeviceTypeDef *) (RHPORT_REGS_BASE + USB_OTG_DEVICE_BASE)
-#define OUT_EP_BASE(_port) (USB_OTG_OUTEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_OUT_ENDPOINT_BASE)
-#define IN_EP_BASE(_port) (USB_OTG_INEndpointTypeDef *) (RHPORT_REGS_BASE + USB_OTG_IN_ENDPOINT_BASE)
-#define FIFO_BASE(_port, _x) ((volatile uint32_t *) (RHPORT_REGS_BASE + USB_OTG_FIFO_BASE + (_x) * USB_OTG_FIFO_SIZE))
-
-enum
-{
- DCD_HIGH_SPEED = 0, // Highspeed mode
- DCD_FULL_SPEED_USE_HS = 1, // Full speed in Highspeed port (probably with internal PHY)
- DCD_FULL_SPEED = 3, // Full speed with internal PHY
-};
-
-static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
-
-typedef struct {
- uint8_t * buffer;
- tu_fifo_t * ff;
- uint16_t total_len;
- uint16_t max_size;
- uint8_t interval;
-} xfer_ctl_t;
-
-typedef volatile uint32_t * usb_fifo_t;
-
-xfer_ctl_t xfer_status[EP_MAX][2];
-#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
-
-// EP0 transfers are limited to 1 packet - larger sizes has to be split
-static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
-
-// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from usb_otg->GRXFSIZ
-static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
-static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
-
-// Calculate the RX FIFO size according to recommendations from reference manual
-static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
-{
- return 15 + 2*(ep_size/4) + 2*EP_MAX;
-}
-
-static void update_grxfsiz(uint8_t rhport)
-{
- (void) rhport;
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
-
- // Determine largest EP size for RX FIFO
- uint16_t max_epsize = 0;
- for (uint8_t epnum = 0; epnum < EP_MAX; epnum++)
- {
- max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
- }
-
- // Update size of RX FIFO
- usb_otg->GRXFSIZ = calc_rx_ff_size(max_epsize);
-}
-
-// Setup the control endpoint 0.
-static void bus_reset(uint8_t rhport)
-{
- (void) rhport;
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- tu_memclr(xfer_status, sizeof(xfer_status));
- _out_ep_closed = false;
-
- // clear device address
- dev->DCFG &= ~USB_OTG_DCFG_DAD_Msk;
-
- // 1. NAK for all OUT endpoints
- for(uint8_t n = 0; n < EP_MAX; n++) {
- out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
- }
-
- // 2. Un-mask interrupt bits
- dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
- dev->DOEPMSK = USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM;
- dev->DIEPMSK = USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM;
-
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start.
- // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located
- // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard
- // configuration done below.
- //
- // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed.
- // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a
- // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually
- // opened when the host sends an additional command: setInterface. At this point in time
- // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size
- // an additional memory
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO 0 |
- // --------------- (320 or 1024) - 16
- // | IN FIFO 1 |
- // --------------- (320 or 1024) - 16 - x
- // | . . . . |
- // --------------- (320 or 1024) - 16 - x - y - ... - z
- // | IN FIFO MAX |
- // ---------------
- // | FREE |
- // --------------- 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
- // - 13 for setup packets + control words (up to 3 setup packets).
- // - 1 for global NAK (not required/used here).
- // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1"
- // - 2 for each used OUT endpoint
- //
- // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum
- // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x EP_MAX = 47 + 2 x EP_MAX
- // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x EP_MAX = 271 + 2 x EP_MAX
- //
- // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
- // of the overall picture yet. We will use the worst scenario: largest possible + EP_MAX
- //
- // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
- // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to
- // overwrite this.
-
- usb_otg->GRXFSIZ = calc_rx_ff_size(TUD_OPT_HIGH_SPEED ? 512 : 64);
-
- _allocated_fifo_words_tx = 16;
-
- // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
- usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
-
- // Fixed control EP0 size to 64 bytes
- in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos);
- xfer_status[0][TUSB_DIR_OUT].max_size = xfer_status[0][TUSB_DIR_IN].max_size = 64;
-
- out_ep[0].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos);
-
- usb_otg->GINTMSK |= USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT;
-}
-
-// Set turn-around timeout according to link speed
-extern uint32_t SystemCoreClock;
-static void set_turnaround(USB_OTG_GlobalTypeDef * usb_otg, tusb_speed_t speed)
-{
- usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_TRDT;
-
- if ( speed == TUSB_SPEED_HIGH )
- {
- // Use fixed 0x09 for Highspeed
- usb_otg->GUSBCFG |= (0x09 << USB_OTG_GUSBCFG_TRDT_Pos);
- }
- else
- {
- // Turnaround timeout depends on the MCU clock
- uint32_t turnaround;
-
- if ( SystemCoreClock >= 32000000U )
- turnaround = 0x6U;
- else if ( SystemCoreClock >= 27500000U )
- turnaround = 0x7U;
- else if ( SystemCoreClock >= 24000000U )
- turnaround = 0x8U;
- else if ( SystemCoreClock >= 21800000U )
- turnaround = 0x9U;
- else if ( SystemCoreClock >= 20000000U )
- turnaround = 0xAU;
- else if ( SystemCoreClock >= 18500000U )
- turnaround = 0xBU;
- else if ( SystemCoreClock >= 17200000U )
- turnaround = 0xCU;
- else if ( SystemCoreClock >= 16000000U )
- turnaround = 0xDU;
- else if ( SystemCoreClock >= 15000000U )
- turnaround = 0xEU;
- else
- turnaround = 0xFU;
-
- // Fullspeed depends on MCU clocks, but we will use 0x06 for 32+ Mhz
- usb_otg->GUSBCFG |= (turnaround << USB_OTG_GUSBCFG_TRDT_Pos);
- }
-}
-
-static tusb_speed_t get_speed(uint8_t rhport)
-{
- (void) rhport;
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- uint32_t const enum_spd = (dev->DSTS & USB_OTG_DSTS_ENUMSPD_Msk) >> USB_OTG_DSTS_ENUMSPD_Pos;
- return (enum_spd == DCD_HIGH_SPEED) ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL;
-}
-
-static void set_speed(uint8_t rhport, tusb_speed_t speed)
-{
- uint32_t bitvalue;
-
- if ( rhport == 1 )
- {
- bitvalue = ((TUSB_SPEED_HIGH == speed) ? DCD_HIGH_SPEED : DCD_FULL_SPEED_USE_HS);
- }
- else
- {
- bitvalue = DCD_FULL_SPEED;
- }
-
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
-
- // Clear and set speed bits
- dev->DCFG &= ~(3 << USB_OTG_DCFG_DSPD_Pos);
- dev->DCFG |= (bitvalue << USB_OTG_DCFG_DSPD_Pos);
-}
-
-#if defined(USB_HS_PHYC)
-static bool USB_HS_PHYCInit(void)
-{
- USB_HS_PHYC_GlobalTypeDef *usb_hs_phyc = (USB_HS_PHYC_GlobalTypeDef*) USB_HS_PHYC_CONTROLLER_BASE;
-
- // Enable LDO: Note STM32F72/3xx Reference Manual rev 3 June 2018 incorrectly defined this bit as Disabled !!
- usb_hs_phyc->USB_HS_PHYC_LDO |= USB_HS_PHYC_LDO_ENABLE;
-
- // Wait until LDO ready
- while ( 0 == (usb_hs_phyc->USB_HS_PHYC_LDO & USB_HS_PHYC_LDO_STATUS) ) {}
-
- uint32_t phyc_pll = 0;
-
- // TODO Try to get HSE_VALUE from registers instead of depending CFLAGS
- switch ( HSE_VALUE )
- {
- case 12000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12MHZ ; break;
- case 12500000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_12_5MHZ ; break;
- case 16000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_16MHZ ; break;
- case 24000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_24MHZ ; break;
- case 25000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_25MHZ ; break;
- case 32000000: phyc_pll = USB_HS_PHYC_PLL1_PLLSEL_Msk ; break; // Value not defined in header
- default:
- TU_ASSERT(0);
- }
- usb_hs_phyc->USB_HS_PHYC_PLL = phyc_pll;
-
- // Control the tuning interface of the High Speed PHY
- // Use magic value (USB_HS_PHYC_TUNE_VALUE) from ST driver
- usb_hs_phyc->USB_HS_PHYC_TUNE |= 0x00000F13U;
-
- // Enable PLL internal PHY
- usb_hs_phyc->USB_HS_PHYC_PLL |= USB_HS_PHYC_PLL_PLLEN;
-
- // Original ST code has 2 ms delay for PLL stabilization.
- // Primitive test shows that more than 10 USB un/replug cycle showed no error with enumeration
-
- return true;
-}
-#endif
-
-static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, uint16_t total_bytes)
-{
- (void) rhport;
-
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- // EP0 is limited to one packet each xfer
- // We use multiple transaction of xfer->max_size length to get a whole transfer done
- if(epnum == 0) {
- xfer_ctl_t * const xfer = XFER_CTL_BASE(epnum, dir);
- total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
- ep0_pending[dir] -= 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.
- in_ep[epnum].DIEPTSIZ = (num_packets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << USB_OTG_DIEPTSIZ_XFRSIZ_Pos) & USB_OTG_DIEPTSIZ_XFRSIZ_Msk);
-
- in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK;
- // For ISO endpoint set correct odd/even bit for next frame.
- if ((in_ep[epnum].DIEPCTL & USB_OTG_DIEPCTL_EPTYP) == USB_OTG_DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
- {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
- in_ep[epnum].DIEPCTL |= (odd_frame_now ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DIEPCTL_SODDFRM_Msk);
- }
- // Enable fifo empty interrupt only if there are something to put in the fifo.
- if(total_bytes != 0) {
- dev->DIEPEMPMSK |= (1 << epnum);
- }
- } else {
- // A full OUT transfer (multiple packets, possibly) triggers XFRC.
- out_ep[epnum].DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT_Msk | USB_OTG_DOEPTSIZ_XFRSIZ);
- out_ep[epnum].DOEPTSIZ |= (num_packets << USB_OTG_DOEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) & USB_OTG_DOEPTSIZ_XFRSIZ_Msk);
-
- out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
- if ((out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPTYP) == USB_OTG_DOEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1)
- {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dev->DSTS & (1u << USB_OTG_DSTS_FNSOF_Pos));
- out_ep[epnum].DOEPCTL |= (odd_frame_now ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk : USB_OTG_DOEPCTL_SODDFRM_Msk);
- }
- }
-}
-
-/*------------------------------------------------------------------*/
-/* Controller API
- *------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
- // Programming model begins in the last section of the chapter on the USB
- // peripheral in each Reference Manual.
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
-
- // No HNP/SRP (no OTG support), program timeout later.
- if ( rhport == 1 )
- {
- // On selected MCUs HS port1 can be used with external PHY via ULPI interface
-#if CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED
- // deactivate internal PHY
- usb_otg->GCCFG &= ~USB_OTG_GCCFG_PWRDWN;
-
- // Init The UTMI Interface
- usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_TSDPS | USB_OTG_GUSBCFG_ULPIFSLS | USB_OTG_GUSBCFG_PHYSEL);
-
- // Select default internal VBUS Indicator and Drive for ULPI
- usb_otg->GUSBCFG &= ~(USB_OTG_GUSBCFG_ULPIEVBUSD | USB_OTG_GUSBCFG_ULPIEVBUSI);
-#else
- usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL;
-#endif
-
-#if defined(USB_HS_PHYC)
- // Highspeed with embedded UTMI PHYC
-
- // Select UTMI Interface
- usb_otg->GUSBCFG &= ~USB_OTG_GUSBCFG_ULPI_UTMI_SEL;
- usb_otg->GCCFG |= USB_OTG_GCCFG_PHYHSEN;
-
- // Enables control of a High Speed USB PHY
- USB_HS_PHYCInit();
-#endif
- } else
- {
- // Enable internal PHY
- usb_otg->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL;
- }
-
- // Reset core after selecting PHY
- // Wait AHB IDLE, reset then wait until it is cleared
- while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U) {}
- usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
- while ((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST) {}
-
- // Restart PHY clock
- *((volatile uint32_t *)(RHPORT_REGS_BASE + USB_OTG_PCGCCTL_BASE)) = 0;
-
- // Clear all interrupts
- usb_otg->GINTSTS |= usb_otg->GINTSTS;
-
- // Required as part of core initialization.
- // TODO: How should mode mismatch be handled? It will cause
- // the core to stop working/require reset.
- usb_otg->GINTMSK |= USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_MMISM;
-
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
-
- // If USB host misbehaves during status portion of control xfer
- // (non zero-length packet), send STALL back and discard.
- dev->DCFG |= USB_OTG_DCFG_NZLSOHSK;
-
- set_speed(rhport, TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL);
-
- // Enable internal USB transceiver, unless using HS core (port 1) with external PHY.
- if (!(rhport == 1 && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED))) usb_otg->GCCFG |= USB_OTG_GCCFG_PWRDWN;
-
- usb_otg->GINTMSK |= USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM |
- USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_WUIM |
- USB_OTG_GINTMSK_RXFLVLM | (USE_SOF ? USB_OTG_GINTMSK_SOFM : 0);
-
- // Enable global interrupt
- usb_otg->GAHBCFG |= USB_OTG_GAHBCFG_GINT;
-
- dcd_connect(rhport);
-}
-
-void dcd_int_enable (uint8_t rhport)
-{
- (void) rhport;
- NVIC_EnableIRQ(RHPORT_IRQn);
-}
-
-void dcd_int_disable (uint8_t rhport)
-{
- (void) rhport;
- NVIC_DisableIRQ(RHPORT_IRQn);
-}
-
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- dev->DCFG = (dev->DCFG & ~USB_OTG_DCFG_DAD_Msk) | (dev_addr << USB_OTG_DCFG_DAD_Pos);
-
- // Response with status after changing device address
- dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
-}
-
-static void remote_wakeup_delay(void)
-{
- // try to delay for 1 ms
- uint32_t count = SystemCoreClock / 1000;
- while ( count-- )
- {
- __NOP();
- }
-}
-
-void dcd_remote_wakeup(uint8_t rhport)
-{
- (void) rhport;
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
-
- // set remote wakeup
- dev->DCTL |= USB_OTG_DCTL_RWUSIG;
-
- // enable SOF to detect bus resume
- usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
- usb_otg->GINTMSK |= USB_OTG_GINTMSK_SOFM;
-
- // Per specs: remote wakeup signal bit must be clear within 1-15ms
- remote_wakeup_delay();
-
- dev->DCTL &= ~USB_OTG_DCTL_RWUSIG;
-}
-
-void dcd_connect(uint8_t rhport)
-{
- (void) rhport;
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- dev->DCTL &= ~USB_OTG_DCTL_SDIS;
-}
-
-void dcd_disconnect(uint8_t rhport)
-{
- (void) rhport;
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- dev->DCTL |= USB_OTG_DCTL_SDIS;
-}
-
-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)
-{
- (void) rhport;
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- 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;
-
- uint16_t const fifo_size = (xfer->max_size + 3) / 4; // Round up to next full word
-
- if(dir == TUSB_DIR_OUT)
- {
- // Calculate required size of RX FIFO
- uint16_t const sz = calc_rx_ff_size(4*fifo_size);
-
- // If size_rx needs to be extended check if possible and if so enlarge it
- if (usb_otg->GRXFSIZ < sz)
- {
- TU_ASSERT(sz + _allocated_fifo_words_tx <= EP_FIFO_SIZE/4);
-
- // Enlarge RX FIFO
- usb_otg->GRXFSIZ = sz;
- }
-
- out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
- (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DOEPCTL_SD0PID_SEVNFRM : 0) |
- (xfer->max_size << USB_OTG_DOEPCTL_MPSIZ_Pos);
-
- dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_OEPM_Pos + epnum));
- }
- else
- {
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO 0 |
- // --------------- (320 or 1024) - 16
- // | IN FIFO 1 |
- // --------------- (320 or 1024) - 16 - x
- // | . . . . |
- // --------------- (320 or 1024) - 16 - x - y - ... - z
- // | IN FIFO MAX |
- // ---------------
- // | FREE |
- // --------------- GRXFSIZ
- // | OUT FIFO |
- // | ( Shared ) |
- // --------------- 0
- //
- // In FIFO is allocated by following rules:
- // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
-
- // Check if free space is available
- TU_ASSERT(_allocated_fifo_words_tx + fifo_size + usb_otg->GRXFSIZ <= EP_FIFO_SIZE/4);
-
- _allocated_fifo_words_tx += fifo_size;
-
- TU_LOG(2, " Allocated %u bytes at offset %u", fifo_size*4, EP_FIFO_SIZE-_allocated_fifo_words_tx*4);
-
- // DIEPTXF starts at FIFO #1.
- // Both TXFD and TXSA are in unit of 32-bit words.
- usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
-
- in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
- (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
- (desc_edpt->bmAttributes.xfer << USB_OTG_DIEPCTL_EPTYP_Pos) |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_OTG_DIEPCTL_SD0PID_SEVNFRM : 0) |
- (xfer->max_size << USB_OTG_DIEPCTL_MPSIZ_Pos);
-
- dev->DAINTMSK |= (1 << (USB_OTG_DAINTMSK_IEPM_Pos + epnum));
- }
-
- return true;
-}
-
-// Close all non-control endpoints, cancel all pending transfers if any.
-void dcd_edpt_close_all (uint8_t rhport)
-{
- (void) rhport;
-
-// USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- // Disable non-control interrupt
- dev->DAINTMSK = (1 << USB_OTG_DAINTMSK_OEPM_Pos) | (1 << USB_OTG_DAINTMSK_IEPM_Pos);
-
- 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;
- }
-
- // reset allocated fifo IN
- _allocated_fifo_words_tx = 16;
-}
-
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
-
- // EP0 can only handle one packet
- if(epnum == 0) {
- ep0_pending[dir] = total_bytes;
- // Schedule the first transaction for EP0 transfer
- edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]);
- return true;
- }
-
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if(short_packet_size > 0 || (total_bytes == 0)) {
- num_packets++;
- }
-
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
-
- return true;
-}
-
-// The number of bytes has to be given explicitly to allow more flexible control of how many
-// bytes should be written and second to keep the return value free to give back a boolean
-// success message. If total_bytes is too big, the FIFO will copy only what is available
-// into the USB buffer!
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
- // USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
- TU_ASSERT(ff->item_size == 1);
-
- 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 = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
-
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
-
- // Zero-size packet is special case.
- if(short_packet_size > 0 || (total_bytes == 0)) num_packets++;
-
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
-
- return true;
-}
-
-static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
-{
- (void) rhport;
-
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- 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_OTG_DIEPCTL_EPENA) ){
- in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK | (stall ? USB_OTG_DIEPCTL_STALL : 0);
- } else {
- // Stop transmitting packets and NAK IN xfers.
- in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
- while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_INEPNE) == 0);
-
- // Disable the endpoint.
- in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_EPDIS | (stall ? USB_OTG_DIEPCTL_STALL : 0);
- while((in_ep[epnum].DIEPINT & USB_OTG_DIEPINT_EPDISD_Msk) == 0);
- in_ep[epnum].DIEPINT = USB_OTG_DIEPINT_EPDISD;
- }
-
- // Flush the FIFO, and wait until we have confirmed it cleared.
- usb_otg->GRSTCTL |= (epnum << USB_OTG_GRSTCTL_TXFNUM_Pos);
- usb_otg->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH;
- while((usb_otg->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH_Msk) != 0);
- } else {
- // Only disable currently enabled non-control endpoint
- if ( (epnum == 0) || !(out_ep[epnum].DOEPCTL & USB_OTG_DOEPCTL_EPENA) ){
- out_ep[epnum].DOEPCTL |= stall ? USB_OTG_DOEPCTL_STALL : 0;
- } 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.
- dev->DCTL |= USB_OTG_DCTL_SGONAK;
- while((usb_otg->GINTSTS & USB_OTG_GINTSTS_BOUTNAKEFF_Msk) == 0);
-
- // Ditto here- disable the endpoint.
- out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_EPDIS | (stall ? USB_OTG_DOEPCTL_STALL : 0);
- while((out_ep[epnum].DOEPINT & USB_OTG_DOEPINT_EPDISD_Msk) == 0);
- out_ep[epnum].DOEPINT = USB_OTG_DOEPINT_EPDISD;
-
- // Allow other OUT endpoints to keep receiving.
- dev->DCTL |= USB_OTG_DCTL_CGONAK;
- }
- }
-}
-
-/**
- * Close an endpoint.
- */
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- dcd_edpt_disable(rhport, ep_addr, false);
-
- // Update max_size
- xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
-
- if (dir == TUSB_DIR_IN)
- {
- uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos;
- uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos;
- // For now only the last opened endpoint can be closed without fuss.
- TU_ASSERT(fifo_start == EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,);
- _allocated_fifo_words_tx -= fifo_size;
- }
- else
- {
- _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
- }
-}
-
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
- dcd_edpt_disable(rhport, ep_addr, true);
-}
-
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- // Clear stall and reset data toggle
- if(dir == TUSB_DIR_IN) {
- in_ep[epnum].DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
- in_ep[epnum].DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
- } else {
- out_ep[epnum].DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
- out_ep[epnum].DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
- }
-}
-
-/*------------------------------------------------------------------*/
-
-// Read a single data packet from receive FIFO
-static void read_fifo_packet(uint8_t rhport, uint8_t * dst, uint16_t len)
-{
- (void) rhport;
-
- usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0);
-
- // Reading full available 32 bit words from fifo
- uint16_t full_words = len >> 2;
- for(uint16_t i = 0; i < full_words; i++) {
- uint32_t tmp = *rx_fifo;
- dst[0] = tmp & 0x000000FF;
- dst[1] = (tmp & 0x0000FF00) >> 8;
- dst[2] = (tmp & 0x00FF0000) >> 16;
- dst[3] = (tmp & 0xFF000000) >> 24;
- dst += 4;
- }
-
- // Read the remaining 1-3 bytes from fifo
- uint8_t bytes_rem = len & 0x03;
- if(bytes_rem != 0) {
- uint32_t tmp = *rx_fifo;
- dst[0] = tmp & 0x000000FF;
- if(bytes_rem > 1) {
- dst[1] = (tmp & 0x0000FF00) >> 8;
- }
- if(bytes_rem > 2) {
- dst[2] = (tmp & 0x00FF0000) >> 16;
- }
- }
-}
-
-// Write a single data packet to EPIN FIFO
-static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t * src, uint16_t len)
-{
- (void) rhport;
-
- usb_fifo_t tx_fifo = FIFO_BASE(rhport, fifo_num);
-
- // Pushing full available 32 bit words to fifo
- uint16_t full_words = len >> 2;
- for(uint16_t i = 0; i < full_words; i++){
- *tx_fifo = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
- src += 4;
- }
-
- // Write the remaining 1-3 bytes into fifo
- uint8_t bytes_rem = len & 0x03;
- if(bytes_rem){
- uint32_t tmp_word = 0;
- tmp_word |= src[0];
- if(bytes_rem > 1){
- tmp_word |= src[1] << 8;
- }
- if(bytes_rem > 2){
- tmp_word |= src[2] << 16;
- }
- *tx_fifo = tmp_word;
- }
-}
-
-static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_ep) {
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- usb_fifo_t rx_fifo = FIFO_BASE(rhport, 0);
-
- // Pop control word off FIFO
- uint32_t ctl_word = usb_otg->GRXSTSP;
- uint8_t pktsts = (ctl_word & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;
- uint8_t epnum = (ctl_word & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
- uint16_t bcnt = (ctl_word & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
-
- switch(pktsts) {
- case 0x01: // Global OUT NAK (Interrupt)
- break;
-
- case 0x02: // Out packet recvd
- {
- xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
-
- // Read packet off RxFIFO
- if (xfer->ff)
- {
- // Ring buffer
- tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *)(uintptr_t) rx_fifo, bcnt);
- }
- else
- {
- // Linear buffer
- read_fifo_packet(rhport, xfer->buffer, bcnt);
-
- // Increment pointer to xfer data
- xfer->buffer += bcnt;
- }
-
- // Truncate transfer length in case of short packet
- if(bcnt < xfer->max_size) {
- xfer->total_len -= (out_ep[epnum].DOEPTSIZ & USB_OTG_DOEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DOEPTSIZ_XFRSIZ_Pos;
- if(epnum == 0) {
- xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
- ep0_pending[TUSB_DIR_OUT] = 0;
- }
- }
- }
- break;
-
- case 0x03: // Out packet done (Interrupt)
- break;
-
- case 0x04: // Setup packet done (Interrupt)
- out_ep[epnum].DOEPTSIZ |= (3 << USB_OTG_DOEPTSIZ_STUPCNT_Pos);
- break;
-
- case 0x06: // Setup packet recvd
- // We can receive up to three setup packets in succession, but
- // only the last one is valid.
- _setup_packet[0] = (* rx_fifo);
- _setup_packet[1] = (* rx_fifo);
- break;
-
- default: // Invalid
- TU_BREAKPOINT();
- break;
- }
-}
-
-static void handle_epout_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OTG_OUTEndpointTypeDef * out_ep) {
- // DAINT for a given EP clears when DOEPINTx is cleared.
- // OEPINT will be cleared when DAINT's out bits are cleared.
- for(uint8_t n = 0; n < EP_MAX; n++) {
- xfer_ctl_t * xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
-
- if(dev->DAINT & (1 << (USB_OTG_DAINT_OEPINT_Pos + n))) {
- // SETUP packet Setup Phase done.
- if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_STUP) {
- out_ep[n].DOEPINT = USB_OTG_DOEPINT_STUP;
- dcd_event_setup_received(rhport, (uint8_t*) &_setup_packet[0], true);
- }
-
- // OUT XFER complete
- if(out_ep[n].DOEPINT & USB_OTG_DOEPINT_XFRC) {
- out_ep[n].DOEPINT = USB_OTG_DOEPINT_XFRC;
-
- // EP0 can only handle one packet
- if((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
- // Schedule another packet to be received.
- edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- } else {
- dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- }
- }
- }
-}
-
-static void handle_epin_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OTG_INEndpointTypeDef * in_ep) {
- // DAINT for a given EP clears when DIEPINTx is cleared.
- // IEPINT will be cleared when DAINT's out bits are cleared.
- for ( uint8_t n = 0; n < EP_MAX; n++ )
- {
- xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
-
- if ( dev->DAINT & (1 << (USB_OTG_DAINT_IEPINT_Pos + n)) )
- {
- // IN XFER complete (entire xfer).
- if ( in_ep[n].DIEPINT & USB_OTG_DIEPINT_XFRC )
- {
- in_ep[n].DIEPINT = USB_OTG_DIEPINT_XFRC;
-
- // EP0 can only handle one packet
- if((n == 0) && ep0_pending[TUSB_DIR_IN]) {
- // Schedule another packet to be transmitted.
- edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
- } else {
- dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
- }
-
- // XFER FIFO empty
- if ( (in_ep[n].DIEPINT & USB_OTG_DIEPINT_TXFE) && (dev->DIEPEMPMSK & (1 << n)) )
- {
- // DIEPINT's TXFE bit is read-only, software cannot clear it.
- // It will only be cleared by hardware when written bytes is more than
- // - 64 bytes or
- // - Half of TX FIFO size (configured by DIEPTXF)
-
- uint16_t remaining_packets = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
-
- // Process every single packet (only whole packets can be written to fifo)
- for(uint16_t i = 0; i < remaining_packets; i++)
- {
- uint16_t const remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
-
- // Packet can not be larger than ep max size
- uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
-
- // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
- // EP has to be checked if the buffer can take another WHOLE packet
- if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)) break;
-
- // Push packet to Tx-FIFO
- if (xfer->ff)
- {
- usb_fifo_t tx_fifo = FIFO_BASE(rhport, n);
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *)(uintptr_t) tx_fifo, packet_size);
- }
- else
- {
- write_fifo_packet(rhport, n, xfer->buffer, packet_size);
-
- // Increment pointer to xfer data
- xfer->buffer += packet_size;
- }
- }
-
- // Turn off TXFE if all bytes are written.
- if (((in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos) == 0)
- {
- dev->DIEPEMPMSK &= ~(1 << n);
- }
- }
- }
- }
-}
-
-void dcd_int_handler(uint8_t rhport)
-{
- USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
- USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
- USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
- USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
-
- uint32_t const int_status = usb_otg->GINTSTS & usb_otg->GINTMSK;
-
- if(int_status & USB_OTG_GINTSTS_USBRST)
- {
- // USBRST is start of reset.
- usb_otg->GINTSTS = USB_OTG_GINTSTS_USBRST;
- bus_reset(rhport);
- }
-
- if(int_status & USB_OTG_GINTSTS_ENUMDNE)
- {
- // ENUMDNE is the end of reset where speed of the link is detected
-
- usb_otg->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
-
- tusb_speed_t const speed = get_speed(rhport);
-
- set_turnaround(usb_otg, speed);
- dcd_event_bus_reset(rhport, speed, true);
- }
-
- if(int_status & USB_OTG_GINTSTS_USBSUSP)
- {
- usb_otg->GINTSTS = USB_OTG_GINTSTS_USBSUSP;
- dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
- }
-
- if(int_status & USB_OTG_GINTSTS_WKUINT)
- {
- usb_otg->GINTSTS = USB_OTG_GINTSTS_WKUINT;
- dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
- }
-
- // TODO check USB_OTG_GINTSTS_DISCINT for disconnect detection
- // if(int_status & USB_OTG_GINTSTS_DISCINT)
-
- if(int_status & USB_OTG_GINTSTS_OTGINT)
- {
- // OTG INT bit is read-only
- uint32_t const otg_int = usb_otg->GOTGINT;
-
- if (otg_int & USB_OTG_GOTGINT_SEDET)
- {
- dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
- }
-
- usb_otg->GOTGINT = otg_int;
- }
-
- if(int_status & USB_OTG_GINTSTS_SOF)
- {
- usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
-
- // Disable SOF interrupt since currently only used for remote wakeup detection
- usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_SOFM;
-
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
- }
-
- // RxFIFO non-empty interrupt handling.
- if(int_status & USB_OTG_GINTSTS_RXFLVL)
- {
- // RXFLVL bit is read-only
-
- // Mask out RXFLVL while reading data from FIFO
- usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
-
- // Loop until all available packets were handled
- do
- {
- handle_rxflvl_ints(rhport, out_ep);
- } while(usb_otg->GINTSTS & USB_OTG_GINTSTS_RXFLVL);
-
- // Manage RX FIFO size
- if (_out_ep_closed)
- {
- update_grxfsiz(rhport);
-
- // Disable flag
- _out_ep_closed = false;
- }
-
- usb_otg->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
- }
-
- // OUT endpoint interrupt handling.
- if(int_status & USB_OTG_GINTSTS_OEPINT)
- {
- // OEPINT is read-only
- handle_epout_ints(rhport, dev, out_ep);
- }
-
- // IN endpoint interrupt handling.
- if(int_status & USB_OTG_GINTSTS_IEPINT)
- {
- // IEPINT bit read-only
- handle_epin_ints(rhport, dev, in_ep);
- }
-
- // // Check for Incomplete isochronous IN transfer
- // if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
- // printf(" IISOIXFR!\r\n");
- //// TU_LOG2(" IISOIXFR!\r\n");
- // }
-}
-
-#endif
diff --git a/src/portable/st/synopsys/synopsys_common.h b/src/portable/st/synopsys/synopsys_common.h
deleted file mode 100644
index 6f0602fe9..000000000
--- a/src/portable/st/synopsys/synopsys_common.h
+++ /dev/null
@@ -1,1465 +0,0 @@
-/**
- ******************************************************************************
- * @file synopsys_common.h
- * @author MCD Application Team
- * @brief CMSIS Cortex-M3 Device USB OTG peripheral Header File.
- * This file contains the USB OTG peripheral register's definitions, bits
- * definitions and memory mapping for STM32F1xx devices.
- *
- * This file contains:
- * - Data structures and the address mapping for the USB OTG peripheral
- * - The Peripheral's registers declarations and bits definition
- * - Macros to access the peripheral's registers hardware
- *
- ******************************************************************************
- * @attention
- *
- * <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
- * All rights reserved.</center></h2>
- *
- * This software component is licensed by ST under BSD 3-Clause license,
- * the "License"; You may not use this file except in compliance with the
- * License. You may obtain a copy of the License at:
- * opensource.org/licenses/BSD-3-Clause
- *
- ******************************************************************************
- */
-
-#include "stdint.h"
-
-#pragma once
-
-#ifdef __cplusplus
- #define __I volatile
-#else
- #define __I volatile const
-#endif
-#define __O volatile
-#define __IO volatile
-#define __IM volatile const
-#define __OM volatile
-#define __IOM volatile
-
-/**
- * @brief __USB_OTG_Core_register
- */
-
-typedef struct
-{
- __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register Address offset: 000h */
- __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register Address offset: 004h */
- __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register Address offset: 008h */
- __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register Address offset: 00Ch */
- __IO uint32_t GRSTCTL; /*!< Core Reset Register Address offset: 010h */
- __IO uint32_t GINTSTS; /*!< Core Interrupt Register Address offset: 014h */
- __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register Address offset: 018h */
- __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register Address offset: 01Ch */
- __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register Address offset: 020h */
- __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register Address offset: 024h */
- __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register Address offset: 028h */
- __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg Address offset: 02Ch */
- uint32_t Reserved30[2]; /*!< Reserved 030h*/
- __IO uint32_t GCCFG; /*!< General Purpose IO Register Address offset: 038h */
- __IO uint32_t CID; /*!< User ID Register Address offset: 03Ch */
- uint32_t Reserved40[48]; /*!< Reserved 040h-0FFh */
- __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg Address offset: 100h */
- __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO Address offset: 0x104 */
-} USB_OTG_GlobalTypeDef;
-
-/**
- * @brief __device_Registers
- */
-
-typedef struct
-{
- __IO uint32_t DCFG; /*!< dev Configuration Register Address offset: 800h*/
- __IO uint32_t DCTL; /*!< dev Control Register Address offset: 804h*/
- __IO uint32_t DSTS; /*!< dev Status Register (RO) Address offset: 808h*/
- uint32_t Reserved0C; /*!< Reserved 80Ch*/
- __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask Address offset: 810h*/
- __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask Address offset: 814h*/
- __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg Address offset: 818h*/
- __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask Address offset: 81Ch*/
- uint32_t Reserved20; /*!< Reserved 820h*/
- uint32_t Reserved9; /*!< Reserved 824h*/
- __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register Address offset: 828h*/
- __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register Address offset: 82Ch*/
- __IO uint32_t DTHRCTL; /*!< dev thr Address offset: 830h*/
- __IO uint32_t DIEPEMPMSK; /*!< dev empty msk Address offset: 834h*/
- __IO uint32_t DEACHINT; /*!< dedicated EP interrupt Address offset: 838h*/
- __IO uint32_t DEACHMSK; /*!< dedicated EP msk Address offset: 83Ch*/
- uint32_t Reserved40; /*!< dedicated EP mask Address offset: 840h*/
- __IO uint32_t DINEP1MSK; /*!< dedicated EP mask Address offset: 844h*/
- uint32_t Reserved44[15]; /*!< Reserved 844-87Ch*/
- __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk Address offset: 884h*/
-} USB_OTG_DeviceTypeDef;
-
-/**
- * @brief __IN_Endpoint-Specific_Register
- */
-
-typedef struct
-{
- __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h*/
- uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h*/
- __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h*/
- uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch*/
- __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h*/
- __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h*/
- __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h*/
- uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch*/
-} USB_OTG_INEndpointTypeDef;
-
-/**
- * @brief __OUT_Endpoint-Specific_Registers
- */
-
-typedef struct
-{
- __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h*/
- uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h*/
- __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h*/
- uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch*/
- __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h*/
- __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h*/
- uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch*/
-} USB_OTG_OUTEndpointTypeDef;
-
-/**
- * @brief __Host_Mode_Register_Structures
- */
-
-typedef struct
-{
- __IO uint32_t HCFG; /*!< Host Configuration Register 400h*/
- __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h*/
- __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h*/
- uint32_t Reserved40C; /*!< Reserved 40Ch*/
- __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h*/
- __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h*/
- __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h*/
-} USB_OTG_HostTypeDef;
-
-/**
- * @brief __Host_Channel_Specific_Registers
- */
-
-typedef struct
-{
- __IO uint32_t HCCHAR;
- __IO uint32_t HCSPLT;
- __IO uint32_t HCINT;
- __IO uint32_t HCINTMSK;
- __IO uint32_t HCTSIZ;
- __IO uint32_t HCDMA;
- uint32_t Reserved[2];
-} USB_OTG_HostChannelTypeDef;
-
-/*!< USB registers base address */
-#define USB_OTG_FS_PERIPH_BASE 0x50000000UL
-
-#define USB_OTG_GLOBAL_BASE 0x00000000UL
-#define USB_OTG_DEVICE_BASE 0x00000800UL
-#define USB_OTG_IN_ENDPOINT_BASE 0x00000900UL
-#define USB_OTG_OUT_ENDPOINT_BASE 0x00000B00UL
-#define USB_OTG_EP_REG_SIZE 0x00000020UL
-#define USB_OTG_HOST_BASE 0x00000400UL
-#define USB_OTG_HOST_PORT_BASE 0x00000440UL
-#define USB_OTG_HOST_CHANNEL_BASE 0x00000500UL
-#define USB_OTG_HOST_CHANNEL_SIZE 0x00000020UL
-#define USB_OTG_PCGCCTL_BASE 0x00000E00UL
-#define USB_OTG_FIFO_BASE 0x00001000UL
-#define USB_OTG_FIFO_SIZE 0x00001000UL
-
-/******************************************************************************/
-/* */
-/* USB_OTG */
-/* */
-/******************************************************************************/
-/******************** Bit definition for USB_OTG_GOTGCTL register ***********/
-#define USB_OTG_GOTGCTL_SRQSCS_Pos (0U)
-#define USB_OTG_GOTGCTL_SRQSCS_Msk (0x1UL << USB_OTG_GOTGCTL_SRQSCS_Pos) /*!< 0x00000001 */
-#define USB_OTG_GOTGCTL_SRQSCS USB_OTG_GOTGCTL_SRQSCS_Msk /*!< Session request success */
-#define USB_OTG_GOTGCTL_SRQ_Pos (1U)
-#define USB_OTG_GOTGCTL_SRQ_Msk (0x1UL << USB_OTG_GOTGCTL_SRQ_Pos) /*!< 0x00000002 */
-#define USB_OTG_GOTGCTL_SRQ USB_OTG_GOTGCTL_SRQ_Msk /*!< Session request */
-#define USB_OTG_GOTGCTL_HNGSCS_Pos (8U)
-#define USB_OTG_GOTGCTL_HNGSCS_Msk (0x1UL << USB_OTG_GOTGCTL_HNGSCS_Pos) /*!< 0x00000100 */
-#define USB_OTG_GOTGCTL_HNGSCS USB_OTG_GOTGCTL_HNGSCS_Msk /*!< Host set HNP enable */
-#define USB_OTG_GOTGCTL_HNPRQ_Pos (9U)
-#define USB_OTG_GOTGCTL_HNPRQ_Msk (0x1UL << USB_OTG_GOTGCTL_HNPRQ_Pos) /*!< 0x00000200 */
-#define USB_OTG_GOTGCTL_HNPRQ USB_OTG_GOTGCTL_HNPRQ_Msk /*!< HNP request */
-#define USB_OTG_GOTGCTL_HSHNPEN_Pos (10U)
-#define USB_OTG_GOTGCTL_HSHNPEN_Msk (0x1UL << USB_OTG_GOTGCTL_HSHNPEN_Pos) /*!< 0x00000400 */
-#define USB_OTG_GOTGCTL_HSHNPEN USB_OTG_GOTGCTL_HSHNPEN_Msk /*!< Host set HNP enable */
-#define USB_OTG_GOTGCTL_DHNPEN_Pos (11U)
-#define USB_OTG_GOTGCTL_DHNPEN_Msk (0x1UL << USB_OTG_GOTGCTL_DHNPEN_Pos) /*!< 0x00000800 */
-#define USB_OTG_GOTGCTL_DHNPEN USB_OTG_GOTGCTL_DHNPEN_Msk /*!< Device HNP enabled */
-#define USB_OTG_GOTGCTL_CIDSTS_Pos (16U)
-#define USB_OTG_GOTGCTL_CIDSTS_Msk (0x1UL << USB_OTG_GOTGCTL_CIDSTS_Pos) /*!< 0x00010000 */
-#define USB_OTG_GOTGCTL_CIDSTS USB_OTG_GOTGCTL_CIDSTS_Msk /*!< Connector ID status */
-#define USB_OTG_GOTGCTL_DBCT_Pos (17U)
-#define USB_OTG_GOTGCTL_DBCT_Msk (0x1UL << USB_OTG_GOTGCTL_DBCT_Pos) /*!< 0x00020000 */
-#define USB_OTG_GOTGCTL_DBCT USB_OTG_GOTGCTL_DBCT_Msk /*!< Long/short debounce time */
-#define USB_OTG_GOTGCTL_ASVLD_Pos (18U)
-#define USB_OTG_GOTGCTL_ASVLD_Msk (0x1UL << USB_OTG_GOTGCTL_ASVLD_Pos) /*!< 0x00040000 */
-#define USB_OTG_GOTGCTL_ASVLD USB_OTG_GOTGCTL_ASVLD_Msk /*!< A-session valid */
-#define USB_OTG_GOTGCTL_BSVLD_Pos (19U)
-#define USB_OTG_GOTGCTL_BSVLD_Msk (0x1UL << USB_OTG_GOTGCTL_BSVLD_Pos) /*!< 0x00080000 */
-#define USB_OTG_GOTGCTL_BSVLD USB_OTG_GOTGCTL_BSVLD_Msk /*!< B-session valid */
-
-/******************** Bit definition for USB_OTG_HCFG register ********************/
-
-#define USB_OTG_HCFG_FSLSPCS_Pos (0U)
-#define USB_OTG_HCFG_FSLSPCS_Msk (0x3UL << USB_OTG_HCFG_FSLSPCS_Pos) /*!< 0x00000003 */
-#define USB_OTG_HCFG_FSLSPCS USB_OTG_HCFG_FSLSPCS_Msk /*!< FS/LS PHY clock select */
-#define USB_OTG_HCFG_FSLSPCS_0 (0x1UL << USB_OTG_HCFG_FSLSPCS_Pos) /*!< 0x00000001 */
-#define USB_OTG_HCFG_FSLSPCS_1 (0x2UL << USB_OTG_HCFG_FSLSPCS_Pos) /*!< 0x00000002 */
-#define USB_OTG_HCFG_FSLSS_Pos (2U)
-#define USB_OTG_HCFG_FSLSS_Msk (0x1UL << USB_OTG_HCFG_FSLSS_Pos) /*!< 0x00000004 */
-#define USB_OTG_HCFG_FSLSS USB_OTG_HCFG_FSLSS_Msk /*!< FS- and LS-only support */
-
-/******************** Bit definition for USB_OTG_DCFG register ********************/
-
-#define USB_OTG_DCFG_DSPD_Pos (0U)
-#define USB_OTG_DCFG_DSPD_Msk (0x3UL << USB_OTG_DCFG_DSPD_Pos) /*!< 0x00000003 */
-#define USB_OTG_DCFG_DSPD USB_OTG_DCFG_DSPD_Msk /*!< Device speed */
-#define USB_OTG_DCFG_DSPD_0 (0x1UL << USB_OTG_DCFG_DSPD_Pos) /*!< 0x00000001 */
-#define USB_OTG_DCFG_DSPD_1 (0x2UL << USB_OTG_DCFG_DSPD_Pos) /*!< 0x00000002 */
-#define USB_OTG_DCFG_NZLSOHSK_Pos (2U)
-#define USB_OTG_DCFG_NZLSOHSK_Msk (0x1UL << USB_OTG_DCFG_NZLSOHSK_Pos) /*!< 0x00000004 */
-#define USB_OTG_DCFG_NZLSOHSK USB_OTG_DCFG_NZLSOHSK_Msk /*!< Nonzero-length status OUT handshake */
-
-#define USB_OTG_DCFG_DAD_Pos (4U)
-#define USB_OTG_DCFG_DAD_Msk (0x7FUL << USB_OTG_DCFG_DAD_Pos) /*!< 0x000007F0 */
-#define USB_OTG_DCFG_DAD USB_OTG_DCFG_DAD_Msk /*!< Device address */
-#define USB_OTG_DCFG_DAD_0 (0x01UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000010 */
-#define USB_OTG_DCFG_DAD_1 (0x02UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000020 */
-#define USB_OTG_DCFG_DAD_2 (0x04UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000040 */
-#define USB_OTG_DCFG_DAD_3 (0x08UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000080 */
-#define USB_OTG_DCFG_DAD_4 (0x10UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000100 */
-#define USB_OTG_DCFG_DAD_5 (0x20UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000200 */
-#define USB_OTG_DCFG_DAD_6 (0x40UL << USB_OTG_DCFG_DAD_Pos) /*!< 0x00000400 */
-
-#define USB_OTG_DCFG_PFIVL_Pos (11U)
-#define USB_OTG_DCFG_PFIVL_Msk (0x3UL << USB_OTG_DCFG_PFIVL_Pos) /*!< 0x00001800 */
-#define USB_OTG_DCFG_PFIVL USB_OTG_DCFG_PFIVL_Msk /*!< Periodic (micro)frame interval */
-#define USB_OTG_DCFG_PFIVL_0 (0x1UL << USB_OTG_DCFG_PFIVL_Pos) /*!< 0x00000800 */
-#define USB_OTG_DCFG_PFIVL_1 (0x2UL << USB_OTG_DCFG_PFIVL_Pos) /*!< 0x00001000 */
-
-#define USB_OTG_DCFG_PERSCHIVL_Pos (24U)
-#define USB_OTG_DCFG_PERSCHIVL_Msk (0x3UL << USB_OTG_DCFG_PERSCHIVL_Pos) /*!< 0x03000000 */
-#define USB_OTG_DCFG_PERSCHIVL USB_OTG_DCFG_PERSCHIVL_Msk /*!< Periodic scheduling interval */
-#define USB_OTG_DCFG_PERSCHIVL_0 (0x1UL << USB_OTG_DCFG_PERSCHIVL_Pos) /*!< 0x01000000 */
-#define USB_OTG_DCFG_PERSCHIVL_1 (0x2UL << USB_OTG_DCFG_PERSCHIVL_Pos) /*!< 0x02000000 */
-
-/******************** Bit definition for USB_OTG_PCGCR register ********************/
-#define USB_OTG_PCGCR_STPPCLK_Pos (0U)
-#define USB_OTG_PCGCR_STPPCLK_Msk (0x1UL << USB_OTG_PCGCR_STPPCLK_Pos) /*!< 0x00000001 */
-#define USB_OTG_PCGCR_STPPCLK USB_OTG_PCGCR_STPPCLK_Msk /*!< Stop PHY clock */
-#define USB_OTG_PCGCR_GATEHCLK_Pos (1U)
-#define USB_OTG_PCGCR_GATEHCLK_Msk (0x1UL << USB_OTG_PCGCR_GATEHCLK_Pos) /*!< 0x00000002 */
-#define USB_OTG_PCGCR_GATEHCLK USB_OTG_PCGCR_GATEHCLK_Msk /*!< Gate HCLK */
-#define USB_OTG_PCGCR_PHYSUSP_Pos (4U)
-#define USB_OTG_PCGCR_PHYSUSP_Msk (0x1UL << USB_OTG_PCGCR_PHYSUSP_Pos) /*!< 0x00000010 */
-#define USB_OTG_PCGCR_PHYSUSP USB_OTG_PCGCR_PHYSUSP_Msk /*!< PHY suspended */
-
-/******************** Bit definition for USB_OTG_GOTGINT register ********************/
-#define USB_OTG_GOTGINT_SEDET_Pos (2U)
-#define USB_OTG_GOTGINT_SEDET_Msk (0x1UL << USB_OTG_GOTGINT_SEDET_Pos) /*!< 0x00000004 */
-#define USB_OTG_GOTGINT_SEDET USB_OTG_GOTGINT_SEDET_Msk /*!< Session end detected */
-#define USB_OTG_GOTGINT_SRSSCHG_Pos (8U)
-#define USB_OTG_GOTGINT_SRSSCHG_Msk (0x1UL << USB_OTG_GOTGINT_SRSSCHG_Pos) /*!< 0x00000100 */
-#define USB_OTG_GOTGINT_SRSSCHG USB_OTG_GOTGINT_SRSSCHG_Msk /*!< Session request success status change */
-#define USB_OTG_GOTGINT_HNSSCHG_Pos (9U)
-#define USB_OTG_GOTGINT_HNSSCHG_Msk (0x1UL << USB_OTG_GOTGINT_HNSSCHG_Pos) /*!< 0x00000200 */
-#define USB_OTG_GOTGINT_HNSSCHG USB_OTG_GOTGINT_HNSSCHG_Msk /*!< Host negotiation success status change */
-#define USB_OTG_GOTGINT_HNGDET_Pos (17U)
-#define USB_OTG_GOTGINT_HNGDET_Msk (0x1UL << USB_OTG_GOTGINT_HNGDET_Pos) /*!< 0x00020000 */
-#define USB_OTG_GOTGINT_HNGDET USB_OTG_GOTGINT_HNGDET_Msk /*!< Host negotiation detected */
-#define USB_OTG_GOTGINT_ADTOCHG_Pos (18U)
-#define USB_OTG_GOTGINT_ADTOCHG_Msk (0x1UL << USB_OTG_GOTGINT_ADTOCHG_Pos) /*!< 0x00040000 */
-#define USB_OTG_GOTGINT_ADTOCHG USB_OTG_GOTGINT_ADTOCHG_Msk /*!< A-device timeout change */
-#define USB_OTG_GOTGINT_DBCDNE_Pos (19U)
-#define USB_OTG_GOTGINT_DBCDNE_Msk (0x1UL << USB_OTG_GOTGINT_DBCDNE_Pos) /*!< 0x00080000 */
-#define USB_OTG_GOTGINT_DBCDNE USB_OTG_GOTGINT_DBCDNE_Msk /*!< Debounce done */
-
-/******************** Bit definition for USB_OTG_DCTL register ********************/
-#define USB_OTG_DCTL_RWUSIG_Pos (0U)
-#define USB_OTG_DCTL_RWUSIG_Msk (0x1UL << USB_OTG_DCTL_RWUSIG_Pos) /*!< 0x00000001 */
-#define USB_OTG_DCTL_RWUSIG USB_OTG_DCTL_RWUSIG_Msk /*!< Remote wakeup signaling */
-#define USB_OTG_DCTL_SDIS_Pos (1U)
-#define USB_OTG_DCTL_SDIS_Msk (0x1UL << USB_OTG_DCTL_SDIS_Pos) /*!< 0x00000002 */
-#define USB_OTG_DCTL_SDIS USB_OTG_DCTL_SDIS_Msk /*!< Soft disconnect */
-#define USB_OTG_DCTL_GINSTS_Pos (2U)
-#define USB_OTG_DCTL_GINSTS_Msk (0x1UL << USB_OTG_DCTL_GINSTS_Pos) /*!< 0x00000004 */
-#define USB_OTG_DCTL_GINSTS USB_OTG_DCTL_GINSTS_Msk /*!< Global IN NAK status */
-#define USB_OTG_DCTL_GONSTS_Pos (3U)
-#define USB_OTG_DCTL_GONSTS_Msk (0x1UL << USB_OTG_DCTL_GONSTS_Pos) /*!< 0x00000008 */
-#define USB_OTG_DCTL_GONSTS USB_OTG_DCTL_GONSTS_Msk /*!< Global OUT NAK status */
-
-#define USB_OTG_DCTL_TCTL_Pos (4U)
-#define USB_OTG_DCTL_TCTL_Msk (0x7UL << USB_OTG_DCTL_TCTL_Pos) /*!< 0x00000070 */
-#define USB_OTG_DCTL_TCTL USB_OTG_DCTL_TCTL_Msk /*!< Test control */
-#define USB_OTG_DCTL_TCTL_0 (0x1UL << USB_OTG_DCTL_TCTL_Pos) /*!< 0x00000010 */
-#define USB_OTG_DCTL_TCTL_1 (0x2UL << USB_OTG_DCTL_TCTL_Pos) /*!< 0x00000020 */
-#define USB_OTG_DCTL_TCTL_2 (0x4UL << USB_OTG_DCTL_TCTL_Pos) /*!< 0x00000040 */
-#define USB_OTG_DCTL_SGINAK_Pos (7U)
-#define USB_OTG_DCTL_SGINAK_Msk (0x1UL << USB_OTG_DCTL_SGINAK_Pos) /*!< 0x00000080 */
-#define USB_OTG_DCTL_SGINAK USB_OTG_DCTL_SGINAK_Msk /*!< Set global IN NAK */
-#define USB_OTG_DCTL_CGINAK_Pos (8U)
-#define USB_OTG_DCTL_CGINAK_Msk (0x1UL << USB_OTG_DCTL_CGINAK_Pos) /*!< 0x00000100 */
-#define USB_OTG_DCTL_CGINAK USB_OTG_DCTL_CGINAK_Msk /*!< Clear global IN NAK */
-#define USB_OTG_DCTL_SGONAK_Pos (9U)
-#define USB_OTG_DCTL_SGONAK_Msk (0x1UL << USB_OTG_DCTL_SGONAK_Pos) /*!< 0x00000200 */
-#define USB_OTG_DCTL_SGONAK USB_OTG_DCTL_SGONAK_Msk /*!< Set global OUT NAK */
-#define USB_OTG_DCTL_CGONAK_Pos (10U)
-#define USB_OTG_DCTL_CGONAK_Msk (0x1UL << USB_OTG_DCTL_CGONAK_Pos) /*!< 0x00000400 */
-#define USB_OTG_DCTL_CGONAK USB_OTG_DCTL_CGONAK_Msk /*!< Clear global OUT NAK */
-#define USB_OTG_DCTL_POPRGDNE_Pos (11U)
-#define USB_OTG_DCTL_POPRGDNE_Msk (0x1UL << USB_OTG_DCTL_POPRGDNE_Pos) /*!< 0x00000800 */
-#define USB_OTG_DCTL_POPRGDNE USB_OTG_DCTL_POPRGDNE_Msk /*!< Power-on programming done */
-
-/******************** Bit definition for USB_OTG_HFIR register ********************/
-#define USB_OTG_HFIR_FRIVL_Pos (0U)
-#define USB_OTG_HFIR_FRIVL_Msk (0xFFFFUL << USB_OTG_HFIR_FRIVL_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HFIR_FRIVL USB_OTG_HFIR_FRIVL_Msk /*!< Frame interval */
-
-/******************** Bit definition for USB_OTG_HFNUM register ********************/
-#define USB_OTG_HFNUM_FRNUM_Pos (0U)
-#define USB_OTG_HFNUM_FRNUM_Msk (0xFFFFUL << USB_OTG_HFNUM_FRNUM_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HFNUM_FRNUM USB_OTG_HFNUM_FRNUM_Msk /*!< Frame number */
-#define USB_OTG_HFNUM_FTREM_Pos (16U)
-#define USB_OTG_HFNUM_FTREM_Msk (0xFFFFUL << USB_OTG_HFNUM_FTREM_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_HFNUM_FTREM USB_OTG_HFNUM_FTREM_Msk /*!< Frame time remaining */
-
-/******************** Bit definition for USB_OTG_DSTS register ********************/
-#define USB_OTG_DSTS_SUSPSTS_Pos (0U)
-#define USB_OTG_DSTS_SUSPSTS_Msk (0x1UL << USB_OTG_DSTS_SUSPSTS_Pos) /*!< 0x00000001 */
-#define USB_OTG_DSTS_SUSPSTS USB_OTG_DSTS_SUSPSTS_Msk /*!< Suspend status */
-
-#define USB_OTG_DSTS_ENUMSPD_Pos (1U)
-#define USB_OTG_DSTS_ENUMSPD_Msk (0x3UL << USB_OTG_DSTS_ENUMSPD_Pos) /*!< 0x00000006 */
-#define USB_OTG_DSTS_ENUMSPD USB_OTG_DSTS_ENUMSPD_Msk /*!< Enumerated speed */
-#define USB_OTG_DSTS_ENUMSPD_0 (0x1UL << USB_OTG_DSTS_ENUMSPD_Pos) /*!< 0x00000002 */
-#define USB_OTG_DSTS_ENUMSPD_1 (0x2UL << USB_OTG_DSTS_ENUMSPD_Pos) /*!< 0x00000004 */
-#define USB_OTG_DSTS_EERR_Pos (3U)
-#define USB_OTG_DSTS_EERR_Msk (0x1UL << USB_OTG_DSTS_EERR_Pos) /*!< 0x00000008 */
-#define USB_OTG_DSTS_EERR USB_OTG_DSTS_EERR_Msk /*!< Erratic error */
-#define USB_OTG_DSTS_FNSOF_Pos (8U)
-#define USB_OTG_DSTS_FNSOF_Msk (0x3FFFUL << USB_OTG_DSTS_FNSOF_Pos) /*!< 0x003FFF00 */
-#define USB_OTG_DSTS_FNSOF USB_OTG_DSTS_FNSOF_Msk /*!< Frame number of the received SOF */
-
-/******************** Bit definition for USB_OTG_GAHBCFG register ********************/
-#define USB_OTG_GAHBCFG_GINT_Pos (0U)
-#define USB_OTG_GAHBCFG_GINT_Msk (0x1UL << USB_OTG_GAHBCFG_GINT_Pos) /*!< 0x00000001 */
-#define USB_OTG_GAHBCFG_GINT USB_OTG_GAHBCFG_GINT_Msk /*!< Global interrupt mask */
-#define USB_OTG_GAHBCFG_HBSTLEN_Pos (1U)
-#define USB_OTG_GAHBCFG_HBSTLEN_Msk (0xFUL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< 0x0000001E */
-#define USB_OTG_GAHBCFG_HBSTLEN USB_OTG_GAHBCFG_HBSTLEN_Msk /*!< Burst length/type */
-#define USB_OTG_GAHBCFG_HBSTLEN_0 (0x0UL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< Single */
-#define USB_OTG_GAHBCFG_HBSTLEN_1 (0x1UL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< INCR */
-#define USB_OTG_GAHBCFG_HBSTLEN_2 (0x3UL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< INCR4 */
-#define USB_OTG_GAHBCFG_HBSTLEN_3 (0x5UL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< INCR8 */
-#define USB_OTG_GAHBCFG_HBSTLEN_4 (0x7UL << USB_OTG_GAHBCFG_HBSTLEN_Pos) /*!< INCR16 */
-#define USB_OTG_GAHBCFG_DMAEN_Pos (5U)
-#define USB_OTG_GAHBCFG_DMAEN_Msk (0x1UL << USB_OTG_GAHBCFG_DMAEN_Pos) /*!< 0x00000020 */
-#define USB_OTG_GAHBCFG_DMAEN USB_OTG_GAHBCFG_DMAEN_Msk /*!< DMA enable */
-#define USB_OTG_GAHBCFG_TXFELVL_Pos (7U)
-#define USB_OTG_GAHBCFG_TXFELVL_Msk (0x1UL << USB_OTG_GAHBCFG_TXFELVL_Pos) /*!< 0x00000080 */
-#define USB_OTG_GAHBCFG_TXFELVL USB_OTG_GAHBCFG_TXFELVL_Msk /*!< TxFIFO empty level */
-#define USB_OTG_GAHBCFG_PTXFELVL_Pos (8U)
-#define USB_OTG_GAHBCFG_PTXFELVL_Msk (0x1UL << USB_OTG_GAHBCFG_PTXFELVL_Pos) /*!< 0x00000100 */
-#define USB_OTG_GAHBCFG_PTXFELVL USB_OTG_GAHBCFG_PTXFELVL_Msk /*!< Periodic TxFIFO empty level */
-
-/******************** Bit definition for USB_OTG_GUSBCFG register ********************/
-
-#define USB_OTG_GUSBCFG_TOCAL_Pos (0U)
-#define USB_OTG_GUSBCFG_TOCAL_Msk (0x7UL << USB_OTG_GUSBCFG_TOCAL_Pos) /*!< 0x00000007 */
-#define USB_OTG_GUSBCFG_TOCAL USB_OTG_GUSBCFG_TOCAL_Msk /*!< FS timeout calibration */
-#define USB_OTG_GUSBCFG_TOCAL_0 (0x1UL << USB_OTG_GUSBCFG_TOCAL_Pos) /*!< 0x00000001 */
-#define USB_OTG_GUSBCFG_TOCAL_1 (0x2UL << USB_OTG_GUSBCFG_TOCAL_Pos) /*!< 0x00000002 */
-#define USB_OTG_GUSBCFG_TOCAL_2 (0x4UL << USB_OTG_GUSBCFG_TOCAL_Pos) /*!< 0x00000004 */
-#define USB_OTG_GUSBCFG_PHYSEL_Pos (6U)
-#define USB_OTG_GUSBCFG_PHYSEL_Msk (0x1UL << USB_OTG_GUSBCFG_PHYSEL_Pos) /*!< 0x00000040 */
-#define USB_OTG_GUSBCFG_PHYSEL USB_OTG_GUSBCFG_PHYSEL_Msk /*!< USB 2.0 high-speed ULPI PHY or USB 1.1 full-speed serial transceiver select */
-#define USB_OTG_GUSBCFG_SRPCAP_Pos (8U)
-#define USB_OTG_GUSBCFG_SRPCAP_Msk (0x1UL << USB_OTG_GUSBCFG_SRPCAP_Pos) /*!< 0x00000100 */
-#define USB_OTG_GUSBCFG_SRPCAP USB_OTG_GUSBCFG_SRPCAP_Msk /*!< SRP-capable */
-#define USB_OTG_GUSBCFG_HNPCAP_Pos (9U)
-#define USB_OTG_GUSBCFG_HNPCAP_Msk (0x1UL << USB_OTG_GUSBCFG_HNPCAP_Pos) /*!< 0x00000200 */
-#define USB_OTG_GUSBCFG_HNPCAP USB_OTG_GUSBCFG_HNPCAP_Msk /*!< HNP-capable */
-#define USB_OTG_GUSBCFG_TRDT_Pos (10U)
-#define USB_OTG_GUSBCFG_TRDT_Msk (0xFUL << USB_OTG_GUSBCFG_TRDT_Pos) /*!< 0x00003C00 */
-#define USB_OTG_GUSBCFG_TRDT USB_OTG_GUSBCFG_TRDT_Msk /*!< USB turnaround time */
-#define USB_OTG_GUSBCFG_TRDT_0 (0x1UL << USB_OTG_GUSBCFG_TRDT_Pos) /*!< 0x00000400 */
-#define USB_OTG_GUSBCFG_TRDT_1 (0x2UL << USB_OTG_GUSBCFG_TRDT_Pos) /*!< 0x00000800 */
-#define USB_OTG_GUSBCFG_TRDT_2 (0x4UL << USB_OTG_GUSBCFG_TRDT_Pos) /*!< 0x00001000 */
-#define USB_OTG_GUSBCFG_TRDT_3 (0x8UL << USB_OTG_GUSBCFG_TRDT_Pos) /*!< 0x00002000 */
-#define USB_OTG_GUSBCFG_PHYLPCS_Pos (15U)
-#define USB_OTG_GUSBCFG_PHYLPCS_Msk (0x1UL << USB_OTG_GUSBCFG_PHYLPCS_Pos) /*!< 0x00008000 */
-#define USB_OTG_GUSBCFG_PHYLPCS USB_OTG_GUSBCFG_PHYLPCS_Msk /*!< PHY Low-power clock select */
-#define USB_OTG_GUSBCFG_ULPIFSLS_Pos (17U)
-#define USB_OTG_GUSBCFG_ULPIFSLS_Msk (0x1UL << USB_OTG_GUSBCFG_ULPIFSLS_Pos) /*!< 0x00020000 */
-#define USB_OTG_GUSBCFG_ULPIFSLS USB_OTG_GUSBCFG_ULPIFSLS_Msk /*!< ULPI FS/LS select */
-#define USB_OTG_GUSBCFG_ULPIAR_Pos (18U)
-#define USB_OTG_GUSBCFG_ULPIAR_Msk (0x1UL << USB_OTG_GUSBCFG_ULPIAR_Pos) /*!< 0x00040000 */
-#define USB_OTG_GUSBCFG_ULPIAR USB_OTG_GUSBCFG_ULPIAR_Msk /*!< ULPI Auto-resume */
-#define USB_OTG_GUSBCFG_ULPICSM_Pos (19U)
-#define USB_OTG_GUSBCFG_ULPICSM_Msk (0x1UL << USB_OTG_GUSBCFG_ULPICSM_Pos) /*!< 0x00080000 */
-#define USB_OTG_GUSBCFG_ULPICSM USB_OTG_GUSBCFG_ULPICSM_Msk /*!< ULPI Clock SuspendM */
-#define USB_OTG_GUSBCFG_ULPIEVBUSD_Pos (20U)
-#define USB_OTG_GUSBCFG_ULPIEVBUSD_Msk (0x1UL << USB_OTG_GUSBCFG_ULPIEVBUSD_Pos) /*!< 0x00100000 */
-#define USB_OTG_GUSBCFG_ULPIEVBUSD USB_OTG_GUSBCFG_ULPIEVBUSD_Msk /*!< ULPI External VBUS Drive */
-#define USB_OTG_GUSBCFG_ULPIEVBUSI_Pos (21U)
-#define USB_OTG_GUSBCFG_ULPIEVBUSI_Msk (0x1UL << USB_OTG_GUSBCFG_ULPIEVBUSI_Pos) /*!< 0x00200000 */
-#define USB_OTG_GUSBCFG_ULPIEVBUSI USB_OTG_GUSBCFG_ULPIEVBUSI_Msk /*!< ULPI external VBUS indicator */
-#define USB_OTG_GUSBCFG_TSDPS_Pos (22U)
-#define USB_OTG_GUSBCFG_TSDPS_Msk (0x1UL << USB_OTG_GUSBCFG_TSDPS_Pos) /*!< 0x00400000 */
-#define USB_OTG_GUSBCFG_TSDPS USB_OTG_GUSBCFG_TSDPS_Msk /*!< TermSel DLine pulsing selection */
-#define USB_OTG_GUSBCFG_PCCI_Pos (23U)
-#define USB_OTG_GUSBCFG_PCCI_Msk (0x1UL << USB_OTG_GUSBCFG_PCCI_Pos) /*!< 0x00800000 */
-#define USB_OTG_GUSBCFG_PCCI USB_OTG_GUSBCFG_PCCI_Msk /*!< Indicator complement */
-#define USB_OTG_GUSBCFG_PTCI_Pos (24U)
-#define USB_OTG_GUSBCFG_PTCI_Msk (0x1UL << USB_OTG_GUSBCFG_PTCI_Pos) /*!< 0x01000000 */
-#define USB_OTG_GUSBCFG_PTCI USB_OTG_GUSBCFG_PTCI_Msk /*!< Indicator pass through */
-#define USB_OTG_GUSBCFG_ULPIIPD_Pos (25U)
-#define USB_OTG_GUSBCFG_ULPIIPD_Msk (0x1UL << USB_OTG_GUSBCFG_ULPIIPD_Pos) /*!< 0x02000000 */
-#define USB_OTG_GUSBCFG_ULPIIPD USB_OTG_GUSBCFG_ULPIIPD_Msk /*!< ULPI interface protect disable */
-#define USB_OTG_GUSBCFG_FHMOD_Pos (29U)
-#define USB_OTG_GUSBCFG_FHMOD_Msk (0x1UL << USB_OTG_GUSBCFG_FHMOD_Pos) /*!< 0x20000000 */
-#define USB_OTG_GUSBCFG_FHMOD USB_OTG_GUSBCFG_FHMOD_Msk /*!< Forced host mode */
-#define USB_OTG_GUSBCFG_FDMOD_Pos (30U)
-#define USB_OTG_GUSBCFG_FDMOD_Msk (0x1UL << USB_OTG_GUSBCFG_FDMOD_Pos) /*!< 0x40000000 */
-#define USB_OTG_GUSBCFG_FDMOD USB_OTG_GUSBCFG_FDMOD_Msk /*!< Forced peripheral mode */
-#define USB_OTG_GUSBCFG_CTXPKT_Pos (31U)
-#define USB_OTG_GUSBCFG_CTXPKT_Msk (0x1UL << USB_OTG_GUSBCFG_CTXPKT_Pos) /*!< 0x80000000 */
-#define USB_OTG_GUSBCFG_CTXPKT USB_OTG_GUSBCFG_CTXPKT_Msk /*!< Corrupt Tx packet */
-
-/******************** Bit definition for USB_OTG_GRSTCTL register ********************/
-#define USB_OTG_GRSTCTL_CSRST_Pos (0U)
-#define USB_OTG_GRSTCTL_CSRST_Msk (0x1UL << USB_OTG_GRSTCTL_CSRST_Pos) /*!< 0x00000001 */
-#define USB_OTG_GRSTCTL_CSRST USB_OTG_GRSTCTL_CSRST_Msk /*!< Core soft reset */
-#define USB_OTG_GRSTCTL_HSRST_Pos (1U)
-#define USB_OTG_GRSTCTL_HSRST_Msk (0x1UL << USB_OTG_GRSTCTL_HSRST_Pos) /*!< 0x00000002 */
-#define USB_OTG_GRSTCTL_HSRST USB_OTG_GRSTCTL_HSRST_Msk /*!< HCLK soft reset */
-#define USB_OTG_GRSTCTL_FCRST_Pos (2U)
-#define USB_OTG_GRSTCTL_FCRST_Msk (0x1UL << USB_OTG_GRSTCTL_FCRST_Pos) /*!< 0x00000004 */
-#define USB_OTG_GRSTCTL_FCRST USB_OTG_GRSTCTL_FCRST_Msk /*!< Host frame counter reset */
-#define USB_OTG_GRSTCTL_RXFFLSH_Pos (4U)
-#define USB_OTG_GRSTCTL_RXFFLSH_Msk (0x1UL << USB_OTG_GRSTCTL_RXFFLSH_Pos) /*!< 0x00000010 */
-#define USB_OTG_GRSTCTL_RXFFLSH USB_OTG_GRSTCTL_RXFFLSH_Msk /*!< RxFIFO flush */
-#define USB_OTG_GRSTCTL_TXFFLSH_Pos (5U)
-#define USB_OTG_GRSTCTL_TXFFLSH_Msk (0x1UL << USB_OTG_GRSTCTL_TXFFLSH_Pos) /*!< 0x00000020 */
-#define USB_OTG_GRSTCTL_TXFFLSH USB_OTG_GRSTCTL_TXFFLSH_Msk /*!< TxFIFO flush */
-
-
-#define USB_OTG_GRSTCTL_TXFNUM_Pos (6U)
-#define USB_OTG_GRSTCTL_TXFNUM_Msk (0x1FUL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x000007C0 */
-#define USB_OTG_GRSTCTL_TXFNUM USB_OTG_GRSTCTL_TXFNUM_Msk /*!< TxFIFO number */
-#define USB_OTG_GRSTCTL_TXFNUM_0 (0x01UL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x00000040 */
-#define USB_OTG_GRSTCTL_TXFNUM_1 (0x02UL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x00000080 */
-#define USB_OTG_GRSTCTL_TXFNUM_2 (0x04UL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x00000100 */
-#define USB_OTG_GRSTCTL_TXFNUM_3 (0x08UL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x00000200 */
-#define USB_OTG_GRSTCTL_TXFNUM_4 (0x10UL << USB_OTG_GRSTCTL_TXFNUM_Pos) /*!< 0x00000400 */
-#define USB_OTG_GRSTCTL_DMAREQ_Pos (30U)
-#define USB_OTG_GRSTCTL_DMAREQ_Msk (0x1UL << USB_OTG_GRSTCTL_DMAREQ_Pos) /*!< 0x40000000 */
-#define USB_OTG_GRSTCTL_DMAREQ USB_OTG_GRSTCTL_DMAREQ_Msk /*!< DMA request signal */
-#define USB_OTG_GRSTCTL_AHBIDL_Pos (31U)
-#define USB_OTG_GRSTCTL_AHBIDL_Msk (0x1UL << USB_OTG_GRSTCTL_AHBIDL_Pos) /*!< 0x80000000 */
-#define USB_OTG_GRSTCTL_AHBIDL USB_OTG_GRSTCTL_AHBIDL_Msk /*!< AHB master idle */
-
-/******************** Bit definition for USB_OTG_DIEPMSK register ********************/
-#define USB_OTG_DIEPMSK_XFRCM_Pos (0U)
-#define USB_OTG_DIEPMSK_XFRCM_Msk (0x1UL << USB_OTG_DIEPMSK_XFRCM_Pos) /*!< 0x00000001 */
-#define USB_OTG_DIEPMSK_XFRCM USB_OTG_DIEPMSK_XFRCM_Msk /*!< Transfer completed interrupt mask */
-#define USB_OTG_DIEPMSK_EPDM_Pos (1U)
-#define USB_OTG_DIEPMSK_EPDM_Msk (0x1UL << USB_OTG_DIEPMSK_EPDM_Pos) /*!< 0x00000002 */
-#define USB_OTG_DIEPMSK_EPDM USB_OTG_DIEPMSK_EPDM_Msk /*!< Endpoint disabled interrupt mask */
-#define USB_OTG_DIEPMSK_TOM_Pos (3U)
-#define USB_OTG_DIEPMSK_TOM_Msk (0x1UL << USB_OTG_DIEPMSK_TOM_Pos) /*!< 0x00000008 */
-#define USB_OTG_DIEPMSK_TOM USB_OTG_DIEPMSK_TOM_Msk /*!< Timeout condition mask (nonisochronous endpoints) */
-#define USB_OTG_DIEPMSK_ITTXFEMSK_Pos (4U)
-#define USB_OTG_DIEPMSK_ITTXFEMSK_Msk (0x1UL << USB_OTG_DIEPMSK_ITTXFEMSK_Pos) /*!< 0x00000010 */
-#define USB_OTG_DIEPMSK_ITTXFEMSK USB_OTG_DIEPMSK_ITTXFEMSK_Msk /*!< IN token received when TxFIFO empty mask */
-#define USB_OTG_DIEPMSK_INEPNMM_Pos (5U)
-#define USB_OTG_DIEPMSK_INEPNMM_Msk (0x1UL << USB_OTG_DIEPMSK_INEPNMM_Pos) /*!< 0x00000020 */
-#define USB_OTG_DIEPMSK_INEPNMM USB_OTG_DIEPMSK_INEPNMM_Msk /*!< IN token received with EP mismatch mask */
-#define USB_OTG_DIEPMSK_INEPNEM_Pos (6U)
-#define USB_OTG_DIEPMSK_INEPNEM_Msk (0x1UL << USB_OTG_DIEPMSK_INEPNEM_Pos) /*!< 0x00000040 */
-#define USB_OTG_DIEPMSK_INEPNEM USB_OTG_DIEPMSK_INEPNEM_Msk /*!< IN endpoint NAK effective mask */
-#define USB_OTG_DIEPMSK_TXFURM_Pos (8U)
-#define USB_OTG_DIEPMSK_TXFURM_Msk (0x1UL << USB_OTG_DIEPMSK_TXFURM_Pos) /*!< 0x00000100 */
-#define USB_OTG_DIEPMSK_TXFURM USB_OTG_DIEPMSK_TXFURM_Msk /*!< FIFO underrun mask */
-#define USB_OTG_DIEPMSK_BIM_Pos (9U)
-#define USB_OTG_DIEPMSK_BIM_Msk (0x1UL << USB_OTG_DIEPMSK_BIM_Pos) /*!< 0x00000200 */
-#define USB_OTG_DIEPMSK_BIM USB_OTG_DIEPMSK_BIM_Msk /*!< BNA interrupt mask */
-
-/******************** Bit definition for USB_OTG_HPTXSTS register ********************/
-#define USB_OTG_HPTXSTS_PTXFSAVL_Pos (0U)
-#define USB_OTG_HPTXSTS_PTXFSAVL_Msk (0xFFFFUL << USB_OTG_HPTXSTS_PTXFSAVL_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HPTXSTS_PTXFSAVL USB_OTG_HPTXSTS_PTXFSAVL_Msk /*!< Periodic transmit data FIFO space available */
-#define USB_OTG_HPTXSTS_PTXQSAV_Pos (16U)
-#define USB_OTG_HPTXSTS_PTXQSAV_Msk (0xFFUL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00FF0000 */
-#define USB_OTG_HPTXSTS_PTXQSAV USB_OTG_HPTXSTS_PTXQSAV_Msk /*!< Periodic transmit request queue space available */
-#define USB_OTG_HPTXSTS_PTXQSAV_0 (0x01UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00010000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_1 (0x02UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00020000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_2 (0x04UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00040000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_3 (0x08UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00080000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_4 (0x10UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00100000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_5 (0x20UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00200000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_6 (0x40UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00400000 */
-#define USB_OTG_HPTXSTS_PTXQSAV_7 (0x80UL << USB_OTG_HPTXSTS_PTXQSAV_Pos) /*!< 0x00800000 */
-
-#define USB_OTG_HPTXSTS_PTXQTOP_Pos (24U)
-#define USB_OTG_HPTXSTS_PTXQTOP_Msk (0xFFUL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0xFF000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP USB_OTG_HPTXSTS_PTXQTOP_Msk /*!< Top of the periodic transmit request queue */
-#define USB_OTG_HPTXSTS_PTXQTOP_0 (0x01UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x01000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_1 (0x02UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x02000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_2 (0x04UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x04000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_3 (0x08UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x08000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_4 (0x10UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x10000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_5 (0x20UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x20000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_6 (0x40UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x40000000 */
-#define USB_OTG_HPTXSTS_PTXQTOP_7 (0x80UL << USB_OTG_HPTXSTS_PTXQTOP_Pos) /*!< 0x80000000 */
-
-/******************** Bit definition for USB_OTG_HAINT register ********************/
-#define USB_OTG_HAINT_HAINT_Pos (0U)
-#define USB_OTG_HAINT_HAINT_Msk (0xFFFFUL << USB_OTG_HAINT_HAINT_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HAINT_HAINT USB_OTG_HAINT_HAINT_Msk /*!< Channel interrupts */
-
-/******************** Bit definition for USB_OTG_DOEPMSK register ********************/
-#define USB_OTG_DOEPMSK_XFRCM_Pos (0U)
-#define USB_OTG_DOEPMSK_XFRCM_Msk (0x1UL << USB_OTG_DOEPMSK_XFRCM_Pos) /*!< 0x00000001 */
-#define USB_OTG_DOEPMSK_XFRCM USB_OTG_DOEPMSK_XFRCM_Msk /*!< Transfer completed interrupt mask */
-#define USB_OTG_DOEPMSK_EPDM_Pos (1U)
-#define USB_OTG_DOEPMSK_EPDM_Msk (0x1UL << USB_OTG_DOEPMSK_EPDM_Pos) /*!< 0x00000002 */
-#define USB_OTG_DOEPMSK_EPDM USB_OTG_DOEPMSK_EPDM_Msk /*!< Endpoint disabled interrupt mask */
-#define USB_OTG_DOEPMSK_AHBERRM_Pos (2U)
-#define USB_OTG_DOEPMSK_AHBERRM_Msk (0x1UL << USB_OTG_DOEPMSK_AHBERRM_Pos) /*!< 0x00000004 */
-#define USB_OTG_DOEPMSK_AHBERRM USB_OTG_DOEPMSK_AHBERRM_Msk /*!< OUT transaction AHB Error interrupt mask */
-#define USB_OTG_DOEPMSK_STUPM_Pos (3U)
-#define USB_OTG_DOEPMSK_STUPM_Msk (0x1UL << USB_OTG_DOEPMSK_STUPM_Pos) /*!< 0x00000008 */
-#define USB_OTG_DOEPMSK_STUPM USB_OTG_DOEPMSK_STUPM_Msk /*!< SETUP phase done mask */
-#define USB_OTG_DOEPMSK_OTEPDM_Pos (4U)
-#define USB_OTG_DOEPMSK_OTEPDM_Msk (0x1UL << USB_OTG_DOEPMSK_OTEPDM_Pos) /*!< 0x00000010 */
-#define USB_OTG_DOEPMSK_OTEPDM USB_OTG_DOEPMSK_OTEPDM_Msk /*!< OUT token received when endpoint disabled mask */
-#define USB_OTG_DOEPMSK_OTEPSPRM_Pos (5U)
-#define USB_OTG_DOEPMSK_OTEPSPRM_Msk (0x1UL << USB_OTG_DOEPMSK_OTEPSPRM_Pos) /*!< 0x00000020 */
-#define USB_OTG_DOEPMSK_OTEPSPRM USB_OTG_DOEPMSK_OTEPSPRM_Msk /*!< Status Phase Received mask */
-#define USB_OTG_DOEPMSK_B2BSTUP_Pos (6U)
-#define USB_OTG_DOEPMSK_B2BSTUP_Msk (0x1UL << USB_OTG_DOEPMSK_B2BSTUP_Pos) /*!< 0x00000040 */
-#define USB_OTG_DOEPMSK_B2BSTUP USB_OTG_DOEPMSK_B2BSTUP_Msk /*!< Back-to-back SETUP packets received mask */
-#define USB_OTG_DOEPMSK_OPEM_Pos (8U)
-#define USB_OTG_DOEPMSK_OPEM_Msk (0x1UL << USB_OTG_DOEPMSK_OPEM_Pos) /*!< 0x00000100 */
-#define USB_OTG_DOEPMSK_OPEM USB_OTG_DOEPMSK_OPEM_Msk /*!< OUT packet error mask */
-#define USB_OTG_DOEPMSK_BOIM_Pos (9U)
-#define USB_OTG_DOEPMSK_BOIM_Msk (0x1UL << USB_OTG_DOEPMSK_BOIM_Pos) /*!< 0x00000200 */
-#define USB_OTG_DOEPMSK_BOIM USB_OTG_DOEPMSK_BOIM_Msk /*!< BNA interrupt mask */
-#define USB_OTG_DOEPMSK_BERRM_Pos (12U)
-#define USB_OTG_DOEPMSK_BERRM_Msk (0x1UL << USB_OTG_DOEPMSK_BERRM_Pos) /*!< 0x00001000 */
-#define USB_OTG_DOEPMSK_BERRM USB_OTG_DOEPMSK_BERRM_Msk /*!< Babble error interrupt mask */
-#define USB_OTG_DOEPMSK_NAKM_Pos (13U)
-#define USB_OTG_DOEPMSK_NAKM_Msk (0x1UL << USB_OTG_DOEPMSK_NAKM_Pos) /*!< 0x00002000 */
-#define USB_OTG_DOEPMSK_NAKM USB_OTG_DOEPMSK_NAKM_Msk /*!< OUT Packet NAK interrupt mask */
-#define USB_OTG_DOEPMSK_NYETM_Pos (14U)
-#define USB_OTG_DOEPMSK_NYETM_Msk (0x1UL << USB_OTG_DOEPMSK_NYETM_Pos) /*!< 0x00004000 */
-#define USB_OTG_DOEPMSK_NYETM USB_OTG_DOEPMSK_NYETM_Msk /*!< NYET interrupt mask */
-/******************** Bit definition for USB_OTG_GINTSTS register ********************/
-#define USB_OTG_GINTSTS_CMOD_Pos (0U)
-#define USB_OTG_GINTSTS_CMOD_Msk (0x1UL << USB_OTG_GINTSTS_CMOD_Pos) /*!< 0x00000001 */
-#define USB_OTG_GINTSTS_CMOD USB_OTG_GINTSTS_CMOD_Msk /*!< Current mode of operation */
-#define USB_OTG_GINTSTS_MMIS_Pos (1U)
-#define USB_OTG_GINTSTS_MMIS_Msk (0x1UL << USB_OTG_GINTSTS_MMIS_Pos) /*!< 0x00000002 */
-#define USB_OTG_GINTSTS_MMIS USB_OTG_GINTSTS_MMIS_Msk /*!< Mode mismatch interrupt */
-#define USB_OTG_GINTSTS_OTGINT_Pos (2U)
-#define USB_OTG_GINTSTS_OTGINT_Msk (0x1UL << USB_OTG_GINTSTS_OTGINT_Pos) /*!< 0x00000004 */
-#define USB_OTG_GINTSTS_OTGINT USB_OTG_GINTSTS_OTGINT_Msk /*!< OTG interrupt */
-#define USB_OTG_GINTSTS_SOF_Pos (3U)
-#define USB_OTG_GINTSTS_SOF_Msk (0x1UL << USB_OTG_GINTSTS_SOF_Pos) /*!< 0x00000008 */
-#define USB_OTG_GINTSTS_SOF USB_OTG_GINTSTS_SOF_Msk /*!< Start of frame */
-#define USB_OTG_GINTSTS_RXFLVL_Pos (4U)
-#define USB_OTG_GINTSTS_RXFLVL_Msk (0x1UL << USB_OTG_GINTSTS_RXFLVL_Pos) /*!< 0x00000010 */
-#define USB_OTG_GINTSTS_RXFLVL USB_OTG_GINTSTS_RXFLVL_Msk /*!< RxFIFO nonempty */
-#define USB_OTG_GINTSTS_NPTXFE_Pos (5U)
-#define USB_OTG_GINTSTS_NPTXFE_Msk (0x1UL << USB_OTG_GINTSTS_NPTXFE_Pos) /*!< 0x00000020 */
-#define USB_OTG_GINTSTS_NPTXFE USB_OTG_GINTSTS_NPTXFE_Msk /*!< Nonperiodic TxFIFO empty */
-#define USB_OTG_GINTSTS_GINAKEFF_Pos (6U)
-#define USB_OTG_GINTSTS_GINAKEFF_Msk (0x1UL << USB_OTG_GINTSTS_GINAKEFF_Pos) /*!< 0x00000040 */
-#define USB_OTG_GINTSTS_GINAKEFF USB_OTG_GINTSTS_GINAKEFF_Msk /*!< Global IN nonperiodic NAK effective */
-#define USB_OTG_GINTSTS_BOUTNAKEFF_Pos (7U)
-#define USB_OTG_GINTSTS_BOUTNAKEFF_Msk (0x1UL << USB_OTG_GINTSTS_BOUTNAKEFF_Pos) /*!< 0x00000080 */
-#define USB_OTG_GINTSTS_BOUTNAKEFF USB_OTG_GINTSTS_BOUTNAKEFF_Msk /*!< Global OUT NAK effective */
-#define USB_OTG_GINTSTS_ESUSP_Pos (10U)
-#define USB_OTG_GINTSTS_ESUSP_Msk (0x1UL << USB_OTG_GINTSTS_ESUSP_Pos) /*!< 0x00000400 */
-#define USB_OTG_GINTSTS_ESUSP USB_OTG_GINTSTS_ESUSP_Msk /*!< Early suspend */
-#define USB_OTG_GINTSTS_USBSUSP_Pos (11U)
-#define USB_OTG_GINTSTS_USBSUSP_Msk (0x1UL << USB_OTG_GINTSTS_USBSUSP_Pos) /*!< 0x00000800 */
-#define USB_OTG_GINTSTS_USBSUSP USB_OTG_GINTSTS_USBSUSP_Msk /*!< USB suspend */
-#define USB_OTG_GINTSTS_USBRST_Pos (12U)
-#define USB_OTG_GINTSTS_USBRST_Msk (0x1UL << USB_OTG_GINTSTS_USBRST_Pos) /*!< 0x00001000 */
-#define USB_OTG_GINTSTS_USBRST USB_OTG_GINTSTS_USBRST_Msk /*!< USB reset */
-#define USB_OTG_GINTSTS_ENUMDNE_Pos (13U)
-#define USB_OTG_GINTSTS_ENUMDNE_Msk (0x1UL << USB_OTG_GINTSTS_ENUMDNE_Pos) /*!< 0x00002000 */
-#define USB_OTG_GINTSTS_ENUMDNE USB_OTG_GINTSTS_ENUMDNE_Msk /*!< Enumeration done */
-#define USB_OTG_GINTSTS_ISOODRP_Pos (14U)
-#define USB_OTG_GINTSTS_ISOODRP_Msk (0x1UL << USB_OTG_GINTSTS_ISOODRP_Pos) /*!< 0x00004000 */
-#define USB_OTG_GINTSTS_ISOODRP USB_OTG_GINTSTS_ISOODRP_Msk /*!< Isochronous OUT packet dropped interrupt */
-#define USB_OTG_GINTSTS_EOPF_Pos (15U)
-#define USB_OTG_GINTSTS_EOPF_Msk (0x1UL << USB_OTG_GINTSTS_EOPF_Pos) /*!< 0x00008000 */
-#define USB_OTG_GINTSTS_EOPF USB_OTG_GINTSTS_EOPF_Msk /*!< End of periodic frame interrupt */
-#define USB_OTG_GINTSTS_IEPINT_Pos (18U)
-#define USB_OTG_GINTSTS_IEPINT_Msk (0x1UL << USB_OTG_GINTSTS_IEPINT_Pos) /*!< 0x00040000 */
-#define USB_OTG_GINTSTS_IEPINT USB_OTG_GINTSTS_IEPINT_Msk /*!< IN endpoint interrupt */
-#define USB_OTG_GINTSTS_OEPINT_Pos (19U)
-#define USB_OTG_GINTSTS_OEPINT_Msk (0x1UL << USB_OTG_GINTSTS_OEPINT_Pos) /*!< 0x00080000 */
-#define USB_OTG_GINTSTS_OEPINT USB_OTG_GINTSTS_OEPINT_Msk /*!< OUT endpoint interrupt */
-#define USB_OTG_GINTSTS_IISOIXFR_Pos (20U)
-#define USB_OTG_GINTSTS_IISOIXFR_Msk (0x1UL << USB_OTG_GINTSTS_IISOIXFR_Pos) /*!< 0x00100000 */
-#define USB_OTG_GINTSTS_IISOIXFR USB_OTG_GINTSTS_IISOIXFR_Msk /*!< Incomplete isochronous IN transfer */
-#define USB_OTG_GINTSTS_PXFR_INCOMPISOOUT_Pos (21U)
-#define USB_OTG_GINTSTS_PXFR_INCOMPISOOUT_Msk (0x1UL << USB_OTG_GINTSTS_PXFR_INCOMPISOOUT_Pos) /*!< 0x00200000 */
-#define USB_OTG_GINTSTS_PXFR_INCOMPISOOUT USB_OTG_GINTSTS_PXFR_INCOMPISOOUT_Msk /*!< Incomplete periodic transfer */
-#define USB_OTG_GINTSTS_DATAFSUSP_Pos (22U)
-#define USB_OTG_GINTSTS_DATAFSUSP_Msk (0x1UL << USB_OTG_GINTSTS_DATAFSUSP_Pos) /*!< 0x00400000 */
-#define USB_OTG_GINTSTS_DATAFSUSP USB_OTG_GINTSTS_DATAFSUSP_Msk /*!< Data fetch suspended */
-#define USB_OTG_GINTSTS_HPRTINT_Pos (24U)
-#define USB_OTG_GINTSTS_HPRTINT_Msk (0x1UL << USB_OTG_GINTSTS_HPRTINT_Pos) /*!< 0x01000000 */
-#define USB_OTG_GINTSTS_HPRTINT USB_OTG_GINTSTS_HPRTINT_Msk /*!< Host port interrupt */
-#define USB_OTG_GINTSTS_HCINT_Pos (25U)
-#define USB_OTG_GINTSTS_HCINT_Msk (0x1UL << USB_OTG_GINTSTS_HCINT_Pos) /*!< 0x02000000 */
-#define USB_OTG_GINTSTS_HCINT USB_OTG_GINTSTS_HCINT_Msk /*!< Host channels interrupt */
-#define USB_OTG_GINTSTS_PTXFE_Pos (26U)
-#define USB_OTG_GINTSTS_PTXFE_Msk (0x1UL << USB_OTG_GINTSTS_PTXFE_Pos) /*!< 0x04000000 */
-#define USB_OTG_GINTSTS_PTXFE USB_OTG_GINTSTS_PTXFE_Msk /*!< Periodic TxFIFO empty */
-#define USB_OTG_GINTSTS_CIDSCHG_Pos (28U)
-#define USB_OTG_GINTSTS_CIDSCHG_Msk (0x1UL << USB_OTG_GINTSTS_CIDSCHG_Pos) /*!< 0x10000000 */
-#define USB_OTG_GINTSTS_CIDSCHG USB_OTG_GINTSTS_CIDSCHG_Msk /*!< Connector ID status change */
-#define USB_OTG_GINTSTS_DISCINT_Pos (29U)
-#define USB_OTG_GINTSTS_DISCINT_Msk (0x1UL << USB_OTG_GINTSTS_DISCINT_Pos) /*!< 0x20000000 */
-#define USB_OTG_GINTSTS_DISCINT USB_OTG_GINTSTS_DISCINT_Msk /*!< Disconnect detected interrupt */
-#define USB_OTG_GINTSTS_SRQINT_Pos (30U)
-#define USB_OTG_GINTSTS_SRQINT_Msk (0x1UL << USB_OTG_GINTSTS_SRQINT_Pos) /*!< 0x40000000 */
-#define USB_OTG_GINTSTS_SRQINT USB_OTG_GINTSTS_SRQINT_Msk /*!< Session request/new session detected interrupt */
-#define USB_OTG_GINTSTS_WKUINT_Pos (31U)
-#define USB_OTG_GINTSTS_WKUINT_Msk (0x1UL << USB_OTG_GINTSTS_WKUINT_Pos) /*!< 0x80000000 */
-#define USB_OTG_GINTSTS_WKUINT USB_OTG_GINTSTS_WKUINT_Msk /*!< Resume/remote wakeup detected interrupt */
-
-/******************** Bit definition for USB_OTG_GINTMSK register ********************/
-#define USB_OTG_GINTMSK_MMISM_Pos (1U)
-#define USB_OTG_GINTMSK_MMISM_Msk (0x1UL << USB_OTG_GINTMSK_MMISM_Pos) /*!< 0x00000002 */
-#define USB_OTG_GINTMSK_MMISM USB_OTG_GINTMSK_MMISM_Msk /*!< Mode mismatch interrupt mask */
-#define USB_OTG_GINTMSK_OTGINT_Pos (2U)
-#define USB_OTG_GINTMSK_OTGINT_Msk (0x1UL << USB_OTG_GINTMSK_OTGINT_Pos) /*!< 0x00000004 */
-#define USB_OTG_GINTMSK_OTGINT USB_OTG_GINTMSK_OTGINT_Msk /*!< OTG interrupt mask */
-#define USB_OTG_GINTMSK_SOFM_Pos (3U)
-#define USB_OTG_GINTMSK_SOFM_Msk (0x1UL << USB_OTG_GINTMSK_SOFM_Pos) /*!< 0x00000008 */
-#define USB_OTG_GINTMSK_SOFM USB_OTG_GINTMSK_SOFM_Msk /*!< Start of frame mask */
-#define USB_OTG_GINTMSK_RXFLVLM_Pos (4U)
-#define USB_OTG_GINTMSK_RXFLVLM_Msk (0x1UL << USB_OTG_GINTMSK_RXFLVLM_Pos) /*!< 0x00000010 */
-#define USB_OTG_GINTMSK_RXFLVLM USB_OTG_GINTMSK_RXFLVLM_Msk /*!< Receive FIFO nonempty mask */
-#define USB_OTG_GINTMSK_NPTXFEM_Pos (5U)
-#define USB_OTG_GINTMSK_NPTXFEM_Msk (0x1UL << USB_OTG_GINTMSK_NPTXFEM_Pos) /*!< 0x00000020 */
-#define USB_OTG_GINTMSK_NPTXFEM USB_OTG_GINTMSK_NPTXFEM_Msk /*!< Nonperiodic TxFIFO empty mask */
-#define USB_OTG_GINTMSK_GINAKEFFM_Pos (6U)
-#define USB_OTG_GINTMSK_GINAKEFFM_Msk (0x1UL << USB_OTG_GINTMSK_GINAKEFFM_Pos) /*!< 0x00000040 */
-#define USB_OTG_GINTMSK_GINAKEFFM USB_OTG_GINTMSK_GINAKEFFM_Msk /*!< Global nonperiodic IN NAK effective mask */
-#define USB_OTG_GINTMSK_GONAKEFFM_Pos (7U)
-#define USB_OTG_GINTMSK_GONAKEFFM_Msk (0x1UL << USB_OTG_GINTMSK_GONAKEFFM_Pos) /*!< 0x00000080 */
-#define USB_OTG_GINTMSK_GONAKEFFM USB_OTG_GINTMSK_GONAKEFFM_Msk /*!< Global OUT NAK effective mask */
-#define USB_OTG_GINTMSK_ESUSPM_Pos (10U)
-#define USB_OTG_GINTMSK_ESUSPM_Msk (0x1UL << USB_OTG_GINTMSK_ESUSPM_Pos) /*!< 0x00000400 */
-#define USB_OTG_GINTMSK_ESUSPM USB_OTG_GINTMSK_ESUSPM_Msk /*!< Early suspend mask */
-#define USB_OTG_GINTMSK_USBSUSPM_Pos (11U)
-#define USB_OTG_GINTMSK_USBSUSPM_Msk (0x1UL << USB_OTG_GINTMSK_USBSUSPM_Pos) /*!< 0x00000800 */
-#define USB_OTG_GINTMSK_USBSUSPM USB_OTG_GINTMSK_USBSUSPM_Msk /*!< USB suspend mask */
-#define USB_OTG_GINTMSK_USBRST_Pos (12U)
-#define USB_OTG_GINTMSK_USBRST_Msk (0x1UL << USB_OTG_GINTMSK_USBRST_Pos) /*!< 0x00001000 */
-#define USB_OTG_GINTMSK_USBRST USB_OTG_GINTMSK_USBRST_Msk /*!< USB reset mask */
-#define USB_OTG_GINTMSK_ENUMDNEM_Pos (13U)
-#define USB_OTG_GINTMSK_ENUMDNEM_Msk (0x1UL << USB_OTG_GINTMSK_ENUMDNEM_Pos) /*!< 0x00002000 */
-#define USB_OTG_GINTMSK_ENUMDNEM USB_OTG_GINTMSK_ENUMDNEM_Msk /*!< Enumeration done mask */
-#define USB_OTG_GINTMSK_ISOODRPM_Pos (14U)
-#define USB_OTG_GINTMSK_ISOODRPM_Msk (0x1UL << USB_OTG_GINTMSK_ISOODRPM_Pos) /*!< 0x00004000 */
-#define USB_OTG_GINTMSK_ISOODRPM USB_OTG_GINTMSK_ISOODRPM_Msk /*!< Isochronous OUT packet dropped interrupt mask */
-#define USB_OTG_GINTMSK_EOPFM_Pos (15U)
-#define USB_OTG_GINTMSK_EOPFM_Msk (0x1UL << USB_OTG_GINTMSK_EOPFM_Pos) /*!< 0x00008000 */
-#define USB_OTG_GINTMSK_EOPFM USB_OTG_GINTMSK_EOPFM_Msk /*!< End of periodic frame interrupt mask */
-#define USB_OTG_GINTMSK_EPMISM_Pos (17U)
-#define USB_OTG_GINTMSK_EPMISM_Msk (0x1UL << USB_OTG_GINTMSK_EPMISM_Pos) /*!< 0x00020000 */
-#define USB_OTG_GINTMSK_EPMISM USB_OTG_GINTMSK_EPMISM_Msk /*!< Endpoint mismatch interrupt mask */
-#define USB_OTG_GINTMSK_IEPINT_Pos (18U)
-#define USB_OTG_GINTMSK_IEPINT_Msk (0x1UL << USB_OTG_GINTMSK_IEPINT_Pos) /*!< 0x00040000 */
-#define USB_OTG_GINTMSK_IEPINT USB_OTG_GINTMSK_IEPINT_Msk /*!< IN endpoints interrupt mask */
-#define USB_OTG_GINTMSK_OEPINT_Pos (19U)
-#define USB_OTG_GINTMSK_OEPINT_Msk (0x1UL << USB_OTG_GINTMSK_OEPINT_Pos) /*!< 0x00080000 */
-#define USB_OTG_GINTMSK_OEPINT USB_OTG_GINTMSK_OEPINT_Msk /*!< OUT endpoints interrupt mask */
-#define USB_OTG_GINTMSK_IISOIXFRM_Pos (20U)
-#define USB_OTG_GINTMSK_IISOIXFRM_Msk (0x1UL << USB_OTG_GINTMSK_IISOIXFRM_Pos) /*!< 0x00100000 */
-#define USB_OTG_GINTMSK_IISOIXFRM USB_OTG_GINTMSK_IISOIXFRM_Msk /*!< Incomplete isochronous IN transfer mask */
-#define USB_OTG_GINTMSK_PXFRM_IISOOXFRM_Pos (21U)
-#define USB_OTG_GINTMSK_PXFRM_IISOOXFRM_Msk (0x1UL << USB_OTG_GINTMSK_PXFRM_IISOOXFRM_Pos) /*!< 0x00200000 */
-#define USB_OTG_GINTMSK_PXFRM_IISOOXFRM USB_OTG_GINTMSK_PXFRM_IISOOXFRM_Msk /*!< Incomplete periodic transfer mask */
-#define USB_OTG_GINTMSK_FSUSPM_Pos (22U)
-#define USB_OTG_GINTMSK_FSUSPM_Msk (0x1UL << USB_OTG_GINTMSK_FSUSPM_Pos) /*!< 0x00400000 */
-#define USB_OTG_GINTMSK_FSUSPM USB_OTG_GINTMSK_FSUSPM_Msk /*!< Data fetch suspended mask */
-#define USB_OTG_GINTMSK_PRTIM_Pos (24U)
-#define USB_OTG_GINTMSK_PRTIM_Msk (0x1UL << USB_OTG_GINTMSK_PRTIM_Pos) /*!< 0x01000000 */
-#define USB_OTG_GINTMSK_PRTIM USB_OTG_GINTMSK_PRTIM_Msk /*!< Host port interrupt mask */
-#define USB_OTG_GINTMSK_HCIM_Pos (25U)
-#define USB_OTG_GINTMSK_HCIM_Msk (0x1UL << USB_OTG_GINTMSK_HCIM_Pos) /*!< 0x02000000 */
-#define USB_OTG_GINTMSK_HCIM USB_OTG_GINTMSK_HCIM_Msk /*!< Host channels interrupt mask */
-#define USB_OTG_GINTMSK_PTXFEM_Pos (26U)
-#define USB_OTG_GINTMSK_PTXFEM_Msk (0x1UL << USB_OTG_GINTMSK_PTXFEM_Pos) /*!< 0x04000000 */
-#define USB_OTG_GINTMSK_PTXFEM USB_OTG_GINTMSK_PTXFEM_Msk /*!< Periodic TxFIFO empty mask */
-#define USB_OTG_GINTMSK_CIDSCHGM_Pos (28U)
-#define USB_OTG_GINTMSK_CIDSCHGM_Msk (0x1UL << USB_OTG_GINTMSK_CIDSCHGM_Pos) /*!< 0x10000000 */
-#define USB_OTG_GINTMSK_CIDSCHGM USB_OTG_GINTMSK_CIDSCHGM_Msk /*!< Connector ID status change mask */
-#define USB_OTG_GINTMSK_DISCINT_Pos (29U)
-#define USB_OTG_GINTMSK_DISCINT_Msk (0x1UL << USB_OTG_GINTMSK_DISCINT_Pos) /*!< 0x20000000 */
-#define USB_OTG_GINTMSK_DISCINT USB_OTG_GINTMSK_DISCINT_Msk /*!< Disconnect detected interrupt mask */
-#define USB_OTG_GINTMSK_SRQIM_Pos (30U)
-#define USB_OTG_GINTMSK_SRQIM_Msk (0x1UL << USB_OTG_GINTMSK_SRQIM_Pos) /*!< 0x40000000 */
-#define USB_OTG_GINTMSK_SRQIM USB_OTG_GINTMSK_SRQIM_Msk /*!< Session request/new session detected interrupt mask */
-#define USB_OTG_GINTMSK_WUIM_Pos (31U)
-#define USB_OTG_GINTMSK_WUIM_Msk (0x1UL << USB_OTG_GINTMSK_WUIM_Pos) /*!< 0x80000000 */
-#define USB_OTG_GINTMSK_WUIM USB_OTG_GINTMSK_WUIM_Msk /*!< Resume/remote wakeup detected interrupt mask */
-
-/******************** Bit definition for USB_OTG_DAINT register ********************/
-#define USB_OTG_DAINT_IEPINT_Pos (0U)
-#define USB_OTG_DAINT_IEPINT_Msk (0xFFFFUL << USB_OTG_DAINT_IEPINT_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DAINT_IEPINT USB_OTG_DAINT_IEPINT_Msk /*!< IN endpoint interrupt bits */
-#define USB_OTG_DAINT_OEPINT_Pos (16U)
-#define USB_OTG_DAINT_OEPINT_Msk (0xFFFFUL << USB_OTG_DAINT_OEPINT_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_DAINT_OEPINT USB_OTG_DAINT_OEPINT_Msk /*!< OUT endpoint interrupt bits */
-
-/******************** Bit definition for USB_OTG_HAINTMSK register ********************/
-#define USB_OTG_HAINTMSK_HAINTM_Pos (0U)
-#define USB_OTG_HAINTMSK_HAINTM_Msk (0xFFFFUL << USB_OTG_HAINTMSK_HAINTM_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HAINTMSK_HAINTM USB_OTG_HAINTMSK_HAINTM_Msk /*!< Channel interrupt mask */
-
-/******************** Bit definition for USB_OTG_GRXSTSP register ********************/
-#define USB_OTG_GRXSTSP_EPNUM_Pos (0U)
-#define USB_OTG_GRXSTSP_EPNUM_Msk (0xFUL << USB_OTG_GRXSTSP_EPNUM_Pos) /*!< 0x0000000F */
-#define USB_OTG_GRXSTSP_EPNUM USB_OTG_GRXSTSP_EPNUM_Msk /*!< IN EP interrupt mask bits */
-#define USB_OTG_GRXSTSP_BCNT_Pos (4U)
-#define USB_OTG_GRXSTSP_BCNT_Msk (0x7FFUL << USB_OTG_GRXSTSP_BCNT_Pos) /*!< 0x00007FF0 */
-#define USB_OTG_GRXSTSP_BCNT USB_OTG_GRXSTSP_BCNT_Msk /*!< OUT EP interrupt mask bits */
-#define USB_OTG_GRXSTSP_DPID_Pos (15U)
-#define USB_OTG_GRXSTSP_DPID_Msk (0x3UL << USB_OTG_GRXSTSP_DPID_Pos) /*!< 0x00018000 */
-#define USB_OTG_GRXSTSP_DPID USB_OTG_GRXSTSP_DPID_Msk /*!< OUT EP interrupt mask bits */
-#define USB_OTG_GRXSTSP_PKTSTS_Pos (17U)
-#define USB_OTG_GRXSTSP_PKTSTS_Msk (0xFUL << USB_OTG_GRXSTSP_PKTSTS_Pos) /*!< 0x001E0000 */
-#define USB_OTG_GRXSTSP_PKTSTS USB_OTG_GRXSTSP_PKTSTS_Msk /*!< OUT EP interrupt mask bits */
-
-/******************** Bit definition for USB_OTG_DAINTMSK register ********************/
-#define USB_OTG_DAINTMSK_IEPM_Pos (0U)
-#define USB_OTG_DAINTMSK_IEPM_Msk (0xFFFFUL << USB_OTG_DAINTMSK_IEPM_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DAINTMSK_IEPM USB_OTG_DAINTMSK_IEPM_Msk /*!< IN EP interrupt mask bits */
-#define USB_OTG_DAINTMSK_OEPM_Pos (16U)
-#define USB_OTG_DAINTMSK_OEPM_Msk (0xFFFFUL << USB_OTG_DAINTMSK_OEPM_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_DAINTMSK_OEPM USB_OTG_DAINTMSK_OEPM_Msk /*!< OUT EP interrupt mask bits */
-
-/******************** Bit definition for USB_OTG_GRXFSIZ register ********************/
-#define USB_OTG_GRXFSIZ_RXFD_Pos (0U)
-#define USB_OTG_GRXFSIZ_RXFD_Msk (0xFFFFUL << USB_OTG_GRXFSIZ_RXFD_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_GRXFSIZ_RXFD USB_OTG_GRXFSIZ_RXFD_Msk /*!< RxFIFO depth */
-
-/******************** Bit definition for USB_OTG_DVBUSDIS register ********************/
-#define USB_OTG_DVBUSDIS_VBUSDT_Pos (0U)
-#define USB_OTG_DVBUSDIS_VBUSDT_Msk (0xFFFFUL << USB_OTG_DVBUSDIS_VBUSDT_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DVBUSDIS_VBUSDT USB_OTG_DVBUSDIS_VBUSDT_Msk /*!< Device VBUS discharge time */
-
-/******************** Bit definition for OTG register ********************/
-#define USB_OTG_NPTXFSA_Pos (0U)
-#define USB_OTG_NPTXFSA_Msk (0xFFFFUL << USB_OTG_NPTXFSA_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_NPTXFSA USB_OTG_NPTXFSA_Msk /*!< Nonperiodic transmit RAM start address */
-#define USB_OTG_NPTXFD_Pos (16U)
-#define USB_OTG_NPTXFD_Msk (0xFFFFUL << USB_OTG_NPTXFD_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_NPTXFD USB_OTG_NPTXFD_Msk /*!< Nonperiodic TxFIFO depth */
-#define USB_OTG_TX0FSA_Pos (0U)
-#define USB_OTG_TX0FSA_Msk (0xFFFFUL << USB_OTG_TX0FSA_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_TX0FSA USB_OTG_TX0FSA_Msk /*!< Endpoint 0 transmit RAM start address */
-#define USB_OTG_TX0FD_Pos (16U)
-#define USB_OTG_TX0FD_Msk (0xFFFFUL << USB_OTG_TX0FD_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_TX0FD USB_OTG_TX0FD_Msk /*!< Endpoint 0 TxFIFO depth */
-
-/******************** Bit definition for USB_OTG_DVBUSPULSE register ********************/
-#define USB_OTG_DVBUSPULSE_DVBUSP_Pos (0U)
-#define USB_OTG_DVBUSPULSE_DVBUSP_Msk (0xFFFUL << USB_OTG_DVBUSPULSE_DVBUSP_Pos) /*!< 0x00000FFF */
-#define USB_OTG_DVBUSPULSE_DVBUSP USB_OTG_DVBUSPULSE_DVBUSP_Msk /*!< Device VBUS pulsing time */
-
-/******************** Bit definition for USB_OTG_GNPTXSTS register ********************/
-#define USB_OTG_GNPTXSTS_NPTXFSAV_Pos (0U)
-#define USB_OTG_GNPTXSTS_NPTXFSAV_Msk (0xFFFFUL << USB_OTG_GNPTXSTS_NPTXFSAV_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_GNPTXSTS_NPTXFSAV USB_OTG_GNPTXSTS_NPTXFSAV_Msk /*!< Nonperiodic TxFIFO space available */
-
-#define USB_OTG_GNPTXSTS_NPTQXSAV_Pos (16U)
-#define USB_OTG_GNPTXSTS_NPTQXSAV_Msk (0xFFUL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00FF0000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV USB_OTG_GNPTXSTS_NPTQXSAV_Msk /*!< Nonperiodic transmit request queue space available */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_0 (0x01UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00010000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_1 (0x02UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00020000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_2 (0x04UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00040000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_3 (0x08UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00080000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_4 (0x10UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00100000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_5 (0x20UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00200000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_6 (0x40UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00400000 */
-#define USB_OTG_GNPTXSTS_NPTQXSAV_7 (0x80UL << USB_OTG_GNPTXSTS_NPTQXSAV_Pos) /*!< 0x00800000 */
-
-#define USB_OTG_GNPTXSTS_NPTXQTOP_Pos (24U)
-#define USB_OTG_GNPTXSTS_NPTXQTOP_Msk (0x7FUL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x7F000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP USB_OTG_GNPTXSTS_NPTXQTOP_Msk /*!< Top of the nonperiodic transmit request queue */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_0 (0x01UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x01000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_1 (0x02UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x02000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_2 (0x04UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x04000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_3 (0x08UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x08000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_4 (0x10UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x10000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_5 (0x20UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x20000000 */
-#define USB_OTG_GNPTXSTS_NPTXQTOP_6 (0x40UL << USB_OTG_GNPTXSTS_NPTXQTOP_Pos) /*!< 0x40000000 */
-
-/******************** Bit definition for USB_OTG_DTHRCTL register ********************/
-#define USB_OTG_DTHRCTL_NONISOTHREN_Pos (0U)
-#define USB_OTG_DTHRCTL_NONISOTHREN_Msk (0x1UL << USB_OTG_DTHRCTL_NONISOTHREN_Pos) /*!< 0x00000001 */
-#define USB_OTG_DTHRCTL_NONISOTHREN USB_OTG_DTHRCTL_NONISOTHREN_Msk /*!< Nonisochronous IN endpoints threshold enable */
-#define USB_OTG_DTHRCTL_ISOTHREN_Pos (1U)
-#define USB_OTG_DTHRCTL_ISOTHREN_Msk (0x1UL << USB_OTG_DTHRCTL_ISOTHREN_Pos) /*!< 0x00000002 */
-#define USB_OTG_DTHRCTL_ISOTHREN USB_OTG_DTHRCTL_ISOTHREN_Msk /*!< ISO IN endpoint threshold enable */
-
-#define USB_OTG_DTHRCTL_TXTHRLEN_Pos (2U)
-#define USB_OTG_DTHRCTL_TXTHRLEN_Msk (0x1FFUL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x000007FC */
-#define USB_OTG_DTHRCTL_TXTHRLEN USB_OTG_DTHRCTL_TXTHRLEN_Msk /*!< Transmit threshold length */
-#define USB_OTG_DTHRCTL_TXTHRLEN_0 (0x001UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000004 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_1 (0x002UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000008 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_2 (0x004UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000010 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_3 (0x008UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000020 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_4 (0x010UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000040 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_5 (0x020UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000080 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_6 (0x040UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000100 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_7 (0x080UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000200 */
-#define USB_OTG_DTHRCTL_TXTHRLEN_8 (0x100UL << USB_OTG_DTHRCTL_TXTHRLEN_Pos) /*!< 0x00000400 */
-#define USB_OTG_DTHRCTL_RXTHREN_Pos (16U)
-#define USB_OTG_DTHRCTL_RXTHREN_Msk (0x1UL << USB_OTG_DTHRCTL_RXTHREN_Pos) /*!< 0x00010000 */
-#define USB_OTG_DTHRCTL_RXTHREN USB_OTG_DTHRCTL_RXTHREN_Msk /*!< Receive threshold enable */
-
-#define USB_OTG_DTHRCTL_RXTHRLEN_Pos (17U)
-#define USB_OTG_DTHRCTL_RXTHRLEN_Msk (0x1FFUL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x03FE0000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN USB_OTG_DTHRCTL_RXTHRLEN_Msk /*!< Receive threshold length */
-#define USB_OTG_DTHRCTL_RXTHRLEN_0 (0x001UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00020000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_1 (0x002UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00040000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_2 (0x004UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00080000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_3 (0x008UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00100000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_4 (0x010UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00200000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_5 (0x020UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00400000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_6 (0x040UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x00800000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_7 (0x080UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x01000000 */
-#define USB_OTG_DTHRCTL_RXTHRLEN_8 (0x100UL << USB_OTG_DTHRCTL_RXTHRLEN_Pos) /*!< 0x02000000 */
-#define USB_OTG_DTHRCTL_ARPEN_Pos (27U)
-#define USB_OTG_DTHRCTL_ARPEN_Msk (0x1UL << USB_OTG_DTHRCTL_ARPEN_Pos) /*!< 0x08000000 */
-#define USB_OTG_DTHRCTL_ARPEN USB_OTG_DTHRCTL_ARPEN_Msk /*!< Arbiter parking enable */
-
-/******************** Bit definition for USB_OTG_DIEPEMPMSK register ********************/
-#define USB_OTG_DIEPEMPMSK_INEPTXFEM_Pos (0U)
-#define USB_OTG_DIEPEMPMSK_INEPTXFEM_Msk (0xFFFFUL << USB_OTG_DIEPEMPMSK_INEPTXFEM_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DIEPEMPMSK_INEPTXFEM USB_OTG_DIEPEMPMSK_INEPTXFEM_Msk /*!< IN EP Tx FIFO empty interrupt mask bits */
-
-/******************** Bit definition for USB_OTG_DEACHINT register ********************/
-#define USB_OTG_DEACHINT_IEP1INT_Pos (1U)
-#define USB_OTG_DEACHINT_IEP1INT_Msk (0x1UL << USB_OTG_DEACHINT_IEP1INT_Pos) /*!< 0x00000002 */
-#define USB_OTG_DEACHINT_IEP1INT USB_OTG_DEACHINT_IEP1INT_Msk /*!< IN endpoint 1interrupt bit */
-#define USB_OTG_DEACHINT_OEP1INT_Pos (17U)
-#define USB_OTG_DEACHINT_OEP1INT_Msk (0x1UL << USB_OTG_DEACHINT_OEP1INT_Pos) /*!< 0x00020000 */
-#define USB_OTG_DEACHINT_OEP1INT USB_OTG_DEACHINT_OEP1INT_Msk /*!< OUT endpoint 1 interrupt bit */
-
-/******************** Bit definition for USB_OTG_GCCFG register ********************/
-#define USB_OTG_GCCFG_PWRDWN_Pos (16U)
-#define USB_OTG_GCCFG_PWRDWN_Msk (0x1UL << USB_OTG_GCCFG_PWRDWN_Pos) /*!< 0x00010000 */
-#define USB_OTG_GCCFG_PWRDWN USB_OTG_GCCFG_PWRDWN_Msk /*!< Power down */
-#define USB_OTG_GCCFG_VBUSASEN_Pos (18U)
-#define USB_OTG_GCCFG_VBUSASEN_Msk (0x1UL << USB_OTG_GCCFG_VBUSASEN_Pos) /*!< 0x00040000 */
-#define USB_OTG_GCCFG_VBUSASEN USB_OTG_GCCFG_VBUSASEN_Msk /*!< Enable the VBUS sensing device */
-#define USB_OTG_GCCFG_VBUSBSEN_Pos (19U)
-#define USB_OTG_GCCFG_VBUSBSEN_Msk (0x1UL << USB_OTG_GCCFG_VBUSBSEN_Pos) /*!< 0x00080000 */
-#define USB_OTG_GCCFG_VBUSBSEN USB_OTG_GCCFG_VBUSBSEN_Msk /*!< Enable the VBUS sensing device */
-#define USB_OTG_GCCFG_SOFOUTEN_Pos (20U)
-#define USB_OTG_GCCFG_SOFOUTEN_Msk (0x1UL << USB_OTG_GCCFG_SOFOUTEN_Pos) /*!< 0x00100000 */
-#define USB_OTG_GCCFG_SOFOUTEN USB_OTG_GCCFG_SOFOUTEN_Msk /*!< SOF output enable */
-
-/******************** Bit definition for USB_OTG_DEACHINTMSK register ********************/
-#define USB_OTG_DEACHINTMSK_IEP1INTM_Pos (1U)
-#define USB_OTG_DEACHINTMSK_IEP1INTM_Msk (0x1UL << USB_OTG_DEACHINTMSK_IEP1INTM_Pos) /*!< 0x00000002 */
-#define USB_OTG_DEACHINTMSK_IEP1INTM USB_OTG_DEACHINTMSK_IEP1INTM_Msk /*!< IN Endpoint 1 interrupt mask bit */
-#define USB_OTG_DEACHINTMSK_OEP1INTM_Pos (17U)
-#define USB_OTG_DEACHINTMSK_OEP1INTM_Msk (0x1UL << USB_OTG_DEACHINTMSK_OEP1INTM_Pos) /*!< 0x00020000 */
-#define USB_OTG_DEACHINTMSK_OEP1INTM USB_OTG_DEACHINTMSK_OEP1INTM_Msk /*!< OUT Endpoint 1 interrupt mask bit */
-
-/******************** Bit definition for USB_OTG_CID register ********************/
-#define USB_OTG_CID_PRODUCT_ID_Pos (0U)
-#define USB_OTG_CID_PRODUCT_ID_Msk (0xFFFFFFFFUL << USB_OTG_CID_PRODUCT_ID_Pos) /*!< 0xFFFFFFFF */
-#define USB_OTG_CID_PRODUCT_ID USB_OTG_CID_PRODUCT_ID_Msk /*!< Product ID field */
-
-/******************** Bit definition for USB_OTG_DIEPEACHMSK1 register ********************/
-#define USB_OTG_DIEPEACHMSK1_XFRCM_Pos (0U)
-#define USB_OTG_DIEPEACHMSK1_XFRCM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_XFRCM_Pos) /*!< 0x00000001 */
-#define USB_OTG_DIEPEACHMSK1_XFRCM USB_OTG_DIEPEACHMSK1_XFRCM_Msk /*!< Transfer completed interrupt mask */
-#define USB_OTG_DIEPEACHMSK1_EPDM_Pos (1U)
-#define USB_OTG_DIEPEACHMSK1_EPDM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_EPDM_Pos) /*!< 0x00000002 */
-#define USB_OTG_DIEPEACHMSK1_EPDM USB_OTG_DIEPEACHMSK1_EPDM_Msk /*!< Endpoint disabled interrupt mask */
-#define USB_OTG_DIEPEACHMSK1_TOM_Pos (3U)
-#define USB_OTG_DIEPEACHMSK1_TOM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_TOM_Pos) /*!< 0x00000008 */
-#define USB_OTG_DIEPEACHMSK1_TOM USB_OTG_DIEPEACHMSK1_TOM_Msk /*!< Timeout condition mask (nonisochronous endpoints) */
-#define USB_OTG_DIEPEACHMSK1_ITTXFEMSK_Pos (4U)
-#define USB_OTG_DIEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_ITTXFEMSK_Pos) /*!< 0x00000010 */
-#define USB_OTG_DIEPEACHMSK1_ITTXFEMSK USB_OTG_DIEPEACHMSK1_ITTXFEMSK_Msk /*!< IN token received when TxFIFO empty mask */
-#define USB_OTG_DIEPEACHMSK1_INEPNMM_Pos (5U)
-#define USB_OTG_DIEPEACHMSK1_INEPNMM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_INEPNMM_Pos) /*!< 0x00000020 */
-#define USB_OTG_DIEPEACHMSK1_INEPNMM USB_OTG_DIEPEACHMSK1_INEPNMM_Msk /*!< IN token received with EP mismatch mask */
-#define USB_OTG_DIEPEACHMSK1_INEPNEM_Pos (6U)
-#define USB_OTG_DIEPEACHMSK1_INEPNEM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_INEPNEM_Pos) /*!< 0x00000040 */
-#define USB_OTG_DIEPEACHMSK1_INEPNEM USB_OTG_DIEPEACHMSK1_INEPNEM_Msk /*!< IN endpoint NAK effective mask */
-#define USB_OTG_DIEPEACHMSK1_TXFURM_Pos (8U)
-#define USB_OTG_DIEPEACHMSK1_TXFURM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_TXFURM_Pos) /*!< 0x00000100 */
-#define USB_OTG_DIEPEACHMSK1_TXFURM USB_OTG_DIEPEACHMSK1_TXFURM_Msk /*!< FIFO underrun mask */
-#define USB_OTG_DIEPEACHMSK1_BIM_Pos (9U)
-#define USB_OTG_DIEPEACHMSK1_BIM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_BIM_Pos) /*!< 0x00000200 */
-#define USB_OTG_DIEPEACHMSK1_BIM USB_OTG_DIEPEACHMSK1_BIM_Msk /*!< BNA interrupt mask */
-#define USB_OTG_DIEPEACHMSK1_NAKM_Pos (13U)
-#define USB_OTG_DIEPEACHMSK1_NAKM_Msk (0x1UL << USB_OTG_DIEPEACHMSK1_NAKM_Pos) /*!< 0x00002000 */
-#define USB_OTG_DIEPEACHMSK1_NAKM USB_OTG_DIEPEACHMSK1_NAKM_Msk /*!< NAK interrupt mask */
-
-/******************** Bit definition for USB_OTG_HPRT register ********************/
-#define USB_OTG_HPRT_PCSTS_Pos (0U)
-#define USB_OTG_HPRT_PCSTS_Msk (0x1UL << USB_OTG_HPRT_PCSTS_Pos) /*!< 0x00000001 */
-#define USB_OTG_HPRT_PCSTS USB_OTG_HPRT_PCSTS_Msk /*!< Port connect status */
-#define USB_OTG_HPRT_PCDET_Pos (1U)
-#define USB_OTG_HPRT_PCDET_Msk (0x1UL << USB_OTG_HPRT_PCDET_Pos) /*!< 0x00000002 */
-#define USB_OTG_HPRT_PCDET USB_OTG_HPRT_PCDET_Msk /*!< Port connect detected */
-#define USB_OTG_HPRT_PENA_Pos (2U)
-#define USB_OTG_HPRT_PENA_Msk (0x1UL << USB_OTG_HPRT_PENA_Pos) /*!< 0x00000004 */
-#define USB_OTG_HPRT_PENA USB_OTG_HPRT_PENA_Msk /*!< Port enable */
-#define USB_OTG_HPRT_PENCHNG_Pos (3U)
-#define USB_OTG_HPRT_PENCHNG_Msk (0x1UL << USB_OTG_HPRT_PENCHNG_Pos) /*!< 0x00000008 */
-#define USB_OTG_HPRT_PENCHNG USB_OTG_HPRT_PENCHNG_Msk /*!< Port enable/disable change */
-#define USB_OTG_HPRT_POCA_Pos (4U)
-#define USB_OTG_HPRT_POCA_Msk (0x1UL << USB_OTG_HPRT_POCA_Pos) /*!< 0x00000010 */
-#define USB_OTG_HPRT_POCA USB_OTG_HPRT_POCA_Msk /*!< Port overcurrent active */
-#define USB_OTG_HPRT_POCCHNG_Pos (5U)
-#define USB_OTG_HPRT_POCCHNG_Msk (0x1UL << USB_OTG_HPRT_POCCHNG_Pos) /*!< 0x00000020 */
-#define USB_OTG_HPRT_POCCHNG USB_OTG_HPRT_POCCHNG_Msk /*!< Port overcurrent change */
-#define USB_OTG_HPRT_PRES_Pos (6U)
-#define USB_OTG_HPRT_PRES_Msk (0x1UL << USB_OTG_HPRT_PRES_Pos) /*!< 0x00000040 */
-#define USB_OTG_HPRT_PRES USB_OTG_HPRT_PRES_Msk /*!< Port resume */
-#define USB_OTG_HPRT_PSUSP_Pos (7U)
-#define USB_OTG_HPRT_PSUSP_Msk (0x1UL << USB_OTG_HPRT_PSUSP_Pos) /*!< 0x00000080 */
-#define USB_OTG_HPRT_PSUSP USB_OTG_HPRT_PSUSP_Msk /*!< Port suspend */
-#define USB_OTG_HPRT_PRST_Pos (8U)
-#define USB_OTG_HPRT_PRST_Msk (0x1UL << USB_OTG_HPRT_PRST_Pos) /*!< 0x00000100 */
-#define USB_OTG_HPRT_PRST USB_OTG_HPRT_PRST_Msk /*!< Port reset */
-
-#define USB_OTG_HPRT_PLSTS_Pos (10U)
-#define USB_OTG_HPRT_PLSTS_Msk (0x3UL << USB_OTG_HPRT_PLSTS_Pos) /*!< 0x00000C00 */
-#define USB_OTG_HPRT_PLSTS USB_OTG_HPRT_PLSTS_Msk /*!< Port line status */
-#define USB_OTG_HPRT_PLSTS_0 (0x1UL << USB_OTG_HPRT_PLSTS_Pos) /*!< 0x00000400 */
-#define USB_OTG_HPRT_PLSTS_1 (0x2UL << USB_OTG_HPRT_PLSTS_Pos) /*!< 0x00000800 */
-#define USB_OTG_HPRT_PPWR_Pos (12U)
-#define USB_OTG_HPRT_PPWR_Msk (0x1UL << USB_OTG_HPRT_PPWR_Pos) /*!< 0x00001000 */
-#define USB_OTG_HPRT_PPWR USB_OTG_HPRT_PPWR_Msk /*!< Port power */
-
-#define USB_OTG_HPRT_PTCTL_Pos (13U)
-#define USB_OTG_HPRT_PTCTL_Msk (0xFUL << USB_OTG_HPRT_PTCTL_Pos) /*!< 0x0001E000 */
-#define USB_OTG_HPRT_PTCTL USB_OTG_HPRT_PTCTL_Msk /*!< Port test control */
-#define USB_OTG_HPRT_PTCTL_0 (0x1UL << USB_OTG_HPRT_PTCTL_Pos) /*!< 0x00002000 */
-#define USB_OTG_HPRT_PTCTL_1 (0x2UL << USB_OTG_HPRT_PTCTL_Pos) /*!< 0x00004000 */
-#define USB_OTG_HPRT_PTCTL_2 (0x4UL << USB_OTG_HPRT_PTCTL_Pos) /*!< 0x00008000 */
-#define USB_OTG_HPRT_PTCTL_3 (0x8UL << USB_OTG_HPRT_PTCTL_Pos) /*!< 0x00010000 */
-
-#define USB_OTG_HPRT_PSPD_Pos (17U)
-#define USB_OTG_HPRT_PSPD_Msk (0x3UL << USB_OTG_HPRT_PSPD_Pos) /*!< 0x00060000 */
-#define USB_OTG_HPRT_PSPD USB_OTG_HPRT_PSPD_Msk /*!< Port speed */
-#define USB_OTG_HPRT_PSPD_0 (0x1UL << USB_OTG_HPRT_PSPD_Pos) /*!< 0x00020000 */
-#define USB_OTG_HPRT_PSPD_1 (0x2UL << USB_OTG_HPRT_PSPD_Pos) /*!< 0x00040000 */
-
-/******************** Bit definition for USB_OTG_DOEPEACHMSK1 register ********************/
-#define USB_OTG_DOEPEACHMSK1_XFRCM_Pos (0U)
-#define USB_OTG_DOEPEACHMSK1_XFRCM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_XFRCM_Pos) /*!< 0x00000001 */
-#define USB_OTG_DOEPEACHMSK1_XFRCM USB_OTG_DOEPEACHMSK1_XFRCM_Msk /*!< Transfer completed interrupt mask */
-#define USB_OTG_DOEPEACHMSK1_EPDM_Pos (1U)
-#define USB_OTG_DOEPEACHMSK1_EPDM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_EPDM_Pos) /*!< 0x00000002 */
-#define USB_OTG_DOEPEACHMSK1_EPDM USB_OTG_DOEPEACHMSK1_EPDM_Msk /*!< Endpoint disabled interrupt mask */
-#define USB_OTG_DOEPEACHMSK1_TOM_Pos (3U)
-#define USB_OTG_DOEPEACHMSK1_TOM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_TOM_Pos) /*!< 0x00000008 */
-#define USB_OTG_DOEPEACHMSK1_TOM USB_OTG_DOEPEACHMSK1_TOM_Msk /*!< Timeout condition mask */
-#define USB_OTG_DOEPEACHMSK1_ITTXFEMSK_Pos (4U)
-#define USB_OTG_DOEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_ITTXFEMSK_Pos) /*!< 0x00000010 */
-#define USB_OTG_DOEPEACHMSK1_ITTXFEMSK USB_OTG_DOEPEACHMSK1_ITTXFEMSK_Msk /*!< IN token received when TxFIFO empty mask */
-#define USB_OTG_DOEPEACHMSK1_INEPNMM_Pos (5U)
-#define USB_OTG_DOEPEACHMSK1_INEPNMM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_INEPNMM_Pos) /*!< 0x00000020 */
-#define USB_OTG_DOEPEACHMSK1_INEPNMM USB_OTG_DOEPEACHMSK1_INEPNMM_Msk /*!< IN token received with EP mismatch mask */
-#define USB_OTG_DOEPEACHMSK1_INEPNEM_Pos (6U)
-#define USB_OTG_DOEPEACHMSK1_INEPNEM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_INEPNEM_Pos) /*!< 0x00000040 */
-#define USB_OTG_DOEPEACHMSK1_INEPNEM USB_OTG_DOEPEACHMSK1_INEPNEM_Msk /*!< IN endpoint NAK effective mask */
-#define USB_OTG_DOEPEACHMSK1_TXFURM_Pos (8U)
-#define USB_OTG_DOEPEACHMSK1_TXFURM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_TXFURM_Pos) /*!< 0x00000100 */
-#define USB_OTG_DOEPEACHMSK1_TXFURM USB_OTG_DOEPEACHMSK1_TXFURM_Msk /*!< OUT packet error mask */
-#define USB_OTG_DOEPEACHMSK1_BIM_Pos (9U)
-#define USB_OTG_DOEPEACHMSK1_BIM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_BIM_Pos) /*!< 0x00000200 */
-#define USB_OTG_DOEPEACHMSK1_BIM USB_OTG_DOEPEACHMSK1_BIM_Msk /*!< BNA interrupt mask */
-#define USB_OTG_DOEPEACHMSK1_BERRM_Pos (12U)
-#define USB_OTG_DOEPEACHMSK1_BERRM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_BERRM_Pos) /*!< 0x00001000 */
-#define USB_OTG_DOEPEACHMSK1_BERRM USB_OTG_DOEPEACHMSK1_BERRM_Msk /*!< Bubble error interrupt mask */
-#define USB_OTG_DOEPEACHMSK1_NAKM_Pos (13U)
-#define USB_OTG_DOEPEACHMSK1_NAKM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_NAKM_Pos) /*!< 0x00002000 */
-#define USB_OTG_DOEPEACHMSK1_NAKM USB_OTG_DOEPEACHMSK1_NAKM_Msk /*!< NAK interrupt mask */
-#define USB_OTG_DOEPEACHMSK1_NYETM_Pos (14U)
-#define USB_OTG_DOEPEACHMSK1_NYETM_Msk (0x1UL << USB_OTG_DOEPEACHMSK1_NYETM_Pos) /*!< 0x00004000 */
-#define USB_OTG_DOEPEACHMSK1_NYETM USB_OTG_DOEPEACHMSK1_NYETM_Msk /*!< NYET interrupt mask */
-
-/******************** Bit definition for USB_OTG_HPTXFSIZ register ********************/
-#define USB_OTG_HPTXFSIZ_PTXSA_Pos (0U)
-#define USB_OTG_HPTXFSIZ_PTXSA_Msk (0xFFFFUL << USB_OTG_HPTXFSIZ_PTXSA_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_HPTXFSIZ_PTXSA USB_OTG_HPTXFSIZ_PTXSA_Msk /*!< Host periodic TxFIFO start address */
-#define USB_OTG_HPTXFSIZ_PTXFD_Pos (16U)
-#define USB_OTG_HPTXFSIZ_PTXFD_Msk (0xFFFFUL << USB_OTG_HPTXFSIZ_PTXFD_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_HPTXFSIZ_PTXFD USB_OTG_HPTXFSIZ_PTXFD_Msk /*!< Host periodic TxFIFO depth */
-
-/******************** Bit definition for USB_OTG_DIEPCTL register ********************/
-#define USB_OTG_DIEPCTL_MPSIZ_Pos (0U)
-#define USB_OTG_DIEPCTL_MPSIZ_Msk (0x7FFUL << USB_OTG_DIEPCTL_MPSIZ_Pos) /*!< 0x000007FF */
-#define USB_OTG_DIEPCTL_MPSIZ USB_OTG_DIEPCTL_MPSIZ_Msk /*!< Maximum packet size */
-#define USB_OTG_DIEPCTL_USBAEP_Pos (15U)
-#define USB_OTG_DIEPCTL_USBAEP_Msk (0x1UL << USB_OTG_DIEPCTL_USBAEP_Pos) /*!< 0x00008000 */
-#define USB_OTG_DIEPCTL_USBAEP USB_OTG_DIEPCTL_USBAEP_Msk /*!< USB active endpoint */
-#define USB_OTG_DIEPCTL_EONUM_DPID_Pos (16U)
-#define USB_OTG_DIEPCTL_EONUM_DPID_Msk (0x1UL << USB_OTG_DIEPCTL_EONUM_DPID_Pos) /*!< 0x00010000 */
-#define USB_OTG_DIEPCTL_EONUM_DPID USB_OTG_DIEPCTL_EONUM_DPID_Msk /*!< Even/odd frame */
-#define USB_OTG_DIEPCTL_NAKSTS_Pos (17U)
-#define USB_OTG_DIEPCTL_NAKSTS_Msk (0x1UL << USB_OTG_DIEPCTL_NAKSTS_Pos) /*!< 0x00020000 */
-#define USB_OTG_DIEPCTL_NAKSTS USB_OTG_DIEPCTL_NAKSTS_Msk /*!< NAK status */
-
-#define USB_OTG_DIEPCTL_EPTYP_Pos (18U)
-#define USB_OTG_DIEPCTL_EPTYP_Msk (0x3UL << USB_OTG_DIEPCTL_EPTYP_Pos) /*!< 0x000C0000 */
-#define USB_OTG_DIEPCTL_EPTYP USB_OTG_DIEPCTL_EPTYP_Msk /*!< Endpoint type */
-#define USB_OTG_DIEPCTL_EPTYP_0 (0x1UL << USB_OTG_DIEPCTL_EPTYP_Pos) /*!< 0x00040000 */
-#define USB_OTG_DIEPCTL_EPTYP_1 (0x2UL << USB_OTG_DIEPCTL_EPTYP_Pos) /*!< 0x00080000 */
-#define USB_OTG_DIEPCTL_STALL_Pos (21U)
-#define USB_OTG_DIEPCTL_STALL_Msk (0x1UL << USB_OTG_DIEPCTL_STALL_Pos) /*!< 0x00200000 */
-#define USB_OTG_DIEPCTL_STALL USB_OTG_DIEPCTL_STALL_Msk /*!< STALL handshake */
-
-#define USB_OTG_DIEPCTL_TXFNUM_Pos (22U)
-#define USB_OTG_DIEPCTL_TXFNUM_Msk (0xFUL << USB_OTG_DIEPCTL_TXFNUM_Pos) /*!< 0x03C00000 */
-#define USB_OTG_DIEPCTL_TXFNUM USB_OTG_DIEPCTL_TXFNUM_Msk /*!< TxFIFO number */
-#define USB_OTG_DIEPCTL_TXFNUM_0 (0x1UL << USB_OTG_DIEPCTL_TXFNUM_Pos) /*!< 0x00400000 */
-#define USB_OTG_DIEPCTL_TXFNUM_1 (0x2UL << USB_OTG_DIEPCTL_TXFNUM_Pos) /*!< 0x00800000 */
-#define USB_OTG_DIEPCTL_TXFNUM_2 (0x4UL << USB_OTG_DIEPCTL_TXFNUM_Pos) /*!< 0x01000000 */
-#define USB_OTG_DIEPCTL_TXFNUM_3 (0x8UL << USB_OTG_DIEPCTL_TXFNUM_Pos) /*!< 0x02000000 */
-#define USB_OTG_DIEPCTL_CNAK_Pos (26U)
-#define USB_OTG_DIEPCTL_CNAK_Msk (0x1UL << USB_OTG_DIEPCTL_CNAK_Pos) /*!< 0x04000000 */
-#define USB_OTG_DIEPCTL_CNAK USB_OTG_DIEPCTL_CNAK_Msk /*!< Clear NAK */
-#define USB_OTG_DIEPCTL_SNAK_Pos (27U)
-#define USB_OTG_DIEPCTL_SNAK_Msk (0x1UL << USB_OTG_DIEPCTL_SNAK_Pos) /*!< 0x08000000 */
-#define USB_OTG_DIEPCTL_SNAK USB_OTG_DIEPCTL_SNAK_Msk /*!< Set NAK */
-#define USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Pos (28U)
-#define USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Pos) /*!< 0x10000000 */
-#define USB_OTG_DIEPCTL_SD0PID_SEVNFRM USB_OTG_DIEPCTL_SD0PID_SEVNFRM_Msk /*!< Set DATA0 PID */
-#define USB_OTG_DIEPCTL_SODDFRM_Pos (29U)
-#define USB_OTG_DIEPCTL_SODDFRM_Msk (0x1UL << USB_OTG_DIEPCTL_SODDFRM_Pos) /*!< 0x20000000 */
-#define USB_OTG_DIEPCTL_SODDFRM USB_OTG_DIEPCTL_SODDFRM_Msk /*!< Set odd frame */
-#define USB_OTG_DIEPCTL_EPDIS_Pos (30U)
-#define USB_OTG_DIEPCTL_EPDIS_Msk (0x1UL << USB_OTG_DIEPCTL_EPDIS_Pos) /*!< 0x40000000 */
-#define USB_OTG_DIEPCTL_EPDIS USB_OTG_DIEPCTL_EPDIS_Msk /*!< Endpoint disable */
-#define USB_OTG_DIEPCTL_EPENA_Pos (31U)
-#define USB_OTG_DIEPCTL_EPENA_Msk (0x1UL << USB_OTG_DIEPCTL_EPENA_Pos) /*!< 0x80000000 */
-#define USB_OTG_DIEPCTL_EPENA USB_OTG_DIEPCTL_EPENA_Msk /*!< Endpoint enable */
-
-/******************** Bit definition for USB_OTG_HCCHAR register ********************/
-#define USB_OTG_HCCHAR_MPSIZ_Pos (0U)
-#define USB_OTG_HCCHAR_MPSIZ_Msk (0x7FFUL << USB_OTG_HCCHAR_MPSIZ_Pos) /*!< 0x000007FF */
-#define USB_OTG_HCCHAR_MPSIZ USB_OTG_HCCHAR_MPSIZ_Msk /*!< Maximum packet size */
-
-#define USB_OTG_HCCHAR_EPNUM_Pos (11U)
-#define USB_OTG_HCCHAR_EPNUM_Msk (0xFUL << USB_OTG_HCCHAR_EPNUM_Pos) /*!< 0x00007800 */
-#define USB_OTG_HCCHAR_EPNUM USB_OTG_HCCHAR_EPNUM_Msk /*!< Endpoint number */
-#define USB_OTG_HCCHAR_EPNUM_0 (0x1UL << USB_OTG_HCCHAR_EPNUM_Pos) /*!< 0x00000800 */
-#define USB_OTG_HCCHAR_EPNUM_1 (0x2UL << USB_OTG_HCCHAR_EPNUM_Pos) /*!< 0x00001000 */
-#define USB_OTG_HCCHAR_EPNUM_2 (0x4UL << USB_OTG_HCCHAR_EPNUM_Pos) /*!< 0x00002000 */
-#define USB_OTG_HCCHAR_EPNUM_3 (0x8UL << USB_OTG_HCCHAR_EPNUM_Pos) /*!< 0x00004000 */
-#define USB_OTG_HCCHAR_EPDIR_Pos (15U)
-#define USB_OTG_HCCHAR_EPDIR_Msk (0x1UL << USB_OTG_HCCHAR_EPDIR_Pos) /*!< 0x00008000 */
-#define USB_OTG_HCCHAR_EPDIR USB_OTG_HCCHAR_EPDIR_Msk /*!< Endpoint direction */
-#define USB_OTG_HCCHAR_LSDEV_Pos (17U)
-#define USB_OTG_HCCHAR_LSDEV_Msk (0x1UL << USB_OTG_HCCHAR_LSDEV_Pos) /*!< 0x00020000 */
-#define USB_OTG_HCCHAR_LSDEV USB_OTG_HCCHAR_LSDEV_Msk /*!< Low-speed device */
-
-#define USB_OTG_HCCHAR_EPTYP_Pos (18U)
-#define USB_OTG_HCCHAR_EPTYP_Msk (0x3UL << USB_OTG_HCCHAR_EPTYP_Pos) /*!< 0x000C0000 */
-#define USB_OTG_HCCHAR_EPTYP USB_OTG_HCCHAR_EPTYP_Msk /*!< Endpoint type */
-#define USB_OTG_HCCHAR_EPTYP_0 (0x1UL << USB_OTG_HCCHAR_EPTYP_Pos) /*!< 0x00040000 */
-#define USB_OTG_HCCHAR_EPTYP_1 (0x2UL << USB_OTG_HCCHAR_EPTYP_Pos) /*!< 0x00080000 */
-
-#define USB_OTG_HCCHAR_MC_Pos (20U)
-#define USB_OTG_HCCHAR_MC_Msk (0x3UL << USB_OTG_HCCHAR_MC_Pos) /*!< 0x00300000 */
-#define USB_OTG_HCCHAR_MC USB_OTG_HCCHAR_MC_Msk /*!< Multi Count (MC) / Error Count (EC) */
-#define USB_OTG_HCCHAR_MC_0 (0x1UL << USB_OTG_HCCHAR_MC_Pos) /*!< 0x00100000 */
-#define USB_OTG_HCCHAR_MC_1 (0x2UL << USB_OTG_HCCHAR_MC_Pos) /*!< 0x00200000 */
-
-#define USB_OTG_HCCHAR_DAD_Pos (22U)
-#define USB_OTG_HCCHAR_DAD_Msk (0x7FUL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x1FC00000 */
-#define USB_OTG_HCCHAR_DAD USB_OTG_HCCHAR_DAD_Msk /*!< Device address */
-#define USB_OTG_HCCHAR_DAD_0 (0x01UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x00400000 */
-#define USB_OTG_HCCHAR_DAD_1 (0x02UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x00800000 */
-#define USB_OTG_HCCHAR_DAD_2 (0x04UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x01000000 */
-#define USB_OTG_HCCHAR_DAD_3 (0x08UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x02000000 */
-#define USB_OTG_HCCHAR_DAD_4 (0x10UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x04000000 */
-#define USB_OTG_HCCHAR_DAD_5 (0x20UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x08000000 */
-#define USB_OTG_HCCHAR_DAD_6 (0x40UL << USB_OTG_HCCHAR_DAD_Pos) /*!< 0x10000000 */
-#define USB_OTG_HCCHAR_ODDFRM_Pos (29U)
-#define USB_OTG_HCCHAR_ODDFRM_Msk (0x1UL << USB_OTG_HCCHAR_ODDFRM_Pos) /*!< 0x20000000 */
-#define USB_OTG_HCCHAR_ODDFRM USB_OTG_HCCHAR_ODDFRM_Msk /*!< Odd frame */
-#define USB_OTG_HCCHAR_CHDIS_Pos (30U)
-#define USB_OTG_HCCHAR_CHDIS_Msk (0x1UL << USB_OTG_HCCHAR_CHDIS_Pos) /*!< 0x40000000 */
-#define USB_OTG_HCCHAR_CHDIS USB_OTG_HCCHAR_CHDIS_Msk /*!< Channel disable */
-#define USB_OTG_HCCHAR_CHENA_Pos (31U)
-#define USB_OTG_HCCHAR_CHENA_Msk (0x1UL << USB_OTG_HCCHAR_CHENA_Pos) /*!< 0x80000000 */
-#define USB_OTG_HCCHAR_CHENA USB_OTG_HCCHAR_CHENA_Msk /*!< Channel enable */
-
-/******************** Bit definition for USB_OTG_HCSPLT register ********************/
-
-#define USB_OTG_HCSPLT_PRTADDR_Pos (0U)
-#define USB_OTG_HCSPLT_PRTADDR_Msk (0x7FUL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x0000007F */
-#define USB_OTG_HCSPLT_PRTADDR USB_OTG_HCSPLT_PRTADDR_Msk /*!< Port address */
-#define USB_OTG_HCSPLT_PRTADDR_0 (0x01UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000001 */
-#define USB_OTG_HCSPLT_PRTADDR_1 (0x02UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000002 */
-#define USB_OTG_HCSPLT_PRTADDR_2 (0x04UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000004 */
-#define USB_OTG_HCSPLT_PRTADDR_3 (0x08UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000008 */
-#define USB_OTG_HCSPLT_PRTADDR_4 (0x10UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000010 */
-#define USB_OTG_HCSPLT_PRTADDR_5 (0x20UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000020 */
-#define USB_OTG_HCSPLT_PRTADDR_6 (0x40UL << USB_OTG_HCSPLT_PRTADDR_Pos) /*!< 0x00000040 */
-
-#define USB_OTG_HCSPLT_HUBADDR_Pos (7U)
-#define USB_OTG_HCSPLT_HUBADDR_Msk (0x7FUL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00003F80 */
-#define USB_OTG_HCSPLT_HUBADDR USB_OTG_HCSPLT_HUBADDR_Msk /*!< Hub address */
-#define USB_OTG_HCSPLT_HUBADDR_0 (0x01UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00000080 */
-#define USB_OTG_HCSPLT_HUBADDR_1 (0x02UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00000100 */
-#define USB_OTG_HCSPLT_HUBADDR_2 (0x04UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00000200 */
-#define USB_OTG_HCSPLT_HUBADDR_3 (0x08UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00000400 */
-#define USB_OTG_HCSPLT_HUBADDR_4 (0x10UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00000800 */
-#define USB_OTG_HCSPLT_HUBADDR_5 (0x20UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00001000 */
-#define USB_OTG_HCSPLT_HUBADDR_6 (0x40UL << USB_OTG_HCSPLT_HUBADDR_Pos) /*!< 0x00002000 */
-
-#define USB_OTG_HCSPLT_XACTPOS_Pos (14U)
-#define USB_OTG_HCSPLT_XACTPOS_Msk (0x3UL << USB_OTG_HCSPLT_XACTPOS_Pos) /*!< 0x0000C000 */
-#define USB_OTG_HCSPLT_XACTPOS USB_OTG_HCSPLT_XACTPOS_Msk /*!< XACTPOS */
-#define USB_OTG_HCSPLT_XACTPOS_0 (0x1UL << USB_OTG_HCSPLT_XACTPOS_Pos) /*!< 0x00004000 */
-#define USB_OTG_HCSPLT_XACTPOS_1 (0x2UL << USB_OTG_HCSPLT_XACTPOS_Pos) /*!< 0x00008000 */
-#define USB_OTG_HCSPLT_COMPLSPLT_Pos (16U)
-#define USB_OTG_HCSPLT_COMPLSPLT_Msk (0x1UL << USB_OTG_HCSPLT_COMPLSPLT_Pos) /*!< 0x00010000 */
-#define USB_OTG_HCSPLT_COMPLSPLT USB_OTG_HCSPLT_COMPLSPLT_Msk /*!< Do complete split */
-#define USB_OTG_HCSPLT_SPLITEN_Pos (31U)
-#define USB_OTG_HCSPLT_SPLITEN_Msk (0x1UL << USB_OTG_HCSPLT_SPLITEN_Pos) /*!< 0x80000000 */
-#define USB_OTG_HCSPLT_SPLITEN USB_OTG_HCSPLT_SPLITEN_Msk /*!< Split enable */
-
-/******************** Bit definition for USB_OTG_HCINT register ********************/
-#define USB_OTG_HCINT_XFRC_Pos (0U)
-#define USB_OTG_HCINT_XFRC_Msk (0x1UL << USB_OTG_HCINT_XFRC_Pos) /*!< 0x00000001 */
-#define USB_OTG_HCINT_XFRC USB_OTG_HCINT_XFRC_Msk /*!< Transfer completed */
-#define USB_OTG_HCINT_CHH_Pos (1U)
-#define USB_OTG_HCINT_CHH_Msk (0x1UL << USB_OTG_HCINT_CHH_Pos) /*!< 0x00000002 */
-#define USB_OTG_HCINT_CHH USB_OTG_HCINT_CHH_Msk /*!< Channel halted */
-#define USB_OTG_HCINT_AHBERR_Pos (2U)
-#define USB_OTG_HCINT_AHBERR_Msk (0x1UL << USB_OTG_HCINT_AHBERR_Pos) /*!< 0x00000004 */
-#define USB_OTG_HCINT_AHBERR USB_OTG_HCINT_AHBERR_Msk /*!< AHB error */
-#define USB_OTG_HCINT_STALL_Pos (3U)
-#define USB_OTG_HCINT_STALL_Msk (0x1UL << USB_OTG_HCINT_STALL_Pos) /*!< 0x00000008 */
-#define USB_OTG_HCINT_STALL USB_OTG_HCINT_STALL_Msk /*!< STALL response received interrupt */
-#define USB_OTG_HCINT_NAK_Pos (4U)
-#define USB_OTG_HCINT_NAK_Msk (0x1UL << USB_OTG_HCINT_NAK_Pos) /*!< 0x00000010 */
-#define USB_OTG_HCINT_NAK USB_OTG_HCINT_NAK_Msk /*!< NAK response received interrupt */
-#define USB_OTG_HCINT_ACK_Pos (5U)
-#define USB_OTG_HCINT_ACK_Msk (0x1UL << USB_OTG_HCINT_ACK_Pos) /*!< 0x00000020 */
-#define USB_OTG_HCINT_ACK USB_OTG_HCINT_ACK_Msk /*!< ACK response received/transmitted interrupt */
-#define USB_OTG_HCINT_NYET_Pos (6U)
-#define USB_OTG_HCINT_NYET_Msk (0x1UL << USB_OTG_HCINT_NYET_Pos) /*!< 0x00000040 */
-#define USB_OTG_HCINT_NYET USB_OTG_HCINT_NYET_Msk /*!< Response received interrupt */
-#define USB_OTG_HCINT_TXERR_Pos (7U)
-#define USB_OTG_HCINT_TXERR_Msk (0x1UL << USB_OTG_HCINT_TXERR_Pos) /*!< 0x00000080 */
-#define USB_OTG_HCINT_TXERR USB_OTG_HCINT_TXERR_Msk /*!< Transaction error */
-#define USB_OTG_HCINT_BBERR_Pos (8U)
-#define USB_OTG_HCINT_BBERR_Msk (0x1UL << USB_OTG_HCINT_BBERR_Pos) /*!< 0x00000100 */
-#define USB_OTG_HCINT_BBERR USB_OTG_HCINT_BBERR_Msk /*!< Babble error */
-#define USB_OTG_HCINT_FRMOR_Pos (9U)
-#define USB_OTG_HCINT_FRMOR_Msk (0x1UL << USB_OTG_HCINT_FRMOR_Pos) /*!< 0x00000200 */
-#define USB_OTG_HCINT_FRMOR USB_OTG_HCINT_FRMOR_Msk /*!< Frame overrun */
-#define USB_OTG_HCINT_DTERR_Pos (10U)
-#define USB_OTG_HCINT_DTERR_Msk (0x1UL << USB_OTG_HCINT_DTERR_Pos) /*!< 0x00000400 */
-#define USB_OTG_HCINT_DTERR USB_OTG_HCINT_DTERR_Msk /*!< Data toggle error */
-
-/******************** Bit definition for USB_OTG_DIEPINT register ********************/
-#define USB_OTG_DIEPINT_XFRC_Pos (0U)
-#define USB_OTG_DIEPINT_XFRC_Msk (0x1UL << USB_OTG_DIEPINT_XFRC_Pos) /*!< 0x00000001 */
-#define USB_OTG_DIEPINT_XFRC USB_OTG_DIEPINT_XFRC_Msk /*!< Transfer completed interrupt */
-#define USB_OTG_DIEPINT_EPDISD_Pos (1U)
-#define USB_OTG_DIEPINT_EPDISD_Msk (0x1UL << USB_OTG_DIEPINT_EPDISD_Pos) /*!< 0x00000002 */
-#define USB_OTG_DIEPINT_EPDISD USB_OTG_DIEPINT_EPDISD_Msk /*!< Endpoint disabled interrupt */
-#define USB_OTG_DIEPINT_AHBERR_Pos (2U)
-#define USB_OTG_DIEPINT_AHBERR_Msk (0x1UL << USB_OTG_DIEPINT_AHBERR_Pos) /*!< 0x00000004 */
-#define USB_OTG_DIEPINT_AHBERR USB_OTG_DIEPINT_AHBERR_Msk /*!< AHB Error (AHBErr) during an IN transaction */
-#define USB_OTG_DIEPINT_TOC_Pos (3U)
-#define USB_OTG_DIEPINT_TOC_Msk (0x1UL << USB_OTG_DIEPINT_TOC_Pos) /*!< 0x00000008 */
-#define USB_OTG_DIEPINT_TOC USB_OTG_DIEPINT_TOC_Msk /*!< Timeout condition */
-#define USB_OTG_DIEPINT_ITTXFE_Pos (4U)
-#define USB_OTG_DIEPINT_ITTXFE_Msk (0x1UL << USB_OTG_DIEPINT_ITTXFE_Pos) /*!< 0x00000010 */
-#define USB_OTG_DIEPINT_ITTXFE USB_OTG_DIEPINT_ITTXFE_Msk /*!< IN token received when TxFIFO is empty */
-#define USB_OTG_DIEPINT_INEPNM_Pos (5U)
-#define USB_OTG_DIEPINT_INEPNM_Msk (0x1UL << USB_OTG_DIEPINT_INEPNM_Pos) /*!< 0x00000004 */
-#define USB_OTG_DIEPINT_INEPNM USB_OTG_DIEPINT_INEPNM_Msk /*!< IN token received with EP mismatch */
-#define USB_OTG_DIEPINT_INEPNE_Pos (6U)
-#define USB_OTG_DIEPINT_INEPNE_Msk (0x1UL << USB_OTG_DIEPINT_INEPNE_Pos) /*!< 0x00000040 */
-#define USB_OTG_DIEPINT_INEPNE USB_OTG_DIEPINT_INEPNE_Msk /*!< IN endpoint NAK effective */
-#define USB_OTG_DIEPINT_TXFE_Pos (7U)
-#define USB_OTG_DIEPINT_TXFE_Msk (0x1UL << USB_OTG_DIEPINT_TXFE_Pos) /*!< 0x00000080 */
-#define USB_OTG_DIEPINT_TXFE USB_OTG_DIEPINT_TXFE_Msk /*!< Transmit FIFO empty */
-#define USB_OTG_DIEPINT_TXFIFOUDRN_Pos (8U)
-#define USB_OTG_DIEPINT_TXFIFOUDRN_Msk (0x1UL << USB_OTG_DIEPINT_TXFIFOUDRN_Pos) /*!< 0x00000100 */
-#define USB_OTG_DIEPINT_TXFIFOUDRN USB_OTG_DIEPINT_TXFIFOUDRN_Msk /*!< Transmit Fifo Underrun */
-#define USB_OTG_DIEPINT_BNA_Pos (9U)
-#define USB_OTG_DIEPINT_BNA_Msk (0x1UL << USB_OTG_DIEPINT_BNA_Pos) /*!< 0x00000200 */
-#define USB_OTG_DIEPINT_BNA USB_OTG_DIEPINT_BNA_Msk /*!< Buffer not available interrupt */
-#define USB_OTG_DIEPINT_PKTDRPSTS_Pos (11U)
-#define USB_OTG_DIEPINT_PKTDRPSTS_Msk (0x1UL << USB_OTG_DIEPINT_PKTDRPSTS_Pos) /*!< 0x00000800 */
-#define USB_OTG_DIEPINT_PKTDRPSTS USB_OTG_DIEPINT_PKTDRPSTS_Msk /*!< Packet dropped status */
-#define USB_OTG_DIEPINT_BERR_Pos (12U)
-#define USB_OTG_DIEPINT_BERR_Msk (0x1UL << USB_OTG_DIEPINT_BERR_Pos) /*!< 0x00001000 */
-#define USB_OTG_DIEPINT_BERR USB_OTG_DIEPINT_BERR_Msk /*!< Babble error interrupt */
-#define USB_OTG_DIEPINT_NAK_Pos (13U)
-#define USB_OTG_DIEPINT_NAK_Msk (0x1UL << USB_OTG_DIEPINT_NAK_Pos) /*!< 0x00002000 */
-#define USB_OTG_DIEPINT_NAK USB_OTG_DIEPINT_NAK_Msk /*!< NAK interrupt */
-
-/******************** Bit definition for USB_OTG_HCINTMSK register ********************/
-#define USB_OTG_HCINTMSK_XFRCM_Pos (0U)
-#define USB_OTG_HCINTMSK_XFRCM_Msk (0x1UL << USB_OTG_HCINTMSK_XFRCM_Pos) /*!< 0x00000001 */
-#define USB_OTG_HCINTMSK_XFRCM USB_OTG_HCINTMSK_XFRCM_Msk /*!< Transfer completed mask */
-#define USB_OTG_HCINTMSK_CHHM_Pos (1U)
-#define USB_OTG_HCINTMSK_CHHM_Msk (0x1UL << USB_OTG_HCINTMSK_CHHM_Pos) /*!< 0x00000002 */
-#define USB_OTG_HCINTMSK_CHHM USB_OTG_HCINTMSK_CHHM_Msk /*!< Channel halted mask */
-#define USB_OTG_HCINTMSK_AHBERR_Pos (2U)
-#define USB_OTG_HCINTMSK_AHBERR_Msk (0x1UL << USB_OTG_HCINTMSK_AHBERR_Pos) /*!< 0x00000004 */
-#define USB_OTG_HCINTMSK_AHBERR USB_OTG_HCINTMSK_AHBERR_Msk /*!< AHB error */
-#define USB_OTG_HCINTMSK_STALLM_Pos (3U)
-#define USB_OTG_HCINTMSK_STALLM_Msk (0x1UL << USB_OTG_HCINTMSK_STALLM_Pos) /*!< 0x00000008 */
-#define USB_OTG_HCINTMSK_STALLM USB_OTG_HCINTMSK_STALLM_Msk /*!< STALL response received interrupt mask */
-#define USB_OTG_HCINTMSK_NAKM_Pos (4U)
-#define USB_OTG_HCINTMSK_NAKM_Msk (0x1UL << USB_OTG_HCINTMSK_NAKM_Pos) /*!< 0x00000010 */
-#define USB_OTG_HCINTMSK_NAKM USB_OTG_HCINTMSK_NAKM_Msk /*!< NAK response received interrupt mask */
-#define USB_OTG_HCINTMSK_ACKM_Pos (5U)
-#define USB_OTG_HCINTMSK_ACKM_Msk (0x1UL << USB_OTG_HCINTMSK_ACKM_Pos) /*!< 0x00000020 */
-#define USB_OTG_HCINTMSK_ACKM USB_OTG_HCINTMSK_ACKM_Msk /*!< ACK response received/transmitted interrupt mask */
-#define USB_OTG_HCINTMSK_NYET_Pos (6U)
-#define USB_OTG_HCINTMSK_NYET_Msk (0x1UL << USB_OTG_HCINTMSK_NYET_Pos) /*!< 0x00000040 */
-#define USB_OTG_HCINTMSK_NYET USB_OTG_HCINTMSK_NYET_Msk /*!< response received interrupt mask */
-#define USB_OTG_HCINTMSK_TXERRM_Pos (7U)
-#define USB_OTG_HCINTMSK_TXERRM_Msk (0x1UL << USB_OTG_HCINTMSK_TXERRM_Pos) /*!< 0x00000080 */
-#define USB_OTG_HCINTMSK_TXERRM USB_OTG_HCINTMSK_TXERRM_Msk /*!< Transaction error mask */
-#define USB_OTG_HCINTMSK_BBERRM_Pos (8U)
-#define USB_OTG_HCINTMSK_BBERRM_Msk (0x1UL << USB_OTG_HCINTMSK_BBERRM_Pos) /*!< 0x00000100 */
-#define USB_OTG_HCINTMSK_BBERRM USB_OTG_HCINTMSK_BBERRM_Msk /*!< Babble error mask */
-#define USB_OTG_HCINTMSK_FRMORM_Pos (9U)
-#define USB_OTG_HCINTMSK_FRMORM_Msk (0x1UL << USB_OTG_HCINTMSK_FRMORM_Pos) /*!< 0x00000200 */
-#define USB_OTG_HCINTMSK_FRMORM USB_OTG_HCINTMSK_FRMORM_Msk /*!< Frame overrun mask */
-#define USB_OTG_HCINTMSK_DTERRM_Pos (10U)
-#define USB_OTG_HCINTMSK_DTERRM_Msk (0x1UL << USB_OTG_HCINTMSK_DTERRM_Pos) /*!< 0x00000400 */
-#define USB_OTG_HCINTMSK_DTERRM USB_OTG_HCINTMSK_DTERRM_Msk /*!< Data toggle error mask */
-
-/******************** Bit definition for USB_OTG_DIEPTSIZ register ********************/
-
-#define USB_OTG_DIEPTSIZ_XFRSIZ_Pos (0U)
-#define USB_OTG_DIEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << USB_OTG_DIEPTSIZ_XFRSIZ_Pos) /*!< 0x0007FFFF */
-#define USB_OTG_DIEPTSIZ_XFRSIZ USB_OTG_DIEPTSIZ_XFRSIZ_Msk /*!< Transfer size */
-#define USB_OTG_DIEPTSIZ_PKTCNT_Pos (19U)
-#define USB_OTG_DIEPTSIZ_PKTCNT_Msk (0x3FFUL << USB_OTG_DIEPTSIZ_PKTCNT_Pos) /*!< 0x1FF80000 */
-#define USB_OTG_DIEPTSIZ_PKTCNT USB_OTG_DIEPTSIZ_PKTCNT_Msk /*!< Packet count */
-#define USB_OTG_DIEPTSIZ_MULCNT_Pos (29U)
-#define USB_OTG_DIEPTSIZ_MULCNT_Msk (0x3UL << USB_OTG_DIEPTSIZ_MULCNT_Pos) /*!< 0x60000000 */
-#define USB_OTG_DIEPTSIZ_MULCNT USB_OTG_DIEPTSIZ_MULCNT_Msk /*!< Packet count */
-/******************** Bit definition for USB_OTG_HCTSIZ register ********************/
-#define USB_OTG_HCTSIZ_XFRSIZ_Pos (0U)
-#define USB_OTG_HCTSIZ_XFRSIZ_Msk (0x7FFFFUL << USB_OTG_HCTSIZ_XFRSIZ_Pos) /*!< 0x0007FFFF */
-#define USB_OTG_HCTSIZ_XFRSIZ USB_OTG_HCTSIZ_XFRSIZ_Msk /*!< Transfer size */
-#define USB_OTG_HCTSIZ_PKTCNT_Pos (19U)
-#define USB_OTG_HCTSIZ_PKTCNT_Msk (0x3FFUL << USB_OTG_HCTSIZ_PKTCNT_Pos) /*!< 0x1FF80000 */
-#define USB_OTG_HCTSIZ_PKTCNT USB_OTG_HCTSIZ_PKTCNT_Msk /*!< Packet count */
-#define USB_OTG_HCTSIZ_DOPING_Pos (31U)
-#define USB_OTG_HCTSIZ_DOPING_Msk (0x1UL << USB_OTG_HCTSIZ_DOPING_Pos) /*!< 0x80000000 */
-#define USB_OTG_HCTSIZ_DOPING USB_OTG_HCTSIZ_DOPING_Msk /*!< Do PING */
-#define USB_OTG_HCTSIZ_DPID_Pos (29U)
-#define USB_OTG_HCTSIZ_DPID_Msk (0x3UL << USB_OTG_HCTSIZ_DPID_Pos) /*!< 0x60000000 */
-#define USB_OTG_HCTSIZ_DPID USB_OTG_HCTSIZ_DPID_Msk /*!< Data PID */
-#define USB_OTG_HCTSIZ_DPID_0 (0x1UL << USB_OTG_HCTSIZ_DPID_Pos) /*!< 0x20000000 */
-#define USB_OTG_HCTSIZ_DPID_1 (0x2UL << USB_OTG_HCTSIZ_DPID_Pos) /*!< 0x40000000 */
-
-/******************** Bit definition for USB_OTG_DIEPDMA register ********************/
-#define USB_OTG_DIEPDMA_DMAADDR_Pos (0U)
-#define USB_OTG_DIEPDMA_DMAADDR_Msk (0xFFFFFFFFUL << USB_OTG_DIEPDMA_DMAADDR_Pos) /*!< 0xFFFFFFFF */
-#define USB_OTG_DIEPDMA_DMAADDR USB_OTG_DIEPDMA_DMAADDR_Msk /*!< DMA address */
-
-/******************** Bit definition for USB_OTG_HCDMA register ********************/
-#define USB_OTG_HCDMA_DMAADDR_Pos (0U)
-#define USB_OTG_HCDMA_DMAADDR_Msk (0xFFFFFFFFUL << USB_OTG_HCDMA_DMAADDR_Pos) /*!< 0xFFFFFFFF */
-#define USB_OTG_HCDMA_DMAADDR USB_OTG_HCDMA_DMAADDR_Msk /*!< DMA address */
-
-/******************** Bit definition for USB_OTG_DTXFSTS register ********************/
-#define USB_OTG_DTXFSTS_INEPTFSAV_Pos (0U)
-#define USB_OTG_DTXFSTS_INEPTFSAV_Msk (0xFFFFUL << USB_OTG_DTXFSTS_INEPTFSAV_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DTXFSTS_INEPTFSAV USB_OTG_DTXFSTS_INEPTFSAV_Msk /*!< IN endpoint TxFIFO space available */
-
-/******************** Bit definition for USB_OTG_DIEPTXF register ********************/
-#define USB_OTG_DIEPTXF_INEPTXSA_Pos (0U)
-#define USB_OTG_DIEPTXF_INEPTXSA_Msk (0xFFFFUL << USB_OTG_DIEPTXF_INEPTXSA_Pos) /*!< 0x0000FFFF */
-#define USB_OTG_DIEPTXF_INEPTXSA USB_OTG_DIEPTXF_INEPTXSA_Msk /*!< IN endpoint FIFOx transmit RAM start address */
-#define USB_OTG_DIEPTXF_INEPTXFD_Pos (16U)
-#define USB_OTG_DIEPTXF_INEPTXFD_Msk (0xFFFFUL << USB_OTG_DIEPTXF_INEPTXFD_Pos) /*!< 0xFFFF0000 */
-#define USB_OTG_DIEPTXF_INEPTXFD USB_OTG_DIEPTXF_INEPTXFD_Msk /*!< IN endpoint TxFIFO depth */
-
-/******************** Bit definition for USB_OTG_DOEPCTL register ********************/
-
-#define USB_OTG_DOEPCTL_MPSIZ_Pos (0U)
-#define USB_OTG_DOEPCTL_MPSIZ_Msk (0x7FFUL << USB_OTG_DOEPCTL_MPSIZ_Pos) /*!< 0x000007FF */
-#define USB_OTG_DOEPCTL_MPSIZ USB_OTG_DOEPCTL_MPSIZ_Msk /*!< Maximum packet size */ /*!<Bit 1 */
-#define USB_OTG_DOEPCTL_USBAEP_Pos (15U)
-#define USB_OTG_DOEPCTL_USBAEP_Msk (0x1UL << USB_OTG_DOEPCTL_USBAEP_Pos) /*!< 0x00008000 */
-#define USB_OTG_DOEPCTL_USBAEP USB_OTG_DOEPCTL_USBAEP_Msk /*!< USB active endpoint */
-#define USB_OTG_DOEPCTL_NAKSTS_Pos (17U)
-#define USB_OTG_DOEPCTL_NAKSTS_Msk (0x1UL << USB_OTG_DOEPCTL_NAKSTS_Pos) /*!< 0x00020000 */
-#define USB_OTG_DOEPCTL_NAKSTS USB_OTG_DOEPCTL_NAKSTS_Msk /*!< NAK status */
-#define USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Pos (28U)
-#define USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Pos) /*!< 0x10000000 */
-#define USB_OTG_DOEPCTL_SD0PID_SEVNFRM USB_OTG_DOEPCTL_SD0PID_SEVNFRM_Msk /*!< Set DATA0 PID */
-#define USB_OTG_DOEPCTL_SODDFRM_Pos (29U)
-#define USB_OTG_DOEPCTL_SODDFRM_Msk (0x1UL << USB_OTG_DOEPCTL_SODDFRM_Pos) /*!< 0x20000000 */
-#define USB_OTG_DOEPCTL_SODDFRM USB_OTG_DOEPCTL_SODDFRM_Msk /*!< Set odd frame */
-#define USB_OTG_DOEPCTL_EPTYP_Pos (18U)
-#define USB_OTG_DOEPCTL_EPTYP_Msk (0x3UL << USB_OTG_DOEPCTL_EPTYP_Pos) /*!< 0x000C0000 */
-#define USB_OTG_DOEPCTL_EPTYP USB_OTG_DOEPCTL_EPTYP_Msk /*!< Endpoint type */
-#define USB_OTG_DOEPCTL_EPTYP_0 (0x1UL << USB_OTG_DOEPCTL_EPTYP_Pos) /*!< 0x00040000 */
-#define USB_OTG_DOEPCTL_EPTYP_1 (0x2UL << USB_OTG_DOEPCTL_EPTYP_Pos) /*!< 0x00080000 */
-#define USB_OTG_DOEPCTL_SNPM_Pos (20U)
-#define USB_OTG_DOEPCTL_SNPM_Msk (0x1UL << USB_OTG_DOEPCTL_SNPM_Pos) /*!< 0x00100000 */
-#define USB_OTG_DOEPCTL_SNPM USB_OTG_DOEPCTL_SNPM_Msk /*!< Snoop mode */
-#define USB_OTG_DOEPCTL_STALL_Pos (21U)
-#define USB_OTG_DOEPCTL_STALL_Msk (0x1UL << USB_OTG_DOEPCTL_STALL_Pos) /*!< 0x00200000 */
-#define USB_OTG_DOEPCTL_STALL USB_OTG_DOEPCTL_STALL_Msk /*!< STALL handshake */
-#define USB_OTG_DOEPCTL_CNAK_Pos (26U)
-#define USB_OTG_DOEPCTL_CNAK_Msk (0x1UL << USB_OTG_DOEPCTL_CNAK_Pos) /*!< 0x04000000 */
-#define USB_OTG_DOEPCTL_CNAK USB_OTG_DOEPCTL_CNAK_Msk /*!< Clear NAK */
-#define USB_OTG_DOEPCTL_SNAK_Pos (27U)
-#define USB_OTG_DOEPCTL_SNAK_Msk (0x1UL << USB_OTG_DOEPCTL_SNAK_Pos) /*!< 0x08000000 */
-#define USB_OTG_DOEPCTL_SNAK USB_OTG_DOEPCTL_SNAK_Msk /*!< Set NAK */
-#define USB_OTG_DOEPCTL_EPDIS_Pos (30U)
-#define USB_OTG_DOEPCTL_EPDIS_Msk (0x1UL << USB_OTG_DOEPCTL_EPDIS_Pos) /*!< 0x40000000 */
-#define USB_OTG_DOEPCTL_EPDIS USB_OTG_DOEPCTL_EPDIS_Msk /*!< Endpoint disable */
-#define USB_OTG_DOEPCTL_EPENA_Pos (31U)
-#define USB_OTG_DOEPCTL_EPENA_Msk (0x1UL << USB_OTG_DOEPCTL_EPENA_Pos) /*!< 0x80000000 */
-#define USB_OTG_DOEPCTL_EPENA USB_OTG_DOEPCTL_EPENA_Msk /*!< Endpoint enable */
-
-/******************** Bit definition for USB_OTG_DOEPINT register ********************/
-#define USB_OTG_DOEPINT_XFRC_Pos (0U)
-#define USB_OTG_DOEPINT_XFRC_Msk (0x1UL << USB_OTG_DOEPINT_XFRC_Pos) /*!< 0x00000001 */
-#define USB_OTG_DOEPINT_XFRC USB_OTG_DOEPINT_XFRC_Msk /*!< Transfer completed interrupt */
-#define USB_OTG_DOEPINT_EPDISD_Pos (1U)
-#define USB_OTG_DOEPINT_EPDISD_Msk (0x1UL << USB_OTG_DOEPINT_EPDISD_Pos) /*!< 0x00000002 */
-#define USB_OTG_DOEPINT_EPDISD USB_OTG_DOEPINT_EPDISD_Msk /*!< Endpoint disabled interrupt */
-#define USB_OTG_DOEPINT_AHBERR_Pos (2U)
-#define USB_OTG_DOEPINT_AHBERR_Msk (0x1UL << USB_OTG_DOEPINT_AHBERR_Pos) /*!< 0x00000004 */
-#define USB_OTG_DOEPINT_AHBERR USB_OTG_DOEPINT_AHBERR_Msk /*!< AHB Error (AHBErr) during an OUT transaction */
-#define USB_OTG_DOEPINT_STUP_Pos (3U)
-#define USB_OTG_DOEPINT_STUP_Msk (0x1UL << USB_OTG_DOEPINT_STUP_Pos) /*!< 0x00000008 */
-#define USB_OTG_DOEPINT_STUP USB_OTG_DOEPINT_STUP_Msk /*!< SETUP phase done */
-#define USB_OTG_DOEPINT_OTEPDIS_Pos (4U)
-#define USB_OTG_DOEPINT_OTEPDIS_Msk (0x1UL << USB_OTG_DOEPINT_OTEPDIS_Pos) /*!< 0x00000010 */
-#define USB_OTG_DOEPINT_OTEPDIS USB_OTG_DOEPINT_OTEPDIS_Msk /*!< OUT token received when endpoint disabled */
-#define USB_OTG_DOEPINT_OTEPSPR_Pos (5U)
-#define USB_OTG_DOEPINT_OTEPSPR_Msk (0x1UL << USB_OTG_DOEPINT_OTEPSPR_Pos) /*!< 0x00000020 */
-#define USB_OTG_DOEPINT_OTEPSPR USB_OTG_DOEPINT_OTEPSPR_Msk /*!< Status Phase Received For Control Write */
-#define USB_OTG_DOEPINT_B2BSTUP_Pos (6U)
-#define USB_OTG_DOEPINT_B2BSTUP_Msk (0x1UL << USB_OTG_DOEPINT_B2BSTUP_Pos) /*!< 0x00000040 */
-#define USB_OTG_DOEPINT_B2BSTUP USB_OTG_DOEPINT_B2BSTUP_Msk /*!< Back-to-back SETUP packets received */
-#define USB_OTG_DOEPINT_OUTPKTERR_Pos (8U)
-#define USB_OTG_DOEPINT_OUTPKTERR_Msk (0x1UL << USB_OTG_DOEPINT_OUTPKTERR_Pos) /*!< 0x00000100 */
-#define USB_OTG_DOEPINT_OUTPKTERR USB_OTG_DOEPINT_OUTPKTERR_Msk /*!< OUT packet error */
-#define USB_OTG_DOEPINT_NAK_Pos (13U)
-#define USB_OTG_DOEPINT_NAK_Msk (0x1UL << USB_OTG_DOEPINT_NAK_Pos) /*!< 0x00002000 */
-#define USB_OTG_DOEPINT_NAK USB_OTG_DOEPINT_NAK_Msk /*!< NAK Packet is transmitted by the device */
-#define USB_OTG_DOEPINT_NYET_Pos (14U)
-#define USB_OTG_DOEPINT_NYET_Msk (0x1UL << USB_OTG_DOEPINT_NYET_Pos) /*!< 0x00004000 */
-#define USB_OTG_DOEPINT_NYET USB_OTG_DOEPINT_NYET_Msk /*!< NYET interrupt */
-#define USB_OTG_DOEPINT_STPKTRX_Pos (15U)
-#define USB_OTG_DOEPINT_STPKTRX_Msk (0x1UL << USB_OTG_DOEPINT_STPKTRX_Pos) /*!< 0x00008000 */
-#define USB_OTG_DOEPINT_STPKTRX USB_OTG_DOEPINT_STPKTRX_Msk /*!< Setup Packet Received */
-/******************** Bit definition for USB_OTG_DOEPTSIZ register ********************/
-
-#define USB_OTG_DOEPTSIZ_XFRSIZ_Pos (0U)
-#define USB_OTG_DOEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << USB_OTG_DOEPTSIZ_XFRSIZ_Pos) /*!< 0x0007FFFF */
-#define USB_OTG_DOEPTSIZ_XFRSIZ USB_OTG_DOEPTSIZ_XFRSIZ_Msk /*!< Transfer size */
-#define USB_OTG_DOEPTSIZ_PKTCNT_Pos (19U)
-#define USB_OTG_DOEPTSIZ_PKTCNT_Msk (0x3FFUL << USB_OTG_DOEPTSIZ_PKTCNT_Pos) /*!< 0x1FF80000 */
-#define USB_OTG_DOEPTSIZ_PKTCNT USB_OTG_DOEPTSIZ_PKTCNT_Msk /*!< Packet count */
-
-#define USB_OTG_DOEPTSIZ_STUPCNT_Pos (29U)
-#define USB_OTG_DOEPTSIZ_STUPCNT_Msk (0x3UL << USB_OTG_DOEPTSIZ_STUPCNT_Pos) /*!< 0x60000000 */
-#define USB_OTG_DOEPTSIZ_STUPCNT USB_OTG_DOEPTSIZ_STUPCNT_Msk /*!< SETUP packet count */
-#define USB_OTG_DOEPTSIZ_STUPCNT_0 (0x1UL << USB_OTG_DOEPTSIZ_STUPCNT_Pos) /*!< 0x20000000 */
-#define USB_OTG_DOEPTSIZ_STUPCNT_1 (0x2UL << USB_OTG_DOEPTSIZ_STUPCNT_Pos) /*!< 0x40000000 */
-
-/******************** Bit definition for PCGCCTL register ********************/
-#define USB_OTG_PCGCCTL_STOPCLK_Pos (0U)
-#define USB_OTG_PCGCCTL_STOPCLK_Msk (0x1UL << USB_OTG_PCGCCTL_STOPCLK_Pos) /*!< 0x00000001 */
-#define USB_OTG_PCGCCTL_STOPCLK USB_OTG_PCGCCTL_STOPCLK_Msk /*!< SETUP packet count */
-#define USB_OTG_PCGCCTL_GATECLK_Pos (1U)
-#define USB_OTG_PCGCCTL_GATECLK_Msk (0x1UL << USB_OTG_PCGCCTL_GATECLK_Pos) /*!< 0x00000002 */
-#define USB_OTG_PCGCCTL_GATECLK USB_OTG_PCGCCTL_GATECLK_Msk /*!<Bit 0 */
-#define USB_OTG_PCGCCTL_PHYSUSP_Pos (4U)
-#define USB_OTG_PCGCCTL_PHYSUSP_Msk (0x1UL << USB_OTG_PCGCCTL_PHYSUSP_Pos) /*!< 0x00000010 */
-#define USB_OTG_PCGCCTL_PHYSUSP USB_OTG_PCGCCTL_PHYSUSP_Msk /*!<Bit 1 */
-
-/* Legacy define */
-/******************** Bit definition for OTG register ********************/
-#define USB_OTG_CHNUM_Pos (0U)
-#define USB_OTG_CHNUM_Msk (0xFUL << USB_OTG_CHNUM_Pos) /*!< 0x0000000F */
-#define USB_OTG_CHNUM USB_OTG_CHNUM_Msk /*!< Channel number */
-#define USB_OTG_CHNUM_0 (0x1UL << USB_OTG_CHNUM_Pos) /*!< 0x00000001 */
-#define USB_OTG_CHNUM_1 (0x2UL << USB_OTG_CHNUM_Pos) /*!< 0x00000002 */
-#define USB_OTG_CHNUM_2 (0x4UL << USB_OTG_CHNUM_Pos) /*!< 0x00000004 */
-#define USB_OTG_CHNUM_3 (0x8UL << USB_OTG_CHNUM_Pos) /*!< 0x00000008 */
-#define USB_OTG_BCNT_Pos (4U)
-#define USB_OTG_BCNT_Msk (0x7FFUL << USB_OTG_BCNT_Pos) /*!< 0x00007FF0 */
-#define USB_OTG_BCNT USB_OTG_BCNT_Msk /*!< Byte count */
-
-#define USB_OTG_DPID_Pos (15U)
-#define USB_OTG_DPID_Msk (0x3UL << USB_OTG_DPID_Pos) /*!< 0x00018000 */
-#define USB_OTG_DPID USB_OTG_DPID_Msk /*!< Data PID */
-#define USB_OTG_DPID_0 (0x1UL << USB_OTG_DPID_Pos) /*!< 0x00008000 */
-#define USB_OTG_DPID_1 (0x2UL << USB_OTG_DPID_Pos) /*!< 0x00010000 */
-
-#define USB_OTG_PKTSTS_Pos (17U)
-#define USB_OTG_PKTSTS_Msk (0xFUL << USB_OTG_PKTSTS_Pos) /*!< 0x001E0000 */
-#define USB_OTG_PKTSTS USB_OTG_PKTSTS_Msk /*!< Packet status */
-#define USB_OTG_PKTSTS_0 (0x1UL << USB_OTG_PKTSTS_Pos) /*!< 0x00020000 */
-#define USB_OTG_PKTSTS_1 (0x2UL << USB_OTG_PKTSTS_Pos) /*!< 0x00040000 */
-#define USB_OTG_PKTSTS_2 (0x4UL << USB_OTG_PKTSTS_Pos) /*!< 0x00080000 */
-#define USB_OTG_PKTSTS_3 (0x8UL << USB_OTG_PKTSTS_Pos) /*!< 0x00100000 */
-
-#define USB_OTG_EPNUM_Pos (0U)
-#define USB_OTG_EPNUM_Msk (0xFUL << USB_OTG_EPNUM_Pos) /*!< 0x0000000F */
-#define USB_OTG_EPNUM USB_OTG_EPNUM_Msk /*!< Endpoint number */
-#define USB_OTG_EPNUM_0 (0x1UL << USB_OTG_EPNUM_Pos) /*!< 0x00000001 */
-#define USB_OTG_EPNUM_1 (0x2UL << USB_OTG_EPNUM_Pos) /*!< 0x00000002 */
-#define USB_OTG_EPNUM_2 (0x4UL << USB_OTG_EPNUM_Pos) /*!< 0x00000004 */
-#define USB_OTG_EPNUM_3 (0x8UL << USB_OTG_EPNUM_Pos) /*!< 0x00000008 */
-
-#define USB_OTG_FRMNUM_Pos (21U)
-#define USB_OTG_FRMNUM_Msk (0xFUL << USB_OTG_FRMNUM_Pos) /*!< 0x01E00000 */
-#define USB_OTG_FRMNUM USB_OTG_FRMNUM_Msk /*!< Frame number */
-#define USB_OTG_FRMNUM_0 (0x1UL << USB_OTG_FRMNUM_Pos) /*!< 0x00200000 */
-#define USB_OTG_FRMNUM_1 (0x2UL << USB_OTG_FRMNUM_Pos) /*!< 0x00400000 */
-#define USB_OTG_FRMNUM_2 (0x4UL << USB_OTG_FRMNUM_Pos) /*!< 0x00800000 */
-#define USB_OTG_FRMNUM_3 (0x8UL << USB_OTG_FRMNUM_Pos) /*!< 0x01000000 */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/src/portable/st/typec/typec_stm32.c b/src/portable/st/typec/typec_stm32.c
new file mode 100644
index 000000000..8da2e39ba
--- /dev/null
+++ b/src/portable/st/typec/typec_stm32.c
@@ -0,0 +1,372 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "tusb_option.h"
+#include "typec/tcd.h"
+
+#if CFG_TUC_ENABLED && defined(TUP_USBIP_TYPEC_STM32)
+
+#include "common/tusb_common.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32G4
+ #include "stm32g4xx.h"
+ #include "stm32g4xx_ll_dma.h" // for UCPD REQID
+#else
+ #error "Unsupported STM32 family"
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+enum {
+ IMR_ATTACHED = UCPD_IMR_TXMSGDISCIE | UCPD_IMR_TXMSGSENTIE | UCPD_IMR_TXMSGABTIE | UCPD_IMR_TXUNDIE |
+ UCPD_IMR_RXHRSTDETIE | UCPD_IMR_RXOVRIE | UCPD_IMR_RXMSGENDIE | UCPD_IMR_RXORDDETIE |
+ UCPD_IMR_HRSTDISCIE | UCPD_IMR_HRSTSENTIE | UCPD_IMR_FRSEVTIE
+};
+
+#define PHY_SYNC1 0x18u
+#define PHY_SYNC2 0x11u
+#define PHY_SYNC3 0x06u
+#define PHY_RST1 0x07u
+#define PHY_RST2 0x19u
+#define PHY_EOP 0x0Du
+
+#define PHY_ORDERED_SET_SOP (PHY_SYNC1 | (PHY_SYNC1<<5u) | (PHY_SYNC1<<10u) | (PHY_SYNC2<<15u)) // SOP Ordered set coding
+#define PHY_ORDERED_SET_SOP_P (PHY_SYNC1 | (PHY_SYNC1<<5u) | (PHY_SYNC3<<10u) | (PHY_SYNC3<<15u)) // SOP' Ordered set coding
+#define PHY_ORDERED_SET_SOP_PP (PHY_SYNC1 | (PHY_SYNC3<<5u) | (PHY_SYNC1<<10u) | (PHY_SYNC3<<15u)) // SOP'' Ordered set coding
+#define PHY_ORDERED_SET_HARD_RESET (PHY_RST1 | (PHY_RST1<<5u) | (PHY_RST1<<10u) | (PHY_RST2<<15u )) // Hard Reset Ordered set coding
+#define PHY_ORDERED_SET_CABLE_RESET (PHY_RST1 | (PHY_SYNC1<<5u) | (PHY_RST1<<10u) | (PHY_SYNC3<<15u)) // Cable Reset Ordered set coding
+#define PHY_ORDERED_SET_SOP_P_DEBUG (PHY_SYNC1 | (PHY_RST2<<5u) | (PHY_RST2<<10u) | (PHY_SYNC3<<15u)) // SOP' Debug Ordered set coding
+#define PHY_ORDERED_SET_SOP_PP_DEBUG (PHY_SYNC1 | (PHY_RST2<<5u) | (PHY_SYNC3<<10u) | (PHY_SYNC2<<15u)) // SOP'' Debug Ordered set coding
+
+
+static uint8_t const* _rx_buf;
+static uint8_t const* _tx_pending_buf;
+static uint16_t _tx_pending_bytes;
+static uint16_t _tx_xferring_bytes;
+
+static pd_header_t _good_crc = {
+ .msg_type = PD_CTRL_GOOD_CRC,
+ .data_role = 0, // UFP
+ .specs_rev = PD_REV_20,
+ .power_role = 0, // Sink
+ .msg_id = 0,
+ .n_data_obj = 0,
+ .extended = 0
+};
+
+// address of DMA channel rx, tx for each port
+#define CFG_TUC_STM32_DMA { { DMA1_Channel1_BASE, DMA1_Channel2_BASE } }
+
+//--------------------------------------------------------------------+
+// DMA
+//--------------------------------------------------------------------+
+
+static const uint32_t _dma_addr_arr[TUP_TYPEC_RHPORTS_NUM][2] = CFG_TUC_STM32_DMA;
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t dma_get_addr(uint8_t rhport, bool is_rx) {
+ return _dma_addr_arr[rhport][is_rx ? 0 : 1];
+}
+
+static void dma_init(uint8_t rhport, bool is_rx) {
+ uint32_t dma_addr = dma_get_addr(rhport, is_rx);
+ DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_addr;
+ uint32_t req_id;
+
+ if (is_rx) {
+ // Peripheral -> Memory, Memory inc, 8-bit, High priority
+ dma_ch->CCR = DMA_CCR_MINC | DMA_CCR_PL_1;
+ dma_ch->CPAR = (uint32_t) &UCPD1->RXDR;
+
+ req_id = LL_DMAMUX_REQ_UCPD1_RX;
+ } else {
+ // Memory -> Peripheral, Memory inc, 8-bit, High priority
+ dma_ch->CCR = DMA_CCR_MINC | DMA_CCR_PL_1 | DMA_CCR_DIR;
+ dma_ch->CPAR = (uint32_t) &UCPD1->TXDR;
+
+ req_id = LL_DMAMUX_REQ_UCPD1_TX;
+ }
+
+ // find and set up mux channel TODO support mcu with multiple DMAMUXs
+ enum {
+ CH_DIFF = DMA1_Channel2_BASE - DMA1_Channel1_BASE
+ };
+ uint32_t mux_ch_num;
+
+ #ifdef DMA2_BASE
+ if (dma_addr > DMA2_BASE) {
+ mux_ch_num = 8 * ((dma_addr - DMA2_Channel1_BASE) / CH_DIFF);
+ } else
+ #endif
+ {
+ mux_ch_num = (dma_addr - DMA1_Channel1_BASE) / CH_DIFF;
+ }
+
+ DMAMUX_Channel_TypeDef* mux_ch = DMAMUX1_Channel0 + mux_ch_num;
+
+ uint32_t mux_ccr = mux_ch->CCR & ~(DMAMUX_CxCR_DMAREQ_ID);
+ mux_ccr |= req_id;
+ mux_ch->CCR = mux_ccr;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dma_start(uint8_t rhport, bool is_rx, void const* buf, uint16_t len) {
+ DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
+
+ dma_ch->CMAR = (uint32_t) buf;
+ dma_ch->CNDTR = len;
+ dma_ch->CCR |= DMA_CCR_EN;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dma_stop(uint8_t rhport, bool is_rx) {
+ DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
+ dma_ch->CCR &= ~DMA_CCR_EN;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(uint8_t rhport, bool is_rx) {
+ DMA_Channel_TypeDef* dma_ch = (DMA_Channel_TypeDef*) dma_get_addr(rhport, is_rx);
+ return dma_ch->CCR & DMA_CCR_EN;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dma_tx_start(uint8_t rhport, void const* buf, uint16_t len) {
+ UCPD1->TX_ORDSET = PHY_ORDERED_SET_SOP;
+ UCPD1->TX_PAYSZ = len;
+ dma_start(rhport, false, buf, len);
+ UCPD1->CR |= UCPD_CR_TXSEND;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dma_tx_stop(uint8_t rhport) {
+ dma_stop(rhport, false);
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+bool tcd_init(uint8_t rhport, uint32_t port_type) {
+ (void) rhport;
+
+ // Init DMA for RX, TX
+ dma_init(rhport, true);
+ dma_init(rhport, false);
+
+ // Initialization phase: CFG1, detect all SOPs
+ UCPD1->CFG1 = (0x0d << UCPD_CFG1_HBITCLKDIV_Pos) | (0x10 << UCPD_CFG1_IFRGAP_Pos) | (0x07 << UCPD_CFG1_TRANSWIN_Pos) |
+ (0x01 << UCPD_CFG1_PSC_UCPDCLK_Pos) | (0x1f << UCPD_CFG1_RXORDSETEN_Pos);
+ UCPD1->CFG1 |= UCPD_CFG1_UCPDEN;
+
+ // General programming sequence (with UCPD configured then enabled)
+ if (port_type == TUSB_TYPEC_PORT_SNK) {
+ // Set analog mode enable both CC Phy
+ UCPD1->CR = (0x01 << UCPD_CR_ANAMODE_Pos) | (UCPD_CR_CCENABLE_0 | UCPD_CR_CCENABLE_1);
+
+ // Read Voltage State on CC1 & CC2 fore initial state
+ uint32_t v_cc[2];
+ (void) v_cc;
+ v_cc[0] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC1_Pos) & 0x03;
+ v_cc[1] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC2_Pos) & 0x03;
+ TU_LOG1("Initial VState CC1 = %lu, CC2 = %lu\r\n", v_cc[0], v_cc[1]);
+
+ // Enable CC1 & CC2 Interrupt
+ UCPD1->IMR = UCPD_IMR_TYPECEVT1IE | UCPD_IMR_TYPECEVT2IE;
+ }
+
+ // Disable dead battery in PWR's CR3
+ PWR->CR3 |= PWR_CR3_UCPD_DBDIS;
+
+ return true;
+}
+
+// Enable interrupt
+void tcd_int_enable (uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(UCPD1_IRQn);
+}
+
+// Disable interrupt
+void tcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(UCPD1_IRQn);
+}
+
+bool tcd_msg_receive(uint8_t rhport, uint8_t* buffer, uint16_t total_bytes) {
+ _rx_buf = buffer;
+ dma_start(rhport, true, buffer, total_bytes);
+ return true;
+}
+
+bool tcd_msg_send(uint8_t rhport, uint8_t const* buffer, uint16_t total_bytes) {
+ (void) rhport;
+
+ if (dma_enabled(rhport, false)) {
+ // DMA is busy, probably sending GoodCRC, save as pending TX
+ _tx_pending_buf = buffer;
+ _tx_pending_bytes = total_bytes;
+ }else {
+ // DMA is free, start sending
+ _tx_pending_buf = NULL;
+ _tx_pending_bytes = 0;
+
+ _tx_xferring_bytes = total_bytes;
+ dma_tx_start(rhport, buffer, total_bytes);
+ }
+
+ return true;
+}
+
+void tcd_int_handler(uint8_t rhport) {
+ (void) rhport;
+
+ uint32_t sr = UCPD1->SR;
+ sr &= UCPD1->IMR;
+
+ if (sr & (UCPD_SR_TYPECEVT1 | UCPD_SR_TYPECEVT2)) {
+ uint32_t v_cc[2];
+ v_cc[0] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC1_Pos) & 0x03;
+ v_cc[1] = (UCPD1->SR >> UCPD_SR_TYPEC_VSTATE_CC2_Pos) & 0x03;
+
+ TU_LOG3("VState CC1 = %lu, CC2 = %lu\r\n", v_cc[0], v_cc[1]);
+
+ uint32_t cr = UCPD1->CR;
+
+ // TODO only support SNK for now, required highest voltage for now
+ // Enable PHY on active CC and disable Rd on other CC
+ // FIXME somehow CC2 is vstate is not correct, always 1 even not attached.
+ // on DPOW1 board, it is connected to PA10 (USBPD_DBCC2), we probably miss something.
+ if ((sr & UCPD_SR_TYPECEVT1) && (v_cc[0] == 3)) {
+ TU_LOG3("Attach CC1\r\n");
+ cr &= ~(UCPD_CR_PHYCCSEL | UCPD_CR_CCENABLE);
+ cr |= UCPD_CR_PHYRXEN | UCPD_CR_CCENABLE_0;
+ } else if ((sr & UCPD_SR_TYPECEVT2) && (v_cc[1] == 3)) {
+ TU_LOG3("Attach CC2\r\n");
+ cr &= ~UCPD_CR_CCENABLE;
+ cr |= (UCPD_CR_PHYCCSEL | UCPD_CR_PHYRXEN | UCPD_CR_CCENABLE_1);
+ } else {
+ TU_LOG3("Detach\r\n");
+ cr &= ~UCPD_CR_PHYRXEN;
+ cr |= UCPD_CR_CCENABLE_0 | UCPD_CR_CCENABLE_1;
+ }
+
+ if (cr & UCPD_CR_PHYRXEN) {
+ // Attached
+ UCPD1->IMR |= IMR_ATTACHED;
+ UCPD1->CFG1 |= UCPD_CFG1_RXDMAEN | UCPD_CFG1_TXDMAEN;
+ }else {
+ // Detached
+ UCPD1->CFG1 &= ~(UCPD_CFG1_RXDMAEN | UCPD_CFG1_TXDMAEN);
+ UCPD1->IMR &= ~IMR_ATTACHED;
+ }
+
+ // notify stack
+ tcd_event_cc_changed(rhport, v_cc[0], v_cc[1], true);
+
+ UCPD1->CR = cr;
+
+ // ack
+ UCPD1->ICR = UCPD_ICR_TYPECEVT1CF | UCPD_ICR_TYPECEVT2CF;
+ }
+
+ //------------- RX -------------//
+ if (sr & UCPD_SR_RXORDDET) {
+ // SOP: Start of Packet.
+ TU_LOG3("SOP\r\n");
+ // UCPD1->RX_ORDSET & UCPD_RX_ORDSET_RXORDSET_Msk;
+
+ // ack
+ UCPD1->ICR = UCPD_ICR_RXORDDETCF;
+ }
+
+ // Received full message
+ if (sr & UCPD_SR_RXMSGEND) {
+ TU_LOG3("RX MSG END\r\n");
+
+ // stop TX
+ dma_stop(rhport, true);
+
+ uint8_t result;
+
+ if (!(sr & UCPD_SR_RXERR)) {
+ // response with good crc
+ // TODO move this to usbc stack
+ if (_rx_buf) {
+ _good_crc.msg_id = ((pd_header_t const *) _rx_buf)->msg_id;
+ dma_tx_start(rhport, &_good_crc, 2);
+ }
+
+ result = XFER_RESULT_SUCCESS;
+ }else {
+ // CRC failed
+ result = XFER_RESULT_FAILED;
+ }
+
+ // notify stack
+ tcd_event_rx_complete(rhport, UCPD1->RX_PAYSZ, result, true);
+
+ // ack
+ UCPD1->ICR = UCPD_ICR_RXMSGENDCF;
+ }
+
+ if (sr & UCPD_SR_RXOVR) {
+ TU_LOG3("RXOVR\r\n");
+ // ack
+ UCPD1->ICR = UCPD_ICR_RXOVRCF;
+ }
+
+ //------------- TX -------------//
+ // All tx events: complete and error
+ if (sr & (UCPD_SR_TXMSGSENT | (UCPD_SR_TXMSGDISC | UCPD_SR_TXMSGABT | UCPD_SR_TXUND))) {
+ // force TX stop
+ dma_tx_stop(rhport);
+
+ uint16_t const xferred_bytes = _tx_xferring_bytes - UCPD1->TX_PAYSZ;
+ uint8_t result;
+
+ if ( sr & UCPD_SR_TXMSGSENT ) {
+ TU_LOG3("TX MSG SENT\r\n");
+ result = XFER_RESULT_SUCCESS;
+ // ack
+ UCPD1->ICR = UCPD_ICR_TXMSGSENTCF;
+ }else {
+ TU_LOG3("TX Error\r\n");
+ result = XFER_RESULT_FAILED;
+ // ack
+ UCPD1->ICR = UCPD_SR_TXMSGDISC | UCPD_SR_TXMSGABT | UCPD_SR_TXUND;
+ }
+
+ // start pending TX if any
+ if (_tx_pending_buf && _tx_pending_bytes ) {
+ // Start the pending TX
+ dma_tx_start(rhport, _tx_pending_buf, _tx_pending_bytes);
+
+ // clear pending
+ _tx_pending_buf = NULL;
+ _tx_pending_bytes = 0;
+ }
+
+ // notify stack
+ tcd_event_tx_complete(rhport, xferred_bytes, result, true);
+ }
+}
+
+#endif
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index a0be846a5..21f13b279 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -35,7 +35,9 @@
#include <f1c100s-irq.h>
#include <device/dcd.h>
#include "musb_def.h"
-#include "bsp/board.h"
+
+//#include "bsp/board_api.h"
+extern uint32_t board_millis(void); // TODO remove
typedef uint32_t u32;
typedef uint16_t u16;
@@ -58,7 +60,7 @@ typedef struct TU_ATTR_PACKED
typedef struct
{
- tusb_control_request_t setup_packet;
+ CFG_TUD_MEM_ALIGN tusb_control_request_t setup_packet;
uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
int8_t status_out;
pipe_state_t pipe0;
@@ -243,7 +245,7 @@ static void USBC_Dev_SetAddress(u8 address)
static void __USBC_Dev_Tx_SendStall(void)
{
- //send stall, and fifo is flushed automaticly
+ //send stall, and fifo is flushed automatically
USBC_REG_set_bit_w(USBC_BP_TXCSR_D_SEND_STALL, USBC_REG_TXCSR(USBC0_BASE));
}
static u32 __USBC_Dev_Tx_IsEpStall(void)
@@ -350,7 +352,7 @@ static void USBC_INT_DisableRxEp(u8 ep_index)
* INTERNAL FUNCTION DECLARATION
*------------------------------------------------------------------*/
-static dcd_data_t _dcd;
+CFG_TUD_MEM_ALIGN static dcd_data_t _dcd;
static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
{
@@ -408,9 +410,9 @@ static inline unsigned free_block_size(free_block_t const *blk)
#if 0
static inline void print_block_list(free_block_t const *blk, unsigned num)
{
- TU_LOG1("*************\n");
+ TU_LOG1("*************\r\n");
for (unsigned i = 0; i < num; ++i) {
- TU_LOG1(" Blk%u %u %u\n", i, blk->beg, blk->end);
+ TU_LOG1(" Blk%u %u %u\r\n", i, blk->beg, blk->end);
++blk;
}
}
@@ -560,7 +562,7 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne
static void process_setup_packet(uint8_t rhport)
{
- uint32_t *p = (uint32_t*)&_dcd.setup_packet;
+ uint32_t *p = (uint32_t*)(uintptr_t) &_dcd.setup_packet;
p[0] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
p[1] = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
@@ -590,11 +592,11 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
const unsigned mps = USBC_Readw(USBC_REG_TXMAXP(USBC0_BASE));
const unsigned len = TU_MIN(mps, rem);
uint8_t *buf = pipe->buf;
- // TU_LOG1(" %p mps %d len %d rem %d\n", buf, mps, len, rem);
+ // TU_LOG1(" %p mps %d len %d rem %d\r\n", buf, mps, len, rem);
if (len) {
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_IN);
+ pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t) buf, addr, len, TUSB_DIR_IN);
} else {
pipe_write_packet(buf, addr, len);
pipe->buf = buf + len;
@@ -602,7 +604,7 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
pipe->remaining = rem - len;
}
__USBC_Dev_Tx_WriteDataComplete();
- // TU_LOG1(" TXCSRL%d = %x %d\n", epnum_minus1 + 1, regs->TXCSRL, rem - len);
+ // TU_LOG1(" TXCSRL%d = %x %d\r\n", epnum_minus1 + 1, regs->TXCSRL, rem - len);
return false;
}
@@ -610,7 +612,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
{
unsigned epnum_minus1 = tu_edpt_number(ep_addr) - 1;
pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1];
- // TU_LOG1(" RXCSRL%d = %x\n", epnum_minus1 + 1, regs->RXCSRL);
+ // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, regs->RXCSRL);
TU_ASSERT(__USBC_Dev_Rx_IsReadDataReady());
@@ -622,7 +624,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
if (len) {
volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
- pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_OUT);
+ pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t )buf, addr, len, TUSB_DIR_OUT);
} else {
pipe_read_packet(buf, addr, len);
pipe->buf = buf + len;
@@ -677,14 +679,14 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
* may have already finished and received the next setup packet
* without calling this function, so we have no choice but to
* invoke the callback function of status packet here. */
- // TU_LOG1(" STATUS OUT CSRL0 = %x\n", CSRL0);
+ // TU_LOG1(" STATUS OUT CSRL0 = %x\r\n", CSRL0);
_dcd.status_out = 0;
if (req == REQUEST_TYPE_INVALID) {
dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
} else {
/* The next setup packet has already been received, it aborts
* invoking callback function to avoid confusing TUSB stack. */
- TU_LOG1("Drop CONTROL_STAGE_ACK\n");
+ TU_LOG1("Drop CONTROL_STAGE_ACK\r\n");
}
return true;
}
@@ -709,16 +711,16 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_
} else {
__USBC_Dev_ep0_WriteDataHalf();
}
- // TU_LOG1(" IN CSRL0 = %x\n", CSRL0);
+ // TU_LOG1(" IN CSRL0 = %x\r\n", CSRL0);
} else {
- // TU_LOG1(" OUT CSRL0 = %x\n", CSRL0);
+ // TU_LOG1(" OUT CSRL0 = %x\r\n", CSRL0);
_dcd.pipe0.buf = buffer;
_dcd.pipe0.length = len;
_dcd.pipe0.remaining = len;
__USBC_Dev_ep0_ReadDataHalf();
}
} else if (dir_in) {
- // TU_LOG1(" STATUS IN CSRL0 = %x\n", CSRL0);
+ // TU_LOG1(" STATUS IN CSRL0 = %x\r\n", CSRL0);
_dcd.pipe0.buf = NULL;
_dcd.pipe0.length = 0;
_dcd.pipe0.remaining = 0;
@@ -733,7 +735,7 @@ static void process_ep0(uint8_t rhport)
USBC_SelectActiveEp(0);
uint_fast8_t csrl = USBC_Readw(USBC_REG_CSR0(USBC0_BASE));
- // TU_LOG1(" EP0 CSRL0 = %x\n", csrl);
+ // TU_LOG1(" EP0 CSRL0 = %x\r\n", csrl);
if (csrl & USB_CSRL0_STALLED) {
/* Returned STALL packet to HOST. */
@@ -743,7 +745,7 @@ static void process_ep0(uint8_t rhport)
unsigned req = _dcd.setup_packet.bmRequestType;
if (csrl & USB_CSRL0_SETEND) {
- // TU_LOG1(" ABORT by the next packets\n");
+ // TU_LOG1(" ABORT by the next packets\r\n");
USBC_Dev_Ctrl_ClearSetupEnd();
if (req != REQUEST_TYPE_INVALID && _dcd.pipe0.buf) {
/* DATA stage was aborted by receiving STATUS or SETUP packet. */
@@ -819,14 +821,14 @@ static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr)
USBC_SelectActiveEp(epn);
if (dir_in) {
- // TU_LOG1(" TXCSRL%d = %x\n", epn_minus1 + 1, regs->TXCSRL);
+ // TU_LOG1(" TXCSRL%d = %x\r\n", epn_minus1 + 1, regs->TXCSRL);
if (__USBC_Dev_Tx_IsEpStall()) {
__USBC_Dev_Tx_ClearStall();
return;
}
completed = handle_xfer_in(ep_addr);
} else {
- // TU_LOG1(" RXCSRL%d = %x\n", epn_minus1 + 1, regs->RXCSRL);
+ // TU_LOG1(" RXCSRL%d = %x\r\n", epn_minus1 + 1, regs->RXCSRL);
if (__USBC_Dev_Rx_IsEpStall()) {
__USBC_Dev_Rx_ClearStall();
return;
@@ -865,8 +867,9 @@ static void usb_isr_handler(void) {
dcd_int_handler(0);
}
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+
dcd_disconnect(rhport);
USBC_HardwareReset();
USBC_PhyConfig();
@@ -893,6 +896,8 @@ void dcd_init(uint8_t rhport)
f1c100s_intc_set_isr(F1C100S_IRQ_USBOTG, usb_isr_handler);
dcd_connect(rhport);
+
+ return true;
}
// Connect by enabling internal pull-up resistor on D+/D-
@@ -996,7 +1001,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
if (dir_in) {
USBC_Writew(mps, USBC_REG_TXMAXP(USBC0_BASE));
- reg_val = (1 << USBC_BP_TXCSR_D_MODE)
+ reg_val = (1 << USBC_BP_TXCSR_D_MODE)
| (1 << USBC_BP_TXCSR_D_FLUSH_FIFO)
| (1 << USBC_BP_TXCSR_D_CLEAR_DATA_TOGGLE);
if (xfer == TUSB_XFER_ISOCHRONOUS)
@@ -1048,7 +1053,7 @@ void dcd_edpt_close_all(uint8_t rhport)
USBC_REG_TXCSR(USBC0_BASE));
USBC_Writew(0, USBC_REG_RXMAXP(USBC0_BASE));
- USBC_Writew((1 << USBC_BP_RXCSR_D_CLEAR_DATA_TOGGLE) | (1 << USBC_BP_RXCSR_D_FLUSH_FIFO),
+ USBC_Writew((1 << USBC_BP_RXCSR_D_CLEAR_DATA_TOGGLE) | (1 << USBC_BP_RXCSR_D_FLUSH_FIFO),
USBC_REG_RXCSR(USBC0_BASE));
USBC_Writew(0, USBC_REG_TXFIFOAD(USBC0_BASE));
@@ -1078,7 +1083,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
} else {
USBC_INT_DisableRxEp(epn);
USBC_Writew(0, USBC_REG_RXMAXP(USBC0_BASE));
- USBC_Writew((1 << USBC_BP_RXCSR_D_CLEAR_DATA_TOGGLE) | (1 << USBC_BP_RXCSR_D_FLUSH_FIFO),
+ USBC_Writew((1 << USBC_BP_RXCSR_D_CLEAR_DATA_TOGGLE) | (1 << USBC_BP_RXCSR_D_FLUSH_FIFO),
USBC_REG_RXCSR(USBC0_BASE));
USBC_Writew(0, USBC_REG_RXFIFOAD(USBC0_BASE));
@@ -1092,7 +1097,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
{
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\n", ep_addr, total_bytes);
+ // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
musb_int_mask();
@@ -1111,7 +1116,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
{
(void)rhport;
bool ret;
- // TU_LOG1("X %x %d\n", ep_addr, total_bytes);
+ // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes);
unsigned const epnum = tu_edpt_number(ep_addr);
TU_ASSERT(epnum);
diff --git a/src/portable/sunxi/musb_def.h b/src/portable/sunxi/musb_def.h
index 602b4f113..53da5ded2 100644
--- a/src/portable/sunxi/musb_def.h
+++ b/src/portable/sunxi/musb_def.h
@@ -93,7 +93,7 @@
#define USBC1_BASE 0x01c14000
#define USBC2_BASE 0x01c1E000
-//Some reg whithin musb
+//Some reg within musb
#define USBPHY_CLK_REG 0x01c200CC
#define USBPHY_CLK_RST_BIT 0
#define USBPHY_CLK_GAT_BIT 1
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index f5ecdfe4f..c461d9a79 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -31,59 +31,28 @@
#if CFG_TUD_ENABLED && defined(TUP_USBIP_DWC2)
-#include "device/dcd.h"
-#include "dwc2_type.h"
-
-// Following symbols must be defined by port header
-// - _dwc2_controller[]: array of controllers
-// - DWC2_EP_MAX: largest EP counts of all controllers
-// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
-// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
-// - dwc2_remote_wakeup_delay
-
-#if defined(TUP_USBIP_DWC2_STM32)
- #include "dwc2_stm32.h"
-#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
- #include "dwc2_esp32.h"
-#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
- #include "dwc2_gd32.h"
-#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
- #include "dwc2_bcm.h"
-#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
- #include "dwc2_efm32.h"
-#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
- #include "dwc2_xmc.h"
-#else
- #error "Unsupported MCUs"
+#if !(CFG_TUD_DWC2_SLAVE_ENABLE || CFG_TUD_DWC2_DMA_ENABLE)
+#error DWC2 require either CFG_TUD_DWC2_SLAVE_ENABLE or CFG_TUD_DWC2_DMA_ENABLE to be enabled
#endif
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM
-//--------------------------------------------------------------------+
-
-// DWC2 registers
-#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base)
-
// Debug level for DWC2
#define DWC2_DEBUG 2
-#ifndef dcache_clean
-#define dcache_clean(_addr, _size)
-#endif
-
-#ifndef dcache_invalidate
-#define dcache_invalidate(_addr, _size)
-#endif
+#include "device/dcd.h"
+#include "dwc2_common.h"
-#ifndef dcache_clean_invalidate
-#define dcache_clean_invalidate(_addr, _size)
+#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX
+#else
+ #define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep + 1)
#endif
-static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
-
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM
+//--------------------------------------------------------------------+
typedef struct {
- uint8_t * buffer;
- tu_fifo_t * ff;
+ uint8_t* buffer;
+ tu_fifo_t* ff;
uint16_t total_len;
uint16_t max_size;
uint8_t interval;
@@ -92,431 +61,366 @@ typedef struct {
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
-// EP0 transfers are limited to 1 packet - larger sizes has to be split
-static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
-
-// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz
-static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs)
-static bool _out_ep_closed; // Flag to check if RX FIFO size needs an update (reduce its size)
+typedef struct {
+ // EP0 transfers are limited to 1 packet - larger sizes has to be split
+ uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
+ uint16_t dfifo_top; // top free location in DFIFO in words
-// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
-static bool _sof_en;
+ // Number of IN endpoints active
+ uint8_t allocated_epin_count;
-// Calculate the RX FIFO size according to recommendations from reference manual
-static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count)
-{
- return 15 + 2*(max_ep_size/4) + 2*ep_count;
-}
+ // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
+ bool sof_en;
+} dcd_data_t;
-static void update_grxfsiz(uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+static dcd_data_t _dcd_data;
- // Determine largest EP size for RX FIFO
- uint16_t max_epsize = 0;
- for (uint8_t epnum = 0; epnum < ep_count; epnum++)
- {
- max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
- }
+CFG_TUD_MEM_SECTION static struct {
+ TUD_EPBUF_DEF(setup_packet, 8);
+} _dcd_usbbuf;
- // Update size of RX FIFO
- dwc2->grxfsiz = calc_grxfsiz(max_epsize, ep_count);
+//--------------------------------------------------------------------
+// DMA
+//--------------------------------------------------------------------
+#if CFG_TUD_MEM_DCACHE_ENABLE
+bool dcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
}
-// Start of Bus Reset
-static void bus_reset(uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+bool dcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
+}
- tu_memclr(xfer_status, sizeof(xfer_status));
- _out_ep_closed = false;
+bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
- _sof_en = false;
+TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) {
+ (void) dwc2;
+ // Internal DMA only
+ return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+}
- // clear device address
- dwc2->dcfg &= ~DCFG_DAD_Msk;
+static void dma_setup_prepare(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- // 1. NAK for all OUT endpoints
- for ( uint8_t n = 0; n < ep_count; n++ )
- {
- dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
+ if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) {
+ if(dwc2->epout[0].doepctl & DOEPCTL_EPENA) {
+ return;
+ }
}
- // 2. Set up interrupt mask
- dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
- dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
- dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
+ // Receive only 1 packet
+ dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
+ dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet;
+ dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
+}
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start.
- // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located
- // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard
- // configuration done below.
- //
- // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed.
- // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a
- // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually
- // opened when the host sends an additional command: setInterface. At this point in time
- // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size
- // an additional memory
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO 0 |
- // --------------- (320 or 1024) - 16
- // | IN FIFO 1 |
- // --------------- (320 or 1024) - 16 - x
- // | . . . . |
- // --------------- (320 or 1024) - 16 - x - y - ... - z
- // | IN FIFO MAX |
- // ---------------
- // | FREE |
- // --------------- 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
- // - 13 for setup packets + control words (up to 3 setup packets).
- // - 1 for global NAK (not required/used here).
- // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1"
- // - 2 for each used OUT endpoint
- //
- // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum
- // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count
- // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count
- //
- // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
- // of the overall picture yet. We will use the worst scenario: largest possible + ep_count
- //
- // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
- // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to
- // overwrite this.
+//--------------------------------------------------------------------+
+// Data FIFO
+//--------------------------------------------------------------------+
- // EP0 out max is 64
- dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
- // Setup the control endpoint 0
- _allocated_fifo_words_tx = 16;
+/* Device Data FIFO scheme
- // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
- dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx);
+ The FIFO is split up into
+ - EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called
+ EP_LOC_CNT = ep_fifo_size - ghwcfg3.dfifo_depth. For value less than EP_LOC_CNT, gdfifocfg must be configured before
+ gahbcfg.dmaen is set
+ - Buffer mode: 1 word per endpoint direction
+ - Scatter/Gather DMA: 4 words per endpoint direction
+ - TX FIFO: one fifo for each IN endpoint. Size is dynamic depending on packet size, starting from top with EP0 IN.
+ - Shared RX FIFO: a shared fifo for all OUT endpoints. Typically, can hold up to 2 packets of the largest EP size.
- // Fixed control EP0 size to 64 bytes
- dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
- xfer_status[0][TUSB_DIR_OUT].max_size = 64;
- xfer_status[0][TUSB_DIR_IN ].max_size = 64;
+ We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
+ possible since the free space is located between the RX and TX FIFOs.
- dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
+ ---------------- ep_fifo_size
+ | DxEPIDMAn |
+ |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ | IN FIFO 0 | control EP
+ |-------------|
+ | IN FIFO 1 |
+ |-------------|
+ | . . . . |
+ |-------------|
+ | IN FIFO n |
+ |-------------|
+ | FREE |
+ |-------------|-- GRXFSIZ (expandable)
+ | OUT FIFO |
+ | ( Shared ) |
+ --------------- 0
- dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
-}
+ 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
+ - All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula):
+ - 13 for setup packets + control words (up to 3 setup packets).
+ - 1 for global NAK (not required/used here).
+ - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)"
+ - 2 for each used OUT endpoint
-static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, uint16_t total_bytes)
-{
- (void) rhport;
+ Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum
+*/
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
-
- // EP0 is limited to one packet each xfer
- // We use multiple transaction of xfer->max_size length to get a whole transfer done
- if ( epnum == 0 )
- {
- xfer_ctl_t *const xfer = XFER_CTL_BASE(epnum, dir);
- total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
- ep0_pending[dir] -= total_bytes;
- }
+TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) {
+ return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count;
+}
- // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here.
- if ( dir == TUSB_DIR_IN )
- {
- dwc2_epin_t* epin = dwc2->epin;
+static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
+ const uint8_t ep_count = dwc2_controller->ep_count;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
- // A full IN transfer (multiple packets, possibly) triggers XFRC.
- epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
+ TU_ASSERT(epnum < ep_count);
- epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
+ uint16_t fifo_size = tu_div_ceil(packet_size, 4);
+ if (dir == TUSB_DIR_OUT) {
+ // Calculate required size of RX FIFO
+ const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count);
- // For ISO endpoint set correct odd/even bit for next frame.
- if ( (epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1 )
- {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
+ // If size_rx needs to be extended check if there is enough free space
+ if (dwc2->grxfsiz < new_sz) {
+ TU_ASSERT(new_sz <= _dcd_data.dfifo_top);
+ dwc2->grxfsiz = new_sz; // Enlarge RX FIFO
}
- // Enable fifo empty interrupt only if there are something to put in the fifo.
- if ( total_bytes != 0 )
- {
- dwc2->diepempmsk |= (1 << epnum);
+ } else {
+ // Check IN endpoints concurrently active limit
+ if(dwc2_controller->ep_in_count) {
+ TU_ASSERT(_dcd_data.allocated_epin_count < dwc2_controller->ep_in_count);
+ _dcd_data.allocated_epin_count++;
}
- }
- else
- {
- dwc2_epout_t* epout = dwc2->epout;
- // A full OUT transfer (multiple packets, possibly) triggers XFRC.
- epout[epnum].doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ);
- epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) |
- ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk);
-
- epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
- if ( (epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 &&
- XFER_CTL_BASE(epnum, dir)->interval == 1 )
- {
- // Take odd/even bit from frame counter.
- uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
- epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk);
+ // If The TXFELVL is configured as half empty, the fifo must be twice the max_size.
+ if ((dwc2->gahbcfg & GAHBCFG_TX_FIFO_EPMTY_LVL) == 0) {
+ fifo_size *= 2;
}
- }
-}
-/*------------------------------------------------------------------*/
-/* Controller API
- *------------------------------------------------------------------*/
-#if CFG_TUSB_DEBUG >= DWC2_DEBUG
-void print_dwc2_info(dwc2_regs_t * dwc2)
-{
- dwc2_ghwcfg2_t const * hw_cfg2 = &dwc2->ghwcfg2_bm;
- dwc2_ghwcfg3_t const * hw_cfg3 = &dwc2->ghwcfg3_bm;
- dwc2_ghwcfg4_t const * hw_cfg4 = &dwc2->ghwcfg4_bm;
-
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg);
- TU_LOG_HEX(DWC2_DEBUG, dwc2->guid);
- TU_LOG_HEX(DWC2_DEBUG, dwc2->gsnpsid);
- TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg1);
-
- // HW configure 2
- TU_LOG(DWC2_DEBUG, "\r\n");
- TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg2);
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->op_mode );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->arch );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->point2point );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->hs_phy_type );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->fs_phy_type );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->num_dev_ep );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->num_host_ch );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->period_channel_support );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->enable_dynamic_fifo );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->mul_cpu_int );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->nperiod_tx_q_depth );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->host_period_tx_q_depth );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->dev_token_q_depth );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg2->otg_enable_ic_usb );
+ // Check if free space is available
+ TU_ASSERT(_dcd_data.dfifo_top >= fifo_size + dwc2->grxfsiz);
+ _dcd_data.dfifo_top -= fifo_size;
+ // TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, dfifo_top);
- // HW configure 3
- TU_LOG(DWC2_DEBUG, "\r\n");
- TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg3);
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->xfer_size_width );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->packet_size_width );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_enable );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->i2c_enable );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->vendor_ctrl_itf );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->optional_feature_removed );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->synch_reset );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_adp_support );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->otg_enable_hsic );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->battery_charger_support );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->lpm_mode );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg3->total_fifo_size );
+ // Both TXFD and TXSA are in unit of 32-bit words.
+ if (epnum == 0) {
+ dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dcd_data.dfifo_top;
+ } else {
+ // DIEPTXF starts at FIFO #1.
+ dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dcd_data.dfifo_top;
+ }
+ }
- // HW configure 4
- TU_LOG(DWC2_DEBUG, "\r\n");
- TU_LOG_HEX(DWC2_DEBUG, dwc2->ghwcfg4);
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->num_dev_period_in_ep );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->power_optimized );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->ahb_freq_min );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->hibernation );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->service_interval_mode );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->ipg_isoc_en );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->acg_enable );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->utmi_phy_data_width );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dev_ctrl_ep_num );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->iddg_filter_enabled );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->vbus_valid_filter_enabled );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->a_valid_filter_enabled );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->b_valid_filter_enabled );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dedicated_fifos );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->num_dev_in_eps );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dma_desc_enable );
- TU_LOG_INT(DWC2_DEBUG, hw_cfg4->dma_dynamic );
+ return true;
}
-#endif
-
-static void reset_core(dwc2_regs_t * dwc2)
-{
- // reset core
- dwc2->grstctl |= GRSTCTL_CSRST;
- // wait for reset bit is cleared
- // TODO version 4.20a should wait for RESET DONE mask
- while (dwc2->grstctl & GRSTCTL_CSRST) { }
+static void dfifo_device_init(uint8_t rhport) {
+ const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count);
- // wait for AHB master IDLE
- while ( !(dwc2->grstctl & GRSTCTL_AHBIDL) ) { }
-
- // wait for device mode ?
-}
+ // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction
+ const bool is_dma = dma_device_enabled(dwc2);
+ _dcd_data.dfifo_top = dwc2_controller->ep_fifo_size/4;
+ if (is_dma) {
+ _dcd_data.dfifo_top -= 2 * dwc2_controller->ep_count;
+ }
+ dwc2->gdfifocfg = (_dcd_data.dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dcd_data.dfifo_top;
-static bool phy_hs_supported(dwc2_regs_t * dwc2)
-{
- // note: esp32 incorrect report its hs_phy_type as utmi
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
- return false;
-#else
- return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
-#endif
+ // Allocate FIFO for EP0 IN
+ dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE);
}
-static void phy_fs_init(dwc2_regs_t * dwc2)
-{
- TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n");
-
- // Select FS PHY
- dwc2->gusbcfg |= GUSBCFG_PHYSEL;
- // MCU specific PHY init before reset
- dwc2_phy_init(dwc2, HS_PHY_TYPE_NONE);
+//--------------------------------------------------------------------
+// Endpoint
+//--------------------------------------------------------------------
+static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint_desc) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
+ const uint8_t dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
- // Reset core after selecting PHY
- reset_core(dwc2);
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
+ xfer->interval = p_endpoint_desc->bInterval;
- // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
- // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
- // these bits can be programmed to a larger value. Default is 5
- dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos);
+ // Endpoint control
+ union {
+ uint32_t value;
+ dwc2_depctl_t bm;
+ } depctl;
+ depctl.value = 0;
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, HS_PHY_TYPE_NONE);
+ depctl.bm.mps = xfer->max_size;
+ depctl.bm.active = 1;
+ depctl.bm.type = p_endpoint_desc->bmAttributes.xfer;
+ if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+ depctl.bm.set_data0_iso_even = 1;
+ }
+ if (dir == TUSB_DIR_IN) {
+ depctl.bm.tx_fifo_num = epnum;
+ }
- // set max speed
- dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
+ dep->ctl = depctl.value;
+ dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir));
}
-static void phy_hs_init(dwc2_regs_t * dwc2)
-{
- uint32_t gusbcfg = dwc2->gusbcfg;
+static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
+ (void) rhport;
- // De-select FS PHY
- gusbcfg &= ~GUSBCFG_PHYSEL;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const uint8_t dir = tu_edpt_dir(ep_addr);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
- if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI)
- {
- TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n");
+ if (dir == TUSB_DIR_IN) {
+ // Only disable currently enabled non-control endpoint
+ if ((epnum == 0) || !(dep->diepctl & DIEPCTL_EPENA)) {
+ dep->diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
+ } else {
+ // Stop transmitting packets and NAK IN xfers.
+ dep->diepctl |= DIEPCTL_SNAK;
+ while ((dep->diepint & DIEPINT_INEPNE) == 0) {}
- // Select ULPI
- gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+ // Disable the endpoint.
+ dep->diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
+ while ((dep->diepint & DIEPINT_EPDISD_Msk) == 0) {}
- // ULPI 8-bit interface, single data rate
- gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
+ dep->diepint = DIEPINT_EPDISD;
+ }
- // default internal VBUS Indicator and Drive
- gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
+ // Flush the FIFO, and wait until we have confirmed it cleared.
+ dfifo_flush_tx(dwc2, epnum);
+ } else {
+ // Only disable currently enabled non-control endpoint
+ if ((epnum == 0) || !(dep->doepctl & DOEPCTL_EPENA)) {
+ dep->doepctl |= stall ? DOEPCTL_STALL : 0;
+ } 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.
+ dwc2->dctl |= DCTL_SGONAK;
+ while ((dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0) {}
- // Disable FS/LS ULPI
- gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
- }else
- {
- TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n");
+ // Ditto here disable the endpoint.
+ dep->doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
+ while ((dep->doepint & DOEPINT_EPDISD_Msk) == 0) {}
- // Select UTMI+ with 8-bit interface
- gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
+ dep->doepint = DOEPINT_EPDISD;
- // Set 16-bit interface if supported
- if (dwc2->ghwcfg4_bm.utmi_phy_data_width) gusbcfg |= GUSBCFG_PHYIF16;
+ // Allow other OUT endpoints to keep receiving.
+ dwc2->dctl |= DCTL_CGONAK;
+ }
}
+}
- // Apply config
- dwc2->gusbcfg = gusbcfg;
-
- // mcu specific phy init
- dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
- // Reset core after selecting PHY
- reset_core(dwc2);
+ uint16_t num_packets;
+ uint16_t total_bytes;
- // Set turn-around, must after core reset otherwise it will be clear
- // - 9 if using 8-bit PHY interface
- // - 5 if using 16-bit PHY interface
- gusbcfg &= ~GUSBCFG_TRDT_Msk;
- gusbcfg |= (dwc2->ghwcfg4_bm.utmi_phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
- dwc2->gusbcfg = gusbcfg;
+ // EP0 is limited to one packet per xfer
+ if (epnum == 0) {
+ total_bytes = tu_min16(_dcd_data.ep0_pending[dir], xfer->max_size);
+ _dcd_data.ep0_pending[dir] -= total_bytes;
+ num_packets = 1;
+ } else {
+ total_bytes = xfer->total_len;
+ num_packets = tu_div_ceil(total_bytes, xfer->max_size);
+ if (num_packets == 0) {
+ num_packets = 1; // zero length packet still count as 1
+ }
+ }
- // MCU specific PHY update post reset
- dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+ // transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC.
+ union {
+ uint32_t value;
+ dwc2_ep_tsize_t bm;
+ } deptsiz;
+ deptsiz.value = 0;
+ deptsiz.bm.xfer_size = total_bytes;
+ deptsiz.bm.packet_count = num_packets;
- // Set max speed
- uint32_t dcfg = dwc2->dcfg;
- dcfg &= ~DCFG_DSPD_Msk;
- dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
+ dep->tsiz = deptsiz.value;
- // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
- // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
- if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) dcfg |= DCFG_XCVRDLY;
+ // control
+ union {
+ dwc2_depctl_t bm;
+ uint32_t value;
+ } depctl;
+ depctl.value = dep->ctl;
- dwc2->dcfg = dcfg;
-}
-
-static bool check_dwc2(dwc2_regs_t * dwc2)
-{
-#if CFG_TUSB_DEBUG >= DWC2_DEBUG
- print_dwc2_info(dwc2);
-#endif
+ depctl.bm.clear_nak = 1;
+ depctl.bm.enable = 1;
+ if (depctl.bm.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
+ const uint32_t odd_now = (dwc2->dsts_bm.frame_number & 1u);
+ if (odd_now) {
+ depctl.bm.set_data0_iso_even = 1;
+ } else {
+ depctl.bm.set_data1_iso_odd = 1;
+ }
+ }
- // For some reasons: GD32VF103 snpsid and all hwcfg register are always zero (skip it)
- (void) dwc2;
-#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
- uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
- TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
-#endif
+ const bool is_dma = dma_device_enabled(dwc2);
+ if(is_dma) {
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dcd_dcache_clean(xfer->buffer, total_bytes);
+ }
+ dep->diepdma = (uintptr_t) xfer->buffer;
+ dep->diepctl = depctl.value; // enable endpoint
+ } else {
+ dep->diepctl = depctl.value; // enable endpoint
- return true;
+ // Enable tx fifo empty interrupt only if there is data. Note must after depctl enable
+ if (dir == TUSB_DIR_IN && total_bytes != 0) {
+ dwc2->diepempmsk |= (1 << epnum);
+ }
+ }
}
-void dcd_init (uint8_t rhport)
-{
- // Programming model begins in the last section of the chapter on the USB
- // peripheral in each Reference Manual.
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+//--------------------------------------------------------------------
+// Controller API
+//--------------------------------------------------------------------
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- // Check Synopsys ID register, failed if controller clock/power is not enabled
- TU_VERIFY(check_dwc2(dwc2), );
+ tu_memclr(&_dcd_data, sizeof(_dcd_data));
- dcd_disconnect(rhport);
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
+ const bool is_dma = dma_device_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
- // max number of endpoints & total_fifo_size are:
- // hw_cfg2->num_dev_ep, hw_cfg2->total_fifo_size
+ //------------- 7.1 Device Initialization -------------//
+ // Set device max speed
+ uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
+ if (is_highspeed) {
+ dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos;
- if( phy_hs_supported(dwc2) )
- {
- // Highspeed
- phy_hs_init(dwc2);
- }else
- {
- // core does not support highspeed or hs-phy is not present
- phy_fs_init(dwc2);
+ // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
+ // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ dcfg |= DCFG_XCVRDLY;
+ }
+ } else {
+ dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos;
}
- // Restart PHY clock
- dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE);
+ dcfg |= DCFG_NZLSOHSK; // send STALL back and discard if host send non-zlp during control status
+ dwc2->dcfg = dcfg;
- /* Set HS/FS Timeout Calibration to 7 (max available value).
- * The number of PHY clocks that the application programs in
- * this field is added to the high/full speed interpacket timeout
- * duration in the core to account for any additional delays
- * introduced by the PHY. This can be required, because the delay
- * introduced by the PHY in generating the linestate condition
- * can vary from one PHY to another.
- */
- dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
+ dcd_disconnect(rhport);
// Force device mode
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
@@ -524,58 +428,39 @@ void dcd_init (uint8_t rhport)
// Clear A override, force B Valid
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
- // If USB host misbehaves during status portion of control xfer
- // (non zero-length packet), send STALL back and discard.
- dwc2->dcfg |= DCFG_NZLSOHSK;
-
- // Clear all interrupts
- dwc2->gintsts |= dwc2->gintsts;
- dwc2->gotgint |= dwc2->gotgint;
-
- // Required as part of core initialization.
- // TODO: How should mode mismatch be handled? It will cause
- // the core to stop working/require reset.
- dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_MMISM | GINTMSK_RXFLVLM |
- GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
- // Enable global interrupt
- dwc2->gahbcfg |= GAHBCFG_GINT;
-
- // make sure we are in device mode
-// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), );
-
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg);
-// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg);
+ // TX FIFO empty level for interrupt is complete empty
+ uint32_t gahbcfg = dwc2->gahbcfg;
+ gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
dcd_connect(rhport);
+ return true;
}
-void dcd_int_enable (uint8_t rhport)
-{
+void dcd_int_enable(uint8_t rhport) {
dwc2_dcd_int_enable(rhport);
}
-void dcd_int_disable (uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
dwc2_dcd_int_disable(rhport);
}
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
dwc2->dcfg = (dwc2->dcfg & ~DCFG_DAD_Msk) | (dev_addr << DCFG_DAD_Pos);
// 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 dcd_remote_wakeup(uint8_t rhport) {
(void) rhport;
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// set remote wakeup
dwc2->dctl |= DCTL_RWUSIG;
@@ -590,35 +475,51 @@ void dcd_remote_wakeup(uint8_t rhport)
dwc2->dctl &= ~DCTL_RWUSIG;
}
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
(void) rhport;
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+#ifdef TUP_USBIP_DWC2_ESP32
+ usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
+ conf.pad_pull_override = 0;
+ conf.dp_pullup = 0;
+ conf.dp_pulldown = 0;
+ conf.dm_pullup = 0;
+ conf.dm_pulldown = 0;
+ USB_WRAP.otg_conf = conf;
+#endif
+
dwc2->dctl &= ~DCTL_SDIS;
}
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
(void) rhport;
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+#ifdef TUP_USBIP_DWC2_ESP32
+ usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf;
+ conf.pad_pull_override = 1;
+ conf.dp_pullup = 0;
+ conf.dp_pulldown = 1;
+ conf.dm_pullup = 0;
+ conf.dm_pulldown = 1;
+ USB_WRAP.otg_conf = conf;
+#endif
+
dwc2->dctl |= DCTL_SDIS;
}
// Be advised: audio, video and possibly other iso-ep classes use dcd_sof_enable() to enable/disable its corresponding ISR on purpose!
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- _sof_en = en;
+ _dcd_data.sof_en = en;
- if (en)
- {
+ if (en) {
dwc2->gintsts = GINTSTS_SOF;
dwc2->gintmsk |= GINTMSK_SOFM;
- }
- else
- {
+ } else {
dwc2->gintmsk &= ~GINTMSK_SOFM;
}
}
@@ -627,144 +528,66 @@ void dcd_sof_enable(uint8_t rhport, bool en)
/* DCD Endpoint port
*------------------------------------------------------------------*/
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
-{
- (void) rhport;
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+ TU_ASSERT(dfifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt)));
+ edpt_activate(rhport, desc_edpt);
+ return true;
+}
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+// Close all non-control endpoints, cancel all pending transfers if any.
+void dcd_edpt_close_all(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
- uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+ _dcd_data.allocated_epin_count = 0;
- TU_ASSERT(epnum < ep_count);
-
- xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
- xfer->max_size = tu_edpt_packet_size(desc_edpt);
- xfer->interval = desc_edpt->bInterval;
-
- uint16_t const fifo_size = tu_div_ceil(xfer->max_size, 4);
-
- if(dir == TUSB_DIR_OUT)
- {
- // Calculate required size of RX FIFO
- uint16_t const sz = calc_grxfsiz(4*fifo_size, ep_count);
-
- // If size_rx needs to be extended check if possible and if so enlarge it
- if (dwc2->grxfsiz < sz)
- {
- TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size/4);
+ // Disable non-control interrupt
+ dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos);
- // Enlarge RX FIFO
- dwc2->grxfsiz = sz;
+ for (uint8_t n = 1; n < ep_count; n++) {
+ for (uint8_t d = 0; d < 2; d++) {
+ dwc2_dep_t* dep = &dwc2->ep[d][n];
+ if (dep->ctl & EPCTL_EPENA) {
+ dep->ctl |= EPCTL_SNAK | EPCTL_EPDIS;
+ }
+ xfer_status[n][1-d].max_size = 0;
}
-
- dwc2->epout[epnum].doepctl |= (1 << DOEPCTL_USBAEP_Pos) |
- (desc_edpt->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
- (xfer->max_size << DOEPCTL_MPSIZ_Pos);
-
- dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
}
- else
- {
- // "USB Data FIFOs" section in reference manual
- // Peripheral FIFO architecture
- //
- // --------------- 320 or 1024 ( 1280 or 4096 bytes )
- // | IN FIFO 0 |
- // --------------- (320 or 1024) - 16
- // | IN FIFO 1 |
- // --------------- (320 or 1024) - 16 - x
- // | . . . . |
- // --------------- (320 or 1024) - 16 - x - y - ... - z
- // | IN FIFO MAX |
- // ---------------
- // | FREE |
- // --------------- GRXFSIZ
- // | OUT FIFO |
- // | ( Shared ) |
- // --------------- 0
- //
- // In FIFO is allocated by following rules:
- // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
-
- // Check if free space is available
- TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size/4);
-
- _allocated_fifo_words_tx += fifo_size;
- TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %lu", fifo_size*4, _dwc2_controller[rhport].ep_fifo_size-_allocated_fifo_words_tx*4);
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
- // DIEPTXF starts at FIFO #1.
- // Both TXFD and TXSA are in unit of 32-bit words.
- dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | (_dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx);
-
- dwc2->epin[epnum].diepctl |= (1 << DIEPCTL_USBAEP_Pos) |
- (epnum << DIEPCTL_TXFNUM_Pos) |
- (desc_edpt->bmAttributes.xfer << DIEPCTL_EPTYP_Pos) |
- (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DIEPCTL_SD0PID_SEVNFRM : 0) |
- (xfer->max_size << DIEPCTL_MPSIZ_Pos);
-
- dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
- }
+ dfifo_device_init(rhport); // re-init dfifo
+}
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ TU_ASSERT(dfifo_alloc(rhport, ep_addr, largest_packet_size));
return true;
}
-// Close all non-control endpoints, cancel all pending transfers if any.
-void dcd_edpt_close_all (uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
-
- // Disable non-control interrupt
- dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos);
-
- for(uint8_t n = 1; n < ep_count; n++)
- {
- // disable OUT endpoint
- dwc2->epout[n].doepctl = 0;
- xfer_status[n][TUSB_DIR_OUT].max_size = 0;
-
- // disable IN endpoint
- dwc2->epin[n].diepctl = 0;
- xfer_status[n][TUSB_DIR_IN].max_size = 0;
- }
-
- // reset allocated fifo IN
- _allocated_fifo_words_tx = 16;
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
+ // Disable EP to clear potential incomplete transfers
+ edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false);
+ edpt_activate(rhport, p_endpoint_desc);
+ return true;
}
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
- xfer->buffer = buffer;
- xfer->ff = NULL;
- xfer->total_len = total_bytes;
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = buffer;
+ xfer->ff = NULL;
+ xfer->total_len = total_bytes;
// EP0 can only handle one packet
- if(epnum == 0)
- {
- ep0_pending[dir] = total_bytes;
-
- // Schedule the first transaction for EP0 transfer
- edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]);
+ if (epnum == 0) {
+ _dcd_data.ep0_pending[dir] = total_bytes;
}
- else
- {
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
- // Zero-size packet is special case.
- if ( (short_packet_size > 0) || (total_bytes == 0) ) num_packets++;
-
- // Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
- }
+ // Schedule packets to be sent within interrupt
+ edpt_schedule_packets(rhport, epnum, dir);
return true;
}
@@ -773,496 +596,415 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t total_bytes) {
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
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 = NULL;
- xfer->ff = ff;
- xfer->total_len = total_bytes;
-
- uint16_t num_packets = (total_bytes / xfer->max_size);
- uint16_t const short_packet_size = total_bytes % xfer->max_size;
+ uint8_t const dir = tu_edpt_dir(ep_addr);
- // Zero-size packet is special case.
- if ( short_packet_size > 0 || (total_bytes == 0) ) num_packets++;
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
+ xfer->buffer = NULL;
+ xfer->ff = ff;
+ xfer->total_len = total_bytes;
// Schedule packets to be sent within interrupt
- edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
+ // TODO xfer fifo may only available for slave mode
+ edpt_schedule_packets(rhport, epnum, dir);
return true;
}
-static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
-{
- (void) rhport;
-
- dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ edpt_disable(rhport, ep_addr, true);
+ if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) {
+ dma_setup_prepare(rhport);
+ }
+}
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ uint8_t const dir = tu_edpt_dir(ep_addr);
+ dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
- if ( dir == TUSB_DIR_IN )
- {
- dwc2_epin_t* epin = dwc2->epin;
-
- // Only disable currently enabled non-control endpoint
- if ( (epnum == 0) || !(epin[epnum].diepctl & DIEPCTL_EPENA) )
- {
- epin[epnum].diepctl |= DIEPCTL_SNAK | (stall ? DIEPCTL_STALL : 0);
- }
- else
- {
- // Stop transmitting packets and NAK IN xfers.
- epin[epnum].diepctl |= DIEPCTL_SNAK;
- while ( (epin[epnum].diepint & DIEPINT_INEPNE) == 0 ) {}
-
- // Disable the endpoint.
- epin[epnum].diepctl |= DIEPCTL_EPDIS | (stall ? DIEPCTL_STALL : 0);
- while ( (epin[epnum].diepint & DIEPINT_EPDISD_Msk) == 0 ) {}
+ // Clear stall and reset data toggle
+ dep->ctl &= ~EPCTL_STALL;;
+ dep->ctl |= EPCTL_SD0PID_SEVNFRM;
+}
- epin[epnum].diepint = DIEPINT_EPDISD;
- }
+//--------------------------------------------------------------------
+// Interrupt Handler
+//--------------------------------------------------------------------
- // Flush the FIFO, and wait until we have confirmed it cleared.
- dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
- while ( (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0 ) {}
- }
- else
- {
- dwc2_epout_t* epout = dwc2->epout;
+// 7.4.1 Initialization on USB Reset
+static void handle_bus_reset(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
- // Only disable currently enabled non-control endpoint
- if ( (epnum == 0) || !(epout[epnum].doepctl & DOEPCTL_EPENA) )
- {
- epout[epnum].doepctl |= stall ? DOEPCTL_STALL : 0;
- }
- 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.
- dwc2->dctl |= DCTL_SGONAK;
- while ( (dwc2->gintsts & GINTSTS_BOUTNAKEFF_Msk) == 0 ) {}
+ tu_memclr(xfer_status, sizeof(xfer_status));
- // Ditto here- disable the endpoint.
- epout[epnum].doepctl |= DOEPCTL_EPDIS | (stall ? DOEPCTL_STALL : 0);
- while ( (epout[epnum].doepint & DOEPINT_EPDISD_Msk) == 0 ) {}
+ _dcd_data.sof_en = false;
+ _dcd_data.allocated_epin_count = 0;
- epout[epnum].doepint = DOEPINT_EPDISD;
+ // 1. NAK for all OUT endpoints
+ for (uint8_t n = 0; n < ep_count; n++) {
+ dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
+ }
- // Allow other OUT endpoints to keep receiving.
- dwc2->dctl |= DCTL_CGONAK;
+ // Disable all IN endpoints
+ for (uint8_t n = 0; n < ep_count; n++) {
+ if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
+ dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
}
}
-}
-/**
- * Close an endpoint.
- */
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
+ // 2. Set up interrupt mask for EP0
+ dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
+ dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
+ dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
+ // 4. Set up DFIFO
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+ dfifo_device_init(rhport);
- dcd_edpt_disable(rhport, ep_addr, false);
+ // 5. Reset device address
+ dwc2->dcfg_bm.address = 0;
- // Update max_size
- xfer_status[epnum][dir].max_size = 0; // max_size = 0 marks a disabled EP - required for changing FIFO allocation
+ // Fixed both control EP0 size to 64 bytes
+ dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
+ dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos);
- if (dir == TUSB_DIR_IN)
- {
- uint16_t const fifo_size = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXFD_Msk) >> DIEPTXF_INEPTXFD_Pos;
- uint16_t const fifo_start = (dwc2->dieptxf[epnum - 1] & DIEPTXF_INEPTXSA_Msk) >> DIEPTXF_INEPTXSA_Pos;
+ xfer_status[0][TUSB_DIR_OUT].max_size = 64;
+ xfer_status[0][TUSB_DIR_IN].max_size = 64;
- // For now only the last opened endpoint can be closed without fuss.
- TU_ASSERT(fifo_start == _dwc2_controller[rhport].ep_fifo_size/4 - _allocated_fifo_words_tx,);
- _allocated_fifo_words_tx -= fifo_size;
- }
- else
- {
- _out_ep_closed = true; // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
+ if(dma_device_enabled(dwc2)) {
+ dma_setup_prepare(rhport);
+ } else {
+ dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
}
-}
-
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
- dcd_edpt_disable(rhport, ep_addr, true);
-}
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
- (void) rhport;
-
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
-
- uint8_t const epnum = tu_edpt_number(ep_addr);
- uint8_t const dir = tu_edpt_dir(ep_addr);
-
- // Clear stall and reset data toggle
- if ( dir == TUSB_DIR_IN )
- {
- dwc2->epin[epnum].diepctl &= ~DIEPCTL_STALL;
- dwc2->epin[epnum].diepctl |= DIEPCTL_SD0PID_SEVNFRM;
- }
- else
- {
- dwc2->epout[epnum].doepctl &= ~DOEPCTL_STALL;
- dwc2->epout[epnum].doepctl |= DOEPCTL_SD0PID_SEVNFRM;
- }
+ dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
}
-/*------------------------------------------------------------------*/
-
-// Read a single data packet from receive FIFO
-static void read_fifo_packet(uint8_t rhport, uint8_t * dst, uint16_t len)
-{
- (void) rhport;
+static void handle_enum_done(uint8_t rhport) {
+ dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+ tusb_speed_t speed;
+ switch (dwc2->dsts_bm.enum_speed) {
+ case DCFG_SPEED_HIGH:
+ speed = TUSB_SPEED_HIGH;
+ break;
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- volatile const uint32_t * rx_fifo = dwc2->fifo[0];
+ case DCFG_SPEED_LOW:
+ speed = TUSB_SPEED_LOW;
+ break;
- // Reading full available 32 bit words from fifo
- uint16_t full_words = len >> 2;
- while(full_words--)
- {
- tu_unaligned_write32(dst, *rx_fifo);
- dst += 4;
+ case DCFG_SPEED_FULL_30_60MHZ:
+ case DCFG_SPEED_FULL_48MHZ:
+ default:
+ speed = TUSB_SPEED_FULL;
+ break;
}
- // Read the remaining 1-3 bytes from fifo
- uint8_t const bytes_rem = len & 0x03;
- if ( bytes_rem != 0 )
- {
- uint32_t const tmp = *rx_fifo;
- dst[0] = tu_u32_byte0(tmp);
- if ( bytes_rem > 1 ) dst[1] = tu_u32_byte1(tmp);
- if ( bytes_rem > 2 ) dst[2] = tu_u32_byte2(tmp);
- }
+ // TODO must update GUSBCFG_TRDT according to link speed
+ dcd_event_bus_reset(rhport, speed, true);
}
-// Write a single data packet to EPIN FIFO
-static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const * src, uint16_t len)
-{
- (void) rhport;
-
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- volatile uint32_t * tx_fifo = dwc2->fifo[fifo_num];
-
- // Pushing full available 32 bit words to fifo
- uint16_t full_words = len >> 2;
- while(full_words--)
- {
- *tx_fifo = tu_unaligned_read32(src);
- src += 4;
- }
+#if 0
+TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) {
+ const char* str[] = {
+ "XFRC", "DIS", "AHBERR", "SETUP_DONE",
+ "ORXED", "STATUS_RX", "SETUP_B2B", "RSV7",
+ "OPERR", "BNA", "RSV10", "ISODROP",
+ "BBLERR", "NAK", "NYET", "SETUP_RX"
+ };
- // Write the remaining 1-3 bytes into fifo
- uint8_t const bytes_rem = len & 0x03;
- if ( bytes_rem )
- {
- uint32_t tmp_word = src[0];
- if ( bytes_rem > 1 ) tmp_word |= (src[1] << 8);
- if ( bytes_rem > 2 ) tmp_word |= (src[2] << 16);
-
- *tx_fifo = tmp_word;
+ for(uint32_t i=0; i<TU_ARRAY_SIZE(str); i++) {
+ if (doepint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
}
+ TU_LOG1("\r\n");
}
+#endif
-static void handle_rxflvl_irq(uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- volatile uint32_t const * rx_fifo = dwc2->fifo[0];
+#if CFG_TUD_DWC2_SLAVE_ENABLE
+// Process shared receive FIFO, this interrupt is only used in Slave mode
+static void handle_rxflvl_irq(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
// Pop control word off FIFO
- uint32_t const ctl_word = dwc2->grxstsp;
- uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk ) >> GRXSTSP_PKTSTS_Pos;
- uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk ) >> GRXSTSP_EPNUM_Pos;
- uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk ) >> GRXSTSP_BCNT_Pos;
-
- dwc2_epout_t* epout = &dwc2->epout[epnum];
+ const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
+ const uint8_t epnum = grxstsp_bm.ep_ch_num;
-//#if CFG_TUSB_DEBUG >= DWC2_DEBUG
-// const char * pktsts_str[] =
-// {
-// "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)",
-// "Setup Complete (ISR)", "ASSERT", "Setup Data Received"
-// };
-// TU_LOG_LOCATION();
-// TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]);
-// TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04X\r\n", (unsigned long) dwc2->daint, (unsigned int) epout->doepint);
-//#endif
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
- switch ( pktsts )
- {
- // Global OUT NAK: do nothign
- case GRXSTS_PKTSTS_GLOBALOUTNAK: break;
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
+ // Global OUT NAK: do nothing
+ break;
- case GRXSTS_PKTSTS_SETUPRX:
+ case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
+ uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet;
+ // We can receive up to three setup packets in succession, but only the last one is valid.
+ setup[0] = (*rx_fifo);
+ setup[1] = (*rx_fifo);
+ break;
+ }
- // We can receive up to three setup packets in succession, but
- // only the last one is valid.
- _setup_packet[0] = (*rx_fifo);
- _setup_packet[1] = (*rx_fifo);
- break;
-
- case GRXSTS_PKTSTS_SETUPDONE:
- // Setup packet done (Interrupt)
+ case GRXSTS_PKTSTS_SETUP_DONE:
+ // Setup packet done:
+ // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
- break;
+ break;
- case GRXSTS_PKTSTS_OUTRX:
- {
+ case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
- xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+ const uint16_t byte_count = grxstsp_bm.byte_count;
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // Read packet off RxFIFO
- if ( xfer->ff )
- {
- // Ring buffer
- tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt);
- }
- else
- {
- // Linear buffer
- read_fifo_packet(rhport, xfer->buffer, bcnt);
-
- // Increment pointer to xfer data
- xfer->buffer += bcnt;
- }
+ if (byte_count) {
+ // Read packet off RxFIFO
+ if (xfer->ff) {
+ tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
+ } else {
+ dfifo_read_packet(dwc2, xfer->buffer, byte_count);
+ xfer->buffer += byte_count;
+ }
- // Truncate transfer length in case of short packet
- if ( bcnt < xfer->max_size )
- {
- xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos;
- if ( epnum == 0 )
- {
- xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
- ep0_pending[TUSB_DIR_OUT] = 0;
+ // short packet, minus remaining bytes (xfer_size)
+ if (byte_count < xfer->max_size) {
+ xfer->total_len -= epout->tsiz_bm.xfer_size;
+ if (epnum == 0) {
+ xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
+ _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
+ }
}
}
+ break;
}
- break;
- // Out packet done (Interrupt)
- case GRXSTS_PKTSTS_OUTDONE:
- // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a
- // May (or not) be 3.10a specific feature/bug or depending on MCU configuration
- // XFRC complete is additionally generated when
- // - setup packet is received
- // - complete the data stage of control write is complete
- if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a))
- {
- uint32_t doepint = epout->doepint;
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // Out packet done
+ // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
+ // the specified OUT endpoint which will be handled by handle_epout_irq()
+ break;
- if (doepint & (DOEPINT_STPKTRX | DOEPINT_OTEPSPR))
- {
- // skip this "no-data" transfer complete event
- // Note: STPKTRX will be clear later by setup received handler
- uint32_t clear_flags = DOEPINT_XFRC;
+ default: break;
+ }
+}
- if (doepint & DOEPINT_OTEPSPR) clear_flags |= DOEPINT_OTEPSPR;
+static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
+ if (doepint_bm.setup_phase_done) {
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- epout->doepint = clear_flags;
+ // Normal OUT transfer complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they
+ // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- // TU_LOG(DWC2_DEBUG, " FIX extra transfer complete on setup/data compete\r\n");
- }
- }
- break;
-
- default: // Invalid
- TU_BREAKPOINT();
- break;
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet, Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
}
}
-static void handle_epout_irq (uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
+static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ dwc2_dep_t* epin = &dwc2->epin[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- // DAINT for a given EP clears when DOEPINTx is cleared.
- // OEPINT will be cleared when DAINT's out bits are cleared.
- for ( uint8_t n = 0; n < ep_count; n++ )
- {
- if ( dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n) )
- {
- dwc2_epout_t* epout = &dwc2->epout[n];
-
- uint32_t const doepint = epout->doepint;
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
- // SETUP packet Setup Phase done.
- if ( doepint & DOEPINT_STUP )
- {
- uint32_t clear_flag = DOEPINT_STUP;
+ // TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than
+ // - 64 bytes or
+ // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
+ if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) {
+ const uint16_t remain_packets = epin->tsiz_bm.packet_count;
- // STPKTRX is only available for version from 3_00a
- if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a))
- {
- clear_flag |= DOEPINT_STPKTRX;
- }
+ // Process every single packet (only whole packets can be written to fifo)
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
- epout->doepint = clear_flag;
- dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
+ // Check if dtxfsts has enough space available
+ if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
+ break;
}
- // OUT XFER complete
- if ( epout->doepint & DOEPINT_XFRC )
- {
- epout->doepint = DOEPINT_XFRC;
-
- xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
-
- // EP0 can only handle one packet
- if ( (n == 0) && ep0_pending[TUSB_DIR_OUT] )
- {
- // Schedule another packet to be received.
- edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
- }
- else
- {
- dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
- }
+ // Push packet to Tx-FIFO
+ if (xfer->ff) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[epnum];
+ tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
+ } else {
+ dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
+ xfer->buffer += xact_bytes;
}
}
+
+ // Turn off TXFE if all bytes are written.
+ if (epin->tsiz_bm.xfer_size == 0) {
+ dwc2->diepempmsk &= ~(1 << epnum);
+ }
}
}
+#endif
-static void handle_epin_irq (uint8_t rhport)
-{
- dwc2_regs_t * dwc2 = DWC2_REG(rhport);
- uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
- dwc2_epin_t* epin = dwc2->epin;
+#if CFG_TUD_DWC2_DMA_ENABLE
+static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ if (doepint_bm.setup_phase_done) {
+ dma_setup_prepare(rhport);
+ dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ return;
+ }
- // DAINT for a given EP clears when DIEPINTx is cleared.
- // IEPINT will be cleared when DAINT's out bits are cleared.
- for ( uint8_t n = 0; n < ep_count; n++ )
- {
- if ( dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n) )
- {
- // IN XFER complete (entire xfer).
- xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN);
+ // OUT XFER complete
+ if (doepint_bm.xfer_complete) {
+ // only handle data skip if it is setup or status related
+ // Normal OUT transfer complete
+ if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else {
+ dwc2_dep_t* epout = &dwc2->epout[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
- if ( epin[n].diepint & DIEPINT_XFRC )
- {
- epin[n].diepint = DIEPINT_XFRC;
+ // determine actual received bytes
+ const uint16_t remain = epout->tsiz_bm.xfer_size;
+ xfer->total_len -= remain;
- // EP0 can only handle one packet
- if ( (n == 0) && ep0_pending[TUSB_DIR_IN] )
- {
- // Schedule another packet to be transmitted.
- edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
- }
- else
- {
- dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ // this is ZLP, so prepare EP0 for next setup
+ // TODO use status phase rx
+ if(epnum == 0 && xfer->total_len == 0) {
+ dma_setup_prepare(rhport);
}
- }
- // XFER FIFO empty
- if ( (epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n)) )
- {
- // diepint's TXFE bit is read-only, software cannot clear it.
- // It will only be cleared by hardware when written bytes is more than
- // - 64 bytes or
- // - Half of TX FIFO size (configured by DIEPTXF)
+ dcd_dcache_invalidate(xfer->buffer, xfer->total_len);
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+ }
+}
- uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos;
+static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
- // Process every single packet (only whole packets can be written to fifo)
- for ( uint16_t i = 0; i < remaining_packets; i++ )
- {
- uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos;
+ if (diepint_bm.xfer_complete) {
+ if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_IN]) {
+ // EP0 can only handle one packet. Schedule another packet to be transmitted.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
+ } else {
+ if(epnum == 0) {
+ dma_setup_prepare(rhport);
+ }
+ dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+#endif
- // Packet can not be larger than ep max size
- uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
+static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const bool is_dma = dma_device_enabled(dwc2);
+ const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
+ const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos;
+ dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0];
- // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
- // EP has to be checked if the buffer can take another WHOLE packet
- if ( packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2) ) break;
+ // DAINT for a given EP clears when DEPINTx is cleared.
+ // EPINT will be cleared when DAINT bits are cleared.
+ for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
+ if (dwc2->daint & TU_BIT(daint_offset + epnum)) {
+ dwc2_dep_t* epout = &ep_base[epnum];
+ union {
+ uint32_t value;
+ dwc2_diepint_t diepint_bm;
+ dwc2_doepint_t doepint_bm;
+ } intr;
+ intr.value = epout->intr;
- // Push packet to Tx-FIFO
- if ( xfer->ff )
- {
- volatile uint32_t *tx_fifo = dwc2->fifo[n];
- tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size);
- }
- else
- {
- write_fifo_packet(rhport, n, xfer->buffer, packet_size);
+ epout->intr = intr.value; // Clear interrupt
- // Increment pointer to xfer data
- xfer->buffer += packet_size;
- }
+ if (is_dma) {
+ #if CFG_TUD_DWC2_DMA_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_dma(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_dma(rhport, epnum, intr.doepint_bm);
}
-
- // Turn off TXFE if all bytes are written.
- if ( ((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0 )
- {
- dwc2->diepempmsk &= ~(1 << n);
+ #endif
+ } else {
+ #if CFG_TUD_DWC2_SLAVE_ENABLE
+ if (dir == TUSB_DIR_IN) {
+ handle_epin_slave(rhport, epnum, intr.diepint_bm);
+ } else {
+ handle_epout_slave(rhport, epnum, intr.doepint_bm);
}
+ #endif
}
}
}
}
-void dcd_int_handler(uint8_t rhport)
-{
- dwc2_regs_t *dwc2 = DWC2_REG(rhport);
+/* Interrupt Hierarchy
+ DIEPINT DIEPINT
+ \ /
+ \ /
+ DAINT
+ / \
+ / \
+ GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL
+
+ Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk
+ are combined to generate dedicated interrupt line for each endpoint.
+ */
+void dcd_int_handler(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- uint32_t const int_status = dwc2->gintsts & dwc2->gintmsk;
+ const uint32_t gintmask = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmask;
- if(int_status & GINTSTS_USBRST)
- {
+ if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
- bus_reset(rhport);
+ handle_bus_reset(rhport);
}
- if(int_status & GINTSTS_ENUMDNE)
- {
+ if (gintsts & GINTSTS_ENUMDNE) {
// ENUMDNE is the end of reset where speed of the link is detected
-
dwc2->gintsts = GINTSTS_ENUMDNE;
-
- tusb_speed_t speed;
- switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos)
- {
- case DSTS_ENUMSPD_HS:
- speed = TUSB_SPEED_HIGH;
- break;
-
- case DSTS_ENUMSPD_LS:
- speed = TUSB_SPEED_LOW;
- break;
-
- case DSTS_ENUMSPD_FS_HSPHY:
- case DSTS_ENUMSPD_FS:
- default:
- speed = TUSB_SPEED_FULL;
- break;
- }
-
- dcd_event_bus_reset(rhport, speed, true);
+ handle_enum_done(rhport);
}
- if(int_status & GINTSTS_USBSUSP)
- {
+ if (gintsts & GINTSTS_USBSUSP) {
dwc2->gintsts = GINTSTS_USBSUSP;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
- if(int_status & GINTSTS_WKUINT)
- {
+ if (gintsts & GINTSTS_WKUINT) {
dwc2->gintsts = GINTSTS_WKUINT;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
@@ -1270,82 +1012,63 @@ void dcd_int_handler(uint8_t rhport)
// TODO check GINTSTS_DISCINT for disconnect detection
// if(int_status & GINTSTS_DISCINT)
- if(int_status & GINTSTS_OTGINT)
- {
+ if (gintsts & GINTSTS_OTGINT) {
// OTG INT bit is read-only
- uint32_t const otg_int = dwc2->gotgint;
+ const uint32_t otg_int = dwc2->gotgint;
- if (otg_int & GOTGINT_SEDET)
- {
+ if (otg_int & GOTGINT_SEDET) {
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
dwc2->gotgint = otg_int;
}
- if(int_status & GINTSTS_SOF)
- {
- dwc2->gotgint = GINTSTS_SOF;
+ if(gintsts & GINTSTS_SOF) {
+ dwc2->gintsts = GINTSTS_SOF;
+ const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
- if (_sof_en)
- {
- uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
- dcd_event_sof(rhport, frame, true);
- }
- else
- {
- // Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
+ // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
+ if (!_dcd_data.sof_en) {
dwc2->gintmsk &= ~GINTMSK_SOFM;
}
- dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+ dcd_event_sof(rhport, frame, true);
}
+#if CFG_TUD_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling.
- if(int_status & GINTSTS_RXFLVL)
- {
+ if (gintsts & GINTSTS_RXFLVL) {
// RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
- // Mask out RXFLVL while reading data from FIFO
- dwc2->gintmsk &= ~GINTMSK_RXFLVLM;
-
- // Loop until all available packets were handled
- do
- {
- handle_rxflvl_irq(rhport);
- } while(dwc2->gotgint & GINTSTS_RXFLVL);
-
- // Manage RX FIFO size
- if (_out_ep_closed)
- {
- update_grxfsiz(rhport);
-
- // Disable flag
- _out_ep_closed = false;
- }
+ do {
+ handle_rxflvl_irq(rhport); // read all packets
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
dwc2->gintmsk |= GINTMSK_RXFLVLM;
}
+#endif
// OUT endpoint interrupt handling.
- if(int_status & GINTSTS_OEPINT)
- {
+ if (gintsts & GINTSTS_OEPINT) {
// OEPINT is read-only, clear using DOEPINTn
- handle_epout_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_OUT);
}
// IN endpoint interrupt handling.
- if(int_status & GINTSTS_IEPINT)
- {
+ if (gintsts & GINTSTS_IEPINT) {
// IEPINT bit read-only, clear using DIEPINTn
- handle_epin_irq(rhport);
+ handle_ep_irq(rhport, TUSB_DIR_IN);
}
+}
- // // Check for Incomplete isochronous IN transfer
- // if(int_status & GINTSTS_IISOIXFR) {
- // printf(" IISOIXFR!\r\n");
- //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n");
- // }
+#if CFG_TUD_TEST_MODE
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Enable the test mode
+ dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (((uint8_t) test_selector) << DCTL_TCTL_Pos);
}
+#endif
#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_bcm.h b/src/portable/synopsys/dwc2/dwc2_bcm.h
index 732d96ae0..e5824606a 100644
--- a/src/portable/synopsys/dwc2/dwc2_bcm.h
+++ b/src/portable/synopsys/dwc2/dwc2_bcm.h
@@ -39,7 +39,7 @@
static const dwc2_controller_t _dwc2_controller[] =
{
- { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 4096 }
+ { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 16384 }
};
#define dcache_clean(_addr, _size) data_clean(_addr, _size)
diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c
new file mode 100644
index 000000000..f80ae9acb
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.c
@@ -0,0 +1,311 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#define DWC2_COMMON_DEBUG 2
+
+#if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED)
+
+#if CFG_TUD_ENABLED
+#include "device/dcd.h"
+#endif
+
+#if CFG_TUH_ENABLED
+#include "host/hcd.h"
+#endif
+
+#include "dwc2_common.h"
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+static void reset_core(dwc2_regs_t* dwc2) {
+ // 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
+ 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
+ while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {}
+ dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE;
+ }
+
+ while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE
+}
+
+static void phy_fs_init(dwc2_regs_t* dwc2) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n");
+
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // Select FS PHY
+ gusbcfg |= GUSBCFG_PHYSEL;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY init before reset
+ dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // USB turnaround time is critical for certification where long cables and 5-Hubs are used.
+ // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical,
+ // these bits can be programmed to a larger value. Default is 5
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= 5u << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED);
+}
+
+static void phy_hs_init(dwc2_regs_t* dwc2) {
+ uint32_t gusbcfg = dwc2->gusbcfg;
+
+ // De-select FS PHY
+ gusbcfg &= ~GUSBCFG_PHYSEL;
+
+ if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
+
+ // Select ULPI PHY (external)
+ gusbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+
+ // ULPI is always 8-bit interface
+ gusbcfg &= ~GUSBCFG_PHYIF16;
+
+ // ULPI select single data rate
+ gusbcfg &= ~GUSBCFG_DDRSEL;
+
+ // default internal VBUS Indicator and Drive
+ gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI);
+
+ // Disable FS/LS ULPI
+ gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM);
+ } else {
+ TU_LOG(DWC2_COMMON_DEBUG, "Highspeed UTMI+ PHY init\r\n");
+
+ // Select UTMI+ PHY (internal)
+ gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
+
+ // Set 16-bit interface if supported
+ if (dwc2->ghwcfg4_bm.phy_data_width) {
+ gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
+ } else {
+ gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
+ }
+ }
+
+ // Apply config
+ dwc2->gusbcfg = gusbcfg;
+
+ // mcu specific phy init
+ dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+
+ // Reset core after selecting PHY
+ reset_core(dwc2);
+
+ // Set turn-around, must after core reset otherwise it will be clear
+ // - 9 if using 8-bit PHY interface
+ // - 5 if using 16-bit PHY interface
+ gusbcfg &= ~GUSBCFG_TRDT_Msk;
+ gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
+ dwc2->gusbcfg = gusbcfg;
+
+ // MCU specific PHY update post reset
+ dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
+}
+
+static bool check_dwc2(dwc2_regs_t* dwc2) {
+#if CFG_TUSB_DEBUG >= DWC2_COMMON_DEBUG
+ // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
+ // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports
+ volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
+ TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
+ for (size_t i = 0; i < 5; i++) {
+ TU_LOG1("0x%08" PRIX32 ", ", p[i]);
+ }
+ TU_LOG1("0x%08" PRIX32 "\r\n", p[5]);
+#endif
+
+ // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it)
+ (void)dwc2;
+#if !TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ enum { GSNPSID_ID_MASK = TU_GENMASK(31, 16) };
+ const uint32_t gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK;
+ TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID);
+#endif
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
+ (void)dwc2;
+
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+#if CFG_TUH_ENABLED
+ if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) {
+ return false;
+ }
+#endif
+
+ return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
+}
+
+/* dwc2 has several PHYs option
+ * - UTMI+ is internal highspeed PHY, clock can be 30 Mhz (8-bit) or 60 Mhz (16-bit)
+ * - ULPI is external highspeed PHY, clock is 60Mhz with only 8-bit interface
+ * - Dedicated FS PHY is internal with clock 48Mhz.
+ *
+ * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz
+ *
+*/
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Check Synopsys ID register, failed if controller clock/power is not enabled
+ TU_ASSERT(check_dwc2(dwc2));
+
+ // disable global interrupt
+ dwc2->gahbcfg &= ~GAHBCFG_GINT;
+
+ if (is_highspeed) {
+ phy_hs_init(dwc2);
+ } else {
+ phy_fs_init(dwc2);
+ }
+
+ /* Set HS/FS Timeout Calibration to 7 (max available value).
+ * The number of PHY clocks that the application programs in
+ * this field is added to the high/full speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This can be required, because the delay
+ * introduced by the PHY in generating the linestate condition
+ * can vary from one PHY to another. */
+ dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos);
+
+ // Enable PHY clock TODO stop/gate clock when suspended mode
+ dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE);
+
+ dfifo_flush_tx(dwc2, 0x10); // all tx fifo
+ dfifo_flush_rx(dwc2);
+
+ // Clear pending and disable all interrupts
+ dwc2->gintsts = 0xFFFFFFFFU;
+ dwc2->gotgint = 0xFFFFFFFFU;
+ dwc2->gintmsk = 0;
+
+ TU_LOG(DWC2_COMMON_DEBUG, "DMA = %u\r\n", is_dma);
+
+ if (is_dma) {
+ // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly
+ dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
+ } else {
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ return true;
+}
+
+// void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr) {
+// (void) in_isr;
+// dwc2_regs_t * const dwc2 = DWC2_REG(rhport);
+// const uint32_t int_mask = dwc2->gintmsk;
+// const uint32_t int_status = dwc2->gintsts & int_mask;
+//
+// // Device disconnect
+// if (int_status & GINTSTS_DISCINT) {
+// dwc2->gintsts = GINTSTS_DISCINT;
+// }
+//
+// }
+
+//--------------------------------------------------------------------
+// DFIFO
+//--------------------------------------------------------------------
+// Read a single data packet from receive DFIFO
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) {
+ const volatile uint32_t* rx_fifo = dwc2->fifo[0];
+
+ // Reading full available 32 bit words from fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ tu_unaligned_write32(dst, *rx_fifo);
+ dst += 4;
+ }
+
+ // Read the remaining 1-3 bytes from fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem != 0) {
+ const uint32_t tmp = *rx_fifo;
+ dst[0] = tu_u32_byte0(tmp);
+ if (bytes_rem > 1) {
+ dst[1] = tu_u32_byte1(tmp);
+ }
+ if (bytes_rem > 2) {
+ dst[2] = tu_u32_byte2(tmp);
+ }
+ }
+}
+
+// Write a single data packet to DFIFO
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) {
+ volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num];
+
+ // Pushing full available 32 bit words to fifo
+ uint16_t word_count = len >> 2;
+ while (word_count--) {
+ *tx_fifo = tu_unaligned_read32(src);
+ src += 4;
+ }
+
+ // Write the remaining 1-3 bytes into fifo
+ const uint8_t bytes_rem = len & 0x03;
+ if (bytes_rem) {
+ uint32_t tmp_word = src[0];
+ if (bytes_rem > 1) {
+ tmp_word |= (src[1] << 8);
+ }
+ if (bytes_rem > 2) {
+ tmp_word |= (src[2] << 16);
+ }
+
+ *tx_fifo = tmp_word;
+ }
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h
new file mode 100644
index 000000000..18b93894f
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_common.h
@@ -0,0 +1,101 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_DWC2_COMMON_H
+#define TUSB_DWC2_COMMON_H
+
+#include "common/tusb_common.h"
+#include "dwc2_type.h"
+
+// Following symbols must be defined by port header
+// - _dwc2_controller[]: array of controllers
+// - DWC2_EP_MAX: largest EP counts of all controllers
+// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset
+// - dwc2_dcd_int_enable/dwc2_dcd_int_disable
+// - dwc2_remote_wakeup_delay
+
+#if defined(TUP_USBIP_DWC2_STM32)
+ #include "dwc2_stm32.h"
+#elif defined(TUP_USBIP_DWC2_ESP32)
+ #include "dwc2_esp32.h"
+#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
+ #include "dwc2_gd32.h"
+#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
+ #include "dwc2_bcm.h"
+#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
+ #include "dwc2_efm32.h"
+#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
+ #include "dwc2_xmc.h"
+#else
+ #error "Unsupported MCUs"
+#endif
+
+enum {
+ DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller)
+};
+
+enum {
+ OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG
+};
+
+//--------------------------------------------------------------------+
+// Core/Controller
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) {
+ if (rhport >= DWC2_CONTROLLER_COUNT) {
+ // user mis-configured, ignore and use first controller
+ rhport = 0;
+ }
+ return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base;
+}
+
+bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role);
+bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma);
+void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
+
+//--------------------------------------------------------------------+
+// DFIFO
+//--------------------------------------------------------------------+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) {
+ // flush TX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos);
+ while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) {
+ // flush RX fifo and wait for it cleared
+ dwc2->grstctl = GRSTCTL_RXFFLSH;
+ while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+}
+
+void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len);
+void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, uint16_t len);
+
+//--------------------------------------------------------------------+
+// DMA
+//--------------------------------------------------------------------+
+
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index c50dd66b8..3309760ff 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -25,72 +25,134 @@
*/
-#ifndef _DWC2_ESP32_H_
-#define _DWC2_ESP32_H_
+#ifndef TUSB_DWC2_ESP32_H_
+#define TUSB_DWC2_ESP32_H_
#ifdef __cplusplus
extern "C" {
#endif
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
#include "esp_intr_alloc.h"
#include "soc/periph_defs.h"
-//#include "soc/usb_periph.h"
+#include "soc/usb_wrap_struct.h"
-#define DWC2_REG_BASE 0x60080000UL
-#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN)
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#define DWC2_FS_REG_BASE 0x60080000UL
+#define DWC2_EP_MAX 7
-static const dwc2_controller_t _dwc2_controller[] =
-{
- { .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1024 }
+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 }
};
-static intr_handle_t usb_ih;
+#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
+#define DWC2_FS_REG_BASE 0x50040000UL
+#define DWC2_HS_REG_BASE 0x50000000UL
+#define DWC2_EP_MAX 16
-static void dcd_int_handler_wrap(void* arg)
-{
- (void) arg;
- dcd_int_handler(0);
-}
+// On ESP32 for consistency we associate
+// - Port0 to OTG_FS, and Port1 to OTG_HS
+static const dwc2_controller_t _dwc2_controller[] = {
+{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
+{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
+};
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+static intr_handle_t usb_ih[TU_ARRAY_SIZE(_dwc2_controller)];
-TU_ATTR_ALWAYS_INLINE
-static inline void dwc2_dcd_int_enable (uint8_t rhport)
-{
- (void) rhport;
- esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, dcd_int_handler_wrap, NULL, &usb_ih);
+static void dwc2_int_handler_wrap(void* arg) {
+ const uint8_t rhport = tu_u16_low((uint16_t)(uintptr_t)arg);
+ const tusb_role_t role = (tusb_role_t) tu_u16_high((uint16_t)(uintptr_t)arg);
+#if CFG_TUD_ENABLED
+ if (role == TUSB_ROLE_DEVICE) {
+ dcd_int_handler(rhport);
+ }
+#endif
+#if CFG_TUH_ENABLED && !CFG_TUH_MAX3421
+ if (role == TUSB_ROLE_HOST) {
+ hcd_int_handler(rhport, true);
+ }
+#endif
}
-TU_ATTR_ALWAYS_INLINE
-static inline void dwc2_dcd_int_disable (uint8_t rhport)
-{
- (void) rhport;
- esp_intr_free(usb_ih);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ if (enabled) {
+ esp_intr_alloc(_dwc2_controller[rhport].irqnum, ESP_INTR_FLAG_LOWMED,
+ dwc2_int_handler_wrap, (void*)(uintptr_t)tu_u16(role, rhport), &usb_ih[rhport]);
+ } else {
+ esp_intr_free(usb_ih[rhport]);
+ }
}
-static inline void dwc2_remote_wakeup_delay(void)
-{
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
vTaskDelay(pdMS_TO_TICKS(1));
}
// MCU specific PHY init, called BEFORE core reset
-static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
-{
- (void) dwc2;
- (void) hs_phy_type;
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+ // maybe usb_utmi_hal_init()
- // nothing to do
}
// MCU specific PHY update, it is called AFTER init() and core reset
-static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
-{
- (void) dwc2;
- (void) hs_phy_type;
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
+ (void)dwc2;
+ (void)hs_phy_type;
+ // maybe usb_utmi_hal_disable()
+}
+
+//--------------------------------------------------------------------+
+// Data Cache
+//--------------------------------------------------------------------+
+#if CFG_TUD_DWC2_DMA_ENABLE || CFG_TUH_DWC2_DMA_ENABLE
+#if defined(SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE) && SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
+#include "esp_cache.h"
+
+#if CFG_TUD_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE || \
+ CFG_TUH_MEM_DCACHE_LINE_SIZE != CONFIG_CACHE_L1_CACHE_LINE_SIZE
+#error "CFG_TUD/TUH_MEM_DCACHE_LINE_SIZE must match CONFIG_CACHE_L1_CACHE_LINE_SIZE"
+#endif
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t round_up_to_cache_line_size(uint32_t size) {
+ if (size & (CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) {
+ size = (size & ~(CONFIG_CACHE_L1_CACHE_LINE_SIZE-1)) + CONFIG_CACHE_L1_CACHE_LINE_SIZE;
+ }
+ return size;
+}
- // nothing to do
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
}
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ const int flag = ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_DIR_M2C;
+ data_size = round_up_to_cache_line_size(data_size);
+ return ESP_OK == esp_cache_msync((void*)addr, data_size, flag);
+}
+
+#endif
+#endif
+
#ifdef __cplusplus
}
#endif
-#endif /* _DWC2_ESP32_H_ */
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md
new file mode 100644
index 000000000..dec021f59
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_info.md
@@ -0,0 +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 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 |
diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py
new file mode 100755
index 000000000..25edcf22d
--- /dev/null
+++ b/src/portable/synopsys/dwc2/dwc2_info.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+
+import ctypes
+import argparse
+import click
+import pandas as pd
+
+# hex value for register: guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4
+# Note: FS is FullSpeed, HS is HighSpeed
+dwc2_reg_list = ['GUID', 'GSNPSID', 'GHWCFG1', 'GHWCFG2', 'GHWCFG3', 'GHWCFG4']
+dwc2_reg_value = {
+ 'BCM2711 (Pi4)': [0x2708A000, 0x4F54280A, 0, 0x228DDD50, 0xFF000E8, 0x1FF00020],
+ 'EFM32GG': [0, 0x4F54330A, 0, 0x228F5910, 0x01F204E8, 0x1BF08030],
+ 'ESP32-S2/S3': [0, 0x4F54400A, 0, 0x224DD930, 0x0C804B5, 0xD3F0A030],
+ 'ESP32-P4': [0, 0x4F54400A, 0, 0x215FFFD0, 0x03805EB5, 0xDFF1A030],
+ '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],
+ 'ST F723/L4P5 FS': [0x3000, 0x4F54330A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST F723 HS': [0x3100, 0x4F54330A, 0, 0x229FE1D0, 0x03EED2E8, 0x23F00030],
+ 'ST F76x HS': [0x2100, 0x4F54320A, 0, 0x229FE190, 0x03EED2E8, 0x23F00030],
+ 'ST H743/H750': [0x2300, 0x4F54330A, 0, 0x229FE190, 0x03B8D2E8, 0xE3F00030],
+ 'ST L476 FS': [0x2000, 0x4F54310A, 0, 0x229ED520, 0x0200D1E8, 0x17F08030],
+ 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30],
+ 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030],
+ 'GD32VF103': [0x1000, 0, 0, 0, 0, 0],
+}
+
+# Combine dwc2_info with dwc2_reg_list
+# dwc2_info = {
+# 'BCM2711 (Pi4)': {
+# 'GUID': 0x2708A000,
+# 'GSNPSID': 0x4F54280A,
+# 'GHWCFG1': 0,
+# 'GHWCFG2': 0x228DDD50,
+# 'GHWCFG3': 0xFF000E8,
+# 'GHWCFG4': 0x1FF00020
+# },
+dwc2_info = {key: {field: value for field, value in zip(dwc2_reg_list, values)} for key, values in dwc2_reg_value.items()}
+
+
+class GHWCFG2(ctypes.LittleEndianStructure):
+ _fields_ = [
+ ("op_mode", ctypes.c_uint32, 3),
+ ("arch", ctypes.c_uint32, 2),
+ ("single_point", ctypes.c_uint32, 1),
+ ("hs_phy_type", ctypes.c_uint32, 2),
+ ("fs_phy_type", ctypes.c_uint32, 2),
+ ("num_dev_ep", ctypes.c_uint32, 4),
+ ("num_host_ch", ctypes.c_uint32, 4),
+ ("period_channel_support", ctypes.c_uint32, 1),
+ ("enable_dynamic_fifo", ctypes.c_uint32, 1),
+ ("mul_proc_intrpt", ctypes.c_uint32, 1),
+ ("reserved21", ctypes.c_uint32, 1),
+ ("nptx_q_depth", ctypes.c_uint32, 2),
+ ("ptx_q_depth", ctypes.c_uint32, 2),
+ ("token_q_depth", ctypes.c_uint32, 5),
+ ("otg_enable_ic_usb", ctypes.c_uint32, 1)
+ ]
+
+
+class GHWCFG3(ctypes.LittleEndianStructure):
+ _fields_ = [
+ ("xfer_size_width", ctypes.c_uint32, 4),
+ ("packet_size_width", ctypes.c_uint32, 3),
+ ("otg_enable", ctypes.c_uint32, 1),
+ ("i2c_enable", ctypes.c_uint32, 1),
+ ("vendor_ctrl_itf", ctypes.c_uint32, 1),
+ ("optional_feature_removed", ctypes.c_uint32, 1),
+ ("synch_reset", ctypes.c_uint32, 1),
+ ("otg_adp_support", ctypes.c_uint32, 1),
+ ("otg_enable_hsic", ctypes.c_uint32, 1),
+ ("battery_charger_support", ctypes.c_uint32, 1),
+ ("lpm_mode", ctypes.c_uint32, 1),
+ ("dfifo_depth", ctypes.c_uint32, 16)
+ ]
+
+
+class GHWCFG4(ctypes.LittleEndianStructure):
+ _fields_ = [
+ ("num_dev_period_in_ep", ctypes.c_uint32, 4),
+ ("partial_powerdown", ctypes.c_uint32, 1),
+ ("ahb_freq_min", ctypes.c_uint32, 1),
+ ("hibernation", ctypes.c_uint32, 1),
+ ("extended_hibernation", ctypes.c_uint32, 1),
+ ("reserved8", ctypes.c_uint32, 1),
+ ("enhanced_lpm_support1", ctypes.c_uint32, 1),
+ ("service_interval_flow", ctypes.c_uint32, 1),
+ ("ipg_isoc_support", ctypes.c_uint32, 1),
+ ("acg_support", ctypes.c_uint32, 1),
+ ("enhanced_lpm_support", ctypes.c_uint32, 1),
+ ("phy_data_width", ctypes.c_uint32, 2),
+ ("ctrl_ep_num", ctypes.c_uint32, 4),
+ ("iddg_filter", ctypes.c_uint32, 1),
+ ("vbus_valid_filter", ctypes.c_uint32, 1),
+ ("a_valid_filter", ctypes.c_uint32, 1),
+ ("b_valid_filter", ctypes.c_uint32, 1),
+ ("session_end_filter", ctypes.c_uint32, 1),
+ ("dedicated_fifos", ctypes.c_uint32, 1),
+ ("num_dev_in_eps", ctypes.c_uint32, 4),
+ ("dma_desc_enable", ctypes.c_uint32, 1),
+ ("dma_desc_dynamic", ctypes.c_uint32, 1)
+ ]
+
+# mapping for specific fields in GHWCFG2
+GHWCFG2_field = {
+ 'op_mode': {
+ 0: "HNP SRP",
+ 1: "SRP",
+ 2: "noHNP noSRP",
+ 3: "SRP Device",
+ 4: "noOTG Device",
+ 5: "SRP Host",
+ 6: "noOTG Host"
+ },
+ 'arch': {
+ 0: "Slave only",
+ 1: "DMA external",
+ 2: "DMA internal"
+ },
+ 'single_point': {
+ 0: "hub",
+ 1: "n/a"
+ },
+ 'hs_phy_type': {
+ 0: "n/a",
+ 1: "UTMI+",
+ 2: "ULPI",
+ 3: "UTMI+/ULPI"
+ },
+ 'fs_phy_type': {
+ 0: "n/a",
+ 1: "Dedicated",
+ 2: "Shared UTMI+",
+ 3: "Shared ULPI"
+ },
+ 'nptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ },
+ 'ptx_q_depth': {
+ 0: "2",
+ 1: "4",
+ 2: "8",
+ 3: "16"
+ },
+}
+
+# mapping for specific fields in GHWCFG4
+GHWCFG4_field = {
+ 'phy_data_width': {
+ 0: "8 bit",
+ 1: "16 bit",
+ 2: "8/16 bit",
+ 3: "Reserved"
+ },
+ }
+
+def main():
+ """Render dwc2_info to Markdown table"""
+
+ parser = argparse.ArgumentParser()
+ args = parser.parse_args()
+
+ # Create an empty list to hold the dictionaries
+ md_table = []
+
+ # Iterate over the dwc2_info dictionary and extract fields
+ for device, reg_values in dwc2_info.items():
+ md_item = {"Device": device}
+ for r_name, r_value in reg_values.items():
+ md_item[r_name] = f"0x{r_value:08X}"
+
+ if r_name == 'GSNPSID':
+ # Get dwc2 specs version
+ major = ((r_value >> 8) >> 4) & 0x0F
+ minor = (r_value >> 4) & 0xFF
+ patch = chr((r_value & 0x0F) + ord('a') - 0xA)
+ md_item[f' - specs version'] = f"{major:X}.{minor:02X}{patch}"
+ elif r_name in globals():
+ # Get bit-field values which exist as ctypes structures
+ class_hdl = globals()[r_name]
+ ghwcfg = class_hdl.from_buffer_copy(r_value.to_bytes(4, byteorder='little'))
+ for field_name, field_type, _ in class_hdl._fields_:
+ field_value = getattr(ghwcfg, field_name)
+ if class_hdl == GHWCFG2 and field_name in GHWCFG2_field:
+ field_value = GHWCFG2_field[field_name].get(field_value, f"Unknown ({field_value})")
+ if class_hdl == GHWCFG4 and field_name in GHWCFG4_field:
+ field_value = GHWCFG4_field[field_name].get(field_value, f"Unknown ({field_value})")
+
+ md_item[f' - {field_name}'] = field_value
+
+ md_table.append(md_item)
+
+ # Create a Pandas DataFrame from the list of dictionaries
+ df = pd.DataFrame(md_table).set_index('Device')
+
+ # Transpose the DataFrame to switch rows and columns
+ df = df.T
+ #print(df)
+
+ # Write the Markdown table to a file
+ with open('dwc2_info.md', 'w') as md_file:
+ md_file.write(df.to_markdown())
+ md_file.write('\n')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h
index ea786362e..c11c1eb05 100644
--- a/src/portable/synopsys/dwc2/dwc2_stm32.h
+++ b/src/portable/synopsys/dwc2/dwc2_stm32.h
@@ -24,11 +24,11 @@
* This file is part of the TinyUSB stack.
*/
-#ifndef _DWC2_STM32_H_
-#define _DWC2_STM32_H_
+#ifndef DWC2_STM32_H_
+#define DWC2_STM32_H_
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
// EP_MAX : Max number of bi-directional endpoints including EP0
@@ -69,6 +69,14 @@
#define OTG_FS_IRQn OTG_HS_IRQn
#endif
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H7RS
+ #include "stm32h7rsxx.h"
+ #define EP_MAX_FS 6
+ #define EP_FIFO_SIZE_FS 1280
+
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+
#elif CFG_TUSB_MCU == OPT_MCU_STM32F7
#include "stm32f7xx.h"
#define EP_MAX_FS 6
@@ -84,10 +92,16 @@
#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
#include "stm32u5xx.h"
- #define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE
- #define EP_MAX_FS 6
- #define EP_FIFO_SIZE_FS 1280
-
+ // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
+ #ifdef USB_OTG_FS
+ #define USB_OTG_FS_PERIPH_BASE USB_OTG_FS_BASE
+ #define EP_MAX_FS 6
+ #define EP_FIFO_SIZE_FS 1280
+ #else
+ #define USB_OTG_HS_PERIPH_BASE USB_OTG_HS_BASE
+ #define EP_MAX_HS 9
+ #define EP_FIFO_SIZE_HS 4096
+ #endif
#else
#error "Unsupported MCUs"
#endif
@@ -101,60 +115,82 @@
// On STM32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
-static const dwc2_controller_t _dwc2_controller[] =
-{
-#ifdef USB_OTG_FS_PERIPH_BASE
- { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS },
-#endif
+static const dwc2_controller_t _dwc2_controller[] = {
+ #ifdef USB_OTG_FS_PERIPH_BASE
+ { .reg_base = USB_OTG_FS_PERIPH_BASE, .irqnum = OTG_FS_IRQn, .ep_count = EP_MAX_FS, .ep_fifo_size = EP_FIFO_SIZE_FS },
+ #endif
-#ifdef USB_OTG_HS_PERIPH_BASE
- { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS },
-#endif
+ #ifdef USB_OTG_HS_PERIPH_BASE
+ { .reg_base = USB_OTG_HS_PERIPH_BASE, .irqnum = OTG_HS_IRQn, .ep_count = EP_MAX_HS, .ep_fifo_size = EP_FIFO_SIZE_HS },
+ #endif
};
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
-// SystemCoreClock is alrady included by family header
+// SystemCoreClock is already included by family header
// extern uint32_t SystemCoreClock;
-TU_ATTR_ALWAYS_INLINE
-static inline void dwc2_dcd_int_enable(uint8_t rhport)
-{
- NVIC_EnableIRQ(_dwc2_controller[rhport].irqnum);
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) {
+ (void) role;
+ const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum;
+ if (enabled) {
+ NVIC_EnableIRQ(irqn);
+ } else {
+ NVIC_DisableIRQ(irqn);
+ }
}
-TU_ATTR_ALWAYS_INLINE
-static inline void dwc2_dcd_int_disable (uint8_t rhport)
-{
- NVIC_DisableIRQ(_dwc2_controller[rhport].irqnum);
-}
+#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true)
+#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false)
-TU_ATTR_ALWAYS_INLINE
-static inline void dwc2_remote_wakeup_delay(void)
-{
+
+TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) {
// try to delay for 1 ms
uint32_t count = SystemCoreClock / 1000;
- while ( count-- ) __NOP();
+ while (count--) __NOP();
}
// MCU specific PHY init, called BEFORE core reset
-static inline void dwc2_phy_init(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
-{
- if ( hs_phy_type == HS_PHY_TYPE_NONE )
- {
+// - dwc2 3.30a (H5) use USB_HS_PHYC
+// - dwc2 4.11a (U5) use femtoPHY
+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
dwc2->stm32_gccfg |= STM32_GCCFG_PWRDWN;
- }else
- {
- // Disable FS PHY
+
+ // https://community.st.com/t5/stm32cubemx-mcus/why-stm32h743-usb-fs-doesn-t-work-if-freertos-tickless-idle/m-p/349480#M18867
+ // H7 running on full-speed phy need to disable ULPI clock in sleep mode.
+ // Otherwise, USB won't work when mcu executing WFI/WFE instruction i.e tick-less RTOS.
+ // Note: there may be other family that is affected by this, but only H7 and F7 is tested so far
+ #if defined(USB_OTG_FS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB2OTGFSULPILPEN)
+ if ( USB_OTG_FS_PERIPH_BASE == (uint32_t) dwc2 ) {
+ RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB2OTGFSULPILPEN;
+ }
+ #endif
+
+ #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_USB1OTGHSULPILPEN)
+ if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
+ RCC->AHB1LPENR &= ~RCC_AHB1LPENR_USB1OTGHSULPILPEN;
+ }
+ #endif
+
+ #if defined(USB_OTG_HS_PERIPH_BASE) && defined(RCC_AHB1LPENR_OTGHSULPILPEN)
+ if ( USB_OTG_HS_PERIPH_BASE == (uint32_t) dwc2 ) {
+ RCC->AHB1LPENR &= ~RCC_AHB1LPENR_OTGHSULPILPEN;
+ }
+ #endif
+
+ } else {
+#if CFG_TUSB_MCU != OPT_MCU_STM32U5
+ // 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 == HS_PHY_TYPE_UTMI || hs_phy_type == HS_PHY_TYPE_UTMI_ULPI)
- {
-#ifdef USB_HS_PHYC
+ if (hs_phy_type == GHWCFG2_HSPHY_UTMI || hs_phy_type == GHWCFG2_HSPHY_UTMI_ULPI) {
+ #ifdef USB_HS_PHYC
// Enable UTMI HS PHY
dwc2->stm32_gccfg |= STM32_GCCFG_PHYHSEN;
@@ -186,40 +222,47 @@ 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;
-#endif
+ #else
+
+ #endif
}
}
}
// MCU specific PHY update, it is called AFTER init() and core reset
-static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
-{
+static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) {
// used to set turnaround time for fullspeed, nothing to do in highspeed mode
- if ( hs_phy_type == HS_PHY_TYPE_NONE )
- {
+ if (hs_phy_type == GHWCFG2_HSPHY_NOT_SUPPORTED) {
// Turnaround timeout depends on the AHB clock dictated by STM32 Reference Manual
uint32_t turnaround;
- if ( SystemCoreClock >= 32000000u )
+ if (SystemCoreClock >= 32000000u) {
turnaround = 0x6u;
- else if ( SystemCoreClock >= 27500000u )
+ } else if (SystemCoreClock >= 27500000u) {
turnaround = 0x7u;
- else if ( SystemCoreClock >= 24000000u )
+ } else if (SystemCoreClock >= 24000000u) {
turnaround = 0x8u;
- else if ( SystemCoreClock >= 21800000u )
+ } else if (SystemCoreClock >= 21800000u) {
turnaround = 0x9u;
- else if ( SystemCoreClock >= 20000000u )
+ }
+ else if (SystemCoreClock >= 20000000u) {
turnaround = 0xAu;
- else if ( SystemCoreClock >= 18500000u )
+ }
+ else if (SystemCoreClock >= 18500000u) {
turnaround = 0xBu;
- else if ( SystemCoreClock >= 17200000u )
+ }
+ else if (SystemCoreClock >= 17200000u) {
turnaround = 0xCu;
- else if ( SystemCoreClock >= 16000000u )
+ }
+ else if (SystemCoreClock >= 16000000u) {
turnaround = 0xDu;
- else if ( SystemCoreClock >= 15000000u )
+ }
+ else if (SystemCoreClock >= 15000000u) {
turnaround = 0xEu;
- else
+ }
+ else {
turnaround = 0xFu;
+ }
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (turnaround << GUSBCFG_TRDT_Pos);
}
@@ -229,4 +272,4 @@ static inline void dwc2_phy_update(dwc2_regs_t * dwc2, uint8_t hs_phy_type)
}
#endif
-#endif /* _DWC2_STM32_H_ */
+#endif
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index 3fc979337..812096759 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -1,21 +1,38 @@
-/**
- * @author MCD Application Team
- * Ha Thach (tinyusb.org)
- *
- * @attention
- *
- * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (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.
+ *
+ */
+/** <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
- *
*/
-#ifndef _TUSB_DWC2_TYPES_H_
-#define _TUSB_DWC2_TYPES_H_
+#ifndef TUSB_DWC2_TYPES_H_
+#define TUSB_DWC2_TYPES_H_
#include "stdint.h"
@@ -29,10 +46,11 @@ typedef struct
uintptr_t reg_base;
uint32_t irqnum;
uint8_t ep_count;
+ uint8_t ep_in_count;
uint32_t ep_fifo_size;
}dwc2_controller_t;
-/* DWC OTG HW Release versions */
+// DWC OTG HW Release versions
#define DWC2_CORE_REV_2_71a 0x4f54271a
#define DWC2_CORE_REV_2_72a 0x4f54272a
#define DWC2_CORE_REV_2_80a 0x4f54280a
@@ -43,12 +61,13 @@ typedef struct
#define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#define DWC2_CORE_REV_4_00a 0x4f54400a
+#define DWC2_CORE_REV_4_11a 0x4f54411a
#define DWC2_CORE_REV_4_20a 0x4f54420a
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
#define DWC2_CORE_REV_MASK 0x0000ffff
-/* DWC OTG HW Core ID */
+// DWC OTG HW Core ID
#define DWC2_OTG_ID 0x4f540000
#define DWC2_FS_IOT_ID 0x55310000
#define DWC2_HS_IOT_ID 0x55320000
@@ -57,231 +76,717 @@ typedef struct
// HS PHY
typedef struct
{
- volatile uint32_t HS_PHYC_PLL; // This register is used to control the PLL of the HS PHY. 000h */
- volatile uint32_t Reserved04; // Reserved 004h */
- volatile uint32_t Reserved08; // Reserved 008h */
- volatile uint32_t HS_PHYC_TUNE; // This register is used to control the tuning interface of the High Speed PHY. 00Ch */
- volatile uint32_t Reserved10; // Reserved 010h */
- volatile uint32_t Reserved14; // Reserved 014h */
- volatile uint32_t HS_PHYC_LDO; // This register is used to control the regulator (LDO). 018h */
+ volatile uint32_t HS_PHYC_PLL; // 000h This register is used to control the PLL of the HS PHY.
+ volatile uint32_t Reserved04; // 004h Reserved
+ volatile uint32_t Reserved08; // 008h Reserved
+ volatile uint32_t HS_PHYC_TUNE; // 00Ch This register is used to control the tuning interface of the High Speed PHY.
+ volatile uint32_t Reserved10; // 010h Reserved
+ volatile uint32_t Reserved14; // 014h Reserved
+ volatile uint32_t HS_PHYC_LDO; // 018h This register is used to control the regulator (LDO).
} HS_PHYC_GlobalTypeDef;
#endif
enum {
- HS_PHY_TYPE_NONE = 0 , // not supported
- HS_PHY_TYPE_UTMI , // internal PHY (mostly)
- HS_PHY_TYPE_ULPI , // external PHY
- HS_PHY_TYPE_UTMI_ULPI ,
+ GOTGCTL_OTG_VERSION_1_3 = 0,
+ GOTGCTL_OTG_VERSION_2_0 = 1,
};
enum {
- FS_PHY_TYPE_NONE = 0, // not supported
- FS_PHY_TYPE_DEDICATED,
- FS_PHY_TYPE_UTMI,
- FS_PHY_TYPE_ULPI,
+ GHWCFG2_OPMODE_HNP_SRP = 0,
+ GHWCFG2_OPMODE_SRP = 1,
+ GHWCFG2_OPMODE_NON_HNP_NON_SRP = 2,
+ GHWCFG2_OPMODE_SRP_DEVICE = 3,
+ GHWCFFG2_OPMODE_NON_OTG_DEVICE = 4,
+ GHWCFG2_OPMODE_SRP_HOST = 5,
+ GHWCFG2_OPMODE_NON_OTG_HOST = 6,
+};
+enum {
+ GHWCFG2_ARCH_SLAVE_ONLY = 0,
+ GHWCFG2_ARCH_EXTERNAL_DMA = 1,
+ GHWCFG2_ARCH_INTERNAL_DMA = 2,
};
-typedef struct TU_ATTR_PACKED
-{
- uint32_t op_mode : 3; // 0: HNP and SRP | 1: SRP | 2: non-HNP, non-SRP
- uint32_t arch : 2; // 0: slave-only | 1: External DMA | 2: Internal DMA | 3: others
- uint32_t point2point : 1; // 0: support hub and split | 1: no hub, no split
- uint32_t hs_phy_type : 2; // 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI
- uint32_t fs_phy_type : 2; // 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI
- uint32_t num_dev_ep : 4; // Number of device endpoints (not including EP0)
- uint32_t num_host_ch : 4; // Number of host channel
- uint32_t period_channel_support : 1; // Support Periodic OUT Host Channel
- uint32_t enable_dynamic_fifo : 1; // Dynamic FIFO Sizing Enabled
- uint32_t mul_cpu_int : 1; // Multi-Processor Interrupt Enabled
- uint32_t reserved21 : 1;
- uint32_t nperiod_tx_q_depth : 2; // Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
- uint32_t host_period_tx_q_depth : 2; // Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
- uint32_t dev_token_q_depth : 5; // Device IN token sequence learning queue depth: 0-30
- uint32_t otg_enable_ic_usb : 1; // IC_USB mode specified for mode of operation
-} dwc2_ghwcfg2_t;
+enum {
+ GHWCFG2_HSPHY_NOT_SUPPORTED = 0,
+ GHWCFG2_HSPHY_UTMI = 1, // internal PHY (mostly)
+ GHWCFG2_HSPHY_ULPI = 2, // external PHY (mostly)
+ GHWCFG2_HSPHY_UTMI_ULPI = 3, // both
+
+};
+
+enum {
+ GHWCFG2_FSPHY_NOT_SUPPORTED = 0,
+ GHWCFG2_FSPHY_DEDICATED = 1, // have dedicated FS PHY
+ GHWCFG2_FSPHY_UTMI = 2, // shared with UTMI+
+ GHWCFG2_FSPHY_ULPI = 3, // shared with ULPI
+};
+
+enum {
+ GHWCFFG4_PHY_DATA_WIDTH_8 = 0,
+ GHWCFFG4_PHY_DATA_WIDTH_16 = 1,
+ GHWCFFG4_PHY_DATA_WIDTH_8_16 = 2, // software selectable
+};
+
+enum {
+ HPRT_SPEED_HIGH = 0,
+ HPRT_SPEED_FULL = 1,
+ HPRT_SPEED_LOW = 2
+};
+
+enum {
+ GINTSTS_CMODE_DEVICE = 0,
+ GINTSTS_CMODE_HOST = 1,
+};
+
+enum {
+ HCTSIZ_PID_DATA0 = 0, // 00b
+ HCTSIZ_PID_DATA2 = 1, // 01b
+ HCTSIZ_PID_DATA1 = 2, // 10b
+ HCTSIZ_PID_SETUP = 3, // 11b
+};
+enum {
+ HCTSIZ_PID_MDATA = 3,
+};
+
+enum {
+ GRXSTS_PKTSTS_GLOBAL_OUT_NAK = 1,
+ GRXSTS_PKTSTS_RX_DATA = 2,
+ GRXSTS_PKTSTS_RX_COMPLETE = 3,
+ GRXSTS_PKTSTS_SETUP_DONE = 4,
+ GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5,
+ GRXSTS_PKTSTS_SETUP_RX = 6,
+ GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7
+};
+
+// Same as TUSB_XFER_*
+enum {
+ HCCHAR_EPTYPE_CONTROL = 0,
+ HCCHAR_EPTYPE_ISOCHRONOUS = 1,
+ HCCHAR_EPTYPE_BULK = 2,
+ HCCHAR_EPTYPE_INTERRUPT = 3
+};
+
+enum {
+ DCFG_SPEED_HIGH = 0, // Highspeed with 30/60 Mhz
+ DCFG_SPEED_FULL_30_60MHZ = 1, // Fullspeed with UTMI+/ULPI 30/60 Mhz
+ DCFG_SPEED_LOW = 2, // Lowspeed with FS PHY at 6 Mhz
+ DCFG_SPEED_FULL_48MHZ = 3, // Fullspeed with dedicated FS PHY at 48 Mhz
+};
+
+// Same as TUSB_XFER_*
+enum {
+ DEPCTL_EPTYPE_CONTROL = 0,
+ DEPCTL_EPTYPE_ISOCHRONOUS = 1,
+ DEPCTL_EPTYPE_BULK = 2,
+ DEPCTL_EPTYPE_INTERRUPT = 3
+};
+
+//--------------------------------------------------------------------
+// Common Register Bitfield
+//--------------------------------------------------------------------
+typedef struct TU_ATTR_PACKED {
+ uint32_t ses_req_scs : 1; // 0 Session request success
+ uint32_t ses_req : 1; // 1 Session request
+ uint32_t vbval_ov_en : 1; // 2 VBUS valid override enable
+ uint32_t vbval_ov_val : 1; // 3 VBUS valid override value
+ uint32_t aval_ov_en : 1; // 4 A-peripheral session valid override enable
+ uint32_t aval_ov_al : 1; // 5 A-peripheral session valid override value
+ uint32_t bval_ov_en : 1; // 6 B-peripheral session valid override enable
+ uint32_t bval_ov_val : 1; // 7 B-peripheral session valid override value
+ uint32_t hng_scs : 1; // 8 Host negotiation success
+ uint32_t hnp_rq : 1; // 9 HNP (host negotiation protocol) request
+ uint32_t host_set_hnp_en : 1; // 10 Host set HNP enable
+ uint32_t dev_hnp_en : 1; // 11 Device HNP enabled
+ uint32_t embedded_host_en : 1; // 12 Embedded host enable
+ uint32_t rsv13_14 : 2; // 13.14 Reserved
+ uint32_t dbnc_filter_bypass : 1; // 15 Debounce filter bypass
+ uint32_t cid_status : 1; // 16 Connector ID status
+ uint32_t dbnc_done : 1; // 17 Debounce done
+ uint32_t ases_valid : 1; // 18 A-session valid
+ uint32_t bses_valid : 1; // 19 B-session valid
+ uint32_t otg_ver : 1; // 20 OTG version 0: v1.3, 1: v2.0
+ uint32_t current_mode : 1; // 21 Current mode of operation. Only from v3.00a
+ uint32_t mult_val_id_bc : 5; // 22..26 Multi-valued input pin ID battery charger
+ uint32_t chirp_en : 1; // 27 Chirp detection enable
+ uint32_t rsv28_30 : 3; // 28.30: Reserved
+ uint32_t test_mode_corr_eusb2 : 1; // 31 Test mode control for eUSB2 PHY
+} dwc2_gotgctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_gotgctl_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t rsv0_1 : 2; // 0..1 Reserved
+ uint32_t ses_end_det : 1; // 2 Session end detected
+ uint32_t rsv3_7 : 5; // 3..7 Reserved
+ uint32_t srs_status_change : 1; // 8 Session request success status change
+ uint32_t hns_status_change : 1; // 9 Host negotiation success status change
+ uint32_t rsv10_16 : 7; // 10..16 Reserved
+ uint32_t hng_det : 1; // 17 Host negotiation detected
+ uint32_t adev_timeout_change : 1; // 18 A-device timeout change
+ uint32_t dbnc_done : 1; // 19 Debounce done
+ uint32_t mult_val_lp_change : 1; // 20 Multi-valued input pin change
+ uint32_t rsv21_31 :11; // 21..31 Reserved
+} dwc2_gotgint_t;
+TU_VERIFY_STATIC(sizeof(dwc2_gotgint_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t gintmask : 1; // 0 Global interrupt mask
+ uint32_t hbst_len : 4; // 1..4 Burst length/type
+ uint32_t dma_en : 1; // 5 DMA enable
+ uint32_t rsv6 : 1; // 6 Reserved
+ uint32_t nptxf_empty_lvl : 1; // 7 Non-periodic Tx FIFO empty level
+ uint32_t ptxf_empty_lvl : 1; // 8 Periodic Tx FIFO empty level
+ uint32_t rsv9_20 : 12; // 9.20: Reserved
+ uint32_t remote_mem_support : 1; // 21 Remote memory support
+ uint32_t notify_all_dma_write : 1; // 22 Notify all DMA writes
+ uint32_t ahb_single : 1; // 23 AHB single
+ uint32_t inv_desc_endian : 1; // 24 Inverse descriptor endian
+ uint32_t rsv25_31 : 7; // 25..31 Reserved
+} dwc2_gahbcfg_t;
+TU_VERIFY_STATIC(sizeof(dwc2_gahbcfg_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t timeout_cal : 3; /* 0..2 Timeout calibration.
+ The USB standard timeout value for high-speed operation is 736 to 816 (inclusive) bit times. The USB standard
+ timeout value for full- speed operation is 16 to 18 (inclusive) bit times. The application must program this field
+ based on the speed of enumeration. The number of bit times added per PHY clock are as follows:
+ - High-speed: PHY clock One 30-MHz = 16 bit times, One 60-MHz = 8 bit times
+ - Full-speed: PHY clock One 30-MHz = 0.4 bit times, One 60-MHz = 0.2 bit times, One 48-MHz = 0.25 bit times */
+ uint32_t phy_if16 : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits
+ uint32_t ulpi_utmi_sel : 1; // 4 ULPI/UTMI select. 0: UTMI+, 1: ULPI
+ uint32_t fs_intf_sel : 1; // 5 Fullspeed serial interface select. 0: 6-pin, 1: 3-pin
+ uint32_t phy_sel : 1; // 6 HS/FS PHY selection. 0: HS UTMI+ or ULPI, 1: FS serial transceiver
+ uint32_t ddr_sel : 1; // 7 ULPI DDR select. 0: Single data rate 8-bit, 1: Double data rate 4-bit
+ uint32_t srp_capable : 1; // 8 SRP-capable
+ uint32_t hnp_capable : 1; // 9 HNP-capable
+ uint32_t turnaround_time : 4; // 10..13 Turnaround time. 9: 8-bit UTMI+, 5: 16-bit UTMI+
+ uint32_t rsv14 : 1; // 14 Reserved
+ uint32_t phy_low_power_clk_sel : 1; /* 15 PHY low-power clock select either 480-MHz or 48-MHz (low-power) PHY mode.
+ In FS/LS modes, the PHY can usually operate on a 48-MHz clock to save power. This bit is valid only for UTMI+ PHYs.
+ - 0: 480 Mhz internal PLL: the UTMI interface operates at either 60 MHz (8 bit) or 30 MHz (16-bit)
+ - 1 48 Mhz external clock: the UTMI interface operates at 48 MHz in FS mode and at either 48 or 6 MHz in LS mode */
+ uint32_t otg_i2c_sel : 1; // 16 OTG I2C interface select. 0: UTMI-FS, 1: I2C for OTG signals
+ uint32_t ulpi_fsls : 1; /* 17 ULPI FS/LS select. 0: ULPI, 1: ULPI FS/LS.
+ valid only when the FS serial transceiver is selected on the ULPI PHY. */
+ uint32_t ulpi_auto_resume : 1; // 18 ULPI Auto-resume
+ uint32_t ulpi_clk_sus_m : 1; // 19 ULPI Clock SuspendM
+ uint32_t ulpi_ext_vbus_drv : 1; // 20 ULPI External VBUS Drive
+ uint32_t ulpi_int_vbus_indicator : 1; // 21 ULPI Internal VBUS Indicator
+ uint32_t term_sel_dl_pulse : 1; // 22 TermSel DLine pulsing
+ uint32_t indicator_complement : 1; // 23 Indicator complement
+ uint32_t indicator_pass_through : 1; // 24 Indicator pass through
+ uint32_t ulpi_if_protect_disable : 1; // 25 ULPI interface protect disable
+ uint32_t ic_usb_capable : 1; // 26 IC_USB Capable
+ uint32_t ic_usb_traf_ctl : 1; // 27 IC_USB Traffic Control
+ uint32_t tx_end_delay : 1; // 28 TX end delay
+ uint32_t force_host_mode : 1; // 29 Force host mode
+ uint32_t force_dev_mode : 1; // 30 Force device mode
+ uint32_t corrupt_tx_pkt : 1; // 31 Corrupt Tx packet. 0: normal, 1: debug
+} dwc2_gusbcfg_t;
+TU_VERIFY_STATIC(sizeof(dwc2_gusbcfg_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t core_soft_rst : 1; // 0 Core Soft Reset
+ uint32_t piufs_soft_rst : 1; // 1 PIU FS Dedicated Controller Soft Reset
+ uint32_t frame_counter_rst : 1; // 2 Frame Counter Reset (host)
+ uint32_t intoken_q_flush : 1; // 3 IN Token Queue Flush
+ uint32_t rx_fifo_flush : 1; // 4 RX FIFO Flush
+ uint32_t tx_fifo_flush : 1; // 5 TX FIFO Flush
+ uint32_t tx_fifo_num : 5; // 6..10 TX FIFO Number
+ uint32_t rsv11_28 :18; // 11..28 Reserved
+ uint32_t core_soft_rst_done : 1; // 29 Core Soft Reset Done, from v4.20a
+ uint32_t dma_req : 1; // 30 DMA Request
+ uint32_t ahb_idle : 1; // 31 AHB Idle
+} dwc2_grstctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size");
+typedef struct TU_ATTR_PACKED {
+ uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number
+ uint32_t byte_count :11; // 4..14 Byte Count
+ uint32_t dpid : 2; // 15..16 Data PID
+ uint32_t packet_status : 4; // 17..20 Packet Status
+ uint32_t frame_number : 4; // 21..24 Frame Number
+ uint32_t rsv25_31 : 7; // 25..31 Reserved
+} dwc2_grxstsp_t;
+TU_VERIFY_STATIC(sizeof(dwc2_grxstsp_t) == 4, "incorrect size");
+
+// Hardware Configuration
+typedef struct TU_ATTR_PACKED {
+ uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode
+ uint32_t arch : 2; // 3..4 Slave/External/Internal DMA
+ uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split
+ uint32_t hs_phy_type : 2; // 6..7 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI
+ uint32_t fs_phy_type : 2; // 8..9 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI
+ uint32_t num_dev_ep : 4; // 10..13 Number of device endpoints (excluding EP0)
+ uint32_t num_host_ch : 4; // 14..17 Number of host channel (excluding control)
+ uint32_t period_channel_support : 1; // 18 Support Periodic OUT Host Channel
+ uint32_t enable_dynamic_fifo : 1; // 19 Dynamic FIFO Sizing Enabled
+ uint32_t mul_proc_intrpt : 1; // 20 Multi-Processor Interrupt enabled (OTG_MULTI_PROC_INTRPT)
+ uint32_t reserved21 : 1; // 21 reserved
+ uint32_t nptx_q_depth : 2; // 22..23 Non-periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
+ uint32_t ptx_q_depth : 2; // 24..25 Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
+ uint32_t token_q_depth : 5; // 26..30 Device IN token sequence learning queue depth: 0-30
+ uint32_t otg_enable_ic_usb : 1; // 31 IC_USB mode specified for mode of operation
+} dwc2_ghwcfg2_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg2_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED
-{
- uint32_t xfer_size_width : 4; // Transfer size counter in bits = 11 + n (max 19 bits)
- uint32_t packet_size_width : 3; // Packet size counter in bits = 4 + n (max 10 bits)
- uint32_t otg_enable : 1; // 1 is OTG capable
- uint32_t i2c_enable : 1; // I2C interface is available
- uint32_t vendor_ctrl_itf : 1; // Vendor control interface is available
- uint32_t optional_feature_removed : 1; // remove User ID, GPIO, SOF toggle & counter
- uint32_t synch_reset : 1; // 0: async reset | 1: synch reset
- uint32_t otg_adp_support : 1; // ADP logic is present along with HSOTG controller
- uint32_t otg_enable_hsic : 1; // 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC
- uint32_t battery_charger_support : 1; // support battery charger
- uint32_t lpm_mode : 1; // LPC mode
- uint32_t total_fifo_size : 16; // DFIFO depth value in terms of 32-bit words
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_size_width : 4; // 0..3 Transfer size counter in bits = 11 + n (max 19 bits)
+ uint32_t packet_size_width : 3; // 4..6 Packet size counter in bits = 4 + n (max 10 bits)
+ uint32_t otg_enable : 1; // 7 OTG capable
+ uint32_t i2c_enable : 1; // 8 I2C interface is available
+ uint32_t vendor_ctrl_itf : 1; // 9 Vendor control interface is available
+ uint32_t optional_feature_removed : 1; // 10 remove User ID, GPIO, SOF toggle & counter to save gate count
+ uint32_t synch_reset : 1; // 11 0: async reset | 1: synch reset
+ uint32_t otg_adp_support : 1; // 12 ADP logic is present along with HSOTG controller
+ uint32_t otg_enable_hsic : 1; // 13 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC
+ uint32_t battery_charger_support : 1; // s14 upport battery charger
+ uint32_t lpm_mode : 1; // 15 LPM mode
+ uint32_t dfifo_depth : 16; // DFIFO depth - EP_LOC_CNT in terms of 32-bit words
}dwc2_ghwcfg3_t;
-
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size");
-typedef struct TU_ATTR_PACKED
-{
- uint32_t num_dev_period_in_ep : 4; // Number of Device Periodic IN Endpoints
- uint32_t power_optimized : 1; // Partial Power Down Enabled
- uint32_t ahb_freq_min : 1; // 1: minimum of AHB frequency is less than 60 MHz
- uint32_t hibernation : 1; // Hibernation feature is enabled
- uint32_t reserved7 : 3;
- uint32_t service_interval_mode : 1; // Service Interval supported
- uint32_t ipg_isoc_en : 1; // IPG ISOC supported
- uint32_t acg_enable : 1; // ACG enabled
- uint32_t reserved13 : 1;
- uint32_t utmi_phy_data_width : 2; // 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable
- uint32_t dev_ctrl_ep_num : 4; // Number of Device control endpoints in addition to EP0
- uint32_t iddg_filter_enabled : 1;
- uint32_t vbus_valid_filter_enabled : 1;
- uint32_t a_valid_filter_enabled : 1;
- uint32_t b_valid_filter_enabled : 1;
- uint32_t dedicated_fifos : 1; // Dedicated tx fifo for device IN Endpoint is enabled
- uint32_t num_dev_in_eps : 4; // Number of Device IN Endpoints including EP0
- uint32_t dma_desc_enable : 1; // scatter/gather DMA configuration
- uint32_t dma_dynamic : 1; // Dynamic scatter/gather DMA
+typedef struct TU_ATTR_PACKED {
+ uint32_t num_dev_period_in_ep : 4; // 0..3 Number of Device Periodic IN Endpoints
+ uint32_t partial_powerdown : 1; // 4 Partial Power Down Enabled
+ uint32_t ahb_freq_min : 1; // 5 1: minimum of AHB frequency is less than 60 MHz
+ uint32_t hibernation : 1; // 6 Hibernation feature is enabled
+ uint32_t extended_hibernation : 1; // 7 Extended Hibernation feature is enabled
+ uint32_t reserved8 : 1; // 8 Reserved
+ uint32_t enhanced_lpm_support1 : 1; // 9 Enhanced LPM Support1
+ uint32_t service_interval_flow : 1; // 10 Service Interval flow is supported
+ uint32_t ipg_isoc_support : 1; // 11 Interpacket GAP ISO OUT worst-case is supported
+ uint32_t acg_support : 1; // 12 Active clock gating is supported
+ uint32_t enhanced_lpm_support : 1; // 13 Enhanced LPM Support
+ uint32_t phy_data_width : 2; // 14..15 0: 8 bits | 1: 16 bits | 2: 8/16 software selectable
+ uint32_t ctrl_ep_num : 4; // 16..19 Number of Device control endpoints in addition to EP0
+ uint32_t iddg_filter : 1; // 20 IDDG Filter Enabled
+ uint32_t vbus_valid_filter : 1; // 21 VBUS Valid Filter Enabled
+ uint32_t a_valid_filter : 1; // 22 A Valid Filter Enabled
+ uint32_t b_valid_filter : 1; // 23 B Valid Filter Enabled
+ uint32_t session_end_filter : 1; // 24 Session End Filter Enabled
+ uint32_t dedicated_fifos : 1; // 25 Dedicated tx fifo for device IN Endpoint
+ uint32_t num_dev_in_eps : 4; // 26..29 Number of Device IN Endpoints including EP0
+ uint32_t dma_desc_enabled : 1; // scatter/gather DMA configuration enabled
+ uint32_t dma_desc_dynamic : 1; // Dynamic scatter/gather DMA
}dwc2_ghwcfg4_t;
-
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size");
+//--------------------------------------------------------------------
+// Host Register Bitfield
+//--------------------------------------------------------------------
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU
+ // 24..31 is top entry in the request queue that is currently being processed by the MAC
+ uint32_t qtop_terminate : 1; // 24 Last entry for selected channel
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+} dwc2_hnptxsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
+ uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue
+ uint32_t qtop_terminate : 1; // 23 Last entry for selected channel
+ uint32_t qtop_last_period : 1; // 24 Last entry for selected channel is a periodic entry
+ uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
+ uint32_t qtop_ch_num : 4; // 27..30 Channel number
+ uint32_t qtop_odd_frame : 1; // 31 Send in odd frame
+} dwc2_hptxsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hptxsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t conn_status : 1; // 0 Port connect status
+ uint32_t conn_detected : 1; // 1 Port connect detected
+ uint32_t enable : 1; // 2 Port enable status
+ uint32_t enable_change : 1; // 3 Port enable change
+ uint32_t over_current_active : 1; // 4 Port Over-current active
+ uint32_t over_current_change : 1; // 5 Port Over-current change
+ uint32_t resume : 1; // 6 Port resume
+ uint32_t suspend : 1; // 7 Port suspend
+ uint32_t reset : 1; // 8 Port reset
+ uint32_t rsv9 : 1; // 9 Reserved
+ uint32_t line_status : 2; // 10..11 Line status
+ uint32_t power : 1; // 12 Port power
+ uint32_t test_control : 4; // 13..16 Port Test control
+ uint32_t speed : 2; // 17..18 Port speed
+ uint32_t rsv19_31 :13; // 19..31 Reserved
+}dwc2_hprt_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t ep_size : 11; // 0..10 Maximum packet size
+ uint32_t ep_num : 4; // 11..14 Endpoint number
+ uint32_t ep_dir : 1; // 15 Endpoint direction
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t low_speed_dev : 1; // 17 Low-speed device
+ uint32_t ep_type : 2; // 18..19 Endpoint type
+ uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count
+ uint32_t dev_addr : 7; // 22..28 Device address
+ uint32_t odd_frame : 1; // 29 Odd frame
+ uint32_t disable : 1; // 30 Channel disable
+ uint32_t enable : 1; // 31 Channel enable
+} dwc2_channel_char_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t hub_port : 7; // 0..6 Hub port number
+ uint32_t hub_addr : 7; // 7..13 Hub address
+ uint32_t xact_pos : 2; // 14..15 Transaction position
+ uint32_t split_compl : 1; // 16 Split completion
+ uint32_t rsv17_30 : 14; // 17..30 Reserved
+ uint32_t split_en : 1; // 31 Split enable
+} dwc2_channel_split_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t pid : 2; // 29..30 Packet ID
+ uint32_t do_ping : 1; // 31 Do PING
+} dwc2_channel_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t num : 16; // 0..15 Frame number
+ uint32_t remainning : 16; // 16..31 Frame remaining
+} dwc2_hfnum_t;
+TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size");
+
// Host Channel
-typedef struct
-{
- volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
- volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
- volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
- volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
- volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
- volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
- uint32_t reserved518; // 518 + 20*ch
- volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
+typedef struct {
+ union {
+ volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
+ volatile dwc2_channel_char_t hcchar_bm;
+ };
+ union {
+ volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
+ volatile dwc2_channel_split_t hcsplt_bm;
+ };
+ volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
+ volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
+ union {
+ volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
+ volatile dwc2_channel_tsize_t hctsiz_bm;
+ };
+ volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
+ uint32_t reserved518; // 518 + 20*ch
+ volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
} dwc2_channel_t;
-// Endpoint IN
-typedef struct
-{
- volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control
- uint32_t reserved04; // 904
- volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt
- uint32_t reserved0c; // 90C
- volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size
- volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address
- volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status
- uint32_t reserved1c; // 91C
-} dwc2_epin_t;
+//--------------------------------------------------------------------
+// Device Register Bitfield
+//--------------------------------------------------------------------
+typedef struct TU_ATTR_PACKED {
+ uint32_t speed : 2; // 0..1 Speed
+ uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake
+ uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode
+ uint32_t address : 7; // 4..10 Device address
+ uint32_t period_frame_interval : 2; // 11..12 Periodic frame interval
+ uint32_t en_out_nak : 1; // 13 Enable Device OUT NAK
+ uint32_t xcvr_delay : 1; // 14 Transceiver delay
+ uint32_t erratic_int_mask : 1; // 15 Erratic interrupt mask
+ uint32_t rsv16 : 1; // 16 Reserved
+ uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support
+ uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count
+ uint32_t dma_desc : 1; // 23 Enable scatter/gatter DMA descriptor
+ uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gatter DMA
+ uint32_t resume_valid : 6; // 26..31 Resume valid period
+} dwc2_dcfg_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dcfg_t) == 4, "incorrect size");
-// Endpoint OUT
-typedef struct
-{
- volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control
- uint32_t reserved04; // B04
- volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt
- uint32_t reserved0c; // B0C
- volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size
- volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address
- uint32_t reserved18[2]; // B18..B1C
-} dwc2_epout_t;
+typedef struct TU_ATTR_PACKED {
+ uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal
+ uint32_t soft_disconnet : 1; // 1 Soft disconnect
+ uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status
+ uint32_t gout_nak_status : 1; // 3 Global OUT NAK status
+ uint32_t test_control : 3; // 4..6 Test control
+ uint32_t set_gnp_in_nak : 1; // 7 Set global non-periodic IN NAK
+ uint32_t clear_gnp_in_nak : 1; // 8 Clear global non-periodic IN NAK
+ uint32_t set_gout_nak : 1; // 9 Set global OUT NAK
+ uint32_t clear_gout_nak : 1; // 10 Clear global OUT NAK
+ uint32_t poweron_prog_done : 1; // 11 Power-on programming done
+ uint32_t rsv12 : 1; // 12 Reserved
+ uint32_t global_multi_count : 2; // 13..14 Global multi-count
+ uint32_t ignore_frame_number : 1; // 15 Ignore frame number
+ uint32_t nak_on_babble : 1; // 16 NAK on babble
+ uint32_t en_cont_on_bna : 1; // 17 Enable continue on BNA
+ uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject
+ uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint
+ uint32_t rsv20_31 :12; // 20..31 Reserved
+} dwc2_dctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dctl_t) == 4, "incorrect size");
-typedef struct
-{
- //------------- Core Global -------------//
- volatile uint32_t gotgctl; // 000 OTG Control and Status
- volatile uint32_t gotgint; // 004 OTG Interrupt
- volatile uint32_t gahbcfg; // 008 AHB Configuration
- volatile uint32_t gusbcfg; // 00c USB Configuration
- volatile uint32_t grstctl; // 010 Reset
- volatile uint32_t gintsts; // 014 Interrupt
- volatile uint32_t gintmsk; // 018 Interrupt Mask
- volatile uint32_t grxstsr; // 01c Receive Status Debug Read
- volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
- volatile uint32_t grxfsiz; // 024 Receive FIFO Size
-union {
- volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
- volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size
-};
- volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
- volatile uint32_t gi2cctl; // 030 I2C Address
- volatile uint32_t gpvndctl; // 034 PHY Vendor Control
-union {
- volatile uint32_t ggpio; // 038 General Purpose IO
- volatile uint32_t stm32_gccfg; // 038 STM32 General Core Configuration
-};
- volatile uint32_t guid; // 03C User (Application programmable) ID
- volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version
- volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep)
-union {
- volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2
- dwc2_ghwcfg2_t ghwcfg2_bm;
-};
-union {
- volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3
- dwc2_ghwcfg3_t ghwcfg3_bm;
-};
-union {
- volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4
- dwc2_ghwcfg4_t ghwcfg4_bm;
-};
- volatile uint32_t glpmcfg; // 054 Core LPM Configuration
- volatile uint32_t gpwrdn; // 058 Power Down
- volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration
- volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status
- uint32_t reserved64[39]; // 064..0FF
- volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size
- volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size
- uint32_t reserved140[176]; // 140..3FF
+typedef struct TU_ATTR_PACKED {
+ uint32_t suspend_status : 1; // 0 Suspend status
+ uint32_t enum_speed : 2; // 1..2 Enumerated speed
+ uint32_t erratic_err : 1; // 3 Erratic error
+ uint32_t rsv4_7 : 4; // 4..7 Reserved
+ uint32_t frame_number : 14; // 8..21 Frame/MicroFrame number
+ uint32_t line_status : 2; // 22..23 Line status
+ uint32_t rsv24_31 : 8; // 24..31 Reserved
+} dwc2_dsts_t;
+TU_VERIFY_STATIC(sizeof(dwc2_dsts_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t timeout : 1; // 3 Timeout
+ uint32_t in_rx_txfe : 1; // 4 IN token received when TxFIFO is empty
+ uint32_t in_rx_ep_mismatch : 1; // 5 IN token received with EP mismatch
+ uint32_t in_ep_nak_effective : 1; // 6 IN endpoint NAK effective
+ uint32_t txfifo_empty : 1; // 7 TX FIFO empty
+ uint32_t txfifo_underrun : 1; // 8 Tx FIFO under run
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t rsv14_31 :17; // 15..31 Reserved
+} dwc2_diepint_t;
+TU_VERIFY_STATIC(sizeof(dwc2_diepint_t) == 4, "incorrect size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bit
+ uint32_t next_ep : 4; // 11..14 Next endpoint number
+ uint32_t active : 1; // 15 Active
+ const uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
+ const uint32_t nak_status : 1; // 17 NAK status
+ uint32_t type : 2; // 18..19 Endpoint type
+ uint32_t rsv20 : 1; // 20 Reserved
+ uint32_t stall : 1; // 21 Stall
+ uint32_t tx_fifo_num : 4; // 22..25 Tx FIFO number (IN)
+ uint32_t clear_nak : 1; // 26 Clear NAK
+ uint32_t set_nak : 1; // 27 Set NAK
+ uint32_t set_data0_iso_even : 1; // 28 Set DATA0 if bulk/interrupt, even frame for isochronous
+ uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous
+ uint32_t disable : 1; // 30 Disable
+ uint32_t enable : 1; // 31 Enable
+} dwc2_depctl_t;
+TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size");
- //------------- Host -------------//
- volatile uint32_t hcfg; // 400 Host Configuration
- volatile uint32_t hfir; // 404 Host Frame Interval
- volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
- uint32_t reserved40c; // 40C
- volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
- volatile uint32_t haint; // 414 Host All Channels Interrupt
- volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
- volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
- uint32_t reserved420[8]; // 420..43F
- volatile uint32_t hprt; // 440 Host Port Control and Status
- uint32_t reserved444[47]; // 444..4FF
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_complete : 1; // 0 Transfer complete
+ uint32_t disabled : 1; // 1 Endpoint disabled
+ uint32_t ahb_err : 1; // 2 AHB error
+ uint32_t setup_phase_done : 1; // 3 Setup phase done
+ uint32_t out_rx_ep_disabled : 1; // 4 OUT token received when endpoint disabled
+ uint32_t status_phase_rx : 1; // 5 Status phase received
+ uint32_t setup_b2b : 1; // 6 Setup packet back-to-back
+ uint32_t rsv7 : 1; // 7 Reserved
+ uint32_t out_packet_err : 1; // 8 OUT packet error
+ uint32_t bna : 1; // 9 Buffer not available
+ uint32_t rsv10 : 1; // 10 Reserved
+ uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status
+ uint32_t babble_err : 1; // 12 Babble error
+ uint32_t nak : 1; // 13 NAK
+ uint32_t nyet : 1; // 14 NYET
+ uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only)
+ uint32_t rsv16_31 :16; // 16..31 Reserved
+} dwc2_doepint_t;
+TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size");
- //------------- Host Channel -------------//
- dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15
- uint32_t reserved700[64]; // 700..7FF
+typedef struct TU_ATTR_PACKED {
+ uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
+ uint32_t packet_count : 10; // 19..28 Number of packets
+ uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID
+} dwc2_ep_tsize_t;
+TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
- //------------- Device -------------//
- volatile uint32_t dcfg; // 800 Device Configuration
- volatile uint32_t dctl; // 804 Device Control
- volatile uint32_t dsts; // 808 Device Status (RO)
- uint32_t reserved80c; // 80C
- volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
- volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
- volatile uint32_t daint; // 818 Device All Endpoints Interrupt
- volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
- volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
- volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2
- volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time
- volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time
- volatile uint32_t dthrctl; // 830 Device threshold Control
- volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask
- volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt
- volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt msk
- volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask
- volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask
- uint32_t reserved8c0[16]; // 8C0..8FF
+// Device IN/OUT Endpoint
+typedef struct {
+ union {
+ volatile uint32_t diepctl;
+ volatile uint32_t doepctl;
- //------------- Device Endpoint -------------//
- dwc2_epin_t epin[16]; // 900..AFF IN Endpoints
- dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints
- uint32_t reservedd00[64]; // D00..DFF
+ volatile uint32_t ctl;
+ volatile dwc2_depctl_t ctl_bm;
+ };
+ uint32_t rsv04;
+ union {
+ volatile uint32_t intr;
- //------------- Power Clock -------------//
- volatile uint32_t pcgctl; // E00 Power and Clock Gating Control
- volatile uint32_t pcgctl1; // E04
- uint32_t reservede08[126]; // E08..FFF
+ volatile uint32_t diepint;
+ volatile dwc2_diepint_t diepint_bm;
- //------------- FIFOs -------------//
- // Word-accessed only using first pointer since it auto shift
- volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO
+ volatile uint32_t doepint;
+ volatile dwc2_doepint_t doepint_bm;
+ };
+ uint32_t rsv0c;
+ union {
+ volatile uint32_t dieptsiz;
+ volatile uint32_t doeptsiz;
+ volatile uint32_t tsiz;
+ volatile dwc2_ep_tsize_t tsiz_bm;
+ };
+ union {
+ volatile uint32_t diepdma;
+ volatile uint32_t doepdma;
+ };
+ volatile uint32_t dtxfsts;
+ uint32_t rsv1c;
+}dwc2_dep_t;
+
+TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size");
+
+//--------------------------------------------------------------------
+// CSR Register Map
+//--------------------------------------------------------------------
+typedef struct {
+ //------------- Core Global -------------//
+ union {
+ volatile uint32_t gotgctl; // 000 OTG Control and Status
+ volatile dwc2_gotgctl_t gotgctl_bm;
+ };
+ union {
+ volatile uint32_t gotgint; // 004 OTG Interrupt
+ volatile dwc2_gotgint_t gotgint_bm;
+ };
+ union {
+ volatile uint32_t gahbcfg; // 008 AHB Configuration
+ volatile dwc2_gahbcfg_t gahbcfg_bm;
+ };
+ union {
+ volatile uint32_t gusbcfg; // 00c USB Configuration
+ volatile dwc2_gusbcfg_t gusbcfg_bm;
+ };
+ union {
+ volatile uint32_t grstctl; // 010 Reset
+ volatile dwc2_grstctl_t grstctl_bm;
+ };
+ volatile uint32_t gintsts; // 014 Interrupt
+ volatile uint32_t gintmsk; // 018 Interrupt Mask
+ volatile uint32_t grxstsr; // 01c Receive Status Debug Read
+ union {
+ volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
+ volatile dwc2_grxstsp_t grxstsp_bm;
+ };
+ volatile uint32_t grxfsiz; // 024 Receive FIFO Size
+ union {
+ volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
+ volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size
+ };
+ union {
+ volatile uint32_t hnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
+ volatile dwc2_hnptxsts_t hnptxsts_bm;
+ volatile uint32_t gnptxsts;
+ };
+ volatile uint32_t gi2cctl; // 030 I2C Address
+ volatile uint32_t gpvndctl; // 034 PHY Vendor Control
+ union {
+ volatile uint32_t ggpio; // 038 General Purpose IO
+ volatile uint32_t stm32_gccfg; // 038 STM32 General Core Configuration
+ };
+ volatile uint32_t guid; // 03C User (Application programmable) ID
+ volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version
+ volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep)
+ union {
+ volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2
+ volatile dwc2_ghwcfg2_t ghwcfg2_bm;
+ };
+ union {
+ volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3
+ volatile dwc2_ghwcfg3_t ghwcfg3_bm;
+ };
+ union {
+ volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4
+ volatile dwc2_ghwcfg4_t ghwcfg4_bm;
+ };
+ volatile uint32_t glpmcfg; // 054 Core LPM Configuration
+ volatile uint32_t gpwrdn; // 058 Power Down
+ volatile uint32_t gdfifocfg; // 05C DFIFO Software Configuration
+ volatile uint32_t gadpctl; // 060 ADP Timer, Control and Status
+ uint32_t reserved64[39]; // 064..0FF
+ volatile uint32_t hptxfsiz; // 100 Host Periodic Tx FIFO Size
+ volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size
+ uint32_t reserved140[176]; // 140..3FF
+
+ //------------ Host -------------//
+ volatile uint32_t hcfg; // 400 Host Configuration
+ volatile uint32_t hfir; // 404 Host Frame Interval
+ union {
+ volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
+ volatile dwc2_hfnum_t hfnum_bm;
+ };
+ uint32_t reserved40c; // 40C
+ union {
+ volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
+ volatile dwc2_hptxsts_t hptxsts_bm;
+ };
+ volatile uint32_t haint; // 414 Host All Channels Interrupt
+ volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
+ volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
+ uint32_t reserved420[8]; // 420..43F
+ union {
+ volatile uint32_t hprt; // 440 Host Port Control and Status
+ volatile dwc2_hprt_t hprt_bm;
+ };
+ uint32_t reserved444[47]; // 444..4FF
+
+ //------------- Host Channel -------------//
+ dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15
+ uint32_t reserved700[64]; // 700..7FF
+
+ //------------- Device -----------//
+ union {
+ volatile uint32_t dcfg; // 800 Device Configuration
+ volatile dwc2_dcfg_t dcfg_bm;
+ };
+ union {
+ volatile uint32_t dctl; // 804 Device Control
+ volatile dwc2_dctl_t dctl_bm;
+ };
+ union {
+ volatile uint32_t dsts; // 808 Device Status (RO)
+ volatile dwc2_dsts_t dsts_bm;
+ };
+ uint32_t reserved80c; // 80C
+ union {
+ volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
+ volatile dwc2_diepint_t diepmsk_bm;
+ };
+ union {
+ volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
+ volatile dwc2_doepint_t doepmsk_bm;
+ };
+ volatile uint32_t daint; // 818 Device All Endpoints Interrupt
+ volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
+ volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
+ volatile uint32_t dtknqr2; // 824 Device IN token sequence learning queue read2
+ volatile uint32_t dvbusdis; // 828 Device VBUS Discharge Time
+ volatile uint32_t dvbuspulse; // 82C Device VBUS Pulsing Time
+ volatile uint32_t dthrctl; // 830 Device threshold Control
+ volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask
+
+ // Device Each Endpoint (IN/OUT) Interrupt/Mask for generating dedicated EP interrupt line
+ // require OTG_MULTI_PROC_INTRPT=1
+ volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt
+ volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt mask
+ volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask
+ volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask
+ uint32_t reserved8c0[16]; // 8C0..8FF
+
+ //------------- Device Endpoint -------------//
+ union {
+ dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
+ struct {
+ dwc2_dep_t epin[16]; // 900..AFF IN Endpoints
+ dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints
+ };
+ };
+ uint32_t reservedd00[64]; // D00..DFF
+
+ //------------- Power Clock -------------//
+ volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control
+ volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1
+ uint32_t reservede08[126]; // E08..FFF
+
+ //------------- FIFOs -------------//
+ // Word-accessed only using first pointer since it auto shift
+ volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO
} dwc2_regs_t;
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, hcfg ) == 0x0400, "incorrect size");
@@ -289,7 +794,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size");
-TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size");
+TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgcctl) == 0x0E00, "incorrect size");
TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
//--------------------------------------------------------------------+
@@ -298,103 +803,105 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
/******************** Bit definition for GOTGCTL register ********************/
#define GOTGCTL_SRQSCS_Pos (0U)
-#define GOTGCTL_SRQSCS_Msk (0x1UL << GOTGCTL_SRQSCS_Pos) // 0x00000001 */
-#define GOTGCTL_SRQSCS GOTGCTL_SRQSCS_Msk // Session request success */
+#define GOTGCTL_SRQSCS_Msk (0x1UL << GOTGCTL_SRQSCS_Pos) // 0x00000001
+#define GOTGCTL_SRQSCS GOTGCTL_SRQSCS_Msk // Session request success
#define GOTGCTL_SRQ_Pos (1U)
-#define GOTGCTL_SRQ_Msk (0x1UL << GOTGCTL_SRQ_Pos) // 0x00000002 */
-#define GOTGCTL_SRQ GOTGCTL_SRQ_Msk // Session request */
+#define GOTGCTL_SRQ_Msk (0x1UL << GOTGCTL_SRQ_Pos) // 0x00000002
+#define GOTGCTL_SRQ GOTGCTL_SRQ_Msk // Session request
#define GOTGCTL_VBVALOEN_Pos (2U)
-#define GOTGCTL_VBVALOEN_Msk (0x1UL << GOTGCTL_VBVALOEN_Pos) // 0x00000004 */
-#define GOTGCTL_VBVALOEN GOTGCTL_VBVALOEN_Msk // VBUS valid override enable */
+#define GOTGCTL_VBVALOEN_Msk (0x1UL << GOTGCTL_VBVALOEN_Pos) // 0x00000004
+#define GOTGCTL_VBVALOEN GOTGCTL_VBVALOEN_Msk // VBUS valid override enable
#define GOTGCTL_VBVALOVAL_Pos (3U)
-#define GOTGCTL_VBVALOVAL_Msk (0x1UL << GOTGCTL_VBVALOVAL_Pos) // 0x00000008 */
-#define GOTGCTL_VBVALOVAL GOTGCTL_VBVALOVAL_Msk // VBUS valid override value */
+#define GOTGCTL_VBVALOVAL_Msk (0x1UL << GOTGCTL_VBVALOVAL_Pos) // 0x00000008
+#define GOTGCTL_VBVALOVAL GOTGCTL_VBVALOVAL_Msk // VBUS valid override value
#define GOTGCTL_AVALOEN_Pos (4U)
-#define GOTGCTL_AVALOEN_Msk (0x1UL << GOTGCTL_AVALOEN_Pos) // 0x00000010 */
-#define GOTGCTL_AVALOEN GOTGCTL_AVALOEN_Msk // A-peripheral session valid override enable */
+#define GOTGCTL_AVALOEN_Msk (0x1UL << GOTGCTL_AVALOEN_Pos) // 0x00000010
+#define GOTGCTL_AVALOEN GOTGCTL_AVALOEN_Msk // A-peripheral session valid override enable
#define GOTGCTL_AVALOVAL_Pos (5U)
-#define GOTGCTL_AVALOVAL_Msk (0x1UL << GOTGCTL_AVALOVAL_Pos) // 0x00000020 */
-#define GOTGCTL_AVALOVAL GOTGCTL_AVALOVAL_Msk // A-peripheral session valid override value */
+#define GOTGCTL_AVALOVAL_Msk (0x1UL << GOTGCTL_AVALOVAL_Pos) // 0x00000020
+#define GOTGCTL_AVALOVAL GOTGCTL_AVALOVAL_Msk // A-peripheral session valid override value
#define GOTGCTL_BVALOEN_Pos (6U)
-#define GOTGCTL_BVALOEN_Msk (0x1UL << GOTGCTL_BVALOEN_Pos) // 0x00000040 */
-#define GOTGCTL_BVALOEN GOTGCTL_BVALOEN_Msk // B-peripheral session valid override enable */
+#define GOTGCTL_BVALOEN_Msk (0x1UL << GOTGCTL_BVALOEN_Pos) // 0x00000040
+#define GOTGCTL_BVALOEN GOTGCTL_BVALOEN_Msk // B-peripheral session valid override enable
#define GOTGCTL_BVALOVAL_Pos (7U)
-#define GOTGCTL_BVALOVAL_Msk (0x1UL << GOTGCTL_BVALOVAL_Pos) // 0x00000080 */
-#define GOTGCTL_BVALOVAL GOTGCTL_BVALOVAL_Msk // B-peripheral session valid override value */
+#define GOTGCTL_BVALOVAL_Msk (0x1UL << GOTGCTL_BVALOVAL_Pos) // 0x00000080
+#define GOTGCTL_BVALOVAL GOTGCTL_BVALOVAL_Msk // B-peripheral session valid override value
#define GOTGCTL_HNGSCS_Pos (8U)
-#define GOTGCTL_HNGSCS_Msk (0x1UL << GOTGCTL_HNGSCS_Pos) // 0x00000100 */
-#define GOTGCTL_HNGSCS GOTGCTL_HNGSCS_Msk // Host set HNP enable */
+#define GOTGCTL_HNGSCS_Msk (0x1UL << GOTGCTL_HNGSCS_Pos) // 0x00000100
+#define GOTGCTL_HNGSCS GOTGCTL_HNGSCS_Msk // Host set HNP enable
#define GOTGCTL_HNPRQ_Pos (9U)
-#define GOTGCTL_HNPRQ_Msk (0x1UL << GOTGCTL_HNPRQ_Pos) // 0x00000200 */
-#define GOTGCTL_HNPRQ GOTGCTL_HNPRQ_Msk // HNP request */
+#define GOTGCTL_HNPRQ_Msk (0x1UL << GOTGCTL_HNPRQ_Pos) // 0x00000200
+#define GOTGCTL_HNPRQ GOTGCTL_HNPRQ_Msk // HNP request
#define GOTGCTL_HSHNPEN_Pos (10U)
-#define GOTGCTL_HSHNPEN_Msk (0x1UL << GOTGCTL_HSHNPEN_Pos) // 0x00000400 */
-#define GOTGCTL_HSHNPEN GOTGCTL_HSHNPEN_Msk // Host set HNP enable */
+#define GOTGCTL_HSHNPEN_Msk (0x1UL << GOTGCTL_HSHNPEN_Pos) // 0x00000400
+#define GOTGCTL_HSHNPEN GOTGCTL_HSHNPEN_Msk // Host set HNP enable
#define GOTGCTL_DHNPEN_Pos (11U)
-#define GOTGCTL_DHNPEN_Msk (0x1UL << GOTGCTL_DHNPEN_Pos) // 0x00000800 */
-#define GOTGCTL_DHNPEN GOTGCTL_DHNPEN_Msk // Device HNP enabled */
+#define GOTGCTL_DHNPEN_Msk (0x1UL << GOTGCTL_DHNPEN_Pos) // 0x00000800
+#define GOTGCTL_DHNPEN GOTGCTL_DHNPEN_Msk // Device HNP enabled
#define GOTGCTL_EHEN_Pos (12U)
-#define GOTGCTL_EHEN_Msk (0x1UL << GOTGCTL_EHEN_Pos) // 0x00001000 */
-#define GOTGCTL_EHEN GOTGCTL_EHEN_Msk // Embedded host enable */
+#define GOTGCTL_EHEN_Msk (0x1UL << GOTGCTL_EHEN_Pos) // 0x00001000
+#define GOTGCTL_EHEN GOTGCTL_EHEN_Msk // Embedded host enable
#define GOTGCTL_CIDSTS_Pos (16U)
-#define GOTGCTL_CIDSTS_Msk (0x1UL << GOTGCTL_CIDSTS_Pos) // 0x00010000 */
-#define GOTGCTL_CIDSTS GOTGCTL_CIDSTS_Msk // Connector ID status */
+#define GOTGCTL_CIDSTS_Msk (0x1UL << GOTGCTL_CIDSTS_Pos) // 0x00010000
+#define GOTGCTL_CIDSTS GOTGCTL_CIDSTS_Msk // Connector ID status
#define GOTGCTL_DBCT_Pos (17U)
-#define GOTGCTL_DBCT_Msk (0x1UL << GOTGCTL_DBCT_Pos) // 0x00020000 */
-#define GOTGCTL_DBCT GOTGCTL_DBCT_Msk // Long/short debounce time */
+#define GOTGCTL_DBCT_Msk (0x1UL << GOTGCTL_DBCT_Pos) // 0x00020000
+#define GOTGCTL_DBCT GOTGCTL_DBCT_Msk // Long/short debounce time
#define GOTGCTL_ASVLD_Pos (18U)
-#define GOTGCTL_ASVLD_Msk (0x1UL << GOTGCTL_ASVLD_Pos) // 0x00040000 */
-#define GOTGCTL_ASVLD GOTGCTL_ASVLD_Msk // A-session valid */
+#define GOTGCTL_ASVLD_Msk (0x1UL << GOTGCTL_ASVLD_Pos) // 0x00040000
+#define GOTGCTL_ASVLD GOTGCTL_ASVLD_Msk // A-session valid
#define GOTGCTL_BSESVLD_Pos (19U)
-#define GOTGCTL_BSESVLD_Msk (0x1UL << GOTGCTL_BSESVLD_Pos) // 0x00080000 */
-#define GOTGCTL_BSESVLD GOTGCTL_BSESVLD_Msk // B-session valid */
+#define GOTGCTL_BSESVLD_Msk (0x1UL << GOTGCTL_BSESVLD_Pos) // 0x00080000
+#define GOTGCTL_BSESVLD GOTGCTL_BSESVLD_Msk // B-session valid
#define GOTGCTL_OTGVER_Pos (20U)
-#define GOTGCTL_OTGVER_Msk (0x1UL << GOTGCTL_OTGVER_Pos) // 0x00100000 */
-#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version */
+#define GOTGCTL_OTGVER_Msk (0x1UL << GOTGCTL_OTGVER_Pos) // 0x00100000
+#define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version
/******************** Bit definition for HCFG register ********************/
-#define HCFG_FSLSPCS_Pos (0U)
-#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003 */
-#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select */
-#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001 */
-#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002 */
-#define HCFG_FSLSS_Pos (2U)
-#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004 */
-#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support */
+#define HCFG_FSLS_PHYCLK_SEL_Pos (0U)
+#define HCFG_FSLS_PHYCLK_SEL_Msk (0x3UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000003
+#define HCFG_FSLS_PHYCLK_SEL HCFG_FSLS_PHYCLK_SEL_Msk // FS/LS PHY clock select
+#define HCFG_FSLS_PHYCLK_SEL_30_60MHZ (0x0UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000000
+#define HCFG_FSLS_PHYCLK_SEL_48MHZ (0x1UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000001
+#define HCFG_FSLS_PHYCLK_SEL_6MHZ (0x2UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000002
+
+#define HCFG_FSLS_ONLY_Pos (2U)
+#define HCFG_FSLS_ONLY_Msk (0x1UL << HCFG_FSLS_ONLY_Pos) // 0x00000004
+#define HCFG_FSLS_ONLY HCFG_FSLS_ONLY_Msk // FS- and LS-only support
/******************** Bit definition for PCGCR register ********************/
#define PCGCR_STPPCLK_Pos (0U)
-#define PCGCR_STPPCLK_Msk (0x1UL << PCGCR_STPPCLK_Pos) // 0x00000001 */
-#define PCGCR_STPPCLK PCGCR_STPPCLK_Msk // Stop PHY clock */
+#define PCGCR_STPPCLK_Msk (0x1UL << PCGCR_STPPCLK_Pos) // 0x00000001
+#define PCGCR_STPPCLK PCGCR_STPPCLK_Msk // Stop PHY clock
#define PCGCR_GATEHCLK_Pos (1U)
-#define PCGCR_GATEHCLK_Msk (0x1UL << PCGCR_GATEHCLK_Pos) // 0x00000002 */
-#define PCGCR_GATEHCLK PCGCR_GATEHCLK_Msk // Gate HCLK */
+#define PCGCR_GATEHCLK_Msk (0x1UL << PCGCR_GATEHCLK_Pos) // 0x00000002
+#define PCGCR_GATEHCLK PCGCR_GATEHCLK_Msk // Gate HCLK
#define PCGCR_PHYSUSP_Pos (4U)
-#define PCGCR_PHYSUSP_Msk (0x1UL << PCGCR_PHYSUSP_Pos) // 0x00000010 */
-#define PCGCR_PHYSUSP PCGCR_PHYSUSP_Msk // PHY suspended */
+#define PCGCR_PHYSUSP_Msk (0x1UL << PCGCR_PHYSUSP_Pos) // 0x00000010
+#define PCGCR_PHYSUSP PCGCR_PHYSUSP_Msk // PHY suspended
/******************** Bit definition for GOTGINT register ********************/
#define GOTGINT_SEDET_Pos (2U)
-#define GOTGINT_SEDET_Msk (0x1UL << GOTGINT_SEDET_Pos) // 0x00000004 */
-#define GOTGINT_SEDET GOTGINT_SEDET_Msk // Session end detected */
+#define GOTGINT_SEDET_Msk (0x1UL << GOTGINT_SEDET_Pos) // 0x00000004
+#define GOTGINT_SEDET GOTGINT_SEDET_Msk // Session end detected
#define GOTGINT_SRSSCHG_Pos (8U)
-#define GOTGINT_SRSSCHG_Msk (0x1UL << GOTGINT_SRSSCHG_Pos) // 0x00000100 */
-#define GOTGINT_SRSSCHG GOTGINT_SRSSCHG_Msk // Session request success status change */
+#define GOTGINT_SRSSCHG_Msk (0x1UL << GOTGINT_SRSSCHG_Pos) // 0x00000100
+#define GOTGINT_SRSSCHG GOTGINT_SRSSCHG_Msk // Session request success status change
#define GOTGINT_HNSSCHG_Pos (9U)
-#define GOTGINT_HNSSCHG_Msk (0x1UL << GOTGINT_HNSSCHG_Pos) // 0x00000200 */
-#define GOTGINT_HNSSCHG GOTGINT_HNSSCHG_Msk // Host negotiation success status change */
+#define GOTGINT_HNSSCHG_Msk (0x1UL << GOTGINT_HNSSCHG_Pos) // 0x00000200
+#define GOTGINT_HNSSCHG GOTGINT_HNSSCHG_Msk // Host negotiation success status change
#define GOTGINT_HNGDET_Pos (17U)
-#define GOTGINT_HNGDET_Msk (0x1UL << GOTGINT_HNGDET_Pos) // 0x00020000 */
-#define GOTGINT_HNGDET GOTGINT_HNGDET_Msk // Host negotiation detected */
+#define GOTGINT_HNGDET_Msk (0x1UL << GOTGINT_HNGDET_Pos) // 0x00020000
+#define GOTGINT_HNGDET GOTGINT_HNGDET_Msk // Host negotiation detected
#define GOTGINT_ADTOCHG_Pos (18U)
-#define GOTGINT_ADTOCHG_Msk (0x1UL << GOTGINT_ADTOCHG_Pos) // 0x00040000 */
-#define GOTGINT_ADTOCHG GOTGINT_ADTOCHG_Msk // A-device timeout change */
+#define GOTGINT_ADTOCHG_Msk (0x1UL << GOTGINT_ADTOCHG_Pos) // 0x00040000
+#define GOTGINT_ADTOCHG GOTGINT_ADTOCHG_Msk // A-device timeout change
#define GOTGINT_DBCDNE_Pos (19U)
-#define GOTGINT_DBCDNE_Msk (0x1UL << GOTGINT_DBCDNE_Pos) // 0x00080000 */
-#define GOTGINT_DBCDNE GOTGINT_DBCDNE_Msk // Debounce done */
+#define GOTGINT_DBCDNE_Msk (0x1UL << GOTGINT_DBCDNE_Pos) // 0x00080000
+#define GOTGINT_DBCDNE GOTGINT_DBCDNE_Msk // Debounce done
#define GOTGINT_IDCHNG_Pos (20U)
-#define GOTGINT_IDCHNG_Msk (0x1UL << GOTGINT_IDCHNG_Pos) // 0x00100000 */
-#define GOTGINT_IDCHNG GOTGINT_IDCHNG_Msk // Change in ID pin input value */
+#define GOTGINT_IDCHNG_Msk (0x1UL << GOTGINT_IDCHNG_Pos) // 0x00100000
+#define GOTGINT_IDCHNG GOTGINT_IDCHNG_Msk // Change in ID pin input value
/******************** Bit definition for DCFG register ********************/
#define DCFG_DSPD_Pos (0U)
@@ -405,92 +912,95 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DCFG_DSPD_FS 3 // Fullspeed on FS PHY
#define DCFG_NZLSOHSK_Pos (2U)
-#define DCFG_NZLSOHSK_Msk (0x1UL << DCFG_NZLSOHSK_Pos) // 0x00000004 */
-#define DCFG_NZLSOHSK DCFG_NZLSOHSK_Msk // Nonzero-length status OUT handshake */
+#define DCFG_NZLSOHSK_Msk (0x1UL << DCFG_NZLSOHSK_Pos) // 0x00000004
+#define DCFG_NZLSOHSK DCFG_NZLSOHSK_Msk // Nonzero-length status OUT handshake
#define DCFG_DAD_Pos (4U)
-#define DCFG_DAD_Msk (0x7FUL << DCFG_DAD_Pos) // 0x000007F0 */
-#define DCFG_DAD DCFG_DAD_Msk // Device address */
-#define DCFG_DAD_0 (0x01UL << DCFG_DAD_Pos) // 0x00000010 */
-#define DCFG_DAD_1 (0x02UL << DCFG_DAD_Pos) // 0x00000020 */
-#define DCFG_DAD_2 (0x04UL << DCFG_DAD_Pos) // 0x00000040 */
-#define DCFG_DAD_3 (0x08UL << DCFG_DAD_Pos) // 0x00000080 */
-#define DCFG_DAD_4 (0x10UL << DCFG_DAD_Pos) // 0x00000100 */
-#define DCFG_DAD_5 (0x20UL << DCFG_DAD_Pos) // 0x00000200 */
-#define DCFG_DAD_6 (0x40UL << DCFG_DAD_Pos) // 0x00000400 */
+#define DCFG_DAD_Msk (0x7FUL << DCFG_DAD_Pos) // 0x000007F0
+#define DCFG_DAD DCFG_DAD_Msk // Device address
+#define DCFG_DAD_0 (0x01UL << DCFG_DAD_Pos) // 0x00000010
+#define DCFG_DAD_1 (0x02UL << DCFG_DAD_Pos) // 0x00000020
+#define DCFG_DAD_2 (0x04UL << DCFG_DAD_Pos) // 0x00000040
+#define DCFG_DAD_3 (0x08UL << DCFG_DAD_Pos) // 0x00000080
+#define DCFG_DAD_4 (0x10UL << DCFG_DAD_Pos) // 0x00000100
+#define DCFG_DAD_5 (0x20UL << DCFG_DAD_Pos) // 0x00000200
+#define DCFG_DAD_6 (0x40UL << DCFG_DAD_Pos) // 0x00000400
#define DCFG_PFIVL_Pos (11U)
-#define DCFG_PFIVL_Msk (0x3UL << DCFG_PFIVL_Pos) // 0x00001800 */
-#define DCFG_PFIVL DCFG_PFIVL_Msk // Periodic (micro)frame interval */
-#define DCFG_PFIVL_0 (0x1UL << DCFG_PFIVL_Pos) // 0x00000800 */
-#define DCFG_PFIVL_1 (0x2UL << DCFG_PFIVL_Pos) // 0x00001000 */
+#define DCFG_PFIVL_Msk (0x3UL << DCFG_PFIVL_Pos) // 0x00001800
+#define DCFG_PFIVL DCFG_PFIVL_Msk // Periodic (micro)frame interval
+#define DCFG_PFIVL_0 (0x1UL << DCFG_PFIVL_Pos) // 0x00000800
+#define DCFG_PFIVL_1 (0x2UL << DCFG_PFIVL_Pos) // 0x00001000
#define DCFG_XCVRDLY_Pos (14U)
-#define DCFG_XCVRDLY_Msk (0x1UL << DCFG_XCVRDLY_Pos) /*!< 0x00004000 */
+#define DCFG_XCVRDLY_Msk (0x1UL << DCFG_XCVRDLY_Pos) // 0x00004000
#define DCFG_XCVRDLY DCFG_XCVRDLY_Msk // Enables delay between xcvr_sel and txvalid during device chirp
#define DCFG_PERSCHIVL_Pos (24U)
-#define DCFG_PERSCHIVL_Msk (0x3UL << DCFG_PERSCHIVL_Pos) // 0x03000000 */
-#define DCFG_PERSCHIVL DCFG_PERSCHIVL_Msk // Periodic scheduling interval */
-#define DCFG_PERSCHIVL_0 (0x1UL << DCFG_PERSCHIVL_Pos) // 0x01000000 */
-#define DCFG_PERSCHIVL_1 (0x2UL << DCFG_PERSCHIVL_Pos) // 0x02000000 */
+#define DCFG_PERSCHIVL_Msk (0x3UL << DCFG_PERSCHIVL_Pos) // 0x03000000
+#define DCFG_PERSCHIVL DCFG_PERSCHIVL_Msk // Periodic scheduling interval
+#define DCFG_PERSCHIVL_0 (0x1UL << DCFG_PERSCHIVL_Pos) // 0x01000000
+#define DCFG_PERSCHIVL_1 (0x2UL << DCFG_PERSCHIVL_Pos) // 0x02000000
/******************** Bit definition for DCTL register ********************/
#define DCTL_RWUSIG_Pos (0U)
-#define DCTL_RWUSIG_Msk (0x1UL << DCTL_RWUSIG_Pos) // 0x00000001 */
-#define DCTL_RWUSIG DCTL_RWUSIG_Msk // Remote wakeup signaling */
+#define DCTL_RWUSIG_Msk (0x1UL << DCTL_RWUSIG_Pos) // 0x00000001
+#define DCTL_RWUSIG DCTL_RWUSIG_Msk // Remote wakeup signaling
#define DCTL_SDIS_Pos (1U)
-#define DCTL_SDIS_Msk (0x1UL << DCTL_SDIS_Pos) // 0x00000002 */
-#define DCTL_SDIS DCTL_SDIS_Msk // Soft disconnect */
+#define DCTL_SDIS_Msk (0x1UL << DCTL_SDIS_Pos) // 0x00000002
+#define DCTL_SDIS DCTL_SDIS_Msk // Soft disconnect
#define DCTL_GINSTS_Pos (2U)
-#define DCTL_GINSTS_Msk (0x1UL << DCTL_GINSTS_Pos) // 0x00000004 */
-#define DCTL_GINSTS DCTL_GINSTS_Msk // Global IN NAK status */
+#define DCTL_GINSTS_Msk (0x1UL << DCTL_GINSTS_Pos) // 0x00000004
+#define DCTL_GINSTS DCTL_GINSTS_Msk // Global IN NAK status
#define DCTL_GONSTS_Pos (3U)
-#define DCTL_GONSTS_Msk (0x1UL << DCTL_GONSTS_Pos) // 0x00000008 */
-#define DCTL_GONSTS DCTL_GONSTS_Msk // Global OUT NAK status */
+#define DCTL_GONSTS_Msk (0x1UL << DCTL_GONSTS_Pos) // 0x00000008
+#define DCTL_GONSTS DCTL_GONSTS_Msk // Global OUT NAK status
#define DCTL_TCTL_Pos (4U)
-#define DCTL_TCTL_Msk (0x7UL << DCTL_TCTL_Pos) // 0x00000070 */
-#define DCTL_TCTL DCTL_TCTL_Msk // Test control */
-#define DCTL_TCTL_0 (0x1UL << DCTL_TCTL_Pos) // 0x00000010 */
-#define DCTL_TCTL_1 (0x2UL << DCTL_TCTL_Pos) // 0x00000020 */
-#define DCTL_TCTL_2 (0x4UL << DCTL_TCTL_Pos) // 0x00000040 */
+#define DCTL_TCTL_Msk (0x7UL << DCTL_TCTL_Pos) // 0x00000070
+#define DCTL_TCTL DCTL_TCTL_Msk // Test control
+#define DCTL_TCTL_0 (0x1UL << DCTL_TCTL_Pos) // 0x00000010
+#define DCTL_TCTL_1 (0x2UL << DCTL_TCTL_Pos) // 0x00000020
+#define DCTL_TCTL_2 (0x4UL << DCTL_TCTL_Pos) // 0x00000040
#define DCTL_SGINAK_Pos (7U)
-#define DCTL_SGINAK_Msk (0x1UL << DCTL_SGINAK_Pos) // 0x00000080 */
-#define DCTL_SGINAK DCTL_SGINAK_Msk // Set global IN NAK */
+#define DCTL_SGINAK_Msk (0x1UL << DCTL_SGINAK_Pos) // 0x00000080
+#define DCTL_SGINAK DCTL_SGINAK_Msk // Set global IN NAK
#define DCTL_CGINAK_Pos (8U)
-#define DCTL_CGINAK_Msk (0x1UL << DCTL_CGINAK_Pos) // 0x00000100 */
-#define DCTL_CGINAK DCTL_CGINAK_Msk // Clear global IN NAK */
+#define DCTL_CGINAK_Msk (0x1UL << DCTL_CGINAK_Pos) // 0x00000100
+#define DCTL_CGINAK DCTL_CGINAK_Msk // Clear global IN NAK
#define DCTL_SGONAK_Pos (9U)
-#define DCTL_SGONAK_Msk (0x1UL << DCTL_SGONAK_Pos) // 0x00000200 */
-#define DCTL_SGONAK DCTL_SGONAK_Msk // Set global OUT NAK */
+#define DCTL_SGONAK_Msk (0x1UL << DCTL_SGONAK_Pos) // 0x00000200
+#define DCTL_SGONAK DCTL_SGONAK_Msk // Set global OUT NAK
#define DCTL_CGONAK_Pos (10U)
-#define DCTL_CGONAK_Msk (0x1UL << DCTL_CGONAK_Pos) // 0x00000400 */
-#define DCTL_CGONAK DCTL_CGONAK_Msk // Clear global OUT NAK */
+#define DCTL_CGONAK_Msk (0x1UL << DCTL_CGONAK_Pos) // 0x00000400
+#define DCTL_CGONAK DCTL_CGONAK_Msk // Clear global OUT NAK
#define DCTL_POPRGDNE_Pos (11U)
-#define DCTL_POPRGDNE_Msk (0x1UL << DCTL_POPRGDNE_Pos) // 0x00000800 */
-#define DCTL_POPRGDNE DCTL_POPRGDNE_Msk // Power-on programming done */
+#define DCTL_POPRGDNE_Msk (0x1UL << DCTL_POPRGDNE_Pos) // 0x00000800
+#define DCTL_POPRGDNE DCTL_POPRGDNE_Msk // Power-on programming done
/******************** Bit definition for HFIR register ********************/
#define HFIR_FRIVL_Pos (0U)
-#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF */
-#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval */
+#define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF
+#define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval
+#define HFIR_RELOAD_CTRL_Pos (16U) // available since v2.92a
+#define HFIR_RELOAD_CTRL_Msk (0x1UL << HFIR_RELOAD_CTRL_Pos)
+#define HFIR_RELOAD_CTRL HFIR_RELOAD_CTRL_Msk
/******************** Bit definition for HFNUM register ********************/
#define HFNUM_FRNUM_Pos (0U)
-#define HFNUM_FRNUM_Msk (0xFFFFUL << HFNUM_FRNUM_Pos) // 0x0000FFFF */
-#define HFNUM_FRNUM HFNUM_FRNUM_Msk // Frame number */
+#define HFNUM_FRNUM_Msk (0xFFFFUL << HFNUM_FRNUM_Pos) // 0x0000FFFF
+#define HFNUM_FRNUM HFNUM_FRNUM_Msk // Frame number
#define HFNUM_FTREM_Pos (16U)
-#define HFNUM_FTREM_Msk (0xFFFFUL << HFNUM_FTREM_Pos) // 0xFFFF0000 */
-#define HFNUM_FTREM HFNUM_FTREM_Msk // Frame time remaining */
+#define HFNUM_FTREM_Msk (0xFFFFUL << HFNUM_FTREM_Pos) // 0xFFFF0000
+#define HFNUM_FTREM HFNUM_FTREM_Msk // Frame time remaining
/******************** Bit definition for DSTS register ********************/
#define DSTS_SUSPSTS_Pos (0U)
-#define DSTS_SUSPSTS_Msk (0x1UL << DSTS_SUSPSTS_Pos) // 0x00000001 */
-#define DSTS_SUSPSTS DSTS_SUSPSTS_Msk // Suspend status */
+#define DSTS_SUSPSTS_Msk (0x1UL << DSTS_SUSPSTS_Pos) // 0x00000001
+#define DSTS_SUSPSTS DSTS_SUSPSTS_Msk // Suspend status
#define DSTS_ENUMSPD_Pos (1U)
-#define DSTS_ENUMSPD_Msk (0x3UL << DSTS_ENUMSPD_Pos) // 0x00000006 */
-#define DSTS_ENUMSPD DSTS_ENUMSPD_Msk // Enumerated speed */
+#define DSTS_ENUMSPD_Msk (0x3UL << DSTS_ENUMSPD_Pos) // 0x00000006
+#define DSTS_ENUMSPD DSTS_ENUMSPD_Msk // Enumerated speed
#define DSTS_ENUMSPD_HS 0 // Highspeed
#define DSTS_ENUMSPD_FS_HSPHY 1 // Fullspeed on HS PHY
#define DSTS_ENUMSPD_LS 2 // Lowspeed
@@ -498,1236 +1008,1274 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size");
#define DSTS_EERR_Pos (3U)
-#define DSTS_EERR_Msk (0x1UL << DSTS_EERR_Pos) // 0x00000008 */
-#define DSTS_EERR DSTS_EERR_Msk // Erratic error */
+#define DSTS_EERR_Msk (0x1UL << DSTS_EERR_Pos) // 0x00000008
+#define DSTS_EERR DSTS_EERR_Msk // Erratic error
#define DSTS_FNSOF_Pos (8U)
-#define DSTS_FNSOF_Msk (0x3FFFUL << DSTS_FNSOF_Pos) // 0x003FFF00 */
-#define DSTS_FNSOF DSTS_FNSOF_Msk // Frame number of the received SOF */
+#define DSTS_FNSOF_Msk (0x3FFFUL << DSTS_FNSOF_Pos) // 0x003FFF00
+#define DSTS_FNSOF DSTS_FNSOF_Msk // Frame number of the received SOF
/******************** Bit definition for GAHBCFG register ********************/
#define GAHBCFG_GINT_Pos (0U)
-#define GAHBCFG_GINT_Msk (0x1UL << GAHBCFG_GINT_Pos) // 0x00000001 */
-#define GAHBCFG_GINT GAHBCFG_GINT_Msk // Global interrupt mask */
+#define GAHBCFG_GINT_Msk (0x1UL << GAHBCFG_GINT_Pos) // 0x00000001
+#define GAHBCFG_GINT GAHBCFG_GINT_Msk // Global interrupt mask
#define GAHBCFG_HBSTLEN_Pos (1U)
-#define GAHBCFG_HBSTLEN_Msk (0xFUL << GAHBCFG_HBSTLEN_Pos) // 0x0000001E */
-#define GAHBCFG_HBSTLEN GAHBCFG_HBSTLEN_Msk // Burst length/type */
-#define GAHBCFG_HBSTLEN_0 (0x0UL << GAHBCFG_HBSTLEN_Pos) // Single */
-#define GAHBCFG_HBSTLEN_1 (0x1UL << GAHBCFG_HBSTLEN_Pos) // INCR */
-#define GAHBCFG_HBSTLEN_2 (0x3UL << GAHBCFG_HBSTLEN_Pos) // INCR4 */
-#define GAHBCFG_HBSTLEN_3 (0x5UL << GAHBCFG_HBSTLEN_Pos) // INCR8 */
-#define GAHBCFG_HBSTLEN_4 (0x7UL << GAHBCFG_HBSTLEN_Pos) // INCR16 */
+#define GAHBCFG_HBSTLEN_Msk (0xFUL << GAHBCFG_HBSTLEN_Pos) // 0x0000001E
+#define GAHBCFG_HBSTLEN GAHBCFG_HBSTLEN_Msk // Burst length/type
+#define GAHBCFG_HBSTLEN_0 (0x0UL << GAHBCFG_HBSTLEN_Pos) // Single
+#define GAHBCFG_HBSTLEN_1 (0x1UL << GAHBCFG_HBSTLEN_Pos) // INCR
+#define GAHBCFG_HBSTLEN_2 (0x3UL << GAHBCFG_HBSTLEN_Pos) // INCR4
+#define GAHBCFG_HBSTLEN_3 (0x5UL << GAHBCFG_HBSTLEN_Pos) // INCR8
+#define GAHBCFG_HBSTLEN_4 (0x7UL << GAHBCFG_HBSTLEN_Pos) // INCR16
#define GAHBCFG_DMAEN_Pos (5U)
-#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020 */
-#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable */
-#define GAHBCFG_TXFELVL_Pos (7U)
-#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080 */
-#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level */
-#define GAHBCFG_PTXFELVL_Pos (8U)
-#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100 */
-#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level */
-
-#define GSNPSID_ID_MASK TU_GENMASK(31, 16)
+#define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020
+#define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Pos (7U)
+#define GAHBCFG_TX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_TX_FIFO_EPMTY_LVL_Pos) // 0x00000080
+#define GAHBCFG_TX_FIFO_EPMTY_LVL GAHBCFG_TX_FIFO_EPMTY_LVL_Msk // TxFIFO empty level
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos (8U)
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos) // 0x00000100
+#define GAHBCFG_PTX_FIFO_EPMTY_LVL GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk // Periodic TxFIFO empty level
/******************** Bit definition for GUSBCFG register ********************/
#define GUSBCFG_TOCAL_Pos (0U)
-#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007 */
-#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration */
+#define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007
+#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // HS/FS timeout calibration
#define GUSBCFG_PHYIF16_Pos (3U)
-#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008 */
-#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf) */
+#define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008
+#define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf)
#define GUSBCFG_ULPI_UTMI_SEL_Pos (4U)
-#define GUSBCFG_ULPI_UTMI_SEL_Msk (0x1UL << GUSBCFG_ULPI_UTMI_SEL_Pos) // 0x00000010 */
-#define GUSBCFG_ULPI_UTMI_SEL GUSBCFG_ULPI_UTMI_SEL_Msk // ULPI or UTMI+ Select (ULPI_UTMI_Sel) */
+#define GUSBCFG_ULPI_UTMI_SEL_Msk (0x1UL << GUSBCFG_ULPI_UTMI_SEL_Pos) // 0x00000010
+#define GUSBCFG_ULPI_UTMI_SEL GUSBCFG_ULPI_UTMI_SEL_Msk // ULPI or UTMI+ Select (ULPI_UTMI_Sel)
#define GUSBCFG_PHYSEL_Pos (6U)
-#define GUSBCFG_PHYSEL_Msk (0x1UL << GUSBCFG_PHYSEL_Pos) // 0x00000040 */
-#define GUSBCFG_PHYSEL GUSBCFG_PHYSEL_Msk // USB 2.0 high-speed ULPI PHY or USB 1.1 full-speed serial transceiver select */
+#define GUSBCFG_PHYSEL_Msk (0x1UL << GUSBCFG_PHYSEL_Pos) // 0x00000040
+#define GUSBCFG_PHYSEL GUSBCFG_PHYSEL_Msk // USB 2.0 high-speed ULPI PHY or USB 1.1 full-speed serial transceiver select
#define GUSBCFG_DDRSEL TU_BIT(7) // Single Data Rate (SDR) or Double Data Rate (DDR) or ULPI interface.
#define GUSBCFG_SRPCAP_Pos (8U)
-#define GUSBCFG_SRPCAP_Msk (0x1UL << GUSBCFG_SRPCAP_Pos) // 0x00000100 */
-#define GUSBCFG_SRPCAP GUSBCFG_SRPCAP_Msk // SRP-capable */
+#define GUSBCFG_SRPCAP_Msk (0x1UL << GUSBCFG_SRPCAP_Pos) // 0x00000100
+#define GUSBCFG_SRPCAP GUSBCFG_SRPCAP_Msk // SRP-capable
#define GUSBCFG_HNPCAP_Pos (9U)
-#define GUSBCFG_HNPCAP_Msk (0x1UL << GUSBCFG_HNPCAP_Pos) // 0x00000200 */
-#define GUSBCFG_HNPCAP GUSBCFG_HNPCAP_Msk // HNP-capable */
+#define GUSBCFG_HNPCAP_Msk (0x1UL << GUSBCFG_HNPCAP_Pos) // 0x00000200
+#define GUSBCFG_HNPCAP GUSBCFG_HNPCAP_Msk // HNP-capable
#define GUSBCFG_TRDT_Pos (10U)
-#define GUSBCFG_TRDT_Msk (0xFUL << GUSBCFG_TRDT_Pos) // 0x00003C00 */
-#define GUSBCFG_TRDT GUSBCFG_TRDT_Msk // USB turnaround time */
+#define GUSBCFG_TRDT_Msk (0xFUL << GUSBCFG_TRDT_Pos) // 0x00003C00
+#define GUSBCFG_TRDT GUSBCFG_TRDT_Msk // USB turnaround time
#define GUSBCFG_PHYLPCS_Pos (15U)
-#define GUSBCFG_PHYLPCS_Msk (0x1UL << GUSBCFG_PHYLPCS_Pos) // 0x00008000 */
-#define GUSBCFG_PHYLPCS GUSBCFG_PHYLPCS_Msk // PHY Low-power clock select */
+#define GUSBCFG_PHYLPCS_Msk (0x1UL << GUSBCFG_PHYLPCS_Pos) // 0x00008000
+#define GUSBCFG_PHYLPCS GUSBCFG_PHYLPCS_Msk // PHY Low-power clock select
#define GUSBCFG_ULPIFSLS_Pos (17U)
-#define GUSBCFG_ULPIFSLS_Msk (0x1UL << GUSBCFG_ULPIFSLS_Pos) // 0x00020000 */
-#define GUSBCFG_ULPIFSLS GUSBCFG_ULPIFSLS_Msk // ULPI FS/LS select */
+#define GUSBCFG_ULPIFSLS_Msk (0x1UL << GUSBCFG_ULPIFSLS_Pos) // 0x00020000
+#define GUSBCFG_ULPIFSLS GUSBCFG_ULPIFSLS_Msk // ULPI FS/LS select
#define GUSBCFG_ULPIAR_Pos (18U)
-#define GUSBCFG_ULPIAR_Msk (0x1UL << GUSBCFG_ULPIAR_Pos) // 0x00040000 */
-#define GUSBCFG_ULPIAR GUSBCFG_ULPIAR_Msk // ULPI Auto-resume */
+#define GUSBCFG_ULPIAR_Msk (0x1UL << GUSBCFG_ULPIAR_Pos) // 0x00040000
+#define GUSBCFG_ULPIAR GUSBCFG_ULPIAR_Msk // ULPI Auto-resume
#define GUSBCFG_ULPICSM_Pos (19U)
-#define GUSBCFG_ULPICSM_Msk (0x1UL << GUSBCFG_ULPICSM_Pos) // 0x00080000 */
-#define GUSBCFG_ULPICSM GUSBCFG_ULPICSM_Msk // ULPI Clock SuspendM */
+#define GUSBCFG_ULPICSM_Msk (0x1UL << GUSBCFG_ULPICSM_Pos) // 0x00080000
+#define GUSBCFG_ULPICSM GUSBCFG_ULPICSM_Msk // ULPI Clock SuspendM
#define GUSBCFG_ULPIEVBUSD_Pos (20U)
-#define GUSBCFG_ULPIEVBUSD_Msk (0x1UL << GUSBCFG_ULPIEVBUSD_Pos) // 0x00100000 */
-#define GUSBCFG_ULPIEVBUSD GUSBCFG_ULPIEVBUSD_Msk // ULPI External VBUS Drive */
+#define GUSBCFG_ULPIEVBUSD_Msk (0x1UL << GUSBCFG_ULPIEVBUSD_Pos) // 0x00100000
+#define GUSBCFG_ULPIEVBUSD GUSBCFG_ULPIEVBUSD_Msk // ULPI External VBUS Drive
#define GUSBCFG_ULPIEVBUSI_Pos (21U)
-#define GUSBCFG_ULPIEVBUSI_Msk (0x1UL << GUSBCFG_ULPIEVBUSI_Pos) // 0x00200000 */
-#define GUSBCFG_ULPIEVBUSI GUSBCFG_ULPIEVBUSI_Msk // ULPI external VBUS indicator */
+#define GUSBCFG_ULPIEVBUSI_Msk (0x1UL << GUSBCFG_ULPIEVBUSI_Pos) // 0x00200000
+#define GUSBCFG_ULPIEVBUSI GUSBCFG_ULPIEVBUSI_Msk // ULPI external VBUS indicator
#define GUSBCFG_TSDPS_Pos (22U)
-#define GUSBCFG_TSDPS_Msk (0x1UL << GUSBCFG_TSDPS_Pos) // 0x00400000 */
-#define GUSBCFG_TSDPS GUSBCFG_TSDPS_Msk // TermSel DLine pulsing selection */
+#define GUSBCFG_TSDPS_Msk (0x1UL << GUSBCFG_TSDPS_Pos) // 0x00400000
+#define GUSBCFG_TSDPS GUSBCFG_TSDPS_Msk // TermSel DLine pulsing selection
#define GUSBCFG_PCCI_Pos (23U)
-#define GUSBCFG_PCCI_Msk (0x1UL << GUSBCFG_PCCI_Pos) // 0x00800000 */
-#define GUSBCFG_PCCI GUSBCFG_PCCI_Msk // Indicator complement */
+#define GUSBCFG_PCCI_Msk (0x1UL << GUSBCFG_PCCI_Pos) // 0x00800000
+#define GUSBCFG_PCCI GUSBCFG_PCCI_Msk // Indicator complement
#define GUSBCFG_PTCI_Pos (24U)
-#define GUSBCFG_PTCI_Msk (0x1UL << GUSBCFG_PTCI_Pos) // 0x01000000 */
-#define GUSBCFG_PTCI GUSBCFG_PTCI_Msk // Indicator pass through */
+#define GUSBCFG_PTCI_Msk (0x1UL << GUSBCFG_PTCI_Pos) // 0x01000000
+#define GUSBCFG_PTCI GUSBCFG_PTCI_Msk // Indicator pass through
#define GUSBCFG_ULPIIPD_Pos (25U)
-#define GUSBCFG_ULPIIPD_Msk (0x1UL << GUSBCFG_ULPIIPD_Pos) // 0x02000000 */
-#define GUSBCFG_ULPIIPD GUSBCFG_ULPIIPD_Msk // ULPI interface protect disable */
+#define GUSBCFG_ULPIIPD_Msk (0x1UL << GUSBCFG_ULPIIPD_Pos) // 0x02000000
+#define GUSBCFG_ULPIIPD GUSBCFG_ULPIIPD_Msk // ULPI interface protect disable
#define GUSBCFG_FHMOD_Pos (29U)
-#define GUSBCFG_FHMOD_Msk (0x1UL << GUSBCFG_FHMOD_Pos) // 0x20000000 */
-#define GUSBCFG_FHMOD GUSBCFG_FHMOD_Msk // Forced host mode */
+#define GUSBCFG_FHMOD_Msk (0x1UL << GUSBCFG_FHMOD_Pos) // 0x20000000
+#define GUSBCFG_FHMOD GUSBCFG_FHMOD_Msk // Forced host mode
#define GUSBCFG_FDMOD_Pos (30U)
-#define GUSBCFG_FDMOD_Msk (0x1UL << GUSBCFG_FDMOD_Pos) // 0x40000000 */
-#define GUSBCFG_FDMOD GUSBCFG_FDMOD_Msk // Forced peripheral mode */
+#define GUSBCFG_FDMOD_Msk (0x1UL << GUSBCFG_FDMOD_Pos) // 0x40000000
+#define GUSBCFG_FDMOD GUSBCFG_FDMOD_Msk // Forced peripheral mode
#define GUSBCFG_CTXPKT_Pos (31U)
-#define GUSBCFG_CTXPKT_Msk (0x1UL << GUSBCFG_CTXPKT_Pos) // 0x80000000 */
-#define GUSBCFG_CTXPKT GUSBCFG_CTXPKT_Msk // Corrupt Tx packet */
+#define GUSBCFG_CTXPKT_Msk (0x1UL << GUSBCFG_CTXPKT_Pos) // 0x80000000
+#define GUSBCFG_CTXPKT GUSBCFG_CTXPKT_Msk // Corrupt Tx packet
/******************** Bit definition for GRSTCTL register ********************/
#define GRSTCTL_CSRST_Pos (0U)
-#define GRSTCTL_CSRST_Msk (0x1UL << GRSTCTL_CSRST_Pos) // 0x00000001 */
-#define GRSTCTL_CSRST GRSTCTL_CSRST_Msk // Core soft reset */
+#define GRSTCTL_CSRST_Msk (0x1UL << GRSTCTL_CSRST_Pos) // 0x00000001
+#define GRSTCTL_CSRST GRSTCTL_CSRST_Msk // Core soft reset
#define GRSTCTL_HSRST_Pos (1U)
-#define GRSTCTL_HSRST_Msk (0x1UL << GRSTCTL_HSRST_Pos) // 0x00000002 */
-#define GRSTCTL_HSRST GRSTCTL_HSRST_Msk // HCLK soft reset */
+#define GRSTCTL_HSRST_Msk (0x1UL << GRSTCTL_HSRST_Pos) // 0x00000002
+#define GRSTCTL_HSRST GRSTCTL_HSRST_Msk // HCLK soft reset
#define GRSTCTL_FCRST_Pos (2U)
-#define GRSTCTL_FCRST_Msk (0x1UL << GRSTCTL_FCRST_Pos) // 0x00000004 */
-#define GRSTCTL_FCRST GRSTCTL_FCRST_Msk // Host frame counter reset */
+#define GRSTCTL_FCRST_Msk (0x1UL << GRSTCTL_FCRST_Pos) // 0x00000004
+#define GRSTCTL_FCRST GRSTCTL_FCRST_Msk // Host frame counter reset
#define GRSTCTL_RXFFLSH_Pos (4U)
-#define GRSTCTL_RXFFLSH_Msk (0x1UL << GRSTCTL_RXFFLSH_Pos) // 0x00000010 */
-#define GRSTCTL_RXFFLSH GRSTCTL_RXFFLSH_Msk // RxFIFO flush */
+#define GRSTCTL_RXFFLSH_Msk (0x1UL << GRSTCTL_RXFFLSH_Pos) // 0x00000010
+#define GRSTCTL_RXFFLSH GRSTCTL_RXFFLSH_Msk // RxFIFO flush
#define GRSTCTL_TXFFLSH_Pos (5U)
-#define GRSTCTL_TXFFLSH_Msk (0x1UL << GRSTCTL_TXFFLSH_Pos) // 0x00000020 */
-#define GRSTCTL_TXFFLSH GRSTCTL_TXFFLSH_Msk // TxFIFO flush */
+#define GRSTCTL_TXFFLSH_Msk (0x1UL << GRSTCTL_TXFFLSH_Pos) // 0x00000020
+#define GRSTCTL_TXFFLSH GRSTCTL_TXFFLSH_Msk // TxFIFO flush
#define GRSTCTL_TXFNUM_Pos (6U)
-#define GRSTCTL_TXFNUM_Msk (0x1FUL << GRSTCTL_TXFNUM_Pos) // 0x000007C0 */
-#define GRSTCTL_TXFNUM GRSTCTL_TXFNUM_Msk // TxFIFO number */
-#define GRSTCTL_TXFNUM_0 (0x01UL << GRSTCTL_TXFNUM_Pos) // 0x00000040 */
-#define GRSTCTL_TXFNUM_1 (0x02UL << GRSTCTL_TXFNUM_Pos) // 0x00000080 */
-#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100 */
-#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200 */
-#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400 */
-#define GRSTCTL_CSFTRST_DONE_Pos (29)
-#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a
+#define GRSTCTL_TXFNUM_Msk (0x1FUL << GRSTCTL_TXFNUM_Pos) // 0x000007C0
+#define GRSTCTL_TXFNUM GRSTCTL_TXFNUM_Msk // TxFIFO number
+#define GRSTCTL_TXFNUM_0 (0x01UL << GRSTCTL_TXFNUM_Pos) // 0x00000040
+#define GRSTCTL_TXFNUM_1 (0x02UL << GRSTCTL_TXFNUM_Pos) // 0x00000080
+#define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100
+#define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200
+#define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400
+#define GRSTCTL_CSRST_DONE_Pos (29)
+#define GRSTCTL_CSRST_DONE (1u << GRSTCTL_CSRST_DONE_Pos) // Reset Done, only available from v4.20a
#define GRSTCTL_DMAREQ_Pos (30U)
-#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000 */
-#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal */
+#define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000
+#define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal
#define GRSTCTL_AHBIDL_Pos (31U)
-#define GRSTCTL_AHBIDL_Msk (0x1UL << GRSTCTL_AHBIDL_Pos) // 0x80000000 */
-#define GRSTCTL_AHBIDL GRSTCTL_AHBIDL_Msk // AHB master idle */
+#define GRSTCTL_AHBIDL_Msk (0x1UL << GRSTCTL_AHBIDL_Pos) // 0x80000000
+#define GRSTCTL_AHBIDL GRSTCTL_AHBIDL_Msk // AHB master idle
/******************** Bit definition for DIEPMSK register ********************/
#define DIEPMSK_XFRCM_Pos (0U)
-#define DIEPMSK_XFRCM_Msk (0x1UL << DIEPMSK_XFRCM_Pos) // 0x00000001 */
-#define DIEPMSK_XFRCM DIEPMSK_XFRCM_Msk // Transfer completed interrupt mask */
+#define DIEPMSK_XFRCM_Msk (0x1UL << DIEPMSK_XFRCM_Pos) // 0x00000001
+#define DIEPMSK_XFRCM DIEPMSK_XFRCM_Msk // Transfer completed interrupt mask
#define DIEPMSK_EPDM_Pos (1U)
-#define DIEPMSK_EPDM_Msk (0x1UL << DIEPMSK_EPDM_Pos) // 0x00000002 */
-#define DIEPMSK_EPDM DIEPMSK_EPDM_Msk // Endpoint disabled interrupt mask */
+#define DIEPMSK_EPDM_Msk (0x1UL << DIEPMSK_EPDM_Pos) // 0x00000002
+#define DIEPMSK_EPDM DIEPMSK_EPDM_Msk // Endpoint disabled interrupt mask
#define DIEPMSK_TOM_Pos (3U)
-#define DIEPMSK_TOM_Msk (0x1UL << DIEPMSK_TOM_Pos) // 0x00000008 */
-#define DIEPMSK_TOM DIEPMSK_TOM_Msk // Timeout condition mask (nonisochronous endpoints) */
+#define DIEPMSK_TOM_Msk (0x1UL << DIEPMSK_TOM_Pos) // 0x00000008
+#define DIEPMSK_TOM DIEPMSK_TOM_Msk // Timeout condition mask (nonisochronous endpoints)
#define DIEPMSK_ITTXFEMSK_Pos (4U)
-#define DIEPMSK_ITTXFEMSK_Msk (0x1UL << DIEPMSK_ITTXFEMSK_Pos) // 0x00000010 */
-#define DIEPMSK_ITTXFEMSK DIEPMSK_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask */
+#define DIEPMSK_ITTXFEMSK_Msk (0x1UL << DIEPMSK_ITTXFEMSK_Pos) // 0x00000010
+#define DIEPMSK_ITTXFEMSK DIEPMSK_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask
#define DIEPMSK_INEPNMM_Pos (5U)
-#define DIEPMSK_INEPNMM_Msk (0x1UL << DIEPMSK_INEPNMM_Pos) // 0x00000020 */
-#define DIEPMSK_INEPNMM DIEPMSK_INEPNMM_Msk // IN token received with EP mismatch mask */
+#define DIEPMSK_INEPNMM_Msk (0x1UL << DIEPMSK_INEPNMM_Pos) // 0x00000020
+#define DIEPMSK_INEPNMM DIEPMSK_INEPNMM_Msk // IN token received with EP mismatch mask
#define DIEPMSK_INEPNEM_Pos (6U)
-#define DIEPMSK_INEPNEM_Msk (0x1UL << DIEPMSK_INEPNEM_Pos) // 0x00000040 */
-#define DIEPMSK_INEPNEM DIEPMSK_INEPNEM_Msk // IN endpoint NAK effective mask */
+#define DIEPMSK_INEPNEM_Msk (0x1UL << DIEPMSK_INEPNEM_Pos) // 0x00000040
+#define DIEPMSK_INEPNEM DIEPMSK_INEPNEM_Msk // IN endpoint NAK effective mask
#define DIEPMSK_TXFURM_Pos (8U)
-#define DIEPMSK_TXFURM_Msk (0x1UL << DIEPMSK_TXFURM_Pos) // 0x00000100 */
-#define DIEPMSK_TXFURM DIEPMSK_TXFURM_Msk // FIFO underrun mask */
+#define DIEPMSK_TXFURM_Msk (0x1UL << DIEPMSK_TXFURM_Pos) // 0x00000100
+#define DIEPMSK_TXFURM DIEPMSK_TXFURM_Msk // FIFO underrun mask
#define DIEPMSK_BIM_Pos (9U)
-#define DIEPMSK_BIM_Msk (0x1UL << DIEPMSK_BIM_Pos) // 0x00000200 */
-#define DIEPMSK_BIM DIEPMSK_BIM_Msk // BNA interrupt mask */
+#define DIEPMSK_BIM_Msk (0x1UL << DIEPMSK_BIM_Pos) // 0x00000200
+#define DIEPMSK_BIM DIEPMSK_BIM_Msk // BNA interrupt mask
/******************** Bit definition for HPTXSTS register ********************/
#define HPTXSTS_PTXFSAVL_Pos (0U)
-#define HPTXSTS_PTXFSAVL_Msk (0xFFFFUL << HPTXSTS_PTXFSAVL_Pos) // 0x0000FFFF */
-#define HPTXSTS_PTXFSAVL HPTXSTS_PTXFSAVL_Msk // Periodic transmit data FIFO space available */
+#define HPTXSTS_PTXFSAVL_Msk (0xFFFFUL << HPTXSTS_PTXFSAVL_Pos) // 0x0000FFFF
+#define HPTXSTS_PTXFSAVL HPTXSTS_PTXFSAVL_Msk // Periodic transmit data FIFO space available
#define HPTXSTS_PTXQSAV_Pos (16U)
-#define HPTXSTS_PTXQSAV_Msk (0xFFUL << HPTXSTS_PTXQSAV_Pos) // 0x00FF0000 */
-#define HPTXSTS_PTXQSAV HPTXSTS_PTXQSAV_Msk // Periodic transmit request queue space available */
-#define HPTXSTS_PTXQSAV_0 (0x01UL << HPTXSTS_PTXQSAV_Pos) // 0x00010000 */
-#define HPTXSTS_PTXQSAV_1 (0x02UL << HPTXSTS_PTXQSAV_Pos) // 0x00020000 */
-#define HPTXSTS_PTXQSAV_2 (0x04UL << HPTXSTS_PTXQSAV_Pos) // 0x00040000 */
-#define HPTXSTS_PTXQSAV_3 (0x08UL << HPTXSTS_PTXQSAV_Pos) // 0x00080000 */
-#define HPTXSTS_PTXQSAV_4 (0x10UL << HPTXSTS_PTXQSAV_Pos) // 0x00100000 */
-#define HPTXSTS_PTXQSAV_5 (0x20UL << HPTXSTS_PTXQSAV_Pos) // 0x00200000 */
-#define HPTXSTS_PTXQSAV_6 (0x40UL << HPTXSTS_PTXQSAV_Pos) // 0x00400000 */
-#define HPTXSTS_PTXQSAV_7 (0x80UL << HPTXSTS_PTXQSAV_Pos) // 0x00800000 */
+#define HPTXSTS_PTXQSAV_Msk (0xFFUL << HPTXSTS_PTXQSAV_Pos) // 0x00FF0000
+#define HPTXSTS_PTXQSAV HPTXSTS_PTXQSAV_Msk // Periodic transmit request queue space available
+#define HPTXSTS_PTXQSAV_0 (0x01UL << HPTXSTS_PTXQSAV_Pos) // 0x00010000
+#define HPTXSTS_PTXQSAV_1 (0x02UL << HPTXSTS_PTXQSAV_Pos) // 0x00020000
+#define HPTXSTS_PTXQSAV_2 (0x04UL << HPTXSTS_PTXQSAV_Pos) // 0x00040000
+#define HPTXSTS_PTXQSAV_3 (0x08UL << HPTXSTS_PTXQSAV_Pos) // 0x00080000
+#define HPTXSTS_PTXQSAV_4 (0x10UL << HPTXSTS_PTXQSAV_Pos) // 0x00100000
+#define HPTXSTS_PTXQSAV_5 (0x20UL << HPTXSTS_PTXQSAV_Pos) // 0x00200000
+#define HPTXSTS_PTXQSAV_6 (0x40UL << HPTXSTS_PTXQSAV_Pos) // 0x00400000
+#define HPTXSTS_PTXQSAV_7 (0x80UL << HPTXSTS_PTXQSAV_Pos) // 0x00800000
#define HPTXSTS_PTXQTOP_Pos (24U)
-#define HPTXSTS_PTXQTOP_Msk (0xFFUL << HPTXSTS_PTXQTOP_Pos) // 0xFF000000 */
-#define HPTXSTS_PTXQTOP HPTXSTS_PTXQTOP_Msk // Top of the periodic transmit request queue */
-#define HPTXSTS_PTXQTOP_0 (0x01UL << HPTXSTS_PTXQTOP_Pos) // 0x01000000 */
-#define HPTXSTS_PTXQTOP_1 (0x02UL << HPTXSTS_PTXQTOP_Pos) // 0x02000000 */
-#define HPTXSTS_PTXQTOP_2 (0x04UL << HPTXSTS_PTXQTOP_Pos) // 0x04000000 */
-#define HPTXSTS_PTXQTOP_3 (0x08UL << HPTXSTS_PTXQTOP_Pos) // 0x08000000 */
-#define HPTXSTS_PTXQTOP_4 (0x10UL << HPTXSTS_PTXQTOP_Pos) // 0x10000000 */
-#define HPTXSTS_PTXQTOP_5 (0x20UL << HPTXSTS_PTXQTOP_Pos) // 0x20000000 */
-#define HPTXSTS_PTXQTOP_6 (0x40UL << HPTXSTS_PTXQTOP_Pos) // 0x40000000 */
-#define HPTXSTS_PTXQTOP_7 (0x80UL << HPTXSTS_PTXQTOP_Pos) // 0x80000000 */
+#define HPTXSTS_PTXQTOP_Msk (0xFFUL << HPTXSTS_PTXQTOP_Pos) // 0xFF000000
+#define HPTXSTS_PTXQTOP HPTXSTS_PTXQTOP_Msk // Top of the periodic transmit request queue
+#define HPTXSTS_PTXQTOP_0 (0x01UL << HPTXSTS_PTXQTOP_Pos) // 0x01000000
+#define HPTXSTS_PTXQTOP_1 (0x02UL << HPTXSTS_PTXQTOP_Pos) // 0x02000000
+#define HPTXSTS_PTXQTOP_2 (0x04UL << HPTXSTS_PTXQTOP_Pos) // 0x04000000
+#define HPTXSTS_PTXQTOP_3 (0x08UL << HPTXSTS_PTXQTOP_Pos) // 0x08000000
+#define HPTXSTS_PTXQTOP_4 (0x10UL << HPTXSTS_PTXQTOP_Pos) // 0x10000000
+#define HPTXSTS_PTXQTOP_5 (0x20UL << HPTXSTS_PTXQTOP_Pos) // 0x20000000
+#define HPTXSTS_PTXQTOP_6 (0x40UL << HPTXSTS_PTXQTOP_Pos) // 0x40000000
+#define HPTXSTS_PTXQTOP_7 (0x80UL << HPTXSTS_PTXQTOP_Pos) // 0x80000000
/******************** Bit definition for HAINT register ********************/
#define HAINT_HAINT_Pos (0U)
-#define HAINT_HAINT_Msk (0xFFFFUL << HAINT_HAINT_Pos) // 0x0000FFFF */
-#define HAINT_HAINT HAINT_HAINT_Msk // Channel interrupts */
+#define HAINT_HAINT_Msk (0xFFFFUL << HAINT_HAINT_Pos) // 0x0000FFFF
+#define HAINT_HAINT HAINT_HAINT_Msk // Channel interrupts
/******************** Bit definition for DOEPMSK register ********************/
#define DOEPMSK_XFRCM_Pos (0U)
-#define DOEPMSK_XFRCM_Msk (0x1UL << DOEPMSK_XFRCM_Pos) // 0x00000001 */
-#define DOEPMSK_XFRCM DOEPMSK_XFRCM_Msk // Transfer completed interrupt mask */
+#define DOEPMSK_XFRCM_Msk (0x1UL << DOEPMSK_XFRCM_Pos) // 0x00000001
+#define DOEPMSK_XFRCM DOEPMSK_XFRCM_Msk // Transfer completed interrupt mask
#define DOEPMSK_EPDM_Pos (1U)
-#define DOEPMSK_EPDM_Msk (0x1UL << DOEPMSK_EPDM_Pos) // 0x00000002 */
-#define DOEPMSK_EPDM DOEPMSK_EPDM_Msk // Endpoint disabled interrupt mask */
+#define DOEPMSK_EPDM_Msk (0x1UL << DOEPMSK_EPDM_Pos) // 0x00000002
+#define DOEPMSK_EPDM DOEPMSK_EPDM_Msk // Endpoint disabled interrupt mask
#define DOEPMSK_AHBERRM_Pos (2U)
-#define DOEPMSK_AHBERRM_Msk (0x1UL << DOEPMSK_AHBERRM_Pos) // 0x00000004 */
-#define DOEPMSK_AHBERRM DOEPMSK_AHBERRM_Msk // OUT transaction AHB Error interrupt mask */
+#define DOEPMSK_AHBERRM_Msk (0x1UL << DOEPMSK_AHBERRM_Pos) // 0x00000004
+#define DOEPMSK_AHBERRM DOEPMSK_AHBERRM_Msk // OUT transaction AHB Error interrupt mask
#define DOEPMSK_STUPM_Pos (3U)
-#define DOEPMSK_STUPM_Msk (0x1UL << DOEPMSK_STUPM_Pos) // 0x00000008 */
-#define DOEPMSK_STUPM DOEPMSK_STUPM_Msk // SETUP phase done mask */
+#define DOEPMSK_STUPM_Msk (0x1UL << DOEPMSK_STUPM_Pos) // 0x00000008
+#define DOEPMSK_STUPM DOEPMSK_STUPM_Msk // SETUP phase done mask
#define DOEPMSK_OTEPDM_Pos (4U)
-#define DOEPMSK_OTEPDM_Msk (0x1UL << DOEPMSK_OTEPDM_Pos) // 0x00000010 */
-#define DOEPMSK_OTEPDM DOEPMSK_OTEPDM_Msk // OUT token received when endpoint disabled mask */
+#define DOEPMSK_OTEPDM_Msk (0x1UL << DOEPMSK_OTEPDM_Pos) // 0x00000010
+#define DOEPMSK_OTEPDM DOEPMSK_OTEPDM_Msk // OUT token received when endpoint disabled mask
#define DOEPMSK_OTEPSPRM_Pos (5U)
-#define DOEPMSK_OTEPSPRM_Msk (0x1UL << DOEPMSK_OTEPSPRM_Pos) // 0x00000020 */
-#define DOEPMSK_OTEPSPRM DOEPMSK_OTEPSPRM_Msk // Status Phase Received mask */
+#define DOEPMSK_OTEPSPRM_Msk (0x1UL << DOEPMSK_OTEPSPRM_Pos) // 0x00000020
+#define DOEPMSK_OTEPSPRM DOEPMSK_OTEPSPRM_Msk // Status Phase Received mask
#define DOEPMSK_B2BSTUP_Pos (6U)
-#define DOEPMSK_B2BSTUP_Msk (0x1UL << DOEPMSK_B2BSTUP_Pos) // 0x00000040 */
-#define DOEPMSK_B2BSTUP DOEPMSK_B2BSTUP_Msk // Back-to-back SETUP packets received mask */
+#define DOEPMSK_B2BSTUP_Msk (0x1UL << DOEPMSK_B2BSTUP_Pos) // 0x00000040
+#define DOEPMSK_B2BSTUP DOEPMSK_B2BSTUP_Msk // Back-to-back SETUP packets received mask
#define DOEPMSK_OPEM_Pos (8U)
-#define DOEPMSK_OPEM_Msk (0x1UL << DOEPMSK_OPEM_Pos) // 0x00000100 */
-#define DOEPMSK_OPEM DOEPMSK_OPEM_Msk // OUT packet error mask */
+#define DOEPMSK_OPEM_Msk (0x1UL << DOEPMSK_OPEM_Pos) // 0x00000100
+#define DOEPMSK_OPEM DOEPMSK_OPEM_Msk // OUT packet error mask
#define DOEPMSK_BOIM_Pos (9U)
-#define DOEPMSK_BOIM_Msk (0x1UL << DOEPMSK_BOIM_Pos) // 0x00000200 */
-#define DOEPMSK_BOIM DOEPMSK_BOIM_Msk // BNA interrupt mask */
+#define DOEPMSK_BOIM_Msk (0x1UL << DOEPMSK_BOIM_Pos) // 0x00000200
+#define DOEPMSK_BOIM DOEPMSK_BOIM_Msk // BNA interrupt mask
#define DOEPMSK_BERRM_Pos (12U)
-#define DOEPMSK_BERRM_Msk (0x1UL << DOEPMSK_BERRM_Pos) // 0x00001000 */
-#define DOEPMSK_BERRM DOEPMSK_BERRM_Msk // Babble error interrupt mask */
+#define DOEPMSK_BERRM_Msk (0x1UL << DOEPMSK_BERRM_Pos) // 0x00001000
+#define DOEPMSK_BERRM DOEPMSK_BERRM_Msk // Babble error interrupt mask
#define DOEPMSK_NAKM_Pos (13U)
-#define DOEPMSK_NAKM_Msk (0x1UL << DOEPMSK_NAKM_Pos) // 0x00002000 */
-#define DOEPMSK_NAKM DOEPMSK_NAKM_Msk // OUT Packet NAK interrupt mask */
+#define DOEPMSK_NAKM_Msk (0x1UL << DOEPMSK_NAKM_Pos) // 0x00002000
+#define DOEPMSK_NAKM DOEPMSK_NAKM_Msk // OUT Packet NAK interrupt mask
#define DOEPMSK_NYETM_Pos (14U)
-#define DOEPMSK_NYETM_Msk (0x1UL << DOEPMSK_NYETM_Pos) // 0x00004000 */
-#define DOEPMSK_NYETM DOEPMSK_NYETM_Msk // NYET interrupt mask */
+#define DOEPMSK_NYETM_Msk (0x1UL << DOEPMSK_NYETM_Pos) // 0x00004000
+#define DOEPMSK_NYETM DOEPMSK_NYETM_Msk // NYET interrupt mask
/******************** Bit definition for GINTSTS register ********************/
#define GINTSTS_CMOD_Pos (0U)
-#define GINTSTS_CMOD_Msk (0x1UL << GINTSTS_CMOD_Pos) // 0x00000001 */
-#define GINTSTS_CMOD GINTSTS_CMOD_Msk // Current mode of operation */
+#define GINTSTS_CMOD_Msk (0x1UL << GINTSTS_CMOD_Pos) // 0x00000001
+#define GINTSTS_CMOD GINTSTS_CMOD_Msk // Current mode of operation
#define GINTSTS_MMIS_Pos (1U)
-#define GINTSTS_MMIS_Msk (0x1UL << GINTSTS_MMIS_Pos) // 0x00000002 */
-#define GINTSTS_MMIS GINTSTS_MMIS_Msk // Mode mismatch interrupt */
+#define GINTSTS_MMIS_Msk (0x1UL << GINTSTS_MMIS_Pos) // 0x00000002
+#define GINTSTS_MMIS GINTSTS_MMIS_Msk // Mode mismatch interrupt
#define GINTSTS_OTGINT_Pos (2U)
-#define GINTSTS_OTGINT_Msk (0x1UL << GINTSTS_OTGINT_Pos) // 0x00000004 */
-#define GINTSTS_OTGINT GINTSTS_OTGINT_Msk // OTG interrupt */
+#define GINTSTS_OTGINT_Msk (0x1UL << GINTSTS_OTGINT_Pos) // 0x00000004
+#define GINTSTS_OTGINT GINTSTS_OTGINT_Msk // OTG interrupt
#define GINTSTS_SOF_Pos (3U)
-#define GINTSTS_SOF_Msk (0x1UL << GINTSTS_SOF_Pos) // 0x00000008 */
-#define GINTSTS_SOF GINTSTS_SOF_Msk // Start of frame */
+#define GINTSTS_SOF_Msk (0x1UL << GINTSTS_SOF_Pos) // 0x00000008
+#define GINTSTS_SOF GINTSTS_SOF_Msk // Start of frame
#define GINTSTS_RXFLVL_Pos (4U)
-#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010 */
-#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty */
-#define GINTSTS_NPTXFE_Pos (5U)
-#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020 */
-#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty */
+#define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010
+#define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty
+#define GINTSTS_NPTX_FIFO_EMPTY_Pos (5U)
+#define GINTSTS_NPTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_NPTX_FIFO_EMPTY_Pos) // 0x00000020
+#define GINTSTS_NPTX_FIFO_EMPTY GINTSTS_NPTX_FIFO_EMPTY_Msk // Nonperiodic TxFIFO empty
#define GINTSTS_GINAKEFF_Pos (6U)
-#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040 */
-#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective */
+#define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040
+#define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective
#define GINTSTS_BOUTNAKEFF_Pos (7U)
-#define GINTSTS_BOUTNAKEFF_Msk (0x1UL << GINTSTS_BOUTNAKEFF_Pos) // 0x00000080 */
-#define GINTSTS_BOUTNAKEFF GINTSTS_BOUTNAKEFF_Msk // Global OUT NAK effective */
+#define GINTSTS_BOUTNAKEFF_Msk (0x1UL << GINTSTS_BOUTNAKEFF_Pos) // 0x00000080
+#define GINTSTS_BOUTNAKEFF GINTSTS_BOUTNAKEFF_Msk // Global OUT NAK effective
#define GINTSTS_ESUSP_Pos (10U)
-#define GINTSTS_ESUSP_Msk (0x1UL << GINTSTS_ESUSP_Pos) // 0x00000400 */
-#define GINTSTS_ESUSP GINTSTS_ESUSP_Msk // Early suspend */
+#define GINTSTS_ESUSP_Msk (0x1UL << GINTSTS_ESUSP_Pos) // 0x00000400
+#define GINTSTS_ESUSP GINTSTS_ESUSP_Msk // Early suspend
#define GINTSTS_USBSUSP_Pos (11U)
-#define GINTSTS_USBSUSP_Msk (0x1UL << GINTSTS_USBSUSP_Pos) // 0x00000800 */
-#define GINTSTS_USBSUSP GINTSTS_USBSUSP_Msk // USB suspend */
+#define GINTSTS_USBSUSP_Msk (0x1UL << GINTSTS_USBSUSP_Pos) // 0x00000800
+#define GINTSTS_USBSUSP GINTSTS_USBSUSP_Msk // USB suspend
#define GINTSTS_USBRST_Pos (12U)
-#define GINTSTS_USBRST_Msk (0x1UL << GINTSTS_USBRST_Pos) // 0x00001000 */
-#define GINTSTS_USBRST GINTSTS_USBRST_Msk // USB reset */
+#define GINTSTS_USBRST_Msk (0x1UL << GINTSTS_USBRST_Pos) // 0x00001000
+#define GINTSTS_USBRST GINTSTS_USBRST_Msk // USB reset
#define GINTSTS_ENUMDNE_Pos (13U)
-#define GINTSTS_ENUMDNE_Msk (0x1UL << GINTSTS_ENUMDNE_Pos) // 0x00002000 */
-#define GINTSTS_ENUMDNE GINTSTS_ENUMDNE_Msk // Enumeration done */
+#define GINTSTS_ENUMDNE_Msk (0x1UL << GINTSTS_ENUMDNE_Pos) // 0x00002000
+#define GINTSTS_ENUMDNE GINTSTS_ENUMDNE_Msk // Enumeration done
#define GINTSTS_ISOODRP_Pos (14U)
-#define GINTSTS_ISOODRP_Msk (0x1UL << GINTSTS_ISOODRP_Pos) // 0x00004000 */
-#define GINTSTS_ISOODRP GINTSTS_ISOODRP_Msk // Isochronous OUT packet dropped interrupt */
+#define GINTSTS_ISOODRP_Msk (0x1UL << GINTSTS_ISOODRP_Pos) // 0x00004000
+#define GINTSTS_ISOODRP GINTSTS_ISOODRP_Msk // Isochronous OUT packet dropped interrupt
#define GINTSTS_EOPF_Pos (15U)
-#define GINTSTS_EOPF_Msk (0x1UL << GINTSTS_EOPF_Pos) // 0x00008000 */
-#define GINTSTS_EOPF GINTSTS_EOPF_Msk // End of periodic frame interrupt */
+#define GINTSTS_EOPF_Msk (0x1UL << GINTSTS_EOPF_Pos) // 0x00008000
+#define GINTSTS_EOPF GINTSTS_EOPF_Msk // End of periodic frame interrupt
#define GINTSTS_IEPINT_Pos (18U)
-#define GINTSTS_IEPINT_Msk (0x1UL << GINTSTS_IEPINT_Pos) // 0x00040000 */
-#define GINTSTS_IEPINT GINTSTS_IEPINT_Msk // IN endpoint interrupt */
+#define GINTSTS_IEPINT_Msk (0x1UL << GINTSTS_IEPINT_Pos) // 0x00040000
+#define GINTSTS_IEPINT GINTSTS_IEPINT_Msk // IN endpoint interrupt
#define GINTSTS_OEPINT_Pos (19U)
-#define GINTSTS_OEPINT_Msk (0x1UL << GINTSTS_OEPINT_Pos) // 0x00080000 */
-#define GINTSTS_OEPINT GINTSTS_OEPINT_Msk // OUT endpoint interrupt */
+#define GINTSTS_OEPINT_Msk (0x1UL << GINTSTS_OEPINT_Pos) // 0x00080000
+#define GINTSTS_OEPINT GINTSTS_OEPINT_Msk // OUT endpoint interrupt
#define GINTSTS_IISOIXFR_Pos (20U)
-#define GINTSTS_IISOIXFR_Msk (0x1UL << GINTSTS_IISOIXFR_Pos) // 0x00100000 */
-#define GINTSTS_IISOIXFR GINTSTS_IISOIXFR_Msk // Incomplete isochronous IN transfer */
+#define GINTSTS_IISOIXFR_Msk (0x1UL << GINTSTS_IISOIXFR_Pos) // 0x00100000
+#define GINTSTS_IISOIXFR GINTSTS_IISOIXFR_Msk // Incomplete isochronous IN transfer
#define GINTSTS_PXFR_INCOMPISOOUT_Pos (21U)
-#define GINTSTS_PXFR_INCOMPISOOUT_Msk (0x1UL << GINTSTS_PXFR_INCOMPISOOUT_Pos) // 0x00200000 */
-#define GINTSTS_PXFR_INCOMPISOOUT GINTSTS_PXFR_INCOMPISOOUT_Msk // Incomplete periodic transfer */
+#define GINTSTS_PXFR_INCOMPISOOUT_Msk (0x1UL << GINTSTS_PXFR_INCOMPISOOUT_Pos) // 0x00200000
+#define GINTSTS_PXFR_INCOMPISOOUT GINTSTS_PXFR_INCOMPISOOUT_Msk // Incomplete periodic transfer
#define GINTSTS_DATAFSUSP_Pos (22U)
-#define GINTSTS_DATAFSUSP_Msk (0x1UL << GINTSTS_DATAFSUSP_Pos) // 0x00400000 */
-#define GINTSTS_DATAFSUSP GINTSTS_DATAFSUSP_Msk // Data fetch suspended */
+#define GINTSTS_DATAFSUSP_Msk (0x1UL << GINTSTS_DATAFSUSP_Pos) // 0x00400000
+#define GINTSTS_DATAFSUSP GINTSTS_DATAFSUSP_Msk // Data fetch suspended
#define GINTSTS_RSTDET_Pos (23U)
-#define GINTSTS_RSTDET_Msk (0x1UL << GINTSTS_RSTDET_Pos) // 0x00800000 */
-#define GINTSTS_RSTDET GINTSTS_RSTDET_Msk // Reset detected interrupt */
+#define GINTSTS_RSTDET_Msk (0x1UL << GINTSTS_RSTDET_Pos) // 0x00800000
+#define GINTSTS_RSTDET GINTSTS_RSTDET_Msk // Reset detected interrupt
#define GINTSTS_HPRTINT_Pos (24U)
-#define GINTSTS_HPRTINT_Msk (0x1UL << GINTSTS_HPRTINT_Pos) // 0x01000000 */
-#define GINTSTS_HPRTINT GINTSTS_HPRTINT_Msk // Host port interrupt */
+#define GINTSTS_HPRTINT_Msk (0x1UL << GINTSTS_HPRTINT_Pos) // 0x01000000
+#define GINTSTS_HPRTINT GINTSTS_HPRTINT_Msk // Host port interrupt
#define GINTSTS_HCINT_Pos (25U)
-#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000 */
-#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt */
-#define GINTSTS_PTXFE_Pos (26U)
-#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000 */
-#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty */
+#define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000
+#define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt
+#define GINTSTS_PTX_FIFO_EMPTY_Pos (26U)
+#define GINTSTS_PTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_PTX_FIFO_EMPTY_Pos) // 0x04000000
+#define GINTSTS_PTX_FIFO_EMPTY GINTSTS_PTX_FIFO_EMPTY_Msk // Periodic TxFIFO empty
#define GINTSTS_LPMINT_Pos (27U)
-#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000 */
-#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt */
-#define GINTSTS_CIDSCHG_Pos (28U)
-#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000 */
-#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change */
+#define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000
+#define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt
+#define GINTSTS_CONIDSTSCHNG_Pos (28U)
+#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000
+#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change
#define GINTSTS_DISCINT_Pos (29U)
-#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000 */
-#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt */
+#define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000
+#define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt
#define GINTSTS_SRQINT_Pos (30U)
-#define GINTSTS_SRQINT_Msk (0x1UL << GINTSTS_SRQINT_Pos) // 0x40000000 */
-#define GINTSTS_SRQINT GINTSTS_SRQINT_Msk // Session request/new session detected interrupt */
+#define GINTSTS_SRQINT_Msk (0x1UL << GINTSTS_SRQINT_Pos) // 0x40000000
+#define GINTSTS_SRQINT GINTSTS_SRQINT_Msk // Session request/new session detected interrupt
#define GINTSTS_WKUINT_Pos (31U)
-#define GINTSTS_WKUINT_Msk (0x1UL << GINTSTS_WKUINT_Pos) // 0x80000000 */
-#define GINTSTS_WKUINT GINTSTS_WKUINT_Msk // Resume/remote wakeup detected interrupt */
+#define GINTSTS_WKUINT_Msk (0x1UL << GINTSTS_WKUINT_Pos) // 0x80000000
+#define GINTSTS_WKUINT GINTSTS_WKUINT_Msk // Resume/remote wakeup detected interrupt
/******************** Bit definition for GINTMSK register ********************/
#define GINTMSK_MMISM_Pos (1U)
-#define GINTMSK_MMISM_Msk (0x1UL << GINTMSK_MMISM_Pos) // 0x00000002 */
-#define GINTMSK_MMISM GINTMSK_MMISM_Msk // Mode mismatch interrupt mask */
+#define GINTMSK_MMISM_Msk (0x1UL << GINTMSK_MMISM_Pos) // 0x00000002
+#define GINTMSK_MMISM GINTMSK_MMISM_Msk // Mode mismatch interrupt mask
#define GINTMSK_OTGINT_Pos (2U)
-#define GINTMSK_OTGINT_Msk (0x1UL << GINTMSK_OTGINT_Pos) // 0x00000004 */
-#define GINTMSK_OTGINT GINTMSK_OTGINT_Msk // OTG interrupt mask */
+#define GINTMSK_OTGINT_Msk (0x1UL << GINTMSK_OTGINT_Pos) // 0x00000004
+#define GINTMSK_OTGINT GINTMSK_OTGINT_Msk // OTG interrupt mask
#define GINTMSK_SOFM_Pos (3U)
-#define GINTMSK_SOFM_Msk (0x1UL << GINTMSK_SOFM_Pos) // 0x00000008 */
-#define GINTMSK_SOFM GINTMSK_SOFM_Msk // Start of frame mask */
+#define GINTMSK_SOFM_Msk (0x1UL << GINTMSK_SOFM_Pos) // 0x00000008
+#define GINTMSK_SOFM GINTMSK_SOFM_Msk // Start of frame mask
#define GINTMSK_RXFLVLM_Pos (4U)
-#define GINTMSK_RXFLVLM_Msk (0x1UL << GINTMSK_RXFLVLM_Pos) // 0x00000010 */
-#define GINTMSK_RXFLVLM GINTMSK_RXFLVLM_Msk // Receive FIFO nonempty mask */
+#define GINTMSK_RXFLVLM_Msk (0x1UL << GINTMSK_RXFLVLM_Pos) // 0x00000010
+#define GINTMSK_RXFLVLM GINTMSK_RXFLVLM_Msk // Receive FIFO nonempty mask
#define GINTMSK_NPTXFEM_Pos (5U)
-#define GINTMSK_NPTXFEM_Msk (0x1UL << GINTMSK_NPTXFEM_Pos) // 0x00000020 */
-#define GINTMSK_NPTXFEM GINTMSK_NPTXFEM_Msk // Nonperiodic TxFIFO empty mask */
+#define GINTMSK_NPTXFEM_Msk (0x1UL << GINTMSK_NPTXFEM_Pos) // 0x00000020
+#define GINTMSK_NPTXFEM GINTMSK_NPTXFEM_Msk // Nonperiodic TxFIFO empty mask
#define GINTMSK_GINAKEFFM_Pos (6U)
-#define GINTMSK_GINAKEFFM_Msk (0x1UL << GINTMSK_GINAKEFFM_Pos) // 0x00000040 */
-#define GINTMSK_GINAKEFFM GINTMSK_GINAKEFFM_Msk // Global nonperiodic IN NAK effective mask */
+#define GINTMSK_GINAKEFFM_Msk (0x1UL << GINTMSK_GINAKEFFM_Pos) // 0x00000040
+#define GINTMSK_GINAKEFFM GINTMSK_GINAKEFFM_Msk // Global nonperiodic IN NAK effective mask
#define GINTMSK_GONAKEFFM_Pos (7U)
-#define GINTMSK_GONAKEFFM_Msk (0x1UL << GINTMSK_GONAKEFFM_Pos) // 0x00000080 */
-#define GINTMSK_GONAKEFFM GINTMSK_GONAKEFFM_Msk // Global OUT NAK effective mask */
+#define GINTMSK_GONAKEFFM_Msk (0x1UL << GINTMSK_GONAKEFFM_Pos) // 0x00000080
+#define GINTMSK_GONAKEFFM GINTMSK_GONAKEFFM_Msk // Global OUT NAK effective mask
#define GINTMSK_ESUSPM_Pos (10U)
-#define GINTMSK_ESUSPM_Msk (0x1UL << GINTMSK_ESUSPM_Pos) // 0x00000400 */
-#define GINTMSK_ESUSPM GINTMSK_ESUSPM_Msk // Early suspend mask */
+#define GINTMSK_ESUSPM_Msk (0x1UL << GINTMSK_ESUSPM_Pos) // 0x00000400
+#define GINTMSK_ESUSPM GINTMSK_ESUSPM_Msk // Early suspend mask
#define GINTMSK_USBSUSPM_Pos (11U)
-#define GINTMSK_USBSUSPM_Msk (0x1UL << GINTMSK_USBSUSPM_Pos) // 0x00000800 */
-#define GINTMSK_USBSUSPM GINTMSK_USBSUSPM_Msk // USB suspend mask */
+#define GINTMSK_USBSUSPM_Msk (0x1UL << GINTMSK_USBSUSPM_Pos) // 0x00000800
+#define GINTMSK_USBSUSPM GINTMSK_USBSUSPM_Msk // USB suspend mask
#define GINTMSK_USBRST_Pos (12U)
-#define GINTMSK_USBRST_Msk (0x1UL << GINTMSK_USBRST_Pos) // 0x00001000 */
-#define GINTMSK_USBRST GINTMSK_USBRST_Msk // USB reset mask */
+#define GINTMSK_USBRST_Msk (0x1UL << GINTMSK_USBRST_Pos) // 0x00001000
+#define GINTMSK_USBRST GINTMSK_USBRST_Msk // USB reset mask
#define GINTMSK_ENUMDNEM_Pos (13U)
-#define GINTMSK_ENUMDNEM_Msk (0x1UL << GINTMSK_ENUMDNEM_Pos) // 0x00002000 */
-#define GINTMSK_ENUMDNEM GINTMSK_ENUMDNEM_Msk // Enumeration done mask */
+#define GINTMSK_ENUMDNEM_Msk (0x1UL << GINTMSK_ENUMDNEM_Pos) // 0x00002000
+#define GINTMSK_ENUMDNEM GINTMSK_ENUMDNEM_Msk // Enumeration done mask
#define GINTMSK_ISOODRPM_Pos (14U)
-#define GINTMSK_ISOODRPM_Msk (0x1UL << GINTMSK_ISOODRPM_Pos) // 0x00004000 */
-#define GINTMSK_ISOODRPM GINTMSK_ISOODRPM_Msk // Isochronous OUT packet dropped interrupt mask */
+#define GINTMSK_ISOODRPM_Msk (0x1UL << GINTMSK_ISOODRPM_Pos) // 0x00004000
+#define GINTMSK_ISOODRPM GINTMSK_ISOODRPM_Msk // Isochronous OUT packet dropped interrupt mask
#define GINTMSK_EOPFM_Pos (15U)
-#define GINTMSK_EOPFM_Msk (0x1UL << GINTMSK_EOPFM_Pos) // 0x00008000 */
-#define GINTMSK_EOPFM GINTMSK_EOPFM_Msk // End of periodic frame interrupt mask */
+#define GINTMSK_EOPFM_Msk (0x1UL << GINTMSK_EOPFM_Pos) // 0x00008000
+#define GINTMSK_EOPFM GINTMSK_EOPFM_Msk // End of periodic frame interrupt mask
#define GINTMSK_EPMISM_Pos (17U)
-#define GINTMSK_EPMISM_Msk (0x1UL << GINTMSK_EPMISM_Pos) // 0x00020000 */
-#define GINTMSK_EPMISM GINTMSK_EPMISM_Msk // Endpoint mismatch interrupt mask */
+#define GINTMSK_EPMISM_Msk (0x1UL << GINTMSK_EPMISM_Pos) // 0x00020000
+#define GINTMSK_EPMISM GINTMSK_EPMISM_Msk // Endpoint mismatch interrupt mask
#define GINTMSK_IEPINT_Pos (18U)
-#define GINTMSK_IEPINT_Msk (0x1UL << GINTMSK_IEPINT_Pos) // 0x00040000 */
-#define GINTMSK_IEPINT GINTMSK_IEPINT_Msk // IN endpoints interrupt mask */
+#define GINTMSK_IEPINT_Msk (0x1UL << GINTMSK_IEPINT_Pos) // 0x00040000
+#define GINTMSK_IEPINT GINTMSK_IEPINT_Msk // IN endpoints interrupt mask
#define GINTMSK_OEPINT_Pos (19U)
-#define GINTMSK_OEPINT_Msk (0x1UL << GINTMSK_OEPINT_Pos) // 0x00080000 */
-#define GINTMSK_OEPINT GINTMSK_OEPINT_Msk // OUT endpoints interrupt mask */
+#define GINTMSK_OEPINT_Msk (0x1UL << GINTMSK_OEPINT_Pos) // 0x00080000
+#define GINTMSK_OEPINT GINTMSK_OEPINT_Msk // OUT endpoints interrupt mask
#define GINTMSK_IISOIXFRM_Pos (20U)
-#define GINTMSK_IISOIXFRM_Msk (0x1UL << GINTMSK_IISOIXFRM_Pos) // 0x00100000 */
-#define GINTMSK_IISOIXFRM GINTMSK_IISOIXFRM_Msk // Incomplete isochronous IN transfer mask */
+#define GINTMSK_IISOIXFRM_Msk (0x1UL << GINTMSK_IISOIXFRM_Pos) // 0x00100000
+#define GINTMSK_IISOIXFRM GINTMSK_IISOIXFRM_Msk // Incomplete isochronous IN transfer mask
#define GINTMSK_PXFRM_IISOOXFRM_Pos (21U)
-#define GINTMSK_PXFRM_IISOOXFRM_Msk (0x1UL << GINTMSK_PXFRM_IISOOXFRM_Pos) // 0x00200000 */
-#define GINTMSK_PXFRM_IISOOXFRM GINTMSK_PXFRM_IISOOXFRM_Msk // Incomplete periodic transfer mask */
+#define GINTMSK_PXFRM_IISOOXFRM_Msk (0x1UL << GINTMSK_PXFRM_IISOOXFRM_Pos) // 0x00200000
+#define GINTMSK_PXFRM_IISOOXFRM GINTMSK_PXFRM_IISOOXFRM_Msk // Incomplete periodic transfer mask
#define GINTMSK_FSUSPM_Pos (22U)
-#define GINTMSK_FSUSPM_Msk (0x1UL << GINTMSK_FSUSPM_Pos) // 0x00400000 */
-#define GINTMSK_FSUSPM GINTMSK_FSUSPM_Msk // Data fetch suspended mask */
+#define GINTMSK_FSUSPM_Msk (0x1UL << GINTMSK_FSUSPM_Pos) // 0x00400000
+#define GINTMSK_FSUSPM GINTMSK_FSUSPM_Msk // Data fetch suspended mask
#define GINTMSK_RSTDEM_Pos (23U)
-#define GINTMSK_RSTDEM_Msk (0x1UL << GINTMSK_RSTDEM_Pos) // 0x00800000 */
-#define GINTMSK_RSTDEM GINTMSK_RSTDEM_Msk // Reset detected interrupt mask */
+#define GINTMSK_RSTDEM_Msk (0x1UL << GINTMSK_RSTDEM_Pos) // 0x00800000
+#define GINTMSK_RSTDEM GINTMSK_RSTDEM_Msk // Reset detected interrupt mask
#define GINTMSK_PRTIM_Pos (24U)
-#define GINTMSK_PRTIM_Msk (0x1UL << GINTMSK_PRTIM_Pos) // 0x01000000 */
-#define GINTMSK_PRTIM GINTMSK_PRTIM_Msk // Host port interrupt mask */
+#define GINTMSK_PRTIM_Msk (0x1UL << GINTMSK_PRTIM_Pos) // 0x01000000
+#define GINTMSK_PRTIM GINTMSK_PRTIM_Msk // Host port interrupt mask
#define GINTMSK_HCIM_Pos (25U)
-#define GINTMSK_HCIM_Msk (0x1UL << GINTMSK_HCIM_Pos) // 0x02000000 */
-#define GINTMSK_HCIM GINTMSK_HCIM_Msk // Host channels interrupt mask */
+#define GINTMSK_HCIM_Msk (0x1UL << GINTMSK_HCIM_Pos) // 0x02000000
+#define GINTMSK_HCIM GINTMSK_HCIM_Msk // Host channels interrupt mask
#define GINTMSK_PTXFEM_Pos (26U)
-#define GINTMSK_PTXFEM_Msk (0x1UL << GINTMSK_PTXFEM_Pos) // 0x04000000 */
-#define GINTMSK_PTXFEM GINTMSK_PTXFEM_Msk // Periodic TxFIFO empty mask */
+#define GINTMSK_PTXFEM_Msk (0x1UL << GINTMSK_PTXFEM_Pos) // 0x04000000
+#define GINTMSK_PTXFEM GINTMSK_PTXFEM_Msk // Periodic TxFIFO empty mask
#define GINTMSK_LPMINTM_Pos (27U)
-#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000 */
-#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask */
-#define GINTMSK_CIDSCHGM_Pos (28U)
-#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000 */
-#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask */
+#define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000
+#define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask
+#define GINTMSK_CONIDSTSCHNGM_Pos (28U)
+#define GINTMSK_CONIDSTSCHNGM_Msk (0x1UL << GINTMSK_CONIDSTSCHNGM_Pos) // 0x10000000
+#define GINTMSK_CONIDSTSCHNGM GINTMSK_CONIDSTSCHNGM_Msk // Connector ID status change mask
#define GINTMSK_DISCINT_Pos (29U)
-#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000 */
-#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask */
+#define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000
+#define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask
#define GINTMSK_SRQIM_Pos (30U)
-#define GINTMSK_SRQIM_Msk (0x1UL << GINTMSK_SRQIM_Pos) // 0x40000000 */
-#define GINTMSK_SRQIM GINTMSK_SRQIM_Msk // Session request/new session detected interrupt mask */
+#define GINTMSK_SRQIM_Msk (0x1UL << GINTMSK_SRQIM_Pos) // 0x40000000
+#define GINTMSK_SRQIM GINTMSK_SRQIM_Msk // Session request/new session detected interrupt mask
#define GINTMSK_WUIM_Pos (31U)
-#define GINTMSK_WUIM_Msk (0x1UL << GINTMSK_WUIM_Pos) // 0x80000000 */
-#define GINTMSK_WUIM GINTMSK_WUIM_Msk // Resume/remote wakeup detected interrupt mask */
+#define GINTMSK_WUIM_Msk (0x1UL << GINTMSK_WUIM_Pos) // 0x80000000
+#define GINTMSK_WUIM GINTMSK_WUIM_Msk // Resume/remote wakeup detected interrupt mask
/******************** Bit definition for DAINT register ********************/
#define DAINT_IEPINT_Pos (0U)
-#define DAINT_IEPINT_Msk (0xFFFFUL << DAINT_IEPINT_Pos) // 0x0000FFFF */
-#define DAINT_IEPINT DAINT_IEPINT_Msk // IN endpoint interrupt bits */
+#define DAINT_IEPINT_Msk (0xFFFFUL << DAINT_IEPINT_Pos) // 0x0000FFFF
+#define DAINT_IEPINT DAINT_IEPINT_Msk // IN endpoint interrupt bits
#define DAINT_OEPINT_Pos (16U)
-#define DAINT_OEPINT_Msk (0xFFFFUL << DAINT_OEPINT_Pos) // 0xFFFF0000 */
-#define DAINT_OEPINT DAINT_OEPINT_Msk // OUT endpoint interrupt bits */
+#define DAINT_OEPINT_Msk (0xFFFFUL << DAINT_OEPINT_Pos) // 0xFFFF0000
+#define DAINT_OEPINT DAINT_OEPINT_Msk // OUT endpoint interrupt bits
/******************** Bit definition for HAINTMSK register ********************/
#define HAINTMSK_HAINTM_Pos (0U)
-#define HAINTMSK_HAINTM_Msk (0xFFFFUL << HAINTMSK_HAINTM_Pos) // 0x0000FFFF */
-#define HAINTMSK_HAINTM HAINTMSK_HAINTM_Msk // Channel interrupt mask */
+#define HAINTMSK_HAINTM_Msk (0xFFFFUL << HAINTMSK_HAINTM_Pos) // 0x0000FFFF
+#define HAINTMSK_HAINTM HAINTMSK_HAINTM_Msk // Channel interrupt mask
/******************** Bit definition for GRXSTSP register ********************/
#define GRXSTSP_EPNUM_Pos (0U)
-#define GRXSTSP_EPNUM_Msk (0xFUL << GRXSTSP_EPNUM_Pos) // 0x0000000F */
-#define GRXSTSP_EPNUM GRXSTSP_EPNUM_Msk // IN EP interrupt mask bits */
+#define GRXSTSP_EPNUM_Msk (0xFUL << GRXSTSP_EPNUM_Pos) // 0x0000000F
+#define GRXSTSP_EPNUM GRXSTSP_EPNUM_Msk // IN EP interrupt mask bits
#define GRXSTSP_BCNT_Pos (4U)
-#define GRXSTSP_BCNT_Msk (0x7FFUL << GRXSTSP_BCNT_Pos) // 0x00007FF0 */
-#define GRXSTSP_BCNT GRXSTSP_BCNT_Msk // OUT EP interrupt mask bits */
+#define GRXSTSP_BCNT_Msk (0x7FFUL << GRXSTSP_BCNT_Pos) // 0x00007FF0
+#define GRXSTSP_BCNT GRXSTSP_BCNT_Msk // OUT EP interrupt mask bits
#define GRXSTSP_DPID_Pos (15U)
-#define GRXSTSP_DPID_Msk (0x3UL << GRXSTSP_DPID_Pos) // 0x00018000 */
-#define GRXSTSP_DPID GRXSTSP_DPID_Msk // OUT EP interrupt mask bits */
+#define GRXSTSP_DPID_Msk (0x3UL << GRXSTSP_DPID_Pos) // 0x00018000
+#define GRXSTSP_DPID GRXSTSP_DPID_Msk // OUT EP interrupt mask bits
#define GRXSTSP_PKTSTS_Pos (17U)
-#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000 */
-#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits */
-
-#define GRXSTS_PKTSTS_GLOBALOUTNAK 1
-#define GRXSTS_PKTSTS_OUTRX 2
-#define GRXSTS_PKTSTS_HCHIN 2
-#define GRXSTS_PKTSTS_OUTDONE 3
-#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3
-#define GRXSTS_PKTSTS_SETUPDONE 4
-#define GRXSTS_PKTSTS_DATATOGGLEERR 5
-#define GRXSTS_PKTSTS_SETUPRX 6
-#define GRXSTS_PKTSTS_HCHHALTED 7
-
+#define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000
+#define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits
/******************** Bit definition for DAINTMSK register ********************/
#define DAINTMSK_IEPM_Pos (0U)
-#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF */
-#define DAINTMSK_IEPM DAINTMSK_IEPM_Msk // IN EP interrupt mask bits */
+#define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF
+#define DAINTMSK_IEPM DAINTMSK_IEPM_Msk // IN EP interrupt mask bits
#define DAINTMSK_OEPM_Pos (16U)
-#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000 */
-#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits */
+#define DAINTMSK_OEPM_Msk (0xFFFFUL << DAINTMSK_OEPM_Pos) // 0xFFFF0000
+#define DAINTMSK_OEPM DAINTMSK_OEPM_Msk // OUT EP interrupt mask bits
+
+#define DAINT_SHIFT(_dir) ((_dir == TUSB_DIR_IN) ? 0 : 16)
#if 0
/******************** Bit definition for OTG register ********************/
#define CHNUM_Pos (0U)
-#define CHNUM_Msk (0xFUL << CHNUM_Pos) // 0x0000000F */
-#define CHNUM CHNUM_Msk // Channel number */
-#define CHNUM_0 (0x1UL << CHNUM_Pos) // 0x00000001 */
-#define CHNUM_1 (0x2UL << CHNUM_Pos) // 0x00000002 */
-#define CHNUM_2 (0x4UL << CHNUM_Pos) // 0x00000004 */
-#define CHNUM_3 (0x8UL << CHNUM_Pos) // 0x00000008 */
+#define CHNUM_Msk (0xFUL << CHNUM_Pos) // 0x0000000F
+#define CHNUM CHNUM_Msk // Channel number
+#define CHNUM_0 (0x1UL << CHNUM_Pos) // 0x00000001
+#define CHNUM_1 (0x2UL << CHNUM_Pos) // 0x00000002
+#define CHNUM_2 (0x4UL << CHNUM_Pos) // 0x00000004
+#define CHNUM_3 (0x8UL << CHNUM_Pos) // 0x00000008
#define BCNT_Pos (4U)
-#define BCNT_Msk (0x7FFUL << BCNT_Pos) // 0x00007FF0 */
-#define BCNT BCNT_Msk // Byte count */
+#define BCNT_Msk (0x7FFUL << BCNT_Pos) // 0x00007FF0
+#define BCNT BCNT_Msk // Byte count
#define DPID_Pos (15U)
-#define DPID_Msk (0x3UL << DPID_Pos) // 0x00018000 */
-#define DPID DPID_Msk // Data PID */
-#define DPID_0 (0x1UL << DPID_Pos) // 0x00008000 */
-#define DPID_1 (0x2UL << DPID_Pos) // 0x00010000 */
+#define DPID_Msk (0x3UL << DPID_Pos) // 0x00018000
+#define DPID DPID_Msk // Data PID
+#define DPID_0 (0x1UL << DPID_Pos) // 0x00008000
+#define DPID_1 (0x2UL << DPID_Pos) // 0x00010000
#define PKTSTS_Pos (17U)
-#define PKTSTS_Msk (0xFUL << PKTSTS_Pos) // 0x001E0000 */
-#define PKTSTS PKTSTS_Msk // Packet status */
-#define PKTSTS_0 (0x1UL << PKTSTS_Pos) // 0x00020000 */
-#define PKTSTS_1 (0x2UL << PKTSTS_Pos) // 0x00040000 */
-#define PKTSTS_2 (0x4UL << PKTSTS_Pos) // 0x00080000 */
-#define PKTSTS_3 (0x8UL << PKTSTS_Pos) // 0x00100000 */
+#define PKTSTS_Msk (0xFUL << PKTSTS_Pos) // 0x001E0000
+#define PKTSTS PKTSTS_Msk // Packet status
+#define PKTSTS_0 (0x1UL << PKTSTS_Pos) // 0x00020000
+#define PKTSTS_1 (0x2UL << PKTSTS_Pos) // 0x00040000
+#define PKTSTS_2 (0x4UL << PKTSTS_Pos) // 0x00080000
+#define PKTSTS_3 (0x8UL << PKTSTS_Pos) // 0x00100000
#define EPNUM_Pos (0U)
-#define EPNUM_Msk (0xFUL << EPNUM_Pos) // 0x0000000F */
-#define EPNUM EPNUM_Msk // Endpoint number */
-#define EPNUM_0 (0x1UL << EPNUM_Pos) // 0x00000001 */
-#define EPNUM_1 (0x2UL << EPNUM_Pos) // 0x00000002 */
-#define EPNUM_2 (0x4UL << EPNUM_Pos) // 0x00000004 */
-#define EPNUM_3 (0x8UL << EPNUM_Pos) // 0x00000008 */
+#define EPNUM_Msk (0xFUL << EPNUM_Pos) // 0x0000000F
+#define EPNUM EPNUM_Msk // Endpoint number
+#define EPNUM_0 (0x1UL << EPNUM_Pos) // 0x00000001
+#define EPNUM_1 (0x2UL << EPNUM_Pos) // 0x00000002
+#define EPNUM_2 (0x4UL << EPNUM_Pos) // 0x00000004
+#define EPNUM_3 (0x8UL << EPNUM_Pos) // 0x00000008
#define FRMNUM_Pos (21U)
-#define FRMNUM_Msk (0xFUL << FRMNUM_Pos) // 0x01E00000 */
-#define FRMNUM FRMNUM_Msk // Frame number */
-#define FRMNUM_0 (0x1UL << FRMNUM_Pos) // 0x00200000 */
-#define FRMNUM_1 (0x2UL << FRMNUM_Pos) // 0x00400000 */
-#define FRMNUM_2 (0x4UL << FRMNUM_Pos) // 0x00800000 */
-#define FRMNUM_3 (0x8UL << FRMNUM_Pos) // 0x01000000 */
+#define FRMNUM_Msk (0xFUL << FRMNUM_Pos) // 0x01E00000
+#define FRMNUM FRMNUM_Msk // Frame number
+#define FRMNUM_0 (0x1UL << FRMNUM_Pos) // 0x00200000
+#define FRMNUM_1 (0x2UL << FRMNUM_Pos) // 0x00400000
+#define FRMNUM_2 (0x4UL << FRMNUM_Pos) // 0x00800000
+#define FRMNUM_3 (0x8UL << FRMNUM_Pos) // 0x01000000
#endif
/******************** Bit definition for GRXFSIZ register ********************/
#define GRXFSIZ_RXFD_Pos (0U)
-#define GRXFSIZ_RXFD_Msk (0xFFFFUL << GRXFSIZ_RXFD_Pos) // 0x0000FFFF */
-#define GRXFSIZ_RXFD GRXFSIZ_RXFD_Msk // RxFIFO depth */
+#define GRXFSIZ_RXFD_Msk (0xFFFFUL << GRXFSIZ_RXFD_Pos) // 0x0000FFFF
+#define GRXFSIZ_RXFD GRXFSIZ_RXFD_Msk // RxFIFO depth
/******************** Bit definition for DVBUSDIS register ********************/
#define DVBUSDIS_VBUSDT_Pos (0U)
-#define DVBUSDIS_VBUSDT_Msk (0xFFFFUL << DVBUSDIS_VBUSDT_Pos) // 0x0000FFFF */
-#define DVBUSDIS_VBUSDT DVBUSDIS_VBUSDT_Msk // Device VBUS discharge time */
+#define DVBUSDIS_VBUSDT_Msk (0xFFFFUL << DVBUSDIS_VBUSDT_Pos) // 0x0000FFFF
+#define DVBUSDIS_VBUSDT DVBUSDIS_VBUSDT_Msk // Device VBUS discharge time
/******************** Bit definition for OTG register ********************/
#define GNPTXFSIZ_NPTXFSA_Pos (0U)
-#define GNPTXFSIZ_NPTXFSA_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFSA_Pos) // 0x0000FFFF */
-#define GNPTXFSIZ_NPTXFSA GNPTXFSIZ_NPTXFSA_Msk // Nonperiodic transmit RAM start address */
+#define GNPTXFSIZ_NPTXFSA_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFSA_Pos) // 0x0000FFFF
+#define GNPTXFSIZ_NPTXFSA GNPTXFSIZ_NPTXFSA_Msk // Nonperiodic transmit RAM start address
#define GNPTXFSIZ_NPTXFD_Pos (16U)
-#define GNPTXFSIZ_NPTXFD_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFD_Pos) // 0xFFFF0000 */
-#define GNPTXFSIZ_NPTXFD GNPTXFSIZ_NPTXFD_Msk // Nonperiodic TxFIFO depth */
+#define GNPTXFSIZ_NPTXFD_Msk (0xFFFFUL << GNPTXFSIZ_NPTXFD_Pos) // 0xFFFF0000
+#define GNPTXFSIZ_NPTXFD GNPTXFSIZ_NPTXFD_Msk // Nonperiodic TxFIFO depth
#define DIEPTXF0_TX0FSA_Pos (0U)
-#define DIEPTXF0_TX0FSA_Msk (0xFFFFUL << DIEPTXF0_TX0FSA_Pos) // 0x0000FFFF */
-#define DIEPTXF0_TX0FSA DIEPTXF0_TX0FSA_Msk // Endpoint 0 transmit RAM start address */
+#define DIEPTXF0_TX0FSA_Msk (0xFFFFUL << DIEPTXF0_TX0FSA_Pos) // 0x0000FFFF
+#define DIEPTXF0_TX0FSA DIEPTXF0_TX0FSA_Msk // Endpoint 0 transmit RAM start address
#define DIEPTXF0_TX0FD_Pos (16U)
-#define DIEPTXF0_TX0FD_Msk (0xFFFFUL << DIEPTXF0_TX0FD_Pos) // 0xFFFF0000 */
-#define DIEPTXF0_TX0FD DIEPTXF0_TX0FD_Msk // Endpoint 0 TxFIFO depth */
+#define DIEPTXF0_TX0FD_Msk (0xFFFFUL << DIEPTXF0_TX0FD_Pos) // 0xFFFF0000
+#define DIEPTXF0_TX0FD DIEPTXF0_TX0FD_Msk // Endpoint 0 TxFIFO depth
/******************** Bit definition for DVBUSPULSE register ********************/
#define DVBUSPULSE_DVBUSP_Pos (0U)
-#define DVBUSPULSE_DVBUSP_Msk (0xFFFUL << DVBUSPULSE_DVBUSP_Pos) // 0x00000FFF */
-#define DVBUSPULSE_DVBUSP DVBUSPULSE_DVBUSP_Msk // Device VBUS pulsing time */
+#define DVBUSPULSE_DVBUSP_Msk (0xFFFUL << DVBUSPULSE_DVBUSP_Pos) // 0x00000FFF
+#define DVBUSPULSE_DVBUSP DVBUSPULSE_DVBUSP_Msk // Device VBUS pulsing time
/******************** Bit definition for GNPTXSTS register ********************/
#define GNPTXSTS_NPTXFSAV_Pos (0U)
-#define GNPTXSTS_NPTXFSAV_Msk (0xFFFFUL << GNPTXSTS_NPTXFSAV_Pos) // 0x0000FFFF */
-#define GNPTXSTS_NPTXFSAV GNPTXSTS_NPTXFSAV_Msk // Nonperiodic TxFIFO space available */
+#define GNPTXSTS_NPTXFSAV_Msk (0xFFFFUL << GNPTXSTS_NPTXFSAV_Pos) // 0x0000FFFF
+#define GNPTXSTS_NPTXFSAV GNPTXSTS_NPTXFSAV_Msk // Nonperiodic TxFIFO space available
#define GNPTXSTS_NPTQXSAV_Pos (16U)
-#define GNPTXSTS_NPTQXSAV_Msk (0xFFUL << GNPTXSTS_NPTQXSAV_Pos) // 0x00FF0000 */
-#define GNPTXSTS_NPTQXSAV GNPTXSTS_NPTQXSAV_Msk // Nonperiodic transmit request queue space available */
-#define GNPTXSTS_NPTQXSAV_0 (0x01UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00010000 */
-#define GNPTXSTS_NPTQXSAV_1 (0x02UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00020000 */
-#define GNPTXSTS_NPTQXSAV_2 (0x04UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00040000 */
-#define GNPTXSTS_NPTQXSAV_3 (0x08UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00080000 */
-#define GNPTXSTS_NPTQXSAV_4 (0x10UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00100000 */
-#define GNPTXSTS_NPTQXSAV_5 (0x20UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00200000 */
-#define GNPTXSTS_NPTQXSAV_6 (0x40UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00400000 */
-#define GNPTXSTS_NPTQXSAV_7 (0x80UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00800000 */
+#define GNPTXSTS_NPTQXSAV_Msk (0xFFUL << GNPTXSTS_NPTQXSAV_Pos) // 0x00FF0000
+#define GNPTXSTS_NPTQXSAV GNPTXSTS_NPTQXSAV_Msk // Nonperiodic transmit request queue space available
+#define GNPTXSTS_NPTQXSAV_0 (0x01UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00010000
+#define GNPTXSTS_NPTQXSAV_1 (0x02UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00020000
+#define GNPTXSTS_NPTQXSAV_2 (0x04UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00040000
+#define GNPTXSTS_NPTQXSAV_3 (0x08UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00080000
+#define GNPTXSTS_NPTQXSAV_4 (0x10UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00100000
+#define GNPTXSTS_NPTQXSAV_5 (0x20UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00200000
+#define GNPTXSTS_NPTQXSAV_6 (0x40UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00400000
+#define GNPTXSTS_NPTQXSAV_7 (0x80UL << GNPTXSTS_NPTQXSAV_Pos) // 0x00800000
#define GNPTXSTS_NPTXQTOP_Pos (24U)
-#define GNPTXSTS_NPTXQTOP_Msk (0x7FUL << GNPTXSTS_NPTXQTOP_Pos) // 0x7F000000 */
-#define GNPTXSTS_NPTXQTOP GNPTXSTS_NPTXQTOP_Msk // Top of the nonperiodic transmit request queue */
-#define GNPTXSTS_NPTXQTOP_0 (0x01UL << GNPTXSTS_NPTXQTOP_Pos) // 0x01000000 */
-#define GNPTXSTS_NPTXQTOP_1 (0x02UL << GNPTXSTS_NPTXQTOP_Pos) // 0x02000000 */
-#define GNPTXSTS_NPTXQTOP_2 (0x04UL << GNPTXSTS_NPTXQTOP_Pos) // 0x04000000 */
-#define GNPTXSTS_NPTXQTOP_3 (0x08UL << GNPTXSTS_NPTXQTOP_Pos) // 0x08000000 */
-#define GNPTXSTS_NPTXQTOP_4 (0x10UL << GNPTXSTS_NPTXQTOP_Pos) // 0x10000000 */
-#define GNPTXSTS_NPTXQTOP_5 (0x20UL << GNPTXSTS_NPTXQTOP_Pos) // 0x20000000 */
-#define GNPTXSTS_NPTXQTOP_6 (0x40UL << GNPTXSTS_NPTXQTOP_Pos) // 0x40000000 */
+#define GNPTXSTS_NPTXQTOP_Msk (0x7FUL << GNPTXSTS_NPTXQTOP_Pos) // 0x7F000000
+#define GNPTXSTS_NPTXQTOP GNPTXSTS_NPTXQTOP_Msk // Top of the nonperiodic transmit request queue
+#define GNPTXSTS_NPTXQTOP_0 (0x01UL << GNPTXSTS_NPTXQTOP_Pos) // 0x01000000
+#define GNPTXSTS_NPTXQTOP_1 (0x02UL << GNPTXSTS_NPTXQTOP_Pos) // 0x02000000
+#define GNPTXSTS_NPTXQTOP_2 (0x04UL << GNPTXSTS_NPTXQTOP_Pos) // 0x04000000
+#define GNPTXSTS_NPTXQTOP_3 (0x08UL << GNPTXSTS_NPTXQTOP_Pos) // 0x08000000
+#define GNPTXSTS_NPTXQTOP_4 (0x10UL << GNPTXSTS_NPTXQTOP_Pos) // 0x10000000
+#define GNPTXSTS_NPTXQTOP_5 (0x20UL << GNPTXSTS_NPTXQTOP_Pos) // 0x20000000
+#define GNPTXSTS_NPTXQTOP_6 (0x40UL << GNPTXSTS_NPTXQTOP_Pos) // 0x40000000
/******************** Bit definition for DTHRCTL register ********************/
#define DTHRCTL_NONISOTHREN_Pos (0U)
-#define DTHRCTL_NONISOTHREN_Msk (0x1UL << DTHRCTL_NONISOTHREN_Pos) // 0x00000001 */
-#define DTHRCTL_NONISOTHREN DTHRCTL_NONISOTHREN_Msk // Nonisochronous IN endpoints threshold enable */
+#define DTHRCTL_NONISOTHREN_Msk (0x1UL << DTHRCTL_NONISOTHREN_Pos) // 0x00000001
+#define DTHRCTL_NONISOTHREN DTHRCTL_NONISOTHREN_Msk // Nonisochronous IN endpoints threshold enable
#define DTHRCTL_ISOTHREN_Pos (1U)
-#define DTHRCTL_ISOTHREN_Msk (0x1UL << DTHRCTL_ISOTHREN_Pos) // 0x00000002 */
-#define DTHRCTL_ISOTHREN DTHRCTL_ISOTHREN_Msk // ISO IN endpoint threshold enable */
+#define DTHRCTL_ISOTHREN_Msk (0x1UL << DTHRCTL_ISOTHREN_Pos) // 0x00000002
+#define DTHRCTL_ISOTHREN DTHRCTL_ISOTHREN_Msk // ISO IN endpoint threshold enable
#define DTHRCTL_TXTHRLEN_Pos (2U)
-#define DTHRCTL_TXTHRLEN_Msk (0x1FFUL << DTHRCTL_TXTHRLEN_Pos) // 0x000007FC */
-#define DTHRCTL_TXTHRLEN DTHRCTL_TXTHRLEN_Msk // Transmit threshold length */
-#define DTHRCTL_TXTHRLEN_0 (0x001UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000004 */
-#define DTHRCTL_TXTHRLEN_1 (0x002UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000008 */
-#define DTHRCTL_TXTHRLEN_2 (0x004UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000010 */
-#define DTHRCTL_TXTHRLEN_3 (0x008UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000020 */
-#define DTHRCTL_TXTHRLEN_4 (0x010UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000040 */
-#define DTHRCTL_TXTHRLEN_5 (0x020UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000080 */
-#define DTHRCTL_TXTHRLEN_6 (0x040UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000100 */
-#define DTHRCTL_TXTHRLEN_7 (0x080UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000200 */
-#define DTHRCTL_TXTHRLEN_8 (0x100UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000400 */
+#define DTHRCTL_TXTHRLEN_Msk (0x1FFUL << DTHRCTL_TXTHRLEN_Pos) // 0x000007FC
+#define DTHRCTL_TXTHRLEN DTHRCTL_TXTHRLEN_Msk // Transmit threshold length
+#define DTHRCTL_TXTHRLEN_0 (0x001UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000004
+#define DTHRCTL_TXTHRLEN_1 (0x002UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000008
+#define DTHRCTL_TXTHRLEN_2 (0x004UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000010
+#define DTHRCTL_TXTHRLEN_3 (0x008UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000020
+#define DTHRCTL_TXTHRLEN_4 (0x010UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000040
+#define DTHRCTL_TXTHRLEN_5 (0x020UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000080
+#define DTHRCTL_TXTHRLEN_6 (0x040UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000100
+#define DTHRCTL_TXTHRLEN_7 (0x080UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000200
+#define DTHRCTL_TXTHRLEN_8 (0x100UL << DTHRCTL_TXTHRLEN_Pos) // 0x00000400
#define DTHRCTL_RXTHREN_Pos (16U)
-#define DTHRCTL_RXTHREN_Msk (0x1UL << DTHRCTL_RXTHREN_Pos) // 0x00010000 */
-#define DTHRCTL_RXTHREN DTHRCTL_RXTHREN_Msk // Receive threshold enable */
+#define DTHRCTL_RXTHREN_Msk (0x1UL << DTHRCTL_RXTHREN_Pos) // 0x00010000
+#define DTHRCTL_RXTHREN DTHRCTL_RXTHREN_Msk // Receive threshold enable
#define DTHRCTL_RXTHRLEN_Pos (17U)
-#define DTHRCTL_RXTHRLEN_Msk (0x1FFUL << DTHRCTL_RXTHRLEN_Pos) // 0x03FE0000 */
-#define DTHRCTL_RXTHRLEN DTHRCTL_RXTHRLEN_Msk // Receive threshold length */
-#define DTHRCTL_RXTHRLEN_0 (0x001UL << DTHRCTL_RXTHRLEN_Pos) // 0x00020000 */
-#define DTHRCTL_RXTHRLEN_1 (0x002UL << DTHRCTL_RXTHRLEN_Pos) // 0x00040000 */
-#define DTHRCTL_RXTHRLEN_2 (0x004UL << DTHRCTL_RXTHRLEN_Pos) // 0x00080000 */
-#define DTHRCTL_RXTHRLEN_3 (0x008UL << DTHRCTL_RXTHRLEN_Pos) // 0x00100000 */
-#define DTHRCTL_RXTHRLEN_4 (0x010UL << DTHRCTL_RXTHRLEN_Pos) // 0x00200000 */
-#define DTHRCTL_RXTHRLEN_5 (0x020UL << DTHRCTL_RXTHRLEN_Pos) // 0x00400000 */
-#define DTHRCTL_RXTHRLEN_6 (0x040UL << DTHRCTL_RXTHRLEN_Pos) // 0x00800000 */
-#define DTHRCTL_RXTHRLEN_7 (0x080UL << DTHRCTL_RXTHRLEN_Pos) // 0x01000000 */
-#define DTHRCTL_RXTHRLEN_8 (0x100UL << DTHRCTL_RXTHRLEN_Pos) // 0x02000000 */
+#define DTHRCTL_RXTHRLEN_Msk (0x1FFUL << DTHRCTL_RXTHRLEN_Pos) // 0x03FE0000
+#define DTHRCTL_RXTHRLEN DTHRCTL_RXTHRLEN_Msk // Receive threshold length
+#define DTHRCTL_RXTHRLEN_0 (0x001UL << DTHRCTL_RXTHRLEN_Pos) // 0x00020000
+#define DTHRCTL_RXTHRLEN_1 (0x002UL << DTHRCTL_RXTHRLEN_Pos) // 0x00040000
+#define DTHRCTL_RXTHRLEN_2 (0x004UL << DTHRCTL_RXTHRLEN_Pos) // 0x00080000
+#define DTHRCTL_RXTHRLEN_3 (0x008UL << DTHRCTL_RXTHRLEN_Pos) // 0x00100000
+#define DTHRCTL_RXTHRLEN_4 (0x010UL << DTHRCTL_RXTHRLEN_Pos) // 0x00200000
+#define DTHRCTL_RXTHRLEN_5 (0x020UL << DTHRCTL_RXTHRLEN_Pos) // 0x00400000
+#define DTHRCTL_RXTHRLEN_6 (0x040UL << DTHRCTL_RXTHRLEN_Pos) // 0x00800000
+#define DTHRCTL_RXTHRLEN_7 (0x080UL << DTHRCTL_RXTHRLEN_Pos) // 0x01000000
+#define DTHRCTL_RXTHRLEN_8 (0x100UL << DTHRCTL_RXTHRLEN_Pos) // 0x02000000
#define DTHRCTL_ARPEN_Pos (27U)
-#define DTHRCTL_ARPEN_Msk (0x1UL << DTHRCTL_ARPEN_Pos) // 0x08000000 */
-#define DTHRCTL_ARPEN DTHRCTL_ARPEN_Msk // Arbiter parking enable */
+#define DTHRCTL_ARPEN_Msk (0x1UL << DTHRCTL_ARPEN_Pos) // 0x08000000
+#define DTHRCTL_ARPEN DTHRCTL_ARPEN_Msk // Arbiter parking enable
/******************** Bit definition for DIEPEMPMSK register ********************/
#define DIEPEMPMSK_INEPTXFEM_Pos (0U)
-#define DIEPEMPMSK_INEPTXFEM_Msk (0xFFFFUL << DIEPEMPMSK_INEPTXFEM_Pos) // 0x0000FFFF */
-#define DIEPEMPMSK_INEPTXFEM DIEPEMPMSK_INEPTXFEM_Msk // IN EP Tx FIFO empty interrupt mask bits */
+#define DIEPEMPMSK_INEPTXFEM_Msk (0xFFFFUL << DIEPEMPMSK_INEPTXFEM_Pos) // 0x0000FFFF
+#define DIEPEMPMSK_INEPTXFEM DIEPEMPMSK_INEPTXFEM_Msk // IN EP Tx FIFO empty interrupt mask bits
/******************** Bit definition for DEACHINT register ********************/
#define DEACHINT_IEP1INT_Pos (1U)
-#define DEACHINT_IEP1INT_Msk (0x1UL << DEACHINT_IEP1INT_Pos) // 0x00000002 */
-#define DEACHINT_IEP1INT DEACHINT_IEP1INT_Msk // IN endpoint 1interrupt bit */
+#define DEACHINT_IEP1INT_Msk (0x1UL << DEACHINT_IEP1INT_Pos) // 0x00000002
+#define DEACHINT_IEP1INT DEACHINT_IEP1INT_Msk // IN endpoint 1interrupt bit
#define DEACHINT_OEP1INT_Pos (17U)
-#define DEACHINT_OEP1INT_Msk (0x1UL << DEACHINT_OEP1INT_Pos) // 0x00020000 */
-#define DEACHINT_OEP1INT DEACHINT_OEP1INT_Msk // OUT endpoint 1 interrupt bit */
+#define DEACHINT_OEP1INT_Msk (0x1UL << DEACHINT_OEP1INT_Pos) // 0x00020000
+#define DEACHINT_OEP1INT DEACHINT_OEP1INT_Msk // OUT endpoint 1 interrupt bit
/******************** Bit definition for GCCFG register ********************/
#define STM32_GCCFG_DCDET_Pos (0U)
-#define STM32_GCCFG_DCDET_Msk (0x1UL << STM32_GCCFG_DCDET_Pos) // 0x00000001 */
-#define STM32_GCCFG_DCDET STM32_GCCFG_DCDET_Msk // Data contact detection (DCD) status */
+#define STM32_GCCFG_DCDET_Msk (0x1UL << STM32_GCCFG_DCDET_Pos) // 0x00000001
+#define STM32_GCCFG_DCDET STM32_GCCFG_DCDET_Msk // Data contact detection (DCD) status
+
#define STM32_GCCFG_PDET_Pos (1U)
-#define STM32_GCCFG_PDET_Msk (0x1UL << STM32_GCCFG_PDET_Pos) // 0x00000002 */
-#define STM32_GCCFG_PDET STM32_GCCFG_PDET_Msk // Primary detection (PD) status */
+#define STM32_GCCFG_PDET_Msk (0x1UL << STM32_GCCFG_PDET_Pos) // 0x00000002
+#define STM32_GCCFG_PDET STM32_GCCFG_PDET_Msk // Primary detection (PD) status
+
#define STM32_GCCFG_SDET_Pos (2U)
-#define STM32_GCCFG_SDET_Msk (0x1UL << STM32_GCCFG_SDET_Pos) // 0x00000004 */
-#define STM32_GCCFG_SDET STM32_GCCFG_SDET_Msk // Secondary detection (SD) status */
+#define STM32_GCCFG_SDET_Msk (0x1UL << STM32_GCCFG_SDET_Pos) // 0x00000004
+#define STM32_GCCFG_SDET STM32_GCCFG_SDET_Msk // Secondary detection (SD) status
+
#define STM32_GCCFG_PS2DET_Pos (3U)
-#define STM32_GCCFG_PS2DET_Msk (0x1UL << STM32_GCCFG_PS2DET_Pos) // 0x00000008 */
-#define STM32_GCCFG_PS2DET STM32_GCCFG_PS2DET_Msk // DM pull-up detection status */
+#define STM32_GCCFG_PS2DET_Msk (0x1UL << STM32_GCCFG_PS2DET_Pos) // 0x00000008
+#define STM32_GCCFG_PS2DET STM32_GCCFG_PS2DET_Msk // DM pull-up detection status
+
#define STM32_GCCFG_PWRDWN_Pos (16U)
-#define STM32_GCCFG_PWRDWN_Msk (0x1UL << STM32_GCCFG_PWRDWN_Pos) // 0x00010000 */
-#define STM32_GCCFG_PWRDWN STM32_GCCFG_PWRDWN_Msk // Power down */
+#define STM32_GCCFG_PWRDWN_Msk (0x1UL << STM32_GCCFG_PWRDWN_Pos) // 0x00010000
+#define STM32_GCCFG_PWRDWN STM32_GCCFG_PWRDWN_Msk // Power down
+
#define STM32_GCCFG_BCDEN_Pos (17U)
-#define STM32_GCCFG_BCDEN_Msk (0x1UL << STM32_GCCFG_BCDEN_Pos) // 0x00020000 */
-#define STM32_GCCFG_BCDEN STM32_GCCFG_BCDEN_Msk // Battery charging detector (BCD) enable */
+#define STM32_GCCFG_BCDEN_Msk (0x1UL << STM32_GCCFG_BCDEN_Pos) // 0x00020000
+#define STM32_GCCFG_BCDEN STM32_GCCFG_BCDEN_Msk // Battery charging detector (BCD) enable
+
#define STM32_GCCFG_DCDEN_Pos (18U)
-#define STM32_GCCFG_DCDEN_Msk (0x1UL << STM32_GCCFG_DCDEN_Pos) // 0x00040000 */
+#define STM32_GCCFG_DCDEN_Msk (0x1UL << STM32_GCCFG_DCDEN_Pos) // 0x00040000
#define STM32_GCCFG_DCDEN STM32_GCCFG_DCDEN_Msk // Data contact detection (DCD) mode enable*/
+
#define STM32_GCCFG_PDEN_Pos (19U)
-#define STM32_GCCFG_PDEN_Msk (0x1UL << STM32_GCCFG_PDEN_Pos) // 0x00080000 */
+#define STM32_GCCFG_PDEN_Msk (0x1UL << STM32_GCCFG_PDEN_Pos) // 0x00080000
#define STM32_GCCFG_PDEN STM32_GCCFG_PDEN_Msk // Primary detection (PD) mode enable*/
+
#define STM32_GCCFG_SDEN_Pos (20U)
-#define STM32_GCCFG_SDEN_Msk (0x1UL << STM32_GCCFG_SDEN_Pos) // 0x00100000 */
-#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (SD) mode enable */
+#define STM32_GCCFG_SDEN_Msk (0x1UL << STM32_GCCFG_SDEN_Pos) // 0x00100000
+#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (SD) mode enable
+
#define STM32_GCCFG_VBDEN_Pos (21U)
-#define STM32_GCCFG_VBDEN_Msk (0x1UL << STM32_GCCFG_VBDEN_Pos) // 0x00200000 */
-#define STM32_GCCFG_VBDEN STM32_GCCFG_VBDEN_Msk // VBUS mode enable */
+#define STM32_GCCFG_VBDEN_Msk (0x1UL << STM32_GCCFG_VBDEN_Pos) // 0x00200000
+#define STM32_GCCFG_VBDEN STM32_GCCFG_VBDEN_Msk // VBUS mode enable
+
#define STM32_GCCFG_OTGIDEN_Pos (22U)
-#define STM32_GCCFG_OTGIDEN_Msk (0x1UL << STM32_GCCFG_OTGIDEN_Pos) // 0x00400000 */
-#define STM32_GCCFG_OTGIDEN STM32_GCCFG_OTGIDEN_Msk // OTG Id enable */
+#define STM32_GCCFG_OTGIDEN_Msk (0x1UL << STM32_GCCFG_OTGIDEN_Pos) // 0x00400000
+#define STM32_GCCFG_OTGIDEN STM32_GCCFG_OTGIDEN_Msk // OTG Id enable
+
#define STM32_GCCFG_PHYHSEN_Pos (23U)
-#define STM32_GCCFG_PHYHSEN_Msk (0x1UL << STM32_GCCFG_PHYHSEN_Pos) // 0x00800000 */
-#define STM32_GCCFG_PHYHSEN STM32_GCCFG_PHYHSEN_Msk // HS PHY enable */
+#define STM32_GCCFG_PHYHSEN_Msk (0x1UL << STM32_GCCFG_PHYHSEN_Pos) // 0x00800000
+#define STM32_GCCFG_PHYHSEN STM32_GCCFG_PHYHSEN_Msk // HS PHY enable
+
+// TODO stm32u5a5 SDEN is 22nd bit, conflict with 20th bit above
+//#define STM32_GCCFG_SDEN_Pos (22U)
+//#define STM32_GCCFG_SDEN_Msk (0x1U << STM32_GCCFG_SDEN_Pos) // 0x00400000
+//#define STM32_GCCFG_SDEN STM32_GCCFG_SDEN_Msk // Secondary detection (PD) mode enable
+
+// TODO stm32u5a5 VBVALOVA is 23rd bit, conflict with PHYHSEN bit above
+#define STM32_GCCFG_VBVALOVAL_Pos (23U)
+#define STM32_GCCFG_VBVALOVAL_Msk (0x1U << STM32_GCCFG_VBVALOVAL_Pos) // 0x00800000
+#define STM32_GCCFG_VBVALOVAL STM32_GCCFG_VBVALOVAL_Msk // Value of VBUSVLDEXT0 femtoPHY input
+
+#define STM32_GCCFG_VBVALEXTOEN_Pos (24U)
+#define STM32_GCCFG_VBVALEXTOEN_Msk (0x1U << STM32_GCCFG_VBVALEXTOEN_Pos) // 0x01000000
+#define STM32_GCCFG_VBVALEXTOEN STM32_GCCFG_VBVALEXTOEN_Msk // Enables of VBUSVLDEXT0 femtoPHY input override
+
+#define STM32_GCCFG_PULLDOWNEN_Pos (25U)
+#define STM32_GCCFG_PULLDOWNEN_Msk (0x1U << STM32_GCCFG_PULLDOWNEN_Pos) // 0x02000000
+#define STM32_GCCFG_PULLDOWNEN STM32_GCCFG_PULLDOWNEN_Msk // Enables of femtoPHY pulldown resistors, used when ID PAD is disabled
+
/******************** Bit definition for DEACHINTMSK register ********************/
#define DEACHINTMSK_IEP1INTM_Pos (1U)
-#define DEACHINTMSK_IEP1INTM_Msk (0x1UL << DEACHINTMSK_IEP1INTM_Pos) // 0x00000002 */
-#define DEACHINTMSK_IEP1INTM DEACHINTMSK_IEP1INTM_Msk // IN Endpoint 1 interrupt mask bit */
+#define DEACHINTMSK_IEP1INTM_Msk (0x1UL << DEACHINTMSK_IEP1INTM_Pos) // 0x00000002
+#define DEACHINTMSK_IEP1INTM DEACHINTMSK_IEP1INTM_Msk // IN Endpoint 1 interrupt mask bit
#define DEACHINTMSK_OEP1INTM_Pos (17U)
-#define DEACHINTMSK_OEP1INTM_Msk (0x1UL << DEACHINTMSK_OEP1INTM_Pos) // 0x00020000 */
-#define DEACHINTMSK_OEP1INTM DEACHINTMSK_OEP1INTM_Msk // OUT Endpoint 1 interrupt mask bit */
+#define DEACHINTMSK_OEP1INTM_Msk (0x1UL << DEACHINTMSK_OEP1INTM_Pos) // 0x00020000
+#define DEACHINTMSK_OEP1INTM DEACHINTMSK_OEP1INTM_Msk // OUT Endpoint 1 interrupt mask bit
/******************** Bit definition for CID register ********************/
#define CID_PRODUCT_ID_Pos (0U)
-#define CID_PRODUCT_ID_Msk (0xFFFFFFFFUL << CID_PRODUCT_ID_Pos) // 0xFFFFFFFF */
-#define CID_PRODUCT_ID CID_PRODUCT_ID_Msk // Product ID field */
+#define CID_PRODUCT_ID_Msk (0xFFFFFFFFUL << CID_PRODUCT_ID_Pos) // 0xFFFFFFFF
+#define CID_PRODUCT_ID CID_PRODUCT_ID_Msk // Product ID field
/******************** Bit definition for GLPMCFG register ********************/
#define GLPMCFG_LPMEN_Pos (0U)
-#define GLPMCFG_LPMEN_Msk (0x1UL << GLPMCFG_LPMEN_Pos) // 0x00000001 */
-#define GLPMCFG_LPMEN GLPMCFG_LPMEN_Msk // LPM support enable */
+#define GLPMCFG_LPMEN_Msk (0x1UL << GLPMCFG_LPMEN_Pos) // 0x00000001
+#define GLPMCFG_LPMEN GLPMCFG_LPMEN_Msk // LPM support enable
#define GLPMCFG_LPMACK_Pos (1U)
-#define GLPMCFG_LPMACK_Msk (0x1UL << GLPMCFG_LPMACK_Pos) // 0x00000002 */
-#define GLPMCFG_LPMACK GLPMCFG_LPMACK_Msk // LPM Token acknowledge enable */
+#define GLPMCFG_LPMACK_Msk (0x1UL << GLPMCFG_LPMACK_Pos) // 0x00000002
+#define GLPMCFG_LPMACK GLPMCFG_LPMACK_Msk // LPM Token acknowledge enable
#define GLPMCFG_BESL_Pos (2U)
-#define GLPMCFG_BESL_Msk (0xFUL << GLPMCFG_BESL_Pos) // 0x0000003C */
-#define GLPMCFG_BESL GLPMCFG_BESL_Msk // BESL value received with last ACKed LPM Token */
+#define GLPMCFG_BESL_Msk (0xFUL << GLPMCFG_BESL_Pos) // 0x0000003C
+#define GLPMCFG_BESL GLPMCFG_BESL_Msk // BESL value received with last ACKed LPM Token
#define GLPMCFG_REMWAKE_Pos (6U)
-#define GLPMCFG_REMWAKE_Msk (0x1UL << GLPMCFG_REMWAKE_Pos) // 0x00000040 */
-#define GLPMCFG_REMWAKE GLPMCFG_REMWAKE_Msk // bRemoteWake value received with last ACKed LPM Token */
+#define GLPMCFG_REMWAKE_Msk (0x1UL << GLPMCFG_REMWAKE_Pos) // 0x00000040
+#define GLPMCFG_REMWAKE GLPMCFG_REMWAKE_Msk // bRemoteWake value received with last ACKed LPM Token
#define GLPMCFG_L1SSEN_Pos (7U)
-#define GLPMCFG_L1SSEN_Msk (0x1UL << GLPMCFG_L1SSEN_Pos) // 0x00000080 */
-#define GLPMCFG_L1SSEN GLPMCFG_L1SSEN_Msk // L1 shallow sleep enable */
+#define GLPMCFG_L1SSEN_Msk (0x1UL << GLPMCFG_L1SSEN_Pos) // 0x00000080
+#define GLPMCFG_L1SSEN GLPMCFG_L1SSEN_Msk // L1 shallow sleep enable
#define GLPMCFG_BESLTHRS_Pos (8U)
-#define GLPMCFG_BESLTHRS_Msk (0xFUL << GLPMCFG_BESLTHRS_Pos) // 0x00000F00 */
-#define GLPMCFG_BESLTHRS GLPMCFG_BESLTHRS_Msk // BESL threshold */
+#define GLPMCFG_BESLTHRS_Msk (0xFUL << GLPMCFG_BESLTHRS_Pos) // 0x00000F00
+#define GLPMCFG_BESLTHRS GLPMCFG_BESLTHRS_Msk // BESL threshold
#define GLPMCFG_L1DSEN_Pos (12U)
-#define GLPMCFG_L1DSEN_Msk (0x1UL << GLPMCFG_L1DSEN_Pos) // 0x00001000 */
-#define GLPMCFG_L1DSEN GLPMCFG_L1DSEN_Msk // L1 deep sleep enable */
+#define GLPMCFG_L1DSEN_Msk (0x1UL << GLPMCFG_L1DSEN_Pos) // 0x00001000
+#define GLPMCFG_L1DSEN GLPMCFG_L1DSEN_Msk // L1 deep sleep enable
#define GLPMCFG_LPMRSP_Pos (13U)
-#define GLPMCFG_LPMRSP_Msk (0x3UL << GLPMCFG_LPMRSP_Pos) // 0x00006000 */
-#define GLPMCFG_LPMRSP GLPMCFG_LPMRSP_Msk // LPM response */
+#define GLPMCFG_LPMRSP_Msk (0x3UL << GLPMCFG_LPMRSP_Pos) // 0x00006000
+#define GLPMCFG_LPMRSP GLPMCFG_LPMRSP_Msk // LPM response
#define GLPMCFG_SLPSTS_Pos (15U)
-#define GLPMCFG_SLPSTS_Msk (0x1UL << GLPMCFG_SLPSTS_Pos) // 0x00008000 */
-#define GLPMCFG_SLPSTS GLPMCFG_SLPSTS_Msk // Port sleep status */
+#define GLPMCFG_SLPSTS_Msk (0x1UL << GLPMCFG_SLPSTS_Pos) // 0x00008000
+#define GLPMCFG_SLPSTS GLPMCFG_SLPSTS_Msk // Port sleep status
#define GLPMCFG_L1RSMOK_Pos (16U)
-#define GLPMCFG_L1RSMOK_Msk (0x1UL << GLPMCFG_L1RSMOK_Pos) // 0x00010000 */
-#define GLPMCFG_L1RSMOK GLPMCFG_L1RSMOK_Msk // Sleep State Resume OK */
+#define GLPMCFG_L1RSMOK_Msk (0x1UL << GLPMCFG_L1RSMOK_Pos) // 0x00010000
+#define GLPMCFG_L1RSMOK GLPMCFG_L1RSMOK_Msk // Sleep State Resume OK
#define GLPMCFG_LPMCHIDX_Pos (17U)
-#define GLPMCFG_LPMCHIDX_Msk (0xFUL << GLPMCFG_LPMCHIDX_Pos) // 0x001E0000 */
-#define GLPMCFG_LPMCHIDX GLPMCFG_LPMCHIDX_Msk // LPM Channel Index */
+#define GLPMCFG_LPMCHIDX_Msk (0xFUL << GLPMCFG_LPMCHIDX_Pos) // 0x001E0000
+#define GLPMCFG_LPMCHIDX GLPMCFG_LPMCHIDX_Msk // LPM Channel Index
#define GLPMCFG_LPMRCNT_Pos (21U)
-#define GLPMCFG_LPMRCNT_Msk (0x7UL << GLPMCFG_LPMRCNT_Pos) // 0x00E00000 */
-#define GLPMCFG_LPMRCNT GLPMCFG_LPMRCNT_Msk // LPM retry count */
+#define GLPMCFG_LPMRCNT_Msk (0x7UL << GLPMCFG_LPMRCNT_Pos) // 0x00E00000
+#define GLPMCFG_LPMRCNT GLPMCFG_LPMRCNT_Msk // LPM retry count
#define GLPMCFG_SNDLPM_Pos (24U)
-#define GLPMCFG_SNDLPM_Msk (0x1UL << GLPMCFG_SNDLPM_Pos) // 0x01000000 */
-#define GLPMCFG_SNDLPM GLPMCFG_SNDLPM_Msk // Send LPM transaction */
+#define GLPMCFG_SNDLPM_Msk (0x1UL << GLPMCFG_SNDLPM_Pos) // 0x01000000
+#define GLPMCFG_SNDLPM GLPMCFG_SNDLPM_Msk // Send LPM transaction
#define GLPMCFG_LPMRCNTSTS_Pos (25U)
-#define GLPMCFG_LPMRCNTSTS_Msk (0x7UL << GLPMCFG_LPMRCNTSTS_Pos) // 0x0E000000 */
-#define GLPMCFG_LPMRCNTSTS GLPMCFG_LPMRCNTSTS_Msk // LPM retry count status */
+#define GLPMCFG_LPMRCNTSTS_Msk (0x7UL << GLPMCFG_LPMRCNTSTS_Pos) // 0x0E000000
+#define GLPMCFG_LPMRCNTSTS GLPMCFG_LPMRCNTSTS_Msk // LPM retry count status
#define GLPMCFG_ENBESL_Pos (28U)
-#define GLPMCFG_ENBESL_Msk (0x1UL << GLPMCFG_ENBESL_Pos) // 0x10000000 */
-#define GLPMCFG_ENBESL GLPMCFG_ENBESL_Msk // Enable best effort service latency */
+#define GLPMCFG_ENBESL_Msk (0x1UL << GLPMCFG_ENBESL_Pos) // 0x10000000
+#define GLPMCFG_ENBESL GLPMCFG_ENBESL_Msk // Enable best effort service latency
+
+// GDFIFOCFG
+#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16)
+#define GDFIFOCFG_EPINFOBASE_SHIFT 16
+#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0)
+#define GDFIFOCFG_GDFIFOCFG_SHIFT 0
/******************** Bit definition for DIEPEACHMSK1 register ********************/
#define DIEPEACHMSK1_XFRCM_Pos (0U)
-#define DIEPEACHMSK1_XFRCM_Msk (0x1UL << DIEPEACHMSK1_XFRCM_Pos) // 0x00000001 */
-#define DIEPEACHMSK1_XFRCM DIEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask */
+#define DIEPEACHMSK1_XFRCM_Msk (0x1UL << DIEPEACHMSK1_XFRCM_Pos) // 0x00000001
+#define DIEPEACHMSK1_XFRCM DIEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask
#define DIEPEACHMSK1_EPDM_Pos (1U)
-#define DIEPEACHMSK1_EPDM_Msk (0x1UL << DIEPEACHMSK1_EPDM_Pos) // 0x00000002 */
-#define DIEPEACHMSK1_EPDM DIEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask */
+#define DIEPEACHMSK1_EPDM_Msk (0x1UL << DIEPEACHMSK1_EPDM_Pos) // 0x00000002
+#define DIEPEACHMSK1_EPDM DIEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask
#define DIEPEACHMSK1_TOM_Pos (3U)
-#define DIEPEACHMSK1_TOM_Msk (0x1UL << DIEPEACHMSK1_TOM_Pos) // 0x00000008 */
-#define DIEPEACHMSK1_TOM DIEPEACHMSK1_TOM_Msk // Timeout condition mask (nonisochronous endpoints) */
+#define DIEPEACHMSK1_TOM_Msk (0x1UL << DIEPEACHMSK1_TOM_Pos) // 0x00000008
+#define DIEPEACHMSK1_TOM DIEPEACHMSK1_TOM_Msk // Timeout condition mask (nonisochronous endpoints)
#define DIEPEACHMSK1_ITTXFEMSK_Pos (4U)
-#define DIEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DIEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 */
-#define DIEPEACHMSK1_ITTXFEMSK DIEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask */
+#define DIEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DIEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010
+#define DIEPEACHMSK1_ITTXFEMSK DIEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask
#define DIEPEACHMSK1_INEPNMM_Pos (5U)
-#define DIEPEACHMSK1_INEPNMM_Msk (0x1UL << DIEPEACHMSK1_INEPNMM_Pos) // 0x00000020 */
-#define DIEPEACHMSK1_INEPNMM DIEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask */
+#define DIEPEACHMSK1_INEPNMM_Msk (0x1UL << DIEPEACHMSK1_INEPNMM_Pos) // 0x00000020
+#define DIEPEACHMSK1_INEPNMM DIEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask
#define DIEPEACHMSK1_INEPNEM_Pos (6U)
-#define DIEPEACHMSK1_INEPNEM_Msk (0x1UL << DIEPEACHMSK1_INEPNEM_Pos) // 0x00000040 */
-#define DIEPEACHMSK1_INEPNEM DIEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask */
+#define DIEPEACHMSK1_INEPNEM_Msk (0x1UL << DIEPEACHMSK1_INEPNEM_Pos) // 0x00000040
+#define DIEPEACHMSK1_INEPNEM DIEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask
#define DIEPEACHMSK1_TXFURM_Pos (8U)
-#define DIEPEACHMSK1_TXFURM_Msk (0x1UL << DIEPEACHMSK1_TXFURM_Pos) // 0x00000100 */
-#define DIEPEACHMSK1_TXFURM DIEPEACHMSK1_TXFURM_Msk // FIFO underrun mask */
+#define DIEPEACHMSK1_TXFURM_Msk (0x1UL << DIEPEACHMSK1_TXFURM_Pos) // 0x00000100
+#define DIEPEACHMSK1_TXFURM DIEPEACHMSK1_TXFURM_Msk // FIFO underrun mask
#define DIEPEACHMSK1_BIM_Pos (9U)
-#define DIEPEACHMSK1_BIM_Msk (0x1UL << DIEPEACHMSK1_BIM_Pos) // 0x00000200 */
-#define DIEPEACHMSK1_BIM DIEPEACHMSK1_BIM_Msk // BNA interrupt mask */
+#define DIEPEACHMSK1_BIM_Msk (0x1UL << DIEPEACHMSK1_BIM_Pos) // 0x00000200
+#define DIEPEACHMSK1_BIM DIEPEACHMSK1_BIM_Msk // BNA interrupt mask
#define DIEPEACHMSK1_NAKM_Pos (13U)
-#define DIEPEACHMSK1_NAKM_Msk (0x1UL << DIEPEACHMSK1_NAKM_Pos) // 0x00002000 */
-#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask */
+#define DIEPEACHMSK1_NAKM_Msk (0x1UL << DIEPEACHMSK1_NAKM_Pos) // 0x00002000
+#define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask
/******************** Bit definition for HPRT register ********************/
-#define HPRT_PCSTS_Pos (0U)
-#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001 */
-#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status */
-#define HPRT_PCDET_Pos (1U)
-#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002 */
-#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected */
-#define HPRT_PENA_Pos (2U)
-#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004 */
-#define HPRT_PENA HPRT_PENA_Msk // Port enable */
-#define HPRT_PENCHNG_Pos (3U)
-#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008 */
-#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change */
-#define HPRT_POCA_Pos (4U)
-#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010 */
-#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active */
-#define HPRT_POCCHNG_Pos (5U)
-#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020 */
-#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change */
-#define HPRT_PRES_Pos (6U)
-#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040 */
-#define HPRT_PRES HPRT_PRES_Msk // Port resume */
-#define HPRT_PSUSP_Pos (7U)
-#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080 */
-#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend */
-#define HPRT_PRST_Pos (8U)
-#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100 */
-#define HPRT_PRST HPRT_PRST_Msk // Port reset */
-
-#define HPRT_PLSTS_Pos (10U)
-#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00 */
-#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status */
-#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400 */
-#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800 */
-#define HPRT_PPWR_Pos (12U)
-#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000 */
-#define HPRT_PPWR HPRT_PPWR_Msk // Port power */
-
-#define HPRT_PTCTL_Pos (13U)
-#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000 */
-#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control */
-#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000 */
-#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000 */
-#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000 */
-#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000 */
-
-#define HPRT_PSPD_Pos (17U)
-#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000 */
-#define HPRT_PSPD HPRT_PSPD_Msk // Port speed */
-#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000 */
-#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000 */
+#define HPRT_CONN_STATUS_Pos (0U)
+#define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001
+#define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status
+#define HPRT_CONN_DETECT_Pos (1U)
+#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002
+#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected
+#define HPRT_ENABLE_Pos (2U)
+#define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004
+#define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable
+#define HPRT_ENABLE_CHANGE_Pos (3U)
+#define HPRT_ENABLE_CHANGE_Msk (0x1UL << HPRT_ENABLE_CHANGE_Pos) // 0x00000008
+#define HPRT_ENABLE_CHANGE HPRT_ENABLE_CHANGE_Msk // Port enable/disable change
+#define HPRT_OVER_CURRENT_ACTIVE_Pos (4U)
+#define HPRT_OVER_CURRENT_ACTIVE_Msk (0x1UL << HPRT_OVER_CURRENT_ACTIVE_Pos) // 0x00000010
+#define HPRT_OVER_CURRENT_ACTIVE HPRT_OVER_CURRENT_ACTIVE_Msk // Port overcurrent active
+#define HPRT_OVER_CURRENT_CHANGE_Pos (5U)
+#define HPRT_OVER_CURRENT_CHANGE_Msk (0x1UL << HPRT_OVER_CURRENT_CHANGE_Pos) // 0x00000020
+#define HPRT_OVER_CURRENT_CHANGE HPRT_OVER_CURRENT_CHANGE_Msk // Port overcurrent change
+#define HPRT_RESUME_Pos (6U)
+#define HPRT_RESUME_Msk (0x1UL << HPRT_RESUME_Pos) // 0x00000040
+#define HPRT_RESUME HPRT_RESUME_Msk // Port resume
+#define HPRT_SUSPEND_Pos (7U)
+#define HPRT_SUSPEND_Msk (0x1UL << HPRT_SUSPEND_Pos) // 0x00000080
+#define HPRT_SUSPEND HPRT_SUSPEND_Msk // Port suspend
+#define HPRT_RESET_Pos (8U)
+#define HPRT_RESET_Msk (0x1UL << HPRT_RESET_Pos) // 0x00000100
+#define HPRT_RESET HPRT_RESET_Msk // Port reset
+#define HPRT_LINE_STATUS_Pos (10U)
+#define HPRT_LINE_STATUS_Msk (0x3UL << HPRT_LINE_STATUS_Pos) // 0x00000C00
+#define HPRT_LINE_STATUS HPRT_LINE_STATUS_Msk // Port line status
+#define HPRT_LINE_STATUS_0 (0x1UL << HPRT_LINE_STATUS_Pos) // 0x00000400
+#define HPRT_LINE_STATUS_1 (0x2UL << HPRT_LINE_STATUS_Pos) // 0x00000800
+#define HPRT_POWER_Pos (12U)
+#define HPRT_POWER_Msk (0x1UL << HPRT_POWER_Pos) // 0x00001000
+#define HPRT_POWER HPRT_POWER_Msk // Port power
+#define HPRT_TEST_CONTROL_Pos (13U)
+#define HPRT_TEST_CONTROL_Msk (0xFUL << HPRT_TEST_CONTROL_Pos) // 0x0001E000
+#define HPRT_TEST_CONTROL HPRT_TEST_CONTROL_Msk // Port test control
+#define HPRT_TEST_CONTROL_0 (0x1UL << HPRT_TEST_CONTROL_Pos) // 0x00002000
+#define HPRT_TEST_CONTROL_1 (0x2UL << HPRT_TEST_CONTROL_Pos) // 0x00004000
+#define HPRT_TEST_CONTROL_2 (0x4UL << HPRT_TEST_CONTROL_Pos) // 0x00008000
+#define HPRT_TEST_CONTROL_3 (0x8UL << HPRT_TEST_CONTROL_Pos) // 0x00010000
+#define HPRT_SPEED_Pos (17U)
+#define HPRT_SPEED_Msk (0x3UL << HPRT_SPEED_Pos) // 0x00060000
+#define HPRT_SPEED HPRT_SPEED_Msk // Port speed
+#define HPRT_SPEED_0 (0x1UL << HPRT_SPEED_Pos) // 0x00020000
+#define HPRT_SPEED_1 (0x2UL << HPRT_SPEED_Pos) // 0x00040000
/******************** Bit definition for DOEPEACHMSK1 register ********************/
#define DOEPEACHMSK1_XFRCM_Pos (0U)
-#define DOEPEACHMSK1_XFRCM_Msk (0x1UL << DOEPEACHMSK1_XFRCM_Pos) // 0x00000001 */
-#define DOEPEACHMSK1_XFRCM DOEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask */
+#define DOEPEACHMSK1_XFRCM_Msk (0x1UL << DOEPEACHMSK1_XFRCM_Pos) // 0x00000001
+#define DOEPEACHMSK1_XFRCM DOEPEACHMSK1_XFRCM_Msk // Transfer completed interrupt mask
#define DOEPEACHMSK1_EPDM_Pos (1U)
-#define DOEPEACHMSK1_EPDM_Msk (0x1UL << DOEPEACHMSK1_EPDM_Pos) // 0x00000002 */
-#define DOEPEACHMSK1_EPDM DOEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask */
+#define DOEPEACHMSK1_EPDM_Msk (0x1UL << DOEPEACHMSK1_EPDM_Pos) // 0x00000002
+#define DOEPEACHMSK1_EPDM DOEPEACHMSK1_EPDM_Msk // Endpoint disabled interrupt mask
#define DOEPEACHMSK1_TOM_Pos (3U)
-#define DOEPEACHMSK1_TOM_Msk (0x1UL << DOEPEACHMSK1_TOM_Pos) // 0x00000008 */
-#define DOEPEACHMSK1_TOM DOEPEACHMSK1_TOM_Msk // Timeout condition mask */
+#define DOEPEACHMSK1_TOM_Msk (0x1UL << DOEPEACHMSK1_TOM_Pos) // 0x00000008
+#define DOEPEACHMSK1_TOM DOEPEACHMSK1_TOM_Msk // Timeout condition mask
#define DOEPEACHMSK1_ITTXFEMSK_Pos (4U)
-#define DOEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DOEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010 */
-#define DOEPEACHMSK1_ITTXFEMSK DOEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask */
+#define DOEPEACHMSK1_ITTXFEMSK_Msk (0x1UL << DOEPEACHMSK1_ITTXFEMSK_Pos) // 0x00000010
+#define DOEPEACHMSK1_ITTXFEMSK DOEPEACHMSK1_ITTXFEMSK_Msk // IN token received when TxFIFO empty mask
#define DOEPEACHMSK1_INEPNMM_Pos (5U)
-#define DOEPEACHMSK1_INEPNMM_Msk (0x1UL << DOEPEACHMSK1_INEPNMM_Pos) // 0x00000020 */
-#define DOEPEACHMSK1_INEPNMM DOEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask */
+#define DOEPEACHMSK1_INEPNMM_Msk (0x1UL << DOEPEACHMSK1_INEPNMM_Pos) // 0x00000020
+#define DOEPEACHMSK1_INEPNMM DOEPEACHMSK1_INEPNMM_Msk // IN token received with EP mismatch mask
#define DOEPEACHMSK1_INEPNEM_Pos (6U)
-#define DOEPEACHMSK1_INEPNEM_Msk (0x1UL << DOEPEACHMSK1_INEPNEM_Pos) // 0x00000040 */
-#define DOEPEACHMSK1_INEPNEM DOEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask */
+#define DOEPEACHMSK1_INEPNEM_Msk (0x1UL << DOEPEACHMSK1_INEPNEM_Pos) // 0x00000040
+#define DOEPEACHMSK1_INEPNEM DOEPEACHMSK1_INEPNEM_Msk // IN endpoint NAK effective mask
#define DOEPEACHMSK1_TXFURM_Pos (8U)
-#define DOEPEACHMSK1_TXFURM_Msk (0x1UL << DOEPEACHMSK1_TXFURM_Pos) // 0x00000100 */
-#define DOEPEACHMSK1_TXFURM DOEPEACHMSK1_TXFURM_Msk // OUT packet error mask */
+#define DOEPEACHMSK1_TXFURM_Msk (0x1UL << DOEPEACHMSK1_TXFURM_Pos) // 0x00000100
+#define DOEPEACHMSK1_TXFURM DOEPEACHMSK1_TXFURM_Msk // OUT packet error mask
#define DOEPEACHMSK1_BIM_Pos (9U)
-#define DOEPEACHMSK1_BIM_Msk (0x1UL << DOEPEACHMSK1_BIM_Pos) // 0x00000200 */
-#define DOEPEACHMSK1_BIM DOEPEACHMSK1_BIM_Msk // BNA interrupt mask */
+#define DOEPEACHMSK1_BIM_Msk (0x1UL << DOEPEACHMSK1_BIM_Pos) // 0x00000200
+#define DOEPEACHMSK1_BIM DOEPEACHMSK1_BIM_Msk // BNA interrupt mask
#define DOEPEACHMSK1_BERRM_Pos (12U)
-#define DOEPEACHMSK1_BERRM_Msk (0x1UL << DOEPEACHMSK1_BERRM_Pos) // 0x00001000 */
-#define DOEPEACHMSK1_BERRM DOEPEACHMSK1_BERRM_Msk // Bubble error interrupt mask */
+#define DOEPEACHMSK1_BERRM_Msk (0x1UL << DOEPEACHMSK1_BERRM_Pos) // 0x00001000
+#define DOEPEACHMSK1_BERRM DOEPEACHMSK1_BERRM_Msk // Bubble error interrupt mask
#define DOEPEACHMSK1_NAKM_Pos (13U)
-#define DOEPEACHMSK1_NAKM_Msk (0x1UL << DOEPEACHMSK1_NAKM_Pos) // 0x00002000 */
-#define DOEPEACHMSK1_NAKM DOEPEACHMSK1_NAKM_Msk // NAK interrupt mask */
+#define DOEPEACHMSK1_NAKM_Msk (0x1UL << DOEPEACHMSK1_NAKM_Pos) // 0x00002000
+#define DOEPEACHMSK1_NAKM DOEPEACHMSK1_NAKM_Msk // NAK interrupt mask
#define DOEPEACHMSK1_NYETM_Pos (14U)
-#define DOEPEACHMSK1_NYETM_Msk (0x1UL << DOEPEACHMSK1_NYETM_Pos) // 0x00004000 */
-#define DOEPEACHMSK1_NYETM DOEPEACHMSK1_NYETM_Msk // NYET interrupt mask */
+#define DOEPEACHMSK1_NYETM_Msk (0x1UL << DOEPEACHMSK1_NYETM_Pos) // 0x00004000
+#define DOEPEACHMSK1_NYETM DOEPEACHMSK1_NYETM_Msk // NYET interrupt mask
/******************** Bit definition for HPTXFSIZ register ********************/
#define HPTXFSIZ_PTXSA_Pos (0U)
-#define HPTXFSIZ_PTXSA_Msk (0xFFFFUL << HPTXFSIZ_PTXSA_Pos) // 0x0000FFFF */
-#define HPTXFSIZ_PTXSA HPTXFSIZ_PTXSA_Msk // Host periodic TxFIFO start address */
+#define HPTXFSIZ_PTXSA_Msk (0xFFFFUL << HPTXFSIZ_PTXSA_Pos) // 0x0000FFFF
+#define HPTXFSIZ_PTXSA HPTXFSIZ_PTXSA_Msk // Host periodic TxFIFO start address
#define HPTXFSIZ_PTXFD_Pos (16U)
-#define HPTXFSIZ_PTXFD_Msk (0xFFFFUL << HPTXFSIZ_PTXFD_Pos) // 0xFFFF0000 */
-#define HPTXFSIZ_PTXFD HPTXFSIZ_PTXFD_Msk // Host periodic TxFIFO depth */
+#define HPTXFSIZ_PTXFD_Msk (0xFFFFUL << HPTXFSIZ_PTXFD_Pos) // 0xFFFF0000
+#define HPTXFSIZ_PTXFD HPTXFSIZ_PTXFD_Msk // Host periodic TxFIFO depth
/******************** Bit definition for DIEPCTL register ********************/
#define DIEPCTL_MPSIZ_Pos (0U)
-#define DIEPCTL_MPSIZ_Msk (0x7FFUL << DIEPCTL_MPSIZ_Pos) // 0x000007FF */
-#define DIEPCTL_MPSIZ DIEPCTL_MPSIZ_Msk // Maximum packet size */
+#define DIEPCTL_MPSIZ_Msk (0x7FFUL << DIEPCTL_MPSIZ_Pos) // 0x000007FF
+#define DIEPCTL_MPSIZ DIEPCTL_MPSIZ_Msk // Maximum packet size
#define DIEPCTL_USBAEP_Pos (15U)
-#define DIEPCTL_USBAEP_Msk (0x1UL << DIEPCTL_USBAEP_Pos) // 0x00008000 */
-#define DIEPCTL_USBAEP DIEPCTL_USBAEP_Msk // USB active endpoint */
+#define DIEPCTL_USBAEP_Msk (0x1UL << DIEPCTL_USBAEP_Pos) // 0x00008000
+#define DIEPCTL_USBAEP DIEPCTL_USBAEP_Msk // USB active endpoint
#define DIEPCTL_EONUM_DPID_Pos (16U)
-#define DIEPCTL_EONUM_DPID_Msk (0x1UL << DIEPCTL_EONUM_DPID_Pos) // 0x00010000 */
-#define DIEPCTL_EONUM_DPID DIEPCTL_EONUM_DPID_Msk // Even/odd frame */
+#define DIEPCTL_EONUM_DPID_Msk (0x1UL << DIEPCTL_EONUM_DPID_Pos) // 0x00010000
+#define DIEPCTL_EONUM_DPID DIEPCTL_EONUM_DPID_Msk // Even/odd frame
#define DIEPCTL_NAKSTS_Pos (17U)
-#define DIEPCTL_NAKSTS_Msk (0x1UL << DIEPCTL_NAKSTS_Pos) // 0x00020000 */
-#define DIEPCTL_NAKSTS DIEPCTL_NAKSTS_Msk // NAK status */
+#define DIEPCTL_NAKSTS_Msk (0x1UL << DIEPCTL_NAKSTS_Pos) // 0x00020000
+#define DIEPCTL_NAKSTS DIEPCTL_NAKSTS_Msk // NAK status
#define DIEPCTL_EPTYP_Pos (18U)
-#define DIEPCTL_EPTYP_Msk (0x3UL << DIEPCTL_EPTYP_Pos) // 0x000C0000 */
-#define DIEPCTL_EPTYP DIEPCTL_EPTYP_Msk // Endpoint type */
-#define DIEPCTL_EPTYP_0 (0x1UL << DIEPCTL_EPTYP_Pos) // 0x00040000 */
-#define DIEPCTL_EPTYP_1 (0x2UL << DIEPCTL_EPTYP_Pos) // 0x00080000 */
+#define DIEPCTL_EPTYP_Msk (0x3UL << DIEPCTL_EPTYP_Pos) // 0x000C0000
+#define DIEPCTL_EPTYP DIEPCTL_EPTYP_Msk // Endpoint type
+#define DIEPCTL_EPTYP_0 (0x1UL << DIEPCTL_EPTYP_Pos) // 0x00040000
+#define DIEPCTL_EPTYP_1 (0x2UL << DIEPCTL_EPTYP_Pos) // 0x00080000
#define DIEPCTL_STALL_Pos (21U)
-#define DIEPCTL_STALL_Msk (0x1UL << DIEPCTL_STALL_Pos) // 0x00200000 */
-#define DIEPCTL_STALL DIEPCTL_STALL_Msk // STALL handshake */
+#define DIEPCTL_STALL_Msk (0x1UL << DIEPCTL_STALL_Pos) // 0x00200000
+#define DIEPCTL_STALL DIEPCTL_STALL_Msk // STALL handshake
#define DIEPCTL_TXFNUM_Pos (22U)
-#define DIEPCTL_TXFNUM_Msk (0xFUL << DIEPCTL_TXFNUM_Pos) // 0x03C00000 */
-#define DIEPCTL_TXFNUM DIEPCTL_TXFNUM_Msk // TxFIFO number */
-#define DIEPCTL_TXFNUM_0 (0x1UL << DIEPCTL_TXFNUM_Pos) // 0x00400000 */
-#define DIEPCTL_TXFNUM_1 (0x2UL << DIEPCTL_TXFNUM_Pos) // 0x00800000 */
-#define DIEPCTL_TXFNUM_2 (0x4UL << DIEPCTL_TXFNUM_Pos) // 0x01000000 */
-#define DIEPCTL_TXFNUM_3 (0x8UL << DIEPCTL_TXFNUM_Pos) // 0x02000000 */
+#define DIEPCTL_TXFNUM_Msk (0xFUL << DIEPCTL_TXFNUM_Pos) // 0x03C00000
+#define DIEPCTL_TXFNUM DIEPCTL_TXFNUM_Msk // TxFIFO number
+#define DIEPCTL_TXFNUM_0 (0x1UL << DIEPCTL_TXFNUM_Pos) // 0x00400000
+#define DIEPCTL_TXFNUM_1 (0x2UL << DIEPCTL_TXFNUM_Pos) // 0x00800000
+#define DIEPCTL_TXFNUM_2 (0x4UL << DIEPCTL_TXFNUM_Pos) // 0x01000000
+#define DIEPCTL_TXFNUM_3 (0x8UL << DIEPCTL_TXFNUM_Pos) // 0x02000000
#define DIEPCTL_CNAK_Pos (26U)
-#define DIEPCTL_CNAK_Msk (0x1UL << DIEPCTL_CNAK_Pos) // 0x04000000 */
-#define DIEPCTL_CNAK DIEPCTL_CNAK_Msk // Clear NAK */
+#define DIEPCTL_CNAK_Msk (0x1UL << DIEPCTL_CNAK_Pos) // 0x04000000
+#define DIEPCTL_CNAK DIEPCTL_CNAK_Msk // Clear NAK
#define DIEPCTL_SNAK_Pos (27U)
-#define DIEPCTL_SNAK_Msk (0x1UL << DIEPCTL_SNAK_Pos) // 0x08000000 */
-#define DIEPCTL_SNAK DIEPCTL_SNAK_Msk // Set NAK */
+#define DIEPCTL_SNAK_Msk (0x1UL << DIEPCTL_SNAK_Pos) // 0x08000000
+#define DIEPCTL_SNAK DIEPCTL_SNAK_Msk // Set NAK
#define DIEPCTL_SD0PID_SEVNFRM_Pos (28U)
-#define DIEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DIEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 */
-#define DIEPCTL_SD0PID_SEVNFRM DIEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID */
+#define DIEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DIEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000
+#define DIEPCTL_SD0PID_SEVNFRM DIEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID
#define DIEPCTL_SODDFRM_Pos (29U)
-#define DIEPCTL_SODDFRM_Msk (0x1UL << DIEPCTL_SODDFRM_Pos) // 0x20000000 */
-#define DIEPCTL_SODDFRM DIEPCTL_SODDFRM_Msk // Set odd frame */
+#define DIEPCTL_SODDFRM_Msk (0x1UL << DIEPCTL_SODDFRM_Pos) // 0x20000000
+#define DIEPCTL_SODDFRM DIEPCTL_SODDFRM_Msk // Set odd frame
#define DIEPCTL_EPDIS_Pos (30U)
-#define DIEPCTL_EPDIS_Msk (0x1UL << DIEPCTL_EPDIS_Pos) // 0x40000000 */
-#define DIEPCTL_EPDIS DIEPCTL_EPDIS_Msk // Endpoint disable */
+#define DIEPCTL_EPDIS_Msk (0x1UL << DIEPCTL_EPDIS_Pos) // 0x40000000
+#define DIEPCTL_EPDIS DIEPCTL_EPDIS_Msk // Endpoint disable
#define DIEPCTL_EPENA_Pos (31U)
-#define DIEPCTL_EPENA_Msk (0x1UL << DIEPCTL_EPENA_Pos) // 0x80000000 */
-#define DIEPCTL_EPENA DIEPCTL_EPENA_Msk // Endpoint enable */
+#define DIEPCTL_EPENA_Msk (0x1UL << DIEPCTL_EPENA_Pos) // 0x80000000
+#define DIEPCTL_EPENA DIEPCTL_EPENA_Msk // Endpoint enable
/******************** Bit definition for HCCHAR register ********************/
#define HCCHAR_MPSIZ_Pos (0U)
-#define HCCHAR_MPSIZ_Msk (0x7FFUL << HCCHAR_MPSIZ_Pos) // 0x000007FF */
-#define HCCHAR_MPSIZ HCCHAR_MPSIZ_Msk // Maximum packet size */
+#define HCCHAR_MPSIZ_Msk (0x7FFUL << HCCHAR_MPSIZ_Pos) // 0x000007FF
+#define HCCHAR_MPSIZ HCCHAR_MPSIZ_Msk // Maximum packet size
#define HCCHAR_EPNUM_Pos (11U)
-#define HCCHAR_EPNUM_Msk (0xFUL << HCCHAR_EPNUM_Pos) // 0x00007800 */
-#define HCCHAR_EPNUM HCCHAR_EPNUM_Msk // Endpoint number */
-#define HCCHAR_EPNUM_0 (0x1UL << HCCHAR_EPNUM_Pos) // 0x00000800 */
-#define HCCHAR_EPNUM_1 (0x2UL << HCCHAR_EPNUM_Pos) // 0x00001000 */
-#define HCCHAR_EPNUM_2 (0x4UL << HCCHAR_EPNUM_Pos) // 0x00002000 */
-#define HCCHAR_EPNUM_3 (0x8UL << HCCHAR_EPNUM_Pos) // 0x00004000 */
+#define HCCHAR_EPNUM_Msk (0xFUL << HCCHAR_EPNUM_Pos) // 0x00007800
+#define HCCHAR_EPNUM HCCHAR_EPNUM_Msk // Endpoint number
+#define HCCHAR_EPNUM_0 (0x1UL << HCCHAR_EPNUM_Pos) // 0x00000800
+#define HCCHAR_EPNUM_1 (0x2UL << HCCHAR_EPNUM_Pos) // 0x00001000
+#define HCCHAR_EPNUM_2 (0x4UL << HCCHAR_EPNUM_Pos) // 0x00002000
+#define HCCHAR_EPNUM_3 (0x8UL << HCCHAR_EPNUM_Pos) // 0x00004000
#define HCCHAR_EPDIR_Pos (15U)
-#define HCCHAR_EPDIR_Msk (0x1UL << HCCHAR_EPDIR_Pos) // 0x00008000 */
-#define HCCHAR_EPDIR HCCHAR_EPDIR_Msk // Endpoint direction */
+#define HCCHAR_EPDIR_Msk (0x1UL << HCCHAR_EPDIR_Pos) // 0x00008000
+#define HCCHAR_EPDIR HCCHAR_EPDIR_Msk // Endpoint direction
#define HCCHAR_LSDEV_Pos (17U)
-#define HCCHAR_LSDEV_Msk (0x1UL << HCCHAR_LSDEV_Pos) // 0x00020000 */
-#define HCCHAR_LSDEV HCCHAR_LSDEV_Msk // Low-speed device */
+#define HCCHAR_LSDEV_Msk (0x1UL << HCCHAR_LSDEV_Pos) // 0x00020000
+#define HCCHAR_LSDEV HCCHAR_LSDEV_Msk // Low-speed device
#define HCCHAR_EPTYP_Pos (18U)
-#define HCCHAR_EPTYP_Msk (0x3UL << HCCHAR_EPTYP_Pos) // 0x000C0000 */
-#define HCCHAR_EPTYP HCCHAR_EPTYP_Msk // Endpoint type */
-#define HCCHAR_EPTYP_0 (0x1UL << HCCHAR_EPTYP_Pos) // 0x00040000 */
-#define HCCHAR_EPTYP_1 (0x2UL << HCCHAR_EPTYP_Pos) // 0x00080000 */
+#define HCCHAR_EPTYP_Msk (0x3UL << HCCHAR_EPTYP_Pos) // 0x000C0000
+#define HCCHAR_EPTYP HCCHAR_EPTYP_Msk // Endpoint type
+#define HCCHAR_EPTYP_0 (0x1UL << HCCHAR_EPTYP_Pos) // 0x00040000
+#define HCCHAR_EPTYP_1 (0x2UL << HCCHAR_EPTYP_Pos) // 0x00080000
#define HCCHAR_MC_Pos (20U)
-#define HCCHAR_MC_Msk (0x3UL << HCCHAR_MC_Pos) // 0x00300000 */
-#define HCCHAR_MC HCCHAR_MC_Msk // Multi Count (MC) / Error Count (EC) */
-#define HCCHAR_MC_0 (0x1UL << HCCHAR_MC_Pos) // 0x00100000 */
-#define HCCHAR_MC_1 (0x2UL << HCCHAR_MC_Pos) // 0x00200000 */
+#define HCCHAR_MC_Msk (0x3UL << HCCHAR_MC_Pos) // 0x00300000
+#define HCCHAR_MC HCCHAR_MC_Msk // Multi Count (MC) / Error Count (EC)
+#define HCCHAR_MC_0 (0x1UL << HCCHAR_MC_Pos) // 0x00100000
+#define HCCHAR_MC_1 (0x2UL << HCCHAR_MC_Pos) // 0x00200000
#define HCCHAR_DAD_Pos (22U)
-#define HCCHAR_DAD_Msk (0x7FUL << HCCHAR_DAD_Pos) // 0x1FC00000 */
-#define HCCHAR_DAD HCCHAR_DAD_Msk // Device address */
-#define HCCHAR_DAD_0 (0x01UL << HCCHAR_DAD_Pos) // 0x00400000 */
-#define HCCHAR_DAD_1 (0x02UL << HCCHAR_DAD_Pos) // 0x00800000 */
-#define HCCHAR_DAD_2 (0x04UL << HCCHAR_DAD_Pos) // 0x01000000 */
-#define HCCHAR_DAD_3 (0x08UL << HCCHAR_DAD_Pos) // 0x02000000 */
-#define HCCHAR_DAD_4 (0x10UL << HCCHAR_DAD_Pos) // 0x04000000 */
-#define HCCHAR_DAD_5 (0x20UL << HCCHAR_DAD_Pos) // 0x08000000 */
-#define HCCHAR_DAD_6 (0x40UL << HCCHAR_DAD_Pos) // 0x10000000 */
+#define HCCHAR_DAD_Msk (0x7FUL << HCCHAR_DAD_Pos) // 0x1FC00000
+#define HCCHAR_DAD HCCHAR_DAD_Msk // Device address
+#define HCCHAR_DAD_0 (0x01UL << HCCHAR_DAD_Pos) // 0x00400000
+#define HCCHAR_DAD_1 (0x02UL << HCCHAR_DAD_Pos) // 0x00800000
+#define HCCHAR_DAD_2 (0x04UL << HCCHAR_DAD_Pos) // 0x01000000
+#define HCCHAR_DAD_3 (0x08UL << HCCHAR_DAD_Pos) // 0x02000000
+#define HCCHAR_DAD_4 (0x10UL << HCCHAR_DAD_Pos) // 0x04000000
+#define HCCHAR_DAD_5 (0x20UL << HCCHAR_DAD_Pos) // 0x08000000
+#define HCCHAR_DAD_6 (0x40UL << HCCHAR_DAD_Pos) // 0x10000000
#define HCCHAR_ODDFRM_Pos (29U)
-#define HCCHAR_ODDFRM_Msk (0x1UL << HCCHAR_ODDFRM_Pos) // 0x20000000 */
-#define HCCHAR_ODDFRM HCCHAR_ODDFRM_Msk // Odd frame */
+#define HCCHAR_ODDFRM_Msk (0x1UL << HCCHAR_ODDFRM_Pos) // 0x20000000
+#define HCCHAR_ODDFRM HCCHAR_ODDFRM_Msk // Odd frame
#define HCCHAR_CHDIS_Pos (30U)
-#define HCCHAR_CHDIS_Msk (0x1UL << HCCHAR_CHDIS_Pos) // 0x40000000 */
-#define HCCHAR_CHDIS HCCHAR_CHDIS_Msk // Channel disable */
+#define HCCHAR_CHDIS_Msk (0x1UL << HCCHAR_CHDIS_Pos) // 0x40000000
+#define HCCHAR_CHDIS HCCHAR_CHDIS_Msk // Channel disable
#define HCCHAR_CHENA_Pos (31U)
-#define HCCHAR_CHENA_Msk (0x1UL << HCCHAR_CHENA_Pos) // 0x80000000 */
-#define HCCHAR_CHENA HCCHAR_CHENA_Msk // Channel enable */
+#define HCCHAR_CHENA_Msk (0x1UL << HCCHAR_CHENA_Pos) // 0x80000000
+#define HCCHAR_CHENA HCCHAR_CHENA_Msk // Channel enable
/******************** Bit definition for HCSPLT register ********************/
#define HCSPLT_PRTADDR_Pos (0U)
-#define HCSPLT_PRTADDR_Msk (0x7FUL << HCSPLT_PRTADDR_Pos) // 0x0000007F */
-#define HCSPLT_PRTADDR HCSPLT_PRTADDR_Msk // Port address */
-#define HCSPLT_PRTADDR_0 (0x01UL << HCSPLT_PRTADDR_Pos) // 0x00000001 */
-#define HCSPLT_PRTADDR_1 (0x02UL << HCSPLT_PRTADDR_Pos) // 0x00000002 */
-#define HCSPLT_PRTADDR_2 (0x04UL << HCSPLT_PRTADDR_Pos) // 0x00000004 */
-#define HCSPLT_PRTADDR_3 (0x08UL << HCSPLT_PRTADDR_Pos) // 0x00000008 */
-#define HCSPLT_PRTADDR_4 (0x10UL << HCSPLT_PRTADDR_Pos) // 0x00000010 */
-#define HCSPLT_PRTADDR_5 (0x20UL << HCSPLT_PRTADDR_Pos) // 0x00000020 */
-#define HCSPLT_PRTADDR_6 (0x40UL << HCSPLT_PRTADDR_Pos) // 0x00000040 */
+#define HCSPLT_PRTADDR_Msk (0x7FUL << HCSPLT_PRTADDR_Pos) // 0x0000007F
+#define HCSPLT_PRTADDR HCSPLT_PRTADDR_Msk // Port address
+#define HCSPLT_PRTADDR_0 (0x01UL << HCSPLT_PRTADDR_Pos) // 0x00000001
+#define HCSPLT_PRTADDR_1 (0x02UL << HCSPLT_PRTADDR_Pos) // 0x00000002
+#define HCSPLT_PRTADDR_2 (0x04UL << HCSPLT_PRTADDR_Pos) // 0x00000004
+#define HCSPLT_PRTADDR_3 (0x08UL << HCSPLT_PRTADDR_Pos) // 0x00000008
+#define HCSPLT_PRTADDR_4 (0x10UL << HCSPLT_PRTADDR_Pos) // 0x00000010
+#define HCSPLT_PRTADDR_5 (0x20UL << HCSPLT_PRTADDR_Pos) // 0x00000020
+#define HCSPLT_PRTADDR_6 (0x40UL << HCSPLT_PRTADDR_Pos) // 0x00000040
#define HCSPLT_HUBADDR_Pos (7U)
-#define HCSPLT_HUBADDR_Msk (0x7FUL << HCSPLT_HUBADDR_Pos) // 0x00003F80 */
-#define HCSPLT_HUBADDR HCSPLT_HUBADDR_Msk // Hub address */
-#define HCSPLT_HUBADDR_0 (0x01UL << HCSPLT_HUBADDR_Pos) // 0x00000080 */
-#define HCSPLT_HUBADDR_1 (0x02UL << HCSPLT_HUBADDR_Pos) // 0x00000100 */
-#define HCSPLT_HUBADDR_2 (0x04UL << HCSPLT_HUBADDR_Pos) // 0x00000200 */
-#define HCSPLT_HUBADDR_3 (0x08UL << HCSPLT_HUBADDR_Pos) // 0x00000400 */
-#define HCSPLT_HUBADDR_4 (0x10UL << HCSPLT_HUBADDR_Pos) // 0x00000800 */
-#define HCSPLT_HUBADDR_5 (0x20UL << HCSPLT_HUBADDR_Pos) // 0x00001000 */
-#define HCSPLT_HUBADDR_6 (0x40UL << HCSPLT_HUBADDR_Pos) // 0x00002000 */
+#define HCSPLT_HUBADDR_Msk (0x7FUL << HCSPLT_HUBADDR_Pos) // 0x00003F80
+#define HCSPLT_HUBADDR HCSPLT_HUBADDR_Msk // Hub address
+#define HCSPLT_HUBADDR_0 (0x01UL << HCSPLT_HUBADDR_Pos) // 0x00000080
+#define HCSPLT_HUBADDR_1 (0x02UL << HCSPLT_HUBADDR_Pos) // 0x00000100
+#define HCSPLT_HUBADDR_2 (0x04UL << HCSPLT_HUBADDR_Pos) // 0x00000200
+#define HCSPLT_HUBADDR_3 (0x08UL << HCSPLT_HUBADDR_Pos) // 0x00000400
+#define HCSPLT_HUBADDR_4 (0x10UL << HCSPLT_HUBADDR_Pos) // 0x00000800
+#define HCSPLT_HUBADDR_5 (0x20UL << HCSPLT_HUBADDR_Pos) // 0x00001000
+#define HCSPLT_HUBADDR_6 (0x40UL << HCSPLT_HUBADDR_Pos) // 0x00002000
#define HCSPLT_XACTPOS_Pos (14U)
-#define HCSPLT_XACTPOS_Msk (0x3UL << HCSPLT_XACTPOS_Pos) // 0x0000C000 */
-#define HCSPLT_XACTPOS HCSPLT_XACTPOS_Msk // XACTPOS */
-#define HCSPLT_XACTPOS_0 (0x1UL << HCSPLT_XACTPOS_Pos) // 0x00004000 */
-#define HCSPLT_XACTPOS_1 (0x2UL << HCSPLT_XACTPOS_Pos) // 0x00008000 */
+#define HCSPLT_XACTPOS_Msk (0x3UL << HCSPLT_XACTPOS_Pos) // 0x0000C000
+#define HCSPLT_XACTPOS HCSPLT_XACTPOS_Msk // XACTPOS
+#define HCSPLT_XACTPOS_0 (0x1UL << HCSPLT_XACTPOS_Pos) // 0x00004000
+#define HCSPLT_XACTPOS_1 (0x2UL << HCSPLT_XACTPOS_Pos) // 0x00008000
#define HCSPLT_COMPLSPLT_Pos (16U)
-#define HCSPLT_COMPLSPLT_Msk (0x1UL << HCSPLT_COMPLSPLT_Pos) // 0x00010000 */
-#define HCSPLT_COMPLSPLT HCSPLT_COMPLSPLT_Msk // Do complete split */
+#define HCSPLT_COMPLSPLT_Msk (0x1UL << HCSPLT_COMPLSPLT_Pos) // 0x00010000
+#define HCSPLT_COMPLSPLT HCSPLT_COMPLSPLT_Msk // Do complete split
#define HCSPLT_SPLITEN_Pos (31U)
-#define HCSPLT_SPLITEN_Msk (0x1UL << HCSPLT_SPLITEN_Pos) // 0x80000000 */
-#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable */
+#define HCSPLT_SPLITEN_Msk (0x1UL << HCSPLT_SPLITEN_Pos) // 0x80000000
+#define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable
/******************** Bit definition for HCINT register ********************/
-#define HCINT_XFRC_Pos (0U)
-#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001 */
-#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed */
-#define HCINT_CHH_Pos (1U)
-#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002 */
-#define HCINT_CHH HCINT_CHH_Msk // Channel halted */
-#define HCINT_AHBERR_Pos (2U)
-#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004 */
-#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error */
+#define HCINT_XFER_COMPLETE_Pos (0U)
+#define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001
+#define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed
+#define HCINT_HALTED_Pos (1U)
+#define HCINT_HALTED_Msk (0x1UL << HCINT_HALTED_Pos) // 0x00000002
+#define HCINT_HALTED HCINT_HALTED_Msk // Channel halted
+#define HCINT_AHB_ERR_Pos (2U)
+#define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004
+#define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error
#define HCINT_STALL_Pos (3U)
-#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008 */
-#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt */
+#define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008
+#define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt
#define HCINT_NAK_Pos (4U)
-#define HCINT_NAK_Msk (0x1UL << HCINT_NAK_Pos) // 0x00000010 */
-#define HCINT_NAK HCINT_NAK_Msk // NAK response received interrupt */
+#define HCINT_NAK_Msk (0x1UL << HCINT_NAK_Pos) // 0x00000010
+#define HCINT_NAK HCINT_NAK_Msk // NAK response received interrupt
#define HCINT_ACK_Pos (5U)
-#define HCINT_ACK_Msk (0x1UL << HCINT_ACK_Pos) // 0x00000020 */
-#define HCINT_ACK HCINT_ACK_Msk // ACK response received/transmitted interrupt */
+#define HCINT_ACK_Msk (0x1UL << HCINT_ACK_Pos) // 0x00000020
+#define HCINT_ACK HCINT_ACK_Msk // ACK response received/transmitted interrupt
#define HCINT_NYET_Pos (6U)
-#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040 */
-#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt */
-#define HCINT_TXERR_Pos (7U)
-#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080 */
-#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error */
-#define HCINT_BBERR_Pos (8U)
-#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100 */
-#define HCINT_BBERR HCINT_BBERR_Msk // Babble error */
-#define HCINT_FRMOR_Pos (9U)
-#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200 */
-#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun */
-#define HCINT_DTERR_Pos (10U)
-#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400 */
-#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error */
+#define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040
+#define HCINT_NYET HCINT_NYET_Msk // Response received interrupt
+#define HCINT_XACT_ERR_Pos (7U)
+#define HCINT_XACT_ERR_Msk (0x1UL << HCINT_XACT_ERR_Pos) // 0x00000080
+#define HCINT_XACT_ERR HCINT_XACT_ERR_Msk // Transaction error
+#define HCINT_BABBLE_ERR_Pos (8U)
+#define HCINT_BABBLE_ERR_Msk (0x1UL << HCINT_BABBLE_ERR_Pos) // 0x00000100
+#define HCINT_BABBLE_ERR HCINT_BABBLE_ERR_Msk // Babble error
+#define HCINT_FARME_OVERRUN_Pos (9U)
+#define HCINT_FARME_OVERRUN_Msk (0x1UL << HCINT_FARME_OVERRUN_Pos) // 0x00000200
+#define HCINT_FARME_OVERRUN HCINT_FARME_OVERRUN_Msk // Frame overrun
+#define HCINT_DATATOGGLE_ERR_Pos (10U)
+#define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400
+#define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error
+#define HCINT_BUFFER_NA_Pos (11U)
+#define HCINT_BUFFER_NA_Msk (0x1UL << HCINT_BUFFER_NA_Pos) // 0x00000800
+#define HCINT_BUFFER_NA HCINT_BUFFER_NA_Msk // Buffer not available interrupt
+#define HCINT_XCS_XACT_ERR_Pos (12U)
+#define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000
+#define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error
+#define HCINT_DESC_ROLLOVER_Pos (13U)
+#define HCINT_DESC_ROLLOVER_Msk (0x1UL << HCINT_DESC_ROLLOVER_Pos) // 0x00002000
+#define HCINT_DESC_ROLLOVER HCINT_DESC_ROLLOVER_Msk // Descriptor rollover
/******************** Bit definition for DIEPINT register ********************/
#define DIEPINT_XFRC_Pos (0U)
-#define DIEPINT_XFRC_Msk (0x1UL << DIEPINT_XFRC_Pos) // 0x00000001 */
-#define DIEPINT_XFRC DIEPINT_XFRC_Msk // Transfer completed interrupt */
+#define DIEPINT_XFRC_Msk (0x1UL << DIEPINT_XFRC_Pos) // 0x00000001
+#define DIEPINT_XFRC DIEPINT_XFRC_Msk // Transfer completed interrupt
#define DIEPINT_EPDISD_Pos (1U)
-#define DIEPINT_EPDISD_Msk (0x1UL << DIEPINT_EPDISD_Pos) // 0x00000002 */
-#define DIEPINT_EPDISD DIEPINT_EPDISD_Msk // Endpoint disabled interrupt */
+#define DIEPINT_EPDISD_Msk (0x1UL << DIEPINT_EPDISD_Pos) // 0x00000002
+#define DIEPINT_EPDISD DIEPINT_EPDISD_Msk // Endpoint disabled interrupt
#define DIEPINT_AHBERR_Pos (2U)
-#define DIEPINT_AHBERR_Msk (0x1UL << DIEPINT_AHBERR_Pos) // 0x00000004 */
-#define DIEPINT_AHBERR DIEPINT_AHBERR_Msk // AHB Error (AHBErr) during an IN transaction */
+#define DIEPINT_AHBERR_Msk (0x1UL << DIEPINT_AHBERR_Pos) // 0x00000004
+#define DIEPINT_AHBERR DIEPINT_AHBERR_Msk // AHB Error (AHBErr) during an IN transaction
#define DIEPINT_TOC_Pos (3U)
-#define DIEPINT_TOC_Msk (0x1UL << DIEPINT_TOC_Pos) // 0x00000008 */
-#define DIEPINT_TOC DIEPINT_TOC_Msk // Timeout condition */
+#define DIEPINT_TOC_Msk (0x1UL << DIEPINT_TOC_Pos) // 0x00000008
+#define DIEPINT_TOC DIEPINT_TOC_Msk // Timeout condition
#define DIEPINT_ITTXFE_Pos (4U)
-#define DIEPINT_ITTXFE_Msk (0x1UL << DIEPINT_ITTXFE_Pos) // 0x00000010 */
-#define DIEPINT_ITTXFE DIEPINT_ITTXFE_Msk // IN token received when TxFIFO is empty */
+#define DIEPINT_ITTXFE_Msk (0x1UL << DIEPINT_ITTXFE_Pos) // 0x00000010
+#define DIEPINT_ITTXFE DIEPINT_ITTXFE_Msk // IN token received when TxFIFO is empty
#define DIEPINT_INEPNM_Pos (5U)
-#define DIEPINT_INEPNM_Msk (0x1UL << DIEPINT_INEPNM_Pos) // 0x00000020 */
-#define DIEPINT_INEPNM DIEPINT_INEPNM_Msk // IN token received with EP mismatch */
+#define DIEPINT_INEPNM_Msk (0x1UL << DIEPINT_INEPNM_Pos) // 0x00000020
+#define DIEPINT_INEPNM DIEPINT_INEPNM_Msk // IN token received with EP mismatch
#define DIEPINT_INEPNE_Pos (6U)
-#define DIEPINT_INEPNE_Msk (0x1UL << DIEPINT_INEPNE_Pos) // 0x00000040 */
-#define DIEPINT_INEPNE DIEPINT_INEPNE_Msk // IN endpoint NAK effective */
+#define DIEPINT_INEPNE_Msk (0x1UL << DIEPINT_INEPNE_Pos) // 0x00000040
+#define DIEPINT_INEPNE DIEPINT_INEPNE_Msk // IN endpoint NAK effective
#define DIEPINT_TXFE_Pos (7U)
-#define DIEPINT_TXFE_Msk (0x1UL << DIEPINT_TXFE_Pos) // 0x00000080 */
-#define DIEPINT_TXFE DIEPINT_TXFE_Msk // Transmit FIFO empty */
+#define DIEPINT_TXFE_Msk (0x1UL << DIEPINT_TXFE_Pos) // 0x00000080
+#define DIEPINT_TXFE DIEPINT_TXFE_Msk // Transmit FIFO empty
#define DIEPINT_TXFIFOUDRN_Pos (8U)
-#define DIEPINT_TXFIFOUDRN_Msk (0x1UL << DIEPINT_TXFIFOUDRN_Pos) // 0x00000100 */
-#define DIEPINT_TXFIFOUDRN DIEPINT_TXFIFOUDRN_Msk // Transmit Fifo Underrun */
+#define DIEPINT_TXFIFOUDRN_Msk (0x1UL << DIEPINT_TXFIFOUDRN_Pos) // 0x00000100
+#define DIEPINT_TXFIFOUDRN DIEPINT_TXFIFOUDRN_Msk // Transmit Fifo Underrun
#define DIEPINT_BNA_Pos (9U)
-#define DIEPINT_BNA_Msk (0x1UL << DIEPINT_BNA_Pos) // 0x00000200 */
-#define DIEPINT_BNA DIEPINT_BNA_Msk // Buffer not available interrupt */
+#define DIEPINT_BNA_Msk (0x1UL << DIEPINT_BNA_Pos) // 0x00000200
+#define DIEPINT_BNA DIEPINT_BNA_Msk // Buffer not available interrupt
#define DIEPINT_PKTDRPSTS_Pos (11U)
-#define DIEPINT_PKTDRPSTS_Msk (0x1UL << DIEPINT_PKTDRPSTS_Pos) // 0x00000800 */
-#define DIEPINT_PKTDRPSTS DIEPINT_PKTDRPSTS_Msk // Packet dropped status */
+#define DIEPINT_PKTDRPSTS_Msk (0x1UL << DIEPINT_PKTDRPSTS_Pos) // 0x00000800
+#define DIEPINT_PKTDRPSTS DIEPINT_PKTDRPSTS_Msk // Packet dropped status
#define DIEPINT_BERR_Pos (12U)
-#define DIEPINT_BERR_Msk (0x1UL << DIEPINT_BERR_Pos) // 0x00001000 */
-#define DIEPINT_BERR DIEPINT_BERR_Msk // Babble error interrupt */
+#define DIEPINT_BERR_Msk (0x1UL << DIEPINT_BERR_Pos) // 0x00001000
+#define DIEPINT_BERR DIEPINT_BERR_Msk // Babble error interrupt
#define DIEPINT_NAK_Pos (13U)
-#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000 */
-#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt */
-
-/******************** Bit definition for HCINTMSK register ********************/
-#define HCINTMSK_XFRCM_Pos (0U)
-#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001 */
-#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask */
-#define HCINTMSK_CHHM_Pos (1U)
-#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002 */
-#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask */
-#define HCINTMSK_AHBERR_Pos (2U)
-#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004 */
-#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error */
-#define HCINTMSK_STALLM_Pos (3U)
-#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008 */
-#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask */
-#define HCINTMSK_NAKM_Pos (4U)
-#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010 */
-#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask */
-#define HCINTMSK_ACKM_Pos (5U)
-#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020 */
-#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask */
-#define HCINTMSK_NYET_Pos (6U)
-#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040 */
-#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask */
-#define HCINTMSK_TXERRM_Pos (7U)
-#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080 */
-#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask */
-#define HCINTMSK_BBERRM_Pos (8U)
-#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100 */
-#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask */
-#define HCINTMSK_FRMORM_Pos (9U)
-#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200 */
-#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask */
-#define HCINTMSK_DTERRM_Pos (10U)
-#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400 */
-#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask */
+#define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000
+#define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt
/******************** Bit definition for DIEPTSIZ register ********************/
#define DIEPTSIZ_XFRSIZ_Pos (0U)
-#define DIEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DIEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF */
-#define DIEPTSIZ_XFRSIZ DIEPTSIZ_XFRSIZ_Msk // Transfer size */
+#define DIEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DIEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF
+#define DIEPTSIZ_XFRSIZ DIEPTSIZ_XFRSIZ_Msk // Transfer size
#define DIEPTSIZ_PKTCNT_Pos (19U)
-#define DIEPTSIZ_PKTCNT_Msk (0x3FFUL << DIEPTSIZ_PKTCNT_Pos) // 0x1FF80000 */
-#define DIEPTSIZ_PKTCNT DIEPTSIZ_PKTCNT_Msk // Packet count */
+#define DIEPTSIZ_PKTCNT_Msk (0x3FFUL << DIEPTSIZ_PKTCNT_Pos) // 0x1FF80000
+#define DIEPTSIZ_PKTCNT DIEPTSIZ_PKTCNT_Msk // Packet count
#define DIEPTSIZ_MULCNT_Pos (29U)
-#define DIEPTSIZ_MULCNT_Msk (0x3UL << DIEPTSIZ_MULCNT_Pos) // 0x60000000 */
-#define DIEPTSIZ_MULCNT DIEPTSIZ_MULCNT_Msk // Packet count */
+#define DIEPTSIZ_MULCNT_Msk (0x3UL << DIEPTSIZ_MULCNT_Pos) // 0x60000000
+#define DIEPTSIZ_MULCNT DIEPTSIZ_MULCNT_Msk // Packet count
/******************** Bit definition for HCTSIZ register ********************/
#define HCTSIZ_XFRSIZ_Pos (0U)
-#define HCTSIZ_XFRSIZ_Msk (0x7FFFFUL << HCTSIZ_XFRSIZ_Pos) // 0x0007FFFF */
-#define HCTSIZ_XFRSIZ HCTSIZ_XFRSIZ_Msk // Transfer size */
+#define HCTSIZ_XFRSIZ_Msk (0x7FFFFUL << HCTSIZ_XFRSIZ_Pos) // 0x0007FFFF
+#define HCTSIZ_XFRSIZ HCTSIZ_XFRSIZ_Msk // Transfer size
#define HCTSIZ_PKTCNT_Pos (19U)
-#define HCTSIZ_PKTCNT_Msk (0x3FFUL << HCTSIZ_PKTCNT_Pos) // 0x1FF80000 */
-#define HCTSIZ_PKTCNT HCTSIZ_PKTCNT_Msk // Packet count */
+#define HCTSIZ_PKTCNT_Msk (0x3FFUL << HCTSIZ_PKTCNT_Pos) // 0x1FF80000
+#define HCTSIZ_PKTCNT HCTSIZ_PKTCNT_Msk // Packet count
#define HCTSIZ_DOPING_Pos (31U)
-#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000 */
-#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING */
-#define HCTSIZ_DPID_Pos (29U)
-#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000 */
-#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID */
-#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000 */
-#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000 */
+#define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000
+#define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING
+#define HCTSIZ_PID_Pos (29U)
+#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000
+#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID
/******************** Bit definition for DIEPDMA register ********************/
#define DIEPDMA_DMAADDR_Pos (0U)
-#define DIEPDMA_DMAADDR_Msk (0xFFFFFFFFUL << DIEPDMA_DMAADDR_Pos) // 0xFFFFFFFF */
-#define DIEPDMA_DMAADDR DIEPDMA_DMAADDR_Msk // DMA address */
+#define DIEPDMA_DMAADDR_Msk (0xFFFFFFFFUL << DIEPDMA_DMAADDR_Pos) // 0xFFFFFFFF
+#define DIEPDMA_DMAADDR DIEPDMA_DMAADDR_Msk // DMA address
/******************** Bit definition for HCDMA register ********************/
#define HCDMA_DMAADDR_Pos (0U)
-#define HCDMA_DMAADDR_Msk (0xFFFFFFFFUL << HCDMA_DMAADDR_Pos) // 0xFFFFFFFF */
-#define HCDMA_DMAADDR HCDMA_DMAADDR_Msk // DMA address */
+#define HCDMA_DMAADDR_Msk (0xFFFFFFFFUL << HCDMA_DMAADDR_Pos) // 0xFFFFFFFF
+#define HCDMA_DMAADDR HCDMA_DMAADDR_Msk // DMA address
/******************** Bit definition for DTXFSTS register ********************/
#define DTXFSTS_INEPTFSAV_Pos (0U)
-#define DTXFSTS_INEPTFSAV_Msk (0xFFFFUL << DTXFSTS_INEPTFSAV_Pos) // 0x0000FFFF */
-#define DTXFSTS_INEPTFSAV DTXFSTS_INEPTFSAV_Msk // IN endpoint TxFIFO space available */
+#define DTXFSTS_INEPTFSAV_Msk (0xFFFFUL << DTXFSTS_INEPTFSAV_Pos) // 0x0000FFFF
+#define DTXFSTS_INEPTFSAV DTXFSTS_INEPTFSAV_Msk // IN endpoint TxFIFO space available
/******************** Bit definition for DIEPTXF register ********************/
#define DIEPTXF_INEPTXSA_Pos (0U)
-#define DIEPTXF_INEPTXSA_Msk (0xFFFFUL << DIEPTXF_INEPTXSA_Pos) // 0x0000FFFF */
-#define DIEPTXF_INEPTXSA DIEPTXF_INEPTXSA_Msk // IN endpoint FIFOx transmit RAM start address */
+#define DIEPTXF_INEPTXSA_Msk (0xFFFFUL << DIEPTXF_INEPTXSA_Pos) // 0x0000FFFF
+#define DIEPTXF_INEPTXSA DIEPTXF_INEPTXSA_Msk // IN endpoint FIFOx transmit RAM start address
#define DIEPTXF_INEPTXFD_Pos (16U)
-#define DIEPTXF_INEPTXFD_Msk (0xFFFFUL << DIEPTXF_INEPTXFD_Pos) // 0xFFFF0000 */
-#define DIEPTXF_INEPTXFD DIEPTXF_INEPTXFD_Msk // IN endpoint TxFIFO depth */
+#define DIEPTXF_INEPTXFD_Msk (0xFFFFUL << DIEPTXF_INEPTXFD_Pos) // 0xFFFF0000
+#define DIEPTXF_INEPTXFD DIEPTXF_INEPTXFD_Msk // IN endpoint TxFIFO depth
+
+
+/******************** Bit definition for Common EPCTL register ********************/
+#define EPCTL_MPSIZ_Pos (0U)
+#define EPCTL_MPSIZ_Msk (0x7FFUL << EPCTL_MPSIZ_Pos) // 0x000007FF
+#define EPCTL_MPSIZ EPCTL_MPSIZ_Msk // Maximum packet size //Bit 1
+#define EPCTL_USBAEP_Pos (15U)
+#define EPCTL_USBAEP_Msk (0x1UL << EPCTL_USBAEP_Pos) // 0x00008000
+#define EPCTL_USBAEP EPCTL_USBAEP_Msk // USB active endpoint
+#define EPCTL_NAKSTS_Pos (17U)
+#define EPCTL_NAKSTS_Msk (0x1UL << EPCTL_NAKSTS_Pos) // 0x00020000
+#define EPCTL_NAKSTS EPCTL_NAKSTS_Msk // NAK status
+#define EPCTL_EPTYP_Pos (18U)
+#define EPCTL_EPTYP_Msk (0x3UL << EPCTL_EPTYP_Pos) // 0x000C0000
+#define EPCTL_EPTYP EPCTL_EPTYP_Msk // Endpoint type
+#define EPCTL_EPTYP_0 (0x1UL << EPCTL_EPTYP_Pos) // 0x00040000
+#define EPCTL_EPTYP_1 (0x2UL << EPCTL_EPTYP_Pos) // 0x00080000
+#define EPCTL_SNPM EPCTL_SNPM_Msk // Snoop mode
+#define EPCTL_STALL_Pos (21U)
+#define EPCTL_STALL_Msk (0x1UL << EPCTL_STALL_Pos) // 0x00200000
+#define EPCTL_STALL EPCTL_STALL_Msk // STALL handshake
+#define EPCTL_CNAK_Pos (26U)
+#define EPCTL_CNAK_Msk (0x1UL << EPCTL_CNAK_Pos) // 0x04000000
+#define EPCTL_CNAK EPCTL_CNAK_Msk // Clear NAK
+#define EPCTL_SNAK_Pos (27U)
+#define EPCTL_SNAK_Msk (0x1UL << EPCTL_SNAK_Pos) // 0x08000000
+#define EPCTL_SNAK EPCTL_SNAK_Msk // Set NAK
+#define EPCTL_SD0PID_SEVNFRM_Pos (28U)
+#define EPCTL_SD0PID_SEVNFRM_Msk (0x1UL << EPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000
+#define EPCTL_SD0PID_SEVNFRM EPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID
+#define EPCTL_SODDFRM_Pos (29U)
+#define EPCTL_SODDFRM_Msk (0x1UL << EPCTL_SODDFRM_Pos) // 0x20000000
+#define EPCTL_SODDFRM EPCTL_SODDFRM_Msk // Set odd frame
+#define EPCTL_EPDIS_Pos (30U)
+#define EPCTL_EPDIS_Msk (0x1UL << EPCTL_EPDIS_Pos) // 0x40000000
+#define EPCTL_EPDIS EPCTL_EPDIS_Msk // Endpoint disable
+#define EPCTL_EPENA_Pos (31U)
+#define EPCTL_EPENA_Msk (0x1UL << EPCTL_EPENA_Pos) // 0x80000000
+#define EPCTL_EPENA EPCTL_EPENA_Msk // Endpoint enable
/******************** Bit definition for DOEPCTL register ********************/
#define DOEPCTL_MPSIZ_Pos (0U)
-#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF */
-#define DOEPCTL_MPSIZ DOEPCTL_MPSIZ_Msk // Maximum packet size */ //Bit 1 */
+#define DOEPCTL_MPSIZ_Msk (0x7FFUL << DOEPCTL_MPSIZ_Pos) // 0x000007FF
+#define DOEPCTL_MPSIZ DOEPCTL_MPSIZ_Msk // Maximum packet size //Bit 1
#define DOEPCTL_USBAEP_Pos (15U)
-#define DOEPCTL_USBAEP_Msk (0x1UL << DOEPCTL_USBAEP_Pos) // 0x00008000 */
-#define DOEPCTL_USBAEP DOEPCTL_USBAEP_Msk // USB active endpoint */
+#define DOEPCTL_USBAEP_Msk (0x1UL << DOEPCTL_USBAEP_Pos) // 0x00008000
+#define DOEPCTL_USBAEP DOEPCTL_USBAEP_Msk // USB active endpoint
#define DOEPCTL_NAKSTS_Pos (17U)
-#define DOEPCTL_NAKSTS_Msk (0x1UL << DOEPCTL_NAKSTS_Pos) // 0x00020000 */
-#define DOEPCTL_NAKSTS DOEPCTL_NAKSTS_Msk // NAK status */
+#define DOEPCTL_NAKSTS_Msk (0x1UL << DOEPCTL_NAKSTS_Pos) // 0x00020000
+#define DOEPCTL_NAKSTS DOEPCTL_NAKSTS_Msk // NAK status
#define DOEPCTL_SD0PID_SEVNFRM_Pos (28U)
-#define DOEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DOEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000 */
-#define DOEPCTL_SD0PID_SEVNFRM DOEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID */
+#define DOEPCTL_SD0PID_SEVNFRM_Msk (0x1UL << DOEPCTL_SD0PID_SEVNFRM_Pos) // 0x10000000
+#define DOEPCTL_SD0PID_SEVNFRM DOEPCTL_SD0PID_SEVNFRM_Msk // Set DATA0 PID
#define DOEPCTL_SODDFRM_Pos (29U)
-#define DOEPCTL_SODDFRM_Msk (0x1UL << DOEPCTL_SODDFRM_Pos) // 0x20000000 */
-#define DOEPCTL_SODDFRM DOEPCTL_SODDFRM_Msk // Set odd frame */
+#define DOEPCTL_SODDFRM_Msk (0x1UL << DOEPCTL_SODDFRM_Pos) // 0x20000000
+#define DOEPCTL_SODDFRM DOEPCTL_SODDFRM_Msk // Set odd frame
#define DOEPCTL_EPTYP_Pos (18U)
-#define DOEPCTL_EPTYP_Msk (0x3UL << DOEPCTL_EPTYP_Pos) // 0x000C0000 */
-#define DOEPCTL_EPTYP DOEPCTL_EPTYP_Msk // Endpoint type */
-#define DOEPCTL_EPTYP_0 (0x1UL << DOEPCTL_EPTYP_Pos) // 0x00040000 */
-#define DOEPCTL_EPTYP_1 (0x2UL << DOEPCTL_EPTYP_Pos) // 0x00080000 */
+#define DOEPCTL_EPTYP_Msk (0x3UL << DOEPCTL_EPTYP_Pos) // 0x000C0000
+#define DOEPCTL_EPTYP DOEPCTL_EPTYP_Msk // Endpoint type
+#define DOEPCTL_EPTYP_0 (0x1UL << DOEPCTL_EPTYP_Pos) // 0x00040000
+#define DOEPCTL_EPTYP_1 (0x2UL << DOEPCTL_EPTYP_Pos) // 0x00080000
#define DOEPCTL_SNPM_Pos (20U)
-#define DOEPCTL_SNPM_Msk (0x1UL << DOEPCTL_SNPM_Pos) // 0x00100000 */
-#define DOEPCTL_SNPM DOEPCTL_SNPM_Msk // Snoop mode */
+#define DOEPCTL_SNPM_Msk (0x1UL << DOEPCTL_SNPM_Pos) // 0x00100000
+#define DOEPCTL_SNPM DOEPCTL_SNPM_Msk // Snoop mode
#define DOEPCTL_STALL_Pos (21U)
-#define DOEPCTL_STALL_Msk (0x1UL << DOEPCTL_STALL_Pos) // 0x00200000 */
-#define DOEPCTL_STALL DOEPCTL_STALL_Msk // STALL handshake */
+#define DOEPCTL_STALL_Msk (0x1UL << DOEPCTL_STALL_Pos) // 0x00200000
+#define DOEPCTL_STALL DOEPCTL_STALL_Msk // STALL handshake
#define DOEPCTL_CNAK_Pos (26U)
-#define DOEPCTL_CNAK_Msk (0x1UL << DOEPCTL_CNAK_Pos) // 0x04000000 */
-#define DOEPCTL_CNAK DOEPCTL_CNAK_Msk // Clear NAK */
+#define DOEPCTL_CNAK_Msk (0x1UL << DOEPCTL_CNAK_Pos) // 0x04000000
+#define DOEPCTL_CNAK DOEPCTL_CNAK_Msk // Clear NAK
#define DOEPCTL_SNAK_Pos (27U)
-#define DOEPCTL_SNAK_Msk (0x1UL << DOEPCTL_SNAK_Pos) // 0x08000000 */
-#define DOEPCTL_SNAK DOEPCTL_SNAK_Msk // Set NAK */
+#define DOEPCTL_SNAK_Msk (0x1UL << DOEPCTL_SNAK_Pos) // 0x08000000
+#define DOEPCTL_SNAK DOEPCTL_SNAK_Msk // Set NAK
#define DOEPCTL_EPDIS_Pos (30U)
-#define DOEPCTL_EPDIS_Msk (0x1UL << DOEPCTL_EPDIS_Pos) // 0x40000000 */
-#define DOEPCTL_EPDIS DOEPCTL_EPDIS_Msk // Endpoint disable */
+#define DOEPCTL_EPDIS_Msk (0x1UL << DOEPCTL_EPDIS_Pos) // 0x40000000
+#define DOEPCTL_EPDIS DOEPCTL_EPDIS_Msk // Endpoint disable
#define DOEPCTL_EPENA_Pos (31U)
-#define DOEPCTL_EPENA_Msk (0x1UL << DOEPCTL_EPENA_Pos) // 0x80000000 */
-#define DOEPCTL_EPENA DOEPCTL_EPENA_Msk // Endpoint enable */
+#define DOEPCTL_EPENA_Msk (0x1UL << DOEPCTL_EPENA_Pos) // 0x80000000
+#define DOEPCTL_EPENA DOEPCTL_EPENA_Msk // Endpoint enable
/******************** Bit definition for DOEPINT register ********************/
#define DOEPINT_XFRC_Pos (0U)
-#define DOEPINT_XFRC_Msk (0x1UL << DOEPINT_XFRC_Pos) // 0x00000001 */
-#define DOEPINT_XFRC DOEPINT_XFRC_Msk // Transfer completed interrupt */
+#define DOEPINT_XFRC_Msk (0x1UL << DOEPINT_XFRC_Pos) // 0x00000001
+#define DOEPINT_XFRC DOEPINT_XFRC_Msk // Transfer completed interrupt
#define DOEPINT_EPDISD_Pos (1U)
-#define DOEPINT_EPDISD_Msk (0x1UL << DOEPINT_EPDISD_Pos) // 0x00000002 */
-#define DOEPINT_EPDISD DOEPINT_EPDISD_Msk // Endpoint disabled interrupt */
+#define DOEPINT_EPDISD_Msk (0x1UL << DOEPINT_EPDISD_Pos) // 0x00000002
+#define DOEPINT_EPDISD DOEPINT_EPDISD_Msk // Endpoint disabled interrupt
#define DOEPINT_AHBERR_Pos (2U)
-#define DOEPINT_AHBERR_Msk (0x1UL << DOEPINT_AHBERR_Pos) // 0x00000004 */
-#define DOEPINT_AHBERR DOEPINT_AHBERR_Msk // AHB Error (AHBErr) during an OUT transaction */
-#define DOEPINT_STUP_Pos (3U)
-#define DOEPINT_STUP_Msk (0x1UL << DOEPINT_STUP_Pos) // 0x00000008 */
-#define DOEPINT_STUP DOEPINT_STUP_Msk // SETUP phase done */
+#define DOEPINT_AHBERR_Msk (0x1UL << DOEPINT_AHBERR_Pos) // 0x00000004
+#define DOEPINT_AHBERR DOEPINT_AHBERR_Msk // AHB Error (AHBErr) during an OUT transaction
+
+#define DOEPINT_SETUP_Pos (3U)
+#define DOEPINT_SETUP_Msk (0x1UL << DOEPINT_SETUP_Pos) // 0x00000008
+#define DOEPINT_SETUP DOEPINT_SETUP_Msk // SETUP phase done
+
#define DOEPINT_OTEPDIS_Pos (4U)
-#define DOEPINT_OTEPDIS_Msk (0x1UL << DOEPINT_OTEPDIS_Pos) // 0x00000010 */
-#define DOEPINT_OTEPDIS DOEPINT_OTEPDIS_Msk // OUT token received when endpoint disabled */
-#define DOEPINT_OTEPSPR_Pos (5U)
-#define DOEPINT_OTEPSPR_Msk (0x1UL << DOEPINT_OTEPSPR_Pos) // 0x00000020 */
-#define DOEPINT_OTEPSPR DOEPINT_OTEPSPR_Msk // Status Phase Received For Control Write */
+#define DOEPINT_OTEPDIS_Msk (0x1UL << DOEPINT_OTEPDIS_Pos) // 0x00000010
+#define DOEPINT_OTEPDIS DOEPINT_OTEPDIS_Msk // OUT token received when endpoint disabled
+
+#define DOEPINT_STSPHSRX_Pos (5U)
+#define DOEPINT_STSPHSRX_Msk (0x1UL << DOEPINT_STSPHSRX_Pos) // 0x00000020
+#define DOEPINT_STSPHSRX DOEPINT_STSPHSRX_Msk // Status Phase Received For Control Write
+
#define DOEPINT_B2BSTUP_Pos (6U)
-#define DOEPINT_B2BSTUP_Msk (0x1UL << DOEPINT_B2BSTUP_Pos) // 0x00000040 */
-#define DOEPINT_B2BSTUP DOEPINT_B2BSTUP_Msk // Back-to-back SETUP packets received */
+#define DOEPINT_B2BSTUP_Msk (0x1UL << DOEPINT_B2BSTUP_Pos) // 0x00000040
+#define DOEPINT_B2BSTUP DOEPINT_B2BSTUP_Msk // Back-to-back SETUP packets received
#define DOEPINT_OUTPKTERR_Pos (8U)
-#define DOEPINT_OUTPKTERR_Msk (0x1UL << DOEPINT_OUTPKTERR_Pos) // 0x00000100 */
-#define DOEPINT_OUTPKTERR DOEPINT_OUTPKTERR_Msk // OUT packet error */
+#define DOEPINT_OUTPKTERR_Msk (0x1UL << DOEPINT_OUTPKTERR_Pos) // 0x00000100
+#define DOEPINT_OUTPKTERR DOEPINT_OUTPKTERR_Msk // OUT packet error
#define DOEPINT_NAK_Pos (13U)
-#define DOEPINT_NAK_Msk (0x1UL << DOEPINT_NAK_Pos) // 0x00002000 */
-#define DOEPINT_NAK DOEPINT_NAK_Msk // NAK Packet is transmitted by the device */
+#define DOEPINT_NAK_Msk (0x1UL << DOEPINT_NAK_Pos) // 0x00002000
+#define DOEPINT_NAK DOEPINT_NAK_Msk // NAK Packet is transmitted by the device
#define DOEPINT_NYET_Pos (14U)
-#define DOEPINT_NYET_Msk (0x1UL << DOEPINT_NYET_Pos) // 0x00004000 */
-#define DOEPINT_NYET DOEPINT_NYET_Msk // NYET interrupt */
+#define DOEPINT_NYET_Msk (0x1UL << DOEPINT_NYET_Pos) // 0x00004000
+#define DOEPINT_NYET DOEPINT_NYET_Msk // NYET interrupt
+
#define DOEPINT_STPKTRX_Pos (15U)
-#define DOEPINT_STPKTRX_Msk (0x1UL << DOEPINT_STPKTRX_Pos) // 0x00008000 */
-#define DOEPINT_STPKTRX DOEPINT_STPKTRX_Msk // Setup Packet Received */
+#define DOEPINT_STPKTRX_Msk (0x1UL << DOEPINT_STPKTRX_Pos) // 0x00008000
+#define DOEPINT_STPKTRX DOEPINT_STPKTRX_Msk // Setup Packet Received
/******************** Bit definition for DOEPTSIZ register ********************/
#define DOEPTSIZ_XFRSIZ_Pos (0U)
-#define DOEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DOEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF */
-#define DOEPTSIZ_XFRSIZ DOEPTSIZ_XFRSIZ_Msk // Transfer size */
+#define DOEPTSIZ_XFRSIZ_Msk (0x7FFFFUL << DOEPTSIZ_XFRSIZ_Pos) // 0x0007FFFF
+#define DOEPTSIZ_XFRSIZ DOEPTSIZ_XFRSIZ_Msk // Transfer size
#define DOEPTSIZ_PKTCNT_Pos (19U)
-#define DOEPTSIZ_PKTCNT_Msk (0x3FFUL << DOEPTSIZ_PKTCNT_Pos) // 0x1FF80000 */
-#define DOEPTSIZ_PKTCNT DOEPTSIZ_PKTCNT_Msk // Packet count */
+#define DOEPTSIZ_PKTCNT_Msk (0x3FFUL << DOEPTSIZ_PKTCNT_Pos) // 0x1FF80000
+#define DOEPTSIZ_PKTCNT DOEPTSIZ_PKTCNT_Msk // Packet count
#define DOEPTSIZ_STUPCNT_Pos (29U)
-#define DOEPTSIZ_STUPCNT_Msk (0x3UL << DOEPTSIZ_STUPCNT_Pos) // 0x60000000 */
-#define DOEPTSIZ_STUPCNT DOEPTSIZ_STUPCNT_Msk // SETUP packet count */
-#define DOEPTSIZ_STUPCNT_0 (0x1UL << DOEPTSIZ_STUPCNT_Pos) // 0x20000000 */
-#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000 */
+#define DOEPTSIZ_STUPCNT_Msk (0x3UL << DOEPTSIZ_STUPCNT_Pos) // 0x60000000
+#define DOEPTSIZ_STUPCNT DOEPTSIZ_STUPCNT_Msk // SETUP packet count
+#define DOEPTSIZ_STUPCNT_0 (0x1UL << DOEPTSIZ_STUPCNT_Pos) // 0x20000000
+#define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000
/******************** Bit definition for PCGCTL register ********************/
-#define PCGCTL_IF_DEV_MODE TU_BIT(31)
-#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
-#define PCGCTL_P2HD_PRT_SPD_SHIFT 29
-#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
-#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
-#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
-#define PCGCTL_MAC_DEV_ADDR_SHIFT 20
-#define PCGCTL_MAX_TERMSEL TU_BIT(19)
-#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
-#define PCGCTL_MAX_XCVRSELECT_SHIFT 17
-#define PCGCTL_PORT_POWER TU_BIT(16)
-#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
-#define PCGCTL_PRT_CLK_SEL_SHIFT 14
-#define PCGCTL_ESS_REG_RESTORED TU_BIT(13)
-#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12)
-#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
-#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10)
-#define PCGCTL_RESTOREMODE TU_BIT(9)
-#define PCGCTL_RESETAFTSUSP TU_BIT(8)
-#define PCGCTL_DEEP_SLEEP TU_BIT(7)
-#define PCGCTL_PHY_IN_SLEEP TU_BIT(6)
-#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5)
-#define PCGCTL_RSTPDWNMODULE TU_BIT(3)
-#define PCGCTL_PWRCLMP TU_BIT(2)
-#define PCGCTL_GATEHCLK TU_BIT(1)
-#define PCGCTL_STOPPCLK TU_BIT(0)
+#define PCGCCTL_IF_DEV_MODE TU_BIT(31)
+#define PCGCCTL_P2HD_PRT_SPD_MASK (0x3ul << 29)
+#define PCGCCTL_P2HD_PRT_SPD_SHIFT 29
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27)
+#define PCGCCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
+#define PCGCCTL_MAC_DEV_ADDR_MASK (0x7ful << 20)
+#define PCGCCTL_MAC_DEV_ADDR_SHIFT 20
+#define PCGCCTL_MAX_TERMSEL TU_BIT(19)
+#define PCGCCTL_MAX_XCVRSELECT_MASK (0x3ul << 17)
+#define PCGCCTL_MAX_XCVRSELECT_SHIFT 17
+#define PCGCCTL_PORT_POWER TU_BIT(16)
+#define PCGCCTL_PRT_CLK_SEL_MASK (0x3ul << 14)
+#define PCGCCTL_PRT_CLK_SEL_SHIFT 14
+#define PCGCCTL_ESS_REG_RESTORED TU_BIT(13)
+#define PCGCCTL_EXTND_HIBER_SWITCH TU_BIT(12)
+#define PCGCCTL_EXTND_HIBER_PWRCLMP TU_BIT(11)
+#define PCGCCTL_ENBL_EXTND_HIBER TU_BIT(10)
+#define PCGCCTL_RESTOREMODE TU_BIT(9)
+#define PCGCCTL_RESETAFTSUSP TU_BIT(8)
+#define PCGCCTL_DEEP_SLEEP TU_BIT(7)
+#define PCGCCTL_PHY_IN_SLEEP TU_BIT(6)
+#define PCGCCTL_ENBL_SLEEP_GATING TU_BIT(5)
+#define PCGCCTL_RSTPDWNMODULE TU_BIT(3)
+#define PCGCCTL_PWRCLMP TU_BIT(2)
+#define PCGCCTL_GATEHCLK TU_BIT(1)
+#define PCGCCTL_STOPPCLK TU_BIT(0)
#define PCGCTL1_TIMER (0x3ul << 1)
#define PCGCTL1_GATEEN TU_BIT(0)
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
new file mode 100644
index 000000000..ebbb6200a
--- /dev/null
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -0,0 +1,1360 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2)
+
+#if !(CFG_TUH_DWC2_SLAVE_ENABLE || CFG_TUH_DWC2_DMA_ENABLE)
+#error DWC2 require either CFG_TUH_DWC2_SLAVE_ENABLE or CFG_TUH_DWC2_DMA_ENABLE to be enabled
+#endif
+
+// Debug level for DWC2
+#define DWC2_DEBUG 2
+
+#include "host/hcd.h"
+#include "dwc2_common.h"
+
+// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
+#ifndef CFG_TUH_DWC2_ENDPOINT_MAX
+#define CFG_TUH_DWC2_ENDPOINT_MAX 16
+#endif
+
+#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count
+#define DWC2_CHANNEL_COUNT(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX)
+
+TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
+
+enum {
+ HPRT_W1_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE | HPRT_SUSPEND
+};
+
+enum {
+ HCD_XFER_ERROR_MAX = 3
+};
+
+enum {
+ HCD_XFER_PERIOD_SPLIT_NYET_MAX = 3
+};
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+// Host driver struct for each opened endpoint
+typedef struct {
+ union {
+ uint32_t hcchar;
+ dwc2_channel_char_t hcchar_bm;
+ };
+ union {
+ uint32_t hcsplt;
+ dwc2_channel_split_t hcsplt_bm;
+ };
+
+ struct TU_ATTR_PACKED {
+ uint32_t uframe_interval : 18; // micro-frame interval
+ uint32_t speed : 2;
+ uint32_t next_pid : 2;
+ uint32_t do_ping : 1;
+ // uint32_t : 9;
+ };
+
+ uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit
+
+ uint8_t* buffer;
+ uint16_t buflen;
+} hcd_endpoint_t;
+
+// Additional info for each channel when it is active
+typedef struct {
+ volatile bool allocated;
+ uint8_t ep_id;
+ struct TU_ATTR_PACKED {
+ uint8_t err_count : 3;
+ uint8_t period_split_nyet_count : 3;
+ uint8_t halted_nyet : 1;
+ uint8_t halted_sof_schedule : 1;
+ };
+ uint8_t result;
+
+ uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can
+ // be composed of multiple channel_xfer_start() (retry with NAK/NYET)
+ uint16_t fifo_bytes; // bytes written/read from/to FIFO (may not be transferred on USB bus).
+} hcd_xfer_t;
+
+typedef struct {
+ hcd_xfer_t xfer[DWC2_CHANNEL_COUNT_MAX];
+ hcd_endpoint_t edpt[CFG_TUH_DWC2_ENDPOINT_MAX];
+} hcd_data_t;
+
+hcd_data_t _hcd_data;
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) {
+ tusb_speed_t speed;
+ switch(dwc2->hprt_bm.speed) {
+ case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break;
+ case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break;
+ case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break;
+ default:
+ speed = TUSB_SPEED_INVALID;
+ TU_BREAKPOINT();
+ break;
+ }
+ return speed;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) {
+ (void) dwc2;
+ // Internal DMA only
+ return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
+}
+
+#if CFG_TUH_MEM_DCACHE_ENABLE
+bool hcd_dcache_clean(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean(addr, data_size);
+}
+
+bool hcd_dcache_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_invalidate(addr, data_size);
+}
+
+bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
+ TU_VERIFY(addr && data_size);
+ return dwc2_dcache_clean_invalidate(addr, data_size);
+}
+#endif
+
+// Allocate a channel for new transfer
+TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ if (!xfer->allocated) {
+ tu_memclr(xfer, sizeof(hcd_xfer_t));
+ xfer->allocated = true;
+ return ch_id;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// Check if is periodic (interrupt/isochronous)
+TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) {
+ return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
+ if (is_period) {
+ return dwc2->hptxsts_bm.req_queue_available;
+ } else {
+ return dwc2->hnptxsts_bm.req_queue_available;
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->allocated = false;
+ dwc2->haintmsk &= ~TU_BIT(ch_id);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
+ // disable also require request queue
+ TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ channel->hcintmsk |= HCINT_HALTED;
+ channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
+ return true;
+}
+
+// attempt to send IN token to receive data
+TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
+ TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
+ channel->hcchar |= HCCHAR_CHENA;
+ return true;
+}
+
+// Find currently enabled channel. Note: EP0 is bidirectional
+TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ if (_hcd_data.xfer[ch_id].allocated) {
+ const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm;
+ if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) {
+ return ch_id;
+ }
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+
+// Allocate a new endpoint
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) {
+ for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable == 0) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ edpt->hcchar_bm.enable = 1;
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+// Find a endpoint that is opened previously with hcd_edpt_open()
+// Note: EP0 is bidirectional
+TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
+ for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ const dwc2_channel_char_t* hcchar_bm = &_hcd_data.edpt[i].hcchar_bm;
+ if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr &&
+ hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) {
+ return i;
+ }
+ }
+ return TUSB_INDEX_INVALID_8;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t cal_packet_count(uint16_t len, uint16_t ep_size) {
+ if (len == 0) {
+ return 1;
+ } else {
+ return tu_div_ceil(len, ep_size);
+ }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) {
+ if (packet_count & 0x01) {
+ return pid ^ 0x02; // toggle DATA0 and DATA1
+ } else {
+ return pid;
+ }
+}
+
+//--------------------------------------------------------------------
+//
+//--------------------------------------------------------------------
+
+/* USB Data FIFO Layout
+
+ The FIFO is split up into
+ - EPInfo: for storing DMA metadata (check dcd_dwc2.c for more details)
+ - 1 RX FIFO: for receiving data
+ - 1 TX FIFO for non-periodic (NPTX)
+ - 1 TX FIFO for periodic (PTX)
+
+ We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is
+ possible since the free space is located between the RX and TX FIFOs.
+
+ ----------------- ep_fifo_size
+ | HCDMAn |
+ |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth)
+ | Non-Periodic |
+ | TX FIFO |
+ |--------------|--- GNPTXFSIZ.addr (fixed size)
+ | Periodic |
+ | TX FIFO |
+ |--------------|--- HPTXFSIZ.addr (expandable downward)
+ | FREE |
+ | |
+ |--------------|-- GRXFSIZ (expandable upward)
+ | RX FIFO |
+ ---------------- 0
+*/
+
+/* Programming Guide 2.1.2 FIFO RAM allocation
+ * RX
+ * - Largest-EPsize/4 + 2 (status info). recommended x2 if high bandwidth or multiple ISO are used.
+ * - 2 for transfer complete and channel halted status
+ * - 1 for each Control/Bulk out endpoint to Handle NAK/NYET (i.e max is number of host channel)
+ *
+ * TX non-periodic (NPTX)
+ * - At least largest-EPsize/4, recommended x2
+ *
+ * TX periodic (PTX)
+ * - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt)
+*/
+static void dfifo_host_init(uint8_t rhport) {
+ const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel
+ const bool is_dma = dma_host_enabled(dwc2);
+ uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4;
+ if (is_dma) {
+ dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch;
+ }
+
+ // fixed allocation for now, improve later:
+ // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total
+ bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4;
+ uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
+
+ uint16_t nptxfsiz = 2 * nptx_largest;
+ uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch;
+ TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
+ uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
+
+ dwc2->gdfifocfg = (dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | dfifo_top;
+
+ dfifo_top -= rxfsiz;
+ dwc2->grxfsiz = rxfsiz;
+
+ dfifo_top -= nptxfsiz;
+ dwc2->gnptxfsiz = tu_u32_from_u16(nptxfsiz, dfifo_top);
+
+ dfifo_top -= ptxfsiz;
+ dwc2->hptxfsiz = tu_u32_from_u16(ptxfsiz, dfifo_top);
+}
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ (void) cfg_id;
+ (void) cfg_param;
+
+ return true;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ tu_memclr(&_hcd_data, sizeof(_hcd_data));
+
+ // Core Initialization
+ const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
+ const bool is_dma = dma_host_enabled(dwc2);
+ TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
+
+ //------------- 3.1 Host Initialization -------------//
+
+ // work at max supported speed
+ dwc2->hcfg &= ~HCFG_FSLS_ONLY;
+
+ // Enable HFIR reload
+ if (dwc2->gsnpsid >= DWC2_CORE_REV_2_92a) {
+ dwc2->hfir |= HFIR_RELOAD_CTRL;
+ }
+
+ // force host mode and wait for mode switch
+ dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD;
+ while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {}
+
+ // configure fixed-allocated fifo scheme
+ dfifo_host_init(rhport);
+
+ dwc2->hprt = HPRT_W1_MASK; // clear all write-1-clear bits
+ dwc2->hprt = HPRT_POWER; // turn on VBUS
+
+ // Enable required interrupts
+ dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT;
+
+ // NPTX can hold at least 2 packet, change interrupt level to half-empty
+ uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
+ gahbcfg |= GAHBCFG_GINT; // Enable global interrupt
+ dwc2->gahbcfg = gahbcfg;
+
+ return true;
+}
+
+// Enable USB interrupt
+void hcd_int_enable (uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, true);
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport) {
+ dwc2_int_set(rhport, TUSB_ROLE_HOST, false);
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hfnum & HFNUM_FRNUM_Msk;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ return dwc2->hprt & HPRT_CONN_STATUS;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
+void hcd_port_reset(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
+ hprt |= HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; // skip w1c bits
+ hprt &= ~HPRT_RESET;
+ dwc2->hprt = hprt;
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ return speed;
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[i];
+ if (edpt->hcchar_bm.enable && edpt->hcchar_bm.dev_addr == dev_addr) {
+ tu_memclr(edpt, sizeof(hcd_endpoint_t));
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const tusb_speed_t rh_speed = hprt_speed_get(dwc2);
+
+ hcd_devtree_info_t devtree_info;
+ hcd_devtree_get_info(dev_addr, &devtree_info);
+
+ // find a free endpoint
+ const uint8_t ep_id = edpt_alloc();
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
+ hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep);
+ hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress);
+ hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
+ hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_*
+ hcchar_bm->err_multi_count = 0;
+ hcchar_bm->dev_addr = dev_addr;
+ hcchar_bm->odd_frame = 0;
+ hcchar_bm->disable = 0;
+ hcchar_bm->enable = 1;
+
+ dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm;
+ hcsplt_bm->hub_port = devtree_info.hub_port;
+ hcsplt_bm->hub_addr = devtree_info.hub_addr;
+ hcsplt_bm->xact_pos = 0;
+ hcsplt_bm->split_compl = 0;
+ hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
+
+ edpt->speed = devtree_info.speed;
+ edpt->next_pid = HCTSIZ_PID_DATA0;
+ if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ if (devtree_info.speed == TUSB_SPEED_FULL) {
+ edpt->uframe_interval <<= 3;
+ }
+ } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
+ if (devtree_info.speed == TUSB_SPEED_HIGH) {
+ edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
+ } else {
+ edpt->uframe_interval = desc_ep->bInterval << 3;
+ }
+ }
+
+ return true;
+}
+
+// clean up channel after part of transfer is done but the whole urb is not complete
+static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+
+ /* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB)
+ * For IN: we can use hctsiz.xfersize as remaining bytes.
+ * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the
+ * number of packets that have been transferred via the USB. This is always an integral number of packets if the
+ * transfer was halted before its normal completion.
+ */
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size);
+ const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
+
+ xfer->fifo_bytes = 0;
+ xfer->xferred_bytes += actual_bytes;
+ edpt->buffer += actual_bytes;
+ edpt->buflen -= actual_bytes;
+}
+
+static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ bool const is_period = edpt_is_periodic(hcchar_bm->ep_type);
+
+ // clear previous state
+ xfer->fifo_bytes = 0;
+
+ // hchar: restore but don't enable yet
+ if (is_period) {
+ hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ }
+ channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA);
+
+ // hctsiz: zero length packet still count as 1
+ const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size);
+ uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen;
+ if (edpt->do_ping && edpt->speed == TUSB_SPEED_HIGH &&
+ edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
+ hctsiz |= HCTSIZ_DOPING;
+ }
+ channel->hctsiz = hctsiz;
+ edpt->do_ping = 0;
+
+ // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet
+ if (hcchar_bm->ep_num == 0) {
+ edpt->next_pid = HCTSIZ_PID_DATA1; // control data and status stage always start with DATA1
+ } else {
+ edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count);
+ }
+
+ channel->hcsplt = edpt->hcsplt;
+ channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts
+
+ if (dma_host_enabled(dwc2)) {
+ uint32_t hcintmsk = HCINT_HALTED;
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ channel->hcdma = (uint32_t) edpt->buffer;
+
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ hcd_dcache_clean(edpt->buffer, edpt->buflen);
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else {
+ uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR;
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
+ } else {
+ hcintmsk |= HCINT_NYET;
+ if (edpt->hcsplt_bm.split_en) {
+ hcintmsk |= HCINT_ACK;
+ }
+ }
+ channel->hcintmsk = hcintmsk;
+ dwc2->haintmsk |= TU_BIT(ch_id);
+
+ // enable channel for slave mode:
+ // - OUT: it will enable corresponding FIFO channel
+ // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send
+ // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only
+ // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically.
+ if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
+ channel_send_in_token(dwc2, channel);
+ } else {
+ channel->hcchar |= HCCHAR_CHENA;
+ if (edpt->buflen > 0) {
+ // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly
+ // And write packet in the interrupt handler
+ dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY);
+ }
+ }
+ }
+
+ return true;
+}
+
+// kick-off transfer with an endpoint
+static bool edpt_xfer_kickoff(dwc2_regs_t* dwc2, uint8_t ep_id) {
+ uint8_t ch_id = channel_alloc(dwc2);
+ TU_ASSERT(ch_id < 16); // all channel are in used
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ xfer->ep_id = ep_id;
+ xfer->result = XFER_RESULT_INVALID;
+
+ return channel_xfer_start(dwc2, ch_id);
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+
+ uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->buffer = buffer;
+ edpt->buflen = buflen;
+
+ if (ep_num == 0) {
+ // update ep_dir since control endpoint can switch direction
+ edpt->hcchar_bm.ep_dir = ep_dir;
+ }
+
+ return edpt_xfer_kickoff(dwc2, ep_id);
+}
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+
+ // hcd_int_disable(rhport);
+
+ // Find enabled channeled and disable it, channel will be de-allocated in the interrupt handler
+ const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir);
+ if (ch_id < 16) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ channel_disable(dwc2, channel);
+ }
+
+ // hcd_int_enable(rhport);
+
+ return true;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) {
+ uint8_t ep_id = edpt_find_opened(dev_addr, 0, TUSB_DIR_OUT);
+ TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened endpoint
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ edpt->next_pid = HCTSIZ_PID_SETUP;
+
+ return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8);
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ const uint8_t ep_num = tu_edpt_number(ep_addr);
+ const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+ const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir);
+ TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+
+ edpt->next_pid = HCTSIZ_PID_DATA0;
+
+ return true;
+}
+
+//--------------------------------------------------------------------
+// HCD Event Handler
+//--------------------------------------------------------------------
+static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
+ // retry immediately for periodic split NYET if we haven't reach max retry
+ if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
+ xfer->period_split_nyet_count++;
+ xfer->halted_nyet = 0;
+ if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) {
+ channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ channel_send_in_token(dwc2, channel);
+ return;
+ } else {
+ // too many NYET, de-allocate channel with below code
+ xfer->period_split_nyet_count = 0;
+ }
+ }
+
+ // for periodic, de-allocate channel, enable SOF set frame counter for later transfer
+ edpt->next_pid = channel->hctsiz_bm.pid; // save PID
+ edpt->uframe_countdown = edpt->uframe_interval;
+ dwc2->gintmsk |= GINTSTS_SOF;
+
+ if (hcint & HCINT_HALTED) {
+ // already halted, de-allocate channel (called from DMA isr)
+ channel_dealloc(dwc2, ch_id);
+ } else {
+ // disable channel first if not halted (called slave isr)
+ xfer->halted_sof_schedule = 1;
+ channel_disable(dwc2, channel);
+ }
+ } else {
+ // for control/bulk: retry immediately
+ channel_send_in_token(dwc2, channel);
+ }
+}
+
+#if CFG_TUSB_DEBUG
+TU_ATTR_ALWAYS_INLINE static inline void print_hcint(uint32_t hcint) {
+ const char* str[] = {
+ "XFRC", "HALTED", "AHBERR", "STALL",
+ "NAK", "ACK", "NYET", "XERR",
+ "BBLERR", "FRMOR", "DTERR", "BNA",
+ "XCSERR", "DESC_LST"
+ };
+
+ for(uint32_t i=0; i<14; i++) {
+ if (hcint & TU_BIT(i)) {
+ TU_LOG1("%s ", str[i]);
+ }
+ }
+ TU_LOG1("\r\n");
+}
+#endif
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+static void handle_rxflvl_irq(uint8_t rhport) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ // Pop control word off FIFO
+ const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
+ const uint8_t ch_id = grxstsp_bm.ep_ch_num;
+
+ switch (grxstsp_bm.packet_status) {
+ case GRXSTS_PKTSTS_RX_DATA: {
+ // In packet received, pop this entry --> ACK interrupt
+ const uint16_t byte_count = grxstsp_bm.byte_count;
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ if (byte_count) {
+ dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count);
+ xfer->xferred_bytes += byte_count;
+ xfer->fifo_bytes = byte_count;
+ }
+ break;
+ }
+
+ case GRXSTS_PKTSTS_RX_COMPLETE:
+ // In transfer complete: After this entry is popped from the rx FIFO, dwc2 asserts a Transfer Completed
+ // interrupt --> handle_channel_irq()
+ break;
+
+ case GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR:
+ TU_ASSERT(0, ); // maybe try to change DToggle
+ break;
+
+ case GRXSTS_PKTSTS_HOST_CHANNEL_HALTED:
+ // triggered when channel.hcchar_bm.disable is set
+ // TODO handle later
+ break;
+
+ default: break; // ignore other status
+ }
+}
+
+// return true if there is still pending data and need more ISR
+static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
+ // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough)
+ volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts);
+
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ // skip writing to FIFO if channel is expecting halted.
+ if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ for (uint16_t i = 0; i < remain_packets; i++) {
+ const uint16_t remain_bytes = edpt->buflen - xfer->fifo_bytes;
+ const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size);
+
+ // skip if there is not enough space in FIFO and RequestQueue.
+ // Packet's last word written to FIFO will trigger a request queue
+ if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) {
+ return true;
+ }
+
+ dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->fifo_bytes, xact_bytes);
+ xfer->fifo_bytes += xact_bytes;
+ }
+ }
+ }
+
+ return false; // no channel has pending data
+}
+
+static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ bool is_done = false;
+
+ // if (channel->hcsplt_bm.split_en) {
+ // if (edpt->hcchar_bm.ep_num == 1) {
+ // TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, channel->hcchar_bm.ep_num, hcint);
+ // print_hcint(hcint);
+ // }
+
+ if (hcint & HCINT_XFER_COMPLETE) {
+ if (edpt->hcchar_bm.ep_num != 0) {
+ edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled)
+ }
+
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ if (channel->hcsplt_bm.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
+ // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
+ channel->hcsplt_bm.split_compl = 0;
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) {
+ if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ } else if (hcint & HCINT_BABBLE_ERR) {
+ xfer->result = XFER_RESULT_FAILED;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NYET) {
+ // restart complete split
+ channel->hcsplt_bm.split_compl = 1;
+ xfer->halted_nyet = 1;
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NAK) {
+ // NAK received, re-enable channel if request queue is available
+ if (channel->hcsplt_bm.split_en) {
+ channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ }
+
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+
+ if (channel->hcsplt_bm.split_en) {
+ if (!channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcintmsk |= HCINT_NYET;
+ channel->hcsplt_bm.split_compl = 1;
+ channel_send_in_token(dwc2, channel);
+ } else {
+ // do nothing for complete split with DATA, this will trigger XferComplete and handled there
+ }
+ } else {
+ // ACK with data
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ if (remain_packets) {
+ // still more packet to receive, also reset to start split
+ channel->hcsplt_bm.split_compl = 0;
+ channel_send_in_token(dwc2, channel);
+ }
+ }
+ } else if (hcint & HCINT_HALTED) {
+ channel->hcintmsk &= ~HCINT_HALTED;
+ if (xfer->halted_sof_schedule) {
+ // de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt
+ channel_dealloc(dwc2, ch_id);
+ } else if (xfer->result != XFER_RESULT_INVALID) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // got here due to NAK or NYET
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_DATATOGGLE_ERR) {
+ xfer->err_count = 0;
+ TU_ASSERT(false);
+ }
+ return is_done;
+}
+
+static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+ bool is_done = false;
+
+ if (hcint & HCINT_XFER_COMPLETE) {
+ is_done = true;
+ xfer->result = XFER_RESULT_SUCCESS;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_NYET) {
+ xfer->err_count = 0;
+ if (channel->hcsplt_bm.split_en) {
+ // retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ } else {
+ edpt->do_ping = 1;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR)) {
+ // clean up transfer so far, disable and start again later
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_disable(dwc2, channel);
+ if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ channel->hcintmsk |= HCINT_ACK;
+ } else {
+ // NAK disable channel to flush all posted request and try again
+ edpt->do_ping = 1;
+ xfer->err_count = 0;
+ }
+ } else if (hcint & HCINT_HALTED) {
+ channel->hcintmsk &= ~HCINT_HALTED;
+ if (xfer->result != XFER_RESULT_INVALID) {
+ is_done = true;
+ } else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // Got here due to NAK or NYET
+ TU_ASSERT(channel_xfer_start(dwc2, ch_id));
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+
+ if (is_done) {
+ xfer->xferred_bytes += xfer->fifo_bytes;
+ xfer->fifo_bytes = 0;
+ }
+
+ return is_done;
+}
+#endif
+
+#if CFG_TUH_DWC2_DMA_ENABLE
+static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ bool is_done = false;
+
+ // TU_LOG1("in hcint = %02lX\r\n", hcint);
+
+ if (hcint & HCINT_HALTED) {
+ if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
+ const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size;
+ const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
+ const uint16_t actual_len = edpt->buflen - remain_bytes;
+ xfer->xferred_bytes += actual_len;
+
+ is_done = true;
+
+ if (hcint & HCINT_STALL) {
+ xfer->result = XFER_RESULT_STALLED;
+ } else if (hcint & HCINT_BABBLE_ERR) {
+ xfer->result = XFER_RESULT_FAILED;
+ } else if (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) {
+ // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
+ is_done = false;
+ edpt->buffer += actual_len;
+ edpt->buflen -= actual_len;
+
+ channel->hcsplt_bm.split_compl = 0;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else {
+ xfer->result = XFER_RESULT_SUCCESS;
+ }
+
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ is_done = true;
+ xfer->result = XFER_RESULT_FAILED;
+ } else {
+ channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
+ channel->hcsplt_bm.split_compl = 0;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_NYET) {
+ // Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL
+ // OUT that started with a PING. The nyet takes precedence.
+ if (channel->hcsplt_bm.split_en) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ if (channel->hcsplt_bm.split_en) {
+ // start split is ACK --> do complete split
+ // TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe)
+ channel->hcsplt_bm.split_compl = 1;
+ if (edpt_is_periodic(channel->hcchar_bm.ep_type)) {
+ channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
+ }
+ channel_send_in_token(dwc2, channel);
+ }
+ } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
+ channel->hcsplt_bm.split_compl = 0; // restart with start-split
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ // retry start-split in next binterval
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
+ }
+
+ return is_done;
+}
+
+static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
+
+ bool is_done = false;
+
+ // TU_LOG1("out hcint = %02lX\r\n", hcint);
+
+ if (hcint & HCINT_HALTED) {
+ if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) {
+ is_done = true;
+ xfer->err_count = 0;
+ if (hcint & HCINT_XFER_COMPLETE) {
+ xfer->result = XFER_RESULT_SUCCESS;
+ xfer->xferred_bytes += edpt->buflen;
+ } else {
+ xfer->result = XFER_RESULT_STALLED;
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ }
+ channel->hcintmsk &= ~HCINT_ACK;
+ } else if (hcint & HCINT_XACT_ERR) {
+ if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) {
+ xfer->err_count = 0;
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ } else {
+ xfer->err_count++;
+ if (xfer->err_count >= HCD_XFER_ERROR_MAX) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ // clean up transfer so far and start again
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ channel_xfer_start(dwc2, ch_id);
+ }
+ }
+ } else if (hcint & HCINT_NYET) {
+ if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
+ // split not yet mean hub has no data, retry complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
+ // start split is ACK --> do complete split
+ channel->hcsplt_bm.split_compl = 1;
+ channel->hcchar |= HCCHAR_CHENA;
+ }
+ }
+ } else if (hcint & HCINT_ACK) {
+ xfer->err_count = 0;
+ channel->hcintmsk &= ~HCINT_ACK;
+ }
+
+ return is_done;
+}
+#endif
+
+static void handle_channel_irq(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const bool is_dma = dma_host_enabled(dwc2);
+ const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
+
+ for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
+ if (tu_bit_test(dwc2->haint, ch_id)) {
+ dwc2_channel_t* channel = &dwc2->channel[ch_id];
+ hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
+ TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
+ dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
+
+ const uint32_t hcint = channel->hcint;
+ channel->hcint = hcint; // clear interrupt
+
+ bool is_done = false;
+ if (is_dma) {
+ #if CFG_TUH_DWC2_DMA_ENABLE
+ if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ is_done = handle_channel_out_dma(dwc2, ch_id, hcint);
+ } else {
+ is_done = handle_channel_in_dma(dwc2, ch_id, hcint);
+ if (is_done && (channel->hcdma > xfer->xferred_bytes)) {
+ // hcdma is increased by word --> need to align4
+ hcd_dcache_invalidate((void*) tu_align4(channel->hcdma - xfer->xferred_bytes), xfer->xferred_bytes);
+ }
+ }
+ #endif
+ } else {
+ #if CFG_TUH_DWC2_SLAVE_ENABLE
+ if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
+ is_done = handle_channel_out_slave(dwc2, ch_id, hcint);
+ } else {
+ is_done = handle_channel_in_slave(dwc2, ch_id, hcint);
+ }
+ #endif
+ }
+
+ if (is_done) {
+ const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir);
+ hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr);
+ channel_dealloc(dwc2, ch_id);
+ }
+ }
+ }
+}
+
+// SOF is enabled for scheduled periodic transfer
+static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
+ (void) in_isr;
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+ bool more_isr = false;
+
+ // If highspeed then SOF is 125us, else 1ms
+ const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
+
+ for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
+ hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
+ if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
+ edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
+ if (edpt->uframe_countdown == 0) {
+ if (!edpt_xfer_kickoff(dwc2, ep_id)) {
+ edpt->uframe_countdown = ucount; // failed to start, try again next frame
+ }
+ }
+
+ more_isr = true;
+ }
+ }
+
+ return more_isr;
+}
+
+// Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit)
+static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
+ uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
+
+ const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
+ uint32_t phy_clock;
+
+ if (gusbcfg_bm.phy_sel) {
+ phy_clock = 48; // dedicated FS is 48Mhz
+ if (speed == TUSB_SPEED_LOW) {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
+ } else {
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
+ }
+ } else {
+ if (gusbcfg_bm.ulpi_utmi_sel) {
+ phy_clock = 60; // ULPI 8-bit is 60Mhz
+ } else {
+ // UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
+ phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60;
+
+ // Enable UTMI+ low power mode 48Mhz external clock if not highspeed
+ if (speed == TUSB_SPEED_HIGH) {
+ dwc2->gusbcfg &= ~GUSBCFG_PHYLPCS;
+ } else {
+ dwc2->gusbcfg |= GUSBCFG_PHYLPCS;
+ // may need to reset port
+ }
+ }
+ hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ;
+ }
+
+ dwc2->hcfg = hcfg;
+
+ uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk;
+ if (speed == TUSB_SPEED_HIGH) {
+ hfir |= 125*phy_clock;
+ } else {
+ hfir |= 1000*phy_clock;
+ }
+
+ dwc2->hfir = hfir;
+}
+
+/* Handle Host Port interrupt, possible source are:
+ - Connection Detection
+ - Enable Change
+ - Over Current Change
+*/
+static void handle_hprt_irq(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
+ const dwc2_hprt_t hprt_bm = dwc2->hprt_bm;
+
+ if (dwc2->hprt & HPRT_CONN_DETECT) {
+ // Port Connect Detect
+ hprt |= HPRT_CONN_DETECT;
+
+ if (hprt_bm.conn_status) {
+ hcd_event_device_attach(rhport, in_isr);
+ } else {
+ hcd_event_device_remove(rhport, in_isr);
+ }
+ }
+
+ if (dwc2->hprt & HPRT_ENABLE_CHANGE) {
+ // Port enable change
+ hprt |= HPRT_ENABLE_CHANGE;
+
+ if (hprt_bm.enable) {
+ // Port enable
+ const tusb_speed_t speed = hprt_speed_get(dwc2);
+ port0_enable(dwc2, speed);
+ } else {
+ // TU_ASSERT(false, );
+ }
+ }
+
+ dwc2->hprt = hprt; // clear interrupt
+}
+
+/* Interrupt Hierarchy
+ HCINTn HPRT
+ | |
+ HAINT.CHn |
+ | |
+ GINTSTS : HCInt | PrtInt | NPTxFEmp | PTxFEmpp | RXFLVL | SOF
+*/
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+ const uint32_t gintmsk = dwc2->gintmsk;
+ const uint32_t gintsts = dwc2->gintsts & gintmsk;
+
+ // TU_LOG1_HEX(gintsts);
+
+ if (gintsts & GINTSTS_CONIDSTSCHNG) {
+ // Connector ID status change
+ dwc2->gintsts = GINTSTS_CONIDSTSCHNG;
+
+ //if (dwc2->gotgctl)
+ // dwc2->hprt = HPRT_POWER; // power on port to turn on VBUS
+ //dwc2->gintmsk |= GINTMSK_PRTIM;
+ // TODO wait for SRP if OTG
+ }
+
+ if (gintsts & GINTSTS_SOF) {
+ const bool more_sof = handle_sof_irq(rhport, in_isr);
+ if (!more_sof) {
+ dwc2->gintmsk &= ~GINTSTS_SOF;
+ }
+ }
+
+ if (gintsts & GINTSTS_HPRTINT) {
+ // Host port interrupt: source is cleared in HPRT register
+ // TU_LOG1_HEX(dwc2->hprt);
+ handle_hprt_irq(rhport, in_isr);
+ }
+
+ if (gintsts & GINTSTS_HCINT) {
+ // Host Channel interrupt: source is cleared in HCINT register
+ // must be handled after TX FIFO empty
+ handle_channel_irq(rhport, in_isr);
+ }
+
+#if CFG_TUH_DWC2_SLAVE_ENABLE
+ // RxFIFO non-empty interrupt handling
+ if (gintsts & GINTSTS_RXFLVL) {
+ // RXFLVL bit is read-only
+ dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading
+
+ do {
+ handle_rxflvl_irq(rhport); // read all packets
+ } while(dwc2->gintsts & GINTSTS_RXFLVL);
+
+ dwc2->gintmsk |= GINTSTS_RXFLVL;
+ }
+
+ if (gintsts & GINTSTS_NPTX_FIFO_EMPTY) {
+ // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_nptxfe = handle_txfifo_empty(dwc2, false);
+ if (!more_nptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY;
+ }
+ }
+
+ if (gintsts & GINTSTS_PTX_FIFO_EMPTY) {
+ // PTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written
+ const bool more_ptxfe = handle_txfifo_empty(dwc2, true);
+ if (!more_ptxfe) {
+ // no more pending packet, disable interrupt
+ dwc2->gintmsk &= ~GINTSTS_PTX_FIFO_EMPTY;
+ }
+ }
+#endif
+}
+
+#endif
diff --git a/src/portable/synopsys/dwc2/hwcfg_list.md b/src/portable/synopsys/dwc2/hwcfg_list.md
deleted file mode 100644
index b5590da00..000000000
--- a/src/portable/synopsys/dwc2/hwcfg_list.md
+++ /dev/null
@@ -1,777 +0,0 @@
-# DWC2 Hardware Configuration Registers
-
-## Broadcom BCM2711 (Pi4)
-
-dwc2->guid = 2708A000
-dwc2->gsnpsid = 4F54280A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 228DDD50
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 1
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 7
-hw_cfg2->num_host_ch = 7
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 0
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = FF000E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 4080
-
-dwc2->ghwcfg4 = 1FF00020
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 0
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 0
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 15
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## EFM32GG FS
-
-dwc2->guid = 0
-dwc2->gsnpsid = 4F54330A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 228F5910
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 6
-hw_cfg2->num_host_ch = 13
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 0
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 1F204E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 1
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 498
-
-dwc2->ghwcfg4 = 1BF08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 13
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## ESP32-S2 Fullspeed
-
-dwc2->guid = 0
-dwc2->gsnpsid = 4F54400A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 224DD930
-hw_cfg2->op_mode = 2
-hw_cfg2->arch = 3
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 1
-hw_cfg2->fs_phy_type = 2
-hw_cfg2->num_dev_ep = 6
-hw_cfg2->num_host_ch = 9
-hw_cfg2->period_channel_support = 0
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 1
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 22
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = C804B5
-hw_cfg3->xfer_size_width = 10
-hw_cfg3->packet_size_width = 5
-hw_cfg3->otg_enable = 0
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 1
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 1
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 23130
-
-dwc2->ghwcfg4 = D3F0A030
-hw_cfg4->num_dev_period_in_ep = 10
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 0
-hw_cfg4->hibernation = 1
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 1
-hw_cfg4->acg_enable = 1
-hw_cfg4->utmi_phy_data_width = 1
-hw_cfg4->dev_ctrl_ep_num = 10
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 0
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 0
-hw_cfg4->dedicated_fifos = 0
-hw_cfg4->num_dev_in_eps = 13
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 1
-
-## STM32F407 and STM32F207
-
-STM32F407 and STM32F207 are exactly the same
-
-### STM32F407 Fullspeed
-
-dwc2->guid = 1200
-dwc2->gsnpsid = 4F54281A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229DCD20
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 3
-hw_cfg2->num_host_ch = 7
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 20001E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = FF08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 7
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-### STM32F407 Highspeed
-
-dwc2->guid = 1100
-dwc2->gsnpsid = 4F54281A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229ED590
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 2
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 5
-hw_cfg2->num_host_ch = 11
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 3F403E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 1
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 1012
-
-dwc2->ghwcfg4 = 17F00030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 0
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 11
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## STM32F411 Fullspeed
-
-dwc2->guid = 1200
-dwc2->gsnpsid = 4F54281A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229DCD20
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 3
-hw_cfg2->num_host_ch = 7
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 20001E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = FF08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 7
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## STM32F412 FS
-
-dwc2->guid = 2000
-dwc2->gsnpsid = 4F54320A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229ED520
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 5
-hw_cfg2->num_host_ch = 11
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 200D1E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = 17F08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 11
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## STM32F723
-
-### STM32F723 HighSpeed
-
-dwc2->guid = 3100
-dwc2->gsnpsid = 4F54330A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229FE1D0
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 3
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 8
-hw_cfg2->num_host_ch = 15
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 3EED2E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 1
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 1006
-
-dwc2->ghwcfg4 = 23F00030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 0
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 1
-hw_cfg4->dma_desc_enable = 1
-hw_cfg4->dma_dynamic = 0
-
-### STM32F723 Fullspeed
-
-dwc2->guid = 3000
-dwc2->gsnpsid = 4F54330A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229ED520
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 5
-hw_cfg2->num_host_ch = 11
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 200D1E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = 17F08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 11
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## STM32F767 FS
-
-dwc2->guid = 2000
-dwc2->gsnpsid = 4F54320A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229ED520
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 5
-hw_cfg2->num_host_ch = 11
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 200D1E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = 17F08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 11
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## STM32H743 (both cores HS)
-
-dwc2->guid = 2300
-dwc2->gsnpsid = 4F54330A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229FE190
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 2
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 8
-hw_cfg2->num_host_ch = 15
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 3B8D2E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 1
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 952
-
-dwc2->ghwcfg4 = E3F00030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 0
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 1
-hw_cfg4->dma_desc_enable = 1
-hw_cfg4->dma_dynamic = 1
-
-## STM32L476 FS
-
-dwc2->guid = 2000
-dwc2->gsnpsid = 4F54310A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 229ED520
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 5
-hw_cfg2->num_host_ch = 11
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 1
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 200D1E8
-hw_cfg3->xfer_size_width = 8
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 1
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 1
-hw_cfg3->lpm_mode = 1
-hw_cfg3->total_fifo_size = 512
-
-dwc2->ghwcfg4 = 17F08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 11
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## GD32VF103 Fullspeed
-
-dwc2->guid = 1000
-dwc2->gsnpsid = 0
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 0
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 0
-hw_cfg2->point2point = 0
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 0
-hw_cfg2->num_dev_ep = 0
-hw_cfg2->num_host_ch = 0
-hw_cfg2->period_channel_support = 0
-hw_cfg2->enable_dynamic_fifo = 0
-hw_cfg2->mul_cpu_int = 0
-hw_cfg2->nperiod_tx_q_depth = 0
-hw_cfg2->host_period_tx_q_depth = 0
-hw_cfg2->dev_token_q_depth = 0
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 0
-hw_cfg3->xfer_size_width = 0
-hw_cfg3->packet_size_width = 0
-hw_cfg3->otg_enable = 0
-hw_cfg3->i2c_enable = 0
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 0
-
-dwc2->ghwcfg4 = 0
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 0
-hw_cfg4->ahb_freq_min = 0
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 0
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 0
-hw_cfg4->vbus_valid_filter_enabled = 0
-hw_cfg4->a_valid_filter_enabled = 0
-hw_cfg4->b_valid_filter_enabled = 0
-hw_cfg4->dedicated_fifos = 0
-hw_cfg4->num_dev_in_eps = 0
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 0
-
-## XMC4500
-
-dwc2->guid = AEC000
-dwc2->gsnpsid = 4F54292A
-dwc2->ghwcfg1 = 0
-
-dwc2->ghwcfg2 = 228F5930
-hw_cfg2->op_mode = 0
-hw_cfg2->arch = 2
-hw_cfg2->point2point = 1
-hw_cfg2->hs_phy_type = 0
-hw_cfg2->fs_phy_type = 1
-hw_cfg2->num_dev_ep = 6
-hw_cfg2->num_host_ch = 13
-hw_cfg2->period_channel_support = 1
-hw_cfg2->enable_dynamic_fifo = 1
-hw_cfg2->mul_cpu_int = 0
-hw_cfg2->nperiod_tx_q_depth = 2
-hw_cfg2->host_period_tx_q_depth = 2
-hw_cfg2->dev_token_q_depth = 8
-hw_cfg2->otg_enable_ic_usb = 0
-
-dwc2->ghwcfg3 = 27A01E5
-hw_cfg3->xfer_size_width = 5
-hw_cfg3->packet_size_width = 6
-hw_cfg3->otg_enable = 1
-hw_cfg3->i2c_enable = 1
-hw_cfg3->vendor_ctrl_itf = 0
-hw_cfg3->optional_feature_removed = 0
-hw_cfg3->synch_reset = 0
-hw_cfg3->otg_adp_support = 0
-hw_cfg3->otg_enable_hsic = 0
-hw_cfg3->battery_charger_support = 0
-hw_cfg3->lpm_mode = 0
-hw_cfg3->total_fifo_size = 634
-
-dwc2->ghwcfg4 = DBF08030
-hw_cfg4->num_dev_period_in_ep = 0
-hw_cfg4->power_optimized = 1
-hw_cfg4->ahb_freq_min = 1
-hw_cfg4->hibernation = 0
-hw_cfg4->service_interval_mode = 0
-hw_cfg4->ipg_isoc_en = 0
-hw_cfg4->acg_enable = 0
-hw_cfg4->utmi_phy_data_width = 2
-hw_cfg4->dev_ctrl_ep_num = 0
-hw_cfg4->iddg_filter_enabled = 1
-hw_cfg4->vbus_valid_filter_enabled = 1
-hw_cfg4->a_valid_filter_enabled = 1
-hw_cfg4->b_valid_filter_enabled = 1
-hw_cfg4->dedicated_fifos = 1
-hw_cfg4->num_dev_in_eps = 13
-hw_cfg4->dma_desc_enable = 0
-hw_cfg4->dma_dynamic = 1
diff --git a/src/portable/template/dcd_template.c b/src/portable/template/dcd_template.c
index 26b5dce9d..3738ac0cb 100644
--- a/src/portable/template/dcd_template.c
+++ b/src/portable/template/dcd_template.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2018, hathach (tinyusb.org)
@@ -26,7 +26,7 @@
#include "tusb_option.h"
-#if CFG_TUSB_MCU == OPT_MCU_NONE
+#if CFG_TUD_ENABLED && CFG_TUSB_MCU == OPT_MCU_NONE
#include "device/dcd.h"
@@ -40,54 +40,45 @@
*------------------------------------------------------------------*/
// Initialize controller to device mode
-void dcd_init (uint8_t rhport)
-{
- (void) rhport;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport; (void) rh_init;
+ return true;
}
// Enable device interrupt
-void dcd_int_enable (uint8_t rhport)
-{
+void dcd_int_enable (uint8_t rhport) {
(void) rhport;
}
// Disable device interrupt
-void dcd_int_disable (uint8_t rhport)
-{
+void dcd_int_disable (uint8_t rhport) {
(void) rhport;
}
// Receive Set Address request, mcu port must also include status IN response
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr) {
(void) rhport;
(void) dev_addr;
}
// Wake up host
-void dcd_remote_wakeup (uint8_t rhport)
-{
+void dcd_remote_wakeup (uint8_t rhport) {
(void) rhport;
}
// Connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
(void) rhport;
}
// Disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
(void) rhport;
}
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
(void) rhport;
(void) en;
-
- // TODO implement later
}
//--------------------------------------------------------------------+
@@ -95,21 +86,34 @@ void dcd_sof_enable(uint8_t rhport, bool en)
//--------------------------------------------------------------------+
// Configure endpoint's registers according to descriptor
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
-{
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) {
(void) rhport;
(void) ep_desc;
return false;
}
-void dcd_edpt_close_all (uint8_t rhport)
-{
+// Allocate packet buffer used by ISO endpoints
+// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
+ (void) rhport;
+ (void) ep_addr;
+ (void) largest_packet_size;
+ return false;
+}
+
+// Configure and enable an ISO endpoint according to descriptor
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep) {
+ (void) rhport;
+ (void) desc_ep;
+ return false;
+}
+
+void dcd_edpt_close_all (uint8_t rhport) {
(void) rhport;
}
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) {
(void) rhport;
(void) ep_addr;
(void) buffer;
@@ -118,8 +122,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
+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;
@@ -128,15 +131,13 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16
}
// Stall endpoint
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
(void) ep_addr;
}
// clear stall, data toggle is also reset to DATA0
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
(void) ep_addr;
}
diff --git a/src/portable/template/hcd_template.c b/src/portable/template/hcd_template.c
new file mode 100644
index 000000000..d8bca196f
--- /dev/null
+++ b/src/portable/template/hcd_template.c
@@ -0,0 +1,163 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && CFG_TUSB_MCU == OPT_MCU_NONE
+
+#include "host/hcd.h"
+
+//--------------------------------------------------------------------+
+// Controller API
+//--------------------------------------------------------------------+
+
+// optional hcd configuration, called by tuh_configure()
+bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
+ (void) rhport;
+ (void) cfg_id;
+ (void) cfg_param;
+
+ return false;
+}
+
+// Initialize controller to host mode
+bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
+ return false;
+}
+
+// Interrupt Handler
+void hcd_int_handler(uint8_t rhport, bool in_isr) {
+ (void) rhport;
+ (void) in_isr;
+}
+
+// Enable USB interrupt
+void hcd_int_enable (uint8_t rhport) {
+ (void) rhport;
+}
+
+// Disable USB interrupt
+void hcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+}
+
+// Get frame number (1ms)
+uint32_t hcd_frame_number(uint8_t rhport) {
+ (void) rhport;
+
+ return 0;
+}
+
+//--------------------------------------------------------------------+
+// Port API
+//--------------------------------------------------------------------+
+
+// Get the current connect status of roothub port
+bool hcd_port_connect_status(uint8_t rhport) {
+ (void) rhport;
+
+ return false;
+}
+
+// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete.
+// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence.
+void hcd_port_reset(uint8_t rhport) {
+ (void) rhport;
+}
+
+// Complete bus reset sequence, may be required by some controllers
+void hcd_port_reset_end(uint8_t rhport) {
+ (void) rhport;
+}
+
+// Get port link speed
+tusb_speed_t hcd_port_speed_get(uint8_t rhport) {
+ (void) rhport;
+
+ return TUSB_SPEED_FULL;
+}
+
+// HCD closes all opened endpoints belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
+ (void) rhport;
+ (void) dev_addr;
+}
+
+//--------------------------------------------------------------------+
+// Endpoints API
+//--------------------------------------------------------------------+
+
+// Open an endpoint
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_desc;
+
+ return false;
+}
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+ (void) buffer;
+ (void) buflen;
+
+ return false;
+}
+
+// Abort a queued transfer. Note: it can only abort transfer that has not been started
+// Return true if a queued transfer is aborted, false if there is no transfer to abort
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) setup_packet;
+
+ return false;
+}
+
+// clear stall, data toggle is also reset to DATA0
+bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
+ (void) rhport;
+ (void) dev_addr;
+ (void) ep_addr;
+
+ return false;
+}
+
+#endif
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index b4dfda575..3752ae251 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -94,7 +94,8 @@ static void bus_reset(void)
USBOEPCNT_0 &= ~NAK;
USBIEPCNT_0 &= ~NAK;
- USBCTL |= FEN; // Enable responding to packets.
+ // Enable responding to packets.
+ USBCTL |= FEN;
// Dedicated buffers in hardware for SETUP and EP0, no setup needed.
// Now safe to respond to SETUP packets.
@@ -103,13 +104,34 @@ static void bus_reset(void)
USBKEYPID = 0;
}
+// Controls reset behavior of the USB module on receipt of a bus reset event.
+// - enable: When true, bus reset events will cause a reset the USB module.
+static void enable_functional_reset(const bool enable)
+{
+ // Check whether or not the USB configuration registers were
+ // locked prior to this function being called so that, if
+ // necessary, the lock state can be restored on exit.
+ bool unlocked = (USBKEYPID == 0xA528) ? true : false;
+
+ if(!unlocked) USBKEYPID = USBKEY;
+
+ if(enable)
+ {
+ USBCTL |= FRSTE;
+ }
+ else
+ {
+ USBCTL &= ~FRSTE;
+ }
+
+ if(!unlocked) USBKEYPID = 0;
+}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
-void dcd_init (uint8_t rhport)
-{
- (void) rhport;
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport; (void) rh_init;
USBKEYPID = USBKEY;
@@ -131,13 +153,18 @@ void dcd_init (uint8_t rhport)
USBVECINT = 0;
- // Enable reset and wait for it before continuing.
- USBIE |= RSTRIE;
-
- // Enable pullup.
- USBCNF |= PUR_EN;
+ if(USBPWRCTL & USBBGVBV) {// Bus power detected?
+ USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
+ USBIE |= RSTRIE; // Enable reset and wait for it before continuing.
+ USBCNF |= PUR_EN; // Enable pullup.
+ } else {
+ USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
+ USBCNF &= ~USB_EN; // Disable USB module until bus power is detected.
+ }
USBKEYPID = 0;
+
+ return true;
}
// There is no "USB peripheral interrupt disable" bit on MSP430, so we have
@@ -306,6 +333,11 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
return true;
}
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport; (void) ep_addr;
+ // TODO implement dcd_edpt_close()
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
@@ -610,14 +642,80 @@ static void handle_setup_packet(void)
_setup_packet[i] = setup_buf[i];
}
- // Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we
- // have a SETUP packet, force NAKs until tinyusb can handle the SETUP
- // packet and prepare for a new xfer.
+ // Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer.
USBIEPCNT_0 |= NAK;
USBOEPCNT_0 |= NAK;
+
+ // Clear SETUPIFG to avoid handling in the USBVECINT switch statement.
+ // When handled there the NAKs applied to the endpoints above are
+ // cleared by hardware and the host will receive stale/duplicate data.
+ //
+ // Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide:
+ //
+ // "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on
+ // input endpoint 0 and output endpoint 0 is also cleared."
+ USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+ USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+ USBIFG &= ~SETUPIFG;
+ USBIEPCNF_0 |= UBME; // Errata USB10 workaround.
+ USBOEPCNF_0 |= UBME; // Errata USB10 workaround.
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.
+
+ USBKEYPID = USBKEY;
+
+ if(USBPWRCTL & USBBGVBV) { // Event caused by application of bus power.
+ USBPWRCTL |= VBOFFIE; // Enable bus-power-removed interrupt.
+ USBPLLDIVB = USBPLLDIVB; // For some reason the PLL will *NOT* lock unless the divider
+ // register is re-written. The assumption here is that this
+ // register was already properly configured during board-level
+ // initialization.
+ USBPLLCTL |= (UPLLEN | UPFDEN); // Enable the PLL.
+
+ uint16_t attempts = 0;
+ do { // Poll the PLL, checking for a successful lock.
+ USBPLLIR = 0;
+ tu_delay(1);
+ attempts++;
+ } while ((attempts < 10) && (USBPLLIR != 0));
+
+ // A successful lock is indicated by all PLL-related interrupt flags being cleared.
+ if(!USBPLLIR) {
+ const tusb_rhport_init_t rhport_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_FULL
+ };
+ dcd_init(0, &rhport_init); // Re-initialize the USB module.
+ }
+ } else { // Event caused by removal of bus power.
+ USBPWRCTL |= VBONIE; // Enable bus-power-applied interrupt.
+ USBPLLCTL &= ~(UPLLEN | UPFDEN); // Disable the PLL.
+ USBCNF = 0; // Disable the USB module.
+ dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
+ }
+
+ USBKEYPID = 0;
+}
+
void dcd_int_handler(uint8_t rhport)
{
(void) rhport;
@@ -628,6 +726,7 @@ void dcd_int_handler(uint8_t rhport)
if(setup_status)
{
+ enable_functional_reset(true);
handle_setup_packet();
}
@@ -646,11 +745,32 @@ void dcd_int_handler(uint8_t rhport)
switch(curr_vector)
{
+ case USBVECINT_NONE:
+ break;
+
case USBVECINT_RSTR:
+ enable_functional_reset(false); // Errata USB4 workaround.
bus_reset();
dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
break;
+ case USBVECINT_PWR_VBUSOn:
+ case USBVECINT_PWR_VBUSOff: {
+ USBKEYPID = USBKEY;
+ // Prevent (possibly) unstable power from generating spurious interrupts.
+ USBPWRCTL &= ~(VBONIE | VBOFFIE);
+ USBKEYPID = 0;
+
+ dcd_event_t event;
+
+ event.rhport = 0;
+ event.event_id = USBD_EVENT_FUNC_CALL;
+ event.func_call.func = handle_bus_power_event;
+
+ dcd_event_handler(&event, true);
+ }
+ break;
+
// Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
// is received. At this point, even though the hardware is no longer
// forcing NAKs, the EP0 NAK bits should still be set to avoid
@@ -675,10 +795,12 @@ void dcd_int_handler(uint8_t rhport)
break;
case USBVECINT_INPUT_ENDPOINT0:
+ enable_functional_reset(true);
transmit_packet(0);
break;
case USBVECINT_OUTPUT_ENDPOINT0:
+ enable_functional_reset(true);
receive_packet(0);
break;
@@ -710,7 +832,6 @@ void dcd_int_handler(uint8_t rhport)
default:
while(true);
- break;
}
}
diff --git a/src/portable/valentyusb/eptri/dcd_eptri.c b/src/portable/valentyusb/eptri/dcd_eptri.c
index e368b5f39..a03c94558 100644
--- a/src/portable/valentyusb/eptri/dcd_eptri.c
+++ b/src/portable/valentyusb/eptri/dcd_eptri.c
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -336,9 +336,9 @@ static void dcd_reset(void)
}
// Initializes the USB peripheral for device mode and enables it.
-void dcd_init(uint8_t rhport)
-{
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rhport;
+ (void) rh_init;
usb_pullup_out_write(0);
@@ -352,6 +352,8 @@ void dcd_init(uint8_t rhport)
// Turn on the external pullup
usb_pullup_out_write(1);
+
+ return true;
}
// Enables or disables the USB device interrupt(s). May be used to
@@ -436,6 +438,11 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
return true;
}
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport; (void) ep_addr;
+ // TODO implement dcd_edpt_close()
+}
+
void dcd_edpt_close_all (uint8_t rhport)
{
(void) rhport;
@@ -465,7 +472,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
enable = 1;
usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
}
- // IN endpoints will get unstalled when more data is written.
+ // IN endpoints will get un-stalled when more data is written.
}
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
diff --git a/src/portable/valentyusb/eptri/dcd_eptri.h b/src/portable/valentyusb/eptri/dcd_eptri.h
index 0fa6ecc64..d67635d7c 100644
--- a/src/portable/valentyusb/eptri/dcd_eptri.h
+++ b/src/portable/valentyusb/eptri/dcd_eptri.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h
new file mode 100644
index 000000000..68be64f5e
--- /dev/null
+++ b/src/portable/wch/ch32_usbfs_reg.h
@@ -0,0 +1,175 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 USB_CH32_USBFS_REG_H
+#define USB_CH32_USBFS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V103
+ #include <ch32v10x.h>
+ typedef struct
+ {
+ __IO uint8_t BASE_CTRL;
+ __IO uint8_t UDEV_CTRL;
+ __IO uint8_t INT_EN;
+ __IO uint8_t DEV_ADDR;
+ __IO uint8_t Reserve0;
+ __IO uint8_t MIS_ST;
+ __IO uint8_t INT_FG;
+ __IO uint8_t INT_ST;
+ __IO uint32_t RX_LEN;
+ __IO uint8_t UEP4_1_MOD;
+ __IO uint8_t UEP2_3_MOD;
+ __IO uint8_t UEP5_6_MOD;
+ __IO uint8_t UEP7_MOD;
+ __IO uint32_t UEP0_DMA;
+ __IO uint32_t UEP1_DMA;
+ __IO uint32_t UEP2_DMA;
+ __IO uint32_t UEP3_DMA;
+ __IO uint32_t UEP4_DMA;
+ __IO uint32_t UEP5_DMA;
+ __IO uint32_t UEP6_DMA;
+ __IO uint32_t UEP7_DMA;
+ __IO uint16_t UEP0_TX_LEN;
+ __IO uint8_t UEP0_TX_CTRL;
+ __IO uint8_t UEP0_RX_CTRL;
+ __IO uint16_t UEP1_TX_LEN;
+ __IO uint8_t UEP1_TX_CTRL;
+ __IO uint8_t UEP1_RX_CTRL;
+ __IO uint16_t UEP2_TX_LEN;
+ __IO uint8_t UEP2_TX_CTRL;
+ __IO uint8_t UEP2_RX_CTRL;
+ __IO uint16_t UEP3_TX_LEN;
+ __IO uint8_t UEP3_TX_CTRL;
+ __IO uint8_t UEP3_RX_CTRL;
+ __IO uint16_t UEP4_TX_LEN;
+ __IO uint8_t UEP4_TX_CTRL;
+ __IO uint8_t UEP4_RX_CTRL;
+ __IO uint16_t UEP5_TX_LEN;
+ __IO uint8_t UEP5_TX_CTRL;
+ __IO uint8_t UEP5_RX_CTRL;
+ __IO uint16_t UEP6_TX_LEN;
+ __IO uint8_t UEP6_TX_CTRL;
+ __IO uint8_t UEP6_RX_CTRL;
+ __IO uint16_t UEP7_TX_LEN;
+ __IO uint8_t UEP7_TX_CTRL;
+ __IO uint8_t UEP7_RX_CTRL;
+ __IO uint32_t Reserve1;
+ __IO uint32_t OTG_CR;
+ __IO uint32_t OTG_SR;
+ } USBOTG_FS_TypeDef;
+
+ #define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400)
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+ #include <ch32v20x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V307
+ #include <ch32v30x.h>
+ #define USBHD_IRQn OTG_FS_IRQn
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// CTRL
+#define USBFS_CTRL_DMA_EN (1 << 0)
+#define USBFS_CTRL_CLR_ALL (1 << 1)
+#define USBFS_CTRL_RESET_SIE (1 << 2)
+#define USBFS_CTRL_INT_BUSY (1 << 3)
+#define USBFS_CTRL_SYS_CTRL (1 << 4)
+#define USBFS_CTRL_DEV_PUEN (1 << 5)
+#define USBFS_CTRL_LOW_SPEED (1 << 6)
+#define USBFS_CTRL_HOST_MODE (1 << 7)
+
+// INT_EN
+#define USBFS_INT_EN_BUS_RST (1 << 0)
+#define USBFS_INT_EN_DETECT (1 << 0)
+#define USBFS_INT_EN_TRANSFER (1 << 1)
+#define USBFS_INT_EN_SUSPEND (1 << 2)
+#define USBFS_INT_EN_HST_SOF (1 << 3)
+#define USBFS_INT_EN_FIFO_OV (1 << 4)
+#define USBFS_INT_EN_DEV_NAK (1 << 6)
+#define USBFS_INT_EN_DEV_SOF (1 << 7)
+
+// INT_FG
+#define USBFS_INT_FG_BUS_RST (1 << 0)
+#define USBFS_INT_FG_DETECT (1 << 0)
+#define USBFS_INT_FG_TRANSFER (1 << 1)
+#define USBFS_INT_FG_SUSPEND (1 << 2)
+#define USBFS_INT_FG_HST_SOF (1 << 3)
+#define USBFS_INT_FG_FIFO_OV (1 << 4)
+#define USBFS_INT_FG_SIE_FREE (1 << 5)
+#define USBFS_INT_FG_TOG_OK (1 << 6)
+#define USBFS_INT_FG_IS_NAK (1 << 7)
+
+// INT_ST
+#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F)
+#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03)
+
+// UDEV_CTRL
+#define USBFS_UDEV_CTRL_PORT_EN (1 << 0)
+#define USBFS_UDEV_CTRL_GP_BIT (1 << 1)
+#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2)
+#define USBFS_UDEV_CTRL_DM_PIN (1 << 4)
+#define USBFS_UDEV_CTRL_DP_PIN (1 << 5)
+#define USBFS_UDEV_CTRL_PD_DIS (1 << 7)
+
+// TX_CTRL
+#define USBFS_EP_T_RES_MASK (3 << 0)
+#define USBFS_EP_T_TOG (1 << 2)
+#define USBFS_EP_T_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_T_RES_ACK (0 << 0)
+#define USBFS_EP_T_RES_NYET (1 << 0)
+#define USBFS_EP_T_RES_NAK (2 << 0)
+#define USBFS_EP_T_RES_STALL (3 << 0)
+
+// RX_CTRL
+#define USBFS_EP_R_RES_MASK (3 << 0)
+#define USBFS_EP_R_TOG (1 << 2)
+#define USBFS_EP_R_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_R_RES_ACK (0 << 0)
+#define USBFS_EP_R_RES_NYET (1 << 0)
+#define USBFS_EP_R_RES_NAK (2 << 0)
+#define USBFS_EP_R_RES_STALL (3 << 0)
+
+// token PID
+#define PID_OUT 0
+#define PID_SOF 1
+#define PID_IN 2
+#define PID_SETUP 3
+
+#endif // USB_CH32_USBFS_REG_H
diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h
new file mode 100644
index 000000000..87300b497
--- /dev/null
+++ b/src/portable/wch/ch32_usbhs_reg.h
@@ -0,0 +1,395 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 USB_CH32_USBHS_REG_H
+#define USB_CH32_USBHS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32V307
+ #include <ch32v30x.h>
+#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X
+ #include <ch32f20x.h>
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+
+/******************* GLOBAL ******************/
+
+// USB CONTROL
+#define USBHS_CONTROL_OFFSET 0x00
+#define USBHS_DMA_EN (1 << 0)
+#define USBHS_ALL_CLR (1 << 1)
+#define USBHS_FORCE_RST (1 << 2)
+#define USBHS_INT_BUSY_EN (1 << 3)
+#define USBHS_DEV_PU_EN (1 << 4)
+#define USBHS_SPEED_MASK (3 << 5)
+#define USBHS_FULL_SPEED (0 << 5)
+#define USBHS_HIGH_SPEED (1 << 5)
+#define USBHS_LOW_SPEED (2 << 5)
+#define USBHS_HOST_MODE (1 << 7)
+
+// USB_INT_EN
+#define USBHS_INT_EN_OFFSET 0x02
+#define USBHS_BUS_RST_EN (1 << 0)
+#define USBHS_DETECT_EN (1 << 0)
+#define USBHS_TRANSFER_EN (1 << 1)
+#define USBHS_SUSPEND_EN (1 << 2)
+#define USBHS_SOF_ACT_EN (1 << 3)
+#define USBHS_FIFO_OV_EN (1 << 4)
+#define USBHS_SETUP_ACT_EN (1 << 5)
+#define USBHS_ISO_ACT_EN (1 << 6)
+#define USBHS_DEV_NAK_EN (1 << 7)
+
+// USB DEV AD
+#define USBHS_DEV_AD_OFFSET 0x03
+
+// USB FRAME_NO
+#define USBHS_FRAME_NO_OFFSET 0x04
+#define USBHS_FRAME_NO_NUM_MASK (0x7FF)
+#define USBHS_FRAME_NO_MICROFRAME_SHIFT (11)
+#define USBHS_FRAME_NO_MICROFRAME_MASK (0x7 << USBHS_FRAME_NO_MICROFRAME_SHIFT)
+
+// USB SUSPEND
+#define USBHS_SUSPEND_OFFSET 0x06
+#define USBHS_DEV_REMOTE_WAKEUP (1 << 2)
+#define USBHS_LINESTATE_MASK (2 << 4) /* Read Only */
+
+// RESERVED0
+
+// USB SPEED TYPE
+#define USBHS_SPEED_TYPE_OFFSET 0x08
+#define USBHS_SPEED_TYPE_MASK 0x03
+#define USBHS_SPEED_TYPE_FULL 0
+#define USBHS_SPEED_TYPE_HIGH 1
+#define USBHS_SPEED_TYPE_LOW 2
+
+// USB_MIS_ST
+#define USBHS_MIS_ST_OFFSET 0x09
+#define USBHS_SPLIT_CAN (1 << 0)
+#define USBHS_ATTACH (1 << 1)
+#define USBHS_SUSPEND (1 << 2)
+#define USBHS_BUS_RESET (1 << 3)
+#define USBHS_R_FIFO_RDY (1 << 4)
+#define USBHS_SIE_FREE (1 << 5)
+#define USBHS_SOF_ACT (1 << 6)
+#define USBHS_SOF_PRES (1 << 7)
+
+// INT_FLAG
+#define USBHS_INT_FLAG_OFFSET 0x0A
+#define USBHS_BUS_RST_FLAG (1 << 0)
+#define USBHS_DETECT_FLAG (1 << 0)
+#define USBHS_TRANSFER_FLAG (1 << 1)
+#define USBHS_SUSPEND_FLAG (1 << 2)
+#define USBHS_HST_SOF_FLAG (1 << 3)
+#define USBHS_FIFO_OV_FLAG (1 << 4)
+#define USBHS_SETUP_FLAG (1 << 5)
+#define USBHS_ISO_ACT_FLAG (1 << 6)
+
+// INT_ST
+#define USBHS_INT_ST_OFFSET 0x0B
+#define USBHS_DEV_UIS_IS_NAK (1 << 7)
+#define USBHS_DEV_UIS_TOG_OK (1 << 6)
+#define MASK_UIS_TOKEN (3 << 4)
+#define USBHS_TOKEN_PID_OUT (0 << 4)
+#define USBHS_TOKEN_PID_SOF (1 << 4)
+#define USBHS_TOKEN_PID_IN (2 << 4)
+#define USBHS_TOKEN_PID_SETUP (3 << 4)
+#define MASK_UIS_ENDP (0x0F)
+#define MASK_UIS_H_RES (0x0F)
+
+#define USBHS_TOGGLE_OK (0x40)
+#define USBHS_HOST_RES (0x0f)
+
+//USB_RX_LEN
+#define USBHS_RX_LEN_OFFSET 0x0C
+/******************* DEVICE ******************/
+
+//UEP_CONFIG
+#define USBHS_UEP_CONFIG_OFFSET 0x10
+#define USBHS_EP0_T_EN (1 << 0)
+#define USBHS_EP0_R_EN (1 << 16)
+
+#define USBHS_EP1_T_EN (1 << 1)
+#define USBHS_EP1_R_EN (1 << 17)
+
+#define USBHS_EP2_T_EN (1 << 2)
+#define USBHS_EP2_R_EN (1 << 18)
+
+#define USBHS_EP3_T_EN (1 << 3)
+#define USBHS_EP3_R_EN (1 << 19)
+
+#define USBHS_EP4_T_EN (1 << 4)
+#define USBHS_EP4_R_EN (1 << 20)
+
+#define USBHS_EP5_T_EN (1 << 5)
+#define USBHS_EP5_R_EN (1 << 21)
+
+#define USBHS_EP6_T_EN (1 << 6)
+#define USBHS_EP6_R_EN (1 << 22)
+
+#define USBHS_EP7_T_EN (1 << 7)
+#define USBHS_EP7_R_EN (1 << 23)
+
+#define USBHS_EP8_T_EN (1 << 8)
+#define USBHS_EP8_R_EN (1 << 24)
+
+#define USBHS_EP9_T_EN (1 << 9)
+#define USBHS_EP9_R_EN (1 << 25)
+
+#define USBHS_EP10_T_EN (1 << 10)
+#define USBHS_EP10_R_EN (1 << 26)
+
+#define USBHS_EP11_T_EN (1 << 11)
+#define USBHS_EP11_R_EN (1 << 27)
+
+#define USBHS_EP12_T_EN (1 << 12)
+#define USBHS_EP12_R_EN (1 << 28)
+
+#define USBHS_EP13_T_EN (1 << 13)
+#define USBHS_EP13_R_EN (1 << 29)
+
+#define USBHS_EP14_T_EN (1 << 14)
+#define USBHS_EP14_R_EN (1 << 30)
+
+#define USBHS_EP15_T_EN (1 << 15)
+#define USBHS_EP15_R_EN (1 << 31)
+
+//UEP_TYPE
+#define USBHS_UEP_TYPE_OFFSET 0x14
+#define USBHS_EP0_T_TYP (1 << 0)
+#define USBHS_EP0_R_TYP (1 << 16)
+
+#define USBHS_EP1_T_TYP (1 << 1)
+#define USBHS_EP1_R_TYP (1 << 17)
+
+#define USBHS_EP2_T_TYP (1 << 2)
+#define USBHS_EP2_R_TYP (1 << 18)
+
+#define USBHS_EP3_T_TYP (1 << 3)
+#define USBHS_EP3_R_TYP (1 << 19)
+
+#define USBHS_EP4_T_TYP (1 << 4)
+#define USBHS_EP4_R_TYP (1 << 20)
+
+#define USBHS_EP5_T_TYP (1 << 5)
+#define USBHS_EP5_R_TYP (1 << 21)
+
+#define USBHS_EP6_T_TYP (1 << 6)
+#define USBHS_EP6_R_TYP (1 << 22)
+
+#define USBHS_EP7_T_TYP (1 << 7)
+#define USBHS_EP7_R_TYP (1 << 23)
+
+#define USBHS_EP8_T_TYP (1 << 8)
+#define USBHS_EP8_R_TYP (1 << 24)
+
+#define USBHS_EP9_T_TYP (1 << 8)
+#define USBHS_EP9_R_TYP (1 << 25)
+
+#define USBHS_EP10_T_TYP (1 << 10)
+#define USBHS_EP10_R_TYP (1 << 26)
+
+#define USBHS_EP11_T_TYP (1 << 11)
+#define USBHS_EP11_R_TYP (1 << 27)
+
+#define USBHS_EP12_T_TYP (1 << 12)
+#define USBHS_EP12_R_TYP (1 << 28)
+
+#define USBHS_EP13_T_TYP (1 << 13)
+#define USBHS_EP13_R_TYP (1 << 29)
+
+#define USBHS_EP14_T_TYP (1 << 14)
+#define USBHS_EP14_R_TYP (1 << 30)
+
+#define USBHS_EP15_T_TYP (1 << 15)
+#define USBHS_EP15_R_TYP (1 << 31)
+
+/* BUF_MOD UEP1~15 */
+#define USBHS_BUF_MOD_OFFSET 0x18
+#define USBHS_EP0_BUF_MOD (1 << 0)
+#define USBHS_EP0_ISO_BUF_MOD (1 << 16)
+
+#define USBHS_EP1_BUF_MOD (1 << 1)
+#define USBHS_EP1_ISO_BUF_MOD (1 << 17)
+
+#define USBHS_EP2_BUF_MOD (1 << 2)
+#define USBHS_EP2_ISO_BUF_MOD (1 << 18)
+
+#define USBHS_EP3_BUF_MOD (1 << 3)
+#define USBHS_EP3_ISO_BUF_MOD (1 << 19)
+
+#define USBHS_EP4_BUF_MOD (1 << 4)
+#define USBHS_EP4_ISO_BUF_MOD (1 << 20)
+
+#define USBHS_EP5_BUF_MOD (1 << 5)
+#define USBHS_EP5_ISO_BUF_MOD (1 << 21)
+
+#define USBHS_EP6_BUF_MOD (1 << 6)
+#define USBHS_EP6_ISO_BUF_MOD (1 << 22)
+
+#define USBHS_EP7_BUF_MOD (1 << 7)
+#define USBHS_EP7_ISO_BUF_MOD (1 << 23)
+
+#define USBHS_EP8_BUF_MOD (1 << 8)
+#define USBHS_EP8_ISO_BUF_MOD (1 << 24)
+
+#define USBHS_EP9_BUF_MOD (1 << 9)
+#define USBHS_EP9_ISO_BUF_MOD (1 << 25)
+
+#define USBHS_EP10_BUF_MOD (1 << 10)
+#define USBHS_EP10_ISO_BUF_MOD (1 << 26)
+
+#define USBHS_EP11_BUF_MOD (1 << 11)
+#define USBHS_EP11_ISO_BUF_MOD (1 << 27)
+
+#define USBHS_EP12_BUF_MOD (1 << 12)
+#define USBHS_EP12_ISO_BUF_MOD (1 << 28)
+
+#define USBHS_EP13_BUF_MOD (1 << 13)
+#define USBHS_EP13_ISO_BUF_MOD (1 << 29)
+
+#define USBHS_EP14_BUF_MOD (1 << 14)
+#define USBHS_EP14_ISO_BUF_MOD (1 << 30)
+
+#define USBHS_EP15_BUF_MOD (1 << 15)
+#define USBHS_EP15_ISO_BUF_MOD (1 << 31)
+//USBHS_EPn_T_EN USBHS_EPn_R_EN USBHS_EPn_BUF_MOD Description: Arrange from low to high with UEPn_DMA as the starting address
+// 0 0 x The endpoint is disabled and the UEPn_*_DMA buffers are not used.
+// 1 0 0 The first address of the receive (OUT) buffer is UEPn_RX_DMA
+// 1 0 1 RB_UEPn_RX_TOG[0]=0, use buffer UEPn_RX_DMA RB_UEPn_RX_TOG[0]=1, use buffer UEPn_TX_DMA
+// 0 1 0 The first address of the transmit (IN) buffer is UEPn_TX_DMA.
+// 0 1 1 RB_UEPn_TX_TOG[0]=0, use buffer UEPn_TX_DMA RB_UEPn_TX_TOG[0]=1, use buffer UEPn_RX_DMA
+
+/* USB0_DMA */
+#define USBHS_UEP0_DMA_OFFSET(n) (0x1C) // endpoint 0 DMA buffer address
+
+/* USBX_RX_DMA */
+#define USBHS_UEPx_RX_DMA_OFFSET(n) (0x1C + 4 * (n)) // endpoint x DMA buffer address
+
+#define USBHS_UEPx_TX_DMA_OFFSET(n) (0x58 + 4 * (n)) // endpoint x DMA buffer address
+
+#define USBHS_UEPx_MAX_LEN_OFFSET(n) (0x98 + 4 * (n)) // endpoint x DMA buffer address
+
+#define USBHS_UEPx_T_LEN_OFFSET(n) (0xD8 + 4 * (n)) // endpoint x DMA buffer address
+#define USBHS_UEPx_TX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 2) // endpoint x DMA buffer address
+#define USBHS_UEPx_RX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 3) // endpoint x DMA buffer address
+
+// UEPn_T_LEN
+#define USBHS_EP_T_LEN_MASK (0x7FF)
+
+//UEPn_TX_CTRL
+#define USBHS_EP_T_RES_MASK (3 << 0)
+#define USBHS_EP_T_RES_ACK (0 << 0)
+#define USBHS_EP_T_RES_NYET (1 << 0)
+#define USBHS_EP_T_RES_NAK (2 << 0)
+#define USBHS_EP_T_RES_STALL (3 << 0)
+
+#define USBHS_EP_T_TOG_MASK (3 << 3)
+#define USBHS_EP_T_TOG_0 (0 << 3)
+#define USBHS_EP_T_TOG_1 (1 << 3)
+#define USBHS_EP_T_TOG_2 (2 << 3)
+#define USBHS_EP_T_TOG_M (3 << 3)
+
+#define USBHS_EP_T_AUTOTOG (1 << 5)
+
+//UEPn_RX_CTRL
+#define USBHS_EP_R_RES_MASK (3 << 0)
+#define USBHS_EP_R_RES_ACK (0 << 0)
+#define USBHS_EP_R_RES_NYET (1 << 0)
+#define USBHS_EP_R_RES_NAK (2 << 0)
+#define USBHS_EP_R_RES_STALL (3 << 0)
+
+#define USBHS_EP_R_TOG_MASK (3 << 3)
+#define USBHS_EP_R_TOG_0 (0 << 3)
+#define USBHS_EP_R_TOG_1 (1 << 3)
+#define USBHS_EP_R_TOG_2 (2 << 3)
+#define USBHS_EP_R_TOG_M (3 << 3)
+
+#define USBHS_EP_R_AUTOTOG (1 << 5)
+
+#define USBHS_TOG_MATCH (1 << 6)
+
+/******************* HOST ******************/
+// USB HOST_CTRL
+#define USBHS_SEND_BUS_RESET (1 << 0)
+#define USBHS_SEND_BUS_SUSPEND (1 << 1)
+#define USBHS_SEND_BUS_RESUME (1 << 2)
+#define USBHS_REMOTE_WAKE (1 << 3)
+#define USBHS_PHY_SUSPENDM (1 << 4)
+#define USBHS_UH_SOFT_FREE (1 << 6)
+#define USBHS_SEND_SOF_EN (1 << 7)
+
+//UH_CONFIG
+#define USBHS_HOST_TX_EN (1 << 3)
+#define USBHS_HOST_RX_EN (1 << 18)
+
+// HOST_EP_TYPE
+#define USBHS_ENDP_TX_ISO (1 << 3)
+#define USBHS_ENDP_RX_ISO (1 << (16 + 2))
+
+// R32_UH_EP_PID
+#define USBHS_HOST_MASK_TOKEN (0x0f)
+#define USBHS_HOST_MASK_ENDP (0x0f << 4)
+
+//R8_UH_RX_CTRL
+#define USBHS_EP_R_RES_MASK (3 << 0)
+#define USBHS_EP_R_RES_ACK (0 << 0)
+#define USBHS_EP_R_RES_NYET (1 << 0)
+#define USBHS_EP_R_RES_NAK (2 << 0)
+#define USBHS_EP_R_RES_STALL (3 << 0)
+
+#define USBHS_UH_R_RES_NO (1 << 2)
+#define USBHS_UH_R_TOG_1 (1 << 3)
+#define USBHS_UH_R_TOG_2 (2 << 3)
+#define USBHS_UH_R_TOG_3 (3 << 3)
+#define USBHS_UH_R_TOG_AUTO (1 << 5)
+#define USBHS_UH_R_DATA_NO (1 << 6)
+//R8_UH_TX_CTRL
+#define USBHS_UH_T_RES_MASK (3 << 0)
+#define USBHS_UH_T_RES_ACK (0 << 0)
+#define USBHS_UH_T_RES_NYET (1 << 0)
+#define USBHS_UH_T_RES_NAK (2 << 0)
+#define USBHS_UH_T_RES_STALL (3 << 0)
+
+#define USBHS_UH_T_RES_NO (1 << 2)
+#define USBHS_UH_T_TOG_1 (1 << 3)
+#define USBHS_UH_T_TOG_2 (2 << 3)
+#define USBHS_UH_T_TOG_3 (3 << 3)
+#define USBHS_UH_T_TOG_AUTO (1 << 5)
+#define USBHS_UH_T_DATA_NO (1 << 6)
+
+
+#endif
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
new file mode 100644
index 000000000..c248ba14e
--- /dev/null
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -0,0 +1,348 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS
+
+#include "device/dcd.h"
+#include "ch32_usbfs_reg.h"
+
+/* private defines */
+#define EP_MAX (8)
+
+#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep])
+#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
+#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
+#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
+
+/* private data */
+struct usb_xfer {
+ bool valid;
+ uint8_t* buffer;
+ size_t len;
+ size_t processed_len;
+ size_t max_size;
+};
+
+static struct {
+ bool ep0_tog;
+ bool isochronous[EP_MAX];
+ struct usb_xfer xfer[EP_MAX][2];
+ TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
+ TU_ATTR_ALIGNED(4) struct {
+ // OUT transfers >64 bytes will overwrite queued IN data!
+ uint8_t out[64];
+ uint8_t in[1023];
+ uint8_t pad;
+ } ep3_buffer;
+} data;
+
+/* private helpers */
+static void update_in(uint8_t rhport, uint8_t ep, bool force) {
+ struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN];
+ if (xfer->valid) {
+ if (force || xfer->len) {
+ size_t len = TU_MIN(xfer->max_size, xfer->len);
+ if (ep == 0) {
+ memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
+ } else if (ep == 3) {
+ memcpy(data.ep3_buffer.in, xfer->buffer, len);
+ } else {
+ memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ EP_TX_LEN(ep) = len;
+ if (ep == 0) {
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0);
+ data.ep0_tog = !data.ep0_tog;
+ } else if (data.isochronous[ep]) {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK;
+ }
+ } else {
+ xfer->valid = false;
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK;
+ dcd_event_xfer_complete(
+ rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len,
+ XFER_RESULT_SUCCESS, true);
+ }
+ }
+}
+
+static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
+ struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT];
+ if (xfer->valid) {
+ size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
+ if (ep == 3) {
+ memcpy(xfer->buffer, data.ep3_buffer.out, len);
+ } else {
+ memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
+ }
+ xfer->buffer += len;
+ xfer->len -= len;
+ xfer->processed_len += len;
+
+ if (xfer->len == 0 || len < xfer->max_size) {
+ xfer->valid = false;
+ dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true);
+ }
+
+ if (ep == 0) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ }
+}
+
+/* public functions */
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rh_init;
+ // init registers
+ USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN;
+ USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN;
+ USBOTG_FS->DEV_ADDR = 0x00;
+
+ USBOTG_FS->INT_FG = 0xFF;
+ USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
+
+ // setup endpoint 0
+ EP_DMA(0) = (uint32_t) &data.buffer[0][0];
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ // enable other endpoints but NAK everything
+ USBOTG_FS->UEP4_1_MOD = 0xCC;
+ USBOTG_FS->UEP2_3_MOD = 0xCC;
+ USBOTG_FS->UEP5_6_MOD = 0xCC;
+ USBOTG_FS->UEP7_MOD = 0x0C;
+
+ for (uint8_t ep = 1; ep < EP_MAX; ep++) {
+ EP_DMA(ep) = (uint32_t) &data.buffer[ep][0];
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK;
+ }
+ EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
+
+ dcd_connect(rhport);
+
+ return true;
+}
+
+void dcd_int_handler(uint8_t rhport) {
+ (void) rhport;
+ uint8_t status = USBOTG_FS->INT_FG;
+ if (status & USBFS_INT_FG_TRANSFER) {
+ uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
+ uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
+
+ switch (token) {
+ case PID_OUT: {
+ uint16_t rx_len = USBOTG_FS->RX_LEN;
+ update_out(rhport, ep, rx_len);
+ break;
+ }
+
+ case PID_IN:
+ update_in(rhport, ep, false);
+ break;
+
+ case PID_SETUP:
+ // setup clears stall
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ data.ep0_tog = true;
+ dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
+ break;
+ }
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER;
+ } else if (status & USBFS_INT_FG_BUS_RST) {
+ data.ep0_tog = true;
+ data.xfer[0][TUSB_DIR_OUT].max_size = 64;
+ data.xfer[0][TUSB_DIR_IN].max_size = 64;
+
+ //dcd_event_bus_reset(rhport, (USBOTG_FS->BASE_CTRL & USBFS_CTRL_LOW_SPEED) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL, true);
+ dcd_event_bus_reset(rhport, (USBOTG_FS->UDEV_CTRL & USBFS_UDEV_CTRL_LOW_SPEED) ? TUSB_SPEED_LOW : TUSB_SPEED_FULL, true);
+
+ USBOTG_FS->DEV_ADDR = 0x00;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+ USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST;
+ } else if (status & USBFS_INT_FG_SUSPEND) {
+ dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+ dcd_event_handler(&event, true);
+ USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND;
+ }
+}
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBHD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBHD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void) dev_addr;
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response
+}
+
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_connect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_disconnect(uint8_t rhport) {
+ (void) rhport;
+ USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ (void) rhport;
+ (void) en;
+
+ // TODO implement later
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue;
+ }
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
+ uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+ TU_ASSERT(ep < EP_MAX);
+
+ data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS;
+ data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
+
+ if (ep != 0) {
+ if (dir == TUSB_DIR_OUT) {
+ if (data.isochronous[ep]) {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET;
+ } else {
+ EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+ }
+ }
+ return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+ (void) rhport;
+ // TODO optional
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ (void) ep_addr;
+ // TODO optional
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+
+ struct usb_xfer* xfer = &data.xfer[ep][dir];
+ dcd_int_disable(rhport);
+ xfer->valid = true;
+ xfer->buffer = buffer;
+ xfer->len = total_bytes;
+ xfer->processed_len = 0;
+ dcd_int_enable(rhport);
+
+ if (dir == TUSB_DIR_IN) {
+ update_in(rhport, ep, true);
+ }
+ return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL;
+ }
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+ uint8_t ep = tu_edpt_number(ep_addr);
+ uint8_t dir = tu_edpt_dir(ep_addr);
+ if (ep == 0) {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+ }
+ } else {
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK;
+ } else {
+ EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK;
+ }
+ }
+}
+
+#endif
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
new file mode 100644
index 000000000..f8bf3c889
--- /dev/null
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -0,0 +1,423 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Greg Davill
+ * Copyright (c) 2023 Denis Krasutski
+ *
+ * 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 && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
+#include "ch32_usbhs_reg.h"
+
+#include "device/dcd.h"
+
+// Max number of bi-directional endpoints including EP0
+#define EP_MAX 16
+
+typedef struct {
+ uint8_t* buffer;
+ uint16_t total_len;
+ uint16_t queued_len;
+ uint16_t max_size;
+ bool is_last_packet;
+ bool is_iso;
+} xfer_ctl_t;
+
+typedef enum {
+ EP_RESPONSE_ACK,
+ EP_RESPONSE_NAK,
+} ep_response_list_t;
+
+#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
+static xfer_ctl_t xfer_status[EP_MAX][2];
+
+#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep) * 2)
+#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep) * 4)
+#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep) * 4)
+#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep) * 2)
+
+#define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1))
+#define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1))
+
+/* Endpoint Buffer */
+TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE];
+
+static void ep_set_response_and_toggle(uint8_t ep_num, tusb_dir_t ep_dir, ep_response_list_t response_type) {
+ if (ep_dir == TUSB_DIR_IN) {
+ uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_T_RES_ACK : USBHS_EP_T_RES_NAK;
+ if (ep_num == 0) {
+ if (response_type == EP_RESPONSE_ACK) {
+ if (EP_TX_LEN(ep_num) == 0) {
+ EP_TX_CTRL(ep_num) |= USBHS_EP_T_TOG_1;
+ } else {
+ EP_TX_CTRL(ep_num) ^= USBHS_EP_T_TOG_1;
+ }
+ }
+ }
+ if (xfer_status[ep_num][TUSB_DIR_IN].is_iso == true) {
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG;
+ } else {
+ EP_TX_CTRL(ep_num) = (EP_TX_CTRL(ep_num) & ~(USBHS_EP_T_RES_MASK)) | response;
+ }
+ } else {
+ uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_R_RES_ACK : USBHS_EP_R_RES_NAK;
+ if (ep_num == 0) {
+ if (response_type == EP_RESPONSE_ACK) {
+ if (xfer_status[ep_num][TUSB_DIR_OUT].queued_len == 0) {
+ EP_RX_CTRL(ep_num) |= USBHS_EP_R_TOG_1;
+ }
+ } else {
+ EP_RX_CTRL(ep_num) ^= USBHS_EP_R_TOG_1;
+ }
+ }
+ EP_RX_CTRL(ep_num) = (EP_RX_CTRL(ep_num) & ~(USBHS_EP_R_RES_MASK)) | response;
+ }
+}
+
+static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer) {
+ if (ep_dir == TUSB_DIR_IN) {
+ uint16_t remaining = xfer->total_len - xfer->queued_len;
+ uint16_t next_tx_size = TU_MIN(remaining, xfer->max_size);
+
+ if (ep_num == 0) {
+ memcpy(ep0_buffer, &xfer->buffer[xfer->queued_len], next_tx_size);
+ } else {
+ EP_TX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
+ }
+
+ EP_TX_LEN(ep_num) = next_tx_size;
+ xfer->queued_len += next_tx_size;
+ if (xfer->queued_len == xfer->total_len) {
+ xfer->is_last_packet = true;
+ }
+ if (xfer->is_iso == true) {
+ /* Enable EP to generate ISA_ACT interrupt */
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
+ }
+ } else { /* TUSB_DIR_OUT */
+ uint16_t left_to_receive = xfer->total_len - xfer->queued_len;
+ uint16_t max_possible_rx_size = TU_MIN(xfer->max_size, left_to_receive);
+
+ if (max_possible_rx_size == left_to_receive) {
+ xfer->is_last_packet = true;
+ }
+
+ if (ep_num > 0) {
+ EP_RX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
+ EP_RX_MAX_LEN(ep_num) = max_possible_rx_size;
+ }
+ }
+ ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK);
+}
+
+bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ (void) rhport;
+ (void) rh_init;
+
+ memset(&xfer_status, 0, sizeof(xfer_status));
+
+ USBHSD->HOST_CTRL = 0x00;
+ USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM;
+
+ USBHSD->CONTROL = 0;
+
+#if TUD_OPT_HIGH_SPEED
+ USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED;
+#else
+ #error OPT_MODE_FULL_SPEED not currently supported on CH32
+ USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED;
+#endif
+
+ USBHSD->INT_EN = 0;
+ USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN | USBHS_ISO_ACT_EN;
+
+ USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
+ USBHSD->ENDP_TYPE = 0x00;
+ USBHSD->BUF_MODE = 0x00;
+
+ for (int ep = 0; ep < EP_MAX; ep++) {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+
+ EP_RX_MAX_LEN(ep) = 0;
+ }
+
+ USBHSD->UEP0_DMA = (uint32_t) ep0_buffer;
+ USBHSD->UEP0_MAX_LEN = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
+ xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
+
+ USBHSD->DEV_AD = 0;
+ USBHSD->CONTROL |= USBHS_DEV_PU_EN;
+
+ return true;
+}
+
+void dcd_int_enable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_EnableIRQ(USBHS_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+ (void) rhport;
+ NVIC_DisableIRQ(USBHS_IRQn);
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+ (void) rhport;
+
+ for (size_t ep = 1; ep < EP_MAX; ep++) {
+ EP_TX_LEN(ep) = 0;
+ EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+ EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+
+ EP_RX_MAX_LEN(ep) = 0;
+ }
+
+ USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+ (void) dev_addr;
+
+ // Response with zlp status
+ dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport) {
+ (void) rhport;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en) {
+ (void) rhport;
+ if (en) {
+ USBHSD->INT_EN |= USBHS_SOF_ACT_EN;
+ } else {
+ USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN);
+ }
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+ (void) rhport;
+
+ if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+ request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+ request->bRequest == TUSB_REQ_SET_ADDRESS) {
+ USBHSD->DEV_AD = (uint8_t) request->wValue;
+ }
+
+ EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ EP_RX_CTRL(0) = USBHS_EP_R_RES_NAK | USBHS_EP_R_TOG_0;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+ (void) rhport;
+
+ uint8_t const ep_num = tu_edpt_number(desc_edpt->bEndpointAddress);
+ tusb_dir_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+
+ TU_ASSERT(ep_num < EP_MAX);
+
+ if (ep_num == 0) {
+ return true;
+ }
+
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+ xfer->max_size = tu_edpt_packet_size(desc_edpt);
+
+ xfer->is_iso = (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
+ if (dir == TUSB_DIR_OUT) {
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << ep_num);
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ if (xfer->is_iso == true) {
+ USBHSD->ENDP_TYPE |= (USBHS_EP0_R_TYP << ep_num);
+ }
+ EP_RX_MAX_LEN(ep_num) = xfer->max_size;
+ } else {
+ if (xfer->is_iso == true) {
+ USBHSD->ENDP_TYPE |= (USBHS_EP0_T_TYP << ep_num);
+ } else {
+ /* Enable all types except Isochronous to avoid ISO_ACT interrupt generation */
+ USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
+ }
+ EP_TX_LEN(ep_num) = 0;
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ }
+
+ return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ EP_RX_MAX_LEN(ep_num) = 0;
+ USBHSD->ENDP_TYPE &= ~(USBHS_EP0_R_TYP << ep_num);
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_R_EN << ep_num);
+ } else { // TUSB_DIR_IN
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+ EP_TX_LEN(ep_num) = 0;
+ USBHSD->ENDP_TYPE &= ~(USBHS_EP0_T_TYP << ep_num);
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+ }
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL;
+ } else {
+ EP_TX_LEN(0) = 0;
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL;
+ }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+ (void) rhport;
+
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ if (dir == TUSB_DIR_OUT) {
+ EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+ } else {
+ EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_R_RES_NAK;
+ }
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+ (void) rhport;
+ uint8_t const ep_num = tu_edpt_number(ep_addr);
+ tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+ xfer->buffer = buffer;
+ xfer->total_len = total_bytes;
+ xfer->queued_len = 0;
+ xfer->is_last_packet = false;
+
+ xfer_data_packet(ep_num, dir, xfer);
+
+ return true;
+}
+
+void dcd_int_handler(uint8_t rhport) {
+ (void) rhport;
+
+ uint8_t int_flag = USBHSD->INT_FG;
+ uint8_t int_status = USBHSD->INT_ST;
+
+ if (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)) {
+ uint8_t const token = int_status & MASK_UIS_TOKEN;
+
+ if (token == USBHS_TOKEN_PID_SOF) {
+ uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK;
+ dcd_event_sof(rhport, frame_count, true);
+ }else {
+ uint8_t const ep_num = int_status & MASK_UIS_ENDP;
+ tusb_dir_t const ep_dir = (token == USBHS_TOKEN_PID_IN) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+ uint8_t const ep_addr = tu_edpt_addr(ep_num, ep_dir);
+ xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, ep_dir);
+
+ if (token == USBHS_TOKEN_PID_OUT) {
+ uint16_t rx_len = USBHSD->RX_LEN;
+
+ if (ep_num == 0) {
+ memcpy(&xfer->buffer[xfer->queued_len], ep0_buffer, rx_len);
+ }
+
+ xfer->queued_len += rx_len;
+ if (rx_len < xfer->max_size) {
+ xfer->is_last_packet = true;
+ }
+ } else if (token == USBHS_TOKEN_PID_IN) {
+ if (xfer->is_iso && xfer->is_last_packet) {
+ /* Disable EP to avoid ISO_ACT interrupt generation */
+ USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+ } else {
+ // Do nothing, no need to update xfer->is_last_packet, it is already updated in xfer_data_packet
+ }
+ }
+
+ if (xfer->is_last_packet == true) {
+ ep_set_response_and_toggle(ep_num, ep_dir, EP_RESPONSE_NAK);
+ dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+ } else {
+ /* prepare next part of packet to xref */
+ xfer_data_packet(ep_num, ep_dir, xfer);
+ }
+ }
+
+ USBHSD->INT_FG = (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)); /* Clear flag */
+ } else if (int_flag & USBHS_SETUP_FLAG) {
+ ep_set_response_and_toggle(0, TUSB_DIR_IN, EP_RESPONSE_NAK);
+ ep_set_response_and_toggle(0, TUSB_DIR_OUT, EP_RESPONSE_NAK);
+ dcd_event_setup_received(0, ep0_buffer, true);
+
+ USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */
+ } else if (int_flag & USBHS_BUS_RST_FLAG) {
+ // TODO CH32 does not detect actual speed at this time (should be known at end of reset)
+ // This interrupt probably triggered at start of bus reset
+// tusb_speed_t actual_speed;
+// switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){
+// case USBHS_SPEED_TYPE_HIGH:
+// actual_speed = TUSB_SPEED_HIGH;
+// break;
+// case USBHS_SPEED_TYPE_FULL:
+// actual_speed = TUSB_SPEED_FULL;
+// break;
+// case USBHS_SPEED_TYPE_LOW:
+// actual_speed = TUSB_SPEED_LOW;
+// break;
+// default:
+// TU_ASSERT(0,);
+// break;
+// }
+// dcd_event_bus_reset(0, actual_speed, true);
+
+ dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true);
+
+ USBHSD->DEV_AD = 0;
+ EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
+ EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+
+ USBHSD->INT_FG = USBHS_BUS_RST_FLAG; /* Clear flag */
+ } else if (int_flag & USBHS_SUSPEND_FLAG) {
+ dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+ dcd_event_handler(&event, true);
+
+ USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
+ }
+}
+
+#endif
diff --git a/src/tinyusb.mk b/src/tinyusb.mk
new file mode 100644
index 000000000..89ea0212c
--- /dev/null
+++ b/src/tinyusb.mk
@@ -0,0 +1,26 @@
+# C source files
+TINYUSB_SRC_C += \
+ src/tusb.c \
+ src/common/tusb_fifo.c \
+ src/device/usbd.c \
+ src/device/usbd_control.c \
+ src/typec/usbc.c \
+ src/class/audio/audio_device.c \
+ src/class/cdc/cdc_device.c \
+ src/class/dfu/dfu_device.c \
+ src/class/dfu/dfu_rt_device.c \
+ src/class/hid/hid_device.c \
+ src/class/midi/midi_device.c \
+ src/class/msc/msc_device.c \
+ src/class/net/ecm_rndis_device.c \
+ src/class/net/ncm_device.c \
+ src/class/usbtmc/usbtmc_device.c \
+ src/class/video/video_device.c \
+ src/class/vendor/vendor_device.c \
+ src/host/usbh.c \
+ src/host/hub.c \
+ src/class/cdc/cdc_host.c \
+ src/class/hid/hid_host.c \
+ src/class/msc/msc_host.c \
+ src/class/vendor/vendor_host.c \
+ src/typec/usbc.c \
diff --git a/src/tusb.c b/src/tusb.c
index c3787ff8f..85ab1d6ae 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -31,168 +31,243 @@
#include "tusb.h"
#include "common/tusb_private.h"
-// TODO clean up
#if CFG_TUD_ENABLED
#include "device/usbd_pvt.h"
#endif
-bool tusb_init(void)
-{
-#if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT)
- // init device stack CFG_TUSB_RHPORTx_MODE must be defined
- TU_ASSERT ( tud_init(TUD_OPT_RHPORT) );
+#if CFG_TUH_ENABLED
+#include "host/usbh_pvt.h"
#endif
-#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
- // init host stack CFG_TUSB_RHPORTx_MODE must be defined
- TU_ASSERT( tuh_init(TUH_OPT_RHPORT) );
+tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { TUSB_ROLE_INVALID };
+
+//--------------------------------------------------------------------
+// Weak/Default API, can be overwritten by Application
+//--------------------------------------------------------------------
+
+TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) {
+#if CFG_TUSB_OS != OPT_OS_NONE
+ osal_task_delay(ms);
+#else
+ // delay using millis() (if implemented) and/or frame number if possible
+ const uint32_t time_ms = tusb_time_millis_api();
+ while ((tusb_time_millis_api() - time_ms) < ms) {}
#endif
+}
+
+//--------------------------------------------------------------------+
+// Public API
+//--------------------------------------------------------------------+
+bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
+ // backward compatible called with tusb_init(void)
+ #if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT)
+ if (rh_init == NULL) {
+ #if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT)
+ // init device stack CFG_TUSB_RHPORTx_MODE must be defined
+ const tusb_rhport_init_t dev_init = {
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
+ };
+ TU_ASSERT ( tud_rhport_init(TUD_OPT_RHPORT, &dev_init) );
+ _tusb_rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE;
+ #endif
+
+ #if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
+ // init host stack CFG_TUSB_RHPORTx_MODE must be defined
+ const tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL
+ };
+ TU_ASSERT( tuh_rhport_init(TUH_OPT_RHPORT, &host_init) );
+ _tusb_rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST;
+ #endif
+
+ return true;
+ }
+ #endif
+
+ // new API with explicit rhport and role
+ TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID);
+ _tusb_rhport_role[rhport] = rh_init->role;
+
+ #if CFG_TUD_ENABLED
+ if (rh_init->role == TUSB_ROLE_DEVICE) {
+ TU_ASSERT(tud_rhport_init(rhport, rh_init));
+ }
+ #endif
+
+ #if CFG_TUH_ENABLED
+ if (rh_init->role == TUSB_ROLE_HOST) {
+ TU_ASSERT(tuh_rhport_init(rhport, rh_init));
+ }
+ #endif
return true;
}
-bool tusb_inited(void)
-{
+bool tusb_inited(void) {
bool ret = false;
-#if CFG_TUD_ENABLED
+ #if CFG_TUD_ENABLED
ret = ret || tud_inited();
-#endif
+ #endif
-#if CFG_TUH_ENABLED
+ #if CFG_TUH_ENABLED
ret = ret || tuh_inited();
-#endif
+ #endif
return ret;
}
+void tusb_int_handler(uint8_t rhport, bool in_isr) {
+ TU_VERIFY(rhport < TUP_USBIP_CONTROLLER_NUM,);
+
+ #if CFG_TUD_ENABLED
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_DEVICE) {
+ (void) in_isr;
+ dcd_int_handler(rhport);
+ }
+ #endif
+
+ #if CFG_TUH_ENABLED
+ if (_tusb_rhport_role[rhport] == TUSB_ROLE_HOST) {
+ hcd_int_handler(rhport, in_isr);
+ }
+ #endif
+}
+
+//--------------------------------------------------------------------+
+// Descriptor helper
+//--------------------------------------------------------------------+
+
+uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) {
+ while (desc + 1 < end) {
+ if (desc[1] == byte1) return desc;
+ desc += desc[DESC_OFFSET_LEN];
+ }
+ return NULL;
+}
+
+uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) {
+ while (desc + 2 < end) {
+ if (desc[1] == byte1 && desc[2] == byte2) return desc;
+ desc += desc[DESC_OFFSET_LEN];
+ }
+ return NULL;
+}
+
+uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) {
+ while (desc + 3 < end) {
+ if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc;
+ desc += desc[DESC_OFFSET_LEN];
+ }
+ return NULL;
+}
+
//--------------------------------------------------------------------+
-// Internal Helper for both Host and Device stack
+// Endpoint Helper for both Host and Device stack
//--------------------------------------------------------------------+
-bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
-{
+bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
(void) mutex;
-#if TUSB_OPT_MUTEX
// pre-check to help reducing mutex lock
TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0));
- osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
-#endif
+ (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
// can only claim the endpoint if it is not busy and not claimed yet.
bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0);
- if (available)
- {
+ if (available) {
ep_state->claimed = 1;
}
-#if TUSB_OPT_MUTEX
- osal_mutex_unlock(mutex);
-#endif
-
+ (void) osal_mutex_unlock(mutex);
return available;
}
-bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
-{
+bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
(void) mutex;
-
-#if TUSB_OPT_MUTEX
- osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
-#endif
+ (void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
// can only release the endpoint if it is claimed and not busy
bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0);
- if (ret)
- {
+ if (ret) {
ep_state->claimed = 0;
}
-#if TUSB_OPT_MUTEX
- osal_mutex_unlock(mutex);
-#endif
-
+ (void) osal_mutex_unlock(mutex);
return ret;
}
-bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed)
-{
+bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) {
uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep);
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size);
- switch (desc_ep->bmAttributes.xfer)
- {
- case TUSB_XFER_ISOCHRONOUS:
- {
+ switch (desc_ep->bmAttributes.xfer) {
+ case TUSB_XFER_ISOCHRONOUS: {
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023);
TU_ASSERT(max_packet_size <= spec_size);
+ break;
}
- break;
case TUSB_XFER_BULK:
- if (speed == TUSB_SPEED_HIGH)
- {
+ if (speed == TUSB_SPEED_HIGH) {
// Bulk highspeed must be EXACTLY 512
TU_ASSERT(max_packet_size == 512);
- }else
- {
+ } else {
// TODO Bulk fullspeed can only be 8, 16, 32, 64
TU_ASSERT(max_packet_size <= 64);
}
- break;
+ break;
- case TUSB_XFER_INTERRUPT:
- {
+ case TUSB_XFER_INTERRUPT: {
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64);
TU_ASSERT(max_packet_size <= spec_size);
+ break;
}
- break;
- default: return false;
+ default:
+ return false;
}
return true;
}
-void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, uint8_t driver_id)
-{
+void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len,
+ uint8_t driver_id) {
uint8_t const* p_desc = (uint8_t const*) desc_itf;
uint8_t const* desc_end = p_desc + desc_len;
- while( p_desc < desc_end )
- {
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
+ while (p_desc < desc_end) {
+ if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
-
TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id);
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
}
-
p_desc = tu_desc_next(p_desc);
}
}
-uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len)
-{
+uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) {
uint8_t const* p_desc = (uint8_t const*) desc_itf;
uint16_t len = 0;
- while (itf_count--)
- {
+ while (itf_count--) {
// Next on interface desc
len += tu_desc_len(desc_itf);
p_desc = tu_desc_next(p_desc);
- while (len < max_len)
- {
+ while (len < max_len) {
+ if (tu_desc_len(p_desc) == 0) {
+ // Escape infinite loop
+ break;
+ }
// return on IAD regardless of itf count
- if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len;
-
- if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
- ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 )
- {
+ if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
+ return len;
+ }
+ if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
+ ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) {
break;
}
@@ -204,45 +279,232 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf,
return len;
}
-/*------------------------------------------------------------------*/
-/* Debug
- *------------------------------------------------------------------*/
+//--------------------------------------------------------------------+
+// Endpoint Stream Helper for both Host and Device stack
+//--------------------------------------------------------------------+
+
+bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
+ void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) {
+ (void) is_tx;
+
+ s->is_host = is_host;
+ tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable);
+
+ #if OSAL_MUTEX_REQUIRED
+ if (ff_buf && ff_bufsize) {
+ osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef);
+ tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex);
+ }
+ #endif
+
+ s->ep_buf = ep_buf;
+ s->ep_bufsize = ep_bufsize;
+
+ return true;
+}
+
+bool tu_edpt_stream_deinit(tu_edpt_stream_t* s) {
+ (void) s;
+ #if OSAL_MUTEX_REQUIRED
+ if (s->ff.mutex_wr) osal_mutex_delete(s->ff.mutex_wr);
+ if (s->ff.mutex_rd) osal_mutex_delete(s->ff.mutex_rd);
+ #endif
+ return true;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool stream_claim(uint8_t hwid, tu_edpt_stream_t* s) {
+ if (s->is_host) {
+ #if CFG_TUH_ENABLED
+ return usbh_edpt_claim(hwid, s->ep_addr);
+ #endif
+ } else {
+ #if CFG_TUD_ENABLED
+ return usbd_edpt_claim(hwid, s->ep_addr);
+ #endif
+ }
+ return false;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool stream_xfer(uint8_t hwid, tu_edpt_stream_t* s, uint16_t count) {
+ if (s->is_host) {
+ #if CFG_TUH_ENABLED
+ return usbh_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count);
+ #endif
+ } else {
+ #if CFG_TUD_ENABLED
+ return usbd_edpt_xfer(hwid, s->ep_addr, count ? s->ep_buf : NULL, count);
+ #endif
+ }
+ return false;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool stream_release(uint8_t hwid, tu_edpt_stream_t* s) {
+ if (s->is_host) {
+ #if CFG_TUH_ENABLED
+ return usbh_edpt_release(hwid, s->ep_addr);
+ #endif
+ } else {
+ #if CFG_TUD_ENABLED
+ return usbd_edpt_release(hwid, s->ep_addr);
+ #endif
+ }
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// Stream Write
+//--------------------------------------------------------------------+
+bool tu_edpt_stream_write_zlp_if_needed(uint8_t hwid, tu_edpt_stream_t* s, uint32_t last_xferred_bytes) {
+ // ZLP condition: no pending data, last transferred bytes is multiple of packet size
+ const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
+ TU_VERIFY(!tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (mps - 1))));
+ TU_VERIFY(stream_claim(hwid, s));
+ TU_ASSERT(stream_xfer(hwid, s, 0));
+ return true;
+}
+
+uint32_t tu_edpt_stream_write_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
+ // skip if no data
+ TU_VERIFY(tu_fifo_count(&s->ff), 0);
+
+ TU_VERIFY(stream_claim(hwid, s), 0);
+
+ // Pull data from FIFO -> EP buf
+ uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize);
+
+ if (count) {
+ TU_ASSERT(stream_xfer(hwid, s, count), 0);
+ return count;
+ } else {
+ // Release endpoint since we don't make any transfer
+ // Note: data is dropped if terminal is not connected
+ stream_release(hwid, s);
+ return 0;
+ }
+}
+
+uint32_t tu_edpt_stream_write(uint8_t hwid, tu_edpt_stream_t* s, void const* buffer, uint32_t bufsize) {
+ TU_VERIFY(bufsize); // TODO support ZLP
+
+ if (0 == tu_fifo_depth(&s->ff)) {
+ // no fifo for buffered
+ TU_VERIFY(stream_claim(hwid, s), 0);
+ const uint32_t xact_len = tu_min32(bufsize, s->ep_bufsize);
+ memcpy(s->ep_buf, buffer, xact_len);
+ TU_ASSERT(stream_xfer(hwid, s, (uint16_t) xact_len), 0);
+ return xact_len;
+ } else {
+ const uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize);
+
+ // flush if fifo has more than packet size or
+ // in rare case: fifo depth is configured too small (which never reach packet size)
+ const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
+ if ((tu_fifo_count(&s->ff) >= mps) || (tu_fifo_depth(&s->ff) < mps)) {
+ tu_edpt_stream_write_xfer(hwid, s);
+ }
+ return ret;
+ }
+}
+
+uint32_t tu_edpt_stream_write_available(uint8_t hwid, tu_edpt_stream_t* s) {
+ if (tu_fifo_depth(&s->ff)) {
+ return (uint32_t) tu_fifo_remaining(&s->ff);
+ } else {
+ bool is_busy = true;
+ if (s->is_host) {
+ #if CFG_TUH_ENABLED
+ is_busy = usbh_edpt_busy(hwid, s->ep_addr);
+ #endif
+ } else {
+ #if CFG_TUD_ENABLED
+ is_busy = usbd_edpt_busy(hwid, s->ep_addr);
+ #endif
+ }
+ return is_busy ? 0 : s->ep_bufsize;
+ }
+}
+
+//--------------------------------------------------------------------+
+// Stream Read
+//--------------------------------------------------------------------+
+uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s) {
+ if (0 == tu_fifo_depth(&s->ff)) {
+ // no fifo for buffered
+ TU_VERIFY(stream_claim(hwid, s), 0);
+ TU_ASSERT(stream_xfer(hwid, s, s->ep_bufsize), 0);
+ return s->ep_bufsize;
+ } else {
+ const uint16_t mps = s->is_mps512 ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS;
+ uint16_t available = tu_fifo_remaining(&s->ff);
+
+ // Prepare for incoming data but only allow what we can store in the ring buffer.
+ // TODO Actually we can still carry out the transfer, keeping count of received bytes
+ // and slowly move it to the FIFO when read().
+ // This pre-check reduces endpoint claiming
+ TU_VERIFY(available >= mps);
+
+ TU_VERIFY(stream_claim(hwid, s), 0);
+
+ // get available again since fifo can be changed before endpoint is claimed
+ available = tu_fifo_remaining(&s->ff);
+
+ if (available >= mps) {
+ // multiple of packet size limit by ep bufsize
+ uint16_t count = (uint16_t) (available & ~(mps - 1));
+ count = tu_min16(count, s->ep_bufsize);
+ TU_ASSERT(stream_xfer(hwid, s, count), 0);
+ return count;
+ } else {
+ // Release endpoint since we don't make any transfer
+ stream_release(hwid, s);
+ return 0;
+ }
+ }
+}
+
+uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) {
+ uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize);
+ tu_edpt_stream_read_xfer(hwid, s);
+ return num_read;
+}
+
+//--------------------------------------------------------------------+
+// Debug
+//--------------------------------------------------------------------+
+
#if CFG_TUSB_DEBUG
#include <ctype.h>
-#if CFG_TUSB_DEBUG >= 2
-
-char const* const tu_str_speed[] = { "Full", "Low", "High" };
-char const* const tu_str_std_request[] =
-{
- "Get Status" ,
- "Clear Feature" ,
- "Reserved" ,
- "Set Feature" ,
- "Reserved" ,
- "Set Address" ,
- "Get Descriptor" ,
- "Set Descriptor" ,
- "Get Configuration" ,
- "Set Configuration" ,
- "Get Interface" ,
- "Set Interface" ,
- "Synch Frame"
+#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
+char const* const tu_str_speed[] = {"Full", "Low", "High"};
+char const* const tu_str_std_request[] = {
+ "Get Status",
+ "Clear Feature",
+ "Reserved",
+ "Set Feature",
+ "Reserved",
+ "Set Address",
+ "Get Descriptor",
+ "Set Descriptor",
+ "Get Configuration",
+ "Set Configuration",
+ "Get Interface",
+ "Set Interface",
+ "Synch Frame"
};
+char const* const tu_str_xfer_result[] = {
+ "OK", "FAILED", "STALLED", "TIMEOUT"
+};
#endif
-static void dump_str_line(uint8_t const* buf, uint16_t count)
-{
+static void dump_str_line(uint8_t const* buf, uint16_t count) {
tu_printf(" |");
-
// each line is 16 bytes
- for(uint16_t i=0; i<count; i++)
- {
- const char ch = buf[i];
+ for (uint16_t i = 0; i < count; i++) {
+ int ch = buf[i];
tu_printf("%c", isprint(ch) ? ch : '.');
}
-
tu_printf("|\r\n");
}
@@ -251,42 +513,30 @@ static void dump_str_line(uint8_t const* buf, uint16_t count)
* - count : number of item
* - indent: prefix spaces on every line
*/
-void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
-{
+void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) {
uint8_t const size = 1; // fixed 1 byte for now
-
- if ( !buf || !count )
- {
+ if (!buf || !count) {
tu_printf("NULL\r\n");
return;
}
- uint8_t const *buf8 = (uint8_t const *) buf;
-
+ uint8_t const* buf8 = (uint8_t const*) buf;
char format[] = "%00X";
- format[2] += 2*size;
+ format[2] += (uint8_t) (2 * size); // 1 byte = 2 hex digits
+ const uint8_t item_per_line = 16 / size;
- const uint8_t item_per_line = 16 / size;
+ for (unsigned int i = 0; i < count; i++) {
+ unsigned int value = 0;
- for(unsigned int i=0; i<count; i++)
- {
- unsigned int value=0;
-
- if ( i%item_per_line == 0 )
- {
+ if (i % item_per_line == 0) {
// Print Ascii
- if ( i != 0 )
- {
- dump_str_line(buf8-16, 16);
- }
-
- for(uint8_t s=0; s < indent; s++) tu_printf(" ");
-
+ if (i != 0) dump_str_line(buf8 - 16, 16);
+ for (uint8_t s = 0; s < indent; s++) tu_printf(" ");
// print offset or absolute address
- tu_printf("%04X: ", 16*i/item_per_line);
+ tu_printf("%04X: ", 16 * i / item_per_line);
}
- memcpy(&value, buf8, size);
+ tu_memcpy_s(&value, sizeof(value), buf8, size);
buf8 += size;
tu_printf(" ");
@@ -294,19 +544,16 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
}
// fill up last row to 16 for printing ascii
- const uint32_t remain = count%16;
- uint8_t nback = (uint8_t)(remain ? remain : 16);
-
- if ( remain )
- {
- for(uint32_t i=0; i< 16-remain; i++)
- {
+ const uint32_t remain = count % 16;
+ uint8_t nback = (uint8_t) (remain ? remain : 16);
+ if (remain) {
+ for (uint32_t i = 0; i < 16 - remain; i++) {
tu_printf(" ");
- for(int j=0; j<2*size; j++) tu_printf(" ");
+ for (int j = 0; j < 2 * size; j++) tu_printf(" ");
}
}
- dump_str_line(buf8-nback, nback);
+ dump_str_line(buf8 - nback, nback);
}
#endif
diff --git a/src/tusb.h b/src/tusb.h
index 093c053f7..dfba21ddf 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -38,7 +38,10 @@
#include "osal/osal.h"
#include "common/tusb_fifo.h"
-#include "class/hid/hid.h"
+//------------- TypeC -------------//
+#if CFG_TUC_ENABLED
+ #include "typec/usbc.h"
+#endif
//------------- HOST -------------//
#if CFG_TUH_ENABLED
@@ -63,7 +66,10 @@
#if CFG_TUH_VENDOR
#include "class/vendor/vendor_host.h"
#endif
-
+#else
+ #ifndef tuh_int_handler
+ #define tuh_int_handler(...)
+ #endif
#endif
//------------- DEVICE -------------//
@@ -117,24 +123,62 @@
#if CFG_TUD_BTH
#include "class/bth/bth_device.h"
#endif
+#else
+ #ifndef tud_int_handler
+ #define tud_int_handler(...)
+ #endif
#endif
//--------------------------------------------------------------------+
-// APPLICATION API
+// User API
//--------------------------------------------------------------------+
+#if CFG_TUH_ENABLED || CFG_TUD_ENABLED
+
+// Internal helper for backward compatible with tusb_init(void)
+bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
-// Initialize device/host stack
+// Initialize roothub port with device/host role
// Note: when using with RTOS, this should be called after scheduler/kernel is started.
-// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
-bool tusb_init(void);
+// Otherwise, it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+// Note2: defined as macro for backward compatible with tusb_init(void), can be changed to function in the future.
+#if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT)
+ #define _tusb_init_arg0() tusb_rhport_init(0, NULL)
+#else
+ #define _tusb_init_arg0() TU_VERIFY_STATIC(false, "CFG_TUSB_RHPORT0_MODE/CFG_TUSB_RHPORT1_MODE must be defined")
+#endif
+
+#define _tusb_init_arg1(_rhport) _tusb_init_arg0()
+#define _tusb_init_arg2(_rhport, _rh_init) tusb_rhport_init(_rhport, _rh_init)
+#define tusb_init(...) TU_FUNC_OPTIONAL_ARG(_tusb_init, __VA_ARGS__)
// Check if stack is initialized
bool tusb_inited(void);
+// Called to handle usb interrupt/event. tusb_init(rhport, role) must be called before
+void tusb_int_handler(uint8_t rhport, bool in_isr);
+
// TODO
// bool tusb_teardown(void);
+#else
+
+#define tusb_init(...) (false)
+#define tusb_int_handler(...) do {}while(0)
+#define tusb_inited() (false)
+
+#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
diff --git a/src/tusb_option.h b/src/tusb_option.h
index b905fc7ce..84319939a 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -27,14 +27,14 @@
#ifndef _TUSB_OPTION_H_
#define _TUSB_OPTION_H_
-// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C)
-typedef int make_iso_compilers_happy;
-
#include "common/tusb_compiler.h"
+// Version is release as major.minor.revision eg 1.0.0
#define TUSB_VERSION_MAJOR 0
-#define TUSB_VERSION_MINOR 14
+#define TUSB_VERSION_MINOR 18
#define TUSB_VERSION_REVISION 0
+
+#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
//--------------------------------------------------------------------+
@@ -53,9 +53,13 @@ typedef int make_iso_compilers_happy;
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
-#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
-#define OPT_MCU_LPC54XXX 10 ///< NXP LPC54xxx
-#define OPT_MCU_LPC55XX 11 ///< NXP LPC55xx
+#define OPT_MCU_LPC51 9 ///< NXP LPC51
+#define OPT_MCU_LPC51UXX OPT_MCU_LPC51 ///< NXP LPC51
+#define OPT_MCU_LPC54 10 ///< NXP LPC54
+#define OPT_MCU_LPC55 11 ///< NXP LPC55
+// legacy naming
+#define OPT_MCU_LPC54XXX OPT_MCU_LPC54
+#define OPT_MCU_LPC55XX OPT_MCU_LPC55
// NRF
#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
@@ -85,6 +89,11 @@ typedef int make_iso_compilers_happy;
#define OPT_MCU_STM32G4 311 ///< ST G4
#define OPT_MCU_STM32WB 312 ///< ST WB
#define OPT_MCU_STM32U5 313 ///< ST U5
+#define OPT_MCU_STM32L5 314 ///< ST L5
+#define OPT_MCU_STM32H5 315 ///< ST H5
+#define OPT_MCU_STM32U0 316 ///< ST U0
+#define OPT_MCU_STM32H7RS 317 ///< ST F7RS
+#define OPT_MCU_STM32C0 318 ///< ST C0
// Sony
#define OPT_MCU_CXD56 400 ///< SONY CXD56
@@ -99,9 +108,9 @@ typedef int make_iso_compilers_happy;
#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config
// NXP iMX RT
-#define OPT_MCU_MIMXRT 700 ///< NXP iMX RT Series
-#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT ///< RT10xx
-#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT ///< RT11xx
+#define OPT_MCU_MIMXRT1XXX 700 ///< NXP iMX RT1xxx Series
+#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT1XXX ///< RT10xx
+#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT1XXX ///< RT11xx
// Nuvoton
#define OPT_MCU_NUC121 800
@@ -112,6 +121,14 @@ typedef int make_iso_compilers_happy;
// Espressif
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
+#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e)
+#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3
+#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6
+#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 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
// Dialog
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
@@ -120,8 +137,13 @@ typedef int make_iso_compilers_happy;
#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
// NXP Kinetis
-#define OPT_MCU_MKL25ZXX 1200 ///< NXP MKL25Zxx
-#define OPT_MCU_K32L2BXX 1201 ///< NXP K32L2Bxx
+#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series
+#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series
+#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L
+#define OPT_MCU_KINETIS_K 1202 ///< NXP K series
+
+#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete)
+#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete)
// Silabs
#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG
@@ -130,6 +152,7 @@ typedef int make_iso_compilers_happy;
#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631
#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651
#define OPT_MCU_RX72N 1402 ///< Renesas RX72N
+#define OPT_MCU_RAXXX 1403 ///< Renesas RA generic
// Mind Motion
#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327
@@ -160,10 +183,26 @@ typedef int make_iso_compilers_happy;
// Allwinner
#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family
-// Helper to check if configured MCU is one of listed
+// WCH
+#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
+#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
+#define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X
+#define OPT_MCU_CH32V103 2230 ///< WCH CH32V103
+
+// NXP LPC MCX
+#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
+#define OPT_MCU_MCXA15 2301 ///< NXP MCX A15 Series
+
+// Analog Devices
+#define OPT_MCU_MAX32690 2400 ///< ADI MAX32690
+#define OPT_MCU_MAX32666 2401 ///< ADI MAX32666/5
+#define OPT_MCU_MAX32650 2402 ///< ADI MAX32650/1/2
+#define OPT_MCU_MAX78002 2403 ///< ADI MAX78002
+
+// Check if configured MCU is one of listed
// Apply _TU_CHECK_MCU with || as separator to list of input
-#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
-#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
+#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
+#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
//--------------------------------------------------------------------+
// Supported OS
@@ -176,20 +215,11 @@ typedef int make_iso_compilers_happy;
#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
#define OPT_OS_RTTHREAD 6 ///< RT-Thread
#define OPT_OS_RTX4 7 ///< Keil RTX 4
+#define OPT_OS_ZEPHYR 8 ///< Zephyr
-// Allow to use command line to change the config name/location
-#ifdef CFG_TUSB_CONFIG_FILE
- #include CFG_TUSB_CONFIG_FILE
-#else
- #include "tusb_config.h"
-#endif
-
-#include "common/tusb_mcu.h"
-
-//--------------------------------------------------------------------
-// RootHub Mode Configuration
-// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
-//--------------------------------------------------------------------
+//--------------------------------------------------------------------+
+// Mode and Speed
+//--------------------------------------------------------------------+
// Low byte is operational mode
#define OPT_MODE_NONE 0x0000 ///< Disabled
@@ -203,7 +233,78 @@ typedef int make_iso_compilers_happy;
#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
#define OPT_MODE_SPEED_MASK 0xff00
-//------------- Roothub as Device -------------//
+//--------------------------------------------------------------------+
+// Include tusb_config.h
+//--------------------------------------------------------------------+
+
+// Allow to use command line to change the config name/location
+#ifdef CFG_TUSB_CONFIG_FILE
+ #include CFG_TUSB_CONFIG_FILE
+#else
+ #include "tusb_config.h"
+#endif
+
+#include "common/tusb_mcu.h"
+
+//--------------------------------------------------------------------+
+// USBIP
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUD_DWC2_SLAVE_ENABLE
+ #ifndef CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT
+ #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT 1
+ #endif
+
+ #define CFG_TUD_DWC2_SLAVE_ENABLE CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 DMA for device
+#ifndef CFG_TUD_DWC2_DMA_ENABLE
+ #ifndef CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
+ #define CFG_TUD_DWC2_DMA_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_DWC2_DMA_ENABLE CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 Slave mode for host
+#ifndef CFG_TUH_DWC2_SLAVE_ENABLE
+ #ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT
+ #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT 1
+ #endif
+
+ #define CFG_TUH_DWC2_SLAVE_ENABLE CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT
+#endif
+
+// Enable DWC2 DMA for host
+#ifndef CFG_TUH_DWC2_DMA_ENABLE
+ #ifndef CFG_TUH_DWC2_DMA_ENABLE_DEFAULT
+ #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUH_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE_DEFAULT
+#endif
+
+// Enable PIO-USB software host controller
+#ifndef CFG_TUH_RPI_PIO_USB
+ #define CFG_TUH_RPI_PIO_USB 0
+#endif
+
+#ifndef CFG_TUD_RPI_PIO_USB
+ #define CFG_TUD_RPI_PIO_USB 0
+#endif
+
+// MAX3421 Host controller option
+#ifndef CFG_TUH_MAX3421
+ #define CFG_TUH_MAX3421 0
+#endif
+
+
+//--------------------------------------------------------------------
+// RootHub Mode detection
+//--------------------------------------------------------------------
+
+//------------- Root hub as Device -------------//
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
@@ -231,7 +332,7 @@ typedef int make_iso_compilers_happy;
// highspeed support indicator
#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
-//------------- Roothub as Host -------------//
+//------------- Root hub as Host -------------//
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
@@ -256,6 +357,10 @@ typedef int make_iso_compilers_happy;
// For backward compatible
#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED
+// highspeed support indicator
+#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
+
+
//--------------------------------------------------------------------+
// TODO move later
//--------------------------------------------------------------------+
@@ -264,7 +369,7 @@ typedef int make_iso_compilers_happy;
// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler
// to generate unaligned access code.
// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM
-#if TUD_OPT_HIGH_SPEED && (CFG_TUSB_MCU == OPT_MCU_LPC54XXX || CFG_TUSB_MCU == OPT_MCU_LPC55XX)
+#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX)
#define TUP_MCU_STRICT_ALIGN 1
#else
#define TUP_MCU_STRICT_ALIGN 0
@@ -280,32 +385,75 @@ typedef int make_iso_compilers_happy;
#define CFG_TUSB_DEBUG 0
#endif
-// place data in accessible RAM for usb controller
+// Level where CFG_TUSB_DEBUG must be at least for USBH is logged
+#ifndef CFG_TUH_LOG_LEVEL
+ #define CFG_TUH_LOG_LEVEL 2
+#endif
+
+// Level where CFG_TUSB_DEBUG must be at least for USBD is logged
+#ifndef CFG_TUD_LOG_LEVEL
+ #define CFG_TUD_LOG_LEVEL 2
+#endif
+
+// Memory section for placing buffer used for usb transferring. If MEM_SECTION is different for
+// host and device use: CFG_TUD_MEM_SECTION, CFG_TUH_MEM_SECTION instead
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
-// alignment requirement of buffer used for endpoint transferring
+// Alignment requirement of buffer used for usb transferring. if MEM_ALIGN is different for
+// host and device controller use: CFG_TUD_MEM_ALIGN, CFG_TUH_MEM_ALIGN instead
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
+#ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE
+ #ifndef CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 32
+ #endif
+
+ #define CFG_TUSB_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT
+#endif
+
// OS selection
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_OS_INC_PATH
- #define CFG_TUSB_OS_INC_PATH
-#endif
+ #ifndef CFG_TUSB_OS_INC_PATH_DEFAULT
+ #define CFG_TUSB_OS_INC_PATH_DEFAULT
+ #endif
-// mutex is only needed for RTOS TODO also required with multiple core MCUs
-#define TUSB_OPT_MUTEX (CFG_TUSB_OS != OPT_OS_NONE)
+ #define CFG_TUSB_OS_INC_PATH CFG_TUSB_OS_INC_PATH_DEFAULT
+#endif
//--------------------------------------------------------------------
// Device Options (Default)
//--------------------------------------------------------------------
+// Attribute to place data in accessible RAM for device controller (default: CFG_TUSB_MEM_SECTION)
+#ifndef CFG_TUD_MEM_SECTION
+ #define CFG_TUD_MEM_SECTION CFG_TUSB_MEM_SECTION
+#endif
+
+// Attribute to align memory for device controller (default: CFG_TUSB_MEM_ALIGN)
+#ifndef CFG_TUD_MEM_ALIGN
+ #define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN
+#endif
+
+#ifndef CFG_TUD_MEM_DCACHE_ENABLE
+ #ifndef CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT
+ #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_MEM_DCACHE_ENABLE CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT
+#endif
+
+#ifndef CFG_TUD_MEM_DCACHE_LINE_SIZE
+ #define CFG_TUD_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE
+#endif
+
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
@@ -314,6 +462,29 @@ typedef int make_iso_compilers_happy;
#define CFG_TUD_INTERFACE_MAX 16
#endif
+// default to max hardware endpoint, but can be smaller to save RAM
+#ifndef CFG_TUD_ENDPPOINT_MAX
+ #define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX
+#endif
+
+#if CFG_TUD_ENDPPOINT_MAX > TUP_DCD_ENDPOINT_MAX
+ #error "CFG_TUD_ENDPPOINT_MAX must be less than or equal to TUP_DCD_ENDPOINT_MAX"
+#endif
+
+// USB 2.0 7.1.20: compliance test mode support
+#ifndef CFG_TUD_TEST_MODE
+ #define CFG_TUD_TEST_MODE 0
+#endif
+
+//------------- Device Class Driver -------------//
+#ifndef CFG_TUD_BTH
+ #define CFG_TUD_BTH 0
+#endif
+
+#if CFG_TUD_BTH && !defined(CFG_TUD_BTH_ISO_ALT_COUNT)
+#error CFG_TUD_BTH_ISO_ALT_COUNT must be defined to tell Bluetooth driver the number of ISO endpoints to use
+#endif
+
#ifndef CFG_TUD_CDC
#define CFG_TUD_CDC 0
#endif
@@ -354,10 +525,6 @@ typedef int make_iso_compilers_happy;
#define CFG_TUD_DFU 0
#endif
-#ifndef CFG_TUD_BTH
- #define CFG_TUD_BTH 0
-#endif
-
#ifndef CFG_TUD_ECM_RNDIS
#ifdef CFG_TUD_NET
#warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS"
@@ -384,45 +551,108 @@ typedef int make_iso_compilers_happy;
#endif
#endif // CFG_TUH_ENABLED
+// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION)
+#ifndef CFG_TUH_MEM_SECTION
+ #define CFG_TUH_MEM_SECTION CFG_TUSB_MEM_SECTION
+#endif
+
+// Attribute to align memory for host controller
+#ifndef CFG_TUH_MEM_ALIGN
+ #define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN
+#endif
+
+#ifndef CFG_TUH_MEM_DCACHE_ENABLE
+ #ifndef CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT
+ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT 0
+ #endif
+
+ #define CFG_TUH_MEM_DCACHE_ENABLE CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT
+#endif
+
+#ifndef CFG_TUH_MEM_DCACHE_LINE_SIZE
+ #define CFG_TUH_MEM_DCACHE_LINE_SIZE CFG_TUSB_MEM_DCACHE_LINE_SIZE
+#endif
+
//------------- CLASS -------------//
#ifndef CFG_TUH_HUB
-#define CFG_TUH_HUB 0
+ #define CFG_TUH_HUB 0
#endif
#ifndef CFG_TUH_CDC
-#define CFG_TUH_CDC 0
+ #define CFG_TUH_CDC 0
+#endif
+
+// FTDI is not part of CDC class, only to re-use CDC driver API
+#ifndef CFG_TUH_CDC_FTDI
+ #define CFG_TUH_CDC_FTDI 0
+#endif
+
+// List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
+#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
+ #define CFG_TUH_CDC_FTDI_VID_PID_LIST \
+ {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
+ {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
+ {0x0403, 0xCD18}
+#endif
+
+// CP210X is not part of CDC class, only to re-use CDC driver API
+#ifndef CFG_TUH_CDC_CP210X
+ #define CFG_TUH_CDC_CP210X 0
+#endif
+
+// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
+#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
+ #define CFG_TUH_CDC_CP210X_VID_PID_LIST \
+ {0x10C4, 0xEA60}, {0x10C4, 0xEA70}
+#endif
+
+#ifndef CFG_TUH_CDC_CH34X
+ // CH34X is not part of CDC class, only to re-use CDC driver API
+ #define CFG_TUH_CDC_CH34X 0
+#endif
+
+// List of product IDs that can use the CH34X CDC driver
+#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
+ #define CFG_TUH_CDC_CH34X_VID_PID_LIST \
+ { 0x1a86, 0x5523 }, /* ch341 chip */ \
+ { 0x1a86, 0x7522 }, /* ch340k chip */ \
+ { 0x1a86, 0x7523 }, /* ch340 chip */ \
+ { 0x1a86, 0xe523 }, /* ch330 chip */ \
+ { 0x4348, 0x5523 }, /* ch340 custom chip */ \
+ { 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \
+ { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
#endif
#ifndef CFG_TUH_HID
-#define CFG_TUH_HID 0
+ #define CFG_TUH_HID 0
#endif
#ifndef CFG_TUH_MIDI
-#define CFG_TUH_MIDI 0
+ #define CFG_TUH_MIDI 0
#endif
#ifndef CFG_TUH_MSC
-#define CFG_TUH_MSC 0
+ #define CFG_TUH_MSC 0
#endif
#ifndef CFG_TUH_VENDOR
-#define CFG_TUH_VENDOR 0
+ #define CFG_TUH_VENDOR 0
#endif
#ifndef CFG_TUH_API_EDPT_XFER
-#define CFG_TUH_API_EDPT_XFER 0
+ #define CFG_TUH_API_EDPT_XFER 0
#endif
-// Enable PIO-USB software host controller
-#ifndef CFG_TUH_RPI_PIO_USB
-#define CFG_TUH_RPI_PIO_USB 0
-#endif
+//--------------------------------------------------------------------+
+// TypeC Options (Default)
+//--------------------------------------------------------------------+
-#ifndef CFG_TUD_RPI_PIO_USB
-#define CFG_TUD_RPI_PIO_USB 0
-#endif
+#ifndef CFG_TUC_ENABLED
+#define CFG_TUC_ENABLED 0
+#define tuc_int_handler(_p)
+#endif
//------------------------------------------------------------------
// Configuration Validation
@@ -431,6 +661,9 @@ typedef int make_iso_compilers_happy;
// #error Control Endpoint Max Packet Size cannot be larger than 64
//#endif
+// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C)
+typedef int make_iso_compilers_happy;
+
#endif /* _TUSB_OPTION_H_ */
/** @} */
diff --git a/src/typec/pd_types.h b/src/typec/pd_types.h
new file mode 100644
index 000000000..1b2968f65
--- /dev/null
+++ b/src/typec/pd_types.h
@@ -0,0 +1,237 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_PD_TYPES_H_
+#define _TUSB_PD_TYPES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "common/tusb_compiler.h"
+
+// Start of all packed definitions for compiler without per-type packed
+TU_ATTR_PACKED_BEGIN
+TU_ATTR_BIT_FIELD_ORDER_BEGIN
+
+//--------------------------------------------------------------------+
+// TYPE-C
+//--------------------------------------------------------------------+
+
+typedef enum {
+ TUSB_TYPEC_PORT_SRC,
+ TUSB_TYPEC_PORT_SNK,
+ TUSB_TYPEC_PORT_DRP
+} tusb_typec_port_type_t;
+
+enum {
+ PD_CTRL_RESERVED = 0, // 0b00000: 0
+ PD_CTRL_GOOD_CRC, // 0b00001: 1
+ PD_CTRL_GO_TO_MIN, // 0b00010: 2
+ PD_CTRL_ACCEPT, // 0b00011: 3
+ PD_CTRL_REJECT, // 0b00100: 4
+ PD_CTRL_PING, // 0b00101: 5
+ PD_CTRL_PS_READY, // 0b00110: 6
+ PD_CTRL_GET_SOURCE_CAP, // 0b00111: 7
+ PD_CTRL_GET_SINK_CAP, // 0b01000: 8
+ PD_CTRL_DR_SWAP, // 0b01001: 9
+ PD_CTRL_PR_SWAP, // 0b01010: 10
+ PD_CTRL_VCONN_SWAP, // 0b01011: 11
+ PD_CTRL_WAIT, // 0b01100: 12
+ PD_CTRL_SOFT_RESET, // 0b01101: 13
+ PD_CTRL_DATA_RESET, // 0b01110: 14
+ PD_CTRL_DATA_RESET_COMPLETE, // 0b01111: 15
+ PD_CTRL_NOT_SUPPORTED, // 0b10000: 16
+ PD_CTRL_GET_SOURCE_CAP_EXTENDED, // 0b10001: 17
+ PD_CTRL_GET_STATUS, // 0b10010: 18
+ PD_CTRL_FR_SWAP, // 0b10011: 19
+ PD_CTRL_GET_PPS_STATUS, // 0b10100: 20
+ PD_CTRL_GET_COUNTRY_CODES, // 0b10101: 21
+ PD_CTRL_GET_SINK_CAP_EXTENDED, // 0b10110: 22
+ PD_CTRL_GET_SOURCE_INFO, // 0b10111: 23
+ PD_CTRL_REVISION, // 0b11000: 24
+};
+
+enum {
+ PD_DATA_RESERVED = 0, // 0b00000: 0
+ PD_DATA_SOURCE_CAP, // 0b00001: 1
+ PD_DATA_REQUEST, // 0b00010: 2
+ PD_DATA_BIST, // 0b00011: 3
+ PD_DATA_SINK_CAP, // 0b00100: 4
+ PD_DATA_BATTERY_STATUS, // 0b00101: 5
+ PD_DATA_ALERT, // 0b00110: 6
+ PD_DATA_GET_COUNTRY_INFO, // 0b00111: 7
+ PD_DATA_ENTER_USB, // 0b01000: 8
+ PD_DATA_EPR_REQUEST, // 0b01001: 9
+ PD_DATA_EPR_MODE, // 0b01010: 10
+ PD_DATA_SRC_INFO, // 0b01011: 11
+ PD_DATA_REVISION, // 0b01100: 12
+ PD_DATA_RESERVED_13, // 0b01101: 13
+ PD_DATA_RESERVED_14, // 0b01110: 14
+ PD_DATA_VENDOR_DEFINED, // 0b01111: 15
+};
+
+enum {
+ PD_REV_10 = 0x0,
+ PD_REV_20 = 0x1,
+ PD_REV_30 = 0x2,
+};
+
+enum {
+ PD_DATA_ROLE_UFP = 0x0,
+ PD_DATA_ROLE_DFP = 0x1,
+};
+
+enum {
+ PD_POWER_ROLE_SINK = 0x0,
+ PD_POWER_ROLE_SOURCE = 0x1,
+};
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t msg_type : 5; // [0:4]
+ uint16_t data_role : 1; // [5] SOP only: 0 UFP, 1 DFP
+ uint16_t specs_rev : 2; // [6:7]
+ uint16_t power_role : 1; // [8] SOP only: 0 Sink, 1 Source
+ uint16_t msg_id : 3; // [9:11]
+ uint16_t n_data_obj : 3; // [12:14]
+ uint16_t extended : 1; // [15]
+} pd_header_t;
+TU_VERIFY_STATIC(sizeof(pd_header_t) == 2, "size is not correct");
+
+typedef struct TU_ATTR_PACKED {
+ uint16_t data_size : 9; // [0:8]
+ uint16_t reserved : 1; // [9]
+ uint16_t request_chunk : 1; // [10]
+ uint16_t chunk_number : 4; // [11:14]
+ uint16_t chunked : 1; // [15]
+} pd_header_extended_t;
+TU_VERIFY_STATIC(sizeof(pd_header_extended_t) == 2, "size is not correct");
+
+//--------------------------------------------------------------------+
+// Source Capability
+//--------------------------------------------------------------------+
+
+// All table references are from USBPD Specification rev3.1 version 1.8
+enum {
+ PD_PDO_TYPE_FIXED = 0, // Vmin = Vmax
+ PD_PDO_TYPE_BATTERY,
+ PD_PDO_TYPE_VARIABLE, // non-battery
+ PD_PDO_TYPE_APDO, // Augmented Power Data Object
+};
+
+// Fixed Power Data Object (PDO) table 6-9
+typedef struct TU_ATTR_PACKED {
+ uint32_t current_max_10ma : 10; // [9..0] Max current in 10mA unit
+ uint32_t voltage_50mv : 10; // [19..10] Voltage in 50mV unit
+ uint32_t current_peak : 2; // [21..20] Peak current
+ uint32_t reserved : 1; // [22] Reserved
+ uint32_t epr_mode_capable : 1; // [23] epr_mode_capable
+ uint32_t unchunked_ext_msg_support : 1; // [24] UnChunked Extended Message Supported
+ uint32_t dual_role_data : 1; // [25] Dual Role Data
+ uint32_t usb_comm_capable : 1; // [26] USB Communications Capable
+ uint32_t unconstrained_power : 1; // [27] Unconstrained Power
+ uint32_t usb_suspend_supported : 1; // [28] USB Suspend Supported
+ uint32_t dual_role_power : 1; // [29] Dual Role Power
+ uint32_t type : 2; // [30] Fixed Supply type = PD_PDO_TYPE_FIXED
+} pd_pdo_fixed_t;
+TU_VERIFY_STATIC(sizeof(pd_pdo_fixed_t) == 4, "Invalid size");
+
+// Battery Power Data Object (PDO) table 6-12
+typedef struct TU_ATTR_PACKED {
+ uint32_t power_max_250mw : 10; // [9..0] Max allowable power in 250mW unit
+ uint32_t voltage_min_50mv : 10; // [19..10] Minimum voltage in 50mV unit
+ uint32_t voltage_max_50mv : 10; // [29..20] Maximum voltage in 50mV unit
+ uint32_t type : 2; // [31..30] Battery type = PD_PDO_TYPE_BATTERY
+} pd_pdo_battery_t;
+TU_VERIFY_STATIC(sizeof(pd_pdo_battery_t) == 4, "Invalid size");
+
+// Variable Power Data Object (PDO) table 6-11
+typedef struct TU_ATTR_PACKED {
+ uint32_t current_max_10ma : 10; // [9..0] Max current in 10mA unit
+ uint32_t voltage_min_50mv : 10; // [19..10] Minimum voltage in 50mV unit
+ uint32_t voltage_max_50mv : 10; // [29..20] Maximum voltage in 50mV unit
+ uint32_t type : 2; // [31..30] Variable Supply type = PD_PDO_TYPE_VARIABLE
+} pd_pdo_variable_t;
+TU_VERIFY_STATIC(sizeof(pd_pdo_variable_t) == 4, "Invalid size");
+
+// Augmented Power Data Object (PDO) table 6-13
+typedef struct TU_ATTR_PACKED {
+ uint32_t current_max_50ma : 7; // [6..0] Max current in 50mA unit
+ uint32_t reserved1 : 1; // [7] Reserved
+ uint32_t voltage_min_100mv : 8; // [15..8] Minimum Voltage in 100mV unit
+ uint32_t reserved2 : 1; // [16] Reserved
+ uint32_t voltage_max_100mv : 8; // [24..17] Maximum Voltage in 100mV unit
+ uint32_t reserved3 : 2; // [26..25] Reserved
+ uint32_t pps_power_limited : 1; // [27] PPS Power Limited
+ uint32_t spr_programmable : 2; // [29..28] SPR Programmable Power Supply
+ uint32_t type : 2; // [31..30] Augmented Power Data Object = PD_PDO_TYPE_APDO
+} pd_pdo_apdo_t;
+TU_VERIFY_STATIC(sizeof(pd_pdo_apdo_t) == 4, "Invalid size");
+
+//--------------------------------------------------------------------+
+// Request
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t current_extremum_10ma : 10; // [9..0] Max (give back = 0) or Min (give back = 1) current in 10mA unit
+ uint32_t current_operate_10ma : 10; // [19..10] Operating current in 10mA unit
+ uint32_t reserved : 2; // [21..20] Reserved
+ uint32_t epr_mode_capable : 1; // [22] EPR mode capable
+ uint32_t unchunked_ext_msg_support : 1; // [23] UnChunked Extended Message Supported
+ uint32_t no_usb_suspend : 1; // [24] No USB Suspend
+ uint32_t usb_comm_capable : 1; // [25] USB Communications Capable
+ uint32_t capability_mismatch : 1; // [26] Capability Mismatch
+ uint32_t give_back_flag : 1; // [27] GiveBack Flag: 0 = Max, 1 = Min
+ uint32_t object_position : 4; // [31..28] Object Position
+} pd_rdo_fixed_variable_t;
+TU_VERIFY_STATIC(sizeof(pd_rdo_fixed_variable_t) == 4, "Invalid size");
+
+typedef struct TU_ATTR_PACKED {
+ uint32_t power_extremum_250mw : 10; // [9..0] Max (give back = 0) or Min (give back = 1) operating power in 250mW unit
+ uint32_t power_operate_250mw : 10; // [19..10] Operating power in 250mW unit
+ uint32_t reserved : 2; // [21..20] Reserved
+ uint32_t epr_mode_capable : 1; // [22] EPR mode capable
+ uint32_t unchunked_ext_msg_support : 1; // [23] UnChunked Extended Message Supported
+ uint32_t no_usb_suspend : 1; // [24] No USB Suspend
+ uint32_t usb_comm_capable : 1; // [25] USB Communications Capable
+ uint32_t capability_mismatch : 1; // [26] Capability Mismatch
+ uint32_t give_back_flag : 1; // [27] GiveBack Flag: 0 = Max, 1 = Min
+ uint32_t object_position : 4; // [31..28] Object Position
+} pd_rdo_battery_t;
+TU_VERIFY_STATIC(sizeof(pd_rdo_battery_t) == 4, "Invalid size");
+
+
+TU_ATTR_PACKED_END // End of all packed definitions
+TU_ATTR_BIT_FIELD_ORDER_END
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/typec/tcd.h b/src/typec/tcd.h
new file mode 100644
index 000000000..bcbdab8ed
--- /dev/null
+++ b/src/typec/tcd.h
@@ -0,0 +1,143 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach ([email protected]) for Adafruit Industries
+ *
+ * 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_TCD_H_
+#define _TUSB_TCD_H_
+
+#include "common/tusb_common.h"
+#include "pd_types.h"
+
+#include "osal/osal.h"
+#include "common/tusb_fifo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+enum {
+ TCD_EVENT_INVALID = 0,
+ TCD_EVENT_CC_CHANGED,
+ TCD_EVENT_RX_COMPLETE,
+ TCD_EVENT_TX_COMPLETE,
+};
+
+typedef struct TU_ATTR_PACKED {
+ uint8_t rhport;
+ uint8_t event_id;
+
+ union {
+ struct {
+ uint8_t cc_state[2];
+ } cc_changed;
+
+ struct TU_ATTR_PACKED {
+ uint16_t result : 2;
+ uint16_t xferred_bytes : 14;
+ } xfer_complete;
+ };
+
+} tcd_event_t;
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+// Initialize controller
+bool tcd_init(uint8_t rhport, uint32_t port_type);
+
+// Enable interrupt
+void tcd_int_enable (uint8_t rhport);
+
+// Disable interrupt
+void tcd_int_disable(uint8_t rhport);
+
+// Interrupt Handler
+void tcd_int_handler(uint8_t rhport);
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+bool tcd_msg_receive(uint8_t rhport, uint8_t* buffer, uint16_t total_bytes);
+bool tcd_msg_send(uint8_t rhport, uint8_t const* buffer, uint16_t total_bytes);
+
+//--------------------------------------------------------------------+
+// Event API (implemented by stack)
+// Called by TCD to notify stack
+//--------------------------------------------------------------------+
+
+extern void tcd_event_handler(tcd_event_t const * event, bool in_isr);
+
+TU_ATTR_ALWAYS_INLINE static inline
+void tcd_event_cc_changed(uint8_t rhport, uint8_t cc1, uint8_t cc2, bool in_isr) {
+ tcd_event_t event = {
+ .rhport = rhport,
+ .event_id = TCD_EVENT_CC_CHANGED,
+ .cc_changed = {
+ .cc_state = {cc1, cc2 }
+ }
+ };
+
+ tcd_event_handler(&event, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline
+void tcd_event_rx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t result, bool in_isr) {
+ tcd_event_t event = {
+ .rhport = rhport,
+ .event_id = TCD_EVENT_RX_COMPLETE,
+ .xfer_complete = {
+ .xferred_bytes = xferred_bytes,
+ .result = result
+ }
+ };
+
+ tcd_event_handler(&event, in_isr);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline
+void tcd_event_tx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t result, bool in_isr) {
+ tcd_event_t event = {
+ .rhport = rhport,
+ .event_id = TCD_EVENT_TX_COMPLETE,
+ .xfer_complete = {
+ .xferred_bytes = xferred_bytes,
+ .result = result
+ }
+ };
+
+ tcd_event_handler(&event, in_isr);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/typec/usbc.c b/src/typec/usbc.c
new file mode 100644
index 000000000..fdf2a0cd6
--- /dev/null
+++ b/src/typec/usbc.c
@@ -0,0 +1,219 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach ([email protected])
+ *
+ * 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_TUC_ENABLED
+
+#include "tcd.h"
+#include "usbc.h"
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+// Debug level of USBD
+#define USBC_DEBUG 2
+#define TU_LOG_USBC(...) TU_LOG(USBC_DEBUG, __VA_ARGS__)
+
+// Event queue
+// usbc_int_set() is used as mutex in OS NONE config
+void usbc_int_set(bool enabled);
+OSAL_QUEUE_DEF(usbc_int_set, _usbc_qdef, CFG_TUC_TASK_QUEUE_SZ, tcd_event_t);
+tu_static osal_queue_t _usbc_q;
+
+// if stack is initialized
+static bool _usbc_inited = false;
+
+// if port is initialized
+static bool _port_inited[TUP_TYPEC_RHPORTS_NUM];
+
+// Max possible PD size is 262 bytes
+static uint8_t _rx_buf[64] TU_ATTR_ALIGNED(4);
+static uint8_t _tx_buf[64] TU_ATTR_ALIGNED(4);
+
+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);
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+bool tuc_inited(uint8_t rhport) {
+ return _usbc_inited && _port_inited[rhport];
+}
+
+bool tuc_init(uint8_t rhport, uint32_t port_type) {
+ // Initialize stack
+ if (!_usbc_inited) {
+ tu_memclr(_port_inited, sizeof(_port_inited));
+
+ _usbc_q = osal_queue_create(&_usbc_qdef);
+ TU_ASSERT(_usbc_q != NULL);
+
+ _usbc_inited = true;
+ }
+
+ // skip if port already initialized
+ if ( _port_inited[rhport] ) {
+ return true;
+ }
+
+ TU_LOG_USBC("USBC init on port %u\r\n", rhport);
+ TU_LOG_INT(USBC_DEBUG, sizeof(tcd_event_t));
+
+ TU_ASSERT(tcd_init(rhport, port_type));
+ tcd_int_enable(rhport);
+
+ _port_inited[rhport] = true;
+ return true;
+}
+
+void tuc_task_ext(uint32_t timeout_ms, bool in_isr) {
+ (void) in_isr; // not implemented yet
+
+ // Skip if stack is not initialized
+ if (!_usbc_inited) return;
+
+ // Loop until there is no more events in the queue
+ while (1) {
+ tcd_event_t event;
+ if (!osal_queue_receive(_usbc_q, &event, timeout_ms)) return;
+
+ switch (event.event_id) {
+ case TCD_EVENT_CC_CHANGED:
+ break;
+
+ case TCD_EVENT_RX_COMPLETE:
+ // TODO process message here in ISR, move to thread later
+ if (event.xfer_complete.result == XFER_RESULT_SUCCESS) {
+ pd_header_t const* header = (pd_header_t const*) _rx_buf;
+
+ if (header->n_data_obj == 0) {
+ parse_msg_control(event.rhport, header);
+
+ }else {
+ uint8_t const* p_end = _rx_buf + event.xfer_complete.xferred_bytes;
+ uint8_t const * dobj = _rx_buf + sizeof(pd_header_t);
+
+ parse_msg_data(event.rhport, header, dobj, p_end);
+ }
+ }
+
+ // prepare for next message
+ tcd_msg_receive(event.rhport, _rx_buf, sizeof(_rx_buf));
+ break;
+
+ case TCD_EVENT_TX_COMPLETE:
+ break;
+
+ default: break;
+ }
+ }
+}
+
+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);
+ }
+
+ 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);
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+bool usbc_msg_send(uint8_t rhport, pd_header_t const* header, void const* data) {
+ // copy header
+ memcpy(_tx_buf, header, sizeof(pd_header_t));
+
+ // copy data objcet if available
+ uint16_t const n_data_obj = header->n_data_obj;
+ if (n_data_obj > 0) {
+ memcpy(_tx_buf + sizeof(pd_header_t), data, n_data_obj * 4);
+ }
+
+ return tcd_msg_send(rhport, _tx_buf, sizeof(pd_header_t) + n_data_obj * 4);
+}
+
+bool tuc_msg_request(uint8_t rhport, void const* rdo) {
+ pd_header_t const header = {
+ .msg_type = PD_DATA_REQUEST,
+ .data_role = PD_DATA_ROLE_UFP,
+ .specs_rev = PD_REV_30,
+ .power_role = PD_POWER_ROLE_SINK,
+ .msg_id = 0,
+ .n_data_obj = 1,
+ .extended = 0,
+ };
+
+ return usbc_msg_send(rhport, &header, rdo);
+}
+
+void tcd_event_handler(tcd_event_t const * event, bool in_isr) {
+ (void) in_isr;
+ switch(event->event_id) {
+ case TCD_EVENT_CC_CHANGED:
+ if (event->cc_changed.cc_state[0] || event->cc_changed.cc_state[1]) {
+ // Attach, start receiving
+ tcd_msg_receive(event->rhport, _rx_buf, sizeof(_rx_buf));
+ }else {
+ // Detach
+ }
+ break;
+
+ default: break;
+ }
+
+ osal_queue_send(_usbc_q, event, in_isr);
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+void usbc_int_set(bool enabled) {
+ // Disable all controllers since they shared the same event queue
+ for (uint8_t p = 0; p < TUP_TYPEC_RHPORTS_NUM; p++) {
+ if ( _port_inited[p] ) {
+ if (enabled) {
+ tcd_int_enable(p);
+ }else {
+ tcd_int_disable(p);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/src/typec/usbc.h b/src/typec/usbc.h
new file mode 100644
index 000000000..9fbff9bc6
--- /dev/null
+++ b/src/typec/usbc.h
@@ -0,0 +1,91 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_UTCD_H_
+#define _TUSB_UTCD_H_
+
+#include "common/tusb_common.h"
+#include "pd_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TypeC Configuration
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUC_TASK_QUEUE_SZ
+#define CFG_TUC_TASK_QUEUE_SZ 8
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// Init typec stack on a port
+bool tuc_init(uint8_t rhport, uint32_t port_type);
+
+// Check if typec port is initialized
+bool tuc_inited(uint8_t rhport);
+
+// Task function should be called in main/rtos loop, extended version of tud_task()
+// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever
+// - in_isr: if function is called in ISR
+void tuc_task_ext(uint32_t timeout_ms, bool in_isr);
+
+// Task function should be called in main/rtos loop
+TU_ATTR_ALWAYS_INLINE static inline
+void tuc_task (void) {
+ tuc_task_ext(UINT32_MAX, false);
+}
+
+#ifndef _TUSB_TCD_H_
+extern void tcd_int_handler(uint8_t rhport);
+#endif
+
+// Interrupt handler, name alias to TCD
+#define tuc_int_handler tcd_int_handler
+
+//--------------------------------------------------------------------+
+// 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_msg_request(uint8_t rhport, void const* rdo);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif