summaryrefslogtreecommitdiff
path: root/components/drivers/usb/cherryusb/class
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2024-06-02 11:20:13 +0800
committerGitHub <[email protected]>2024-06-02 11:20:13 +0800
commit62d321caa028dee04bf0b43fc0abb68b40e80ddd (patch)
tree31fa88156af25b434a194398154dc8027d6b413d /components/drivers/usb/cherryusb/class
parent658813213e5ad1dc81739746f6001099c6cc9e41 (diff)
refactor(drivers/usb):replace rtt usb stack with cherryusb (#8799)
Diffstat (limited to 'components/drivers/usb/cherryusb/class')
-rw-r--r--components/drivers/usb/cherryusb/class/audio/usb_audio.h1165
-rw-r--r--components/drivers/usb/cherryusb/class/audio/usbd_audio.c344
-rw-r--r--components/drivers/usb/cherryusb/class/audio/usbd_audio.h43
-rw-r--r--components/drivers/usb/cherryusb/class/audio/usbh_audio.c483
-rw-r--r--components/drivers/usb/cherryusb/class/audio/usbh_audio.h76
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usb_cdc.h698
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c118
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h29
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c247
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h42
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c263
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.h50
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c319
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.h49
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c388
-rw-r--r--components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.h53
-rw-r--r--components/drivers/usb/cherryusb/class/dfu/usb_dfu.h137
-rw-r--r--components/drivers/usb/cherryusb/class/dfu/usbd_dfu.c505
-rw-r--r--components/drivers/usb/cherryusb/class/dfu/usbd_dfu.h27
-rw-r--r--components/drivers/usb/cherryusb/class/hid/usb_hid.h585
-rw-r--r--components/drivers/usb/cherryusb/class/hid/usbd_hid.c89
-rw-r--r--components/drivers/usb/cherryusb/class/hid/usbd_hid.h34
-rw-r--r--components/drivers/usb/cherryusb/class/hid/usbh_hid.c231
-rw-r--r--components/drivers/usb/cherryusb/class/hid/usbh_hid.h41
-rw-r--r--components/drivers/usb/cherryusb/class/hub/usb_hub.h121
-rw-r--r--components/drivers/usb/cherryusb/class/hub/usbh_hub.c711
-rw-r--r--components/drivers/usb/cherryusb/class/hub/usbh_hub.h33
-rw-r--r--components/drivers/usb/cherryusb/class/midi/usb_midi.h240
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usb_msc.h89
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usb_scsi.h972
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usbd_msc.c938
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usbd_msc.h32
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usbh_msc.c436
-rw-r--r--components/drivers/usb/cherryusb/class/msc/usbh_msc.h49
-rw-r--r--components/drivers/usb/cherryusb/class/template/usb_xxx.h4
-rw-r--r--components/drivers/usb/cherryusb/class/template/usbd_xxx.c39
-rw-r--r--components/drivers/usb/cherryusb/class/template/usbd_xxx.h16
-rw-r--r--components/drivers/usb/cherryusb/class/template/usbh_xxx.c97
-rw-r--r--components/drivers/usb/cherryusb/class/template/usbh_xxx.h22
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c774
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.h176
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c2249
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.h67
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c356
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.h76
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c295
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.h73
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c363
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.h76
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c426
-rw-r--r--components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.h62
-rw-r--r--components/drivers/usb/cherryusb/class/video/usb_video.h1198
-rw-r--r--components/drivers/usb/cherryusb/class/video/usbd_video.c789
-rw-r--r--components/drivers/usb/cherryusb/class/video/usbd_video.h29
-rw-r--r--components/drivers/usb/cherryusb/class/video/usbh_video.c522
-rw-r--r--components/drivers/usb/cherryusb/class/video/usbh_video.h85
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/ndis.h270
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/rndis_protocol.h302
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c566
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h32
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c402
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.h56
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c561
-rw-r--r--components/drivers/usb/cherryusb/class/wireless/usbh_rndis.h51
64 files changed, 19671 insertions, 0 deletions
diff --git a/components/drivers/usb/cherryusb/class/audio/usb_audio.h b/components/drivers/usb/cherryusb/class/audio/usb_audio.h
new file mode 100644
index 0000000000..2b3cdaffa8
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/audio/usb_audio.h
@@ -0,0 +1,1165 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_AUDIO_H
+#define USB_AUDIO_H
+
+/** Audio Interface Subclass Codes
+ * Refer to Table A-2 from audio10.pdf
+ */
+#define AUDIO_SUBCLASS_UNDEFINED 0x00
+#define AUDIO_SUBCLASS_AUDIOCONTROL 0x01
+#define AUDIO_SUBCLASS_AUDIOSTREAMING 0x02
+#define AUDIO_SUBCLASS_MIDISTREAMING 0x03
+
+#define AUDIO_PROTOCOL_UNDEFINED 0x00
+#define AUDIO_PROTOCOLv20 0x20 /* IP version 2.0 */
+
+/** Audio Class-Specific Request Codes
+ * Refer to Table A-9 from audio10.pdf
+ */
+#define AUDIO_REQUEST_UNDEFINED 0x00
+#define AUDIO_REQUEST_SET_CUR 0x01
+#define AUDIO_REQUEST_GET_CUR 0x81
+#define AUDIO_REQUEST_SET_MIN 0x02
+#define AUDIO_REQUEST_GET_MIN 0x82
+#define AUDIO_REQUEST_SET_MAX 0x03
+#define AUDIO_REQUEST_GET_MAX 0x83
+#define AUDIO_REQUEST_SET_RES 0x04
+#define AUDIO_REQUEST_GET_RES 0x84
+#define AUDIO_REQUEST_SET_MEM 0x05
+#define AUDIO_REQUEST_GET_MEM 0x85
+#define AUDIO_REQUEST_GET_STAT 0xFF
+
+/** Audio Class-Specific Request Codes
+ * Refer from audio20_final.pdf
+ */
+#define AUDIO_REQUEST_CUR 0x01
+#define AUDIO_REQUEST_RANGE 0x02
+/** Audio Class-Specific Control Interface Descriptor Subtypes
+ * Refer to Table A-5 from audio10.pdf
+ */
+#define AUDIO_CONTROL_UNDEF 0x01U
+#define AUDIO_CONTROL_HEADER 0x01U
+#define AUDIO_CONTROL_INPUT_TERMINAL 0x02U
+#define AUDIO_CONTROL_OUTPUT_TERMINAL 0x03U
+#define AUDIO_CONTROL_MIXER_UNIT 0x04U
+#define AUDIO_CONTROL_SELECTOR_UNIT 0x05U
+#define AUDIO_CONTROL_FEATURE_UNIT 0x06U
+#define AUDIO_CONTROL_EFFECT_UNIT 0x07U
+#define AUDIO_CONTROL_PROCESSING_UNIT 0x08U
+#define AUDIO_CONTROL_EXTENSION_UNIT 0x09U
+#define AUDIO_CONTROL_CLOCK_SOURCE 0x0aU
+#define AUDIO_CONTROL_CLOCK_SELECTOR 0x0bU
+#define AUDIO_CONTROL_CLOCK_MULTIPLIER 0x0cU
+#define AUDIO_CONTROL_SAMPLERATE_CONVERTER 0x0dU
+
+/** Audio Class-Specific AS Interface Descriptor Subtypes
+ * Refer to Table A-6 from audio10.pdf
+ */
+#define AUDIO_STREAMING_UNDEFINED 0x00U
+#define AUDIO_STREAMING_GENERAL 0x01U
+#define AUDIO_STREAMING_FORMAT_TYPE 0x02U
+#define AUDIO_STREAMING_ENCODER 0x03U
+#define AUDIO_STREAMING_DECODER 0x04U
+
+/* Clock Source Descriptor Clock Types */
+#define AUDIO_CLKSRC_EXTERNAL 0x00 /* External clock */
+#define AUDIO_CLKSRC_INTERNAL_FIXED 0x01 /* Internal fixed clock */
+#define AUDIO_CLKSRC_INTERNAL_VAR 0x02 /* Internal variable clock */
+#define AUDIO_CLKSRC_INTERNAL_PROG 0x03 /* Internal programmable clock */
+
+/* Effect Unit Effect Types */
+#define AUDIO_EFFECT_UNDEF 0x00
+#define AUDIO_EFFECT_PARAM_EQ_SECTION 0x01
+#define AUDIO_EFFECT_REVERBERATION 0x02
+#define AUDIO_EFFECT_MOD_DELAY 0x03
+#define AUDIO_EFFECT_DYN_RANGE_COMP 0x04
+
+/* Processing Unit Process Types */
+#define AUDIO_PROCESS_UNDEFINED 0x00
+#define AUDIO_PROCESS_UPDOWNMIX 0x01
+#define AUDIO_PROCESS_DOLBY_PROLOGIC 0x02
+#define AUDIO_PROCESS_STEREO_EXTENDER 0x03
+
+/* Audio Class-Specific Endpoint Descriptor Subtypes */
+#define AUDIO_ENDPOINT_UNDEFINED 0x00U
+#define AUDIO_ENDPOINT_GENERAL 0x01U
+
+/* Feature Unit Control Bits */
+#define AUDIO_CONTROL_MUTE 0x0001
+#define AUDIO_CONTROL_VOLUME 0x0002
+#define AUDIO_CONTROL_BASS 0x0004
+#define AUDIO_CONTROL_MID 0x0008
+#define AUDIO_CONTROL_TREBLE 0x0010
+#define AUDIO_CONTROL_GRAPHIC_EQUALIZER 0x0020
+#define AUDIO_CONTROL_AUTOMATIC_GAIN 0x0040
+#define AUDIO_CONTROL_DEALY 0x0080
+#define AUDIO_CONTROL_BASS_BOOST 0x0100
+#define AUDIO_CONTROL_LOUDNESS 0x0200
+
+/* Encoder Type Codes */
+#define AUDIO_ENCODER_UNDEF 0x00
+#define AUDIO_ENCODER_OTHER 0x01
+#define AUDIO_ENCODER_MPEG 0x02
+#define AUDIO_ENCODER_AC3 0x03
+#define AUDIO_ENCODER_WMA 0x04
+#define AUDIO_ENCODER_DTS 0x05
+
+/* Decoder Type Codes */
+#define AUDIO_DECODER_UNDEF 0x00
+#define AUDIO_DECODER_OTHER 0x01
+#define AUDIO_DECODER_MPEG 0x02
+#define AUDIO_DECODER_AC3 0x03
+#define AUDIO_DECODER_WMA 0x04
+#define AUDIO_DECODER_DTS 0x05
+
+/* Audio Descriptor Types */
+#define AUDIO_UNDEFINED_DESCRIPTOR_TYPE 0x20
+#define AUDIO_DEVICE_DESCRIPTOR_TYPE 0x21
+#define AUDIO_CONFIGURATION_DESCRIPTOR_TYPE 0x22
+#define AUDIO_STRING_DESCRIPTOR_TYPE 0x23
+#define AUDIO_INTERFACE_DESCRIPTOR_TYPE 0x24
+#define AUDIO_ENDPOINT_DESCRIPTOR_TYPE 0x25
+
+/* Audio Data Format Type I Codes */
+#define AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0000
+#define AUDIO_FORMAT_PCM 0x0001
+#define AUDIO_FORMAT_PCM8 0x0002
+#define AUDIO_FORMAT_IEEE_FLOAT 0x0003
+#define AUDIO_FORMAT_ALAW 0x0004
+#define AUDIO_FORMAT_MULAW 0x0005
+
+#define AUDIO_V2_FORMAT_PCM 0x00000001
+#define AUDIO_V2_FORMAT_PCM8 0x00000002
+#define AUDIO_V2_FORMAT_IEEE_FLOAT 0x00000004
+#define AUDIO_V2_FORMAT_ALAW 0x00000008
+#define AUDIO_V2_FORMAT_MULAW 0x00000010
+
+/* bmChannelConfig: a bitmap field that indicates which spatial locations
+ * are occupied by the channels present in the cluster. The bit allocations
+ * are as follows:
+ */
+#define AUDIO_CHANNEL_M 0 /* Mono */
+#define AUDIO_CHANNEL_FL (1 << 0) /* Front Left */
+#define AUDIO_CHANNEL_FR (1 << 1) /* Front Right */
+#define AUDIO_CHANNEL_FC (1 << 2) /* Front Center */
+#define AUDIO_CHANNEL_LFE (1 << 3) /* Low Frequency Effects */
+#define AUDIO_CHANNEL_BL (1 << 4) /* Back Left */
+#define AUDIO_CHANNEL_BR (1 << 5) /* Back Right */
+#define AUDIO_CHANNEL_FLC (1 << 6) /* Front Left of Center */
+#define AUDIO_CHANNEL_FRC (1 << 7) /* Front Right of Center */
+#define AUDIO_CHANNEL_BC (1 << 8) /* Back Center */
+#define AUDIO_CHANNEL_SL (1 << 9) /* Side Left */
+#define AUDIO_CHANNEL_SR (1 << 10) /* Side Right */
+#define AUDIO_CHANNEL_TC (1 << 11) /* Top Center */
+#define AUDIO_CHANNEL_TFL (1 << 12) /* Top Front Left */
+#define AUDIO_CHANNEL_TFC (1 << 13) /* Top Front Center */
+#define AUDIO_CHANNEL_TFR (1 << 14) /* Top Front Right */
+#define AUDIO_CHANNEL_TBL (1 << 15) /* Top Back Left */
+#define AUDIO_CHANNEL_TBC (1 << 16) /* Top Back Center */
+#define AUDIO_CHANNEL_TBR (1 << 17) /* Top Back Right */
+#define AUDIO_CHANNEL_TFLC (1 << 18) /* Top Front Left of Center */
+#define AUDIO_CHANNEL_TFRC (1 << 19) /* Top Front Right of Center */
+#define AUDIO_CHANNEL_LLFE (1 << 20) /* Left Low Frequency Effects */
+#define AUDIO_CHANNEL_RLFE (1 << 21) /* Right Low Frequency Effects */
+#define AUDIO_CHANNEL_TSL (1 << 22) /* Top Side Left */
+#define AUDIO_CHANNEL_TSR (1 << 23) /* Top Side Right */
+#define AUDIO_CHANNEL_BOC (1 << 24) /* Bottom Center */
+#define AUDIO_CHANNEL_BLC (1 << 25) /* Back Left of Center */
+#define AUDIO_CHANNEL_BRC (1 << 26) /* Back Right of Center */
+ /* Bits 27-30: Reserved */
+#define AUDIO_CHANNEL_RD (1 << 31) /* Raw Data */
+
+/* Audio Function Category Codes */
+#define AUDIO_CATEGORY_UNDEF 0x00 /* Undefined */
+#define AUDIO_CATEGORY_SPEAKER 0x01 /* Desktop speaker */
+#define AUDIO_CATEGORY_THEATER 0x02 /* Home theater */
+#define AUDIO_CATEGORY_MICROPHONE 0x03 /* Microphone */
+#define AUDIO_CATEGORY_HEADSET 0x04 /* Headset */
+#define AUDIO_CATEGORY_TELEPHONE 0x05 /* Telephone */
+#define AUDIO_CATEGORY_CONVERTER 0x06 /* Converter */
+#define AUDIO_CATEGORY_RECORDER 0x07 /* Voice/Sound recorder */
+#define AUDIO_CATEGORY_IO_BOX 0x08 /* I/O box */
+#define AUDIO_CATEGORY_INSTRUMENT 0x09 /* Musical instrument */
+#define AUDIO_CATEGORY_PROAUDIO 0x0a /* Pro-audio */
+#define AUDIO_CATEGORY_AV 0x0b /* Audio/video */
+#define AUDIO_CATEGORY_CONTROL 0x0c /* Control panel */
+#define AUDIO_CATEGORY_OTHER 0xff
+
+/* Clock Source Control Selectors */
+#define AUDIO_CS_CONTROL_UNDEF 0x00
+#define AUDIO_CS_CONTROL_SAM_FREQ 0x01
+#define AUDIO_CS_CONTROL_CLOCK_VALID 0x02
+
+/* Clock Selector Control Selectors */
+#define AUDIO_CX_CONTROL_UNDEF 0x00
+#define AUDIO_CX_CONTROL_CLOCKSEL 0x01
+
+/* Clock Multiplier Control Selectors */
+#define AUDIO_CM_CONTROL_UNDEF 0x00
+#define AUDIO_CM_CONTROL_NUMERATOR 0x01
+#define AUDIO_CM_CONTROL_DENOMINATOR 0x02
+
+/* Terminal Control Selectors */
+#define AUDIO_TE_CONTROL_UNDEF 0x00
+#define AUDIO_TE_CONTROL_COPY_PROTECT 0x01
+#define AUDIO_TE_CONTROL_CONNECTOR 0x02
+#define AUDIO_TE_CONTROL_OVERLOAD 0x03
+#define AUDIO_TE_CONTROL_CLUSTER 0x04
+#define AUDIO_TE_CONTROL_UNDERFLOW 0x05
+#define AUDIO_TE_CONTROL_OVERFLOW 0x06
+#define AUDIO_TE_CONTROL_LATENCY 0x07
+
+/* Mixer Control Selectors */
+#define AUDIO_MU_CONTROL_UNDEF 0x00
+#define AUDIO_MU_CONTROL_MIXER 0x01
+#define AUDIO_MU_CONTROL_CLUSTER 0x02
+#define AUDIO_MU_CONTROL_UNDERFLOW 0x03
+#define AUDIO_MU_CONTROL_OVERFLOW 0x04
+#define AUDIO_MU_CONTROL_LATENCY 0x05
+
+/* Selector Control Selectors */
+#define AUDIO_SU_CONTROL_UNDEFINE 0x00
+#define AUDIO_SU_CONTROL_SELECTOR 0x01
+#define AUDIO_SU_CONTROL_LATENCY 0x02
+
+/* Feature Unit Control Selectors */
+#define AUDIO_FU_CONTROL_UNDEF 0x00
+#define AUDIO_FU_CONTROL_MUTE 0x01
+#define AUDIO_FU_CONTROL_VOLUME 0x02
+#define AUDIO_FU_CONTROL_BASS 0x03
+#define AUDIO_FU_CONTROL_MID 0x04
+#define AUDIO_FU_CONTROL_TREBLE 0x05
+#define AUDIO_FU_CONTROL_EQUALIZER 0x06
+#define AUDIO_FU_CONTROL_AGC 0x07
+#define AUDIO_FU_CONTROL_DELAY 0x08
+#define AUDIO_FU_CONTROL_BASS_BOOST 0x09
+#define AUDIO_FU_CONTROL_LOUDNESS 0x0a
+#define AUDIO_FU_CONTROL_INP_GAIN 0x0b
+#define AUDIO_FU_CONTROL_INP_GAIN_PAD 0x0c
+#define AUDIO_FU_CONTROL_PHASE_INVERT 0x0d
+#define AUDIO_FU_CONTROL_UNDERFLOW 0x0e
+#define AUDIO_FU_CONTROL_OVERFLOW 0x0f
+#define AUDIO_FU_CONTROL_LATENCY 0x10
+
+#define AUDIO_V2_FU_CONTROL_UNDEF 0x00
+#define AUDIO_V2_FU_CONTROL_MUTE (0x03 << 0)
+#define AUDIO_V2_FU_CONTROL_VOLUME (0x03 << 2)
+#define AUDIO_V2_FU_CONTROL_BASS (0x03 << 4)
+#define AUDIO_V2_FU_CONTROL_MID (0x03 << 6)
+#define AUDIO_V2_FU_CONTROL_TREBLE (0x03 << 8)
+#define AUDIO_V2_FU_CONTROL_EQUALIZER (0x03 << 10)
+#define AUDIO_V2_FU_CONTROL_AGC (0x03 << 12)
+#define AUDIO_V2_FU_CONTROL_DELAY (0x03 << 14)
+#define AUDIO_V2_FU_CONTROL_BASS_BOOST (0x03 << 16)
+#define AUDIO_V2_FU_CONTROL_LOUDNESS (0x03 << 18)
+#define AUDIO_V2_FU_CONTROL_INP_GAIN (0x03 << 20)
+#define AUDIO_V2_FU_CONTROL_INP_GAIN_PAD (0x03 << 22)
+#define AUDIO_V2_FU_CONTROL_PHASE_INVERT (0x03 << 24)
+#define AUDIO_V2_FU_CONTROL_UNDERFLOW (0x03 << 26)
+#define AUDIO_V2_FU_CONTROL_OVERFLOW (0x03 << 28)
+
+/* Parametric Equalizer Section Effect Unit Control Selectors */
+#define AUDIO_PE_CONTROL_UNDEF 0x00
+#define AUDIO_PE_CONTROL_ENABLE 0x01
+#define AUDIO_PE_CONTROL_CENTERFREQ 0x02
+#define AUDIO_PE_CONTROL_QFACTOR 0x03
+#define AUDIO_PE_CONTROL_GAIN 0x04
+#define AUDIO_PE_CONTROL_UNDERFLOW 0x05
+#define AUDIO_PE_CONTROL_OVERFLOW 0x06
+#define AUDIO_PE_CONTROL_LATENCY 0x07
+
+/* Reverberation Effect Unit Control Selectors */
+#define AUDIO_RV_CONTROL_UNDEF 0x00
+#define AUDIO_RV_CONTROL_ENABLE 0x01
+#define AUDIO_RV_CONTROL_TYPE 0x02
+#define AUDIO_RV_CONTROL_LEVEL 0x03
+#define AUDIO_RV_CONTROL_TIME 0x04
+#define AUDIO_RV_CONTROL_FEEDBACK 0x05
+#define AUDIO_RV_CONTROL_PREDELAY 0x06
+#define AUDIO_RV_CONTROL_DENSITY 0x07
+#define AUDIO_RV_CONTROL_HF_ROLLOFF 0x08
+#define AUDIO_RV_CONTROL_UNDERFLOW 0x09
+#define AUDIO_RV_CONTROL_OVERFLOW 0x0a
+#define AUDIO_RV_CONTROL_LATENCY 0x0b
+
+/* Modulation Delay Effect Unit Control Selectors */
+#define AUDIO_MD_CONTROL_UNDEF 0x00
+#define AUDIO_MD_CONTROL_ENABLE 0x01
+#define AUDIO_MD_CONTROL_BALANCE 0x02
+#define AUDIO_MD_CONTROL_RATE 0x03
+#define AUDIO_MD_CONTROL_DEPTH 0x04
+#define AUDIO_MD_CONTROL_TIME 0x05
+#define AUDIO_MD_CONTROL_FEEDBACK 0x06
+#define AUDIO_MD_CONTROL_UNDERFLOW 0x07
+#define AUDIO_MD_CONTROL_OVERFLOW 0x08
+#define AUDIO_MD_CONTROL_LATENCY 0x09
+
+/* Dynamic Range Compressor Effect Unit Control Selectors */
+#define AUDIO_DR_CONTROL_UNDEF 0x00
+#define AUDIO_DR_CONTROL_ENABLE 0x01
+#define AUDIO_DR_CONTROL_COMP_RATE 0x02
+#define AUDIO_DR_CONTROL_MAXAMPL 0x03
+#define AUDIO_DR_CONTROL_THRESHOLD 0x04
+#define AUDIO_DR_CONTROL_ATTACK_TIME 0x05
+#define AUDIO_DR_CONTROL_RELEASE_TIME 0x06
+#define AUDIO_DR_CONTROL_UNDERFLOW 0x07
+#define AUDIO_DR_CONTROL_OVERFLOW 0x08
+#define AUDIO_DR_CONTROL_LATENCY 0x09
+
+/* Up/Down-mix Processing Unit Control Selectors */
+#define AUDIO_UD_CONTROL_UNDEF 0x00
+#define AUDIO_UD_CONTROL_ENABLE 0x01
+#define AUDIO_UD_CONTROL_MODE_SELECT 0x02
+#define AUDIO_UD_CONTROL_CLUSTER 0x03
+#define AUDIO_UD_CONTROL_UNDERFLOW 0x04
+#define AUDIO_UD_CONTROL_OVERFLOW 0x05
+#define AUDIO_UD_CONTROL_LATENCY 0x06
+
+/* Dolby Prologic?Processing Unit Control Selectors */
+#define AUDIO_DP_CONTROL_UNDEF 0x00
+#define AUDIO_DP_CONTROL_ENABLE 0x01
+#define AUDIO_DP_CONTROL_MODE_SELECT 0x02
+#define AUDIO_DP_CONTROL_CLUSTER 0x03
+#define AUDIO_DP_CONTROL_UNDERFLOW 0x04
+#define AUDIO_DP_CONTROL_OVERFLOW 0x05
+#define AUDIO_DP_CONTROL_LATENCY 0x06
+
+/* Stereo Extender Processing Unit Control Selectors */
+#define AUDIO_STEXT_CONTROL_UNDEF 0x00
+#define AUDIO_STEXT_CONTROL_ENABLE 0x01
+#define AUDIO_STEXT_CONTROL_WIDTH 0x02
+#define AUDIO_STEXT_CONTROL_UNDERFLOW 0x03
+#define AUDIO_STEXT_CONTROL_OVERFLOW 0x04
+#define AUDIO_STEXT_CONTROL_LATENCY 0x05
+
+/* Extension Unit Control Selectors */
+
+#define AUDIO_XU_CONTROL_UNDEF 0x00
+#define AUDIO_XU_CONTROL_ENABLE 0x01
+#define AUDIO_XU_CONTROL_CLUSTER 0x02
+#define AUDIO_XU_CONTROL_UNDERFLOW 0x03
+#define AUDIO_XU_CONTROL_OVERFLOW 0x04
+#define AUDIO_XU_CONTROL_LATENCY 0x05
+
+/* AudioStreaming Interface Control Selectors */
+
+#define AUDIO_AS_CONTROL_UNDEF 0x00
+#define AUDIO_AS_CONTROL_ACT_ALT 0x01
+#define AUDIO_AS_CONTROL_VAL_ALT 0x02
+#define AUDIO_AS_CONTROL_AUDIO_FORMAT 0x03
+
+/* Encoder Control Selectors */
+
+#define AUDIO_EN_CONTROL_UNDEF 0x00
+#define AUDIO_EN_CONTROL_BIT_RATE 0x01
+#define AUDIO_EN_CONTROL_QUALITY 0x02
+#define AUDIO_EN_CONTROL_VBR 0x03
+#define AUDIO_EN_CONTROL_TYPE 0x04
+#define AUDIO_EN_CONTROL_UNDERFLOW 0x05
+#define AUDIO_EN_CONTROL_OVERFLOW 0x06
+#define AUDIO_EN_CONTROL_ENCODER_ERR 0x07
+#define AUDIO_EN_CONTROL_PARAM1 0x08
+#define AUDIO_EN_CONTROL_PARAM2 0x09
+#define AUDIO_EN_CONTROL_PARAM3 0x0a
+#define AUDIO_EN_CONTROL_PARAM4 0x0b
+#define AUDIO_EN_CONTROL_PARAM5 0x0c
+#define AUDIO_EN_CONTROL_PARAM6 0x0d
+#define AUDIO_EN_CONTROL_PARAM7 0x0e
+#define AUDIO_EN_CONTROL_PARAM8 0x0f
+
+/* MPEG Decoder Control Selectors */
+
+#define AUDIO_MPGD_CONTROL_UNDEF 0x00
+#define AUDIO_MPGD_CONTROL_DUAL_CHAN 0x01
+#define AUDIO_MPGD_CONTROL_2ND_STEREO 0x02
+#define AUDIO_MPGD_CONTROL_MULTILING 0x03
+#define AUDIO_MPGD_CONTROL_DYN_RANGE 0x04
+#define AUDIO_MPGD_CONTROL_SCALING 0x05
+#define AUDIO_MPGD_CONTROL_HILO_SCALE 0x06
+#define AUDIO_MPGD_CONTROL_UNDERFLOW 0x07
+#define AUDIO_MPGD_CONTROL_OVERFLOW 0x08
+#define AUDIO_MPGD_CONTROL_DECODE_ERR 0x09
+
+/* AC-3 Decoder Control Selectors */
+
+#define AUDIO_AC3D_CONTROL_UNDEF 0x00
+#define AUDIO_AC3D_CONTROL_MODE 0x01
+#define AUDIO_AC3D_CONTROL_DYN_RANGE 0x02
+#define AUDIO_AC3D_CONTROL_SCALING 0x03
+#define AUDIO_AC3D_CONTROL_HILO_SCALE 0x04
+#define AUDIO_AC3D_CONTROL_UNDERFLOW 0x05
+#define AUDIO_AC3D_CONTROL_OVERFLOW 0x06
+#define AUDIO_AC3D_CONTROL_DECODE_ERR 0x07
+
+/* WMA Decoder Control Selectors */
+
+#define AUDIO_WMAD_CONTROL_UNDEF 0x00
+#define AUDIO_WMAD_CONTROL_UNDERFLOW 0x01
+#define AUDIO_WMAD_CONTROL_OVERFLOW 0x02
+#define AUDIO_WMAD_CONTROL_DECODE_ERR 0x03
+
+/* DTS Decoder Control Selectors */
+
+#define AUDIO_DTSD_CONTROL_UNDEF 0x00
+#define AUDIO_DTSD_CONTROL_UNDERFLOW 0x01
+#define AUDIO_DTSD_CONTROL_OVERFLOW 0x02
+#define AUDIO_DTSD_CONTROL_DECODE_ERR 0x03
+
+/* Endpoint Control Selectors */
+#define AUDIO_EP_CONTROL_UNDEF 0x00
+#define AUDIO_EP_CONTROL_SAMPLING_FEQ 0x01
+#define AUDIO_EP_CONTROL_PITCH 0x02
+
+/* Encoder Error Codes */
+
+/* <0: Reserved for vendor extensions */
+
+#define AUDIO_ENCODER_SUCCESS 0 /* No Error */
+#define AUDIO_ENCODER_ERROR_NOMEM 1 /* Out of Memory */
+#define AUDIO_ENCODER_ERROR_BW 2 /* Out of Bandwidth */
+#define AUDIO_ENCODER_ERROR_CYCLE 3 /* Out of Processing Cycles */
+#define AUDIO_ENCODER_ERROR_FRAME 4 /* General Format Frame Error */
+#define AUDIO_ENCODER_ERROR_TOOSMALL 5 /* Format Frame Too Small */
+#define AUDIO_ENCODER_ERROR_TOOBIG 6 /* Format Frame Too Large */
+#define AUDIO_ENCODER_ERROR_BADFORMAT 7 /* Bad Data Format */
+#define AUDIO_ENCODER_ERROR_NCHAN 8 /* Incorrect Number of Channels */
+#define AUDIO_ENCODER_ERROR_RATE 9 /* Incorrect Sampling Rate */
+#define AUDIO_ENCODER_ERROR_BITRATE 10 /* Unable to Meet Target Bitrate */
+#define AUDIO_ENCODER_ERROR_PARMS 11 /* Inconsistent Set of Parameters */
+#define AUDIO_ENCODER_ERROR_NOTREADY 12 /* Not Ready */
+#define AUDIO_ENCODER_ERROR_BUSY 13 /* Busy */
+ /* >13: Reserved */
+
+/* Format Type Codes */
+
+#define AUDIO_FORMAT_TYPE_UNDEF 0x00
+#define AUDIO_FORMAT_TYPEI 0x01
+#define AUDIO_FORMAT_TYPEII 0x02
+#define AUDIO_FORMAT_TYPEIII 0x03
+#define AUDIO_FORMAT_TYPEIV 0x04
+#define AUDIO_FORMAT_EXT_TYPEI 0x81
+#define AUDIO_FORMAT_EXT_TYPEII 0x82
+#define AUDIO_FORMAT_EXT_TYPEIII 0x83
+
+/* Audio Data Format Type I Bit Allocations */
+
+#define AUDIO_FORMAT_TYPEI_PCM (1 << 0)
+#define AUDIO_FORMAT_TYPEI_PCM8 (1 << 1)
+#define AUDIO_FORMAT_TYPEI_IEEEFLOAT (1 << 2)
+#define AUDIO_FORMAT_TYPEI_ALAW (1 << 3)
+#define AUDIO_FORMAT_TYPEI_MULAW (1 << 4)
+#define AUDIO_FORMAT_TYPEI_RAWDATA (1 << 31)
+
+/* Audio Data Format Type II Bit Allocations */
+
+#define AUDIO_FORMAT_TYPEII_MPEG (1 << 0)
+#define AUDIO_FORMAT_TYPEII_AC3 (1 << 1)
+#define AUDIO_FORMAT_TYPEII_WMA (1 << 2)
+#define AUDIO_FORMAT_TYPEII_DTS (1 << 3)
+#define AUDIO_FORMAT_TYPEII_RAWDATA (1 << 31)
+
+/* Audio Data Format Type III Bit Allocations */
+
+#define AUDIO_FORMAT_TYPEIII_IEC61937_AC3 (1 << 0)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG1_L1 (1 << 1)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG1_L2_3 (1 << 1)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG2_NOEXT (1 << 2)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG2_EXT (1 << 3)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG2_AAC_ADTS (1 << 4)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG2_L1_LS (1 << 5)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_MPEG2_L2_3_LS (1 << 6)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_DTS_I (1 << 7)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_DTS_II (1 << 8)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_DTS_III (1 << 9)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_ATRAC (1 << 10)
+#define AUDIO_FORMAT_TYPEIII_IEC61937_ATRAC2_3 (1 << 11)
+#define AUDIO_FORMAT_TYPEIII_WMA (1 << 12)
+
+/* Audio Data Format Type IV Bit Allocations */
+
+#define AUDIO_FORMAT_TYPEIV_PCM (1 << 0)
+#define AUDIO_FORMAT_TYPEIV_PCM8 (1 << 1)
+#define AUDIO_FORMAT_TYPEIV_IEEE_FLOAT (1 << 2)
+#define AUDIO_FORMAT_TYPEIV_ALAW (1 << 3)
+#define AUDIO_FORMAT_TYPEIV_MULAW (1 << 4)
+#define AUDIO_FORMAT_TYPEIV_MPEG (1 << 5)
+#define AUDIO_FORMAT_TYPEIV_AC3 (1 << 6)
+#define AUDIO_FORMAT_TYPEIV_WMA (1 << 7)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_AC3 (1 << 8)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG1_L1 (1 << 9)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG1_L2_3 (1 << 10)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG2_NOEXT (1 << 10)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG2_EXT (1 << 11)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG2_AAC_ADTS (1 << 12)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG2_L1_LS (1 << 13)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_MPEG2_L2_3_LS (1 << 14)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_DTS_I (1 << 15)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_DTS_II (1 << 16)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_DTS_III (1 << 17)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_ATRAC (1 << 18)
+#define AUDIO_FORMAT_TYPEIV_IEC61937_ATRAC2_3 (1 << 19)
+#define AUDIO_FORMAT_TYPEIV_TYPE_III_WMA (1 << 20)
+#define AUDIO_FORMAT_TYPEIV_IEC60958_PCM (1 << 21)
+
+/* Side Band Protocol Codes */
+#define AUDIO_SIDEBAND_PROTOCOL_UNDEF 0x00
+#define AUDIO_PRES_TIMESTAMP_PROTOCOL 0x01
+
+/** USB Terminal Types
+ * Refer to Table 2-1 - Table 2-4 from termt10.pdf
+ */
+
+/* USB Terminal Types */
+#define AUDIO_TERMINAL_UNDEF 0x0100
+#define AUDIO_TERMINAL_STREAMING 0x0101
+#define AUDIO_TERMINAL_VENDOR 0x01ff
+
+/* Input Terminal Types */
+#define AUDIO_INTERM_UNDEF 0x0200 /* Undefined Type */
+#define AUDIO_INTERM_MIC 0x0201 /* A generic microhpone */
+#define AUDIO_INTERM_DESKTOP_MIC 0x0202 /* A desktop microphone */
+#define AUDIO_INTERM_PERSONAL_MIC 0x0203 /* Head-mounted or clip-on microphone */
+#define AUDIO_INTERM_OMNI_MIC 0x0204 /* Omni-directional microphone */
+#define AUDIO_INTERM_MIC_ARRAY 0x0205 /* Microphone array */
+#define AUDIO_INTERM_PROC_MIC_ARRAY 0x0206 /* Microphone array with signal processor */
+
+/* Output Terminal Types */
+#define AUDIO_OUTTERM_UNDEF 0x0300 /* Undefined Type */
+#define AUDIO_OUTTERM_SPEAKER 0x0301 /* Generic speakers */
+#define AUDIO_OUTTERM_HEADPHONES 0x0302 /* A head-mounted audio output device */
+#define AUDIO_OUTTERM_HEADDISPLAY 0x0303 /* Head Mounted Display Audio */
+#define AUDIO_OUTTERM_DESKTOP 0x0304 /* Desktop speaker */
+#define AUDIO_OUTTERM_ROOM 0x0305 /* Room speaker */
+#define AUDIO_OUTTERM_COMMS 0x0306 /* Communication speaker */
+#define AUDIO_OUTTERM_LOFREQ 0x0307 /* Low frequency effects speaker */
+
+/* Bi-directional Terminal Types */
+#define AUDIO_BIDITERM_UNDEF 0x0400 /* Undefined Type */
+#define AUDIO_BIDITERM_HANDSET 0x0401 /* Hand-held bi-directional audio device */
+#define AUDIO_BIDITERM_HEADSET 0x0402 /* Head-mounted bi-directional audio device */
+#define AUDIO_BIDITERM_SPEAKERPHONE 0x0403 /* Speakerphone, no echo reduction */
+#define AUDIO_BIDITERM_ECHOSUPPRESS 0x0404 /* Echo-suppressing speakerphone */
+#define AUDIO_BIDITERM_ECHOCANCEL 0x0405 /* Echo-canceling speakerphone */
+
+/* Telephony Terminal Types */
+#define AUDIO_TELETERM_UNDEF 0x0500 /* Undefined Type */
+#define AUDIO_TELETERM_PHONELINE 0x0501 /* Analog telephone line jack, an ISDN line,
+ * a proprietary PBX interface, or a wireless link */
+#define AUDIO_TELETERM_TELEPHONE 0x0502 /* Device can be used as a telephone */
+#define AUDIO_TELETERM_DOWNLINE 0x0503 /* Down Line Phone */
+
+/* External Terminal Types */
+#define AUDIO_EXTTERM_UNDEF 0x0600 /* Undefined Type */
+#define AUDIO_EXTTERM_ANALOG 0x0601 /* Generic analog connector */
+#define AUDIO_EXTTERM_DIGITAL 0x0602 /* Generic digital audio interface */
+#define AUDIO_EXTTERM_LINE 0x0603 /* Analog connector at standard line levels */
+#define AUDIO_EXTTERM_LEGACY 0x0604 /* Legacy audio line out connector */
+#define AUDIO_EXTTERM_SPDIF 0x0605 /* SPDIF interface */
+#define AUDIO_EXTTERM_1394DA 0x0606 /* 1394 DA stream */
+#define AUDIO_EXTTERM_1394DV 0x0607 /* 1394 DV stream soundtrack */
+#define AUDIO_EXTTERM_ADAT 0x0608 /* ADAT Lightpipe */
+#define AUDIO_EXTTERM_TDIF 0x0609 /* TDIF - Tascam Digital Interface */
+#define AUDIO_EXTTERM_MADI 0x060a /* MADI - Multi-channel Audio Digital Interface (AES) */
+
+/* Embedded Function Terminal Types */
+#define AUDIO_EMBEDTERM_UNDEF 0x0700 /* Undefined Type */
+#define AUDIO_EMBEDTERM_CALIBRATION 0x0701 /* Level Calibration Noise Source */
+#define AUDIO_EMBEDTERM_EQUALIZATION 0x0702 /* Equalization Noise */
+#define AUDIO_EMBEDTERM_CD 0x0703 /* CD player */
+#define AUDIO_EMBEDTERM_DAT 0x0704 /* Digital Audio Tape */
+#define AUDIO_EMBEDTERM_DCC 0x0705 /* Digital Compact Cassette */
+#define AUDIO_EMBEDTERM_COMPRESSED 0x0706 /* Compressed Audio Player */
+#define AUDIO_EMBEDTERM_TAPE 0x0707 /* Analog Audio Tape */
+#define AUDIO_EMBEDTERM_PHONOGRAPH 0x0708 /* Analog vinyl record player */
+#define AUDIO_EMBEDTERM_VCR 0x0709 /* Audio track of VCR */
+#define AUDIO_EMBEDTERM_VIDDISC 0x070a /* Audio track of VideoDisc player */
+#define AUDIO_EMBEDTERM_DVD 0x070b /* Audio track of DVD player */
+#define AUDIO_EMBEDTERM_TVTUNER 0x070c /* Audio track of TV tuner */
+#define AUDIO_EMBEDTERM_SATELLITE 0x070d /* Audio track of satellite receiver */
+#define AUDIO_EMBEDTERM_CABLETUNER 0x070e /* Audio track of cable tuner */
+#define AUDIO_EMBEDTERM_DSS 0x070f /* Audio track of DSS receiver */
+#define AUDIO_EMBEDTERM_RADIO 0x0710 /* AM/FM radio receiver */
+#define AUDIO_EMBEDTERM_TRANSMITTER 0x0711 /* AM/FM radio transmitter */
+#define AUDIO_EMBEDTERM_MULTITRACK 0x0712 /* A multi-track recording system */
+#define AUDIO_EMBEDTERM_SYNTHESIZER 0x0713 /* Synthesizer */
+#define AUDIO_EMBEDTERM_PIANO 0x0714 /* Piano */
+#define AUDIO_EMBEDTERM_GUITAR 0x0715 /* Guitar */
+#define AUDIO_EMBEDTERM_PERCUSSON 0x0716 /* Percussion Instrument */
+#define AUDIO_EMBEDTERM_INSTRUMENT 0x0717 /* Other Musical Instrument */
+
+#define AUDIO_FORMAT_TYPE_I 0x01
+#define AUDIO_FORMAT_TYPE_II 0x02
+#define AUDIO_FORMAT_TYPE_III 0x03
+
+struct audio_cs_if_ac_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t bcdADC;
+ uint16_t wTotalLength;
+ uint8_t bInCollection;
+ uint8_t baInterfaceNr[];
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_HEADER_DESC(n) (8 + n)
+
+struct audio_cs_if_ac_input_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bNrChannels;
+ uint16_t wChannelConfig;
+ uint8_t iChannelNames;
+ uint8_t iTerminal;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC (12)
+
+struct audio_cs_if_ac_output_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC (9)
+
+struct audio_cs_if_ac_feature_unit_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint8_t bControlSize;
+ uint8_t bmaControls[1];
+ uint8_t iFeature;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(ch, n) (7 + (ch + 1) * n)
+
+struct audio_cs_if_as_general_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalLink;
+ uint8_t bDelay;
+ uint16_t wFormatTag;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AS_GENERAL_DESC (7)
+
+struct audio_cs_if_as_format_type_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bFormatType;
+ uint8_t bNrChannels;
+ uint8_t bSubframeSize;
+ uint8_t bBitResolution;
+ uint8_t bSamFreqType;
+ uint8_t tSamFreq[3];
+} __PACKED;
+
+#define AUDIO_SIZEOF_FORMAT_TYPE_DESC(n) (8 + 3 * n)
+
+struct audio_ep_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+ uint8_t bRefresh;
+ uint8_t bSynchAddress;
+} __PACKED;
+
+#define AUDIO_SIZEOF_EP_DESC (9)
+
+struct audio_cs_ep_ep_general_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bmAttributes;
+ uint8_t bLockDelayUnits;
+ uint16_t wLockDelay;
+} __PACKED;
+
+#define AUDIO_SIZEOF_CS_EP_GENERAL_DESC (7)
+
+// clang-format off
+#define AUDIO_AC_DESCRIPTOR_INIT(bFirstInterface, bInterfaceCount, wTotalLength, stridx, ...) \
+ /* Interface Association Descriptor */ \
+ 0x08, \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, \
+ bFirstInterface, \
+ bInterfaceCount, \
+ USB_DEVICE_CLASS_AUDIO, \
+ AUDIO_SUBCLASS_AUDIOCONTROL, \
+ AUDIO_PROTOCOL_UNDEFINED, \
+ 0x00, \
+ /* ------------------ AudioControl Interface ------------------ */\
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
+ stridx, /* iInterface */ \
+ 0x08 + PP_NARG(__VA_ARGS__), /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(0x0100), /* bcdADC */ \
+ WBVAL(wTotalLength), /* wTotalLength */ \
+ PP_NARG(__VA_ARGS__), /* bInCollection */ \
+ __VA_ARGS__ /* baInterfaceNr */
+
+#define AUDIO_AC_DESCRIPTOR_INIT_LEN(n) (0x08 + 0x09 + 0x08 + n)
+
+#define AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(bTerminalID, wTerminalType, bNrChannels, wChannelConfig) \
+ 0x0C, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ \
+ bTerminalID, /* bTerminalID */ \
+ WBVAL(wTerminalType), /* wTerminalType : Microphone 0x0201 */ \
+ 0x00, /* bAssocTerminal */ \
+ bNrChannels, /* bNrChannels */ \
+ WBVAL(wChannelConfig), /* wChannelConfig : Mono sets no position bits */ \
+ 0x00, /* iChannelNames */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(bTerminalID, wTerminalType, bSourceID) \
+ 0x09, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ \
+ bTerminalID, /* bTerminalID */ \
+ WBVAL(wTerminalType), /* wTerminalType : USB Streaming */ \
+ 0x00, /* bAssocTerminal */ \
+ bSourceID, /* bSourceID */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(bUnitID, bSourceID, bControlSize, ...) \
+ 0x07 + PP_NARG(__VA_ARGS__), /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ \
+ bUnitID, /* bUnitID */ \
+ bSourceID, /* bSourceID */ \
+ bControlSize, /* bControlSize */ \
+ __VA_ARGS__, /* bmaControls(0) Mute */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_AS_DESCRIPTOR_INIT(bInterfaceNumber, bTerminalLink, bNrChannels, bSubFrameSize, bBitResolution, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval, ...) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x01, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ \
+ bTerminalLink, /* bTerminalLink : Unit ID of the Output Terminal*/ \
+ 0x01, /* bDelay */ \
+ WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag : AUDIO_FORMAT_PCM */ \
+ 0x08 + PP_NARG(__VA_ARGS__), /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ \
+ AUDIO_FORMAT_TYPE_I, /* bFormatType */ \
+ bNrChannels, /* bNrChannels */ \
+ bSubFrameSize, /* bSubFrameSize : Bytes per audio subframe */ \
+ bBitResolution, /* bBitResolution : bits per sample */ \
+ (PP_NARG(__VA_ARGS__)/3), /* bSamFreqType : only one frequency supported */ \
+ __VA_ARGS__, /* tSamFreq : Audio sampling frequency coded on 3 bytes */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ bEndpointAddress, /* bEndpointAddress : IN endpoint 1 */ \
+ bmAttributes, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ bInterval, /* bInterval : one packet per frame */ \
+ 0x00, /* bRefresh */ \
+ 0x00, /* bSynchAddress */ \
+ 0x07, /* bLength */ \
+ AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ \
+ AUDIO_EP_CONTROL_SAMPLING_FEQ, /* bmAttributes AUDIO_SAMPLING_FREQ_CONTROL */ \
+ 0x00, /* bLockDelayUnits */ \
+ 0x00, /* wLockDelay */ \
+ 0x00
+
+#define AUDIO_AS_DESCRIPTOR_INIT_LEN(n) (0x09 + 0x09 + 0x07 + 0x08 + 3 * n + 0x09 + 0x07)
+
+#define AUDIO_MS_STANDARD_DESCRIPTOR_INIT(bInterfaceNumber, bNumEndpoints) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ bNumEndpoints, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_MIDISTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ \
+ 0x00 /* iInterface */
+
+#define AUDIO_MS_STANDARD_DESCRIPTOR_INIT_LEN 0x09
+
+struct audio_v2_channel_cluster_descriptor {
+ uint8_t bNrChannels;
+ uint32_t bmChannelConfig;
+ uint8_t iChannelNames;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_CHANNEL_CLUSTER_DESC (6)
+
+struct audio_v2_cs_if_ac_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t bcdADC;
+ uint8_t bCategory;
+ uint16_t wTotalLength;
+ uint8_t bmControls;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AC_HEADER_DESC (9)
+
+struct audio_v2_cs_if_ac_clock_source_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bClockID;
+ uint8_t bmAttributes;
+ uint8_t bmControls;
+ uint8_t bAssocTerminal;
+ uint8_t iClockSource;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AC_CLOCK_SOURCE_DESC (8)
+
+struct audio_v2_cs_if_ac_clock_selector_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bClockID;
+ uint8_t bNrInPins;
+ uint8_t baCSourceID[1];
+ uint8_t iClockSelector;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_CLOCK_SELECTOR_DESC(n) (7 + n)
+
+struct audio_v2_cs_if_ac_clock_multiplier_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bClockID;
+ uint8_t bCSourceID;
+ uint8_t bmControls;
+ uint8_t iClockMultiplier;
+} __PACKED;
+
+#define AUDIO_SIZEOF_AC_CLOCK_MULTIPLIER_DESC() (7)
+
+struct audio_v2_cs_if_ac_input_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bCSourceID;
+ uint8_t bNrChannels;
+ uint32_t wChannelConfig;
+ uint8_t iChannelNames;
+ uint16_t bmControls;
+ uint8_t iTerminal;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AC_INPUT_TERMINAL_DESC (17)
+
+struct audio_v2_cs_if_ac_output_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t bCSourceID;
+ uint16_t bmControls;
+ uint8_t iTerminal;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AC_OUTPUT_TERMINAL_DESC (12)
+
+struct audio_v2_cs_if_ac_feature_unit_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint32_t bmaControls[1];
+ uint8_t iFeature;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AC_FEATURE_UNIT_DESC(ch) (6 + (ch + 1) * 4)
+
+struct audio_v2_cs_if_as_general_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalLink;
+ uint8_t bmControls;
+ uint8_t bFormatType;
+ uint32_t bmFormats;
+ uint8_t bNrChannels;
+ uint32_t bmChannelConfig;
+ uint8_t iChannelNames;
+} __PACKED;
+
+#define AUDIO_V2_SIZEOF_AS_GENERAL_DESC (16)
+
+struct audio_v2_control_range1_param_block {
+ uint16_t wNumSubRanges;
+ struct
+ {
+ uint8_t bMin;
+ uint8_t bMax;
+ uint8_t bRes;
+ }subrange[];
+} __PACKED;
+
+struct audio_v2_control_range2_param_block {
+ uint16_t wNumSubRanges;
+ struct
+ {
+ uint16_t wMin;
+ uint16_t wMax;
+ uint16_t wRes;
+ }subrange[];
+} __PACKED;
+
+struct audio_v2_control_range3_param_block {
+ uint16_t wNumSubRanges;
+ struct
+ {
+ uint32_t dMin;
+ uint32_t dMax;
+ uint32_t dRes;
+ }subrange[];
+} __PACKED;
+
+#define AUDIO_V2_AC_DESCRIPTOR_INIT(bFirstInterface, bInterfaceCount, wTotalLength, bCategory, bmControls, stridx) \
+ /* Interface Association Descriptor */ \
+ 0x08, \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, \
+ bFirstInterface, \
+ bInterfaceCount, \
+ USB_DEVICE_CLASS_AUDIO, \
+ AUDIO_SUBCLASS_UNDEFINED, \
+ AUDIO_PROTOCOLv20, \
+ 0x00, \
+ /* ------------------ AudioControl Interface ------------------ */\
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \
+ stridx, /* iInterface */ \
+ 0x09, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(0x0200), /* bcdADC */ \
+ bCategory, /* bCategory */ \
+ WBVAL(wTotalLength), /* wTotalLength */ \
+ bmControls /* bmControls */ \
+
+#define AUDIO_V2_AC_DESCRIPTOR_INIT_LEN (0x08 + 0x09 + 0x09)
+
+#define AUDIO_V2_AC_CLOCK_SOURCE_DESCRIPTOR_INIT(bClockID, bmAttributes, bmControls) \
+ 0x08, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_CLOCK_SOURCE, /* bDescriptorSubtype */ \
+ bClockID, /* bClockID */ \
+ bmAttributes, /* bmAttributes */ \
+ bmControls, /* bmControls */ \
+ 0x00, /* bAssocTerminal */ \
+ 0x00 /* iClockSource */
+
+#define AUDIO_V2_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(bTerminalID, wTerminalType, bCSourceID, bNrChannels, wChannelConfig, bmControls) \
+ 0x11, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ \
+ bTerminalID, /* bTerminalID */ \
+ WBVAL(wTerminalType), /* wTerminalType : Microphone 0x0201 */ \
+ 0x00, /* bAssocTerminal */ \
+ bCSourceID, /* bCSourceID */ \
+ bNrChannels, /* bNrChannels */ \
+ DBVAL(wChannelConfig), /* wChannelConfig : Mono sets no position bits */ \
+ 0x00, /* iChannelNames */ \
+ WBVAL(bmControls), /* bmControls */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_V2_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(bTerminalID, wTerminalType, bSourceID, bCSourceID, bmControls) \
+ 0x0c, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ \
+ bTerminalID, /* bTerminalID */ \
+ WBVAL(wTerminalType), /* wTerminalType : USB Streaming */ \
+ 0x00, /* bAssocTerminal */ \
+ bSourceID, /* bSourceID */ \
+ bCSourceID, /* bCSourceID */ \
+ WBVAL(bmControls), /* bmControls */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_V2_AC_FEATURE_UNIT_DESCRIPTOR_INIT(bUnitID, bSourceID, ...) \
+ 0x06 + (PP_NARG(__VA_ARGS__)), /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ \
+ bUnitID, /* bUnitID */ \
+ bSourceID, /* bSourceID */ \
+ __VA_ARGS__, /* bmaControls(0) Mute */ \
+ 0x00 /* iTerminal */
+
+#define AUDIO_V2_AS_DESCRIPTOR_INIT(bInterfaceNumber, bTerminalLink, bNrChannels, bmChannelConfig, bSubslotSize, bBitResolution, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x01, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x10, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ \
+ bTerminalLink, /* bTerminalLink : Unit ID of the Output or Input Terminal*/ \
+ 0x00, /* bmControls */ \
+ AUDIO_FORMAT_TYPE_I, /* bFormatType : AUDIO_FORMAT_TYPE_I */ \
+ DBVAL(AUDIO_V2_FORMAT_PCM), /* bmFormats PCM */ \
+ bNrChannels, /* bNrChannels */ \
+ DBVAL(bmChannelConfig), /* bmChannelConfig */ \
+ 0x00, /* iChannelNames */ \
+ 0x06, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ \
+ AUDIO_FORMAT_TYPE_I, /* bFormatType */ \
+ bSubslotSize, /* bSubslotSize */ \
+ bBitResolution, /* bBitResolution */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ bEndpointAddress, /* bEndpointAddress 3 out endpoint for Audio */ \
+ bmAttributes, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* XXXX wMaxPacketSize in Bytes (SampleRate * SlotByteSize * NumChannels) */ \
+ bInterval, /* bInterval */ \
+ 0x08, /* bLength */ \
+ AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ \
+ 0x00, /* bmAttributes */ \
+ 0x00, /* bmControls */ \
+ 0x00, /* bLockDelayUnits */ \
+ 0x00, /* wLockDelay */ \
+ 0x00
+
+#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT(bInterfaceNumber, bTerminalLink, bNrChannels, bmChannelConfig, bSubslotSize, bBitResolution, bEndpointAddress, wMaxPacketSize, bInterval, bFeedbackEndpointAddress) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ 0x01, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ \
+ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ \
+ AUDIO_PROTOCOLv20, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x10, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ \
+ bTerminalLink, /* bTerminalLink : Unit ID of the Output or Input Terminal*/ \
+ 0x00, /* bmControls */ \
+ AUDIO_FORMAT_TYPE_I, /* bFormatType : AUDIO_FORMAT_TYPE_I */ \
+ DBVAL(AUDIO_V2_FORMAT_PCM), /* bmFormats PCM */ \
+ bNrChannels, /* bNrChannels */ \
+ DBVAL(bmChannelConfig), /* bmChannelConfig */ \
+ 0x00, /* iChannelNames */ \
+ 0x06, /* bLength */ \
+ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ \
+ AUDIO_FORMAT_TYPE_I, /* bFormatType */ \
+ bSubslotSize, /* bSubslotSize */ \
+ bBitResolution, /* bBitResolution */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ bEndpointAddress, /* bEndpointAddress 3 out endpoint for Audio */ \
+ 0x05, /* bmAttributes: TransferType=Isochronous SyncType=Asynchronous EndpointType=Data*/ \
+ WBVAL(wMaxPacketSize), /* XXXX wMaxPacketSize in Bytes (SampleRate * SlotByteSize * NumChannels) */ \
+ bInterval, /* bInterval */ \
+ 0x08, /* bLength */ \
+ AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ \
+ AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ \
+ 0x00, /* bmAttributes */ \
+ 0x00, /* bmControls */ \
+ 0x00, /* bLockDelayUnits */ \
+ 0x00, /* wLockDelay */ \
+ 0x00, \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ bFeedbackEndpointAddress, /* bFeedbackEndpointAddress Revise Dir to bEndpointAddress */ \
+ 0x11, /* bmAttributes: TransferType=Isochronous SyncType=None EndpointType=Feedback */ \
+ WBVAL(4), /* XXXX wMaxPacketSize in Bytes */ \
+ bInterval /* bInterval */ \
+
+// clang-format on
+
+#define AUDIO_V2_AS_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08)
+#define AUDIO_V2_AS_FEEDBACK_DESCRIPTOR_INIT_LEN (0x09 + 0x09 + 0x10 + 0x06 + 0x07 + 0x08 + 0x07)
+
+#define AUDIO_SAMPLE_FREQ_NUM(num) (uint8_t)(num), (uint8_t)((num >> 8))
+#define AUDIO_SAMPLE_FREQ_3B(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))
+#define AUDIO_SAMPLE_FREQ_4B(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), \
+ (uint8_t)((frq >> 16)), (uint8_t)((frq >> 24))
+
+#endif /* USB_AUDIO_H */
diff --git a/components/drivers/usb/cherryusb/class/audio/usbd_audio.c b/components/drivers/usb/cherryusb/class/audio/usbd_audio.c
new file mode 100644
index 0000000000..c78c8ed751
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/audio/usbd_audio.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_audio.h"
+
+struct audio_entity_param {
+ uint32_t wCur;
+ uint32_t wMin;
+ uint32_t wMax;
+ uint32_t wRes;
+};
+
+struct usbd_audio_priv {
+ struct audio_entity_info *table;
+ uint8_t num;
+ uint16_t uac_version;
+} g_usbd_audio[CONFIG_USBDEV_MAX_BUS];
+
+static int audio_class_endpoint_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t control_selector;
+ uint32_t sampling_freq = 0;
+ uint8_t ep;
+
+ control_selector = HI_BYTE(setup->wValue);
+ ep = LO_BYTE(setup->wIndex);
+
+ switch (control_selector) {
+ case AUDIO_EP_CONTROL_SAMPLING_FEQ:
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_SET_CUR:
+ memcpy((uint8_t *)&sampling_freq, *data, *len);
+ USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
+ usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
+ break;
+ case AUDIO_REQUEST_GET_CUR:
+ case AUDIO_REQUEST_GET_MIN:
+ case AUDIO_REQUEST_GET_MAX:
+ case AUDIO_REQUEST_GET_RES:
+ sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
+ memcpy(*data, &sampling_freq, 3);
+ USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
+ *len = 3;
+ break;
+ }
+
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Audio Class control selector 0x%02x\r\n", control_selector);
+ return -1;
+ }
+ return 0;
+}
+
+static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("Audio Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ uint8_t entity_id;
+ uint8_t ep;
+ uint8_t subtype = 0x01;
+ uint8_t control_selector;
+ uint8_t ch;
+ uint8_t mute;
+ uint16_t volume;
+ int volume_db = 0;
+ uint32_t sampling_freq = 0;
+
+ const char *mute_string[2] = { "off", "on" };
+
+ entity_id = HI_BYTE(setup->wIndex);
+ control_selector = HI_BYTE(setup->wValue);
+ ch = LO_BYTE(setup->wValue);
+
+ ARG_UNUSED(mute_string);
+
+ for (uint8_t i = 0; i < g_usbd_audio[busid].num; i++) {
+ if (g_usbd_audio[busid].table[i].bEntityId == entity_id) {
+ subtype = g_usbd_audio[busid].table[i].bDescriptorSubtype;
+ ep = g_usbd_audio[busid].table[i].ep;
+ break;
+ }
+ }
+
+ if (subtype == 0x01) {
+ USB_LOG_ERR("Do not find subtype for 0x%02x\r\n", entity_id);
+ return -1;
+ }
+
+ USB_LOG_DBG("Audio entity_id:%02x, subtype:%02x, cs:%02x\r\n", entity_id, subtype, control_selector);
+
+ switch (subtype) {
+ case AUDIO_CONTROL_FEATURE_UNIT:
+ switch (control_selector) {
+ case AUDIO_FU_CONTROL_MUTE:
+ if (g_usbd_audio[busid].uac_version < 0x0200) {
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_SET_CUR:
+ mute = (*data)[0];
+ usbd_audio_set_mute(busid, ep, ch, mute);
+ break;
+ case AUDIO_REQUEST_GET_CUR:
+ (*data)[0] = usbd_audio_get_mute(busid, ep, ch);
+ *len = 1;
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
+ return -1;
+ }
+ } else {
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_CUR:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ (*data)[0] = usbd_audio_get_mute(busid, ep, ch);
+ *len = 1;
+ } else {
+ mute = (*data)[0];
+ usbd_audio_set_mute(busid, ep, ch, mute);
+ }
+ break;
+ default:
+ //USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
+ return -1;
+ }
+ }
+ break;
+ case AUDIO_FU_CONTROL_VOLUME:
+ if (g_usbd_audio[busid].uac_version < 0x0200) {
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_SET_CUR:
+ memcpy(&volume, *data, *len);
+ if (volume < 0x8000) {
+ volume_db = volume / 256;
+ } else if (volume > 0x8000) {
+ volume_db = (0xffff - volume + 1) / -256;
+ }
+ volume_db += 128; /* 0 ~ 255 */
+ USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%04x\r\n", ep, ch, volume);
+ usbd_audio_set_volume(busid, ep, ch, volume_db);
+ break;
+ case AUDIO_REQUEST_GET_CUR:
+ volume_db = usbd_audio_get_volume(busid, ep, ch);
+ volume_db -= 128;
+ if (volume_db >= 0) {
+ volume = volume_db * 256;
+ } else {
+ volume = volume_db * 256 + 0xffff + 1;
+ }
+ memcpy(*data, &volume, 2);
+ *len = 2;
+ break;
+ case AUDIO_REQUEST_GET_MIN:
+ (*data)[0] = 0x00; /* -2560/256 dB */
+ (*data)[1] = 0xdb;
+ *len = 2;
+ break;
+ case AUDIO_REQUEST_GET_MAX:
+ (*data)[0] = 0x00; /* 0 dB */
+ (*data)[1] = 0x00;
+ *len = 2;
+ break;
+ case AUDIO_REQUEST_GET_RES:
+ (*data)[0] = 0x00; /* -256/256 dB */
+ (*data)[1] = 0x01;
+ *len = 2;
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
+ return -1;
+ }
+ } else {
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_CUR:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ volume_db = usbd_audio_get_volume(busid, ep, ch);
+ volume = volume_db;
+ memcpy(*data, &volume, 2);
+ *len = 2;
+ } else {
+ memcpy(&volume, *data, *len);
+ volume_db = volume;
+ USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%02x\r\n", ep, ch, volume);
+ usbd_audio_set_volume(busid, ep, ch, volume_db);
+ }
+ break;
+ case AUDIO_REQUEST_RANGE:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ *((uint16_t *)(*data + 0)) = 1;
+ *((uint16_t *)(*data + 2)) = 0;
+ *((uint16_t *)(*data + 4)) = 100;
+ *((uint16_t *)(*data + 6)) = 1;
+ *len = 8;
+ } else {
+ }
+ break;
+ default:
+ //USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
+ return -1;
+ }
+ break;
+ case AUDIO_CONTROL_CLOCK_SOURCE:
+ switch (control_selector) {
+ case AUDIO_CS_CONTROL_SAM_FREQ:
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_CUR:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
+ memcpy(*data, &sampling_freq, 4);
+ USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
+ *len = 4;
+ } else {
+ memcpy(&sampling_freq, *data, setup->wLength);
+ USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
+ usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
+ }
+ break;
+ case AUDIO_REQUEST_RANGE:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ uint8_t *sampling_freq_table = NULL;
+ uint16_t num;
+
+ usbd_audio_get_sampling_freq_table(busid, ep, &sampling_freq_table);
+ num = (uint16_t)((uint16_t)(sampling_freq_table[1] << 8) | ((uint16_t)sampling_freq_table[0]));
+ memcpy(*data, sampling_freq_table, (12 * num + 2));
+ *len = (12 * num + 2);
+ } else {
+ }
+ break;
+ default:
+ //USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
+ return -1;
+ }
+ break;
+ case AUDIO_CS_CONTROL_CLOCK_VALID:
+ if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
+ (*data)[0] = 1;
+ *len = 1;
+ } else {
+ return -1;
+ }
+ break;
+
+ default:
+ //USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
+ return -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void audio_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+
+ break;
+
+ case USBD_EVENT_SET_INTERFACE: {
+ struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
+ if (intf->bAlternateSetting) {
+ usbd_audio_open(busid, intf->bInterfaceNumber);
+ } else {
+ usbd_audio_close(busid, intf->bInterfaceNumber);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+struct usbd_interface *usbd_audio_init_intf(uint8_t busid,
+ struct usbd_interface *intf,
+ uint16_t uac_version,
+ struct audio_entity_info *table,
+ uint8_t num)
+{
+ if (uac_version < 0x0200) {
+ intf->class_interface_handler = audio_class_interface_request_handler;
+ intf->class_endpoint_handler = audio_class_endpoint_request_handler;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = audio_notify_handler;
+ } else {
+ intf->class_interface_handler = audio_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = audio_notify_handler;
+ }
+
+ g_usbd_audio[busid].uac_version = uac_version;
+ g_usbd_audio[busid].table = table;
+ g_usbd_audio[busid].num = num;
+
+ return intf;
+}
+
+__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume)
+{
+}
+
+__WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
+{
+ return 0;
+}
+
+__WEAK void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute)
+{
+}
+
+__WEAK bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch)
+{
+ return 0;
+}
+
+__WEAK void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq)
+{
+}
+
+__WEAK uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep)
+{
+ return 0;
+}
+
+__WEAK void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table)
+{
+}
diff --git a/components/drivers/usb/cherryusb/class/audio/usbd_audio.h b/components/drivers/usb/cherryusb/class/audio/usbd_audio.h
new file mode 100644
index 0000000000..442405a2f9
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/audio/usbd_audio.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_AUDIO_H
+#define USBD_AUDIO_H
+
+#include "usb_audio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct audio_entity_info {
+ uint8_t bDescriptorSubtype;
+ uint8_t bEntityId;
+ uint8_t ep;
+};
+
+/* Init audio interface driver */
+struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface *intf,
+ uint16_t uac_version,
+ struct audio_entity_info *table,
+ uint8_t num);
+
+void usbd_audio_open(uint8_t busid, uint8_t intf);
+void usbd_audio_close(uint8_t busid, uint8_t intf);
+
+void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume);
+int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch);
+void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute);
+bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch);
+void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq);
+uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep);
+
+void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_AUDIO_H */
diff --git a/components/drivers/usb/cherryusb/class/audio/usbh_audio.c b/components/drivers/usb/cherryusb/class/audio/usbh_audio.c
new file mode 100644
index 0000000000..8687e9a4a6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/audio/usbh_audio.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_audio.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_audio"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/audio%d"
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+#ifndef CONFIG_USBHOST_MAX_AUDIO_CLASS
+#define CONFIG_USBHOST_MAX_AUDIO_CLASS 4
+#endif
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_audio_buf[128];
+
+static struct usbh_audio g_audio_class[CONFIG_USBHOST_MAX_AUDIO_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_audio *usbh_audio_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_AUDIO_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_audio_class[devno], 0, sizeof(struct usbh_audio));
+ g_audio_class[devno].minor = devno;
+ return &g_audio_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_audio_class_free(struct usbh_audio *audio_class)
+{
+ int devno = audio_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(audio_class, 0, sizeof(struct usbh_audio));
+}
+
+int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq)
+{
+ struct usb_setup_packet *setup = audio_class->hport->setup;
+ struct usb_endpoint_descriptor *ep_desc;
+ uint8_t mult;
+ uint16_t mps;
+ int ret;
+ uint8_t intf = 0xff;
+ uint8_t altsetting = 1;
+
+ if (audio_class->is_opened) {
+ return 0;
+ }
+
+ for (uint8_t i = 0; i < audio_class->module_num; i++) {
+ if (strcmp(name, audio_class->module[i].name) == 0) {
+ for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) {
+ for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) {
+ if (audio_class->module[i].altsetting[j].sampfreq[k] == samp_freq) {
+ intf = audio_class->module[i].data_intf;
+ altsetting = j;
+ goto freq_found;
+ }
+ }
+ }
+ }
+ }
+
+ return -USB_ERR_NODEV;
+
+freq_found:
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_SET_INTERFACE;
+ setup->wValue = altsetting;
+ setup->wIndex = intf;
+ setup->wLength = 0;
+
+ ret = usbh_control_transfer(audio_class->hport, setup, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_ENDPOINT;
+ setup->bRequest = AUDIO_REQUEST_SET_CUR;
+ setup->wValue = (AUDIO_EP_CONTROL_SAMPLING_FEQ << 8) | 0x00;
+ setup->wIndex = ep_desc->bEndpointAddress;
+ setup->wLength = 3;
+
+ memcpy(g_audio_buf, &samp_freq, 3);
+ ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
+ if (ret < 0) {
+ return ret;
+ }
+
+ mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
+ mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ audio_class->isoin_mps = mps * (mult + 1);
+ USBH_EP_INIT(audio_class->isoin, ep_desc);
+ } else {
+ audio_class->isoout_mps = mps * (mult + 1);
+ USBH_EP_INIT(audio_class->isoout, ep_desc);
+ }
+
+ USB_LOG_INFO("Open audio module :%s, altsetting: %u\r\n", name, altsetting);
+ audio_class->is_opened = true;
+ return ret;
+}
+
+int usbh_audio_close(struct usbh_audio *audio_class, const char *name)
+{
+ struct usb_setup_packet *setup = audio_class->hport->setup;
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ uint8_t intf = 0xff;
+ uint8_t altsetting = 1;
+
+ for (size_t i = 0; i < audio_class->module_num; i++) {
+ if (strcmp(name, audio_class->module[i].name) == 0) {
+ intf = audio_class->module[i].data_intf;
+ }
+ }
+
+ if (intf == 0xff) {
+ return -USB_ERR_NODEV;
+ }
+
+ USB_LOG_INFO("Close audio module :%s\r\n", name);
+ audio_class->is_opened = false;
+
+ ep_desc = &audio_class->hport->config.intf[intf].altsetting[altsetting].ep[0].ep_desc;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ if (audio_class->isoin) {
+ audio_class->isoin = NULL;
+ }
+ } else {
+ if (audio_class->isoout) {
+ audio_class->isoout = NULL;
+ }
+ }
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_SET_INTERFACE;
+ setup->wValue = 0;
+ setup->wIndex = intf;
+ setup->wLength = 0;
+
+ ret = usbh_control_transfer(audio_class->hport, setup, NULL);
+
+ return ret;
+}
+
+int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume)
+{
+ struct usb_setup_packet *setup = audio_class->hport->setup;
+ int ret;
+ uint8_t intf = 0xff;
+ uint8_t feature_id = 0xff;
+ uint16_t volume_hex;
+
+ for (size_t i = 0; i < audio_class->module_num; i++) {
+ if (strcmp(name, audio_class->module[i].name) == 0) {
+ intf = audio_class->ctrl_intf;
+ feature_id = audio_class->module[i].feature_unit_id;
+ }
+ }
+
+ if (intf == 0xff) {
+ return -USB_ERR_NODEV;
+ }
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = AUDIO_REQUEST_SET_CUR;
+ setup->wValue = (AUDIO_FU_CONTROL_VOLUME << 8) | ch;
+ setup->wIndex = (feature_id << 8) | intf;
+ setup->wLength = 2;
+
+ volume_hex = -0xDB00 / 100 * volume + 0xdb00;
+
+ memcpy(g_audio_buf, &volume_hex, 2);
+ ret = usbh_control_transfer(audio_class->hport, setup, NULL);
+
+ return ret;
+}
+
+int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute)
+{
+ struct usb_setup_packet *setup = audio_class->hport->setup;
+ int ret;
+ uint8_t intf = 0xff;
+ uint8_t feature_id = 0xff;
+
+ for (size_t i = 0; i < audio_class->module_num; i++) {
+ if (strcmp(name, audio_class->module[i].name) == 0) {
+ intf = audio_class->ctrl_intf;
+ feature_id = audio_class->module[i].feature_unit_id;
+ }
+ }
+
+ if (intf == 0xff) {
+ return -USB_ERR_NODEV;
+ }
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = AUDIO_REQUEST_SET_CUR;
+ setup->wValue = (AUDIO_FU_CONTROL_MUTE << 8) | ch;
+ setup->wIndex = (feature_id << 8) | intf;
+ setup->wLength = 1;
+
+ memcpy(g_audio_buf, &mute, 1);
+ ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
+
+ return ret;
+}
+
+void usbh_audio_list_module(struct usbh_audio *audio_class)
+{
+ USB_LOG_INFO("============= Audio module information ===================\r\n");
+ USB_LOG_RAW("bcdADC :%04x\r\n", audio_class->bcdADC);
+ USB_LOG_RAW("Num of modules :%u\r\n", audio_class->module_num);
+ USB_LOG_RAW("Num of altsettings:%u\r\n", audio_class->num_of_intf_altsettings);
+
+ for (uint8_t i = 0; i < audio_class->module_num; i++) {
+ USB_LOG_RAW(" module name :%s\r\n", audio_class->module[i].name);
+ USB_LOG_RAW(" module feature unit id :%d\r\n", audio_class->module[i].feature_unit_id);
+
+ for (uint8_t j = 0; j < audio_class->num_of_intf_altsettings; j++) {
+ if (j == 0) {
+ USB_LOG_RAW(" Ingore altsetting 0\r\n");
+ continue;
+ }
+ USB_LOG_RAW(" Altsetting %u\r\n", j);
+ USB_LOG_RAW(" module channels :%u\r\n", audio_class->module[i].altsetting[j].channels);
+ //USB_LOG_RAW(" module format_type :%u\r\n",audio_class->module[i].altsetting[j].format_type);
+ USB_LOG_RAW(" module bitresolution :%u\r\n", audio_class->module[i].altsetting[j].bitresolution);
+ USB_LOG_RAW(" module sampfreq num :%u\r\n", audio_class->module[i].altsetting[j].sampfreq_num);
+
+ for (uint8_t k = 0; k < audio_class->module[i].altsetting[j].sampfreq_num; k++) {
+ USB_LOG_RAW(" module sampfreq :%d hz\r\n", audio_class->module[i].altsetting[j].sampfreq[k]);
+ }
+ }
+ }
+
+ USB_LOG_INFO("============= Audio module information ===================\r\n");
+}
+
+static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret;
+ uint8_t cur_iface = 0xff;
+ uint8_t cur_iface_count = 0xff;
+ uint8_t cur_alt_setting = 0xff;
+ uint8_t input_offset = 0;
+ uint8_t output_offset = 0;
+ uint8_t feature_unit_offset = 0;
+ uint8_t format_offset = 0;
+ uint8_t *p;
+
+ struct usbh_audio *audio_class = usbh_audio_class_alloc();
+ if (audio_class == NULL) {
+ USB_LOG_ERR("Fail to alloc audio_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ audio_class->hport = hport;
+ audio_class->ctrl_intf = intf;
+ audio_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
+
+ hport->config.intf[intf].priv = audio_class;
+
+ p = hport->raw_config_desc;
+ while (p[DESC_bLength]) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION:
+ cur_iface_count = p[3];
+ break;
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ cur_iface = p[INTF_DESC_bInterfaceNumber];
+ cur_alt_setting = p[INTF_DESC_bAlternateSetting];
+ break;
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ break;
+ case AUDIO_INTERFACE_DESCRIPTOR_TYPE:
+ if (cur_iface == audio_class->ctrl_intf) {
+ switch (p[DESC_bDescriptorSubType]) {
+ case AUDIO_CONTROL_HEADER: {
+ struct audio_cs_if_ac_header_descriptor *desc = (struct audio_cs_if_ac_header_descriptor *)p;
+ audio_class->bcdADC = desc->bcdADC;
+ audio_class->bInCollection = desc->bInCollection;
+ } break;
+ case AUDIO_CONTROL_INPUT_TERMINAL: {
+ struct audio_cs_if_ac_input_terminal_descriptor *desc = (struct audio_cs_if_ac_input_terminal_descriptor *)p;
+
+ audio_class->module[input_offset].input_terminal_id = desc->bTerminalID;
+ audio_class->module[input_offset].input_terminal_type = desc->wTerminalType;
+ audio_class->module[input_offset].input_channel_config = desc->wChannelConfig;
+
+ if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) {
+ audio_class->module[input_offset].terminal_link_id = desc->bTerminalID;
+ }
+ if (desc->wTerminalType == AUDIO_INTERM_MIC) {
+ audio_class->module[input_offset].name = "mic";
+ }
+ input_offset++;
+ } break;
+ break;
+ case AUDIO_CONTROL_OUTPUT_TERMINAL: {
+ struct audio_cs_if_ac_output_terminal_descriptor *desc = (struct audio_cs_if_ac_output_terminal_descriptor *)p;
+ audio_class->module[output_offset].output_terminal_id = desc->bTerminalID;
+ audio_class->module[output_offset].output_terminal_type = desc->wTerminalType;
+ if (desc->wTerminalType == AUDIO_TERMINAL_STREAMING) {
+ audio_class->module[output_offset].terminal_link_id = desc->bTerminalID;
+ }
+ if (desc->wTerminalType == AUDIO_OUTTERM_SPEAKER) {
+ audio_class->module[output_offset].name = "speaker";
+ }
+ output_offset++;
+ } break;
+ case AUDIO_CONTROL_FEATURE_UNIT: {
+ struct audio_cs_if_ac_feature_unit_descriptor *desc = (struct audio_cs_if_ac_feature_unit_descriptor *)p;
+ audio_class->module[feature_unit_offset].feature_unit_id = desc->bUnitID;
+ audio_class->module[feature_unit_offset].feature_unit_controlsize = desc->bControlSize;
+
+ for (uint8_t j = 0; j < desc->bControlSize; j++) {
+ audio_class->module[feature_unit_offset].feature_unit_controls[j] = p[6 + j];
+ }
+ feature_unit_offset++;
+ } break;
+ case AUDIO_CONTROL_PROCESSING_UNIT:
+
+ break;
+ default:
+ break;
+ }
+ } else if ((cur_iface < (audio_class->ctrl_intf + cur_iface_count)) && (cur_iface > audio_class->ctrl_intf)) {
+ switch (p[DESC_bDescriptorSubType]) {
+ case AUDIO_STREAMING_GENERAL:
+
+ break;
+ case AUDIO_STREAMING_FORMAT_TYPE: {
+ struct audio_cs_if_as_format_type_descriptor *desc = (struct audio_cs_if_as_format_type_descriptor *)p;
+
+ audio_class->module[format_offset].data_intf = cur_iface;
+ audio_class->module[format_offset].altsetting[cur_alt_setting].channels = desc->bNrChannels;
+ audio_class->module[format_offset].altsetting[cur_alt_setting].format_type = desc->bFormatType;
+ audio_class->module[format_offset].altsetting[cur_alt_setting].bitresolution = desc->bBitResolution;
+ audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq_num = desc->bSamFreqType;
+
+ for (uint8_t j = 0; j < desc->bSamFreqType; j++) {
+ audio_class->module[format_offset].altsetting[cur_alt_setting].sampfreq[j] = (uint32_t)(p[10 + j] << 16) |
+ (uint32_t)(p[9 + j] << 8) |
+ (uint32_t)(p[8 + j] << 0);
+ }
+ if (cur_alt_setting == (hport->config.intf[intf + 1].altsetting_num - 1)) {
+ format_offset++;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ if ((input_offset != output_offset) && (input_offset != feature_unit_offset) && (input_offset != format_offset)) {
+ return -USB_ERR_INVAL;
+ }
+
+ audio_class->module_num = input_offset;
+
+ for (size_t i = 0; i < audio_class->module_num; i++) {
+ ret = usbh_audio_close(audio_class, audio_class->module[i].name);
+ if (ret < 0) {
+ USB_LOG_ERR("Fail to close audio module :%s\r\n", audio_class->module[i].name);
+ return ret;
+ }
+ }
+
+ usbh_audio_list_module(audio_class);
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, audio_class->minor);
+ USB_LOG_INFO("Register Audio Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_audio_run(audio_class);
+ return 0;
+}
+
+static int usbh_audio_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_audio *audio_class = (struct usbh_audio *)hport->config.intf[intf].priv;
+
+ if (audio_class) {
+ if (audio_class->isoin) {
+ }
+
+ if (audio_class->isoout) {
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister Audio Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_audio_stop(audio_class);
+ }
+
+ usbh_audio_class_free(audio_class);
+ }
+
+ return ret;
+}
+
+static int usbh_audio_data_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+static int usbh_audio_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+__WEAK void usbh_audio_run(struct usbh_audio *audio_class)
+{
+}
+
+__WEAK void usbh_audio_stop(struct usbh_audio *audio_class)
+{
+}
+
+const struct usbh_class_driver audio_ctrl_class_driver = {
+ .driver_name = "audio_ctrl",
+ .connect = usbh_audio_ctrl_connect,
+ .disconnect = usbh_audio_ctrl_disconnect
+};
+
+const struct usbh_class_driver audio_streaming_class_driver = {
+ .driver_name = "audio_streaming",
+ .connect = usbh_audio_data_connect,
+ .disconnect = usbh_audio_data_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info audio_ctrl_intf_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
+ .class = USB_DEVICE_CLASS_AUDIO,
+ .subclass = AUDIO_SUBCLASS_AUDIOCONTROL,
+ .protocol = 0x00,
+ .id_table = NULL,
+ .class_driver = &audio_ctrl_class_driver
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info audio_streaming_intf_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
+ .class = USB_DEVICE_CLASS_AUDIO,
+ .subclass = AUDIO_SUBCLASS_AUDIOSTREAMING,
+ .protocol = 0x00,
+ .id_table = NULL,
+ .class_driver = &audio_streaming_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/audio/usbh_audio.h b/components/drivers/usb/cherryusb/class/audio/usbh_audio.h
new file mode 100644
index 0000000000..038a8f7e93
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/audio/usbh_audio.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_AUDIO_H
+#define USBH_AUDIO_H
+
+#include "usb_audio.h"
+
+struct usbh_audio_format_type {
+ uint8_t channels;
+ uint8_t format_type;
+ uint8_t bitresolution;
+ uint8_t sampfreq_num;
+ uint32_t sampfreq[3];
+};
+
+/**
+ * bSourceID in feature_unit = input_terminal_id
+ * bSourceID in output_terminal = feature_unit_id
+ * terminal_link_id = input_terminal_id or output_terminal_id (if input_terminal_type or output_terminal_type is 0x0101)
+ *
+ *
+*/
+struct usbh_audio_module {
+ const char *name;
+ uint8_t data_intf;
+ uint8_t input_terminal_id;
+ uint16_t input_terminal_type;
+ uint16_t input_channel_config;
+ uint8_t output_terminal_id;
+ uint16_t output_terminal_type;
+ uint8_t feature_unit_id;
+ uint8_t feature_unit_controlsize;
+ uint8_t feature_unit_controls[8];
+ uint8_t terminal_link_id;
+ struct usbh_audio_format_type altsetting[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS];
+};
+
+struct usbh_audio {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *isoin; /* ISO IN endpoint */
+ struct usb_endpoint_descriptor *isoout; /* ISO OUT endpoint */
+
+ uint8_t ctrl_intf; /* interface number */
+ uint8_t minor;
+ uint16_t isoin_mps;
+ uint16_t isoout_mps;
+ bool is_opened;
+ uint16_t bcdADC;
+ uint8_t bInCollection;
+ uint8_t num_of_intf_altsettings;
+ struct usbh_audio_module module[2];
+ uint8_t module_num;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_audio_open(struct usbh_audio *audio_class, const char *name, uint32_t samp_freq);
+int usbh_audio_close(struct usbh_audio *audio_class, const char *name);
+int usbh_audio_set_volume(struct usbh_audio *audio_class, const char *name, uint8_t ch, uint8_t volume);
+int usbh_audio_set_mute(struct usbh_audio *audio_class, const char *name, uint8_t ch, bool mute);
+
+void usbh_audio_run(struct usbh_audio *audio_class);
+void usbh_audio_stop(struct usbh_audio *audio_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_AUDIO_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h b/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h
new file mode 100644
index 0000000000..b8a544f3f3
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usb_cdc.h
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_CDC_H
+#define USB_CDC_H
+
+/*------------------------------------------------------------------------------
+ * Definitions based on usbcdc11.pdf (www.usb.org)
+ *----------------------------------------------------------------------------*/
+/* Communication device class specification version 1.10 */
+#define CDC_V1_10 0x0110U
+// Communication device class specification version 1.2
+#define CDC_V1_2_0 0x0120U
+
+/* Communication interface class code */
+/* (usbcdc11.pdf, 4.2, Table 15) */
+#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02U
+
+/* Communication interface class subclass codes */
+/* (usbcdc11.pdf, 4.3, Table 16) */
+#define CDC_SUBCLASS_NONE 0x00 /* Reserved */
+#define CDC_SUBCLASS_DLC 0x01 /* Direct Line Control Model */
+#define CDC_SUBCLASS_ACM 0x02 /* Abstract Control Model */
+#define CDC_SUBCLASS_TCM 0x03 /* Telephone Control Model */
+#define CDC_SUBCLASS_MCM 0x04 /* Multi-Channel Control Model */
+#define CDC_SUBCLASS_CAPI 0x05 /* CAPI Control Model */
+#define CDC_SUBCLASS_ECM 0x06 /* Ethernet Networking Control Model */
+#define CDC_SUBCLASS_ATM 0x07 /* ATM Networking Control Model */
+ /* 0x08-0x0d Reserved (future use) */
+#define CDC_SUBCLASS_MBIM 0x0e /* MBIM Control Model */
+ /* 0x0f-0x7f Reserved (future use) */
+ /* 0x80-0xfe Reserved (vendor specific) */
+
+#define CDC_DIRECT_LINE_CONTROL_MODEL 0x01U
+#define CDC_ABSTRACT_CONTROL_MODEL 0x02U
+#define CDC_TELEPHONE_CONTROL_MODEL 0x03U
+#define CDC_MULTI_CHANNEL_CONTROL_MODEL 0x04U
+#define CDC_CAPI_CONTROL_MODEL 0x05U
+#define CDC_ETHERNET_NETWORKING_CONTROL_MODEL 0x06U
+#define CDC_ATM_NETWORKING_CONTROL_MODEL 0x07U
+#define CDC_WIRELESS_HANDSET_CONTROL_MODEL 0x08U
+#define CDC_DEVICE_MANAGEMENT 0x09U
+#define CDC_MOBILE_DIRECT_LINE_MODEL 0x0AU
+#define CDC_OBEX 0x0BU
+#define CDC_ETHERNET_EMULATION_MODEL 0x0CU
+#define CDC_NETWORK_CONTROL_MODEL 0x0DU
+
+/* Communication interface class control protocol codes */
+/* (usbcdc11.pdf, 4.4, Table 17) */
+#define CDC_COMMON_PROTOCOL_NONE 0x00U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS 0x01U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101 0x02U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO 0x03U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_GSM_707 0x04U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_3GPP_27007 0x05U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_CDMA 0x06U
+#define CDC_COMMON_PROTOCOL_ETHERNET_EMULATION_MODEL 0x07U
+// NCM Communication Interface Protocol Codes
+// (usbncm10.pdf, 4.2, Table 4-2)
+#define CDC_NCM_PROTOCOL_NONE 0x00U
+#define CDC_NCM_PROTOCOL_OEM 0xFEU
+
+/* Data interface class code */
+/* (usbcdc11.pdf, 4.5, Table 18) */
+#define CDC_DATA_INTERFACE_CLASS 0x0A
+
+/* Data Interface Sub-Class Codes ********************************************/
+#define CDC_DATA_SUBCLASS_NONE 0x00
+
+/* Data interface class protocol codes */
+/* (usbcdc11.pdf, 4.7, Table 19) */
+#define CDC_DATA_PROTOCOL_ISDN_BRI 0x30
+#define CDC_DATA_PROTOCOL_HDLC 0x31
+#define CDC_DATA_PROTOCOL_TRANSPARENT 0x32
+#define CDC_DATA_PROTOCOL_Q921_MANAGEMENT 0x50
+#define CDC_DATA_PROTOCOL_Q921_DATA_LINK 0x51
+#define CDC_DATA_PROTOCOL_Q921_MULTIPLEXOR 0x52
+#define CDC_DATA_PROTOCOL_V42 0x90
+#define CDC_DATA_PROTOCOL_EURO_ISDN 0x91
+#define CDC_DATA_PROTOCOL_V24_RATE_ADAPTATION 0x92
+#define CDC_DATA_PROTOCOL_CAPI 0x93
+#define CDC_DATA_PROTOCOL_HOST_BASED_DRIVER 0xFD
+#define CDC_DATA_PROTOCOL_DESCRIBED_IN_PUFD 0xFE
+
+/* Type values for bDescriptorType field of functional descriptors */
+/* (usbcdc11.pdf, 5.2.3, Table 24) */
+#define CDC_CS_INTERFACE 0x24
+#define CDC_CS_ENDPOINT 0x25
+
+/* Type values for bDescriptorSubtype field of functional descriptors */
+/* (usbcdc11.pdf, 5.2.3, Table 25) */
+#define CDC_FUNC_DESC_HEADER 0x00
+#define CDC_FUNC_DESC_CALL_MANAGEMENT 0x01
+#define CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT 0x02
+#define CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT 0x03
+#define CDC_FUNC_DESC_TELEPHONE_RINGER 0x04
+#define CDC_FUNC_DESC_REPORTING_CAPABILITIES 0x05
+#define CDC_FUNC_DESC_UNION 0x06
+#define CDC_FUNC_DESC_COUNTRY_SELECTION 0x07
+#define CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES 0x08
+#define CDC_FUNC_DESC_USB_TERMINAL 0x09
+#define CDC_FUNC_DESC_NETWORK_CHANNEL 0x0A
+#define CDC_FUNC_DESC_PROTOCOL_UNIT 0x0B
+#define CDC_FUNC_DESC_EXTENSION_UNIT 0x0C
+#define CDC_FUNC_DESC_MULTI_CHANNEL_MANAGEMENT 0x0D
+#define CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT 0x0E
+#define CDC_FUNC_DESC_ETHERNET_NETWORKING 0x0F
+#define CDC_FUNC_DESC_ATM_NETWORKING 0x10
+#define CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL 0x11
+#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL 0x12
+#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL 0x13
+#define CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL 0x14
+#define CDC_FUNC_DESC_OBEX 0x15
+#define CDC_FUNC_DESC_COMMAND_SET 0x16
+#define CDC_FUNC_DESC_COMMAND_SET_DETAIL 0x17
+#define CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL 0x18
+#define CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER 0x19
+#define CDC_FUNC_DESC_NCM 0x1A
+
+/* CDC class-specific request codes */
+/* (usbcdc11.pdf, 6.2, Table 46) */
+/* see Table 45 for info about the specific requests. */
+#define CDC_REQUEST_SEND_ENCAPSULATED_COMMAND 0x00
+#define CDC_REQUEST_GET_ENCAPSULATED_RESPONSE 0x01
+#define CDC_REQUEST_SET_COMM_FEATURE 0x02
+#define CDC_REQUEST_GET_COMM_FEATURE 0x03
+#define CDC_REQUEST_CLEAR_COMM_FEATURE 0x04
+#define CDC_REQUEST_SET_AUX_LINE_STATE 0x10
+#define CDC_REQUEST_SET_HOOK_STATE 0x11
+#define CDC_REQUEST_PULSE_SETUP 0x12
+#define CDC_REQUEST_SEND_PULSE 0x13
+#define CDC_REQUEST_SET_PULSE_TIME 0x14
+#define CDC_REQUEST_RING_AUX_JACK 0x15
+#define CDC_REQUEST_SET_LINE_CODING 0x20
+#define CDC_REQUEST_GET_LINE_CODING 0x21
+#define CDC_REQUEST_SET_CONTROL_LINE_STATE 0x22
+#define CDC_REQUEST_SEND_BREAK 0x23
+#define CDC_REQUEST_SET_RINGER_PARMS 0x30
+#define CDC_REQUEST_GET_RINGER_PARMS 0x31
+#define CDC_REQUEST_SET_OPERATION_PARMS 0x32
+#define CDC_REQUEST_GET_OPERATION_PARMS 0x33
+#define CDC_REQUEST_SET_LINE_PARMS 0x34
+#define CDC_REQUEST_GET_LINE_PARMS 0x35
+#define CDC_REQUEST_DIAL_DIGITS 0x36
+#define CDC_REQUEST_SET_UNIT_PARAMETER 0x37
+#define CDC_REQUEST_GET_UNIT_PARAMETER 0x38
+#define CDC_REQUEST_CLEAR_UNIT_PARAMETER 0x39
+#define CDC_REQUEST_GET_PROFILE 0x3A
+#define CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS 0x40
+#define CDC_REQUEST_SET_ETHERNET_PMP_FILTER 0x41
+#define CDC_REQUEST_GET_ETHERNET_PMP_FILTER 0x42
+#define CDC_REQUEST_SET_ETHERNET_PACKET_FILTER 0x43
+#define CDC_REQUEST_GET_ETHERNET_STATISTIC 0x44
+#define CDC_REQUEST_SET_ATM_DATA_FORMAT 0x50
+#define CDC_REQUEST_GET_ATM_DEVICE_STATISTICS 0x51
+#define CDC_REQUEST_SET_ATM_DEFAULT_VC 0x52
+#define CDC_REQUEST_GET_ATM_VC_STATISTICS 0x53
+#define CDC_REQUEST_GET_NTB_PARAMETERS 0x80
+#define CDC_REQUEST_GET_NET_ADDRESS 0x81
+#define CDC_REQUEST_SET_NET_ADDRESS 0x82
+#define CDC_REQUEST_GET_NTB_FORMAT 0x83
+#define CDC_REQUEST_SET_NTB_FORMAT 0x84
+#define CDC_REQUEST_GET_NTB_INPUT_SIZE 0x85
+#define CDC_REQUEST_SET_NTB_INPUT_SIZE 0x86
+#define CDC_REQUEST_GET_MAX_DATAGRAM_SIZE 0x87
+#define CDC_REQUEST_SET_MAX_DATAGRAM_SIZE 0x88
+#define CDC_REQUEST_GET_CRC_MODE 0x89
+#define CDC_REQUEST_SET_CRC_MODE 0x90
+
+/* Communication feature selector codes */
+/* (usbcdc11.pdf, 6.2.2..6.2.4, Table 47) */
+#define CDC_ABSTRACT_STATE 0x01
+#define CDC_COUNTRY_SETTING 0x02
+
+/** Control Signal Bitmap Values for SetControlLineState */
+#define SET_CONTROL_LINE_STATE_RTS 0x02
+#define SET_CONTROL_LINE_STATE_DTR 0x01
+
+/* Feature Status returned for ABSTRACT_STATE Selector */
+/* (usbcdc11.pdf, 6.2.3, Table 48) */
+#define CDC_IDLE_SETTING (1 << 0)
+#define CDC_DATA_MULTPLEXED_STATE (1 << 1)
+
+/* Control signal bitmap values for the SetControlLineState request */
+/* (usbcdc11.pdf, 6.2.14, Table 51) */
+#define CDC_DTE_PRESENT (1 << 0)
+#define CDC_ACTIVATE_CARRIER (1 << 1)
+
+/* CDC class-specific notification codes */
+/* (usbcdc11.pdf, 6.3, Table 68) */
+/* see Table 67 for Info about class-specific notifications */
+#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00
+#define CDC_RESPONSE_AVAILABLE 0x01
+#define CDC_AUX_JACK_HOOK_STATE 0x08
+#define CDC_RING_DETECT 0x09
+#define CDC_NOTIFICATION_SERIAL_STATE 0x20
+#define CDC_CALL_STATE_CHANGE 0x28
+#define CDC_LINE_STATE_CHANGE 0x29
+#define CDC_CONNECTION_SPEED_CHANGE 0x2A
+
+/* UART state bitmap values (Serial state notification). */
+/* (usbcdc11.pdf, 6.3.5, Table 69) */
+#define CDC_SERIAL_STATE_OVERRUN (1 << 6) /* receive data overrun error has occurred */
+#define CDC_SERIAL_STATE_OVERRUN_Pos (6)
+#define CDC_SERIAL_STATE_OVERRUN_Msk (1 << CDC_SERIAL_STATE_OVERRUN_Pos)
+#define CDC_SERIAL_STATE_PARITY (1 << 5) /* parity error has occurred */
+#define CDC_SERIAL_STATE_PARITY_Pos (5)
+#define CDC_SERIAL_STATE_PARITY_Msk (1 << CDC_SERIAL_STATE_PARITY_Pos)
+#define CDC_SERIAL_STATE_FRAMING (1 << 4) /* framing error has occurred */
+#define CDC_SERIAL_STATE_FRAMING_Pos (4)
+#define CDC_SERIAL_STATE_FRAMING_Msk (1 << CDC_SERIAL_STATE_FRAMING_Pos)
+#define CDC_SERIAL_STATE_RING (1 << 3) /* state of ring signal detection */
+#define CDC_SERIAL_STATE_RING_Pos (3)
+#define CDC_SERIAL_STATE_RING_Msk (1 << CDC_SERIAL_STATE_RING_Pos)
+#define CDC_SERIAL_STATE_BREAK (1 << 2) /* state of break detection */
+#define CDC_SERIAL_STATE_BREAK_Pos (2)
+#define CDC_SERIAL_STATE_BREAK_Msk (1 << CDC_SERIAL_STATE_BREAK_Pos)
+#define CDC_SERIAL_STATE_TX_CARRIER (1 << 1) /* state of transmission carrier */
+#define CDC_SERIAL_STATE_TX_CARRIER_Pos (1)
+#define CDC_SERIAL_STATE_TX_CARRIER_Msk (1 << CDC_SERIAL_STATE_TX_CARRIER_Pos)
+#define CDC_SERIAL_STATE_RX_CARRIER (1 << 0) /* state of receiver carrier */
+#define CDC_SERIAL_STATE_RX_CARRIER_Pos (0)
+#define CDC_SERIAL_STATE_RX_CARRIER_Msk (1 << CDC_SERIAL_STATE_RX_CARRIER_Pos)
+
+#define CDC_ECM_XMIT_OK (1 << 0)
+#define CDC_ECM_RVC_OK (1 << 1)
+#define CDC_ECM_XMIT_ERROR (1 << 2)
+#define CDC_ECM_RCV_ERROR (1 << 3)
+#define CDC_ECM_RCV_NO_BUFFER (1 << 4)
+#define CDC_ECM_DIRECTED_BYTES_XMIT (1 << 5)
+#define CDC_ECM_DIRECTED_FRAMES_XMIT (1 << 6)
+#define CDC_ECM_MULTICAST_BYTES_XMIT (1 << 7)
+#define CDC_ECM_MULTICAST_FRAMES_XMIT (1 << 8)
+#define CDC_ECM_BROADCAST_BYTES_XMIT (1 << 9)
+#define CDC_ECM_BROADCAST_FRAMES_XMIT (1 << 10)
+#define CDC_ECM_DIRECTED_BYTES_RCV (1 << 11)
+#define CDC_ECM_DIRECTED_FRAMES_RCV (1 << 12)
+#define CDC_ECM_MULTICAST_BYTES_RCV (1 << 13)
+#define CDC_ECM_MULTICAST_FRAMES_RCV (1 << 14)
+#define CDC_ECM_BROADCAST_BYTES_RCV (1 << 15)
+#define CDC_ECM_BROADCAST_FRAMES_RCV (1 << 16)
+#define CDC_ECM_RCV_CRC_ERROR (1 << 17)
+#define CDC_ECM_TRANSMIT_QUEUE_LENGTH (1 << 18)
+#define CDC_ECM_RCV_ERROR_ALIGNMENT (1 << 19)
+#define CDC_ECM_XMIT_ONE_COLLISION (1 << 20)
+#define CDC_ECM_XMIT_MORE_COLLISIONS (1 << 21)
+#define CDC_ECM_XMIT_DEFERRED (1 << 22)
+#define CDC_ECM_XMIT_MAX_COLLISIONS (1 << 23)
+#define CDC_ECM_RCV_OVERRUN (1 << 24)
+#define CDC_ECM_XMIT_UNDERRUN (1 << 25)
+#define CDC_ECM_XMIT_HEARTBEAT_FAILURE (1 << 26)
+#define CDC_ECM_XMIT_TIMES_CRS_LOST (1 << 27)
+#define CDC_ECM_XMIT_LATE_COLLISIONS (1 << 28)
+
+#define CDC_ECM_MAC_STR_DESC (uint8_t *)"010202030000"
+#define CDC_ECM_MAC_ADDR0 0x00U /* 01 */
+#define CDC_ECM_MAC_ADDR1 0x02U /* 02 */
+#define CDC_ECM_MAC_ADDR2 0x02U /* 03 */
+#define CDC_ECM_MAC_ADDR3 0x03U /* 00 */
+#define CDC_ECM_MAC_ADDR4 0x00U /* 00 */
+#define CDC_ECM_MAC_ADDR5 0x00U /* 00 */
+
+#define CDC_ECM_NET_DISCONNECTED 0x00U
+#define CDC_ECM_NET_CONNECTED 0x01U
+
+#define CDC_ECM_ETH_STATS_RESERVED 0xE0U
+#define CDC_ECM_BMREQUEST_TYPE_ECM 0xA1U
+
+#define CDC_ECM_CONNECT_SPEED_UPSTREAM 0x004C4B40U /* 5Mbps */
+#define CDC_ECM_CONNECT_SPEED_DOWNSTREAM 0x004C4B40U /* 5Mbps */
+
+#define CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION 0x00
+#define CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE 0x01
+#define CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE 0x2A
+
+#define CDC_NCM_NTH16_SIGNATURE 0x484D434E
+#define CDC_NCM_NDP16_SIGNATURE_NCM0 0x304D434E
+#define CDC_NCM_NDP16_SIGNATURE_NCM1 0x314D434E
+
+/*------------------------------------------------------------------------------
+ * Structures based on usbcdc11.pdf (www.usb.org)
+ *----------------------------------------------------------------------------*/
+
+/* Header functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.1) */
+/* This header must precede any list of class-specific descriptors. */
+struct cdc_header_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* Header functional descriptor subtype */
+ uint16_t bcdCDC; /* USB CDC specification release version */
+} __PACKED;
+
+/* Call management functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.2) */
+/* Describes the processing of calls for the communication class interface. */
+struct cdc_call_management_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* call management functional descriptor subtype */
+ uint8_t bmCapabilities; /* capabilities that this configuration supports */
+ uint8_t bDataInterface; /* interface number of the data class interface used for call management (optional) */
+} __PACKED;
+
+/* Abstract control management functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.3) */
+/* Describes the command supported by the communication interface class with the Abstract Control Model subclass code. */
+struct cdc_abstract_control_management_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* abstract control management functional descriptor subtype */
+ uint8_t bmCapabilities; /* capabilities supported by this configuration */
+} __PACKED;
+
+/* Union functional descriptors */
+/* (usbcdc11.pdf, 5.2.3.8) */
+/* Describes the relationship between a group of interfaces that can be considered to form a functional unit. */
+struct cdc_union_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* union functional descriptor subtype */
+ uint8_t bMasterInterface; /* interface number designated as master */
+} __PACKED;
+
+/* Union functional descriptors with one slave interface */
+/* (usbcdc11.pdf, 5.2.3.8) */
+struct cdc_union_1slave_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bControlInterface;
+ uint8_t bSubordinateInterface0;
+} __PACKED;
+
+/* Line coding structure for GET_LINE_CODING / SET_LINE_CODING class requests*/
+/* Format of the data returned when a GetLineCoding request is received */
+/* (usbcdc11.pdf, 6.2.13) */
+struct cdc_line_coding {
+ uint32_t dwDTERate; /* Data terminal rate in bits per second */
+ uint8_t bCharFormat; /* Number of stop bits */
+ uint8_t bParityType; /* Parity bit type */
+ uint8_t bDataBits; /* Number of data bits */
+} __PACKED;
+
+/** Data structure for the notification about SerialState */
+struct cdc_acm_notification {
+ uint8_t bmRequestType;
+ uint8_t bNotificationType;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint16_t data;
+} __PACKED;
+
+/** Ethernet Networking Functional Descriptor */
+struct cdc_eth_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t iMACAddress;
+ uint32_t bmEthernetStatistics;
+ uint16_t wMaxSegmentSize;
+ uint16_t wNumberMCFilters;
+ uint8_t bNumberPowerFilters;
+} __PACKED;
+
+struct cdc_eth_notification {
+ uint8_t bmRequestType;
+ uint8_t bNotificationType;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint8_t data[8];
+} __PACKED;
+
+struct cdc_ncm_ntb_parameters {
+ 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;
+};
+
+struct cdc_ncm_nth16 {
+ uint32_t dwSignature;
+ uint16_t wHeaderLength;
+ uint16_t wSequence;
+ uint16_t wBlockLength;
+ uint16_t wNdpIndex;
+};
+
+struct cdc_ncm_ndp16_datagram {
+ uint16_t wDatagramIndex;
+ uint16_t wDatagramLength;
+};
+
+struct cdc_ncm_ndp16 {
+ uint32_t dwSignature;
+ uint16_t wLength;
+ uint16_t wNextNdpIndex;
+ struct cdc_ncm_ndp16_datagram datagram[];
+};
+
+/*Length of template descriptor: 66 bytes*/
+#define CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
+// clang-format off
+#define CDC_ACM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x00, /* bmCapabilities */ \
+ (uint8_t)(bFirstInterface + 1), /* bDataInterface */ \
+ 0x04, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x02, /* bmCapabilities */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x08, 0x00, /* wMaxPacketSize */ \
+ 0x0a, /* bInterval */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00 /* bInterval */
+// clang-format on
+
+/*Length of template descriptor: 66 bytes*/
+#define CDC_RNDIS_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
+// clang-format off
+#define CDC_RNDIS_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_WIRELESS, /* bFunctionClass */ \
+ CDC_DIRECT_LINE_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_WIRELESS, /* bInterfaceClass */ \
+ CDC_DIRECT_LINE_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x00, /* bmCapabilities */ \
+ (uint8_t)(bFirstInterface + 1), /* bDataInterface */ \
+ 0x04, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x00, /* bmCapabilities */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x08, 0x00, /* wMaxPacketSize */ \
+ 0x10, /* bInterval */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00 /* bInterval */
+// clang-format on
+
+#define DBVAL_BE(x) ((x >> 24) & 0xFF), ((x >> 16) & 0xFF), ((x >> 8) & 0xFF), (x & 0xFF)
+
+/*Length of template descriptor: 71 bytes*/
+#define CDC_ECM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 7 + 9 + 7 + 7)
+// clang-format off
+#define CDC_ECM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
+eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
+ CDC_ETHERNET_NETWORKING_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
+ CDC_ETHERNET_NETWORKING_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
+ /* CDC_ECM Functional Descriptor */ \
+ 0x0D, /* bFunctionLength */\
+ CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
+ CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
+ str_idx, /* Device's MAC string index */\
+ DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
+ WBVAL(wMaxPacketSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
+ WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
+ bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x10, 0x00, /* wMaxPacketSize */ \
+ 0x10, /* bInterval */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00 /* bInterval */
+// clang-format on
+
+/*Length of template descriptor: 77 bytes*/
+#define CDC_NCM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 6 + 7 + 9 + 7 + 7)
+// clang-format off
+#define CDC_NCM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \
+eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
+ CDC_NETWORK_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
+ CDC_NETWORK_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
+ /* CDC ETH Functional Descriptor */ \
+ 0x0D, /* bFunctionLength */\
+ CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\
+ CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\
+ str_idx, /* Device's MAC string index */\
+ DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\
+ WBVAL(wMaxPacketSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\
+ WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\
+ bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\
+ 0x06, \
+ CDC_CS_INTERFACE, \
+ CDC_FUNC_DESC_NCM, \
+ 0x00, 0x01, \
+ 0x23, \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x10, 0x00, /* wMaxPacketSize */ \
+ 0x10, /* bInterval */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00 /* bInterval */
+// clang-format on
+
+#endif /* USB_CDC_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c
new file mode 100644
index 0000000000..50d13d7d53
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_cdc.h"
+
+const char *stop_name[] = { "1", "1.5", "2" };
+const char *parity_name[] = { "N", "O", "E", "M", "S" };
+
+static int cdc_acm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("CDC Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ struct cdc_line_coding line_coding;
+ bool dtr, rts;
+ uint8_t intf_num = LO_BYTE(setup->wIndex);
+
+ switch (setup->bRequest) {
+ case CDC_REQUEST_SET_LINE_CODING:
+
+ /*******************************************************************************/
+ /* Line Coding Structure */
+ /*-----------------------------------------------------------------------------*/
+ /* Offset | Field | Size | Value | Description */
+ /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
+ /* 4 | bCharFormat | 1 | Number | Stop bits */
+ /* 0 - 1 Stop bit */
+ /* 1 - 1.5 Stop bits */
+ /* 2 - 2 Stop bits */
+ /* 5 | bParityType | 1 | Number | Parity */
+ /* 0 - None */
+ /* 1 - Odd */
+ /* 2 - Even */
+ /* 3 - Mark */
+ /* 4 - Space */
+ /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
+ /*******************************************************************************/
+ memcpy(&line_coding, *data, setup->wLength);
+ USB_LOG_DBG("Set intf:%d linecoding <%d %d %s %s>\r\n",
+ intf_num,
+ line_coding.dwDTERate,
+ line_coding.bDataBits,
+ parity_name[line_coding.bParityType],
+ stop_name[line_coding.bCharFormat]);
+
+ usbd_cdc_acm_set_line_coding(busid, intf_num, &line_coding);
+ break;
+
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ dtr = (setup->wValue & 0x0001);
+ rts = (setup->wValue & 0x0002);
+ USB_LOG_DBG("Set intf:%d DTR 0x%x,RTS 0x%x\r\n",
+ intf_num,
+ dtr,
+ rts);
+ usbd_cdc_acm_set_dtr(busid, intf_num, dtr);
+ usbd_cdc_acm_set_rts(busid, intf_num, rts);
+ break;
+
+ case CDC_REQUEST_GET_LINE_CODING:
+ usbd_cdc_acm_get_line_coding(busid, intf_num, &line_coding);
+ memcpy(*data, &line_coding, 7);
+ *len = 7;
+ USB_LOG_DBG("Get intf:%d linecoding %d %d %d %d\r\n",
+ intf_num,
+ line_coding.dwDTERate,
+ line_coding.bCharFormat,
+ line_coding.bParityType,
+ line_coding.bDataBits);
+ break;
+ case CDC_REQUEST_SEND_BREAK:
+ usbd_cdc_acm_send_break(busid, intf_num);
+ break;
+ default:
+ USB_LOG_WRN("Unhandled CDC Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf)
+{
+ intf->class_interface_handler = cdc_acm_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = NULL;
+
+ return intf;
+}
+
+__WEAK void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding)
+{
+}
+
+__WEAK void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding)
+{
+ line_coding->dwDTERate = 2000000;
+ line_coding->bDataBits = 8;
+ line_coding->bParityType = 0;
+ line_coding->bCharFormat = 0;
+}
+
+__WEAK void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr)
+{
+}
+
+__WEAK void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts)
+{
+}
+
+__WEAK void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf)
+{
+}
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h
new file mode 100644
index 0000000000..ebf3d029e6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_CDC_H
+#define USBD_CDC_H
+
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init cdc acm interface driver */
+struct usbd_interface *usbd_cdc_acm_init_intf(uint8_t busid, struct usbd_interface *intf);
+
+/* Setup request command callback api */
+void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
+void usbd_cdc_acm_get_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding);
+void usbd_cdc_acm_set_dtr(uint8_t busid, uint8_t intf, bool dtr);
+void usbd_cdc_acm_set_rts(uint8_t busid, uint8_t intf, bool rts);
+void usbd_cdc_acm_send_break(uint8_t busid, uint8_t intf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_CDC_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c
new file mode 100644
index 0000000000..1fca9cadce
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2023, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_cdc_ecm.h"
+
+#define CDC_ECM_OUT_EP_IDX 0
+#define CDC_ECM_IN_EP_IDX 1
+#define CDC_ECM_INT_EP_IDX 2
+
+/* Describe EndPoints configuration */
+static struct usbd_endpoint cdc_ecm_ep_data[3];
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
+
+volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
+volatile uint32_t g_cdc_ecm_rx_data_length = 0;
+volatile uint32_t g_cdc_ecm_tx_data_length = 0;
+
+static volatile uint8_t g_current_net_status = 0;
+static volatile uint8_t g_cmd_intf = 0;
+
+static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
+ CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
+
+void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
+{
+ struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ecm_notify_buf;
+ uint8_t bytes2send = 0;
+
+ notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
+ notify->bNotificationType = notifycode;
+
+ switch (notifycode) {
+ case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
+ notify->wValue = value;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0U;
+
+ for (uint8_t i = 0U; i < 8U; i++) {
+ notify->data[i] = 0U;
+ }
+ bytes2send = 8U;
+ break;
+ case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
+ notify->wValue = 0U;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0U;
+ for (uint8_t i = 0U; i < 8U; i++) {
+ notify->data[i] = 0U;
+ }
+ bytes2send = 8U;
+ break;
+ case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
+ notify->wValue = 0U;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0x0008U;
+ bytes2send = 16U;
+
+ memcpy(notify->data, speed, 8);
+ break;
+
+ default:
+ break;
+ }
+
+ if (bytes2send) {
+ usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
+ }
+}
+
+static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("CDC ECM Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ g_cmd_intf = LO_BYTE(setup->wIndex);
+
+ switch (setup->bRequest) {
+ case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
+ /* bit0 Promiscuous
+ * bit1 ALL Multicast
+ * bit2 Directed
+ * bit3 Broadcast
+ * bit4 Multicast
+ */
+ if (g_current_net_status == 0) {
+ g_current_net_status = 1;
+ usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
+ }
+
+ break;
+ default:
+ USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ g_current_net_status = 0;
+ g_cdc_ecm_rx_data_length = 0;
+ g_cdc_ecm_tx_data_length = 0;
+ g_cdc_ecm_rx_data_buffer = NULL;
+ break;
+ case USBD_EVENT_CONFIGURED:
+ usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ g_cdc_ecm_rx_data_length += nbytes;
+
+ if (nbytes < usbd_get_ep_mps(busid, ep)) {
+ g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
+ usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
+ } else {
+ usbd_ep_start_read(0, ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], usbd_get_ep_mps(busid, ep));
+ }
+}
+
+void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
+ /* send zlp */
+ usbd_ep_start_write(0, ep, NULL, 0);
+ } else {
+ g_cdc_ecm_tx_data_length = 0;
+ }
+}
+
+void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if (g_current_net_status == 1) {
+ g_current_net_status = 2;
+ usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
+ }
+}
+
+int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
+{
+ if (g_cdc_ecm_tx_data_length > 0) {
+ return -USB_ERR_BUSY;
+ }
+
+ g_cdc_ecm_tx_data_length = len;
+
+ USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
+ return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, g_cdc_ecm_tx_data_length);
+}
+
+void usbd_cdc_ecm_start_read_next(void)
+{
+ g_cdc_ecm_rx_data_length = 0;
+ g_cdc_ecm_rx_data_buffer = NULL;
+ usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, usbd_get_ep_mps(busid, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr));
+}
+
+#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
+struct pbuf *usbd_cdc_ecm_eth_rx(void)
+{
+ struct pbuf *p;
+
+ if (g_cdc_ecm_rx_data_buffer == NULL) {
+ return NULL;
+ }
+ p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
+ if (p == NULL) {
+ usbd_cdc_ecm_start_read_next();
+ return NULL;
+ }
+ memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
+ p->len = g_cdc_ecm_rx_data_length;
+
+ USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
+ usbd_cdc_ecm_start_read_next();
+ return p;
+}
+
+int usbd_cdc_ecm_eth_tx(struct pbuf *p)
+{
+ struct pbuf *q;
+ uint8_t *buffer;
+
+ if (g_cdc_ecm_tx_data_length > 0) {
+ return -USB_ERR_BUSY;
+ }
+
+ if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
+ p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
+ }
+
+ buffer = g_cdc_ecm_tx_buffer;
+ for (q = p; q != NULL; q = q->next) {
+ memcpy(buffer, q->payload, q->len);
+ buffer += q->len;
+ }
+
+ g_cdc_ecm_tx_data_length = p->tot_len;
+
+ return usbd_cdc_ecm_start_write(g_cdc_ecm_tx_buffer, g_cdc_ecm_tx_data_length);
+}
+#endif
+
+struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep)
+{
+ intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = cdc_ecm_notify_handler;
+
+ cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
+ cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
+ cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
+ cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
+ cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
+ cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
+
+ usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
+ usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
+ usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
+
+ return intf;
+}
+
+void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
+{
+ memcpy(g_connect_speed_table, speed, 8);
+}
+
+__WEAK void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len)
+{
+} \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h
new file mode 100644
index 0000000000..8d9e5c9fe0
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbd_cdc_ecm.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_CDC_ECM_H
+#define USBD_CDC_ECM_H
+
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Ethernet Maximum Segment size, typically 1514 bytes */
+#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U
+
+/* Init cdc ecm interface driver */
+struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep);
+
+/* Setup request command callback api */
+void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]);
+
+/* Api for eth only without any net stack */
+uint8_t *usbd_cdc_ecm_get_tx_buffer(void);
+void usbd_cdc_ecm_send_done(void);
+int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len);
+void usbd_cdc_ecm_data_recv_done(uint8_t *buf, uint32_t len);
+void usbd_cdc_ecm_start_read_next(void);
+
+#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+struct pbuf *usbd_cdc_ecm_eth_rx(void);
+int usbd_cdc_ecm_eth_tx(struct pbuf *p);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_CDC_ECM_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c
new file mode 100644
index 0000000000..05184af81c
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_cdc_acm.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_cdc_acm"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/ttyACM%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_acm_buf[64];
+
+static struct usbh_cdc_acm g_cdc_acm_class[CONFIG_USBHOST_MAX_CDC_ACM_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_cdc_acm_class[devno], 0, sizeof(struct usbh_cdc_acm));
+ g_cdc_acm_class[devno].minor = devno;
+ return &g_cdc_acm_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_cdc_acm_class_free(struct usbh_cdc_acm *cdc_acm_class)
+{
+ int devno = cdc_acm_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
+}
+
+int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = cdc_acm_class->intf;
+ setup->wLength = 7;
+
+ memcpy(g_cdc_acm_buf, line_coding, sizeof(struct cdc_line_coding));
+
+ return usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf);
+}
+
+int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = cdc_acm_class->intf;
+ setup->wLength = 7;
+
+ ret = usbh_control_transfer(cdc_acm_class->hport, setup, g_cdc_acm_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(line_coding, g_cdc_acm_buf, sizeof(struct cdc_line_coding));
+ return ret;
+}
+
+int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
+ setup->wValue = (dtr << 0) | (rts << 1);
+ setup->wIndex = cdc_acm_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cdc_acm_class->hport, setup, NULL);
+}
+
+static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+
+ struct usbh_cdc_acm *cdc_acm_class = usbh_cdc_acm_class_alloc();
+ if (cdc_acm_class == NULL) {
+ USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ cdc_acm_class->hport = hport;
+ cdc_acm_class->intf = intf;
+
+ hport->config.intf[intf].priv = cdc_acm_class;
+ hport->config.intf[intf + 1].priv = NULL;
+
+#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ USBH_EP_INIT(cdc_acm_class->intin, ep_desc);
+#endif
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_acm_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cdc_acm_class->bulkout, ep_desc);
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cdc_acm_class->minor);
+
+ USB_LOG_INFO("Register CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
+
+#if 0
+ USB_LOG_INFO("Test cdc acm rx and tx and rx for 5 times, baudrate is 115200\r\n");
+
+ struct cdc_line_coding linecoding;
+ uint8_t count = 5;
+
+ linecoding.dwDTERate = 115200;
+ linecoding.bDataBits = 8;
+ linecoding.bParityType = 0;
+ linecoding.bCharFormat = 0;
+ usbh_cdc_acm_set_line_coding(cdc_acm_class, &linecoding);
+ usbh_cdc_acm_set_line_state(cdc_acm_class, true, false);
+
+ memset(g_cdc_acm_buf, 'a', sizeof(g_cdc_acm_buf));
+ ret = usbh_cdc_acm_bulk_out_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
+ USB_LOG_RAW("out ret:%d\r\n", ret);
+ while (count--) {
+ ret = usbh_cdc_acm_bulk_in_transfer(cdc_acm_class, g_cdc_acm_buf, sizeof(g_cdc_acm_buf), 0xfffffff);
+ USB_LOG_RAW("in ret:%d\r\n", ret);
+ if (ret > 0) {
+ for (uint32_t i = 0; i < ret; i++) {
+ USB_LOG_RAW("%02x ", g_cdc_acm_buf[i]);
+ }
+ }
+ USB_LOG_RAW("\r\n");
+ }
+#endif
+
+ usbh_cdc_acm_run(cdc_acm_class);
+ return ret;
+}
+
+static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv;
+
+ if (cdc_acm_class) {
+ if (cdc_acm_class->bulkin) {
+ usbh_kill_urb(&cdc_acm_class->bulkin_urb);
+ }
+
+ if (cdc_acm_class->bulkout) {
+ usbh_kill_urb(&cdc_acm_class->bulkout_urb);
+ }
+
+#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
+ if (cdc_acm_class->intin) {
+ usbh_kill_urb(&cdc_acm_class->intin_urb);
+ }
+#endif
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_cdc_acm_stop(cdc_acm_class);
+ }
+
+ usbh_cdc_acm_class_free(cdc_acm_class);
+ }
+
+ return ret;
+}
+
+int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &cdc_acm_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &cdc_acm_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, cdc_acm_class->hport, cdc_acm_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+__WEAK void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
+{
+}
+
+__WEAK void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
+{
+}
+
+const struct usbh_class_driver cdc_acm_class_driver = {
+ .driver_name = "cdc_acm",
+ .connect = usbh_cdc_acm_connect,
+ .disconnect = usbh_cdc_acm_disconnect
+};
+
+const struct usbh_class_driver cdc_data_class_driver = {
+ .driver_name = "cdc_data",
+ .connect = usbh_cdc_data_connect,
+ .disconnect = usbh_cdc_data_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_CDC,
+ .subclass = CDC_ABSTRACT_CONTROL_MODEL,
+ .protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
+ .id_table = NULL,
+ .class_driver = &cdc_acm_class_driver
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS,
+ .class = USB_DEVICE_CLASS_CDC_DATA,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = NULL,
+ .class_driver = &cdc_data_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.h b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.h
new file mode 100644
index 0000000000..80800bf9f2
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_acm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CDC_ACM_H
+#define USBH_CDC_ACM_H
+
+#include "usb_cdc.h"
+
+struct usbh_cdc_acm {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
+ struct usb_endpoint_descriptor *intin; /* INTR IN endpoint (optional) */
+#endif
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+#ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
+ struct usbh_urb intin_urb;
+#endif
+
+ struct cdc_line_coding linecoding;
+
+ uint8_t intf;
+ uint8_t minor;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
+int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding);
+int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts);
+
+int usbh_cdc_acm_bulk_in_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+int usbh_cdc_acm_bulk_out_transfer(struct usbh_cdc_acm *cdc_acm_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+
+void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class);
+void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CDC_ACM_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c
new file mode 100644
index 0000000000..5e3ae7bc71
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_cdc_ecm.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_cdc_ecm"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/cdc_ether"
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+#define CONFIG_USBHOST_CDC_ECM_PKT_FILTER 0x000C
+#define CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE 1514U
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_inttx_buffer[16];
+
+static struct usbh_cdc_ecm g_cdc_ecm_class;
+
+static int usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t filter_value)
+{
+ struct usb_setup_packet *setup = cdc_ecm_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_ETHERNET_PACKET_FILTER;
+ setup->wValue = filter_value;
+ setup->wIndex = cdc_ecm_class->ctrl_intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cdc_ecm_class->hport, setup, NULL);
+}
+
+int usbh_cdc_ecm_get_connect_status(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+ int ret;
+
+ usbh_int_urb_fill(&cdc_ecm_class->intin_urb, cdc_ecm_class->hport, cdc_ecm_class->intin, g_cdc_ecm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&cdc_ecm_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) {
+ if (g_cdc_ecm_inttx_buffer[2] == CDC_ECM_NET_CONNECTED) {
+ cdc_ecm_class->connect_status = true;
+ } else {
+ cdc_ecm_class->connect_status = false;
+ }
+ } else if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) {
+ memcpy(cdc_ecm_class->speed, &g_cdc_ecm_inttx_buffer[8], 8);
+ }
+ return 0;
+}
+
+static int usbh_cdc_ecm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ uint8_t altsetting = 0;
+ char mac_buffer[12];
+ uint8_t *p;
+ uint8_t cur_iface = 0xff;
+ uint8_t mac_str_idx = 0xff;
+
+ struct usbh_cdc_ecm *cdc_ecm_class = &g_cdc_ecm_class;
+
+ memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
+
+ cdc_ecm_class->hport = hport;
+ cdc_ecm_class->ctrl_intf = intf;
+ cdc_ecm_class->data_intf = intf + 1;
+
+ hport->config.intf[intf].priv = cdc_ecm_class;
+ hport->config.intf[intf + 1].priv = NULL;
+
+ p = hport->raw_config_desc;
+ while (p[DESC_bLength]) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ cur_iface = p[INTF_DESC_bInterfaceNumber];
+ //cur_alt_setting = p[INTF_DESC_bAlternateSetting];
+ break;
+ case CDC_CS_INTERFACE:
+ if ((cur_iface == cdc_ecm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
+ struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p;
+ mac_str_idx = desc->iMACAddress;
+ cdc_ecm_class->max_segment_size = desc->wMaxSegmentSize;
+ goto get_mac;
+ }
+ break;
+
+ default:
+ break;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+get_mac:
+ if (mac_str_idx == 0xff) {
+ USB_LOG_ERR("Do not find cdc ecm mac string\r\n");
+ return -1;
+ }
+
+ memset(mac_buffer, 0, 12);
+ ret = usbh_get_string_desc(cdc_ecm_class->hport, mac_str_idx, (uint8_t *)mac_buffer);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (int i = 0, j = 0; i < 12; i += 2, j++) {
+ char byte_str[3];
+ byte_str[0] = mac_buffer[i];
+ byte_str[1] = mac_buffer[i + 1];
+ byte_str[2] = '\0';
+
+ uint32_t byte = strtoul(byte_str, NULL, 16);
+ cdc_ecm_class->mac[j] = (unsigned char)byte;
+ }
+
+ USB_LOG_INFO("CDC ECM MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
+ cdc_ecm_class->mac[0],
+ cdc_ecm_class->mac[1],
+ cdc_ecm_class->mac[2],
+ cdc_ecm_class->mac[3],
+ cdc_ecm_class->mac[4],
+ cdc_ecm_class->mac[5]);
+
+ if (cdc_ecm_class->max_segment_size > CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE) {
+ USB_LOG_ERR("CDC ECM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE, cdc_ecm_class->max_segment_size);
+ } else {
+ USB_LOG_INFO("CDC ECM Max Segment Size:%u\r\n", cdc_ecm_class->max_segment_size);
+ }
+
+ /* enable int ep */
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ USBH_EP_INIT(cdc_ecm_class->intin, ep_desc);
+
+ if (hport->config.intf[intf + 1].altsetting_num > 1) {
+ altsetting = hport->config.intf[intf + 1].altsetting_num - 1;
+
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_ecm_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cdc_ecm_class->bulkout, ep_desc);
+ }
+ }
+
+ USB_LOG_INFO("Select cdc ecm altsetting: %d\r\n", altsetting);
+ usbh_set_interface(cdc_ecm_class->hport, cdc_ecm_class->data_intf, altsetting);
+ } else {
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_ecm_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cdc_ecm_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ /* bit0 Promiscuous
+ * bit1 ALL Multicast
+ * bit2 Directed
+ * bit3 Broadcast
+ * bit4 Multicast
+ */
+ ret = usbh_cdc_ecm_set_eth_packet_filter(cdc_ecm_class, CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
+ if (ret < 0) {
+ return ret;
+ }
+ USB_LOG_INFO("Set CDC ECM packet filter:%04x\r\n", CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
+
+ memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
+
+ USB_LOG_INFO("Register CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_cdc_ecm_run(cdc_ecm_class);
+ return ret;
+}
+
+static int usbh_cdc_ecm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_cdc_ecm *cdc_ecm_class = (struct usbh_cdc_ecm *)hport->config.intf[intf].priv;
+
+ if (cdc_ecm_class) {
+ if (cdc_ecm_class->bulkin) {
+ usbh_kill_urb(&cdc_ecm_class->bulkin_urb);
+ }
+
+ if (cdc_ecm_class->bulkout) {
+ usbh_kill_urb(&cdc_ecm_class->bulkout_urb);
+ }
+
+ if (cdc_ecm_class->intin) {
+ usbh_kill_urb(&cdc_ecm_class->intin_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_cdc_ecm_stop(cdc_ecm_class);
+ }
+
+ memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
+ }
+
+ return ret;
+}
+
+void usbh_cdc_ecm_rx_thread(void *argument)
+{
+ uint32_t g_cdc_ecm_rx_length;
+ int ret;
+
+ USB_LOG_INFO("Create cdc ecm rx thread\r\n");
+ // clang-format off
+find_class:
+ // clang-format on
+ g_cdc_ecm_class.connect_status = false;
+ if (usbh_find_class_instance("/dev/cdc_ether") == NULL) {
+ goto delete;
+ }
+
+ while (g_cdc_ecm_class.connect_status == false) {
+ ret = usbh_cdc_ecm_get_connect_status(&g_cdc_ecm_class);
+ if (ret < 0) {
+ usb_osal_msleep(100);
+ goto find_class;
+ }
+ }
+
+ g_cdc_ecm_rx_length = 0;
+ while (1) {
+ usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkin_urb, g_cdc_ecm_class.hport, g_cdc_ecm_class.bulkin, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_length], CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_cdc_ecm_class.bulkin_urb);
+ if (ret < 0) {
+ goto find_class;
+ }
+
+ g_cdc_ecm_rx_length += g_cdc_ecm_class.bulkin_urb.actual_length;
+
+ if (g_cdc_ecm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ecm_class.bulkin->wMaxPacketSize)) {
+ USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_length);
+
+ usbh_cdc_ecm_eth_input(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_length);
+
+ g_cdc_ecm_rx_length = 0;
+ } else {
+ /* read continue util read short packet */
+ if (g_cdc_ecm_rx_length > CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE) {
+ USB_LOG_ERR("Rx packet is overflow\r\n");
+ g_cdc_ecm_rx_length = 0;
+ }
+ }
+ }
+ // clang-format off
+delete:
+ USB_LOG_INFO("Delete cdc ecm rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+int usbh_cdc_ecm_eth_output(uint8_t *buf, uint32_t buflen)
+{
+ uint8_t *buffer = g_cdc_ecm_tx_buffer;
+
+ if (g_cdc_ecm_class.connect_status == false) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ memcpy(buffer, buf, buflen);
+
+ USB_LOG_DBG("txlen:%d\r\n", buflen);
+
+ usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkout_urb, g_cdc_ecm_class.hport, g_cdc_ecm_class.bulkout, g_cdc_ecm_tx_buffer, buflen, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ return usbh_submit_urb(&g_cdc_ecm_class.bulkout_urb);
+}
+
+__WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+}
+
+__WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+}
+
+const struct usbh_class_driver cdc_ecm_class_driver = {
+ .driver_name = "cdc_ecm",
+ .connect = usbh_cdc_ecm_connect,
+ .disconnect = usbh_cdc_ecm_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_CDC,
+ .subclass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL,
+ .protocol = CDC_COMMON_PROTOCOL_NONE,
+ .id_table = NULL,
+ .class_driver = &cdc_ecm_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.h b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.h
new file mode 100644
index 0000000000..d965ac88a5
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ecm.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CDC_ECM_H
+#define USBH_CDC_ECM_H
+
+#include "usb_cdc.h"
+
+struct usbh_cdc_ecm {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usb_endpoint_descriptor *intin; /* Interrupt IN endpoint */
+ struct usbh_urb bulkout_urb; /* Bulk out endpoint */
+ struct usbh_urb bulkin_urb; /* Bulk IN endpoint */
+ struct usbh_urb intin_urb; /* Interrupt IN endpoint */
+
+ uint8_t ctrl_intf; /* Control interface number */
+ uint8_t data_intf; /* Data interface number */
+ uint8_t minor;
+
+ uint8_t mac[6];
+ bool connect_status;
+ uint16_t max_segment_size;
+ uint32_t speed[2];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_cdc_ecm_get_connect_status(struct usbh_cdc_ecm *cdc_ecm_class);
+
+void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class);
+void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class);
+
+int usbh_cdc_ecm_eth_output(uint8_t *buf, uint32_t buflen);
+void usbh_cdc_ecm_eth_input(uint8_t *buf, uint32_t buflen);
+void usbh_cdc_ecm_rx_thread(void *argument);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CDC_ECM_H */
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c
new file mode 100644
index 0000000000..4f13f791ea
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_cdc_ncm.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_cdc_ncm"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/cdc_ncm"
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE 1514U
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_rx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_tx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_inttx_buffer[16];
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_buf[32];
+
+static struct usbh_cdc_ncm g_cdc_ncm_class;
+
+static int usbh_cdc_ncm_get_ntb_parameters(struct usbh_cdc_ncm *cdc_ncm_class, struct cdc_ncm_ntb_parameters *param)
+{
+ struct usb_setup_packet *setup = cdc_ncm_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_NTB_PARAMETERS;
+ setup->wValue = 0;
+ setup->wIndex = cdc_ncm_class->ctrl_intf;
+ setup->wLength = 28;
+
+ ret = usbh_control_transfer(cdc_ncm_class->hport, setup, g_cdc_ncm_buf);
+ if (ret < 0) {
+ return ret;
+ }
+
+ memcpy((uint8_t *)param, g_cdc_ncm_buf, ret - 8);
+ return 0;
+}
+
+static void print_ntb_parameters(struct cdc_ncm_ntb_parameters *param)
+{
+ USB_LOG_RAW("CDC NCM ntb parameters:\r\n");
+ USB_LOG_RAW("wLength: 0x%02x \r\n", param->wLength);
+ USB_LOG_RAW("bmNtbFormatsSupported: %s \r\n", param->bmNtbFormatsSupported ? "NTB16" : "NTB32");
+
+ USB_LOG_RAW("dwNtbInMaxSize: 0x%04x \r\n", param->dwNtbInMaxSize);
+ USB_LOG_RAW("wNdbInDivisor: 0x%02x \r\n", param->wNdbInDivisor);
+ USB_LOG_RAW("wNdbInPayloadRemainder: 0x%02x \r\n", param->wNdbInPayloadRemainder);
+ USB_LOG_RAW("wNdbInAlignment: 0x%02x \r\n", param->wNdbInAlignment);
+
+ USB_LOG_RAW("dwNtbOutMaxSize: 0x%04x \r\n", param->dwNtbOutMaxSize);
+ USB_LOG_RAW("wNdbOutDivisor: 0x%02x \r\n", param->wNdbOutDivisor);
+ USB_LOG_RAW("wNdbOutPayloadRemainder: 0x%02x \r\n", param->wNdbOutPayloadRemainder);
+ USB_LOG_RAW("wNdbOutAlignment: 0x%02x \r\n", param->wNdbOutAlignment);
+
+ USB_LOG_RAW("wNtbOutMaxDatagrams: 0x%02x \r\n", param->wNtbOutMaxDatagrams);
+}
+
+int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class)
+{
+ int ret;
+
+ usbh_int_urb_fill(&cdc_ncm_class->intin_urb, cdc_ncm_class->hport, cdc_ncm_class->intin, g_cdc_ncm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&cdc_ncm_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) {
+ if (g_cdc_ncm_inttx_buffer[2] == CDC_ECM_NET_CONNECTED) {
+ cdc_ncm_class->connect_status = true;
+ } else {
+ cdc_ncm_class->connect_status = false;
+ }
+ } else if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) {
+ memcpy(cdc_ncm_class->speed, &g_cdc_ncm_inttx_buffer[8], 8);
+ }
+ return 0;
+}
+
+static int usbh_cdc_ncm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ uint8_t altsetting = 0;
+ char mac_buffer[12];
+ uint8_t *p;
+ uint8_t cur_iface = 0xff;
+ uint8_t mac_str_idx = 0xff;
+
+ struct usbh_cdc_ncm *cdc_ncm_class = &g_cdc_ncm_class;
+
+ memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm));
+
+ cdc_ncm_class->hport = hport;
+ cdc_ncm_class->ctrl_intf = intf;
+ cdc_ncm_class->data_intf = intf + 1;
+
+ hport->config.intf[intf].priv = cdc_ncm_class;
+ hport->config.intf[intf + 1].priv = NULL;
+
+ p = hport->raw_config_desc;
+ while (p[DESC_bLength]) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ cur_iface = p[INTF_DESC_bInterfaceNumber];
+ //cur_alt_setting = p[INTF_DESC_bAlternateSetting];
+ break;
+ case CDC_CS_INTERFACE:
+ if ((cur_iface == cdc_ncm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
+ struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p;
+ mac_str_idx = desc->iMACAddress;
+ cdc_ncm_class->max_segment_size = desc->wMaxSegmentSize;
+ goto get_mac;
+ }
+ break;
+
+ default:
+ break;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+get_mac:
+ if (mac_str_idx == 0xff) {
+ USB_LOG_ERR("Do not find cdc ncm mac string\r\n");
+ return -1;
+ }
+
+ memset(mac_buffer, 0, 12);
+ ret = usbh_get_string_desc(cdc_ncm_class->hport, mac_str_idx, (uint8_t *)mac_buffer);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (int i = 0, j = 0; i < 12; i += 2, j++) {
+ char byte_str[3];
+ byte_str[0] = mac_buffer[i];
+ byte_str[1] = mac_buffer[i + 1];
+ byte_str[2] = '\0';
+
+ uint32_t byte = strtoul(byte_str, NULL, 16);
+ cdc_ncm_class->mac[j] = (unsigned char)byte;
+ }
+
+ USB_LOG_INFO("CDC NCM MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
+ cdc_ncm_class->mac[0],
+ cdc_ncm_class->mac[1],
+ cdc_ncm_class->mac[2],
+ cdc_ncm_class->mac[3],
+ cdc_ncm_class->mac[4],
+ cdc_ncm_class->mac[5]);
+
+ if (cdc_ncm_class->max_segment_size > CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE) {
+ USB_LOG_ERR("CDC NCM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE, cdc_ncm_class->max_segment_size);
+ } else {
+ USB_LOG_INFO("CDC NCM Max Segment Size:%u\r\n", cdc_ncm_class->max_segment_size);
+ }
+
+ usbh_cdc_ncm_get_ntb_parameters(cdc_ncm_class, &cdc_ncm_class->ntb_param);
+ print_ntb_parameters(&cdc_ncm_class->ntb_param);
+
+ /* enable int ep */
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ USBH_EP_INIT(cdc_ncm_class->intin, ep_desc);
+
+ if (hport->config.intf[intf + 1].altsetting_num > 1) {
+ altsetting = hport->config.intf[intf + 1].altsetting_num - 1;
+
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc);
+ }
+ }
+
+ USB_LOG_INFO("Select cdc ncm altsetting: %d\r\n", altsetting);
+ usbh_set_interface(cdc_ncm_class->hport, cdc_ncm_class->data_intf, altsetting);
+ } else {
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
+
+ USB_LOG_INFO("Register CDC NCM Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_cdc_ncm_run(cdc_ncm_class);
+ return ret;
+}
+
+static int usbh_cdc_ncm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_cdc_ncm *cdc_ncm_class = (struct usbh_cdc_ncm *)hport->config.intf[intf].priv;
+
+ if (cdc_ncm_class) {
+ if (cdc_ncm_class->bulkin) {
+ usbh_kill_urb(&cdc_ncm_class->bulkin_urb);
+ }
+
+ if (cdc_ncm_class->bulkout) {
+ usbh_kill_urb(&cdc_ncm_class->bulkout_urb);
+ }
+
+ if (cdc_ncm_class->intin) {
+ usbh_kill_urb(&cdc_ncm_class->intin_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CDC NCM Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_cdc_ncm_stop(cdc_ncm_class);
+ }
+
+ memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm));
+ }
+
+ return ret;
+}
+
+void usbh_cdc_ncm_rx_thread(void *argument)
+{
+ uint32_t g_cdc_ncm_rx_length;
+ int ret;
+
+ USB_LOG_INFO("Create cdc ncm rx thread\r\n");
+ // clang-format off
+find_class:
+ // clang-format on
+ g_cdc_ncm_class.connect_status = false;
+ if (usbh_find_class_instance("/dev/cdc_ncm") == NULL) {
+ goto delete;
+ }
+
+ while (g_cdc_ncm_class.connect_status == false) {
+ ret = usbh_cdc_ncm_get_connect_status(&g_cdc_ncm_class);
+ if (ret < 0) {
+ usb_osal_msleep(100);
+ goto find_class;
+ }
+ }
+
+ g_cdc_ncm_rx_length = 0;
+ while (1) {
+ usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkin_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkin, &g_cdc_ncm_rx_buffer[g_cdc_ncm_rx_length], (CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE > (16 * 1024)) ? (16 * 1024) : CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_cdc_ncm_class.bulkin_urb);
+ if (ret < 0) {
+ goto find_class;
+ }
+
+ g_cdc_ncm_rx_length += g_cdc_ncm_class.bulkin_urb.actual_length;
+
+ if (g_cdc_ncm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize)) {
+ USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ncm_rx_length);
+
+ struct cdc_ncm_nth16 *nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_rx_buffer[0];
+ if ((nth16->dwSignature != CDC_NCM_NTH16_SIGNATURE) ||
+ (nth16->wHeaderLength != 12) ||
+ (nth16->wBlockLength != g_cdc_ncm_rx_length)) {
+ USB_LOG_ERR("invalid rx nth16\r\n");
+ g_cdc_ncm_rx_length = 0;
+ continue;
+ }
+
+ struct cdc_ncm_ndp16 *ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_rx_buffer[nth16->wNdpIndex];
+ if ((ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM0) && (ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM1)) {
+ USB_LOG_ERR("invalid rx ndp16\r\n");
+ g_cdc_ncm_rx_length = 0;
+ continue;
+ }
+
+ uint16_t datagram_num = (ndp16->wLength - 8) / 4;
+
+ USB_LOG_DBG("datagram num:%02x\r\n", datagram_num);
+ for (uint16_t i = 0; i < datagram_num; i++) {
+ struct cdc_ncm_ndp16_datagram *ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_rx_buffer[nth16->wNdpIndex + 8 + 4 * i];
+ if (ndp16_datagram->wDatagramIndex && ndp16_datagram->wDatagramLength) {
+ USB_LOG_DBG("ndp16_datagram index:%02x, length:%02x\r\n", ndp16_datagram->wDatagramIndex, ndp16_datagram->wDatagramLength);
+
+ uint8_t *buf = (uint8_t *)&g_cdc_ncm_rx_buffer[ndp16_datagram->wDatagramIndex];
+ usbh_cdc_ncm_eth_input(buf, ndp16_datagram->wDatagramLength);
+ }
+ }
+
+ g_cdc_ncm_rx_length = 0;
+
+ } else {
+ if (g_cdc_ncm_rx_length > CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
+ USB_LOG_ERR("Rx packet is overflow\r\n");
+ g_cdc_ncm_rx_length = 0;
+ }
+ }
+ }
+ // clang-format off
+delete:
+ USB_LOG_INFO("Delete cdc ncm rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+int usbh_cdc_ncm_eth_output(uint8_t *buf, uint32_t buflen)
+{
+ uint8_t *buffer;
+ struct cdc_ncm_ndp16_datagram *ndp16_datagram;
+
+ if (g_cdc_ncm_class.connect_status == false) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ struct cdc_ncm_nth16 *nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_tx_buffer[0];
+
+ nth16->dwSignature = CDC_NCM_NTH16_SIGNATURE;
+ nth16->wHeaderLength = 12;
+ nth16->wSequence = g_cdc_ncm_class.bulkout_sequence++;
+ nth16->wBlockLength = 16 + 16 + USB_ALIGN_UP(buflen, 4);
+ nth16->wNdpIndex = 16 + USB_ALIGN_UP(buflen, 4);
+
+ struct cdc_ncm_ndp16 *ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex];
+
+ ndp16->dwSignature = CDC_NCM_NDP16_SIGNATURE_NCM0;
+ ndp16->wLength = 16;
+ ndp16->wNextNdpIndex = 0;
+
+ ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 0];
+ ndp16_datagram->wDatagramIndex = 16;
+ ndp16_datagram->wDatagramLength = buflen;
+
+ ndp16_datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 1];
+ ndp16_datagram->wDatagramIndex = 0;
+ ndp16_datagram->wDatagramLength = 0;
+
+ buffer = &g_cdc_ncm_tx_buffer[16];
+ memcpy(buffer, buf, buflen);
+
+ USB_LOG_DBG("txlen:%d\r\n", nth16->wBlockLength);
+
+ usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkout_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkout, g_cdc_ncm_tx_buffer, nth16->wBlockLength, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ return usbh_submit_urb(&g_cdc_ncm_class.bulkout_urb);
+}
+
+__WEAK void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class)
+{
+}
+
+__WEAK void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class)
+{
+}
+
+const struct usbh_class_driver cdc_ncm_class_driver = {
+ .driver_name = "cdc_ncm",
+ .connect = usbh_cdc_ncm_connect,
+ .disconnect = usbh_cdc_ncm_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_ncm_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_CDC,
+ .subclass = CDC_NETWORK_CONTROL_MODEL,
+ .protocol = CDC_COMMON_PROTOCOL_NONE,
+ .id_table = NULL,
+ .class_driver = &cdc_ncm_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.h b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.h
new file mode 100644
index 0000000000..5f069401fa
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/cdc/usbh_cdc_ncm.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CDC_NCM_H
+#define USBH_CDC_NCM_H
+
+#include "usb_cdc.h"
+
+struct usbh_cdc_ncm {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usb_endpoint_descriptor *intin; /* Interrupt IN endpoint */
+ struct usbh_urb bulkout_urb; /* Bulk out endpoint */
+ struct usbh_urb bulkin_urb; /* Bulk IN endpoint */
+ struct usbh_urb intin_urb; /* Interrupt IN endpoint */
+
+ uint8_t ctrl_intf; /* Control interface number */
+ uint8_t data_intf; /* Data interface number */
+ uint8_t minor;
+
+ struct cdc_ncm_ntb_parameters ntb_param;
+ uint16_t bulkin_sequence;
+ uint16_t bulkout_sequence;
+
+ uint8_t mac[6];
+ bool connect_status;
+ uint16_t max_segment_size;
+ uint32_t speed[2];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class);
+
+void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class);
+void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class);
+
+int usbh_cdc_ncm_eth_output(uint8_t *buf, uint32_t buflen);
+void usbh_cdc_ncm_eth_input(uint8_t *buf, uint32_t buflen);
+void usbh_cdc_ncm_rx_thread(void *argument);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CDC_NCM_H */
diff --git a/components/drivers/usb/cherryusb/class/dfu/usb_dfu.h b/components/drivers/usb/cherryusb/class/dfu/usb_dfu.h
new file mode 100644
index 0000000000..f1ccba88cf
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/dfu/usb_dfu.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_DFU_H
+#define USB_DFU_H
+
+/**\addtogroup USB_MODULE_DFU USB DFU class
+ * \brief This module contains USB Device Firmware Upgrade class definitions.
+ * \details This module based on
+ * + [USB Device Firmware Upgrade Specification, Revision 1.1]
+ * (https://www.usb.org/sites/default/files/DFU_1.1.pdf)
+ * @{ */
+
+/** DFU Specification release */
+#define DFU_VERSION 0x0110
+
+/** DFU Class Subclass */
+#define DFU_SUBCLASS_DFU 0x01
+
+/** DFU Class runtime Protocol */
+#define DFU_PROTOCOL_RUNTIME 0x01
+
+/** DFU Class DFU mode Protocol */
+#define DFU_PROTOCOL_MODE 0x02
+
+/**
+ * @brief DFU Class Specific Requests
+ */
+#define DFU_REQUEST_DETACH 0x00
+#define DFU_REQUEST_DNLOAD 0x01
+#define DFU_REQUEST_UPLOAD 0x02
+#define DFU_REQUEST_GETSTATUS 0x03
+#define DFU_REQUEST_CLRSTATUS 0x04
+#define DFU_REQUEST_GETSTATE 0x05
+#define DFU_REQUEST_ABORT 0x06
+
+/** DFU FUNCTIONAL descriptor type */
+#define DFU_FUNC_DESC 0x21
+
+/** DFU attributes DFU Functional Descriptor */
+#define DFU_ATTR_WILL_DETACH 0x08
+#define DFU_ATTR_MANIFESTATION_TOLERANT 0x04
+#define DFU_ATTR_CAN_UPLOAD 0x02
+#define DFU_ATTR_CAN_DNLOAD 0x01
+
+/** bStatus values for the DFU_GETSTATUS response */
+#define DFU_STATUS_OK 0x00U
+#define DFU_STATUS_ERR_TARGET 0x01U
+#define DFU_STATUS_ERR_FILE 0x02U
+#define DFU_STATUS_ERR_WRITE 0x03U
+#define DFU_STATUS_ERR_ERASE 0x04U
+#define DFU_STATUS_ERR_CHECK_ERASED 0x05U
+#define DFU_STATUS_ERR_PROG 0x06U
+#define DFU_STATUS_ERR_VERIFY 0x07U
+#define DFU_STATUS_ERR_ADDRESS 0x08U
+#define DFU_STATUS_ERR_NOTDONE 0x09U
+#define DFU_STATUS_ERR_FIRMWARE 0x0AU
+#define DFU_STATUS_ERR_VENDOR 0x0BU
+#define DFU_STATUS_ERR_USB 0x0CU
+#define DFU_STATUS_ERR_POR 0x0DU
+#define DFU_STATUS_ERR_UNKNOWN 0x0EU
+#define DFU_STATUS_ERR_STALLEDPKT 0x0FU
+
+/** bState values for the DFU_GETSTATUS response */
+#define DFU_STATE_APP_IDLE 0U
+#define DFU_STATE_APP_DETACH 1U
+#define DFU_STATE_DFU_IDLE 2U
+#define DFU_STATE_DFU_DNLOAD_SYNC 3U
+#define DFU_STATE_DFU_DNLOAD_BUSY 4U
+#define DFU_STATE_DFU_DNLOAD_IDLE 5U
+#define DFU_STATE_DFU_MANIFEST_SYNC 6U
+#define DFU_STATE_DFU_MANIFEST 7U
+#define DFU_STATE_DFU_MANIFEST_WAIT_RESET 8U
+#define DFU_STATE_DFU_UPLOAD_IDLE 9U
+#define DFU_STATE_DFU_ERROR 10U
+
+/** DFU Manifestation State */
+#define DFU_MANIFEST_COMPLETE 0U
+#define DFU_MANIFEST_IN_PROGRESS 1U
+
+/** Special Commands with Download Request */
+#define DFU_CMD_GETCOMMANDS 0U
+#define DFU_CMD_SETADDRESSPOINTER 0x21U
+#define DFU_CMD_ERASE 0x41U
+#define DFU_MEDIA_ERASE 0x00U
+#define DFU_MEDIA_PROGRAM 0x01U
+
+/** Other defines */
+/* Bit Detach capable = bit 3 in bmAttributes field */
+#define DFU_DETACH_MASK (1U << 3)
+#define DFU_MANIFEST_MASK (1U << 2)
+
+/** Run-Time Functional Descriptor */
+struct dfu_runtime_descriptor {
+ uint8_t bLength; /**<\brief Descriptor length in bytes.*/
+ uint8_t bDescriptorType; /**<\brief DFU functional descriptor type.*/
+ uint8_t bmAttributes; /**<\brief USB DFU capabilities \ref USB_DFU_CAPAB*/
+ uint16_t wDetachTimeout; /**<\brief USB DFU detach timeout in ms.*/
+ uint16_t wTransferSize; /**<\brief USB DFU maximum transfer block size in bytes.*/
+ uint16_t bcdDFUVersion; /**<\brief USB DFU version \ref VERSION_BCD utility macro.*/
+} __PACKED;
+
+/**\brief Payload packet to response in DFU_GETSTATUS request */
+struct dfu_info {
+ uint8_t bStatus; /**<\brief An indication of the status resulting from the
+ * execution of the most recent request.*/
+ uint8_t bPollTimeout; /**<\brief Minimum time (LSB) in ms, that the host should wait
+ * before sending a subsequent DFU_GETSTATUS request.*/
+ uint16_t wPollTimeout; /**<\brief Minimum time (MSB) in ms, that the host should wait
+ * before sending a subsequent DFU_GETSTATUS request.*/
+ uint8_t bState; /**<\brief An indication of the state that the device is going
+ * to enter immediately following transmission of this response.*/
+ uint8_t iString; /**<\brief Index of the status string descriptor.*/
+};
+
+// clang-format off
+#define DFU_DESCRIPTOR_INIT() \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ 0x00, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x00, /* bNumEndpoints Default Control Pipe only */ \
+ USB_DEVICE_CLASS_APP_SPECIFIC, /* bInterfaceClass */ \
+ 0x01, /* bInterfaceSubClass Device Firmware Upgrade */ \
+ 0x02, /* bInterfaceProtocol DFU mode */ \
+ 0x04, /* iInterface */ /*!< Device Firmware Update Functional Descriptor */ \
+ 0x09, /* bLength */ \
+ 0x21, /* DFU Functional Descriptor */ \
+ 0x0B, /* bmAttributes */ \
+ WBVAL(0x00ff), /* wDetachTimeOut */ \
+ WBVAL(USBD_DFU_XFER_SIZE), /* wTransferSize */ \
+ WBVAL(0x011a) /* bcdDFUVersion */
+// clang-format on
+
+#endif /* USB_DFU_H */
diff --git a/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.c b/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.c
new file mode 100644
index 0000000000..a870aee3a9
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_dfu.h"
+
+/** Modify the following three parameters according to different platforms */
+#ifndef USBD_DFU_XFER_SIZE
+#define USBD_DFU_XFER_SIZE 1024
+#endif
+
+#ifndef USBD_DFU_APP_DEFAULT_ADD
+#define USBD_DFU_APP_DEFAULT_ADD 0x8004000
+#endif
+
+#ifndef FLASH_PROGRAM_TIME
+#define FLASH_PROGRAM_TIME 50
+#endif
+
+#ifndef FLASH_ERASE_TIME
+#define FLASH_ERASE_TIME 50
+#endif
+
+struct usbd_dfu_priv {
+ struct dfu_info info;
+ union {
+ uint32_t d32[USBD_DFU_XFER_SIZE / 4U];
+ uint8_t d8[USBD_DFU_XFER_SIZE];
+ } buffer;
+
+ uint32_t wblock_num;
+ uint32_t wlength;
+ uint32_t data_ptr;
+ uint32_t alt_setting;
+
+ uint8_t dev_status[6];
+ uint8_t ReservedForAlign[2];
+ uint8_t dev_state;
+ uint8_t manif_state;
+ uint8_t firmwar_flag;
+} g_usbd_dfu;
+
+static void dfu_reset(void)
+{
+ memset(&g_usbd_dfu, 0, sizeof(g_usbd_dfu));
+
+ g_usbd_dfu.alt_setting = 0U;
+ g_usbd_dfu.data_ptr = USBD_DFU_APP_DEFAULT_ADD;
+ g_usbd_dfu.wblock_num = 0U;
+ g_usbd_dfu.wlength = 0U;
+
+ g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+
+ g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = DFU_STATE_DFU_IDLE;
+ g_usbd_dfu.dev_status[5] = 0U;
+}
+
+static uint16_t dfu_getstatus(uint32_t add, uint8_t cmd, uint8_t *buffer)
+{
+ switch (cmd) {
+ case DFU_MEDIA_PROGRAM:
+ buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;
+ buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);
+ buffer[3] = 0;
+ break;
+
+ case DFU_MEDIA_ERASE:
+ buffer[1] = (uint8_t)FLASH_ERASE_TIME;
+ buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
+ buffer[3] = 0;
+ default:
+
+ break;
+ }
+ return (0);
+}
+
+static void dfu_request_detach(void)
+{
+ if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
+ /* Update the state machine */
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+ g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U; /*bwPollTimeout=0ms*/
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ g_usbd_dfu.dev_status[5] = 0U; /*iString*/
+ g_usbd_dfu.wblock_num = 0U;
+ g_usbd_dfu.wlength = 0U;
+ }
+}
+
+static void dfu_request_upload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ struct usb_setup_packet *req = setup;
+ uint32_t addr;
+ uint8_t *phaddr;
+ /* Data setup request */
+ if (req->wLength > 0U) {
+ if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
+ /* Update the global length and block number */
+ g_usbd_dfu.wblock_num = req->wValue;
+ g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
+
+ /* DFU Get Command */
+ if (g_usbd_dfu.wblock_num == 0U) {
+ /* Update the state machine */
+ g_usbd_dfu.dev_state = (g_usbd_dfu.wlength > 3U) ? DFU_STATE_DFU_IDLE : DFU_STATE_DFU_UPLOAD_IDLE;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+
+ /* Store the values of all supported commands */
+ g_usbd_dfu.buffer.d8[0] = DFU_CMD_GETCOMMANDS;
+ g_usbd_dfu.buffer.d8[1] = DFU_CMD_SETADDRESSPOINTER;
+ g_usbd_dfu.buffer.d8[2] = DFU_CMD_ERASE;
+
+ /* Send the status data over EP0 */
+ memcpy(*data, g_usbd_dfu.buffer.d8, 3);
+ *len = 3;
+ } else if (g_usbd_dfu.wblock_num > 1U) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_UPLOAD_IDLE;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+
+ addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
+
+ /* Return the physical address where data are stored */
+ phaddr = dfu_read_flash((uint8_t *)addr, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
+
+ /* Send the status data over EP0 */
+ memcpy(*data, g_usbd_dfu.buffer.d8, g_usbd_dfu.wlength);
+ *len = g_usbd_dfu.wlength;
+ } else /* unsupported g_usbd_dfu.wblock_num */
+ {
+ g_usbd_dfu.dev_state = DFU_STATUS_ERR_STALLEDPKT;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+
+ /* Call the error management function (command will be NAKed */
+ USB_LOG_ERR("Dfu_request_upload unsupported g_usbd_dfu.wblock_num\r\n");
+ }
+ }
+ /* Unsupported state */
+ else {
+ g_usbd_dfu.wlength = 0U;
+ g_usbd_dfu.wblock_num = 0U;
+
+ /* Call the error management function (command will be NAKed */
+ USB_LOG_ERR("Dfu_request_upload unsupported state\r\n");
+ }
+ }
+ /* No Data setup request */
+ else {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ }
+}
+
+static void dfu_request_dnload(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ /* Data setup request */
+ struct usb_setup_packet *req = setup;
+ if (req->wLength > 0U) {
+ if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE)) {
+ /* Update the global length and block number */
+ g_usbd_dfu.wblock_num = req->wValue;
+ g_usbd_dfu.wlength = MIN(req->wLength, USBD_DFU_XFER_SIZE);
+
+ /* Update the state machine */
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+
+ /*!< Data has received complete */
+ memcpy((uint8_t *)g_usbd_dfu.buffer.d8, (uint8_t *)*data, g_usbd_dfu.wlength);
+ /*!< Set flag = 1 Write the firmware to the flash in the next dfu_request_getstatus */
+ g_usbd_dfu.firmwar_flag = 1;
+ }
+ /* Unsupported state */
+ else {
+ USB_LOG_ERR("Dfu_request_dnload unsupported state\r\n");
+ }
+ }
+ /* 0 Data DNLOAD request */
+ else {
+ /* End of DNLOAD operation*/
+ if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) || (g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE)) {
+ g_usbd_dfu.manif_state = DFU_MANIFEST_IN_PROGRESS;
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ } else {
+ /* Call the error management function (command will be NAKed */
+ USB_LOG_ERR("Dfu_request_dnload End of DNLOAD operation but dev_state %02x \r\n", g_usbd_dfu.dev_state);
+ }
+ }
+}
+
+static int8_t dfu_getstatus_special_handler(void)
+{
+ uint32_t addr;
+ if (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_BUSY) {
+ /* Decode the Special Command */
+ if (g_usbd_dfu.wblock_num == 0U) {
+ if (g_usbd_dfu.wlength == 1U) {
+ if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_GETCOMMANDS) {
+ /* Nothing to do */
+ }
+ } else if (g_usbd_dfu.wlength == 5U) {
+ if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_SETADDRESSPOINTER) {
+ g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
+ } else if (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE) {
+ g_usbd_dfu.data_ptr = g_usbd_dfu.buffer.d8[1];
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[2] << 8;
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[3] << 16;
+ g_usbd_dfu.data_ptr += (uint32_t)g_usbd_dfu.buffer.d8[4] << 24;
+
+ USB_LOG_DBG("Erase start add %08x \r\n", g_usbd_dfu.data_ptr);
+ /*!< Erase */
+ dfu_erase_flash(g_usbd_dfu.data_ptr);
+ } else {
+ return -1;
+ }
+ } else {
+ /* Reset the global length and block number */
+ g_usbd_dfu.wlength = 0U;
+ g_usbd_dfu.wblock_num = 0U;
+ /* Call the error management function (command will be NAKed) */
+ USB_LOG_ERR("Reset the global length and block number\r\n");
+ }
+ }
+ /* Regular Download Command */
+ else {
+ if (g_usbd_dfu.wblock_num > 1U) {
+ /* Decode the required address */
+ addr = ((g_usbd_dfu.wblock_num - 2U) * USBD_DFU_XFER_SIZE) + g_usbd_dfu.data_ptr;
+
+ /* Perform the write operation */
+ /* Write flash */
+ USB_LOG_DBG("Write start add %08x length %d\r\n", addr, g_usbd_dfu.wlength);
+ dfu_write_flash(g_usbd_dfu.buffer.d8, (uint8_t *)addr, g_usbd_dfu.wlength);
+ }
+ }
+
+ /* Reset the global length and block number */
+ g_usbd_dfu.wlength = 0U;
+ g_usbd_dfu.wblock_num = 0U;
+
+ /* Update the state machine */
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_SYNC;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ }
+ return 0;
+}
+
+static void dfu_request_getstatus(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ /*!< Determine whether to leave DFU mode */
+ if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS &&
+ g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC &&
+ g_usbd_dfu.dev_status[1] == 0U &&
+ g_usbd_dfu.dev_status[2] == 0U &&
+ g_usbd_dfu.dev_status[3] == 0U &&
+ g_usbd_dfu.dev_status[4] == g_usbd_dfu.dev_state) {
+ g_usbd_dfu.manif_state = DFU_MANIFEST_COMPLETE;
+
+ if ((0x0B & DFU_MANIFEST_MASK) != 0U) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_SYNC;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ return;
+ } else {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST_WAIT_RESET;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ /* Generate system reset to allow jumping to the user code */
+ dfu_leave();
+ }
+ }
+
+ switch (g_usbd_dfu.dev_state) {
+ case DFU_STATE_DFU_DNLOAD_SYNC:
+ if (g_usbd_dfu.wlength != 0U) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_BUSY;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+
+ if ((g_usbd_dfu.wblock_num == 0U) && (g_usbd_dfu.buffer.d8[0] == DFU_CMD_ERASE)) {
+ dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_ERASE, g_usbd_dfu.dev_status);
+ } else {
+ dfu_getstatus(g_usbd_dfu.data_ptr, DFU_MEDIA_PROGRAM, g_usbd_dfu.dev_status);
+ }
+ } else /* (g_usbd_dfu.wlength==0)*/
+ {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_DNLOAD_IDLE;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ }
+ break;
+
+ case DFU_STATE_DFU_MANIFEST_SYNC:
+ if (g_usbd_dfu.manif_state == DFU_MANIFEST_IN_PROGRESS) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_MANIFEST;
+
+ g_usbd_dfu.dev_status[1] = 1U; /*bwPollTimeout = 1ms*/
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ } else {
+ if ((g_usbd_dfu.manif_state == DFU_MANIFEST_COMPLETE) &&
+ ((0x0B & DFU_MANIFEST_MASK) != 0U)) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U;
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Send the status data over EP0 */
+ memcpy(*data, g_usbd_dfu.dev_status, 6);
+ *len = 6;
+
+ if (g_usbd_dfu.firmwar_flag == 1) {
+ if (dfu_getstatus_special_handler() != 0) {
+ USB_LOG_ERR("dfu_getstatus_special_handler error \r\n");
+ }
+ g_usbd_dfu.firmwar_flag = 0;
+ }
+}
+
+static void dfu_request_clrstatus(void)
+{
+ if (g_usbd_dfu.dev_state == DFU_STATE_DFU_ERROR) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+ g_usbd_dfu.dev_status[0] = DFU_STATUS_OK; /* bStatus */
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
+ g_usbd_dfu.dev_status[5] = 0U; /* iString */
+ } else {
+ /* State Error */
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_ERROR;
+ g_usbd_dfu.dev_status[0] = DFU_STATUS_ERR_UNKNOWN; /* bStatus */
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state; /* bState */
+ g_usbd_dfu.dev_status[5] = 0U; /* iString */
+ }
+}
+
+static void dfu_request_getstate(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ /* Return the current state of the DFU interface */
+ (*data)[0] = g_usbd_dfu.dev_state;
+ *len = 1;
+}
+
+void dfu_request_abort(void)
+{
+ if ((g_usbd_dfu.dev_state == DFU_STATE_DFU_IDLE) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_SYNC) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_DNLOAD_IDLE) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_MANIFEST_SYNC) ||
+ (g_usbd_dfu.dev_state == DFU_STATE_DFU_UPLOAD_IDLE)) {
+ g_usbd_dfu.dev_state = DFU_STATE_DFU_IDLE;
+ g_usbd_dfu.dev_status[0] = DFU_STATUS_OK;
+ g_usbd_dfu.dev_status[1] = 0U;
+ g_usbd_dfu.dev_status[2] = 0U;
+ g_usbd_dfu.dev_status[3] = 0U; /* bwPollTimeout=0ms */
+ g_usbd_dfu.dev_status[4] = g_usbd_dfu.dev_state;
+ g_usbd_dfu.dev_status[5] = 0U; /* iString */
+ g_usbd_dfu.wblock_num = 0U;
+ g_usbd_dfu.wlength = 0U;
+ }
+}
+
+static int dfu_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("DFU Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ switch (setup->bRequest) {
+ case DFU_REQUEST_DETACH:
+ dfu_request_detach();
+ break;
+ case DFU_REQUEST_DNLOAD:
+ dfu_request_dnload(setup, data, len);
+ break;
+ case DFU_REQUEST_UPLOAD:
+ dfu_request_upload(setup, data, len);
+ break;
+ case DFU_REQUEST_GETSTATUS:
+ dfu_request_getstatus(setup, data, len);
+ break;
+ case DFU_REQUEST_CLRSTATUS:
+ dfu_request_clrstatus();
+ break;
+ case DFU_REQUEST_GETSTATE:
+ dfu_request_getstate(setup, data, len);
+ break;
+ case DFU_REQUEST_ABORT:
+ dfu_request_abort();
+ break;
+ default:
+ USB_LOG_WRN("Unhandled DFU Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dfu_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ dfu_reset();
+ break;
+ default:
+ break;
+ }
+}
+
+struct usbd_interface *usbd_dfu_init_intf(struct usbd_interface *intf)
+{
+ intf->class_interface_handler = dfu_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = dfu_notify_handler;
+
+ return intf;
+}
+
+__WEAK uint8_t *dfu_read_flash(uint8_t *src, uint8_t *dest, uint32_t len)
+{
+ return dest;
+}
+
+__WEAK uint16_t dfu_write_flash(uint8_t *src, uint8_t *dest, uint32_t len)
+{
+ return 0;
+}
+
+__WEAK uint16_t dfu_erase_flash(uint32_t add)
+{
+ return 0;
+}
+
+__WEAK void dfu_leave(void)
+{
+}
diff --git a/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.h b/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.h
new file mode 100644
index 0000000000..9081d0dda1
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/dfu/usbd_dfu.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_DFU_H
+#define USBD_DFU_H
+
+#include "usb_dfu.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init dfu interface driver */
+struct usbd_interface *usbd_dfu_init_intf(struct usbd_interface *intf);
+
+/* Interface functions that need to be implemented by the user */
+uint8_t *dfu_read_flash(uint8_t *src, uint8_t *dest, uint32_t len);
+uint16_t dfu_write_flash(uint8_t *src, uint8_t *dest, uint32_t len);
+uint16_t dfu_erase_flash(uint32_t add);
+void dfu_leave(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_DFU_H */
diff --git a/components/drivers/usb/cherryusb/class/hid/usb_hid.h b/components/drivers/usb/cherryusb/class/hid/usb_hid.h
new file mode 100644
index 0000000000..5a88a34be4
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hid/usb_hid.h
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_HID_H
+#define USB_HID_H
+
+/* Subclass codes (HID 4.2) */
+#define HID_SUBCLASS_NONE 0 /* No subclass */
+#define HID_SUBCLASS_BOOTIF 1 /* Boot Interface Subclass */
+
+/* HID Protocol Codes (HID 4.3) */
+#define HID_PROTOCOL_NONE 0x00
+#define HID_PROTOCOL_BOOT 0x00
+#define HID_PROTOCOL_KEYBOARD 0x01
+#define HID_PROTOCOL_REPORT 0x01
+#define HID_PROTOCOL_MOUSE 0x02
+
+/* HID Class Descriptor Types (HID 7.1) */
+#define HID_DESCRIPTOR_TYPE_HID 0x21
+#define HID_DESCRIPTOR_TYPE_HID_REPORT 0x22
+#define HID_DESCRIPTOR_TYPE_HID_PHYSICAL 0x23
+
+/* HID Class Specific Requests (HID 7.2) */
+#define HID_REQUEST_GET_REPORT 0x01
+#define HID_REQUEST_GET_IDLE 0x02
+#define HID_REQUEST_GET_PROTOCOL 0x03
+#define HID_REQUEST_SET_REPORT 0x09
+#define HID_REQUEST_SET_IDLE 0x0A
+#define HID_REQUEST_SET_PROTOCOL 0x0B
+
+/* Report Type (MS byte of wValue for GET_REPORT) (HID 7.2.1) */
+#define HID_REPORT_INPUT 0x01
+#define HID_REPORT_OUTPUT 0x02
+#define HID_REPORT_FEATURE 0x03
+
+/* HID Descriptor ***********************************************************/
+
+#define HID_COUNTRY_NONE 0x00 /* Not Supported */
+#define HID_COUNTRY_ARABIC 0x01 /* Arabic */
+#define HID_COUNTRY_BELGIAN 0x02 /* Belgian */
+#define HID_COUNTRY_CANADA 0x03 /* Canadian-Bilingual */
+#define HID_COUNTRY_CANADRFR 0x04 /* Canadian-French */
+#define HID_COUNTRY_CZECH 0x05 /* Czech Republic */
+#define HID_COUNTRY_DANISH 0x06 /* Danish */
+#define HID_COUNTRY_FINNISH 0x07 /* Finnish */
+#define HID_COUNTRY_FRENCH 0x08 /* French */
+#define HID_COUNTRY_GERMAN 0x09 /* German */
+#define HID_COUNTRY_GREEK 0x10 /* Greek */
+#define HID_COUNTRY_HEBREW 0x11 /* Hebrew */
+#define HID_COUNTRY_HUNGARY 0x12 /* Hungary */
+#define HID_COUNTRY_ISO 0x13 /* International (ISO) */
+#define HID_COUNTRY_ITALIAN 0x14 /* Italian */
+#define HID_COUNTRY_JAPAN 0x15 /* Japan (Katakana) */
+#define HID_COUNTRY_KOREAN 0x16 /* Korean */
+#define HID_COUNTRY_LATINAM 0x17 /* Latin American */
+#define HID_COUNTRY_DUTCH 0x18 /* Netherlands/Dutch */
+#define HID_COUNTRY_NORWEGIAN 0x19 /* Norwegian */
+#define HID_COUNTRY_PERSIAN 0x20 /* Persian (Farsi) */
+#define HID_COUNTRY_POLAND 0x21 /* Poland */
+#define HID_COUNTRY_PORTUGUESE 0x22 /* Portuguese */
+#define HID_COUNTRY_RUSSIA 0x23 /* Russia */
+#define HID_COUNTRY_SLOVAKIA 0x24 /* Slovakia */
+#define HID_COUNTRY_SPANISH 0x25 /* Spanish */
+#define HID_COUNTRY_SWEDISH 0x26 /* Swedish */
+#define HID_COUNTRY_SWISSFR 0x27 /* Swiss/French */
+#define HID_COUNTRY_SWISSGR 0x28 /* Swiss/German */
+#define HID_COUNTRY_SWITZERLAND 0x29 /* Switzerland */
+#define HID_COUNTRY_TAIWAN 0x30 /* Taiwan */
+#define HID_COUNTRY_TURKISHQ 0x31 /* Turkish-Q */
+#define HID_COUNTRY_UK 0x32 /* UK */
+#define HID_COUNTRY_US 0x33 /* US */
+#define HID_COUNTRY_YUGOSLAVIA 0x34 /* Yugoslavia */
+#define HID_COUNTRY_TURKISHF 0x35 /* Turkish-F */
+
+/* HID report items */
+#define HID_REPORT_ITEM_SIZE_MASK 0x03
+#define HID_REPORT_ITEM_SIZE_0 0x00 /* No data follows */
+#define HID_REPORT_ITEM_SIZE_1 0x01 /* 1 byte of data follows */
+#define HID_REPORT_ITEM_SIZE_2 0x02 /* 2 bytes of data follow */
+#define HID_REPORT_ITEM_SIZE_4 0x03 /* 4 bytes of data follow */
+#define HID_REPORT_ITEM_TYPE_MASK 0x0c
+#define HID_REPORT_ITEM_TYPE_MAIN 0x00
+#define HID_REPORT_ITEM_TYPE_GLOBAL 0x04
+#define HID_REPORT_ITEM_TYPE_LOCAL 0x08
+#define HID_REPORT_ITEM_TAG_MASK 0xf0
+
+/* Main Items (HID 6.2.2.4) */
+#define HID_MAIN_ITEM_CONSTANT (1 << 0) /* Constant(1) vs Data(0) */
+#define HID_MAIN_ITEM_VARIABLE (1 << 1) /* Variable(1) vs Array(0) */
+#define HID_MAIN_ITEM_RELATIVE (1 << 2) /* Relative(1) vs Absolute(0) */
+#define HID_MAIN_ITEM_WRAP (1 << 3) /* Wrap(1) vs No Wrap(0) */
+#define HID_MAIN_ITEM_NONLINEAR (1 << 4) /* Non Linear(1) vs Linear(0) */
+#define HID_MAIN_ITEM_NOPREFERRED (1 << 5) /* No Preferred (1) vs Preferred State(0) */
+#define HID_MAIN_ITEM_NULLSTATE (1 << 6) /* Null state(1) vs No Null position(0) */
+#define HID_MAIN_ITEM_VOLATILE (1 << 7) /* Volatile(1) vs Non volatile(0) */
+#define HID_MAIN_ITEM_BUFFEREDBYTES (1 << 8) /* Buffered Bytes(1) vs Bit Field(0) */
+
+#define HID_MAIN_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
+#define HID_MAIN_ITEM_INPUT_PREFIX 0x80
+#define HID_MAIN_ITEM_INPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
+#define HID_MAIN_ITEM_INPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
+#define HID_MAIN_ITEM_INPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
+#define HID_MAIN_ITEM_INPUT_WRAP HID_MAIN_ITEM_WRAP
+#define HID_MAIN_ITEM_INPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
+#define HID_MAIN_ITEM_INPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
+#define HID_MAIN_ITEM_INPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
+#define HID_MAIN_ITEM_INPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
+
+#define HID_MAIN_ITEM_OUTPUT_PREFIX 0x90
+#define HID_MAIN_ITEM_OUTPUT_CONSTANT HID_MAIN_ITEM_CONSTANT
+#define HID_MAIN_ITEM_OUTPUT_VARIABLE HID_MAIN_ITEM_VARIABLE
+#define HID_MAIN_ITEM_OUTPUT_RELATIVE HID_MAIN_ITEM_RELATIVE
+#define HID_MAIN_ITEM_OUTPUT_WRAP HID_MAIN_ITEM_WRAP
+#define HID_MAIN_ITEM_OUTPUT_NONLINEAR HID_MAIN_ITEM_NONLINEAR
+#define HID_MAIN_ITEM_OUTPUT_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
+#define HID_MAIN_ITEM_OUTPUT_NULLSTATE HID_MAIN_ITEM_NULLSTATE
+#define HID_MAIN_ITEM_OUTPUT_VOLATILE HID_MAIN_ITEM_VOLATILE
+#define HID_MAIN_ITEM_OUTPUT_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
+
+#define HID_MAIN_ITEM_FEATURE_PREFIX 0xb0
+#define HID_MAIN_ITEM_FEATURE_CONSTANT HID_MAIN_ITEM_CONSTANT
+#define HID_MAIN_ITEM_FEATURE_VARIABLE HID_MAIN_ITEM_VARIABLE
+#define HID_MAIN_ITEM_FEATURE_RELATIVE HID_MAIN_ITEM_RELATIVE
+#define HID_MAIN_ITEM_FEATURE_WRAP HID_MAIN_ITEM_WRAP
+#define HID_MAIN_ITEM_FEATURE_NONLINEAR HID_MAIN_ITEM_NONLINEAR
+#define HID_MAIN_ITEM_FEATURE_NOPREFERRED HID_MAIN_ITEM_NOPREFERRED
+#define HID_MAIN_ITEM_FEATURE_NULLSTATE HID_MAIN_ITEM_NULLSTATE
+#define HID_MAIN_ITEM_FEATURE_VOLATILE HID_MAIN_ITEM_VOLATILE
+#define HID_MAIN_ITEM_FEATURE_BUFFEREDBYTES HID_MAIN_ITEM_BUFFEREDBYTES
+
+#define HID_MAIN_ITEM_COLLECTION_PREFIX 0xa0
+#define HID_MAIN_ITEM_COLLECTION_PHYSICAL 0x00 /* Physical (group of axes) */
+#define HID_MAIN_ITEM_COLLECTION_APPL 0x01 /* Application (mouse, keyboard) */
+#define HID_MAIN_ITEM_COLLECTION_LOGICAL 0x02 /* Logical (interrelated data) */
+#define HID_MAIN_ITEM_COLLECTION_REPORT 0x03 /* Report */
+#define HID_MAIN_ITEM_COLLECTION_ARRAY 0x04 /* Named Array */
+#define HID_MAIN_ITEM_COLLECTION_SWITCH 0x05 /* Usage Switch */
+#define HID_MAIN_ITEM_COLLECTION_MODIFIER 0x06 /* Usage Modifier */
+#define HID_MAIN_ITEM_ENDCOLLECTION_PREFIX 0xc0
+
+/* Global Items (HID 6.2.2.7) */
+#define HID_GLOBAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
+#define HID_GLOBAL_ITEM_USAGEPAGE_PREFIX 0x04 /* Usage Page */
+#define HID_GLOBAL_ITEM_LOGICALMIN_PREFIX 0x14 /* Logical Minimum */
+#define HID_GLOBAL_ITEM_LOGICALMAX_PREFIX 0x24 /* Logical Maximum */
+#define HID_GLOBAL_ITEM_PHYSICALMIN_PREFIX 0x34 /* Physical Minimum */
+#define HID_GLOBAL_ITEM_PHYSMICALAX_PREFIX 0x44 /* Physical Maximum */
+#define HID_GLOBAL_ITEM_UNITEXP_PREFIX 0x54 /* Unit Exponent */
+#define HID_GLOBAL_ITEM_UNIT_PREFIX 0x64 /* Unit */
+#define HID_GLOBAL_ITEM_REPORTSIZE_PREFIX 0x74 /* Report Size */
+#define HID_GLOBAL_ITEM_REPORTID_PREFIX 0x84 /* Report ID */
+#define HID_GLOBAL_ITEM_REPORTCOUNT_PREFIX 0x94 /* Report Count */
+#define HID_GLOBAL_ITEM_PUSH_PREFIX 0xa4 /* Push */
+#define HID_GLOBAL_ITEM_POP_PREFIX 0xb4 /* Pop */
+
+/* Local Items (HID 6.2.2.8) */
+#define HID_LOCAL_ITEM_SIZE(pfx) ((pfx)&HID_REPORT_ITEM_SIZE_MASK)
+#define HID_LOCAL_ITEM_USAGE_PREFIX 0x08 /* Usage */
+#define HID_LOCAL_ITEM_USAGEMIN_PREFIX 0x18 /* Usage Minimum */
+#define HID_LOCAL_ITEM_USAGEMAX_PREFIX 0x28 /* Usage Maximum */
+#define HID_LOCAL_ITEM_DESIGNATORIDX_PREFIX 0x38 /* Designator Index */
+#define HID_LOCAL_ITEM_DESIGNATORMIN_PREFIX 0x48 /* Designator Minimum */
+#define HID_LOCAL_ITEM_DESIGNATORMAX_PREFIX 0x58 /* Designator Maximum */
+#define HID_LOCAL_ITEM_STRINGIDX_PREFIX 0x78 /* String Index */
+#define HID_LOCAL_ITEM_STRINGMIN_PREFIX 0x88 /* String Minimum */
+#define HID_LOCAL_ITEM_STRINGMAX_PREFIX 0x98 /* xx */
+#define HID_LOCAL_ITEM_DELIMITER_PREFIX 0xa8 /* Delimiter */
+
+/* Modifier Keys (HID 8.3) */
+#define HID_MODIFER_LCTRL (1 << 0) /* Left Ctrl */
+#define HID_MODIFER_LSHIFT (1 << 1) /* Left Shift */
+#define HID_MODIFER_LALT (1 << 2) /* Left Alt */
+#define HID_MODIFER_LGUI (1 << 3) /* Left GUI */
+#define HID_MODIFER_RCTRL (1 << 4) /* Right Ctrl */
+#define HID_MODIFER_RSHIFT (1 << 5) /* Right Shift */
+#define HID_MODIFER_RALT (1 << 6) /* Right Alt */
+#define HID_MODIFER_RGUI (1 << 7) /* Right GUI */
+
+/* Keyboard output report (1 byte) (HID B.1) */
+#define HID_KBD_OUTPUT_REPORT_NUMLOCK (1 << 0)
+#define HID_KBD_OUTPUT_REPORT_CAPSLOCK (1 << 1)
+#define HID_KBD_OUTPUT_REPORT_SCROLLLOCK (1 << 2)
+#define HID_KBD_OUTPUT_REPORT_COMPOSE (1 << 3)
+#define HID_KBD_OUTPUT_REPORT_KANA (1 << 4)
+
+/* Mouse input report (HID B.2) */
+#define HID_MOUSE_INPUT_REPORT_BUTTON1 (1 << 0)
+#define HID_MOUSE_INPUT_REPORT_BUTTON2 (1 << 1)
+#define HID_MOUSE_INPUT_REPORT_BUTTON3 (1 << 2)
+#define HID_MOUSE_INPUT_REPORT_BUTTON_MASK (7)
+
+#define HID_MOUSE_INPUT_BUTTON_LEFT (1 << 0)
+#define HID_MOUSE_INPUT_BUTTON_RIGHT (1 << 1)
+#define HID_MOUSE_INPUT_BUTTON_MIDDLE (1 << 2)
+#define HID_MOUSE_INPUT_BUTTON_BACKWARD (1 << 3)
+#define HID_MOUSE_INPUT_BUTTON_FORWARD (1 << 4)
+
+/* Joystick input report (4 bytes) (HID D.1) */
+#define HID_JS_INPUT_REPORT_HATSWITCH_SHIFT (0)
+#define HID_JS_INPUT_REPORT_HATSWITCH_MASK (15 << HID_JSIN_HATSWITCH_SHIFT)
+#define HID_JS_INPUT_REPORT_BUTTON1 (1 << 4)
+#define HID_JS_INPUT_REPORT_BUTTON2 (1 << 5)
+#define HID_JS_INPUT_REPORT_BUTTON3 (1 << 6)
+#define HID_JS_INPUT_REPORT_BUTTON4 (1 << 7)
+
+/* Usage pages (HuT 3) */
+#define HID_USAGE_PAGE_UNDEFINED 0x00 /* Undefined */
+#define HID_USAGE_PAGE_GENERIC_DCTRL 0x01 /* Generic Desktop Controls */
+#define HID_USAGE_PAGE_SIMCTRL 0x02 /* Simulation Controls */
+#define HID_USAGE_PAGE_VRCTRL 0x03 /* VR Controls */
+#define HID_USAGE_PAGE_SPORTCTRL 0x04 /* Sport Controls */
+#define HID_USAGE_PAGE_GAMECTRL 0x05 /* Game Controls */
+#define HID_USAGE_PAGE_GENERIC_DEVCTRL 0x06 /* Generic Device Controls */
+#define HID_USAGE_PAGE_KBD 0x07 /* Keyboard/Keypad */
+#define HID_USAGE_PAGE_LEDS 0x08 /* LEDs */
+#define HID_USAGE_PAGE_BUTTON 0x09 /* Button */
+#define HID_USAGE_PAGE_ORDINAL 0x0a /* Ordinal */
+#define HID_USAGE_PAGE_TELEPHONY 0x0b /* Telephony */
+#define HID_USAGE_PAGE_CONSUMER 0x0c /* Consumer */
+#define HID_USAGE_PAGE_DIGITIZER 0x0d /* Digitizer */
+ /* 0x0e Reserved */
+#define HID_USAGE_PAGE_PIDPAGE 0x0f /* PID Page Physical Interface Device */
+#define HID_USAGE_PAGE_UNICODE 0x10 /* Unicode */
+ /* 0x11-13 Reserved */
+#define HID_USAGE_PAGE_ALPHA_DISPLAY 0x14 /* Alphanumeric Display */
+ /* 0x15-3f Reserved */
+#define HID_USAGE_PAGE_MEDICAL 0x40 /* Medical Instruments */
+ /* 0x41-7f Reserved */
+ /* 0x80-83 Monitor Devices */
+ /* 0x84-87 Power Devices */
+ /* 0x88-8b Reserved */
+#define HID_USAGE_PAGE_BARCODE_SCANNER 0x8c /* Bar Code Scanner page */
+#define HID_USAGE_PAGE_SCALE 0x8d /* Scale page */
+#define HID_USAGE_PAGE_MSR 0x8e /* Magnetic Stripe Reading (MSR) Devices */
+#define HID_USAGE_PAGE_POS 0x8f /* Point of Sale devices */
+#define HID_USAGE_PAGE_CAMERA_CTRL 0x90 /* Camera Control Page */
+
+/* Generic Desktop Page Usage IDs (HuT 4) */
+#define HID_DESKTOP_USAGE_UNDEFINED 0x00 /* Undefined */
+#define HID_DESKTOP_USAGE_POINTER 0x01 /* Pointer */
+#define HID_DESKTOP_USAGE_MOUSE 0x02 /* Mouse */
+ /* 0x03 Reserved */
+#define HID_DESKTOP_USAGE_JOYSTICK 0x04 /* Joystick */
+#define HID_DESKTOP_USAGE_GAMEPAD 0x05 /* Game Pad */
+#define HID_DESKTOP_USAGE_KEYBOARD 0x06 /* Keyboard */
+#define HID_DESKTOP_USAGE_KEYPAD 0x07 /* Keypad */
+#define HID_DESKTOP_USAGE_MULTIAXIS 0x08 /* Multi-axis Controller */
+#define HID_DESKTOP_USAGE_TABLET 0x09 /* Tablet PC System Controls */
+ /* 0x0a-2f Reserved */
+#define HID_DESKTOP_USAGE_X 0x30 /* X */
+#define HID_DESKTOP_USAGE_Y 0x31 /* Y */
+#define HID_DESKTOP_USAGE_Z 0x32 /* Z */
+#define HID_DESKTOP_USAGE_RX 0x33 /* Rx */
+#define HID_DESKTOP_USAGE_RY 0x34 /* Ry */
+#define HID_DESKTOP_USAGE_RZ 0x35 /* Rz */
+#define HID_DESKTOP_USAGE_SLIDER 0x36 /* Slider */
+#define HID_DESKTOP_USAGE_DIAL 0x37 /* Dial */
+#define HID_DESKTOP_USAGE_WHEEL 0x38 /* Wheel */
+#define HID_DESKTOP_USAGE_HATSWITCH 0x39 /* Hat switch */
+#define HID_DESKTOP_USAGE_COUNTED 0x3a /* Counted Buffer */
+#define HID_DESKTOP_USAGE_BYTECOUNT 0x3b /* Byte Count */
+#define HID_DESKTOP_USAGE_MOTION 0x3c /* Motion Wakeup */
+#define HID_DESKTOP_USAGE_START 0x3d /* Start */
+#define HID_DESKTOP_USAGE_SELECT 0x3e /* Select */
+ /* 0x3f Reserved */
+#define HID_DESKTOP_USAGE_VX 0x40 /* Vx */
+#define HID_DESKTOP_USAGE_VY 0x41 /* Vy */
+#define HID_DESKTOP_USAGE_VZ 0x42 /* Vz */
+#define HID_DESKTOP_USAGE_VBRX 0x43 /* Vbrx */
+#define HID_DESKTOP_USAGE_VBRY 0x44 /* Vbry */
+#define HID_DESKTOP_USAGE_VBRZ 0x45 /* Vbrz */
+#define HID_DESKTOP_USAGE_VNO 0x46 /* Vno */
+#define HID_DESKTOP_USAGE_FEATURE 0x47 /* Feature Notification */
+#define HID_DESKTOP_USAGE_RESOLUTION 0x48 /* Resolution Multiplier */
+ /* 0x49-7f Reserved */
+#define HID_DESKTOP_USAGE_CONTROL 0x80 /* System Control */
+#define HID_DESKTOP_USAGE_POWERDOWN 0x81 /* System Power Down */
+#define HID_DESKTOP_USAGE_SLEEP 0x82 /* System Sleep */
+#define HID_DESKTOP_USAGE_WAKEUP 0x83 /* System Wake Up */
+#define HID_DESKTOP_USAGE_CONTEXT_MENU 0x84 /* System Context Menu */
+#define HID_DESKTOP_USAGE_MAIN_MENU 0x85 /* System Main Menu */
+#define HID_DESKTOP_USAGE_APP_MENU 0x86 /* System App Menu */
+#define HID_DESKTOP_USAGE_MENU_HELP 0x87 /* System Menu Help */
+#define HID_DESKTOP_USAGE_MENU_EXIT 0x88 /* System Menu Exit */
+#define HID_DESKTOP_USAGE_MENU_SELECT 0x89 /* System Menu Select */
+#define HID_DESKTOP_USAGE_MENU_RIGHT 0x8a /* System Menu Right */
+#define HID_DESKTOP_USAGE_MENU_LEFT 0x8b /* System Menu Left */
+#define HID_DESKTOP_USAGE_MENU_UP 0x8c /* System Menu Up */
+#define HID_DESKTOP_USAGE_MENU_DOWN 0x8d /* System Menu Down */
+#define HID_DESKTOP_USAGE_COLD_RESTART 0x8e /* System Cold Restart */
+#define HID_DESKTOP_USAGE_WARM_RESTART 0x8f /* System Warm Restart */
+#define HID_DESKTOP_USAGE_DPAD_UP 0x90 /* D-pad Up */
+#define HID_DESKTOP_USAGE_DPAD_DOWN 0x91 /* D-pad Down */
+#define HID_DESKTOP_USAGE_DPAD_RIGHT 0x92 /* D-pad Right */
+#define HID_DESKTOP_USAGE_DPAD_LEFT 0x93 /* D-pad Left */
+ /* 0x94-9f Reserved */
+#define HID_DESKTOP_USAGE_DOCK 0xa0 /* System Dock */
+#define HID_DESKTOP_USAGE_UNDOCK 0xa1 /* System Undock */
+#define HID_DESKTOP_USAGE_SETUP 0xa2 /* System Setup */
+#define HID_DESKTOP_USAGE_BREAK 0xa3 /* System Break */
+#define HID_DESKTOP_USAGE_DEBUG_BREAK 0xa4 /* System Debugger Break */
+#define HID_DESKTOP_USAGE_APP_BREAK 0xa5 /* Application Break */
+#define HID_DESKTOP_USAGE_APP_DEBUG_BREAK 0xa6 /* Application Debugger Break */
+#define HID_DESKTOP_USAGE_MUTE 0xa7 /* System Speaker Mute */
+#define HID_DESKTOP_USAGE_HIBERNATE 0xa8 /* System Hibernate */
+ /* 0xa9-af Reserved */
+#define HID_DESKTOP_USAGE_DISPLAY_INVERT 0xb0 /* System Display Invert */
+#define HID_DESKTOP_USAGE_DISPALY_INTERNAL 0xb1 /* System Display Internal */
+#define HID_DESKTOP_USAGE_DISPLAY_EXTERNAL 0xb2 /* System Display External */
+#define HID_DESKTOP_USAGE_DISPLAY_BOTH 0xb3 /* System Display Both */
+#define HID_DESKTOP_USAGE_DISPLAY_DUAL 0xb4 /* System Display Dual */
+#define HID_DESKTOP_USAGE_DISPLAY_TOGGLE 0xb5 /* System Display Toggle Int/Ext */
+#define HID_DESKTOP_USAGE_DISPLAY_SWAP 0xb6 /* System Display Swap */
+#define HID_DESKTOP_USAGE_ 0xb7 /* System Display LCD Autoscale */
+ /* 0xb8-ffff Reserved */
+
+/* Keyboard usage IDs (HuT 10) */
+#define HID_KBD_USAGE_NONE 0x00 /* Reserved (no event indicated) */
+#define HID_KBD_USAGE_ERRORROLLOVER 0x01 /* Keyboard ErrorRollOver */
+#define HID_KBD_USAGE_POSTFAIL 0x02 /* Keyboard POSTFail */
+#define HID_KBD_USAGE_ERRUNDEF 0x03 /* Keyboard ErrorUndefined */
+#define HID_KBD_USAGE_A 0x04 /* Keyboard a or A (B-Z follow) */
+#define HID_KBD_USAGE_1 0x1e /* Keyboard 1 (2-9 follow) */
+#define HID_KBD_USAGE_EXCLAM 0x1e /* Keyboard 1 and ! */
+#define HID_KBD_USAGE_AT 0x1f /* Keyboard 2 and @ */
+#define HID_KBD_USAGE_POUND 0x20 /* Keyboard 3 and # */
+#define HID_KBD_USAGE_DOLLAR 0x21 /* Keyboard 4 and $ */
+#define HID_KBD_USAGE_PERCENT 0x22 /* Keyboard 5 and % */
+#define HID_KBD_USAGE_CARAT 0x23 /* Keyboard 6 and ^ */
+#define HID_KBD_USAGE_AMPERSAND 0x24 /* Keyboard 7 and & */
+#define HID_KBD_USAGE_ASTERISK 0x25 /* Keyboard 8 and * */
+#define HID_KBD_USAGE_LPAREN 0x26 /* Keyboard 9 and ( */
+#define HID_KBD_USAGE_0 0x27 /* Keyboard 0 and ) */
+#define HID_KBD_USAGE_RPAREN 0x27 /* Keyboard 0 and ) */
+#define HID_KBD_USAGE_ENTER 0x28 /* Keyboard Return (ENTER) */
+#define HID_KBD_USAGE_ESCAPE 0x29 /* Keyboard ESCAPE */
+#define HID_KBD_USAGE_DELETE 0x2a /* Keyboard DELETE (Backspace) */
+#define HID_KBD_USAGE_TAB 0x2b /* Keyboard Tab */
+#define HID_KBD_USAGE_SPACE 0x2c /* Keyboard Spacebar */
+#define HID_KBD_USAGE_HYPHEN 0x2d /* Keyboard - and (underscore) */
+#define HID_KBD_USAGE_UNDERSCORE 0x2d /* Keyboard - and (underscore) */
+#define HID_KBD_USAGE_EQUAL 0x2e /* Keyboard = and + */
+#define HID_KBD_USAGE_PLUS 0x2e /* Keyboard = and + */
+#define HID_KBD_USAGE_LBRACKET 0x2f /* Keyboard [ and { */
+#define HID_KBD_USAGE_LBRACE 0x2f /* Keyboard [ and { */
+#define HID_KBD_USAGE_RBRACKET 0x30 /* Keyboard ] and } */
+#define HID_KBD_USAGE_RBRACE 0x30 /* Keyboard ] and } */
+#define HID_KBD_USAGE_BSLASH 0x31 /* Keyboard \ and | */
+#define HID_KBD_USAGE_VERTBAR 0x31 /* Keyboard \ and | */
+#define HID_KBD_USAGE_NONUSPOUND 0x32 /* Keyboard Non-US # and ~ */
+#define HID_KBD_USAGE_TILDE 0x32 /* Keyboard Non-US # and ~ */
+#define HID_KBD_USAGE_SEMICOLON 0x33 /* Keyboard ; and : */
+#define HID_KBD_USAGE_COLON 0x33 /* Keyboard ; and : */
+#define HID_KBD_USAGE_SQUOTE 0x34 /* Keyboard ' and " */
+#define HID_KBD_USAGE_DQUOUTE 0x34 /* Keyboard ' and " */
+#define HID_KBD_USAGE_GACCENT 0x35 /* Keyboard Grave Accent and Tilde */
+#define HID_KBD_USAGE_GTILDE 0x35 /* Keyboard Grave Accent and Tilde */
+#define HID_KBD_USAGE_COMMON 0x36 /* Keyboard , and < */
+#define HID_KBD_USAGE_LT 0x36 /* Keyboard , and < */
+#define HID_KBD_USAGE_PERIOD 0x37 /* Keyboard . and > */
+#define HID_KBD_USAGE_GT 0x37 /* Keyboard . and > */
+#define HID_KBD_USAGE_DIV 0x38 /* Keyboard / and ? */
+#define HID_KBD_USAGE_QUESTION 0x38 /* Keyboard / and ? */
+#define HID_KBD_USAGE_CAPSLOCK 0x39 /* Keyboard Caps Lock */
+#define HID_KBD_USAGE_F1 0x3a /* Keyboard F1 */
+#define HID_KBD_USAGE_F2 0x3b /* Keyboard F2 */
+#define HID_KBD_USAGE_F3 0x3c /* Keyboard F3 */
+#define HID_KBD_USAGE_F4 0x3d /* Keyboard F4 */
+#define HID_KBD_USAGE_F5 0x3e /* Keyboard F5 */
+#define HID_KBD_USAGE_F6 0x3f /* Keyboard F6 */
+#define HID_KBD_USAGE_F7 0x40 /* Keyboard F7 */
+#define HID_KBD_USAGE_F8 0x41 /* Keyboard F8 */
+#define HID_KBD_USAGE_F9 0x42 /* Keyboard F9 */
+#define HID_KBD_USAGE_F10 0x43 /* Keyboard F10 */
+#define HID_KBD_USAGE_F11 0x44 /* Keyboard F11 */
+#define HID_KBD_USAGE_F12 0x45 /* Keyboard F12 */
+#define HID_KBD_USAGE_PRINTSCN 0x46 /* Keyboard PrintScreen */
+#define HID_KBD_USAGE_SCROLLLOCK 0x47 /* Keyboard Scroll Lock */
+#define HID_KBD_USAGE_PAUSE 0x48 /* Keyboard Pause */
+#define HID_KBD_USAGE_INSERT 0x49 /* Keyboard Insert */
+#define HID_KBD_USAGE_HOME 0x4a /* Keyboard Home */
+#define HID_KBD_USAGE_PAGEUP 0x4b /* Keyboard PageUp */
+#define HID_KBD_USAGE_DELFWD 0x4c /* Keyboard Delete Forward */
+#define HID_KBD_USAGE_END 0x4d /* Keyboard End */
+#define HID_KBD_USAGE_PAGEDOWN 0x4e /* Keyboard PageDown */
+#define HID_KBD_USAGE_RIGHT 0x4f /* eyboard RightArrow */
+#define HID_KBD_USAGE_LEFT 0x50 /* Keyboard LeftArrow */
+#define HID_KBD_USAGE_DOWN 0x51 /* Keyboard DownArrow */
+#define HID_KBD_USAGE_UP 0x52 /* Keyboard UpArrow */
+#define HID_KBD_USAGE_KPDNUMLOCK 0x53 /* Keypad Num Lock and Clear */
+#define HID_KBD_USAGE_KPDNUMLOCKCLEAR 0x53 /* Keypad Num Lock and Clear */
+#define HID_KBD_USAGE_KPDDIV 0x54 /* Keypad / */
+#define HID_KBD_USAGE_KPDMUL 0x55 /* Keypad * */
+#define HID_KBD_USAGE_KPDHMINUS 0x56 /* Keypad - */
+#define HID_KBD_USAGE_KPDPLUS 0x57 /* Keypad + */
+#define HID_KBD_USAGE_KPDEMTER 0x58 /* Keypad ENTER */
+#define HID_KBD_USAGE_KPD1 0x59 /* Keypad 1 (2-9 follow) */
+#define HID_KBD_USAGE_KPDEND 0x59 /* Keypad 1 and End */
+#define HID_KBD_USAGE_KPDDOWN 0x5a /* Keypad 2 and Down Arrow */
+#define HID_KBD_USAGE_KPDPAGEDN 0x5b /* Keypad 3 and PageDn */
+#define HID_KBD_USAGE_KPDLEFT 0x5c /* Keypad 4 and Left Arrow */
+#define HID_KBD_USAGE_KPDRIGHT 0x5e /* Keypad 6 and Right Arrow */
+#define HID_KBD_USAGE_KPDHOME 0x5f /* Keypad 7 and Home */
+#define HID_KBD_USAGE_KPDUP 0x60 /* Keypad 8 and Up Arrow */
+#define HID_KBD_USAGE_KPDPAGEUP 0x61 /* Keypad 9 and PageUp */
+#define HID_KBD_USAGE_KPD0 0x62 /* Keypad 0 and Insert */
+#define HID_KBD_USAGE_KPDINSERT 0x62 /* Keypad 0 and Insert */
+#define HID_KBD_USAGE_KPDDECIMALPT 0x63 /* Keypad . and Delete */
+#define HID_KBD_USAGE_KPDDELETE 0x63 /* Keypad . and Delete */
+#define HID_KBD_USAGE_NONSLASH 0x64 /* Keyboard Non-US \ and | */
+#define HID_KBD_USAGE_NONUSVERT 0x64 /* Keyboard Non-US \ and | */
+#define HID_KBD_USAGE_APPLICATION 0x65 /* Keyboard Application */
+#define HID_KBD_USAGE_POWER 0x66 /* Keyboard Power */
+#define HID_KBD_USAGE_KPDEQUAL 0x67 /* Keypad = */
+#define HID_KBD_USAGE_F13 0x68 /* Keyboard F13 */
+#define HID_KBD_USAGE_F14 0x69 /* Keyboard F14 */
+#define HID_KBD_USAGE_F15 0x6a /* Keyboard F15 */
+#define HID_KBD_USAGE_F16 0x6b /* Keyboard F16 */
+#define HID_KBD_USAGE_F17 0x6c /* Keyboard F17 */
+#define HID_KBD_USAGE_F18 0x6d /* Keyboard F18 */
+#define HID_KBD_USAGE_F19 0x6e /* Keyboard F19 */
+#define HID_KBD_USAGE_F20 0x6f /* Keyboard F20 */
+#define HID_KBD_USAGE_F21 0x70 /* Keyboard F21 */
+#define HID_KBD_USAGE_F22 0x71 /* Keyboard F22 */
+#define HID_KBD_USAGE_F23 0x72 /* Keyboard F23 */
+#define HID_KBD_USAGE_F24 0x73 /* Keyboard F24 */
+#define HID_KBD_USAGE_EXECUTE 0x74 /* Keyboard Execute */
+#define HID_KBD_USAGE_HELP 0x75 /* Keyboard Help */
+#define HID_KBD_USAGE_MENU 0x76 /* Keyboard Menu */
+#define HID_KBD_USAGE_SELECT 0x77 /* Keyboard Select */
+#define HID_KBD_USAGE_STOP 0x78 /* Keyboard Stop */
+#define HID_KBD_USAGE_AGAIN 0x79 /* Keyboard Again */
+#define HID_KBD_USAGE_UNDO 0x7a /* Keyboard Undo */
+#define HID_KBD_USAGE_CUT 0x7b /* Keyboard Cut */
+#define HID_KBD_USAGE_COPY 0x7c /* Keyboard Copy */
+#define HID_KBD_USAGE_PASTE 0x7d /* Keyboard Paste */
+#define HID_KBD_USAGE_FIND 0x7e /* Keyboard Find */
+#define HID_KBD_USAGE_MUTE 0x7f /* Keyboard Mute */
+#define HID_KBD_USAGE_VOLUP 0x80 /* Keyboard Volume Up */
+#define HID_KBD_USAGE_VOLDOWN 0x81 /* Keyboard Volume Down */
+#define HID_KBD_USAGE_LCAPSLOCK 0x82 /* Keyboard Locking Caps Lock */
+#define HID_KBD_USAGE_LNUMLOCK 0x83 /* Keyboard Locking Num Lock */
+#define HID_KBD_USAGE_LSCROLLLOCK 0x84 /* Keyboard Locking Scroll Lock */
+#define HID_KBD_USAGE_KPDCOMMA 0x85 /* Keypad Comma */
+#define HID_KBD_USAGE_KPDEQUALSIGN 0x86 /* Keypad Equal Sign */
+#define HID_KBD_USAGE_INTERNATIONAL1 0x87 /* Keyboard International 1 */
+#define HID_KBD_USAGE_INTERNATIONAL2 0x88 /* Keyboard International 2 */
+#define HID_KBD_USAGE_INTERNATIONAL3 0x89 /* Keyboard International 3 */
+#define HID_KBD_USAGE_INTERNATIONAL4 0x8a /* Keyboard International 4 */
+#define HID_KBD_USAGE_INTERNATIONAL5 0x8b /* Keyboard International 5 */
+#define HID_KBD_USAGE_INTERNATIONAL6 0x8c /* Keyboard International 6 */
+#define HID_KBD_USAGE_INTERNATIONAL7 0x8d /* Keyboard International 7 */
+#define HID_KBD_USAGE_INTERNATIONAL8 0x8e /* Keyboard International 8 */
+#define HID_KBD_USAGE_INTERNATIONAL9 0x8f /* Keyboard International 9 */
+#define HID_KBD_USAGE_LANG1 0x90 /* Keyboard LANG1 */
+#define HID_KBD_USAGE_LANG2 0x91 /* Keyboard LANG2 */
+#define HID_KBD_USAGE_LANG3 0x92 /* Keyboard LANG3 */
+#define HID_KBD_USAGE_LANG4 0x93 /* Keyboard LANG4 */
+#define HID_KBD_USAGE_LANG5 0x94 /* Keyboard LANG5 */
+#define HID_KBD_USAGE_LANG6 0x95 /* Keyboard LANG6 */
+#define HID_KBD_USAGE_LANG7 0x96 /* Keyboard LANG7 */
+#define HID_KBD_USAGE_LANG8 0x97 /* Keyboard LANG8 */
+#define HID_KBD_USAGE_LANG9 0x98 /* Keyboard LANG9 */
+#define HID_KBD_USAGE_ALTERASE 0x99 /* Keyboard Alternate Erase */
+#define HID_KBD_USAGE_SYSREQ 0x9a /* Keyboard SysReq/Attention */
+#define HID_KBD_USAGE_CANCEL 0x9b /* Keyboard Cancel */
+#define HID_KBD_USAGE_CLEAR 0x9c /* Keyboard Clear */
+#define HID_KBD_USAGE_PRIOR 0x9d /* Keyboard Prior */
+#define HID_KBD_USAGE_RETURN 0x9e /* Keyboard Return */
+#define HID_KBD_USAGE_SEPARATOR 0x9f /* Keyboard Separator */
+#define HID_KBD_USAGE_OUT 0xa0 /* Keyboard Out */
+#define HID_KBD_USAGE_OPER 0xa1 /* Keyboard Oper */
+#define HID_KBD_USAGE_CLEARAGAIN 0xa2 /* Keyboard Clear/Again */
+#define HID_KBD_USAGE_CLRSEL 0xa3 /* Keyboard CrSel/Props */
+#define HID_KBD_USAGE_EXSEL 0xa4 /* Keyboard ExSel */
+#define HID_KBD_USAGE_KPD00 0xb0 /* Keypad 00 */
+#define HID_KBD_USAGE_KPD000 0xb1 /* Keypad 000 */
+#define HID_KBD_USAGE_THOUSEPARATOR 0xb2 /* Thousands Separator */
+#define HID_KBD_USAGE_DECSEPARATOR 0xb3 /* Decimal Separator */
+#define HID_KBD_USAGE_CURRUNIT 0xb4 /* Currency Unit */
+#define HID_KBD_USAGE_CURRSUBUNIT 0xb5 /* Currency Sub-unit */
+#define HID_KBD_USAGE_KPDLPAREN 0xb6 /* Keypad ( */
+#define HID_KBD_USAGE_KPDRPAREN 0xb7 /* Keypad ) */
+#define HID_KBD_USAGE_KPDLBRACE 0xb8 /* Keypad { */
+#define HID_KBD_USAGE_KPDRBRACE 0xb9 /* Keypad } */
+#define HID_KBD_USAGE_KPDTAB 0xba /* Keypad Tab */
+#define HID_KBD_USAGE_KPDBACKSPACE 0xbb /* Keypad Backspace */
+#define HID_KBD_USAGE_KPDA 0xbc /* Keypad A (B-F follow) */
+#define HID_KBD_USAGE_KPDXOR 0xc2 /* Keypad XOR */
+#define HID_KBD_USAGE_KPDEXP 0xc3 /* Keypad ^ */
+#define HID_KBD_USAGE_KPDPERCENT 0xc4 /* Keypad % */
+#define HID_KBD_USAGE_KPDLT 0xc5 /* Keypad < */
+#define HID_KBD_USAGE_KPDGT 0xc6 /* Keypad > */
+#define HID_KBD_USAGE_KPDAMPERSAND 0xc7 /* Keypad & */
+#define HID_KBD_USAGE_KPDAND 0xc8 /* Keypad && */
+#define HID_KBD_USAGE_KPDVERT 0xc9 /* Keypad | */
+#define HID_KBD_USAGE_KPDOR 0xca /* Keypad || */
+#define HID_KBD_USAGE_KPDCOLON 0xcb /* Keypad : */
+#define HID_KBD_USAGE_KPDPOUND 0xcc /* Keypad # */
+#define HID_KBD_USAGE_KPDSPACE 0xcd /* Keypad Space */
+#define HID_KBD_USAGE_KPDAT 0xce /* Keypad @ */
+#define HID_KBD_USAGE_KPDEXCLAM 0xcf /* Keypad ! */
+#define HID_KBD_USAGE_KPDMEMSTORE 0xd0 /* Keypad Memory Store */
+#define HID_KBD_USAGE_KPDMEMRECALL 0xd1 /* Keypad Memory Recall */
+#define HID_KBD_USAGE_KPDMEMCLEAR 0xd2 /* Keypad Memory Clear */
+#define HID_KBD_USAGE_KPDMEMADD 0xd3 /* Keypad Memory Add */
+#define HID_KBD_USAGE_KPDMEMSUB 0xd4 /* Keypad Memory Subtract */
+#define HID_KBD_USAGE_KPDMEMMULT 0xd5 /* Keypad Memory Multiply */
+#define HID_KBD_USAGE_KPDMEMDIV 0xd6 /* Keypad Memory Divide */
+#define HID_KBD_USAGE_KPDPLUSMINUS 0xd7 /* Keypad +/- */
+#define HID_KBD_USAGE_KPDCLEAR 0xd8 /* Keypad Clear */
+#define HID_KBD_USAGE_KPDCLEARENTRY 0xd9 /* Keypad Clear Entry */
+#define HID_KBD_USAGE_KPDBINARY 0xda /* Keypad Binary */
+#define HID_KBD_USAGE_KPDOCTAL 0xdb /* Keypad Octal */
+#define HID_KBD_USAGE_KPDDECIMAL 0xdc /* Keypad Decimal */
+#define HID_KBD_USAGE_KPDHEXADECIMAL 0xdd /* Keypad Hexadecimal */
+#define HID_KBD_USAGE_LCTRL 0xe0 /* Keyboard LeftControl */
+#define HID_KBD_USAGE_LSHIFT 0xe1 /* Keyboard LeftShift */
+#define HID_KBD_USAGE_LALT 0xe2 /* Keyboard LeftAlt */
+#define HID_KBD_USAGE_LGUI 0xe3 /* Keyboard Left GUI */
+#define HID_KBD_USAGE_RCTRL 0xe4 /* Keyboard RightControl */
+#define HID_KBD_USAGE_RSHIFT 0xe5 /* Keyboard RightShift */
+#define HID_KBD_USAGE_RALT 0xe6 /* Keyboard RightAlt */
+#define HID_KBD_USAGE_RGUI 0xe7 /* Keyboard Right GUI */
+
+#define HID_KBD_USAGE_MAX 0xe7
+
+/* HID Report Definitions */
+struct usb_hid_class_subdescriptor {
+ uint8_t bDescriptorType;/* Class descriptor type (See 7.1) */
+ uint16_t wDescriptorLength;/* Size of the report descriptor */
+} __PACKED;
+
+struct usb_hid_descriptor {
+ uint8_t bLength; /* Size of the HID descriptor */
+ uint8_t bDescriptorType;/* HID descriptor type */
+ uint16_t bcdHID;/* HID class specification release */
+ uint8_t bCountryCode;/* Country code */
+ uint8_t bNumDescriptors;/* Number of descriptors (>=1) */
+
+ /*
+ * Specification says at least one Class Descriptor needs to
+ * be present (Report Descriptor).
+ */
+ struct usb_hid_class_subdescriptor subdesc[1];
+} __PACKED;
+
+/* Standard Reports *********************************************************/
+
+/* Keyboard input report (8 bytes) (HID B.1) */
+struct usb_hid_kbd_report
+{
+ uint8_t modifier; /* Modifier keys. See HID_MODIFER_* definitions */
+ uint8_t reserved;
+ uint8_t key[6]; /* Keycode 1-6 */
+};
+
+/* Keyboard output report (1 byte) (HID B.1),
+ * see USBHID_KBDOUT_* definitions
+ */
+
+/* Mouse input report (HID B.2) */
+struct usb_hid_mouse_report
+{
+ uint8_t buttons; /* See HID_MOUSE_INPUT_BUTTON_* definitions */
+ uint8_t xdisp; /* X displacement */
+ uint8_t ydisp; /* y displacement */
+ /* Device specific additional bytes may follow */
+#ifdef CONFIG_INPUT_MOUSE_WHEEL
+ uint8_t wdisp; /* Wheel displacement */
+#endif
+};
+
+/* Joystick input report (1 bytes) (HID D.1) */
+struct usb_hid_js_report
+{
+ uint8_t xpos; /* X position */
+ uint8_t ypos; /* X position */
+ uint8_t buttons; /* See USBHID_JSIN_* definitions */
+ uint8_t throttle; /* Throttle */
+};
+
+#endif /* USB_HID_H */
diff --git a/components/drivers/usb/cherryusb/class/hid/usbd_hid.c b/components/drivers/usb/cherryusb/class/hid/usbd_hid.c
new file mode 100644
index 0000000000..d98b82eb84
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hid/usbd_hid.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_hid.h"
+
+static int hid_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("HID Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ uint8_t intf_num = LO_BYTE(setup->wIndex);
+
+ switch (setup->bRequest) {
+ case HID_REQUEST_GET_REPORT:
+ /* report id ,report type */
+ usbd_hid_get_report(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), data, len);
+ break;
+ case HID_REQUEST_GET_IDLE:
+ (*data)[0] = usbd_hid_get_idle(busid, intf_num, LO_BYTE(setup->wValue));
+ *len = 1;
+ break;
+ case HID_REQUEST_GET_PROTOCOL:
+ (*data)[0] = usbd_hid_get_protocol(busid, intf_num);
+ *len = 1;
+ break;
+ case HID_REQUEST_SET_REPORT:
+ /* report id ,report type, report, report len */
+ usbd_hid_set_report(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue), *data, *len);
+ break;
+ case HID_REQUEST_SET_IDLE:
+ /* report id, duration */
+ usbd_hid_set_idle(busid, intf_num, LO_BYTE(setup->wValue), HI_BYTE(setup->wValue));
+ break;
+ case HID_REQUEST_SET_PROTOCOL:
+ /* protocol */
+ usbd_hid_set_protocol(busid, intf_num, LO_BYTE(setup->wValue));
+ break;
+
+ default:
+ USB_LOG_WRN("Unhandled HID Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len)
+{
+ intf->class_interface_handler = hid_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = NULL;
+
+ intf->hid_report_descriptor = desc;
+ intf->hid_report_descriptor_len = desc_len;
+ return intf;
+}
+
+__WEAK void usbd_hid_get_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t **data, uint32_t *len)
+{
+ (*data[0]) = 0;
+ *len = 1;
+}
+
+__WEAK uint8_t usbd_hid_get_idle(uint8_t busid, uint8_t intf, uint8_t report_id)
+{
+ return 0;
+}
+
+__WEAK uint8_t usbd_hid_get_protocol(uint8_t busid, uint8_t intf)
+{
+ return 0;
+}
+
+__WEAK void usbd_hid_set_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t *report, uint32_t report_len)
+{
+}
+
+__WEAK void usbd_hid_set_idle(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t duration)
+{
+}
+
+__WEAK void usbd_hid_set_protocol(uint8_t busid, uint8_t intf, uint8_t protocol)
+{
+} \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/hid/usbd_hid.h b/components/drivers/usb/cherryusb/class/hid/usbd_hid.h
new file mode 100644
index 0000000000..d5c3484f2f
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hid/usbd_hid.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_HID_H
+#define USBD_HID_H
+
+#include "usb_hid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init hid interface driver */
+struct usbd_interface *usbd_hid_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t *desc, uint32_t desc_len);
+
+/* Register desc api */
+void usbd_hid_descriptor_register(uint8_t busid, uint8_t intf_num, const uint8_t *desc);
+void usbd_hid_report_descriptor_register(uint8_t busid, uint8_t intf_num, const uint8_t *desc, uint32_t desc_len);
+
+/* Setup request command callback api */
+void usbd_hid_get_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t **data, uint32_t *len);
+uint8_t usbd_hid_get_idle(uint8_t busid, uint8_t intf, uint8_t report_id);
+uint8_t usbd_hid_get_protocol(uint8_t busid, uint8_t intf);
+void usbd_hid_set_report(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t report_type, uint8_t *report, uint32_t report_len);
+void usbd_hid_set_idle(uint8_t busid, uint8_t intf, uint8_t report_id, uint8_t duration);
+void usbd_hid_set_protocol(uint8_t busid, uint8_t intf, uint8_t protocol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_HID_H */
diff --git a/components/drivers/usb/cherryusb/class/hid/usbh_hid.c b/components/drivers/usb/cherryusb/class/hid/usbh_hid.c
new file mode 100644
index 0000000000..f3b2546f22
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hid/usbh_hid.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_hid.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_hid"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/input%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hid_buf[128];
+
+static struct usbh_hid g_hid_class[CONFIG_USBHOST_MAX_HID_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_hid *usbh_hid_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_HID_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_hid_class[devno], 0, sizeof(struct usbh_hid));
+ g_hid_class[devno].minor = devno;
+ return &g_hid_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_hid_class_free(struct usbh_hid *hid_class)
+{
+ int devno = hid_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(hid_class, 0, sizeof(struct usbh_hid));
+}
+
+static int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_GET_DESCRIPTOR;
+ setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8;
+ setup->wIndex = hid_class->intf;
+ setup->wLength = 128;
+
+ ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(buffer, g_hid_buf, ret - 8);
+ return ret;
+}
+
+int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = HID_REQUEST_SET_IDLE;
+ setup->wValue = (duration << 8) | report_id;
+ setup->wIndex = hid_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(hid_class->hport, setup, NULL);
+}
+
+int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = HID_REQUEST_GET_IDLE;
+ setup->wValue = 0;
+ setup->wIndex = hid_class->intf;
+ setup->wLength = 1;
+
+ ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(buffer, g_hid_buf, 1);
+ return ret;
+}
+
+int usbh_hid_set_protocol(struct usbh_hid *hid_class, uint8_t protocol)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = HID_REQUEST_SET_PROTOCOL;
+ setup->wValue = protocol;
+ setup->wIndex = 0;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(hid_class->hport, setup, NULL);
+}
+
+int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = HID_REQUEST_SET_REPORT;
+ setup->wValue = (uint16_t)(((uint32_t)report_type << 8U) | (uint32_t)report_id);
+ setup->wIndex = 0;
+ setup->wLength = buflen;
+
+ return usbh_control_transfer(hid_class->hport, setup, buffer);
+}
+
+int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen)
+{
+ struct usb_setup_packet *setup = hid_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = HID_REQUEST_GET_REPORT;
+ setup->wValue = (uint16_t)(((uint32_t)report_type << 8U) | (uint32_t)report_id);
+ setup->wIndex = 0;
+ setup->wLength = buflen;
+
+ return usbh_control_transfer(hid_class->hport, setup, buffer);
+}
+
+int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_hid *hid_class = usbh_hid_class_alloc();
+ if (hid_class == NULL) {
+ USB_LOG_ERR("Fail to alloc hid_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ hid_class->hport = hport;
+ hid_class->intf = intf;
+
+ hport->config.intf[intf].priv = hid_class;
+
+ // /* 0x0 = boot protocol, 0x1 = report protocol */
+ // ret = usbh_hid_set_protocol(hid_class, 0x1);
+ // if (ret < 0) {
+ // return ret;
+ // }
+
+ ret = usbh_hid_set_idle(hid_class, 0, 0);
+ if (ret < 0) {
+ USB_LOG_WRN("Do not support set idle\r\n");
+ }
+
+ ret = usbh_hid_get_report_descriptor(hid_class, hid_class->report_desc);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(hid_class->intin, ep_desc);
+ } else {
+ USBH_EP_INIT(hid_class->intout, ep_desc);
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, hid_class->minor);
+
+ USB_LOG_INFO("Register HID Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_hid_run(hid_class);
+ return ret;
+}
+
+int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv;
+
+ if (hid_class) {
+ if (hid_class->intin) {
+ usbh_kill_urb(&hid_class->intin_urb);
+ }
+
+ if (hid_class->intout) {
+ usbh_kill_urb(&hid_class->intout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister HID Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_hid_stop(hid_class);
+ }
+
+ usbh_hid_class_free(hid_class);
+ }
+
+ return ret;
+}
+
+__WEAK void usbh_hid_run(struct usbh_hid *hid_class)
+{
+}
+
+__WEAK void usbh_hid_stop(struct usbh_hid *hid_class)
+{
+}
+
+const struct usbh_class_driver hid_class_driver = {
+ .driver_name = "hid",
+ .connect = usbh_hid_connect,
+ .disconnect = usbh_hid_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info hid_custom_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS,
+ .class = USB_DEVICE_CLASS_HID,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = NULL,
+ .class_driver = &hid_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/hid/usbh_hid.h b/components/drivers/usb/cherryusb/class/hid/usbh_hid.h
new file mode 100644
index 0000000000..17caaba277
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hid/usbh_hid.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_HID_H
+#define USBH_HID_H
+
+#include "usb_hid.h"
+
+struct usbh_hid {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
+ struct usb_endpoint_descriptor *intout; /* INTR OUT endpoint */
+ struct usbh_urb intin_urb; /* INTR IN urb */
+ struct usbh_urb intout_urb; /* INTR OUT urb */
+
+ uint8_t report_desc[256];
+ uint8_t intf; /* interface number */
+ uint8_t minor;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration);
+int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer);
+int usbh_hid_set_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
+int usbh_hid_get_report(struct usbh_hid *hid_class, uint8_t report_type, uint8_t report_id, uint8_t *buffer, uint32_t buflen);
+
+void usbh_hid_run(struct usbh_hid *hid_class);
+void usbh_hid_stop(struct usbh_hid *hid_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_HID_H */
diff --git a/components/drivers/usb/cherryusb/class/hub/usb_hub.h b/components/drivers/usb/cherryusb/class/hub/usb_hub.h
new file mode 100644
index 0000000000..7956b163e7
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hub/usb_hub.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_HUB_H
+#define USB_HUB_H
+
+/* HUB Class Descriptor Types */
+#define HUB_DESCRIPTOR_TYPE_HUB 0x29
+#define HUB_DESCRIPTOR_TYPE_HUB3 0x2A
+
+/* Hub class requests */
+#define HUB_REQUEST_GET_STATUS USB_REQUEST_GET_STATUS
+#define HUB_REQUEST_CLEAR_FEATURE USB_REQUEST_CLEAR_FEATURE
+#define HUB_REQUEST_SET_FEATURE USB_REQUEST_SET_FEATURE
+#define HUB_REQUEST_GET_DESCRIPTOR USB_REQUEST_GET_DESCRIPTOR
+#define HUB_REQUEST_SET_DESCRIPTOR USB_REQUEST_SET_DESCRIPTOR
+#define HUB_REQUEST_CLEAR_TT_BUFFER (0x08)
+#define HUB_REQUEST_RESET_TT (0x09)
+#define HUB_REQUEST_GET_TT_STATE (0x0a)
+#define HUB_REQUEST_STOP_TT (0x0b)
+#define HUB_REQUEST_SET_HUB_DEPTH (0x0C)
+
+/* Hub class features */
+#define HUB_FEATURE_HUB_C_LOCALPOWER (0x0)
+#define HUB_FEATURE_HUB_C_OVERCURRENT (0x1)
+
+/* Port features */
+#define HUB_PORT_FEATURE_CONNECTION (0x00)
+#define HUB_PORT_FEATURE_ENABLE (0x01)
+#define HUB_PORT_FEATURE_SUSPEND (0x02)
+#define HUB_PORT_FEATURE_OVERCURRENT (0x03)
+#define HUB_PORT_FEATURE_RESET (0x04)
+#define HUB_PORT_FEATURE_L1 (0x05)
+#define HUB_PORT_FEATURE_POWER (0x08)
+#define HUB_PORT_FEATURE_LOWSPEED (0x09)
+#define HUB_PORT_FEATURE_HIGHSPEED (0x0a)
+#define HUB_PORT_FEATURE_C_CONNECTION (0x10)
+#define HUB_PORT_FEATURE_C_ENABLE (0x11)
+#define HUB_PORT_FEATURE_C_SUSPEND (0x12)
+#define HUB_PORT_FEATURE_C_OVER_CURREN (0x13)
+#define HUB_PORT_FEATURE_C_RESET (0x14)
+#define HUB_PORT_FEATURE_TEST (0x15)
+#define HUB_PORT_FEATURE_INDICATOR (0x16)
+#define HUB_PORT_FEATURE_C_PORTL1 (0x17)
+
+/* Hub status */
+#define HUB_STATUS_LOCALPOWER (1 << 0)
+#define HUB_STATUS_OVERCURRENT (1 << 1)
+
+/* Hub status change */
+#define HUB_STATUS_C_LOCALPOWER (1 << 0)
+#define HUB_STATUS_C_OVERCURRENT (1 << 1)
+
+/* Hub port status */
+#define HUB_PORT_STATUS_CONNECTION (1 << 0)
+#define HUB_PORT_STATUS_ENABLE (1 << 1)
+#define HUB_PORT_STATUS_SUSPEND (1 << 2)
+#define HUB_PORT_STATUS_OVERCURRENT (1 << 3)
+#define HUB_PORT_STATUS_RESET (1 << 4)
+#define HUB_PORT_STATUS_L1 (1 << 5)
+#define HUB_PORT_STATUS_POWER (1 << 8)
+#define HUB_PORT_STATUS_LOW_SPEED (1 << 9)
+#define HUB_PORT_STATUS_HIGH_SPEED (1 << 10)
+#define HUB_PORT_STATUS_TEST (1 << 11)
+#define HUB_PORT_STATUS_INDICATOR (1 << 12)
+
+/* Hub port status change */
+#define HUB_PORT_STATUS_C_CONNECTION (1 << 0)
+#define HUB_PORT_STATUS_C_ENABLE (1 << 1)
+#define HUB_PORT_STATUS_C_SUSPEND (1 << 2)
+#define HUB_PORT_STATUS_C_OVERCURRENT (1 << 3)
+#define HUB_PORT_STATUS_C_RESET (1 << 4)
+#define HUB_PORT_STATUS_C_L1 (1 << 5)
+
+/* Hub characteristics */
+#define HUB_CHAR_LPSM_SHIFT (0) /* Bits 0-1: Logical Power Switching Mode */
+#define HUB_CHAR_LPSM_MASK (3 << HUB_CHAR_LPSM_SHIFT)
+#define HUB_CHAR_LPSM_GANGED (0 << HUB_CHAR_LPSM_SHIFT)
+#define HUB_CHAR_LPSM_INDIVIDUAL (1 << HUB_CHAR_LPSM_SHIFT)
+#define HUB_CHAR_COMPOUND (1 << 2) /* Bit 2: Compound device */
+#define HUB_CHAR_OCPM_SHIFT (3) /* Bits 3-4: Over-current Protection Mode */
+#define HUB_CHAR_OCPM_MASK (3 << HUB_CHAR_OCPM_SHIFT)
+#define HUB_CHAR_OCPM_GLOBAL (0 << HUB_CHAR_OCPM_SHIFT)
+#define HUB_CHAR_OCPM_INDIVIDUAL (1 << HUB_CHAR_OCPM_SHIFT)
+#define HUB_CHAR_TTTT_SHIFT (5) /* Bits 5-6: TT Think Time */
+#define HUB_CHAR_TTTT_MASK (3 << HUB_CHAR_TTTT_SHIFT)
+#define HUB_CHAR_TTTT_8_BITS (0 << HUB_CHAR_TTTT_SHIFT)
+#define HUB_CHAR_TTTT_16_BITS (1 << HUB_CHAR_TTTT_SHIFT)
+#define HUB_CHAR_TTTT_24_BITS (2 << HUB_CHAR_TTTT_SHIFT)
+#define HUB_CHAR_TTTT_32_BITS (3 << HUB_CHAR_TTTT_SHIFT)
+#define HUB_CHAR_PORTIND (1 << 7) /* Bit 7: Port Indicators Supported */
+
+/* Hub descriptor */
+struct usb_hub_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bNbrPorts;
+ uint16_t wHubCharacteristics;
+ uint8_t bPwrOn2PwrGood;
+ uint8_t bHubContrCurrent;
+ uint8_t DeviceRemovable;
+ uint8_t PortPwrCtrlMask;
+} __PACKED;
+
+#define USB_SIZEOF_HUB_DESC 9
+
+/* Hub status */
+struct hub_status {
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+};
+
+/* Hub port status */
+struct hub_port_status {
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+};
+
+#endif /* USB_HUB_H */
diff --git a/components/drivers/usb/cherryusb/class/hub/usbh_hub.c b/components/drivers/usb/cherryusb/class/hub/usbh_hub.c
new file mode 100644
index 0000000000..6755b8594c
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hub/usbh_hub.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_hub.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_hub"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/hub%d"
+
+#define HUB_DEBOUNCE_TIMEOUT 1500
+#define HUB_DEBOUNCE_STEP 25
+#define HUB_DEBOUNCE_STABLE 100
+#define DELAY_TIME_AFTER_RESET 200
+
+#define EXTHUB_FIRST_INDEX 2
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hub_buf[CONFIG_USBHOST_MAX_BUS][USB_ALIGN_UP(32, CONFIG_USB_ALIGN_SIZE)];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hub_intbuf[CONFIG_USBHOST_MAX_BUS][CONFIG_USBHOST_MAX_EXTHUBS + 1][USB_ALIGN_UP(1, CONFIG_USB_ALIGN_SIZE)];
+
+extern int usbh_enumerate(struct usbh_hubport *hport);
+extern void usbh_hubport_release(struct usbh_hubport *hport);
+
+static const char *speed_table[] = { "error-speed", "low-speed", "full-speed", "high-speed", "wireless-speed", "super-speed", "superplus-speed" };
+
+#ifdef CONFIG_USBHOST_XHCI
+struct usbh_hubport *usbh_get_roothub_port(unsigned int port)
+{
+ return &roothub.child[port - 1];
+}
+#endif
+
+#if CONFIG_USBHOST_MAX_EXTHUBS > 0
+static struct usbh_hub g_hub_class[CONFIG_USBHOST_MAX_EXTHUBS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_hub *usbh_hub_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_EXTHUBS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_hub_class[devno], 0, sizeof(struct usbh_hub));
+ g_hub_class[devno].index = EXTHUB_FIRST_INDEX + devno;
+ return &g_hub_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_hub_class_free(struct usbh_hub *hub_class)
+{
+ int devno = hub_class->index - EXTHUB_FIRST_INDEX;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(hub_class, 0, sizeof(struct usbh_hub));
+}
+#endif
+
+#if CONFIG_USBHOST_MAX_EXTHUBS > 0
+static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = USB_REQUEST_GET_DESCRIPTOR;
+
+ /* TODO: hub descriptor has some difference between USB 2.0 and USB 3.x,
+ and we havn't handle the difference here */
+ if ((hub->parent->speed == USB_SPEED_SUPER) ||
+ (hub->parent->speed == USB_SPEED_SUPER_PLUS)) {
+ setup->wValue = HUB_DESCRIPTOR_TYPE_HUB3 << 8;
+ } else {
+ setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8;
+ }
+
+ setup->wIndex = 0;
+ setup->wLength = USB_SIZEOF_HUB_DESC;
+
+ ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_DESC);
+ return ret;
+}
+#if 0
+static int _usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = HUB_REQUEST_GET_STATUS;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 2;
+
+ ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(buffer, g_hub_buf[hub->bus->busid], 2);
+ return ret;
+}
+#endif
+#endif
+
+static int _usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status)
+{
+ struct usb_setup_packet *setup;
+ int ret;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_GET_STATUS;
+ setup->wValue = 0;
+ setup->wIndex = port;
+ setup->wLength = 4;
+
+ ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(port_status, g_hub_buf[hub->bus->busid], 4);
+ return ret;
+}
+
+static int _usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
+{
+ struct usb_setup_packet *setup;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_SET_FEATURE;
+ setup->wValue = feature;
+ setup->wIndex = port;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(hub->parent, setup, NULL);
+}
+
+static int _usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
+{
+ struct usb_setup_packet *setup;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_CLEAR_FEATURE;
+ setup->wValue = feature;
+ setup->wIndex = port;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(hub->parent, setup, NULL);
+}
+
+static int _usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth)
+{
+ struct usb_setup_packet *setup;
+
+ setup = hub->parent->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH;
+ setup->wValue = depth;
+ setup->wIndex = 0;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(hub->parent, setup, NULL);
+}
+
+#if CONFIG_USBHOST_MAX_EXTHUBS > 0
+static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length)
+{
+ if (desc->bLength != USB_SIZEOF_HUB_DESC) {
+ USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength);
+ return -1;
+ } else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB) {
+ USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType);
+ return -2;
+ } else {
+ USB_LOG_RAW("Hub Descriptor:\r\n");
+ USB_LOG_RAW("bLength: 0x%02x \r\n", desc->bLength);
+ USB_LOG_RAW("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType);
+ USB_LOG_RAW("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts);
+ USB_LOG_RAW("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics);
+ USB_LOG_RAW("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood);
+ USB_LOG_RAW("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent);
+ USB_LOG_RAW("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable);
+ USB_LOG_RAW("PortPwrCtrlMask: 0x%02x \r\n", desc->PortPwrCtrlMask);
+ }
+ return 0;
+}
+#endif
+
+static int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status)
+{
+ struct usb_setup_packet roothub_setup;
+ struct usb_setup_packet *setup;
+
+ if (hub->is_roothub) {
+ setup = &roothub_setup;
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_GET_STATUS;
+ setup->wValue = 0;
+ setup->wIndex = port;
+ setup->wLength = 4;
+ return usbh_roothub_control(hub->bus, &roothub_setup, (uint8_t *)port_status);
+ } else {
+ return _usbh_hub_get_portstatus(hub, port, port_status);
+ }
+}
+
+int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
+{
+ struct usb_setup_packet roothub_setup;
+ struct usb_setup_packet *setup;
+
+ if (hub->is_roothub) {
+ setup = &roothub_setup;
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_SET_FEATURE;
+ setup->wValue = feature;
+ setup->wIndex = port;
+ setup->wLength = 0;
+ return usbh_roothub_control(hub->bus, setup, NULL);
+ } else {
+ return _usbh_hub_set_feature(hub, port, feature);
+ }
+}
+
+int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature)
+{
+ struct usb_setup_packet roothub_setup;
+ struct usb_setup_packet *setup;
+
+ if (hub->is_roothub) {
+ setup = &roothub_setup;
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER;
+ setup->bRequest = HUB_REQUEST_CLEAR_FEATURE;
+ setup->wValue = feature;
+ setup->wIndex = port;
+ setup->wLength = 0;
+ return usbh_roothub_control(hub->bus, setup, NULL);
+ } else {
+ return _usbh_hub_clear_feature(hub, port, feature);
+ }
+}
+
+static int usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth)
+{
+ struct usb_setup_packet roothub_setup;
+ struct usb_setup_packet *setup;
+
+ if (hub->is_roothub) {
+ setup = &roothub_setup;
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH;
+ setup->wValue = depth;
+ setup->wIndex = 0;
+ setup->wLength = 0;
+ return usbh_roothub_control(hub->bus, setup, NULL);
+ } else {
+ return _usbh_hub_set_depth(hub, depth);
+ }
+}
+
+#if CONFIG_USBHOST_MAX_EXTHUBS > 0
+static void hub_int_complete_callback(void *arg, int nbytes)
+{
+ struct usbh_hub *hub = (struct usbh_hub *)arg;
+
+ if (nbytes > 0) {
+ usbh_hub_thread_wakeup(hub);
+ } else if (nbytes == -USB_ERR_NAK) {
+ /* Restart timer to submit urb again */
+ USB_LOG_DBG("Restart timer\r\n");
+ usb_osal_timer_start(hub->int_timer);
+ } else {
+ }
+}
+
+static void hub_int_timeout(void *arg)
+{
+ struct usbh_hub *hub = (struct usbh_hub *)arg;
+
+ usbh_int_urb_fill(&hub->intin_urb, hub->parent, hub->intin, hub->int_buffer, 1, 0, hub_int_complete_callback, hub);
+ usbh_submit_urb(&hub->intin_urb);
+}
+
+static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct hub_port_status port_status;
+ int ret;
+
+ struct usbh_hub *hub = usbh_hub_class_alloc();
+ if (hub == NULL) {
+ USB_LOG_ERR("Fail to alloc hub_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ hub->hub_addr = hport->dev_addr;
+ hub->parent = hport;
+ hub->bus = hport->bus;
+
+ hport->config.intf[intf].priv = hub;
+
+ ret = _usbh_hub_get_hub_descriptor(hub, (uint8_t *)&hub->hub_desc);
+ if (ret < 0) {
+ return ret;
+ }
+
+ parse_hub_descriptor(&hub->hub_desc, USB_SIZEOF_HUB_DESC);
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ hub->child[port].port = port + 1;
+ hub->child[port].parent = hub;
+ hub->child[port].bus = hport->bus;
+ }
+
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(hub->intin, ep_desc);
+ } else {
+ return -1;
+ }
+
+ if (hport->speed == USB_SPEED_SUPER) {
+ uint16_t depth = 0;
+ struct usbh_hubport *parent = hport->parent->parent;
+ while (parent) {
+ depth++;
+ parent = parent->parent->parent;
+ }
+
+ ret = usbh_hub_set_depth(hub, depth);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_POWER);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
+ USB_LOG_INFO("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, port_status.wPortStatus, port_status.wPortChange);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ hub->connected = true;
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, hub->index);
+
+ usb_slist_add_tail(&hub->bus->hub_list, &hub->list);
+
+ USB_LOG_INFO("Register HUB Class:%s\r\n", hport->config.intf[intf].devname);
+
+ hub->int_buffer = g_hub_intbuf[hub->bus->busid][hub->index - 1];
+
+ hub->int_timer = usb_osal_timer_create("hubint_tim", USBH_GET_URB_INTERVAL(hub->intin->bInterval, hport->speed), hub_int_timeout, hub, 0);
+ if (hub->int_timer == NULL) {
+ USB_LOG_ERR("No memory to alloc int_timer\r\n");
+ return -USB_ERR_NOMEM;
+ }
+ usb_osal_timer_start(hub->int_timer);
+ return 0;
+}
+
+static int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usbh_hubport *child;
+ int ret = 0;
+
+ struct usbh_hub *hub = (struct usbh_hub *)hport->config.intf[intf].priv;
+
+ if (hub) {
+ if (hub->intin) {
+ usbh_kill_urb(&hub->intin_urb);
+ }
+
+ if (hub->int_timer) {
+ usb_osal_timer_delete(hub->int_timer);
+ }
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ child = &hub->child[port];
+ usbh_hubport_release(child);
+ child->parent = NULL;
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister HUB Class:%s\r\n", hport->config.intf[intf].devname);
+ usb_slist_remove(&hub->bus->hub_list, &hub->list);
+ }
+
+ usbh_hub_class_free(hub);
+ }
+ return ret;
+}
+#endif
+
+static void usbh_hubport_enumerate_thread(void *argument)
+{
+ struct usbh_hubport *child = (struct usbh_hubport *)argument;
+
+ if (usbh_enumerate(child) < 0) {
+ /** release child sources */
+ usbh_hubport_release(child);
+ USB_LOG_ERR("Port %u enumerate fail\r\n", child->port);
+ }
+ usb_osal_thread_delete(NULL);
+}
+
+static void usbh_hub_events(struct usbh_hub *hub)
+{
+ struct usbh_hubport *child;
+ struct hub_port_status port_status;
+ uint8_t portchange_index;
+ uint16_t portstatus;
+ uint16_t portchange;
+ uint16_t mask;
+ uint16_t feat;
+ uint8_t speed;
+ int ret;
+
+ if (!hub->connected) {
+ return;
+ }
+
+ portchange_index = hub->int_buffer[0];
+ hub->int_buffer[0] &= ~portchange_index;
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ USB_LOG_DBG("Port change:0x%02x\r\n", portchange_index);
+
+ if (!(portchange_index & (1 << (port + 1)))) {
+ continue;
+ }
+ portchange_index &= ~(1 << (port + 1));
+ USB_LOG_DBG("Port %d change\r\n", port + 1);
+
+ /* Read hub port status */
+ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
+ continue;
+ }
+
+ portstatus = port_status.wPortStatus;
+ portchange = port_status.wPortChange;
+
+ USB_LOG_DBG("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, portstatus, portchange);
+
+ /* First, clear all change bits */
+ mask = 1;
+ feat = HUB_PORT_FEATURE_C_CONNECTION;
+ while (portchange) {
+ if (portchange & mask) {
+ ret = usbh_hub_clear_feature(hub, port + 1, feat);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to clear port %u, change mask:%04x, errorcode:%d\r\n", port + 1, mask, ret);
+ continue;
+ }
+ portchange &= (~mask);
+ }
+ mask <<= 1;
+ feat++;
+ }
+
+ portchange = port_status.wPortChange;
+
+ /* Second, if port changes, debounces first */
+ if (portchange & HUB_PORT_STATUS_C_CONNECTION) {
+ uint16_t connection = 0;
+ uint16_t debouncestable = 0;
+ for (uint32_t debouncetime = 0; debouncetime < HUB_DEBOUNCE_TIMEOUT; debouncetime += HUB_DEBOUNCE_STEP) {
+ /* Read hub port status */
+ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
+ continue;
+ }
+
+ portstatus = port_status.wPortStatus;
+ portchange = port_status.wPortChange;
+
+ USB_LOG_DBG("Port %u, status:0x%02x, change:0x%02x\r\n", port + 1, portstatus, portchange);
+
+ if (!(portchange & HUB_PORT_STATUS_C_CONNECTION) &&
+ ((portstatus & HUB_PORT_STATUS_CONNECTION) == connection)) {
+ debouncestable += HUB_DEBOUNCE_STEP;
+ if (debouncestable >= HUB_DEBOUNCE_STABLE) {
+ break;
+ }
+ } else {
+ debouncestable = 0;
+ connection = portstatus & HUB_PORT_STATUS_CONNECTION;
+ }
+
+ if (portchange & HUB_PORT_STATUS_C_CONNECTION) {
+ usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_CONNECTION);
+ }
+
+ usb_osal_msleep(HUB_DEBOUNCE_STEP);
+ }
+
+ /** check if debounce ok */
+ if (debouncestable < HUB_DEBOUNCE_STABLE) {
+ USB_LOG_ERR("Failed to debounce port %u\r\n", port + 1);
+ break;
+ }
+
+ /* Last, check connect status */
+ if (portstatus & HUB_PORT_STATUS_CONNECTION) {
+ ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to reset port %u,errorcode:%d\r\n", port, ret);
+ continue;
+ }
+
+ usb_osal_msleep(DELAY_TIME_AFTER_RESET);
+ /* Read hub port status */
+ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
+ continue;
+ }
+
+ portstatus = port_status.wPortStatus;
+ portchange = port_status.wPortChange;
+ if (!(portstatus & HUB_PORT_STATUS_RESET) && (portstatus & HUB_PORT_STATUS_ENABLE)) {
+ if (portchange & HUB_PORT_STATUS_C_RESET) {
+ ret = usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_RESET);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to clear port %u reset change, errorcode: %d\r\n", port, ret);
+ }
+ }
+
+ if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) {
+ speed = USB_SPEED_HIGH;
+ } else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) {
+ speed = USB_SPEED_LOW;
+ }
+#ifdef CONFIG_USBHOST_XHCI
+ else {
+ extern uint8_t usbh_get_port_speed(struct usbh_hub * hub, const uint8_t port);
+
+ /* USB3.0 speed cannot get from portstatus, checkout port speed instead */
+ uint8_t super_speed = usbh_get_port_speed(hub, port + 1);
+ if (super_speed > USB_SPEED_HIGH) {
+ /* assert that when using USB 3.0 ports, attached device must also be USB 3.0 speed */
+ speed = super_speed;
+ } else {
+ speed = USB_SPEED_FULL;
+ }
+ }
+#else
+ else {
+ speed = USB_SPEED_FULL;
+ }
+#endif
+
+ child = &hub->child[port];
+ /** release child sources first */
+ usbh_hubport_release(child);
+
+ memset(child, 0, sizeof(struct usbh_hubport));
+ child->parent = hub;
+ child->connected = true;
+ child->port = port + 1;
+ child->speed = speed;
+ child->bus = hub->bus;
+ child->mutex = usb_osal_mutex_create();
+
+ USB_LOG_INFO("New %s device on Bus %u, Hub %u, Port %u connected\r\n", speed_table[speed], hub->bus->busid, hub->index, port + 1);
+
+ /* create disposable thread to enumerate device on current hport, do not block hub thread */
+ usb_osal_thread_create("usbh_enum", CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO + 1, usbh_hubport_enumerate_thread, (void *)child);
+ } else {
+ child = &hub->child[port];
+ /** release child sources */
+ usbh_hubport_release(child);
+
+ /** some USB 3.0 ip may failed to enable USB 2.0 port for USB 3.0 device */
+ USB_LOG_WRN("Failed to enable port %u\r\n", port + 1);
+
+ continue;
+ }
+ } else {
+ child = &hub->child[port];
+ /** release child sources */
+ usbh_hubport_release(child);
+ USB_LOG_INFO("Device on Bus %u, Hub %u, Port %u disconnected\r\n", hub->bus->busid, hub->index, port + 1);
+ }
+ }
+ }
+
+ /* Start next hub int transfer */
+ if (!hub->is_roothub && hub->connected) {
+ usb_osal_timer_start(hub->int_timer);
+ }
+}
+
+static void usbh_hub_thread(void *argument)
+{
+ struct usbh_hub *hub;
+ int ret = 0;
+
+ struct usbh_bus *bus = (struct usbh_bus *)argument;
+
+ usb_hc_init(bus);
+ while (1) {
+ ret = usb_osal_mq_recv(bus->hub_mq, (uintptr_t *)&hub, USB_OSAL_WAITING_FOREVER);
+ if (ret < 0) {
+ continue;
+ }
+ usbh_hub_events(hub);
+ }
+}
+
+void usbh_hub_thread_wakeup(struct usbh_hub *hub)
+{
+ usb_osal_mq_send(hub->bus->hub_mq, (uintptr_t)hub);
+}
+
+int usbh_hub_initialize(struct usbh_bus *bus)
+{
+ char thread_name[32] = { 0 };
+
+ bus->hub_mq = usb_osal_mq_create(7);
+ if (bus->hub_mq == NULL) {
+ USB_LOG_ERR("Failed to create hub mq\r\n");
+ return -1;
+ }
+
+ snprintf(thread_name, 32, "usbh_hub%u", bus->busid);
+ bus->hub_thread = usb_osal_thread_create(thread_name, CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, bus);
+ if (bus->hub_thread == NULL) {
+ USB_LOG_ERR("Failed to create hub thread\r\n");
+ return -1;
+ }
+ return 0;
+}
+
+int usbh_hub_deinitialize(struct usbh_bus *bus)
+{
+ usb_slist_t *hub_list;
+ struct usbh_hubport *hport;
+ size_t flags;
+
+ flags = usb_osal_enter_critical_section();
+
+ usb_slist_for_each(hub_list, &bus->hub_list)
+ {
+ struct usbh_hub *hub = usb_slist_entry(hub_list, struct usbh_hub, list);
+
+ for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) {
+ hport = &hub->child[port];
+
+ usbh_hubport_release(hport);
+ }
+ }
+
+ usb_hc_deinit(bus);
+
+ usb_osal_leave_critical_section(flags);
+
+ usb_osal_mq_delete(bus->hub_mq);
+ usb_osal_thread_delete(bus->hub_thread);
+
+ return 0;
+}
+
+#if CONFIG_USBHOST_MAX_EXTHUBS > 0
+const struct usbh_class_driver hub_class_driver = {
+ .driver_name = "hub",
+ .connect = usbh_hub_connect,
+ .disconnect = usbh_hub_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info hub_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS,
+ .class = USB_DEVICE_CLASS_HUB,
+ .subclass = 0,
+ .protocol = 0,
+ .id_table = NULL,
+ .class_driver = &hub_class_driver
+};
+#endif
diff --git a/components/drivers/usb/cherryusb/class/hub/usbh_hub.h b/components/drivers/usb/cherryusb/class/hub/usbh_hub.h
new file mode 100644
index 0000000000..1f34c53ba7
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/hub/usbh_hub.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_HUB_H
+#define USBH_HUB_H
+
+#include "usb_hub.h"
+
+struct usbh_hub;
+
+#define USBH_HUB_MAX_PORTS 4
+/* Maximum size of an interrupt IN transfer */
+#define USBH_HUB_INTIN_BUFSIZE ((USBH_HUB_MAX_PORTS + 8) >> 3)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature);
+int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature);
+
+void usbh_hub_thread_wakeup(struct usbh_hub *hub);
+
+int usbh_hub_initialize(struct usbh_bus *bus);
+int usbh_hub_deinitialize(struct usbh_bus *bus);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_HUB_H */
diff --git a/components/drivers/usb/cherryusb/class/midi/usb_midi.h b/components/drivers/usb/cherryusb/class/midi/usb_midi.h
new file mode 100644
index 0000000000..3e7a846f4e
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/midi/usb_midi.h
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_MIDI_H
+#define USB_MIDI_H
+
+/* bDescriptorSubType */
+#define MIDI_VC_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define MIDI_MS_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define MIDI_MS_GENERAL_DESCRIPTOR_SUBTYPE 0x01U
+#define MIDI_MIDI_IN_JACK_DESCRIPTOR_SUBTYPE 0x02U
+#define MIDI_MIDI_OUT_JACK_DESCRIPTOR_SUBTYPE 0x03U
+
+/* bJackType */
+#define MIDI_JACK_TYPE_EMBEDDED 0x01
+#define MIDI_JACK_TYPE_EXTERNAL 0x02
+
+#define MIDI_CHANNEL_OMNI 0
+#define MIDI_CHANNEL_OFF 17
+
+#define MIDI_PITCHBEND_MIN -8192
+#define MIDI_PITCHBEND_MAX 8191
+
+/*! Enumeration of MIDI code index number */
+enum MidiCodeIndexNumber {
+ MIDI_CIN_MISC = 0,
+ MIDI_CIN_CABLE_EVENT = 1,
+ MIDI_CIN_SYSCOM_2BYTE = 2, ///< 2 byte system common message e.g MTC, SongSelect
+ MIDI_CIN_SYSCOM_3BYTE = 3, ///< 3 byte system common message e.g SPP
+ MIDI_CIN_SYSEX_START = 4, ///< SysEx starts or continue
+ 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_OFF = 8,
+ MIDI_CIN_NOTE_ON = 9,
+ MIDI_CIN_POLY_KEYPRESS = 10,
+ MIDI_CIN_CONTROL_CHANGE = 11,
+ MIDI_CIN_PROGRAM_CHANGE = 12,
+ MIDI_CIN_CHANNEL_PRESSURE = 13,
+ MIDI_CIN_PITCH_BEND_CHANGE = 14,
+ MIDI_CIN_1BYTE_DATA = 15
+};
+
+/*! Enumeration of MIDI types */
+enum MidiType {
+ InvalidType = 0x00, ///< For notifying errors
+ NoteOff = 0x80, ///< Note Off
+ NoteOn = 0x90, ///< Note On
+ AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
+ ControlChange = 0xB0, ///< Control Change / Channel Mode
+ ProgramChange = 0xC0, ///< Program Change
+ AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch
+ PitchBend = 0xE0, ///< Pitch Bend
+ SystemExclusive = 0xF0, ///< System Exclusive
+ TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
+ SongPosition = 0xF2, ///< System Common - Song Position Pointer
+ SongSelect = 0xF3, ///< System Common - Song Select
+ TuneRequest = 0xF6, ///< System Common - Tune Request
+ Clock = 0xF8, ///< System Real Time - Timing Clock
+ Start = 0xFA, ///< System Real Time - Start
+ Continue = 0xFB, ///< System Real Time - Continue
+ Stop = 0xFC, ///< System Real Time - Stop
+ ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
+ SystemReset = 0xFF, ///< System Real Time - System Reset
+};
+
+/*! Enumeration of Thru filter modes */
+enum MidiFilterMode {
+ Off = 0, ///< Thru disabled (nothing passes through).
+ Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
+ SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
+ DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
+};
+
+/*! \brief Enumeration of Control Change command numbers.
+ See the detailed controllers numbers & description here:
+ http://www.somascape.org/midi/tech/spec.html#ctrlnums
+ */
+enum MidiControlChangeNumber {
+ // High resolution Continuous Controllers MSB (+32 for LSB) ----------------
+ BankSelect = 0,
+ ModulationWheel = 1,
+ BreathController = 2,
+ // CC3 undefined
+ FootController = 4,
+ PortamentoTime = 5,
+ DataEntry = 6,
+ ChannelVolume = 7,
+ Balance = 8,
+ // CC9 undefined
+ Pan = 10,
+ ExpressionController = 11,
+ EffectControl1 = 12,
+ EffectControl2 = 13,
+ // CC14 undefined
+ // CC15 undefined
+ GeneralPurposeController1 = 16,
+ GeneralPurposeController2 = 17,
+ GeneralPurposeController3 = 18,
+ GeneralPurposeController4 = 19,
+
+ // Switches ----------------------------------------------------------------
+ Sustain = 64,
+ Portamento = 65,
+ Sostenuto = 66,
+ SoftPedal = 67,
+ Legato = 68,
+ Hold = 69,
+
+ // Low resolution continuous controllers -----------------------------------
+ SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
+ SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
+ SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
+ SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
+ SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
+ SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
+ SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
+ SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
+ SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
+ SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
+ GeneralPurposeController5 = 80,
+ GeneralPurposeController6 = 81,
+ GeneralPurposeController7 = 82,
+ GeneralPurposeController8 = 83,
+ PortamentoControl = 84,
+ // CC85 to CC90 undefined
+ Effects1 = 91, ///< Reverb send level
+ Effects2 = 92, ///< Tremolo depth
+ Effects3 = 93, ///< Chorus send level
+ Effects4 = 94, ///< Celeste depth
+ Effects5 = 95, ///< Phaser depth
+
+ // Channel Mode messages ---------------------------------------------------
+ AllSoundOff = 120,
+ ResetAllControllers = 121,
+ LocalControl = 122,
+ AllNotesOff = 123,
+ OmniModeOff = 124,
+ OmniModeOn = 125,
+ MonoModeOn = 126,
+ PolyModeOn = 127
+};
+
+struct midi_cs_if_ac_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t bcdADC;
+ uint16_t wTotalLength;
+ uint8_t bInCollection;
+ uint8_t baInterfaceNr[];
+} __PACKED;
+
+#define MIDI_SIZEOF_AC_HEADER_DESC(n) (8 + n)
+
+struct midi_cs_if_ms_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t bcdMSC;
+ uint16_t wTotalLength;
+} __PACKED;
+
+#define MIDI_SIZEOF_MS_HEADER_DESC (7)
+
+struct midi_cs_if_in_jack_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bJackType;
+ uint8_t bJackId;
+ uint8_t iJack;
+} __PACKED;
+
+#define MIDI_SIZEOF_IN_JACK_DESC (6)
+
+struct midi_cs_if_out_jack_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bJackType;
+ uint8_t bJackId;
+ uint8_t bNrInputPins;
+ uint8_t baSourceId;
+ uint8_t baSourcePin;
+ uint8_t iJack;
+} __PACKED;
+
+#define MIDI_SIZEOF_OUT_JACK_DESC (9)
+
+struct midi_cs_ep_ms_general_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bNumEmbMIDIJack;
+ uint8_t baAssocJackID[];
+} __PACKED;
+
+#define MIDI_SIZEOF_MS_GENERAL_DESC(n) (4 + n)
+
+// clang-format off
+#define MIDI_CS_HEADER_DESCRIPTOR_INIT(wTotalLength) \
+ 0x07, /* bLength */ \
+ USB_CS_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ MIDI_MS_HEADER_DESCRIPTOR_SUBTYPE, /* bDescriptorSubtype */ \
+ WBVAL(0x0100), /* bcdMSC */ \
+ WBVAL(wTotalLength) /* wTotalLength */
+
+#define MIDI_IN_JACK_DESCRIPTOR_INIT(bJackType, bJackID) \
+ 0x06, \
+ 0x24, \
+ MIDI_MIDI_IN_JACK_DESCRIPTOR_SUBTYPE, \
+ bJackType, \
+ bJackID, \
+ 0x00
+
+#define MIDI_OUT_JACK_DESCRIPTOR_INIT(bJackType, bJackID, baSourceID) \
+ 0x09, \
+ 0x24, \
+ MIDI_MIDI_OUT_JACK_DESCRIPTOR_SUBTYPE, \
+ bJackType, \
+ bJackID, \
+ 0x01, \
+ baSourceID, \
+ 0x01, \
+ 0x00
+
+#define MIDI_JACK_DESCRIPTOR_INIT(bJackFirstID) \
+ MIDI_IN_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EMBEDDED, bJackFirstID), \
+ MIDI_IN_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EXTERNAL, (bJackFirstID + 1)), \
+ MIDI_OUT_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EMBEDDED, (bJackFirstID + 2), (bJackFirstID + 1)), \
+ MIDI_OUT_JACK_DESCRIPTOR_INIT(MIDI_JACK_TYPE_EXTERNAL, (bJackFirstID + 3), (bJackFirstID))
+
+#define MIDI_SIZEOF_JACK_DESC (6 + 6 + 9 + 9)
+
+// clang-format on
+
+#endif /* USB_MIDI_H */
diff --git a/components/drivers/usb/cherryusb/class/msc/usb_msc.h b/components/drivers/usb/cherryusb/class/msc/usb_msc.h
new file mode 100644
index 0000000000..75187f5ee6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usb_msc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_MSC_H
+#define USB_MSC_H
+
+/* MSC Subclass Codes */
+#define MSC_SUBCLASS_RBC 0x01 /* Reduced block commands (e.g., flash devices) */
+#define MSC_SUBCLASS_SFF8020I_MMC2 0x02 /* SFF-8020i/MMC-2 (ATAPI) (e.g., C/DVD) */
+#define MSC_SUBCLASS_QIC157 0x03 /* QIC-157 (e.g., tape device) */
+#define MSC_SUBCLASS_UFI 0x04 /* e.g. floppy device */
+#define MSC_SUBCLASS_SFF8070I 0x05 /* SFF-8070i (e.g. floppy disk) */
+#define MSC_SUBCLASS_SCSI 0x06 /* SCSI transparent */
+
+/* MSC Protocol Codes */
+#define MSC_PROTOCOL_CBI_INT 0x00 /* CBI transport with command completion interrupt */
+#define MSC_PROTOCOL_CBI_NOINT 0x01 /* CBI transport without command completion interrupt */
+#define MSC_PROTOCOL_BULK_ONLY 0x50 /* Bulk only transport */
+
+/* MSC Request Codes */
+#define MSC_REQUEST_RESET 0xFF
+#define MSC_REQUEST_GET_MAX_LUN 0xFE
+
+/** MSC Command Block Wrapper (CBW) Signature */
+#define MSC_CBW_Signature 0x43425355
+/** Bulk-only Command Status Wrapper (CSW) Signature */
+#define MSC_CSW_Signature 0x53425355
+
+/** MSC Command Block Status Values */
+#define CSW_STATUS_CMD_PASSED 0x00
+#define CSW_STATUS_CMD_FAILED 0x01
+#define CSW_STATUS_PHASE_ERROR 0x02
+
+#define MSC_MAX_CDB_LEN (16) /* Max length of SCSI Command Data Block */
+
+/** MSC Bulk-Only Command Block Wrapper (CBW) */
+struct CBW {
+ uint32_t dSignature; /* 'USBC' = 0x43425355 */
+ uint32_t dTag; /* Depends on command id */
+ uint32_t dDataLength; /* Number of bytes that host expects to transfer */
+ uint8_t bmFlags; /* Bit 7: Direction=IN (other obsolete or reserved) */
+ uint8_t bLUN; /* LUN (normally 0) */
+ uint8_t bCBLength; /* len of cdb[] */
+ uint8_t CB[MSC_MAX_CDB_LEN]; /* Command Data Block */
+} __PACKED;
+
+#define USB_SIZEOF_MSC_CBW 31
+
+/** MSC Bulk-Only Command Status Wrapper (CSW) */
+struct CSW {
+ uint32_t dSignature; /* 'USBS' = 0x53425355 */
+ uint32_t dTag; /* Same tag as original command */
+ uint32_t dDataResidue; /* Amount not transferred */
+ uint8_t bStatus; /* Status of transfer */
+} __PACKED;
+
+#define USB_SIZEOF_MSC_CSW 13
+
+/*Length of template descriptor: 23 bytes*/
+#define MSC_DESCRIPTOR_LEN (9 + 7 + 7)
+// clang-format off
+#define MSC_DESCRIPTOR_INIT(bFirstInterface, out_ep, in_ep, wMaxPacketSize, str_idx) \
+ /* Interface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_MASS_STORAGE, /* bInterfaceClass */ \
+ MSC_SUBCLASS_SCSI, /* bInterfaceSubClass */ \
+ MSC_PROTOCOL_BULK_ONLY, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00 /* bInterval */
+// clang-format on
+
+#endif /* USB_MSC_H */
diff --git a/components/drivers/usb/cherryusb/class/msc/usb_scsi.h b/components/drivers/usb/cherryusb/class/msc/usb_scsi.h
new file mode 100644
index 0000000000..7eeb50a72c
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usb_scsi.h
@@ -0,0 +1,972 @@
+/*
+ * Apache NuttX
+ * Copyright 2020 The Apache Software Foundation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __INCLUDE_NUTTX_SCSI_H
+#define __INCLUDE_NUTTX_SCSI_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* SCSI commands ************************************************************/
+
+#define SCSI_CMD_TESTUNITREADY 0x00
+#define SCSI_CMD_REZEROUNIT 0x01
+#define SCSI_CMD_REQUESTSENSE 0x03
+#define SCSI_CMD_FORMAT_UNIT 0x04
+#define SCSI_CMD_REASSIGNBLOCKS 0x07
+#define SCSI_CMD_READ6 0x08
+#define SCSI_CMD_WRITE6 0x0a
+#define SCSI_CMD_SEEK6 0x0b
+#define SCSI_CMD_SPACE6 0x11
+#define SCSI_CMD_INQUIRY 0x12
+#define SCSI_CMD_MODESELECT6 0x15
+#define SCSI_CMD_RESERVE6 0x16
+#define SCSI_CMD_RELEASE6 0x17
+#define SCSI_CMD_COPY 0x18
+#define SCSI_CMD_MODESENSE6 0x1a
+#define SCSI_CMD_STARTSTOPUNIT 0x1b
+#define SCSI_CMD_RECEIVEDIAGNOSTICRESULTS 0x1c
+#define SCSI_CMD_SENDDIAGNOSTIC 0x1d
+#define SCSI_CMD_PREVENTMEDIAREMOVAL 0x1e
+#define SCSI_CMD_READFORMATCAPACITIES 0x23
+#define SCSI_CMD_READCAPACITY10 0x25
+#define SCSI_CMD_READ10 0x28
+#define SCSI_CMD_WRITE10 0x2a
+#define SCSI_CMD_SEEK10 0x2b
+#define SCSI_CMD_WRITEANDVERIFY 0x2e
+#define SCSI_CMD_VERIFY10 0x2f
+#define SCSI_CMD_SEARCHDATAHIGH 0x30
+#define SCSI_CMD_SEARCHDATAEQUAL 0x31
+#define SCSI_CMD_SEARCHDATALOW 0x32
+#define SCSI_CMD_SETLIMITS10 0x33
+#define SCSI_CMD_PREFETCH10 0x34
+#define SCSI_CMD_SYNCHCACHE10 0x35
+#define SCSI_CMD_LOCKCACHE 0x36
+#define SCSI_CMD_READDEFECTDATA10 0x37
+#define SCSI_CMD_COMPARE 0x39
+#define SCSI_CMD_COPYANDVERIFY 0x3a
+#define SCSI_CMD_WRITEBUFFER 0x3b
+#define SCSI_CMD_READBUFFER 0x3c
+#define SCSI_CMD_READLONG10 0x3e
+#define SCSI_CMD_WRITELONG10 0x3f
+#define SCSI_CMD_CHANGEDEFINITION 0x40
+#define SCSI_CMD_WRITESAME10 0x41
+#define SCSI_CMD_LOGSELECT 0x4c
+#define SCSI_CMD_LOGSENSE 0x4d
+#define SCSI_CMD_XDWRITE10 0x50
+#define SCSI_CMD_XPWRITE10 0x51
+#define SCSI_CMD_XDREAD10 0x52
+#define SCSI_CMD_MODESELECT10 0x55
+#define SCSI_CMD_RESERVE10 0x56
+#define SCSI_CMD_RELEASE10 0x57
+#define SCSI_CMD_MODESENSE10 0x5a
+#define SCSI_CMD_PERSISTENTRESERVEIN 0x5e
+#define SCSI_CMD_PERSISTENTRESERVEOUT 0x5f
+#define SCSI_CMD_32 0x7f
+#define SCSI_CMD_XDWRITEEXTENDED 0x80
+#define SCSI_CMD_REBUILD 0x82
+#define SCSI_CMD_REGENERATE 0x82
+#define SCSI_CMD_EXTENDEDCOPY 0x83
+#define SCSI_CMD_COPYRESULTS 0x84
+#define SCSI_CMD_ACCESSCONTROLIN 0x86
+#define SCSI_CMD_ACCESSCONTROLOUT 0x87
+#define SCSI_CMD_READ16 0x88
+#define SCSI_CMD_WRITE16 0x8a
+#define SCSI_CMD_READATTRIBUTE 0x8c
+#define SCSI_CMD_WRITEATTRIBUTE 0x8d
+#define SCSI_CMD_WRITEANDVERIFY16 0x8e
+#define SCSI_CMD_PREFETCH16 0x90
+#define SCSI_CMD_SYNCHCACHE16 0x91
+#define SCSI_CMD_LOCKUNLOCKACACHE 0x92
+#define SCSI_CMD_WRITESAME16 0x93
+#define SCSI_CMD_READCAPACITY16 0x9e
+#define SCSI_CMD_READLONG16 0x9e
+#define SCSI_CMD_WRITELONG106 0x9f
+#define SCSI_CMD_REPORTLUNS 0xa0
+#define SCSI_CMD_MAINTENANCEIN 0xa3
+#define SCSI_CMD_MAINTENANCEOUT 0xa4
+#define SCSI_CMD_MOVEMEDIUM 0xa5
+#define SCSI_CMD_MOVEMEDIUMATTACHED 0xa7
+#define SCSI_CMD_READ12 0xa8
+#define SCSI_CMD_WRITE12 0xaa
+#define SCSI_CMD_READMEDIASERIALNUMBER 0xab
+#define SCSI_CMD_WRITEANDVERIFY12 0xae
+#define SCSI_CMD_VERIFY12 0xaf
+#define SCSI_CMD_SETLIMITS12 0xb3
+#define SCSI_CMD_READELEMENTSTATUS 0xb4
+#define SCSI_CMD_READDEFECTDATA12 0xb7
+#define SCSI_CMD_REDUNDANCYGROUPIN 0xba
+#define SCSI_CMD_REDUNDANCYGROUPOUT 0xbb
+#define SCSI_CMD_SPAREIN 0xbc
+#define SCSI_CMD_SPAREOUT 0xbd
+#define SCSI_CMD_VOLUMESETIN 0xbe
+#define SCSI_CMD_VOLUMESETOUT 0xbf
+
+/* Common SCSI KCQ values (sense Key/additional sense Code/ASC Qualifier) ***
+ *
+ * 0xnn0386 Write Fault Data Corruption
+ * 0xnn0500 Illegal request
+ * 0xnn0600 Unit attention
+ * 0xnn0700 Data protect
+ * 0xnn0800 LUN communication failure
+ * 0xnn0801 LUN communication timeout
+ * 0xnn0802 LUN communication parity error
+ * 0xnn0803 LUN communication CRC error
+ * 0xnn0900 vendor specific sense key
+ * 0xnn0901 servo fault
+ * 0xnn0904 head select fault
+ * 0xnn0a00 error log overflow
+ * 0xnn0b00 aborted command
+ * 0xnn0c00 write error
+ * 0xnn0c02 write error - auto-realloc failed
+ * 0xnn0e00 data miscompare
+ * 0xnn1200 address mark not founf for ID field
+ * 0xnn1400 logical block not found
+ * 0xnn1500 random positioning error
+ * 0xnn1501 mechanical positioning error
+ * 0xnn1502 positioning error detected by read of medium
+ * 0xnn2700 write protected
+ * 0xnn2900 POR or bus reset occurred
+ * 0xnn3101 format failed
+ * 0xnn3191 format corrupted
+ * 0xnn3201 defect list update error
+ * 0xnn3202 no spares available
+ * 0xnn3501 unspecified enclosure services failure
+ * 0xnn3700 parameter rounded
+ * 0xnn3d00 invalid bits in identify message
+ * 0xnn3e00 LUN not self-configured yet
+ * 0xnn4001 DRAM parity error
+ * 0xnn4002 DRAM parity error
+ * 0xnn4200 power-on or self-test failure
+ * 0xnn4c00 LUN failed self-configuration
+ * 0xnn5c00 RPL status change
+ * 0xnn5c01 spindles synchronized
+ * 0xnn5c02 spindles not synchronized
+ * 0xnn6500 voltage fault
+ * 0xnn8000 general firmware error
+ */
+
+/* No sense KCQ values */
+
+#define SCSI_KCQ_NOSENSE 0x000000 /* No error */
+#define SCSI_KCQ_PFATHRESHOLDREACHED 0x005c00 /* No sense - PFA threshold reached */
+
+/* Soft error KCQ values */
+
+#define SCSI_KCQSE_RWENOINDEX 0x010100 /* Recovered Write error - no index */
+#define SCSI_KCQSE_RECOVEREDNOSEEKCOMPLETION 0x010200 /* Recovered no seek completion */
+#define SCSI_KCQSE_RWEWRITEFAULT 0x010300 /* Recovered Write error - write fault */
+#define SCSI_KCQSE_TRACKFOLLOWINGERROR 0x010900 /* Track following error */
+#define SCSI_KCQSE_TEMPERATUREWARNING 0x010b01 /* Temperature warning */
+#define SCSI_KCQSE_RWEWARREALLOCATED 0x010c01 /* Recovered Write error with auto-realloc - reallocated */
+#define SCSI_KCQSE_RWERECOMMENDREASSIGN 0x010c03 /* Recovered Write error - recommend reassign */
+#define SCSI_KCQSE_RDWOEUSINGPREVLBI 0x011201 /* Recovered data without ECC using prev logical block ID */
+#define SCSI_KCQSE_RDWEUSINGPREVLBI 0x011202 /* Recovered data with ECC using prev logical block ID */
+#define SCSI_KCQSE_RECOVEREDRECORDNOTFOUND 0x011401 /* Recovered Record Not Found */
+#define SCSI_KCQSE_RWEDSME 0x011600 /* Recovered Write error - Data Sync Mark Error */
+#define SCSI_KCQSE_RWEDSEDATAREWRITTEN 0x011601 /* Recovered Write error - Data Sync Error - data rewritten */
+#define SCSI_KCQSE_RWEDSERECOMMENDREWRITE 0x011602 /* Recovered Write error - Data Sync Error - recommend rewrite */
+#define SCSI_KCQSE_RWEDSEDATAAUTOREALLOCATED 0x011603 /* Recovered Write error - Data Sync Error - data auto-reallocated */
+#define SCSI_KCQSE_RWEDSERECOMMENDREASSIGNMENT 0x011604 /* Recovered Write error - Data Sync Error - recommend reassignment */
+#define SCSI_KCQSE_RDWNECORRECTIONAPPLIED 0x011700 /* Recovered data with no error correction applied */
+#define SCSI_KCQSE_RREWITHRETRIES 0x011701 /* Recovered Read error - with retries */
+#define SCSI_KCQSE_RDUSINGPOSITIVEOFFSET 0x011702 /* Recovered data using positive offset */
+#define SCSI_KCQSE_RDUSINGNEGATIVEOFFSET 0x011703 /* Recovered data using negative offset */
+#define SCSI_KCQSE_RDUSINGPREVIOUSLBI 0x011705 /* Recovered data using previous logical block ID */
+#define SCSI_KCQSE_RREWOEAUTOREALLOCATED 0x011706 /* Recovered Read error - without ECC, auto reallocated */
+#define SCSI_KCQSE_RREWOERECOMMENDREASSIGN 0x011707 /* Recovered Read error - without ECC, recommend reassign */
+#define SCSI_KCQSE_RREWOERECOMMENDREWRITE 0x011708 /* Recovered Read error - without ECC, recommend rewrite */
+#define SCSI_KCQSE_RREWOEDATAREWRITTEN 0x011709 /* Recovered Read error - without ECC, data rewritten */
+#define SCSI_KCQSE_RREWE 0x011800 /* Recovered Read error - with ECC */
+#define SCSI_KCQSE_RDWEANDRETRIES 0x011801 /* Recovered data with ECC and retries */
+#define SCSI_KCQSE_RREWEAUTOREALLOCATED 0x011802 /* Recovered Read error - with ECC, auto reallocated */
+#define SCSI_KCQSE_RREWERECOMMENDREASSIGN 0x011805 /* Recovered Read error - with ECC, recommend reassign */
+#define SCSI_KCQSE_RDUSINGECCANDOFFSETS 0x011806 /* Recovered data using ECC and offsets */
+#define SCSI_KCQSE_RREWEDATAREWRITTEN 0x011807 /* Recovered Read error - with ECC, data rewritten */
+#define SCSI_KCQSE_DLNOTFOUND 0x011c00 /* Defect List not found */
+#define SCSI_KCQSE_PRIMARYDLNOTFOUND 0x011c01 /* Primary defect list not found */
+#define SCSI_KCQSE_GROWNDLNOTFOUND 0x011c02 /* Grown defect list not found */
+#define SCSI_KCQSE_PARTIALDLTRANSFERRED 0x011f00 /* Partial defect list transferred */
+#define SCSI_KCQSE_INTERNALTARGETFAILURE 0x014400 /* Internal target failure */
+#define SCSI_KCQSE_PFATHRESHOLDREACHED 0x015d00 /* PFA threshold reached */
+#define SCSI_KCQSE_PFATESTWARNING 0x015dff /* PFA test warning */
+#define SCSI_KCQSE_INTERNALLOGICFAILURE 0x018100 /* Internal logic failure */
+
+/* Not Ready / Diagnostic Failure KCQ values */
+
+#define SCSI_KCQNR_CAUSENOTREPORTABLE 0x020400 /* Not Ready - Cause not reportable. */
+#define SCSI_KCQNR_BECOMINGREADY 0x020401 /* Not Ready - becoming ready */
+#define SCSI_KCQNR_NEEDINITIALIZECOMMAND 0x020402 /* Not Ready - need initialize command (start unit) */
+#define SCSI_KCQNR_MANUALINTERVENTIONREQUIRED 0x020403 /* Not Ready - manual intervention required */
+#define SCSI_KCQNR_FORMATINPROGRESS 0x020404 /* Not Ready - format in progress */
+#define SCSI_KCQNR_SELFTESTINPROGRESS 0x020409 /* Not Ready - self-test in progress */
+#define SCSI_KCQNR_MEDIUMFORMATCORRUPTED 0x023100 /* Not Ready - medium format corrupted */
+#define SCSI_KCQNR_FORMATCOMMANDFAILED 0x023101 /* Not Ready - format command failed */
+#define SCSI_KCQNR_ESUNAVAILABLE 0x023502 /* Not Ready - enclosure services unavailable */
+#define SCSI_KCQNR_MEDIANOTPRESENT 0x023a00 /* Not Ready - media not present */
+#define SCSI_KCQDF_BRINGUPFAILORDEGRADEDMODE 0x024080 /* Diagnostic Failure - bring-up fail or degraded mode */
+#define SCSI_KCQDF_HARDDISKCONTROLLER 0x024081 /* Diagnostic Failure - Hard Disk Controller */
+#define SCSI_KCQDF_RAMMICROCODENOTLOADED 0x024085 /* Diagnostic Failure - RAM microcode not loaded */
+#define SCSI_KCQDF_RROCALIBRATION 0x024090 /* Diagnostic Failure - RRO Calibration */
+#define SCSI_KCQDF_CHANNELCALIBRATION 0x024091 /* Diagnostic Failure - Channel Calibration */
+#define SCSI_KCQDF_HEADLOAD 0x024092 /* Diagnostic Failure - Head Load */
+#define SCSI_KCQDF_WRITEAE 0x024093 /* Diagnostic Failure - Write AE */
+#define SCSI_KCQDF_12VOVERCURRENT 0x024094 /* Diagnostic Failure - 12V over current */
+#define SCSI_KCQDF_OTHERSPINDLEFAILURE 0x024095 /* Diagnostic Failure - Other spindle failure */
+#define SCSI_KCQDF_SELFRESET 0x0240b0 /* Diagnostic Failure - self-reset */
+#define SCSI_KCQDF_CONFIGNOTLOADED 0x024c00 /* Diagnostic Failure - config not loaded */
+
+/* Medium error KCQ values */
+
+#define SCSI_KCQME_WRITEFAULT 0x030300 /* Medium Error - write fault */
+#define SCSI_KCQME_WRITEFAULTAUTOREALLOCFAILED 0x030c02 /* Medium Error - write error - auto-realloc failed */
+#define SCSI_KCQME_WRITERTLIMITEXCEEDED 0x030cbb /* Medium Error - write recovery time limit exceeded */
+#define SCSI_KCQME_IDCRCERROR 0x031000 /* Medium Error - ID CRC error */
+#define SCSI_KCQME_UNRRE1 0x031100 /* Medium Error - unrecovered read error */
+#define SCSI_KCQME_READRETRIESEXHAUSTED 0x031101 /* Medium Error - read retries exhausted */
+#define SCSI_KCQME_ERRORTOOLONGTOCORRECT 0x031102 /* Medium Error - error too long to correct */
+#define SCSI_KCQME_UREAUTOREALLOCFAILED 0x031104 /* Medium Error - unrecovered read error - auto re-alloc failed */
+#define SCSI_KCQME_URERECOMMENDREASSIGN 0x03110b /* Medium Error - unrecovered read error - recommend reassign */
+#define SCSI_KCQME_READRTLIMITEXCEEDED 0x0311ff /* Medium Error - read recovery time limit exceeded */
+#define SCSI_KCQME_RECORDNOTFOUND 0x031401 /* Medium Error - record not found */
+#define SCSI_KCQME_DSME 0x031600 /* Medium Error - Data Sync Mark error */
+#define SCSI_KCQME_DSERECOMMENDREASSIGN 0x031604 /* Medium Error - Data Sync Error - recommend reassign */
+#define SCSI_KCQME_DLE 0x031900 /* Medium Error - defect list error */
+#define SCSI_KCQME_DLNOTAVAILABLE 0x031901 /* Medium Error - defect list not available */
+#define SCSI_KCQME_DLEINPRIMARYLIST 0x031902 /* Medium Error - defect list error in primary list */
+#define SCSI_KCQME_DLEINGROWNLIST 0x031903 /* Medium Error - defect list error in grown list */
+#define SCSI_KCQME_FEWERTHAN50PCTDLCOPIES 0x03190e /* Medium Error - fewer than 50% defect list copies */
+#define SCSI_KCQME_MEDIUMFORMATCORRUPTED 0x033100 /* Medium Error - medium format corrupted */
+#define SCSI_KCQME_FORMATCOMMANDFAILED 0x033101 /* Medium Error - format command failed */
+#define SCSI_KCQME_DATAAUTOREALLOCATED 0x038000 /* Medium Error - data auto-reallocated */
+
+/* Hardware Error KCQ values */
+
+#define SCSI_KCQHE_NOINDEXORSECTOR 0x040100 /* Hardware Error - no index or sector */
+#define SCSI_KCQHE_NOSEEKCOMPLETE 0x040200 /* Hardware Error - no seek complete */
+#define SCSI_KCQHE_WRITEFAULT 0x040300 /* Hardware Error - write fault */
+#define SCSI_KCQHE_COMMUNICATIONFAILURE 0x040800 /* Hardware Error - communication failure */
+#define SCSI_KCQHE_TRACKFOLLOWINGERROR 0x040900 /* Hardware Error - track following error */
+#define SCSI_KCQHE_UREINRESERVEDAREA 0x041100 /* Hardware Error - unrecovered read error in reserved area */
+#define SCSI_KCQHE_DSMEINRESERVEDAREA 0x041600 /* Hardware Error - Data Sync Mark error in reserved area */
+#define SCSI_KCQHE_DLE 0x041900 /* Hardware Error - defect list error */
+#define SCSI_KCQHE_DLEINPRIMARYLIST 0x041902 /* Hardware Error - defect list error in Primary List */
+#define SCSI_KCQHE_DLEINGROWNLIST 0x041903 /* Hardware Error - defect list error in Grown List */
+#define SCSI_KCQHE_REASSIGNFAILED 0x043100 /* Hardware Error - reassign failed */
+#define SCSI_KCQHE_NODEFECTSPAREAVAILABLE 0x043200 /* Hardware Error - no defect spare available */
+#define SCSI_KCQHE_UNSUPPORTEDENCLOSUREFUNCTION 0x043501 /* Hardware Error - unsupported enclosure function */
+#define SCSI_KCQHE_ESUNAVAILABLE 0x043502 /* Hardware Error - enclosure services unavailable */
+#define SCSI_KCQHE_ESTRANSFERFAILURE 0x043503 /* Hardware Error - enclosure services transfer failure */
+#define SCSI_KCQHE_ESREFUSED 0x043504 /* Hardware Error - enclosure services refused */
+#define SCSI_KCQHE_SELFTESTFAILED 0x043e03 /* Hardware Error - self-test failed */
+#define SCSI_KCQHE_UNABLETOUPDATESELFTEST 0x043e04 /* Hardware Error - unable to update self-test */
+#define SCSI_KCQHE_DMDIAGNOSTICFAIL 0x044080 /* Hardware Error - Degrade Mode. Diagnostic Fail */
+#define SCSI_KCQHE_DMHWERROR 0x044081 /* Hardware Error - Degrade Mode. H/W Error */
+#define SCSI_KCQHE_DMRAMMICROCODENOTLOADED 0x044085 /* Hardware Error - Degrade Mode. RAM microcode not loaded */
+#define SCSI_KCQHE_SEEKTESTFAILURE 0x044090 /* Hardware Error - seek test failure */
+#define SCSI_KCQHE_READWRITETESTFAILURE 0x0440a0 /* Hardware Error - read/write test failure */
+#define SCSI_KCQHE_DEVICESELFRESET 0x0440b0 /* Hardware Error - device self-reset */
+#define SCSI_KCQHE_COMPONENTMISMATCH 0x0440d0 /* Hardware Error - component mismatch */
+#define SCSI_KCQHE_INTERNALTARGETFAILURE 0x044400 /* Hardware Error - internal target failure */
+#define SCSI_KCQHE_INTERNALLOGICERROR 0x048100 /* Hardware Error - internal logic error */
+#define SCSI_KCQHE_COMMANDTIMEOUT 0x048200 /* Hardware Error - command timeout */
+
+/* Illegal Request KCQ values */
+
+#define SCSI_KCQIR_PARMLISTLENGTHERROR 0x051a00 /* Illegal Request - parm list length error */
+#define SCSI_KCQIR_INVALIDCOMMAND 0x052000 /* Illegal Request - invalid/unsupported command code */
+#define SCSI_KCQIR_LBAOUTOFRANGE 0x052100 /* Illegal Request - LBA out of range */
+#define SCSI_KCQIR_INVALIDFIELDINCBA 0x052400 /* Illegal Request - invalid field in CDB (Command Descriptor Block) */
+#define SCSI_KCQIR_INVALIDLUN 0x052500 /* Illegal Request - invalid LUN */
+#define SCSI_KCQIR_INVALIDFIELDSINPARMLIST 0x052600 /* Illegal Request - invalid fields in parm list */
+#define SCSI_KCQIR_PARAMETERNOTSUPPORTED 0x052601 /* Illegal Request - parameter not supported */
+#define SCSI_KCQIR_INVALIDPARMVALUE 0x052602 /* Illegal Request - invalid parm value */
+#define SCSI_KCQIR_IFPTHRESHOLDPARAMETER 0x052603 /* Illegal Request - invalid field parameter - threshold parameter */
+#define SCSI_KCQIR_INVALIDRELEASEOFPR 0x052604 /* Illegal Request - invalid release of persistent reservation */
+#define SCSI_KCQIR_IFPTMSFIRMWARETAG 0x052697 /* Illegal Request - invalid field parameter - TMS firmware tag */
+#define SCSI_KCQIR_IFPCHECKSUM 0x052698 /* Illegal Request - invalid field parameter - check sum */
+#define SCSI_KCQIR_IFPFIRMWARETAG 0x052699 /* Illegal Request - invalid field parameter - firmware tag */
+#define SCSI_KCQIR_COMMANDSEQUENCEERROR 0x052c00 /* Illegal Request - command sequence error */
+#define SCSI_KCQIR_UNSUPPORTEDENCLOSUREFUNCTION 0x053501 /* Illegal Request - unsupported enclosure function */
+#define SCSI_KCQIR_SAVINGPARMSNOTSUPPORTED 0x053900 /* Illegal Request - Saving parameters not supported */
+#define SCSI_KCQIR_INVALIDMESSAGE 0x054900 /* Illegal Request - invalid message */
+#define SCSI_KCQIR_MEDIALOADOREJECTFAILED 0x055300 /* Illegal Request - media load or eject failed */
+#define SCSI_KCQIR_UNLOADTAPEFAILURE 0x055301 /* Illegal Request - unload tape failure */
+#define SCSI_KCQIR_MEDIUMREMOVALPREVENTED 0x055302 /* Illegal Request - medium removal prevented */
+#define SCSI_KCQIR_SYSTEMRESOURCEFAILURE 0x055500 /* Illegal Request - system resource failure */
+#define SCSI_KCQIR_SYSTEMBUFFERFULL 0x055501 /* Illegal Request - system buffer full */
+#define SCSI_KCQIR_INSUFFICIENTRR 0x055504 /* Illegal Request - Insufficient Registration Resources */
+
+/* Unit Attention KCQ values */
+
+#define SCSI_KCQUA_NOTREADYTOTRANSITION 0x062800 /* Unit Attention - not-ready to ready transition (format complete) */
+#define SCSI_KCQUA_DEVICERESETOCCURRED 0x062900 /* Unit Attention - POR or device reset occurred */
+#define SCSI_KCQUA_POROCCURRED 0x062901 /* Unit Attention - POR occurred */
+#define SCSI_KCQUA_SCSIBUSRESETOCCURRED 0x062902 /* Unit Attention - SCSI bus reset occurred */
+#define SCSI_KCQUA_TARGETRESETOCCURRED 0x062903 /* Unit Attention - TARGET RESET occurred */
+#define SCSI_KCQUA_SELFINITIATEDRESETOCCURRED 0x062904 /* Unit Attention - self-initiated-reset occurred */
+#define SCSI_KCQUA_TRANSCEIVERMODECHANGETOSE 0x062905 /* Unit Attention - transceiver mode change to SE */
+#define SCSI_KCQUA_TRANSCEIVERMODECHANGETOLVD 0x062906 /* Unit Attention - transceiver mode change to LVD */
+#define SCSI_KCQUA_PARAMETERSCHANGED 0x062a00 /* Unit Attention - parameters changed */
+#define SCSI_KCQUA_MODEPARAMETERSCHANGED 0x062a01 /* Unit Attention - mode parameters changed */
+#define SCSI_KCQUA_LOGSELECTPARMSCHANGED 0x062a02 /* Unit Attention - log select parms changed */
+#define SCSI_KCQUA_RESERVATIONSPREEMPTED 0x062a03 /* Unit Attention - Reservations pre-empted */
+#define SCSI_KCQUA_RESERVATIONSRELEASED 0x062a04 /* Unit Attention - Reservations released */
+#define SCSI_KCQUA_REGISTRATIONSPREEMPTED 0x062a05 /* Unit Attention - Registrations pre-empted */
+#define SCSI_KCQUA_COMMANDSCLEARED 0x062f00 /* Unit Attention - commands cleared by another initiator */
+#define SCSI_KCQUA_OPERATINGCONDITIONSCHANGED 0x063f00 /* Unit Attention - target operating conditions have changed */
+#define SCSI_KCQUA_MICROCODECHANGED 0x063f01 /* Unit Attention - microcode changed */
+#define SCSI_KCQUA_CHANGEDOPERATINGDEFINITION 0x063f02 /* Unit Attention - changed operating definition */
+#define SCSI_KCQUA_INQUIRYPARAMETERSCHANGED 0x063f03 /* Unit Attention - inquiry parameters changed */
+#define SCSI_KCQUA_DEVICEIDENTIFIERCHANGED 0x063f05 /* Unit Attention - device identifier changed */
+#define SCSI_KCQUA_INVALIDAPMPARAMETERS 0x063f90 /* Unit Attention - invalid APM parameters */
+#define SCSI_KCQUA_WORLDWIDENAMEMISMATCH 0x063f91 /* Unit Attention - world-wide name mismatch */
+#define SCSI_KCQUA_PFATHRESHOLDREACHED 0x065d00 /* Unit Attention - PFA threshold reached */
+#define SCSI_KCQUA_PFATHRESHOLDEXCEEDED 0x065dff /* Unit Attention - PFA threshold exceeded */
+
+/* Write Protect KCQ values */
+
+#define SCSI_KCQWP_COMMANDNOTALLOWED 0x072700 /* Write Protect - command not allowed */
+
+/* Aborted Command KCQ values */
+
+#define SCSI_KCQAC_NOADDITIONALSENSECODE 0x0b0000 /* Aborted Command - no additional sense code */
+#define SCSI_KCQAC_SYNCDATATRANSFERERROR 0x0b1b00 /* Aborted Command - sync data transfer error (extra ACK) */
+#define SCSI_KCQAC_UNSUPPORTEDLUN 0x0b2500 /* Aborted Command - unsupported LUN */
+#define SCSI_KCQAC_ECHOBUFFEROVERWRITTEN 0x0b3f0f /* Aborted Command - echo buffer overwritten */
+#define SCSI_KCQAC_MESSAGEREJECTERROR 0x0b4300 /* Aborted Command - message reject error */
+#define SCSI_KCQAC_INTERNALTARGETFAILURE 0x0b4400 /* Aborted Command - internal target failure */
+#define SCSI_KCQAC_SELECTIONFAILURE 0x0b4500 /* Aborted Command - Selection/Reselection failure */
+#define SCSI_KCQAC_SCSIPARITYERROR 0x0b4700 /* Aborted Command - SCSI parity error */
+#define SCSI_KCQAC_INITIATORDETECTEDERRORECEIVED 0x0b4800 /* Aborted Command - initiator-detected error message received */
+#define SCSI_KCQAC_ILLEGALMESSAGE 0x0b4900 /* Aborted Command - inappropriate/illegal message */
+#define SCSI_KCQAC_DATAPHASEERROR 0x0b4b00 /* Aborted Command - data phase error */
+#define SCSI_KCQAC_OVERLAPPEDCOMMANDSATTEMPTED 0x0b4e00 /* Aborted Command - overlapped commands attempted */
+#define SCSI_KCQAC_LOOPINITIALIZATION 0x0b4f00 /* Aborted Command - due to loop initialization */
+
+/* Other KCQ values: */
+
+#define SCSO_KCQOTHER_MISCOMPARE 0x0e1d00 /* Miscompare - during verify byte check operation */
+
+/* SSCSI Status Codes *******************************************************/
+
+#define SCSI_STATUS_OK 0x00 /* OK */
+#define SCSI_STATUS_CHECKCONDITION 0x02 /* Check condition */
+#define SCSI_STATUS_CONDITIONMET 0x04 /* Condition met */
+#define SCSI_STATUS_BUSY 0x08 /* Busy */
+#define SCSI_STATUS_INTERMEDIATE 0x10 /* Intermediate */
+#define SCSI_STATUS_DATAOVERUNDERRUN 0x12 /* Data Under/Over Run? */
+#define SCSI_STATUS_INTERMEDIATECONDITIONMET 0x14 /* Intermediate - Condition met */
+#define SCSI_STATUS_RESERVATIONCONFLICT 0x18 /* Reservation conflict */
+#define SCSI_STATUS_COMMANDTERMINATED 0x22 /* Command terminated */
+#define SCSI_STATUS_QUEUEFULL 0x28 /* Queue (task set) full */
+#define SCSI_STATUS_ACAACTIVE 0x30 /* ACA active */
+#define SCSI_STATUS_TASKABORTED 0x40 /* Task aborted */
+
+/* Definitions for selected SCSI commands ***********************************/
+
+/* Inquiry */
+
+#define SCSICMD_INQUIRYFLAGS_EVPD 0x01 /* Bit 0: EVPD */
+ /* Bits 5-7: Peripheral Qualifier */
+#define SCSIRESP_INQUIRYPQ_CONNECTED 0x00 /* 000: Device is connected */
+#define SCSIRESP_INQUIRYPQ_NOTCONNECTED 0x20 /* 001: Device is NOT connected */
+#define SCSIRESP_INQUIRYPQ_NOTCAPABLE 0x60 /* 011: LUN not supported */
+ /* Bits 0-4: Peripheral Device */
+#define SCSIRESP_INQUIRYPD_DIRECTACCESS 0x00 /* Direct-access block device */
+#define SCSIRESP_INQUIRYPD_SEQUENTIALACCESS 0x01 /* Sequential-access block device */
+#define SCSIRESP_INQUIRYPD_PRINTER 0x02 /* Printer device */
+#define SCSIRESP_INQUIRYPD_PROCESSOR 0x03 /* Processor device */
+#define SCSIRESP_INQUIRYPD_WRONCE 0x04 /* Write once device */
+#define SCSIRESP_INQUIRYPD_CDDVD 0x05 /* CD/DVD device */
+#define SCSIRESP_INQUIRYPD_SCANNER 0x06 /* Scanner device (obsolete) */
+#define SCSIRESP_INQUIRYPD_OPTICAL 0x07 /* Optical memory device */
+#define SCSIRESP_INQUIRYPD_MEDIUMCHANGER 0x08 /* Medium changer device (Jukebox) */
+#define SCSIRESP_INQUIRYPD_COMMUNICATIONS 0x09 /* Communications device (obsolete) */
+#define SCSIRESP_INQUIRYPD_STORAGEARRAY 0x0c /* Storage array controller device */
+#define SCSIRESP_INQUIRYPD_ENCLOSURESERVICES 0x0d /* Enclosure services device */
+#define SCSIRESP_INQUIRYPD_RBC 0x0e /* Simplified direct-access device */
+#define SCSIRESP_INQUIRYPD_OCRW 0x0f /* Optical reader/writer device */
+#define SCSIRESP_INQUIRYPD_BCC 0x10 /* Bridge controller commands */
+#define SCSIRESP_INQUIRYPD_OSD 0x11 /* Object-based storage device */
+#define SCSIRESP_INQUIRYPD_ADC 0x12 /* Automation/drive interface */
+#define SCSIRESP_INQUIRYPD_WKLU 0x1e /* Well-known logical unit */
+#define SCSIRESP_INQUIRYPD_UNKNOWN 0x1f /* Direct-access block device */
+
+#define SCSIRESP_INQUIRYFLAGS1_RMB 0x80 /* Bit 7: RMB */
+#define SCSIRESP_INQUIRYFLAGS2_NORMACA 0x20 /* Bit 5: NormACA */
+#define SCSIRESP_INQUIRYFLAGS2_HISUP 0x10 /* Bit 4: HiSup */
+#define SCSIRESP_INQUIRYFLAGS2_FMTMASK 0x0f /* Bits 0-3: Response data format */
+
+#define SCSIRESP_INQUIRYFLAGS3_SCCS 0x80 /* Bit 8: SCCS */
+#define SCSIRESP_INQUIRYFLAGS3_ACC 0x40 /* Bit 7: ACC */
+#define SCSIRESP_INQUIRYFLAGS3_TPGSMASK 0x30 /* Bits 4-5: TPGS */
+#define SCSIRESP_INQUIRYFLAGS3_3PC 0x08 /* Bit 3: 3PC */
+#define SCSIRESP_INQUIRYFLAGS3_PROTECT 0x01 /* Bit 0: Protect */
+
+#define SCSIRESP_INQUIRYFLAGS4_BQUE 0x80 /* Bit 7: BQue */
+#define SCSIRESP_INQUIRYFLAGS4_ENCSERV 0x40 /* Bit 6: EncServ */
+#define SCSIRESP_INQUIRYFLAGS4_VS 0x20 /* Bit 5: VS */
+#define SCSIRESP_INQUIRYFLAGS4_MULTIP 0x10 /* Bit 4: MultIP */
+#define SCSIRESP_INQUIRYFLAGS4_MCHNGR 0x08 /* Bit 3: MChngr */
+#define SCSIRESP_INQUIRYFLAGS4_ADDR16 0x01 /* Bit 0: Addr16 */
+
+#define SCSIRESP_INQUIRYFLAGS5_WBUS16 0x20 /* Bit 5: WBus16 */
+#define SCSIRESP_INQUIRYFLAGS5_SYNC 0x10 /* Bit 4: SYNC */
+#define SCSIRESP_INQUIRYFLAGS5_LINKED 0x08 /* Bit 3: LINKED */
+#define SCSIRESP_INQUIRYFLAGS5_CMDQUEUE 0x02 /* Bit 1: CmdQue */
+#define SCSIRESP_INQUIRYFLAGS5_VS 0x01 /* Bit 0: VS */
+
+#define SCSIRESP_INQUIRYFLAGS6_CLOCKINGMASK 0xc0 /* Bits 2-3: Clocking */
+#define SCSIRESP_INQUIRYFLAGS6_QAS 0x02 /* Bit 1: QAS */
+#define SCSIRESP_INQUIRYFLAGS6_IUS 0x01 /* Bit 0: IUS */
+
+/* Sense data */
+
+/* Sense data response codes */
+
+#define SCSIRESP_SENSEDATA_CURRENTFIXED 0x70 /* Byte 1 is always the response code */
+#define SCSIRESP_SENSEDATA_DEFERREDFIXED 0x71
+#define SCSIRESP_SENSEDATA_CURRENTDESC 0x72
+#define SCSIRESP_SENSEDATA_DEFERREDDESC 0x73
+
+#define SCSIRESP_SENSEDATA_RESPVALID 0x80
+
+/* Fixed sense data flags */
+
+#define SCSIRESP_SENSEDATA_FILEMARK 0x80 /* Bit 7: FileMark */
+#define SCSIRESP_SENSEDATA_EOM 0x40 /* Bit 6: EOM */
+#define SCSIRESP_SENSEDATA_ILI 0x20 /* Bit 5: ILI */
+#define SCSIRESP_SENSEDATA_SENSEKEYMASK 0x0f /* Bits 0-3: Sense key */
+#define SCSIRESP_SENSEDATA_NOSENSE 0x00 /* Nothing to be reported */
+#define SCSIRESP_SENSEDATA_RECOVEREDERROR 0x01 /* Successful after recovery action */
+#define SCSIRESP_SENSEDATA_NOTREADY 0x02 /* Logical unit is not accessible */
+#define SCSIRESP_SENSEDATA_MEDIUMERROR 0x03 /* Error possibly caused by flaw in medium */
+#define SCSIRESP_SENSEDATA_HARDWAREERROR 0x04 /* Non-recoverable hardware error */
+#define SCSIRESP_SENSEDATA_ILLEGALREQUEST 0x05 /* Error in received request */
+#define SCSIRESP_SENSEDATA_UNITATTENTION 0x06 /* Unit attention condition */
+#define SCSIRESP_SENSEDATA_DATAPROTECT 0x07 /* Action failed, medium protected */
+#define SCSIRESP_SENSEDATA_BLANKCHECK 0x08 /* Encountered blank media */
+#define SCSIRESP_SENSEDATA_VENDORSPECIFIC 0x09 /* Vendor specific condition */
+#define SCSIRESP_SENSEDATA_ABORTEDCOMMAND 0x0b /* Command was aborted */
+
+#define SCSIRESP_SENSEDATA_KEYVALID 0x80 /* Sense-specific data valid */
+
+/* Mode Select 6 */
+
+#define SCSICMD_MODESELECT6_PF 0x10 /* Bit 4: PF */
+#define SCSICMD_MODESELECT6_SP 0x01 /* Bit 0: SP */
+
+/* Mode Sense 6 */
+
+#define SCSICMD_MODESENSE6_DBD 0x08 /* Bit 3: PF */
+
+#define SCSICMD_MODESENSE_PCMASK 0xc0 /* Bits 6-7: Page control (PC) */
+#define SCSICMD_MODESENSE_PCCURRENT 0x00 /* Current values */
+#define SCSICMD_MODESENSE_PCCHANGEABLE 0x40 /* Changeable values */
+#define SCSICMD_MODESENSE_PCDEFAULT 0x80 /* Default values */
+#define SCSICMD_MODESENSE_PCSAVED 0xc0 /* Saved values */
+#define SCSICMD_MODESENSE_PGCODEMASK 0x3f /* Bits 0-5: Page code */
+
+#define SCSICMD_MODESENSE6_PCDEFAULT 0x80 /* Default values */
+ /* Direct-access device page codes */
+#define SCSIRESP_MODESENSE_PGCCODE_VENDOR 0x00 /* Vendor-specific */
+#define SCSIRESP_MODESENSE_PGCCODE_RWERROR 0x01 /* Read/Write error recovery mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_RECONNECT 0x02 /* Disconnect-reconnect mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_FORMATDEV 0x03 /* Format device mode page (obsolete) */
+#define SCSIRESP_MODESENSE_PGCCODE_RIGID 0x04 /* Rigid disk geometry mode page (obsolete) */
+#define SCSIRESP_MODESENSE_PGCCODE_FLEXIBLE 0x05 /* Flexible disk geometry mode page (obsolete) */
+#define SCSIRESP_MODESENSE_PGCCODE_VERIFY 0x07 /* Verify error recovery mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_CACHING 0x08 /* Caching mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_CONTROL 0x0a /* Control mode page (0x0a/0x00) */
+#define SCSIRESP_MODESENSE_PGCCODE_CONTROLEXT 0x0a /* Control extension mode page (0x0a/0x01) */
+#define SCSIRESP_MODESENSE_PGCCODE_MEDIUMTYPES 0x0b /* Medum types supported mode page (obsolete) */
+#define SCSIRESP_MODESENSE_PGCCODE_NP 0x0c /* Notch and partition mode page (obsolete) */
+#define SCSIRESP_MODESENSE_PGCCODE_XOR 0x10 /* XOR control mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_ES 0x14 /* Enclosure services mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_PSLUN 0x18 /* Protocol-specific LUN mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_PSPORT 0x19 /* Protocol-specific port mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_POWER 0x1a /* Power condition mode page */
+#define SCSIRESP_MODESENSE_PGCCODE_IE 0x1c /* Informational exceptions control mode page (0x1c/0x00) */
+#define SCSIRESP_MODESENSE_PGCCODE_BC 0x1c /* Background control mode page (0x1c/0x01) */
+#define SCSIRESP_MODESENSE_PGCCODE_RETURNALL 0x3f /* Return all mode pages */
+ /* Direct-access caching mode page */
+#define SCSIRESP_CACHINGMODEPG_PS 0x80 /* Byte 0, Bit 7: PS */
+#define SCSIRESP_CACHINGMODEPG_SPF 0x60 /* Byte 0, Bit 6: SPF */
+#define SCSIRESP_CACHINGMODEPG_IC 0x80 /* Byte 2, Bit 7: IC */
+#define SCSIRESP_CACHINGMODEPG_ABPF 0x40 /* Byte 2, Bit 6: ABPF */
+#define SCSIRESP_CACHINGMODEPG_CAP 0x20 /* Byte 2, Bit 5: CAP */
+#define SCSIRESP_CACHINGMODEPG_DISC 0x10 /* Byte 2, Bit 4: DISC */
+#define SCSIRESP_CACHINGMODEPG_SIZE 0x08 /* Byte 2, Bit 3: SIZE */
+#define SCSIRESP_CACHINGMODEPG_WCE 0x04 /* Byte 2, Bit 2: Write cache enable (WCE) */
+#define SCSIRESP_CACHINGMODEPG_MF 0x02 /* Byte 2, Bit 1: MF */
+#define SCSIRESP_CACHINGMODEPG_RCD 0x01 /* Byte 2, Bit 0: Read cache disable (RCD) */
+
+#define SCSIRESP_MODEPARMHDR_DAPARM_WP 0x80 /* Bit 7: WP (Direct-access block devices only) */
+#define SCSIRESP_MODEPARMHDR_DAPARM_DBPFUA 0x10 /* Bit 4: DBOFUA (Direct-access block devices only) */
+
+#define SCSIRESP_PAGEFMT_PS 0x80 /* Bit 7: PS */
+#define SCSIRESP_PAGEFMT_SPF 0x40 /* Bit 6: SPF */
+#define SCSIRESP_PAGEFMT_PGCODEMASK 0x3f /* Bits 0-5: Page code */
+
+/* Prevent / Allow Medium Removal */
+
+#define SCSICMD_PREVENTMEDIUMREMOVAL_TRANSPORT 0x01 /* Removal prohibited from data transport */
+#define SCSICMD_PREVENTMEDIUMREMOVAL_MCHANGER 0x02 /* Removal prohibited from medium changer */
+
+/* Read format capacities */
+
+#define SCIRESP_RDFMTCAPACITIES_UNFORMATED 0x01 /* Unformatted media */
+#define SCIRESP_RDFMTCAPACITIES_FORMATED 0x02 /* Formatted media */
+#define SCIRESP_RDFMTCAPACITIES_NOMEDIA 0x03 /* No media */
+
+/* Read 6 */
+
+#define SCSICMD_READ6_MSLBAMASK 0x1f
+
+/* Write 6 */
+
+#define SCSICMD_WRITE6_MSLBAMASK 0x1f
+
+/* Mode Select 10 */
+
+#define SCSICMD_MODESELECT10_PF 0x10 /* Bit 4: PF */
+#define SCSICMD_MODESELECT10_SP 0x01 /* Bit 0: SP */
+
+/* Mode Sense 10 */
+
+#define SCSICMD_MODESENSE10_LLBAA 0x10 /* Bit 4: LLBAA */
+#define SCSICMD_MODESENSE10_DBD 0x08 /* Bit 3: PF */
+
+/* Read 10 */
+
+#define SCSICMD_READ10FLAGS_RDPROTECTMASK 0xe0
+#define SCSICMD_READ10FLAGS_DPO 0x10 /* Disable Page Out */
+#define SCSICMD_READ10FLAGS_FUA 0x08
+#define SCSICMD_READ10FLAGS_FUANV 0x02
+
+/* Write 10 */
+
+#define SCSICMD_WRITE10FLAGS_WRPROTECTMASK 0xe0
+#define SCSICMD_WRITE10FLAGS_DPO 0x10 /* Disable Page Out */
+#define SCSICMD_WRITE10FLAGS_FUA 0x08
+#define SCSICMD_WRITE10FLAGS_FUANV 0x02
+
+/* Verify 10 */
+
+#define SCSICMD_VERIFY10_VRPROTECTMASK 0xe0 /* Byte 1: Bits 5-7: VRPROTECT */
+#define SCSICMD_VERIFY10_DPO 0x10 /* Byte 1: Bit 4: Disable Page Out (DPO) */
+#define SCSICMD_VERIFY10_BYTCHK 0x02 /* Byte 1: Bit 2: BytChk */
+
+/* Read 12 */
+
+#define SCSICMD_READ12FLAGS_RDPROTECTMASK 0xe0
+#define SCSICMD_READ12FLAGS_DPO 0x10 /* Disable Page Out */
+#define SCSICMD_READ12FLAGS_FUA 0x08
+#define SCSICMD_READ12FLAGS_FUANV 0x02
+
+/* Write 12 */
+
+#define SCSICMD_WRITE12FLAGS_WRPROTECTMASK 0xe0
+#define SCSICMD_WRITE12FLAGS_DPO 0x10 /* Disable Page Out */
+#define SCSICMD_WRITE12FLAGS_FUA 0x08
+#define SCSICMD_WRITE12FLAGS_FUANV 0x02
+
+/* Verify 12 */
+
+#define SCSICMD_VERIFY12_VRPROTECTMASK 0xe0 /* Byte 1: Bits 5-7: VRPROTECT */
+#define SCSICMD_VERIFY12_DPO 0x10 /* Byte 1: Bit 4: Disable Page Out (DPO) */
+#define SCSICMD_VERIFY12_BYTCHK 0x02 /* Byte 1: Bit 2: BytChk */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Format structures for selected SCSI primary commands */
+
+#define SCSICMD_TESTUNITREADY_SIZEOF 6
+
+struct scsicmd_requestsense_s
+{
+ uint8_t opcode; /* 0: 0x03 */
+ uint8_t flags; /* 1: See SCSICMD_REQUESTSENSE_FLAGS_* */
+ uint8_t reserved[2]; /* 2-3: Reserved */
+ uint8_t alloclen; /* 4: Allocation length */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_REQUESTSENSE_SIZEOF 6
+#define SCSICMD_REQUESTSENSE_MSSIZEOF 12 /* MS-Windows REQUEST SENSE with cbw->cdblen == 12 */
+
+struct scsiresp_fixedsensedata_s
+{
+ uint8_t code; /* 0: Response code See SCSIRESP_SENSEDATA_*FIXED defns */
+ uint8_t obsolete; /* 1: */
+ uint8_t flags; /* 2: See SCSIRESP_SENSEDATA_* definitions */
+ uint8_t info[4]; /* 3-6: Information */
+ uint8_t len; /* 7: Additional length */
+ uint8_t cmdinfo[4]; /* 8-11: Command-specific information */
+ uint8_t code2; /* 12: Additional sense code */
+ uint8_t qual2; /* 13: Additional sense code qualifier */
+ uint8_t fru; /* 14: Field replacement unit code */
+ uint8_t key[3]; /* 15-17: Sense key specific */
+ /* 18-: Additional bytes may follow */
+};
+#define SCSIRESP_FIXEDSENSEDATA_SIZEOF 18 /* Minimum size */
+
+struct scscicmd_inquiry_s
+{
+ uint8_t opcode; /* 0: 0x12 */
+ uint8_t flags; /* 1: See SCSICMD_INQUIRY_FLAGS_* */
+ uint8_t pagecode; /* 2: Page code */
+ uint8_t alloclen[2]; /* 3-4: Allocation length */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_INQUIRY_SIZEOF 6
+
+struct scsiresp_inquiry_s
+{
+ /* Mandatory */
+
+ uint8_t qualtype; /* 0: Bits 5-7: Peripheral qualifier; Bits 0-4: Peripheral device type */
+ uint8_t flags1; /* 1: See SCSIRESP_INQUIRY_FLAGS1_* */
+ uint8_t version; /* 2: Version */
+ uint8_t flags2; /* 3: See SCSIRESP_INQUIRY_FLAGS2_* */
+ uint8_t len; /* 4: Additional length */
+ uint8_t flags3; /* 5: See SCSIRESP_INQUIRY_FLAGS3_* */
+ uint8_t flags4; /* 6: See SCSIRESP_INQUIRY_FLAGS4_* */
+ uint8_t flags5; /* 7: See SCSIRESP_INQUIRY_FLAGS5_* */
+ uint8_t vendorid[8]; /* 8-15: T10 Vendor Identification */
+ uint8_t productid[16]; /* 16-31: Product Identification */
+ uint8_t revision[4]; /* 32-35: Product Revision Level */
+
+ /* Optional */
+
+ uint8_t vendor[20]; /* 36-55: Vendor specific */
+ uint8_t flags6; /* 56: See SCSIRESP_INQUIRY_FLAGS6_* */
+ uint8_t reserved1; /* 57: Reserved */
+ uint8_t version1[2]; /* 58-59: Version Descriptor 1 */
+ uint8_t version2[2]; /* 60-61: Version Descriptor 2 */
+ uint8_t version3[2]; /* 62-63: Version Descriptor 3 */
+ uint8_t version4[2]; /* 64-65: Version Descriptor 4 */
+ uint8_t version5[2]; /* 66-67: Version Descriptor 5 */
+ uint8_t version6[2]; /* 68-69: Version Descriptor 6 */
+ uint8_t version7[2]; /* 70-71: Version Descriptor 7 */
+ uint8_t version8[2]; /* 72-73: Version Descriptor 8 */
+ uint8_t reserved2[22]; /* 74-95: Reserved */
+ /* 96-: Vendor-specific parameters may follow */
+};
+#define SCSIRESP_INQUIRY_SIZEOF 36 /* Minimum size */
+
+struct scsicmd_modeselect6_s
+{
+ uint8_t opcode; /* 0x15 */
+ uint8_t flags; /* 1: See SCSICMD_MODESELECT6_FLAGS_* */
+ uint8_t reserved[2]; /* 2-3: Reserved */
+ uint8_t plen; /* 4: Parameter list length */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_MODESELECT6_SIZEOF 6
+
+struct scsicmd_modesense6_s
+{
+ uint8_t opcode; /* 0x1a */
+ uint8_t flags; /* 1: See SCSICMD_MODESENSE6_FLAGS_* */
+ uint8_t pcpgcode; /* 2: Bits 6-7: PC, bits 0-5: page code */
+ uint8_t subpgcode; /* 3: subpage code */
+ uint8_t alloclen; /* 4: Allocation length */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_MODESENSE6_SIZEOF 6
+
+struct scsiresp_modeparameterhdr6_s
+{
+ uint8_t mdlen; /* 0: Mode data length */
+ uint8_t type; /* 1: Medium type */
+ uint8_t param; /* 2: Device-specific parameter */
+ uint8_t bdlen; /* 3: Block descriptor length */
+};
+#define SCSIRESP_MODEPARAMETERHDR6_SIZEOF 4
+
+struct scsiresp_blockdesc_s
+{
+ uint8_t density; /* 0: density code */
+ uint8_t nblocks[3]; /* 1-3: Number of blocks */
+ uint8_t reserved; /* 4: reserved */
+ uint8_t blklen[3]; /* 5-7: Block len */
+};
+#define SCSIRESP_BLOCKDESC_SIZEOF 8
+
+struct scsiresp_pageformat_s
+{
+ uint8_t pgcode; /* 0: See SCSIRESP_PAGEFMT_* definitions */
+ uint8_t pglen; /* 1: Page length (n-1) */
+ uint8_t parms[1]; /* 2-n: Mode parameters */
+};
+
+struct scsiresp_subpageformat_s
+{
+ uint8_t pgcode; /* 0: See SCSIRESP_PAGEFMT_* definitions */
+ uint8_t subpgcode; /* 1: sub-page code */
+ uint8_t pglen[2]; /* 2-3: Page length (n-3) */
+ uint8_t parms[1]; /* 4-n: Mode parameters */
+};
+
+struct scsiresp_cachingmodepage_s
+{
+ uint8_t pgcode; /* 0: Bit 7: PS; Bit 6: SPF, Bits 0-5: page code == 8 */
+ uint8_t len; /* 1: Page length (18) */
+ uint8_t flags1; /* 2: See SCSIRESP_CACHINGMODEPG_* definitions */
+ uint8_t priority; /* 3: Bits 4-7: Demand read retention priority; Bits 0-3: Write retention priority */
+ uint8_t dpflen[2]; /* 4-5: Disable prefetch transfer length */
+ uint8_t minpf[2]; /* 6-7: Minimum pre-fetch */
+ uint8_t maxpf[2]; /* 8-9: Maximum pre-fetch */
+ uint8_t maxpfc[2]; /* 10-11: Maximum pref-fetch ceiling */
+ uint8_t flags2; /* 12: See SCSIRESP_CACHINGMODEPG_* definitions */
+ uint8_t nsegments; /* 13: Number of cache segments */
+ uint8_t segsize[2]; /* 14-15: Cache segment size */
+ uint8_t reserved; /* 16: Reserved */
+ uint8_t obsolete[3]; /* 17-19: Obsolete */
+};
+
+/* Format structures for selected SCSI block commands */
+
+struct scsicmd_read6_s
+{
+ uint8_t opcode; /* 0: 0x08 */
+ uint8_t mslba; /* 1: Bits 5-7: reserved; Bits 0-6: MS Logical Block Address (LBA) */
+ uint8_t lslba[2]; /* 2-3: LS Logical Block Address (LBA) */
+ uint8_t xfrlen; /* 4: Transfer length (in contiguous logical blocks) */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_READ6_SIZEOF 6
+
+struct scsicmd_write6_s
+{
+ uint8_t opcode; /* 0: 0x0a */
+ uint8_t mslba; /* 1: Bits 5-7: reserved; Bits 0-6: MS Logical Block Address (LBA) */
+ uint8_t lslba[2]; /* 2-3: LS Logical Block Address (LBA) */
+ uint8_t xfrlen; /* 4: Transfer length (in contiguous logical blocks) */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_WRITE6_SIZEOF 6
+
+struct scsicmd_startstopunit_s
+{
+ uint8_t opcode; /* 0: 0x1b */
+ uint8_t immed; /* 1: Bits 2-7: Reserved, Bit 0: Immed */
+ uint8_t reserved; /* 2: reserved */
+ uint8_t pcm; /* 3: Bits 4-7: Reserved, Bits 0-3: Power condition modifier */
+ uint8_t pc; /* 4: Bits 4-7: Power condition, Bit 2: NO_FLUSH, Bit 1: LOEJ, Bit 0: START */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_STARTSTOPUNIT_SIZEOF 6
+
+struct scsicmd_preventmediumremoval_s
+{
+ uint8_t opcode; /* 0: 0x1e */
+ uint8_t reserved[3]; /* 1-3: Reserved */
+ uint8_t prevent; /* 4: Bits 2-7: Reserved, Bits 0:1: prevent */
+ uint8_t control; /* 5: Control */
+};
+#define SCSICMD_PREVENTMEDIUMREMOVAL_SIZEOF 6
+
+struct scsicmd_readformatcapcacities_s
+{
+ uint8_t opcode; /* 0: 0x23 */
+ uint8_t reserved[6]; /* 1-6: Reserved */
+ uint8_t alloclen[2]; /* 7-8: Allocation length */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_READFORMATCAPACITIES_SIZEOF 10
+
+struct scsiresp_readformatcapacities_s
+{
+ /* Current capacity header */
+
+ uint8_t reserved[3]; /* 0-2: Reserved */
+ uint8_t listlen; /* 3: Capacity list length */
+
+ /* Current/Maximum Capacity Descriptor (actually a separate structure) */
+
+ uint8_t nblocks[4]; /* 4-7: Number of blocks */
+ uint8_t type; /* 8: Bits 2-7: Reserved, Bits 0-1: Descriptor type */
+ uint8_t blocklen[3]; /* 9-11: Block length */
+};
+#define SCSIRESP_READFORMATCAPACITIES_SIZEOF 12
+#define SCSIRESP_CURRCAPACITYDESC_SIZEOF 8
+
+struct scsiresp_formattedcapacitydesc_s
+{
+ uint8_t nblocks[4]; /* 0-3: Number of blocks */
+ uint8_t type; /* 4: Bits 2-7: Type, bits 0-1, reserved */
+ uint8_t param[3]; /* 5-7: Type dependent parameter */
+};
+#define SCSIRESP_FORMATTEDCAPACITYDESC_SIZEOF 8
+
+struct scsicmd_readcapacity10_s
+{
+ uint8_t opcode; /* 0: 0x25 */
+ uint8_t reserved1; /* 1: Bits 1-7: Reserved, Bit 0: Obsolete */
+ uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
+ uint8_t reserved2[2]; /* 6-7: Reserved */
+ uint8_t pmi; /* 8: Bits 1-7 Reserved; Bit 0: PMI */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_READCAPACITY10_SIZEOF 10
+
+struct scsiresp_readcapacity10_s
+{
+ uint8_t lba[4]; /* 0-3: Returned logical block address (LBA) */
+ uint8_t blklen[4]; /* 4-7: Logical block length (in bytes) */
+};
+#define SCSIRESP_READCAPACITY10_SIZEOF 8
+
+struct scsicmd_read10_s
+{
+ uint8_t opcode; /* 0: 0x28 */
+ uint8_t flags; /* 1: See SCSICMD_READ10FLAGS_* */
+ uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
+ uint8_t groupno; /* 6: Bits 5-7: reserved; Bits 0-6: group number */
+ uint8_t xfrlen[2]; /* 7-8: Transfer length (in contiguous logical blocks) */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_READ10_SIZEOF 10
+
+struct scsicmd_write10_s
+{
+ uint8_t opcode; /* 0: 0x2a */
+ uint8_t flags; /* 1: See SCSICMD_WRITE10FLAGS_* */
+ uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
+ uint8_t groupno; /* 6: Bits 5-7: reserved; Bits 0-6: group number */
+ uint8_t xfrlen[2]; /* 7-8: Transfer length (in contiguous logical blocks) */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_WRITE10_SIZEOF 10
+
+struct scsicmd_verify10_s
+{
+ uint8_t opcode; /* 0: 0x2f */
+ uint8_t flags; /* 1: See SCSICMD_VERIFY10_* definitions */
+ uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
+ uint8_t groupno; /* 6: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
+ uint8_t len[2]; /* 7-8: Verification length (in blocks) */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_VERIFY10_SIZEOF 10
+
+struct scsicmd_synchronizecache10_s
+{
+ uint8_t opcode; /* 0: 0x35 */
+ uint8_t flags; /* 1: See SCSICMD_SYNCHRONIZECACHE10_* definitions */
+ uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
+ uint8_t groupno; /* 6: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
+ uint8_t len[2]; /* 7-8: Number of logical blocks */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_SYNCHRONIZECACHE10_SIZEOF 10
+
+struct scsicmd_modeselect10_s
+{
+ uint8_t opcode; /* 0: 0x55 */
+ uint8_t flags; /* 1: See SCSICMD_MODESELECT10_FLAGS_* */
+ uint8_t reserved[5]; /* 2-6: Reserved */
+ uint8_t parmlen[2]; /* 7-8: Parameter list length */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_MODESELECT10_SIZEOF 10
+
+struct scsiresp_modeparameterhdr10_s
+{
+ uint8_t mdlen[2]; /* 0-1: Mode data length */
+ uint8_t type; /* 2: Medium type */
+ uint8_t param; /* 3: Device-specific parameter */
+ uint8_t reserved[2]; /* 4-5: reserved */
+ uint8_t bdlen[2]; /* 6-7: Block descriptor length */
+};
+#define SCSIRESP_MODEPARAMETERHDR10_SIZEOF 8
+
+struct scsicmd_modesense10_s
+{
+ uint8_t opcode; /* O: 0x5a */
+ uint8_t flags; /* 1: See SCSICMD_MODESENSE10_FLAGS_* */
+ uint8_t pcpgcode; /* 2: Bits 6-7: PC, bits 0-5: page code */
+ uint8_t subpgcode; /* 3: subpage code */
+ uint8_t reserved[3]; /* 4-6: reserved */
+ uint8_t alloclen[2]; /* 7-8: Allocation length */
+ uint8_t control; /* 9: Control */
+};
+#define SCSICMD_MODESENSE10_SIZEOF 10
+
+struct scsicmd_readcapacity16_s
+{
+ uint8_t opcode; /* 0: 0x9e */
+ uint8_t action; /* 1: Bits 5-7: Reserved, Bits 0-4: Service action */
+ uint8_t lba[8]; /* 2-9: Logical block address (LBA) */
+ uint8_t len[4]; /* 10-13: Allocation length */
+ uint8_t reserved; /* 14: Reserved */
+ uint8_t control; /* 15: Control */
+};
+#define SCSICMD_READCAPACITY16_SIZEOF 16
+
+struct scsicmd_read12_s
+{
+ uint8_t opcode; /* 0: 0xa8 */
+ uint8_t flags; /* 1: See SCSICMD_READ12FLAGS_* */
+ uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
+ uint8_t xfrlen[4]; /* 6-9: Transfer length (in contiguous logical blocks) */
+ uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: reserved; Bits 0-6: group number */
+ uint8_t control; /* 11: Control */
+};
+#define SCSICMD_READ12_SIZEOF 12
+
+struct scsicmd_write12_s
+{
+ uint8_t opcode; /* 0: 0xaa */
+ uint8_t flags; /* 1: See SCSICMD_WRITE12FLAGS_* */
+ uint8_t lba[4]; /* 2-5: Logical Block Address (LBA) */
+ uint8_t xfrlen[4]; /* 6-9: Transfer length (in contiguous logical blocks) */
+ uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: reserved; Bits 0-6: group number */
+ uint8_t control; /* 11: Control */
+};
+#define SCSICMD_WRITE12_SIZEOF 12
+
+struct scsicmd_verify12_s
+{
+ uint8_t opcode; /* 0: 0xaf */
+ uint8_t flags; /* 1: See SCSICMD_VERIFY12_* definitions */
+ uint8_t lba[4]; /* 2-5: Logical block address (LBA) */
+ uint8_t len[4]; /* 6-9: Verification length */
+ uint8_t groupno; /* 10: Bit 7: restricted; Bits 5-6: Reserved, Bits 0-4: Group number */
+ uint8_t control; /* 11: Control */
+};
+#define SCSICMD_VERIFY12_SIZEOF 12
+
+/****************************************************************************
+ * Public Functions Definitions
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_SCSI_H */
diff --git a/components/drivers/usb/cherryusb/class/msc/usbd_msc.c b/components/drivers/usb/cherryusb/class/msc/usbd_msc.c
new file mode 100644
index 0000000000..055cdc4e65
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usbd_msc.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ * Copyright (c) 2024, zhihong chen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_msc.h"
+#include "usb_scsi.h"
+#if defined(CONFIG_USBDEV_MSC_THREAD)
+#include "usb_osal.h"
+#endif
+
+#define MSD_OUT_EP_IDX 0
+#define MSD_IN_EP_IDX 1
+
+/* Describe EndPoints configuration */
+static struct usbd_endpoint mass_ep_data[CONFIG_USBDEV_MAX_BUS][2];
+
+/* MSC Bulk-only Stage */
+enum Stage {
+ MSC_READ_CBW = 0, /* Command Block Wrapper */
+ MSC_DATA_OUT = 1, /* Data Out Phase */
+ MSC_DATA_IN = 2, /* Data In Phase */
+ MSC_SEND_CSW = 3, /* Command Status Wrapper */
+ MSC_WAIT_CSW = 4, /* Command Status Wrapper */
+};
+
+/* Device data structure */
+USB_NOCACHE_RAM_SECTION struct usbd_msc_priv {
+ /* state of the bulk-only state machine */
+ enum Stage stage;
+ USB_MEM_ALIGNX struct CBW cbw;
+ USB_MEM_ALIGNX struct CSW csw;
+
+ bool readonly;
+ bool popup;
+ uint8_t sKey; /* Sense key */
+ uint8_t ASC; /* Additional Sense Code */
+ uint8_t ASQ; /* Additional Sense Qualifier */
+ uint8_t max_lun;
+ uint32_t start_sector;
+ uint32_t nsectors;
+ uint32_t scsi_blk_size[CONFIG_USBDEV_MSC_MAX_LUN];
+ uint32_t scsi_blk_nbr[CONFIG_USBDEV_MSC_MAX_LUN];
+
+ USB_MEM_ALIGNX uint8_t block_buffer[CONFIG_USBDEV_MSC_MAX_BUFSIZE];
+
+#if defined(CONFIG_USBDEV_MSC_THREAD)
+ usb_osal_mq_t usbd_msc_mq;
+ usb_osal_thread_t usbd_msc_thread;
+ uint32_t nbytes;
+#endif
+} g_usbd_msc[CONFIG_USBDEV_MAX_BUS];
+
+#ifdef CONFIG_USBDEV_MSC_THREAD
+static void usbdev_msc_thread(void *argument);
+#endif
+
+static void usdb_msc_set_max_lun(uint8_t busid)
+{
+ g_usbd_msc[busid].max_lun = CONFIG_USBDEV_MSC_MAX_LUN - 1u;
+}
+
+static void usbd_msc_reset(uint8_t busid)
+{
+ g_usbd_msc[busid].stage = MSC_READ_CBW;
+ g_usbd_msc[busid].readonly = false;
+}
+
+static int msc_storage_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("MSC Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ switch (setup->bRequest) {
+ case MSC_REQUEST_RESET:
+ usbd_msc_reset(busid);
+ break;
+
+ case MSC_REQUEST_GET_MAX_LUN:
+ (*data)[0] = g_usbd_msc[busid].max_lun;
+ *len = 1;
+ break;
+
+ default:
+ USB_LOG_WRN("Unhandled MSC Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+void msc_storage_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_INIT:
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ g_usbd_msc[busid].usbd_msc_mq = usb_osal_mq_create(1);
+ if (g_usbd_msc[busid].usbd_msc_mq == NULL) {
+ USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_mq\r\n");
+ }
+ g_usbd_msc[busid].usbd_msc_thread = usb_osal_thread_create("usbd_msc", CONFIG_USBDEV_MSC_STACKSIZE, CONFIG_USBDEV_MSC_PRIO, usbdev_msc_thread, (void *)busid);
+ if (g_usbd_msc[busid].usbd_msc_thread == NULL) {
+ USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_thread\r\n");
+ }
+#endif
+ break;
+ case USBD_EVENT_DEINIT:
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ if (g_usbd_msc[busid].usbd_msc_mq) {
+ usb_osal_mq_delete(g_usbd_msc[busid].usbd_msc_mq);
+ }
+ if (g_usbd_msc[busid].usbd_msc_thread) {
+ usb_osal_thread_delete(g_usbd_msc[busid].usbd_msc_thread);
+ }
+#endif
+ break;
+ case USBD_EVENT_RESET:
+ usbd_msc_reset(busid);
+ break;
+ case USBD_EVENT_CONFIGURED:
+ USB_LOG_DBG("Start reading cbw\r\n");
+ usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void usbd_msc_bot_abort(uint8_t busid)
+{
+ if ((g_usbd_msc[busid].cbw.bmFlags == 0) && (g_usbd_msc[busid].cbw.dDataLength != 0)) {
+ usbd_ep_set_stall(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr);
+ }
+ usbd_ep_set_stall(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr);
+ usbd_ep_start_read(busid, mass_ep_data[busid][0].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
+}
+
+static void usbd_msc_send_csw(uint8_t busid, uint8_t CSW_Status)
+{
+ g_usbd_msc[busid].csw.dSignature = MSC_CSW_Signature;
+ g_usbd_msc[busid].csw.bStatus = CSW_Status;
+
+ /* updating the State Machine , so that we wait CSW when this
+ * transfer is complete, ie when we get a bulk in callback
+ */
+ g_usbd_msc[busid].stage = MSC_WAIT_CSW;
+
+ USB_LOG_DBG("Send csw\r\n");
+ usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].csw, sizeof(struct CSW));
+}
+
+static void usbd_msc_send_info(uint8_t busid, uint8_t *buffer, uint8_t size)
+{
+ size = MIN(size, g_usbd_msc[busid].cbw.dDataLength);
+
+ /* updating the State Machine , so that we send CSW when this
+ * transfer is complete, ie when we get a bulk in callback
+ */
+ g_usbd_msc[busid].stage = MSC_SEND_CSW;
+
+ usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, buffer, size);
+
+ g_usbd_msc[busid].csw.dDataResidue -= size;
+ g_usbd_msc[busid].csw.bStatus = CSW_STATUS_CMD_PASSED;
+}
+
+static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes);
+static bool SCSI_processRead(uint8_t busid);
+
+/**
+* @brief SCSI_SetSenseData
+* Load the last error code in the error list
+* @param sKey: Sense Key
+* @param ASC: Additional Sense Code
+* @retval none
+
+*/
+static void SCSI_SetSenseData(uint8_t busid, uint32_t KCQ)
+{
+ g_usbd_msc[busid].sKey = (uint8_t)(KCQ >> 16);
+ g_usbd_msc[busid].ASC = (uint8_t)(KCQ >> 8);
+ g_usbd_msc[busid].ASQ = (uint8_t)(KCQ);
+}
+
+/**
+ * @brief SCSI Command list
+ *
+ */
+
+static bool SCSI_testUnitReady(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+ *data = NULL;
+ *len = 0;
+ return true;
+}
+
+static bool SCSI_requestSense(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint8_t data_len = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_FIXEDSENSEDATA_SIZEOF) {
+ data_len = g_usbd_msc[busid].cbw.CB[4];
+ }
+
+ uint8_t request_sense[SCSIRESP_FIXEDSENSEDATA_SIZEOF] = {
+ 0x70,
+ 0x00,
+ 0x00, /* Sense Key */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ SCSIRESP_FIXEDSENSEDATA_SIZEOF - 8,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00, /* Additional Sense Code */
+ 0x00, /* Additional Sense Request */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ };
+
+ request_sense[2] = g_usbd_msc[busid].sKey;
+ request_sense[12] = g_usbd_msc[busid].ASC;
+ request_sense[13] = g_usbd_msc[busid].ASQ;
+#if 0
+ request_sense[ 2] = 0x06; /* UNIT ATTENTION */
+ request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x02; /* NOT READY */
+ request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */
+ request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x00; /* NO SENSE */
+ request_sense[12] = 0x00; /* Additional Sense Code: No additional code */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+
+ memcpy(*data, (uint8_t *)request_sense, data_len);
+ *len = data_len;
+ return true;
+}
+
+static bool SCSI_inquiry(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint8_t data_len = SCSIRESP_INQUIRY_SIZEOF;
+
+ uint8_t inquiry00[6] = {
+ 0x00,
+ 0x00,
+ 0x00,
+ (0x06 - 4U),
+ 0x00,
+ 0x80
+ };
+
+ /* USB Mass storage VPD Page 0x80 Inquiry Data for Unit Serial Number */
+ uint8_t inquiry80[8] = {
+ 0x00,
+ 0x80,
+ 0x00,
+ 0x08,
+ 0x20, /* Put Product Serial number */
+ 0x20,
+ 0x20,
+ 0x20
+ };
+
+ uint8_t inquiry[SCSIRESP_INQUIRY_SIZEOF] = {
+ /* 36 */
+
+ /* LUN 0 */
+ 0x00,
+ 0x80,
+ 0x02,
+ 0x02,
+ (SCSIRESP_INQUIRY_SIZEOF - 5),
+ 0x00,
+ 0x00,
+ 0x00,
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', /* Product : 16 Bytes */
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ' /* Version : 4 Bytes */
+ };
+
+ memcpy(&inquiry[8], CONFIG_USBDEV_MSC_MANUFACTURER_STRING, strlen(CONFIG_USBDEV_MSC_MANUFACTURER_STRING));
+ memcpy(&inquiry[16], CONFIG_USBDEV_MSC_PRODUCT_STRING, strlen(CONFIG_USBDEV_MSC_PRODUCT_STRING));
+ memcpy(&inquiry[32], CONFIG_USBDEV_MSC_VERSION_STRING, strlen(CONFIG_USBDEV_MSC_VERSION_STRING));
+
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ if ((g_usbd_msc[busid].cbw.CB[1] & 0x01U) != 0U) { /* Evpd is set */
+ if (g_usbd_msc[busid].cbw.CB[2] == 0U) { /* Request for Supported Vital Product Data Pages*/
+ data_len = 0x06;
+ memcpy(*data, (uint8_t *)inquiry00, data_len);
+ } else if (g_usbd_msc[busid].cbw.CB[2] == 0x80U) { /* Request for VPD page 0x80 Unit Serial Number */
+ data_len = 0x08;
+ memcpy(*data, (uint8_t *)inquiry80, data_len);
+ } else { /* Request Not supported */
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDFIELDINCBA);
+ return false;
+ }
+ } else {
+ if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_INQUIRY_SIZEOF) {
+ data_len = g_usbd_msc[busid].cbw.CB[4];
+ }
+ memcpy(*data, (uint8_t *)inquiry, data_len);
+ }
+
+ *len = data_len;
+ return true;
+}
+
+static bool SCSI_startStopUnit(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x1U) /* START=1 */
+ {
+ //SCSI_MEDIUM_UNLOCKED;
+ } else if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x2U) /* START=0 and LOEJ Load Eject=1 */
+ {
+ //SCSI_MEDIUM_EJECTED;
+ g_usbd_msc[busid].popup = true;
+ } else if ((g_usbd_msc[busid].cbw.CB[4] & 0x3U) == 0x3U) /* START=1 and LOEJ Load Eject=1 */
+ {
+ //SCSI_MEDIUM_UNLOCKED;
+ } else {
+ }
+
+ *data = NULL;
+ *len = 0;
+ return true;
+}
+
+static bool SCSI_preventAllowMediaRemoval(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (g_usbd_msc[busid].cbw.dDataLength != 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+ if (g_usbd_msc[busid].cbw.CB[4] == 0U) {
+ //SCSI_MEDIUM_UNLOCKED;
+ } else {
+ //SCSI_MEDIUM_LOCKED;
+ }
+ *data = NULL;
+ *len = 0;
+ return true;
+}
+
+static bool SCSI_modeSense6(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint8_t data_len = 4;
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+ if (g_usbd_msc[busid].cbw.CB[4] < SCSIRESP_MODEPARAMETERHDR6_SIZEOF) {
+ data_len = g_usbd_msc[busid].cbw.CB[4];
+ }
+
+ uint8_t sense6[SCSIRESP_MODEPARAMETERHDR6_SIZEOF] = { 0x03, 0x00, 0x00, 0x00 };
+
+ if (g_usbd_msc[busid].readonly) {
+ sense6[2] = 0x80;
+ }
+ memcpy(*data, (uint8_t *)sense6, data_len);
+ *len = data_len;
+ return true;
+}
+
+static bool SCSI_modeSense10(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint8_t data_len = 27;
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.CB[8] < 27) {
+ data_len = g_usbd_msc[busid].cbw.CB[8];
+ }
+
+ uint8_t sense10[27] = {
+ 0x00,
+ 0x26,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x08,
+ 0x12,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00
+ };
+
+ memcpy(*data, (uint8_t *)sense10, data_len);
+ *len = data_len;
+ return true;
+}
+
+static bool SCSI_readFormatCapacity(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+ uint8_t format_capacity[SCSIRESP_READFORMATCAPACITIES_SIZEOF] = {
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x08, /* Capacity List Length */
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 24) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 16) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
+
+ 0x02, /* Descriptor Code: Formatted Media */
+ 0x00,
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
+ };
+
+ memcpy(*data, (uint8_t *)format_capacity, SCSIRESP_READFORMATCAPACITIES_SIZEOF);
+ *len = SCSIRESP_READFORMATCAPACITIES_SIZEOF;
+ return true;
+}
+
+static bool SCSI_readCapacity10(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (g_usbd_msc[busid].cbw.dDataLength == 0U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ uint8_t capacity10[SCSIRESP_READCAPACITY10_SIZEOF] = {
+ (uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 24) & 0xff),
+ (uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 16) & 0xff),
+ (uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 8) & 0xff),
+ (uint8_t)(((g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1) >> 0) & 0xff),
+
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 24) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 16) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 8) & 0xff),
+ (uint8_t)((g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN] >> 0) & 0xff),
+ };
+
+ memcpy(*data, (uint8_t *)capacity10, SCSIRESP_READCAPACITY10_SIZEOF);
+ *len = SCSIRESP_READCAPACITY10_SIZEOF;
+ return true;
+}
+
+static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
+ USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
+
+ g_usbd_msc[busid].nsectors = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]); /* Number of Blocks to transfer */
+ USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
+
+ if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_LBAOUTOFRANGE);
+ USB_LOG_ERR("LBA out of range\r\n");
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.dDataLength != (g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN])) {
+ USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n");
+ return false;
+ }
+ g_usbd_msc[busid].stage = MSC_DATA_IN;
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
+ return true;
+#else
+ return SCSI_processRead(busid);
+#endif
+}
+
+static bool SCSI_read12(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x80U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
+ USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
+
+ g_usbd_msc[busid].nsectors = GET_BE32(&g_usbd_msc[busid].cbw.CB[6]); /* Number of Blocks to transfer */
+ USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
+
+ if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_LBAOUTOFRANGE);
+ USB_LOG_ERR("LBA out of range\r\n");
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.dDataLength != (g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN])) {
+ USB_LOG_ERR("scsi_blk_len does not match with dDataLength\r\n");
+ return false;
+ }
+ g_usbd_msc[busid].stage = MSC_DATA_IN;
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
+ return true;
+#else
+ return SCSI_processRead(busid);
+#endif
+}
+
+static bool SCSI_write10(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint32_t data_len = 0;
+ if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
+ USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
+
+ g_usbd_msc[busid].nsectors = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]); /* Number of Blocks to transfer */
+ USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
+
+ data_len = g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
+ if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
+ USB_LOG_ERR("LBA out of range\r\n");
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.dDataLength != data_len) {
+ return false;
+ }
+ g_usbd_msc[busid].stage = MSC_DATA_OUT;
+ data_len = MIN(data_len, CONFIG_USBDEV_MSC_MAX_BUFSIZE);
+ usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
+ return true;
+}
+
+static bool SCSI_write12(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint32_t data_len = 0;
+ if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]); /* Logical Block Address of First Block */
+ USB_LOG_DBG("lba: 0x%04x\r\n", g_usbd_msc[busid].start_sector);
+
+ g_usbd_msc[busid].nsectors = GET_BE32(&g_usbd_msc[busid].cbw.CB[6]); /* Number of Blocks to transfer */
+ USB_LOG_DBG("nsectors: 0x%02x\r\n", g_usbd_msc[busid].nsectors);
+
+ data_len = g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
+ if ((g_usbd_msc[busid].start_sector + g_usbd_msc[busid].nsectors) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
+ USB_LOG_ERR("LBA out of range\r\n");
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.dDataLength != data_len) {
+ return false;
+ }
+ g_usbd_msc[busid].stage = MSC_DATA_OUT;
+ data_len = MIN(data_len, CONFIG_USBDEV_MSC_MAX_BUFSIZE);
+ usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
+ return true;
+}
+/* do not use verify to reduce code size */
+#if 0
+static bool SCSI_verify10(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ /* Logical Block Address of First Block */
+ uint32_t lba = 0;
+ uint32_t blk_num = 0;
+
+ if ((g_usbd_msc[busid].cbw.CB[1] & 0x02U) == 0x00U) {
+ return true;
+ }
+
+ if (((g_usbd_msc[busid].cbw.bmFlags & 0x80U) != 0x00U) || (g_usbd_msc[busid].cbw.dDataLength == 0U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ if ((g_usbd_msc[busid].cbw.CB[1] & 0x02U) == 0x02U) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDFIELDINCBA);
+ return false; /* Error, Verify Mode Not supported*/
+ }
+
+ lba = GET_BE32(&g_usbd_msc[busid].cbw.CB[2]);
+ USB_LOG_DBG("lba: 0x%x\r\n", lba);
+
+ g_usbd_msc[busid].scsi_blk_addr = lba * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
+
+ /* Number of Blocks to transfer */
+ blk_num = GET_BE16(&g_usbd_msc[busid].cbw.CB[7]);
+
+ USB_LOG_DBG("num (block) : 0x%x\r\n", blk_num);
+ g_usbd_msc[busid].scsi_blk_len = blk_num * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
+
+ if ((lba + blk_num) > g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN]) {
+ USB_LOG_ERR("LBA out of range\r\n");
+ return false;
+ }
+
+ if (g_usbd_msc[busid].cbw.dDataLength != g_usbd_msc[busid].scsi_blk_len) {
+ return false;
+ }
+
+ g_usbd_msc[busid].stage = MSC_DATA_OUT;
+ return true;
+}
+#endif
+
+static bool SCSI_processRead(uint8_t busid)
+{
+ uint32_t transfer_len;
+
+ USB_LOG_DBG("read lba:%d\r\n", g_usbd_msc[busid].start_sector);
+
+ transfer_len = MIN(g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN], CONFIG_USBDEV_MSC_MAX_BUFSIZE);
+
+ if (usbd_msc_sector_read(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, transfer_len) != 0) {
+ SCSI_SetSenseData(busid, SCSI_KCQHE_UREINRESERVEDAREA);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector += (transfer_len / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
+ g_usbd_msc[busid].nsectors -= (transfer_len / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
+ g_usbd_msc[busid].csw.dDataResidue -= transfer_len;
+
+ if (g_usbd_msc[busid].nsectors == 0) {
+ g_usbd_msc[busid].stage = MSC_SEND_CSW;
+ }
+
+ usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, transfer_len);
+
+ return true;
+}
+
+static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes)
+{
+ uint32_t data_len = 0;
+ USB_LOG_DBG("write lba:%d\r\n", g_usbd_msc[busid].start_sector);
+
+ if (usbd_msc_sector_write(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, nbytes) != 0) {
+ SCSI_SetSenseData(busid, SCSI_KCQHE_WRITEFAULT);
+ return false;
+ }
+
+ g_usbd_msc[busid].start_sector += (nbytes / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
+ g_usbd_msc[busid].nsectors -= (nbytes / g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN]);
+ g_usbd_msc[busid].csw.dDataResidue -= nbytes;
+
+ if (g_usbd_msc[busid].nsectors == 0) {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
+ } else {
+ data_len = MIN(g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN], CONFIG_USBDEV_MSC_MAX_BUFSIZE);
+ usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
+ }
+
+ return true;
+}
+
+static bool SCSI_CBWDecode(uint8_t busid, uint32_t nbytes)
+{
+ uint8_t *buf2send = g_usbd_msc[busid].block_buffer;
+ uint32_t len2send = 0;
+ bool ret = false;
+
+ if (nbytes != sizeof(struct CBW)) {
+ USB_LOG_ERR("size != sizeof(cbw)\r\n");
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ g_usbd_msc[busid].csw.dTag = g_usbd_msc[busid].cbw.dTag;
+ g_usbd_msc[busid].csw.dDataResidue = g_usbd_msc[busid].cbw.dDataLength;
+
+ if ((g_usbd_msc[busid].cbw.dSignature != MSC_CBW_Signature) || (g_usbd_msc[busid].cbw.bCBLength < 1) || (g_usbd_msc[busid].cbw.bCBLength > 16)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ } else {
+ USB_LOG_DBG("Decode CB:0x%02x\r\n", g_usbd_msc[busid].cbw.CB[0]);
+ switch (g_usbd_msc[busid].cbw.CB[0]) {
+ case SCSI_CMD_TESTUNITREADY:
+ ret = SCSI_testUnitReady(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_REQUESTSENSE:
+ ret = SCSI_requestSense(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_INQUIRY:
+ ret = SCSI_inquiry(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_STARTSTOPUNIT:
+ ret = SCSI_startStopUnit(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_PREVENTMEDIAREMOVAL:
+ ret = SCSI_preventAllowMediaRemoval(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_MODESENSE6:
+ ret = SCSI_modeSense6(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_MODESENSE10:
+ ret = SCSI_modeSense10(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_READFORMATCAPACITIES:
+ ret = SCSI_readFormatCapacity(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_READCAPACITY10:
+ ret = SCSI_readCapacity10(busid, &buf2send, &len2send);
+ break;
+ case SCSI_CMD_READ10:
+ ret = SCSI_read10(busid, NULL, 0);
+ break;
+ case SCSI_CMD_READ12:
+ ret = SCSI_read12(busid, NULL, 0);
+ break;
+ case SCSI_CMD_WRITE10:
+ ret = SCSI_write10(busid, NULL, 0);
+ break;
+ case SCSI_CMD_WRITE12:
+ ret = SCSI_write12(busid, NULL, 0);
+ break;
+ case SCSI_CMD_VERIFY10:
+ //ret = SCSI_verify10(NULL, 0);
+ ret = false;
+ break;
+
+ default:
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ USB_LOG_WRN("unsupported cmd:0x%02x\r\n", g_usbd_msc[busid].cbw.CB[0]);
+ ret = false;
+ break;
+ }
+ }
+ if (ret) {
+ if (g_usbd_msc[busid].stage == MSC_READ_CBW) {
+ if (len2send) {
+ USB_LOG_DBG("Send info len:%d\r\n", len2send);
+ usbd_msc_send_info(busid, buf2send, len2send);
+ } else {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
+ }
+ }
+ }
+ return ret;
+}
+
+void mass_storage_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ switch (g_usbd_msc[busid].stage) {
+ case MSC_READ_CBW:
+ if (SCSI_CBWDecode(busid, nbytes) == false) {
+ USB_LOG_ERR("Command:0x%02x decode err\r\n", g_usbd_msc[busid].cbw.CB[0]);
+ usbd_msc_bot_abort(busid);
+ return;
+ }
+ break;
+ case MSC_DATA_OUT:
+ switch (g_usbd_msc[busid].cbw.CB[0]) {
+ case SCSI_CMD_WRITE10:
+ case SCSI_CMD_WRITE12:
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ g_usbd_msc[busid].nbytes = nbytes;
+ usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_OUT);
+#else
+ if (SCSI_processWrite(busid, nbytes) == false) {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void mass_storage_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ switch (g_usbd_msc[busid].stage) {
+ case MSC_DATA_IN:
+ switch (g_usbd_msc[busid].cbw.CB[0]) {
+ case SCSI_CMD_READ10:
+ case SCSI_CMD_READ12:
+#ifdef CONFIG_USBDEV_MSC_THREAD
+ usb_osal_mq_send(g_usbd_msc[busid].usbd_msc_mq, MSC_DATA_IN);
+#else
+ if (SCSI_processRead(busid) == false) {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
+ return;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ break;
+ /*the device has to send a CSW*/
+ case MSC_SEND_CSW:
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
+ break;
+
+ /*the host has received the CSW*/
+ case MSC_WAIT_CSW:
+ g_usbd_msc[busid].stage = MSC_READ_CBW;
+ USB_LOG_DBG("Start reading cbw\r\n");
+ usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, (uint8_t *)&g_usbd_msc[busid].cbw, USB_SIZEOF_MSC_CBW);
+ break;
+
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_USBDEV_MSC_THREAD
+static void usbdev_msc_thread(void *argument)
+{
+ uintptr_t event;
+ int ret;
+ uint8_t busid = (uint8_t)argument;
+
+ while (1) {
+ ret = usb_osal_mq_recv(g_usbd_msc[busid].usbd_msc_mq, (uintptr_t *)&event, USB_OSAL_WAITING_FOREVER);
+ if (ret < 0) {
+ continue;
+ }
+ USB_LOG_DBG("%d\r\n", event);
+ if (event == MSC_DATA_OUT) {
+ if (SCSI_processWrite(busid, g_usbd_msc[busid].nbytes) == false) {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
+ }
+ } else if (event == MSC_DATA_IN) {
+ if (SCSI_processRead(busid) == false) {
+ usbd_msc_send_csw(busid, CSW_STATUS_CMD_FAILED); /* send fail status to host,and the host will retry*/
+ }
+ } else {
+ }
+ }
+}
+#endif
+
+struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface *intf, const uint8_t out_ep, const uint8_t in_ep)
+{
+ intf->class_interface_handler = msc_storage_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = msc_storage_notify_handler;
+
+ mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr = out_ep;
+ mass_ep_data[busid][MSD_OUT_EP_IDX].ep_cb = mass_storage_bulk_out;
+ mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr = in_ep;
+ mass_ep_data[busid][MSD_IN_EP_IDX].ep_cb = mass_storage_bulk_in;
+
+ usbd_add_endpoint(busid, &mass_ep_data[busid][MSD_OUT_EP_IDX]);
+ usbd_add_endpoint(busid, &mass_ep_data[busid][MSD_IN_EP_IDX]);
+
+ memset((uint8_t *)&g_usbd_msc[busid], 0, sizeof(struct usbd_msc_priv));
+
+ usdb_msc_set_max_lun(busid);
+ for (uint8_t i = 0u; i <= g_usbd_msc[busid].max_lun; i++) {
+ usbd_msc_get_cap(busid, i, &g_usbd_msc[busid].scsi_blk_nbr[i], &g_usbd_msc[busid].scsi_blk_size[i]);
+
+ if (g_usbd_msc[busid].scsi_blk_size[i] > CONFIG_USBDEV_MSC_MAX_BUFSIZE) {
+ USB_LOG_ERR("msc block buffer overflow\r\n");
+ return NULL;
+ }
+ }
+
+ return intf;
+}
+
+void usbd_msc_set_readonly(uint8_t busid, bool readonly)
+{
+ g_usbd_msc[busid].readonly = readonly;
+}
+
+bool usbd_msc_set_popup(uint8_t busid)
+{
+ return g_usbd_msc[busid].popup;
+}
diff --git a/components/drivers/usb/cherryusb/class/msc/usbd_msc.h b/components/drivers/usb/cherryusb/class/msc/usbd_msc.h
new file mode 100644
index 0000000000..c71f64a232
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usbd_msc.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ * Copyright (c) 2024, zhihong chen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_MSC_H
+#define USBD_MSC_H
+
+#include "usb_msc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init msc interface driver */
+struct usbd_interface *usbd_msc_init_intf(uint8_t busid, struct usbd_interface *intf,
+ const uint8_t out_ep,
+ const uint8_t in_ep);
+
+void usbd_msc_get_cap(uint8_t busid, uint8_t lun, uint32_t *block_num, uint32_t *block_size);
+int usbd_msc_sector_read(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
+int usbd_msc_sector_write(uint8_t busid, uint8_t lun, uint32_t sector, uint8_t *buffer, uint32_t length);
+
+void usbd_msc_set_readonly(uint8_t busid, bool readonly);
+bool usbd_msc_set_popup(uint8_t busid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_MSC_H */
diff --git a/components/drivers/usb/cherryusb/class/msc/usbh_msc.c b/components/drivers/usb/cherryusb/class/msc/usbh_msc.c
new file mode 100644
index 0000000000..71e8e53370
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usbh_msc.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_msc.h"
+#include "usb_scsi.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_msc"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/sd%c"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_msc_buf[64];
+
+static struct usbh_msc g_msc_class[CONFIG_USBHOST_MAX_MSC_CLASS];
+static uint32_t g_devinuse = 0;
+static struct usbh_msc_modeswitch_config *g_msc_modeswitch_config = NULL;
+
+static struct usbh_msc *usbh_msc_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_MSC_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_msc_class[devno], 0, sizeof(struct usbh_msc));
+ g_msc_class[devno].sdchar = 'a' + devno;
+ return &g_msc_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_msc_class_free(struct usbh_msc *msc_class)
+{
+ int devno = msc_class->sdchar - 'a';
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(msc_class, 0, sizeof(struct usbh_msc));
+}
+
+static int usbh_msc_get_maxlun(struct usbh_msc *msc_class, uint8_t *buffer)
+{
+ struct usb_setup_packet *setup = msc_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = MSC_REQUEST_GET_MAX_LUN;
+ setup->wValue = 0;
+ setup->wIndex = msc_class->intf;
+ setup->wLength = 1;
+
+ return usbh_control_transfer(msc_class->hport, setup, buffer);
+}
+
+static void usbh_msc_cbw_dump(struct CBW *cbw)
+{
+ int i;
+
+ USB_LOG_DBG("CBW:\r\n");
+ USB_LOG_DBG(" signature: 0x%08x\r\n", (unsigned int)cbw->dSignature);
+ USB_LOG_DBG(" tag: 0x%08x\r\n", (unsigned int)cbw->dTag);
+ USB_LOG_DBG(" datlen: 0x%08x\r\n", (unsigned int)cbw->dDataLength);
+ USB_LOG_DBG(" flags: 0x%02x\r\n", cbw->bmFlags);
+ USB_LOG_DBG(" lun: 0x%02x\r\n", cbw->bLUN);
+ USB_LOG_DBG(" cblen: 0x%02x\r\n", cbw->bCBLength);
+
+ USB_LOG_DBG("CB:\r\n");
+ for (i = 0; i < cbw->bCBLength; i += 8) {
+ USB_LOG_DBG(" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\r\n",
+ cbw->CB[i], cbw->CB[i + 1], cbw->CB[i + 2],
+ cbw->CB[i + 3], cbw->CB[i + 4], cbw->CB[i + 5],
+ cbw->CB[i + 6], cbw->CB[i + 7]);
+ }
+}
+
+static void usbh_msc_csw_dump(struct CSW *csw)
+{
+ USB_LOG_DBG("CSW:\r\n");
+ USB_LOG_DBG(" signature: 0x%08x\r\n", (unsigned int)csw->dSignature);
+ USB_LOG_DBG(" tag: 0x%08x\r\n", (unsigned int)csw->dTag);
+ USB_LOG_DBG(" residue: 0x%08x\r\n", (unsigned int)csw->dDataResidue);
+ USB_LOG_DBG(" status: 0x%02x\r\n", csw->bStatus);
+}
+
+static inline int usbh_msc_bulk_in_transfer(struct usbh_msc *msc_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &msc_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, msc_class->hport, msc_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+static inline int usbh_msc_bulk_out_transfer(struct usbh_msc *msc_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &msc_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, msc_class->hport, msc_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+static int usbh_bulk_cbw_csw_xfer(struct usbh_msc *msc_class, struct CBW *cbw, struct CSW *csw, uint8_t *buffer)
+{
+ int nbytes;
+
+ usbh_msc_cbw_dump(cbw);
+
+ /* Send the CBW */
+ nbytes = usbh_msc_bulk_out_transfer(msc_class, (uint8_t *)cbw, USB_SIZEOF_MSC_CBW, CONFIG_USBHOST_MSC_TIMEOUT);
+ if (nbytes < 0) {
+ USB_LOG_ERR("cbw transfer error\r\n");
+ goto __err_exit;
+ }
+
+ if (cbw->dDataLength != 0) {
+ if (cbw->CB[0] == SCSI_CMD_WRITE10) {
+ nbytes = usbh_msc_bulk_out_transfer(msc_class, buffer, cbw->dDataLength, CONFIG_USBHOST_MSC_TIMEOUT);
+ } else if (cbw->CB[0] == SCSI_CMD_READCAPACITY10) {
+ nbytes = usbh_msc_bulk_in_transfer(msc_class, buffer, cbw->dDataLength, CONFIG_USBHOST_MSC_TIMEOUT);
+ if (nbytes >= 0) {
+ /* Save the capacity information */
+ msc_class->blocknum = GET_BE32(&buffer[0]) + 1;
+ msc_class->blocksize = GET_BE32(&buffer[4]);
+ }
+ } else {
+ nbytes = usbh_msc_bulk_in_transfer(msc_class, buffer, cbw->dDataLength, CONFIG_USBHOST_MSC_TIMEOUT);
+ }
+
+ if (nbytes < 0) {
+ USB_LOG_ERR("msc data transfer error\r\n");
+ goto __err_exit;
+ }
+ }
+
+ /* Receive the CSW */
+ memset(csw, 0, USB_SIZEOF_MSC_CSW);
+ nbytes = usbh_msc_bulk_in_transfer(msc_class, (uint8_t *)csw, USB_SIZEOF_MSC_CSW, CONFIG_USBHOST_MSC_TIMEOUT);
+ if (nbytes < 0) {
+ USB_LOG_ERR("csw transfer error\r\n");
+ goto __err_exit;
+ }
+
+ usbh_msc_csw_dump(csw);
+
+ /* check csw status */
+ if (csw->dSignature != MSC_CSW_Signature) {
+ USB_LOG_ERR("csw signature error\r\n");
+ return -USB_ERR_INVAL;
+ }
+
+ if (csw->bStatus != 0) {
+ USB_LOG_ERR("csw bStatus %d\r\n", csw->bStatus);
+ return -USB_ERR_INVAL;
+ }
+__err_exit:
+ return nbytes < 0 ? (int)nbytes : 0;
+}
+
+static inline int usbh_msc_scsi_testunitready(struct usbh_msc *msc_class)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->bCBLength = SCSICMD_TESTUNITREADY_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_TESTUNITREADY;
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL);
+}
+
+static inline int usbh_msc_scsi_requestsense(struct usbh_msc *msc_class)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->bmFlags = 0x80;
+ cbw->dDataLength = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
+ cbw->bCBLength = SCSICMD_REQUESTSENSE_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_REQUESTSENSE;
+ cbw->CB[4] = SCSIRESP_FIXEDSENSEDATA_SIZEOF;
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf);
+}
+
+static inline int usbh_msc_scsi_inquiry(struct usbh_msc *msc_class)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->dDataLength = SCSIRESP_INQUIRY_SIZEOF;
+ cbw->bmFlags = 0x80;
+ cbw->bCBLength = SCSICMD_INQUIRY_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_INQUIRY;
+ cbw->CB[4] = SCSIRESP_INQUIRY_SIZEOF;
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf);
+}
+
+static inline int usbh_msc_scsi_readcapacity10(struct usbh_msc *msc_class)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->dDataLength = SCSIRESP_READCAPACITY10_SIZEOF;
+ cbw->bmFlags = 0x80;
+ cbw->bCBLength = SCSICMD_READCAPACITY10_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_READCAPACITY10;
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, g_msc_buf);
+}
+
+static inline void usbh_msc_modeswitch(struct usbh_msc *msc_class, const uint8_t *message)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+
+ memcpy(g_msc_buf, message, 31);
+
+ usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, NULL);
+}
+
+static int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ struct usbh_msc_modeswitch_config *config;
+
+ struct usbh_msc *msc_class = usbh_msc_class_alloc();
+ if (msc_class == NULL) {
+ USB_LOG_ERR("Fail to alloc msc_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ msc_class->hport = hport;
+ msc_class->intf = intf;
+
+ hport->config.intf[intf].priv = msc_class;
+
+ ret = usbh_msc_get_maxlun(msc_class, g_msc_buf);
+ if (ret < 0) {
+ return ret;
+ }
+
+ USB_LOG_INFO("Get max LUN:%u\r\n", g_msc_buf[0] + 1);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(msc_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(msc_class->bulkout, ep_desc);
+ }
+ }
+
+ if (g_msc_modeswitch_config) {
+ uint8_t num = 0;
+ while (1) {
+ config = &g_msc_modeswitch_config[num];
+ if (config && config->name) {
+ if ((hport->device_desc.idVendor == config->vid) &&
+ (hport->device_desc.idProduct == config->pid)) {
+ USB_LOG_INFO("%s usb_modeswitch enable\r\n", config->name);
+ usbh_msc_modeswitch(msc_class, config->message_content);
+ return 0;
+ }
+ num++;
+ } else {
+ break;
+ }
+ }
+ }
+
+ ret = usbh_msc_scsi_testunitready(msc_class);
+ if (ret < 0) {
+ ret = usbh_msc_scsi_requestsense(msc_class);
+ if (ret < 0) {
+ USB_LOG_ERR("Fail to scsi_testunitready\r\n");
+ return ret;
+ }
+ }
+
+ ret = usbh_msc_scsi_inquiry(msc_class);
+ if (ret < 0) {
+ USB_LOG_ERR("Fail to scsi_inquiry\r\n");
+ return ret;
+ }
+ ret = usbh_msc_scsi_readcapacity10(msc_class);
+ if (ret < 0) {
+ USB_LOG_ERR("Fail to scsi_readcapacity10\r\n");
+ return ret;
+ }
+
+ if (msc_class->blocksize > 0) {
+ USB_LOG_INFO("Capacity info:\r\n");
+ USB_LOG_INFO("Block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize);
+ } else {
+ USB_LOG_ERR("Invalid block size\r\n");
+ return -USB_ERR_RANGE;
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar);
+
+ USB_LOG_INFO("Register MSC Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_msc_run(msc_class);
+ return ret;
+}
+
+static int usbh_msc_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_msc *msc_class = (struct usbh_msc *)hport->config.intf[intf].priv;
+
+ if (msc_class) {
+ if (msc_class->bulkin) {
+ usbh_kill_urb(&msc_class->bulkin_urb);
+ }
+
+ if (msc_class->bulkout) {
+ usbh_kill_urb(&msc_class->bulkout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister MSC Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_msc_stop(msc_class);
+ }
+
+ usbh_msc_class_free(msc_class);
+ }
+
+ return ret;
+}
+
+
+int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->dDataLength = (msc_class->blocksize * nsectors);
+ cbw->bCBLength = SCSICMD_WRITE10_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_WRITE10;
+
+ SET_BE32(&cbw->CB[2], start_sector);
+ SET_BE16(&cbw->CB[7], nsectors);
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer);
+}
+
+int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors)
+{
+ struct CBW *cbw;
+
+ /* Construct the CBW */
+ cbw = (struct CBW *)g_msc_buf;
+ memset(cbw, 0, USB_SIZEOF_MSC_CBW);
+ cbw->dSignature = MSC_CBW_Signature;
+
+ cbw->dDataLength = (msc_class->blocksize * nsectors);
+ cbw->bmFlags = 0x80;
+ cbw->bCBLength = SCSICMD_READ10_SIZEOF;
+ cbw->CB[0] = SCSI_CMD_READ10;
+
+ SET_BE32(&cbw->CB[2], start_sector);
+ SET_BE16(&cbw->CB[7], nsectors);
+
+ return usbh_bulk_cbw_csw_xfer(msc_class, cbw, (struct CSW *)g_msc_buf, (uint8_t *)buffer);
+}
+
+void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config)
+{
+ if (config) {
+ g_msc_modeswitch_config = config;
+ } else {
+ g_msc_modeswitch_config = NULL;
+ }
+}
+
+__WEAK void usbh_msc_run(struct usbh_msc *msc_class)
+{
+}
+
+__WEAK void usbh_msc_stop(struct usbh_msc *msc_class)
+{
+}
+
+const struct usbh_class_driver msc_class_driver = {
+ .driver_name = "msc",
+ .connect = usbh_msc_connect,
+ .disconnect = usbh_msc_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info msc_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_MASS_STORAGE,
+ .subclass = MSC_SUBCLASS_SCSI,
+ .protocol = MSC_PROTOCOL_BULK_ONLY,
+ .id_table = NULL,
+ .class_driver = &msc_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/msc/usbh_msc.h b/components/drivers/usb/cherryusb/class/msc/usbh_msc.h
new file mode 100644
index 0000000000..50a61a9c11
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/msc/usbh_msc.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_MSC_H
+#define USBH_MSC_H
+
+#include "usb_msc.h"
+#include "usb_scsi.h"
+
+struct usbh_msc {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkin_urb; /* Bulk IN urb */
+ struct usbh_urb bulkout_urb; /* Bulk OUT urb */
+
+ uint8_t intf; /* Data interface number */
+ uint8_t sdchar;
+ uint32_t blocknum; /* Number of blocks on the USB mass storage device */
+ uint16_t blocksize; /* Block size of USB mass storage device */
+
+ void *user_data;
+};
+
+struct usbh_msc_modeswitch_config {
+ const char *name;
+ uint16_t vid; /* Vendor ID (for vendor/product specific devices) */
+ uint16_t pid; /* Product ID (for vendor/product specific devices) */
+ const uint8_t *message_content;
+};
+
+void usbh_msc_modeswitch_enable(struct usbh_msc_modeswitch_config *config);
+int usbh_msc_scsi_write10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors);
+int usbh_msc_scsi_read10(struct usbh_msc *msc_class, uint32_t start_sector, const uint8_t *buffer, uint32_t nsectors);
+
+void usbh_msc_run(struct usbh_msc *msc_class);
+void usbh_msc_stop(struct usbh_msc *msc_class);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_MSC_H */
diff --git a/components/drivers/usb/cherryusb/class/template/usb_xxx.h b/components/drivers/usb/cherryusb/class/template/usb_xxx.h
new file mode 100644
index 0000000000..d883c8b2e2
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/template/usb_xxx.h
@@ -0,0 +1,4 @@
+#ifndef _USB_XXX_H
+#define _USB_XXX_H
+
+#endif \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/template/usbd_xxx.c b/components/drivers/usb/cherryusb/class/template/usbd_xxx.c
new file mode 100644
index 0000000000..aa4bc01bcd
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/template/usbd_xxx.c
@@ -0,0 +1,39 @@
+#include "usbd_core.h"
+#include "usbd_xxx.h"
+
+static int xxx_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_WRN("XXX Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ switch (setup->bRequest) {
+ default:
+ USB_LOG_WRN("Unhandled XXX Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void xxx_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+struct usbd_interface *usbd_xxx_init_intf(uint8_t busid, struct usbd_interface *intf)
+{
+ intf->class_interface_handler = xxx_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = xxx_notify_handler;
+
+ return intf;
+}
diff --git a/components/drivers/usb/cherryusb/class/template/usbd_xxx.h b/components/drivers/usb/cherryusb/class/template/usbd_xxx.h
new file mode 100644
index 0000000000..8230c3021b
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/template/usbd_xxx.h
@@ -0,0 +1,16 @@
+#ifndef _USBD_XXX_H_
+#define _USBD_XXX_H_
+
+#include "usb_xxx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct usbd_interface *usbd_xxx_init_intf(uint8_t busid, struct usbd_interface *intf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _USBD_XXX_H_ */
diff --git a/components/drivers/usb/cherryusb/class/template/usbh_xxx.c b/components/drivers/usb/cherryusb/class/template/usbh_xxx.c
new file mode 100644
index 0000000000..22b3636d62
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/template/usbh_xxx.c
@@ -0,0 +1,97 @@
+#include "usbh_core.h"
+#include "usbh_xxx.h"
+
+#define DEV_FORMAT "/dev/xxx"
+
+#define CONFIG_USBHOST_MAX_CUSTOM_CLASS 1
+static struct usbh_xxx g_xxx_class[CONFIG_USBHOST_MAX_CUSTOM_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_xxx *usbh_xxx_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_CUSTOM_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_xxx_class[devno], 0, sizeof(struct usbh_xxx));
+ g_xxx_class[devno].minor = devno;
+ return &g_xxx_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_xxx_class_free(struct usbh_xxx *xxx_class)
+{
+ int devno = xxx_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(xxx_class, 0, sizeof(struct usbh_xxx));
+}
+
+static int usbh_xxx_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_xxx *xxx_class = usbh_xxx_class_alloc();
+ if (xxx_class == NULL) {
+ USB_LOG_ERR("Fail to alloc xxx_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ return ret;
+}
+
+
+static int usbh_xxx_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_xxx *xxx_class = (struct usbh_xxx *)hport->config.intf[intf].priv;
+
+ if (xxx_class) {
+ if (xxx_class->xxxin) {
+ usbh_kill_urb(&xxx_class->xxxin_urb);
+ }
+
+ if (xxx_class->xxxout) {
+ usbh_kill_urb(&xxx_class->xxxout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister xxx Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_xxx_stop(xxx_class);
+ }
+
+ usbh_xxx_class_free(xxx_class);
+ }
+
+ return ret;
+}
+
+__WEAK void usbh_xxx_run(struct usbh_xxx *xxx_class)
+{
+}
+
+__WEAK void usbh_xxx_stop(struct usbh_xxx *xxx_class)
+{
+}
+
+static const struct usbh_class_driver xxx_class_driver = {
+ .driver_name = "xxx",
+ .connect = usbh_xxx_connect,
+ .disconnect = usbh_xxx_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info xxx_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = 0,
+ .subclass = 0,
+ .protocol = 0,
+ .id_table = NULL,
+ .class_driver = &xxx_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/template/usbh_xxx.h b/components/drivers/usb/cherryusb/class/template/usbh_xxx.h
new file mode 100644
index 0000000000..0858f51d74
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/template/usbh_xxx.h
@@ -0,0 +1,22 @@
+#ifndef _USBH_XXX_H
+#define _USBH_XXX_H
+
+#include "usb_xxx.h"
+
+struct usbh_xxx {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *xxxin;
+ struct usb_endpoint_descriptor *xxxout;
+ struct usbh_urb xxxin_urb;
+ struct usbh_urb xxxout_urb;
+
+ uint8_t intf; /* interface number */
+ uint8_t minor;
+
+ void *user_data;
+};
+
+void usbh_xxx_run(struct usbh_xxx *xxx_class);
+void usbh_xxx_stop(struct usbh_xxx *xxx_class);
+
+#endif \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c b/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c
new file mode 100644
index 0000000000..62d78ec377
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.c
@@ -0,0 +1,774 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_asix.h"
+#include "usb_cdc.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "asix"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/asix"
+
+static struct usbh_asix g_asix_class;
+#define CONFIG_USBHOST_ASIX_ETH_MAX_SIZE (1514U + 8)
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_rx_buffer[CONFIG_USBHOST_ASIX_ETH_MAX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_tx_buffer[CONFIG_USBHOST_ASIX_ETH_MAX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_inttx_buffer[16];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_buf[32];
+
+#define ETH_ALEN 6
+
+#define PHY_MODE_MARVELL 0x0000
+#define MII_MARVELL_LED_CTRL 0x0018
+#define MII_MARVELL_STATUS 0x001b
+#define MII_MARVELL_CTRL 0x0014
+
+#define MARVELL_LED_MANUAL 0x0019
+
+#define MARVELL_STATUS_HWCFG 0x0004
+
+#define MARVELL_CTRL_TXDELAY 0x0002
+#define MARVELL_CTRL_RXDELAY 0x0080
+
+#define PHY_MODE_RTL8211CL 0x000C
+
+#define AX88772A_PHY14H 0x14
+#define AX88772A_PHY14H_DEFAULT 0x442C
+
+#define AX88772A_PHY15H 0x15
+#define AX88772A_PHY15H_DEFAULT 0x03C8
+
+#define AX88772A_PHY16H 0x16
+#define AX88772A_PHY16H_DEFAULT 0x4044
+
+#define SPEED_100 0
+#define SPEED_10 1
+
+static int usbh_asix_read_cmd(struct usbh_asix *asix_class,
+ uint8_t cmd,
+ uint16_t value,
+ uint16_t index,
+ void *data,
+ uint16_t size)
+{
+ struct usb_setup_packet *setup = asix_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = cmd;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = size;
+
+ ret = usbh_control_transfer(asix_class->hport, setup, g_asix_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(data, g_asix_buf, ret - 8);
+
+ return ret;
+}
+
+static int usbh_asix_write_cmd(struct usbh_asix *asix_class,
+ uint8_t cmd,
+ uint16_t value,
+ uint16_t index,
+ void *data,
+ uint16_t size)
+{
+ struct usb_setup_packet *setup = asix_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = cmd;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = size;
+
+ memcpy(g_asix_buf, data, size);
+
+ return usbh_control_transfer(asix_class->hport, setup, g_asix_buf);
+}
+
+static int usbh_asix_mdio_write(struct usbh_asix *asix_class, int phy_id, int loc, int val)
+{
+ uint8_t smsr;
+ uint16_t res = (uint16_t)val;
+ int ret;
+
+ for (uint8_t i = 0; i < 10; i++) {
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_SW_MII, 0, 0, NULL, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ usb_osal_msleep(1);
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &smsr, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (smsr & AX_HOST_EN) {
+ break;
+ }
+ }
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MII_REG, phy_id, loc, &res, 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_HW_MII, 0, 0, NULL, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static int usbh_asix_mdio_read(struct usbh_asix *asix_class, int phy_id, int loc)
+{
+ uint8_t smsr;
+ uint16_t res;
+ int ret;
+
+ for (uint8_t i = 0; i < 10; i++) {
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_SW_MII, 0, 0, NULL, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ usb_osal_msleep(1);
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &smsr, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (smsr & AX_HOST_EN) {
+ break;
+ }
+ }
+
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_MII_REG, phy_id, loc, &res, 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SET_HW_MII, 0, 0, NULL, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ return res;
+}
+
+static int usbh_asix_read_phy_addr(struct usbh_asix *asix_class, bool internal)
+{
+ int ret, offset;
+ uint8_t buf[2];
+
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_PHY_ID, 0, 0, buf, 2);
+ if (ret < 0) {
+ return ret;
+ }
+
+ offset = (internal ? 1 : 0);
+ ret = buf[offset];
+
+ USB_LOG_INFO("%s PHY address 0x%x\r\n", internal ? "internal" : "external", ret);
+
+ return ret;
+}
+
+static int usbh_asix_sw_reset(struct usbh_asix *asix_class, uint8_t flags)
+{
+ int ret;
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_RESET, flags, 0, NULL, 0);
+ if (ret < 0)
+ USB_LOG_ERR("Failed to send software reset: %d\r\n", ret);
+
+ return ret;
+}
+
+static uint16_t usbh_asix_read_rx_ctl(struct usbh_asix *asix_class)
+{
+ uint16_t v;
+ int ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_RX_CTL, 0, 0, &v, 2);
+ if (ret < 0) {
+ return ret;
+ }
+ return v;
+}
+
+static int usbh_asix_write_rx_ctl(struct usbh_asix *asix_class, uint16_t mode)
+{
+ int ret;
+
+ USB_LOG_DBG("asix_write_rx_ctl() - mode = 0x%04x\r\n", mode);
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_RX_CTL, mode, 0, NULL, 0);
+ if (ret < 0)
+ USB_LOG_ERR("Failed to write RX_CTL mode to 0x%04x: %02x\r\n",
+ mode, ret);
+
+ return ret;
+}
+
+static uint16_t usbh_asix_read_medium_status(struct usbh_asix *asix_class)
+{
+ uint16_t v;
+ int ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_MEDIUM_STATUS, 0, 0, &v, 2);
+
+ if (ret < 0) {
+ USB_LOG_ERR("Error reading Medium Status register: %02x\r\n",
+ ret);
+ return ret; /* TODO: callers not checking for error ret */
+ }
+
+ return v;
+}
+
+static int usbh_asix_write_medium_mode(struct usbh_asix *asix_class, uint16_t mode)
+{
+ int ret;
+
+ USB_LOG_DBG("asix_write_medium_mode() - mode = 0x%04x\r\n", mode);
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, NULL, 0);
+ if (ret < 0)
+ USB_LOG_ERR("Failed to write Medium Mode mode to 0x%04x: %02x\r\n",
+ mode, ret);
+
+ return ret;
+}
+
+static int usbh_asix_write_gpio(struct usbh_asix *asix_class, uint16_t value, int sleep)
+{
+ int ret;
+
+ USB_LOG_DBG("asix_write_gpio() - value = 0x%04x\r\n", value);
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_GPIOS, value, 0, NULL, 0);
+ if (ret < 0)
+ USB_LOG_ERR("Failed to write GPIO value 0x%04x: %d\r\n",
+ value, ret);
+
+ if (sleep)
+ usb_osal_msleep(sleep);
+
+ return ret;
+}
+
+/*
+ * AX88772 & AX88178 have a 16-bit RX_CTL value
+ */
+static void usbh_asix_set_multicast(struct usbh_asix *asix_class)
+{
+ uint16_t rx_ctl = AX_DEFAULT_RX_CTL | AX_RX_CTL_AM;
+ const uint8_t multi_filter[] = { 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x40 };
+
+ usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_MULTI_FILTER, 0, 0, (uint8_t *)multi_filter, AX_MCAST_FILTER_SIZE);
+ usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, NULL, 0);
+}
+
+static int usbh_ax88772_hw_reset(struct usbh_asix *asix_class)
+{
+ uint16_t rx_ctl;
+ int ret;
+
+ ret = usbh_asix_write_gpio(asix_class, AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_PHY_SELECT, asix_class->embd_phy,
+ 0, NULL, 0);
+ if (ret < 0) {
+ USB_LOG_ERR("Select PHY #1 failed: %d\r\n", ret);
+ goto out;
+ }
+
+ if (asix_class->embd_phy) {
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD);
+ if (ret < 0)
+ goto out;
+
+ usb_osal_msleep(10);
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_CLEAR);
+ if (ret < 0)
+ goto out;
+
+ usb_osal_msleep(60);
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL | AX_SWRESET_PRL);
+ if (ret < 0)
+ goto out;
+ } else {
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD | AX_SWRESET_PRL);
+ if (ret < 0)
+ goto out;
+ }
+
+ usb_osal_msleep(150);
+
+ ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_write_medium_mode(asix_class, AX88772_MEDIUM_DEFAULT);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_IPG0,
+ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
+ AX88772_IPG2_DEFAULT, NULL, 0);
+ if (ret < 0) {
+ USB_LOG_ERR("Write IPG,IPG1,IPG2 failed: %d\r\n", ret);
+ goto out;
+ }
+
+ /* Rewrite MAC address */
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_NODE_ID, 0, 0, asix_class->mac, ETH_ALEN);
+ if (ret < 0)
+ goto out;
+
+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */
+ ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
+ goto out;
+
+ rx_ctl = usbh_asix_read_rx_ctl(asix_class);
+ USB_LOG_INFO("RX_CTL is 0x%04x after all initializations\r\n",
+ rx_ctl);
+
+ rx_ctl = usbh_asix_read_medium_status(asix_class);
+ USB_LOG_INFO("Medium Status is 0x%04x after all initializations\r\n",
+ rx_ctl);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int usbh_ax88772a_hw_reset(struct usbh_asix *asix_class)
+{
+ uint16_t rx_ctl, phy14h, phy15h, phy16h;
+ int ret;
+
+ ret = usbh_asix_write_gpio(asix_class, AX_GPIO_RSE, 5);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_SW_PHY_SELECT, asix_class->embd_phy | AX_PHYSEL_SSEN, 0, NULL, 0);
+ if (ret < 0) {
+ USB_LOG_ERR("Select PHY #1 failed: %d\r\n", ret);
+ goto out;
+ }
+ usb_osal_msleep(10);
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPPD | AX_SWRESET_IPRL);
+ if (ret < 0)
+ goto out;
+
+ usb_osal_msleep(10);
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL);
+ if (ret < 0)
+ goto out;
+
+ usb_osal_msleep(160);
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_CLEAR);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_sw_reset(asix_class, AX_SWRESET_IPRL);
+ if (ret < 0)
+ goto out;
+
+ usb_osal_msleep(200);
+
+ if (asix_class->chipcode == AX_AX88772B_CHIPCODE) {
+ ret = usbh_asix_write_cmd(asix_class, AX_QCTCTRL, 0x8000, 0x8001, NULL, 0);
+ if (ret < 0) {
+ USB_LOG_ERR("Write BQ setting failed: %d\r\n", ret);
+ goto out;
+ }
+ } else if (asix_class->chipcode == AX_AX88772A_CHIPCODE) {
+ /* Check if the PHY registers have default settings */
+ phy14h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
+ AX88772A_PHY14H);
+ phy15h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
+ AX88772A_PHY15H);
+ phy16h = usbh_asix_mdio_read(asix_class, asix_class->phy_addr,
+ AX88772A_PHY16H);
+
+ USB_LOG_DBG("772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\r\n",
+ phy14h, phy15h, phy16h);
+
+ /* Restore PHY registers default setting if not */
+ if (phy14h != AX88772A_PHY14H_DEFAULT)
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
+ AX88772A_PHY14H,
+ AX88772A_PHY14H_DEFAULT);
+ if (phy15h != AX88772A_PHY15H_DEFAULT)
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
+ AX88772A_PHY15H,
+ AX88772A_PHY15H_DEFAULT);
+ if (phy16h != AX88772A_PHY16H_DEFAULT)
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr,
+ AX88772A_PHY16H,
+ AX88772A_PHY16H_DEFAULT);
+ }
+
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_IPG0,
+ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
+ AX88772_IPG2_DEFAULT, NULL, 0);
+ if (ret < 0) {
+ USB_LOG_ERR("Write IPG,IPG1,IPG2 failed: %d\r\n", ret);
+ goto out;
+ }
+
+ /* Rewrite MAC address */
+ ret = usbh_asix_write_cmd(asix_class, AX_CMD_WRITE_NODE_ID, 0, 0, asix_class->mac, ETH_ALEN);
+ if (ret < 0)
+ goto out;
+
+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */
+ ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
+ goto out;
+
+ ret = usbh_asix_write_medium_mode(asix_class, AX88772_MEDIUM_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ /* Set RX_CTL to default values with 2k buffer, and enable cactus */
+ ret = usbh_asix_write_rx_ctl(asix_class, AX_DEFAULT_RX_CTL);
+ if (ret < 0)
+ goto out;
+
+ rx_ctl = usbh_asix_read_rx_ctl(asix_class);
+ USB_LOG_INFO("RX_CTL is 0x%04x after all initializations\r\n", rx_ctl);
+
+ rx_ctl = usbh_asix_read_medium_status(asix_class);
+ USB_LOG_INFO("Medium Status is 0x%04x after all initializations\r\n", rx_ctl);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void usbh_ax88772_mac_link_down(struct usbh_asix *asix_class)
+{
+ usbh_asix_write_medium_mode(asix_class, 0);
+}
+
+static void usbh_ax88772_mac_link_up(struct usbh_asix *asix_class, int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ uint16_t m = AX_MEDIUM_AC | AX_MEDIUM_RE;
+
+ m |= duplex ? AX_MEDIUM_FD : 0;
+
+ switch (speed) {
+ case SPEED_100:
+ m |= AX_MEDIUM_PS;
+ break;
+ case SPEED_10:
+ break;
+ default:
+ return;
+ }
+
+ if (tx_pause)
+ m |= AX_MEDIUM_TFC;
+
+ if (rx_pause)
+ m |= AX_MEDIUM_RFC;
+
+ usbh_asix_write_medium_mode(asix_class, m);
+}
+
+static int usbh_asix_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+
+ struct usbh_asix *asix_class = &g_asix_class;
+
+ memset(asix_class, 0, sizeof(struct usbh_asix));
+
+ asix_class->hport = hport;
+ asix_class->intf = intf;
+
+ hport->config.intf[intf].priv = asix_class;
+
+ if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x772b)) {
+ asix_class->name = "ASIX AX88772B";
+ } else if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x7720)) {
+ asix_class->name = "ASIX AX88772";
+ } else if ((hport->device_desc.idVendor == 0x0b95) && (hport->device_desc.idProduct == 0x1780)) {
+ asix_class->name = "ASIX AX88178";
+ }
+
+ for (uint8_t i = 0; i < (ETH_ALEN >> 1); i++) {
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_READ_EEPROM,
+ 0x04 + i, 0, &asix_class->mac[i * 2], 2);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ USB_LOG_INFO("asix MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
+ asix_class->mac[0],
+ asix_class->mac[1],
+ asix_class->mac[2],
+ asix_class->mac[3],
+ asix_class->mac[4],
+ asix_class->mac[5]);
+
+ ret = usbh_asix_read_phy_addr(asix_class, true);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to read phy addr: %d\r\n", ret);
+ return ret;
+ }
+ asix_class->phy_addr = ret;
+ asix_class->embd_phy = ((ret & 0x1f) == AX_EMBD_PHY_ADDR);
+
+ ret = usbh_asix_read_cmd(asix_class, AX_CMD_STATMNGSTS_REG, 0, 0, &asix_class->chipcode, 1);
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to read STATMNGSTS_REG: %d\r\n", ret);
+ return ret;
+ }
+
+ asix_class->chipcode &= AX_CHIPCODE_MASK;
+ USB_LOG_INFO("asix chipcode 0x%x\r\n", asix_class->chipcode);
+
+ if (asix_class->chipcode == AX_AX88772_CHIPCODE) {
+ usbh_ax88772_hw_reset(asix_class);
+ } else {
+ usbh_ax88772a_hw_reset(asix_class);
+ }
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(asix_class->intin, ep_desc);
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+ } else {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(asix_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(asix_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ if (asix_class->chipcode == AX_AX88772B_CHIPCODE) {
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
+
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x8200);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
+
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3900);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
+
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3100);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 4);
+
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 4, 0x01e1);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 1);
+
+ usbh_asix_mdio_write(asix_class, asix_class->phy_addr, 0, 0x3300);
+ usbh_asix_mdio_read(asix_class, asix_class->phy_addr, 0);
+ }
+
+ USB_LOG_INFO("Init %s done\r\n", asix_class->name);
+
+ memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
+
+ USB_LOG_INFO("Register ASIX Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_asix_run(asix_class);
+ return ret;
+}
+
+static int usbh_asix_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_asix *asix_class = (struct usbh_asix *)hport->config.intf[intf].priv;
+
+ if (asix_class) {
+ if (asix_class->bulkin) {
+ usbh_kill_urb(&asix_class->bulkin_urb);
+ }
+
+ if (asix_class->bulkout) {
+ usbh_kill_urb(&asix_class->bulkout_urb);
+ }
+
+ if (asix_class->intin) {
+ usbh_kill_urb(&asix_class->intin_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister ASIX Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_asix_stop(asix_class);
+ }
+
+ memset(asix_class, 0, sizeof(struct usbh_asix));
+ }
+
+ return ret;
+}
+
+int usbh_asix_get_connect_status(struct usbh_asix *asix_class)
+{
+ int ret;
+
+ usbh_int_urb_fill(&asix_class->intin_urb, asix_class->hport, asix_class->intin, g_asix_inttx_buffer, 8, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&asix_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (g_asix_inttx_buffer[1] == 0x00) {
+ if (g_asix_inttx_buffer[2] & 0x01) {
+ asix_class->connect_status = true;
+ usbh_ax88772_mac_link_up(asix_class, SPEED_100, 1, 1, 1);
+ usbh_asix_set_multicast(asix_class);
+ } else {
+ asix_class->connect_status = false;
+ usbh_ax88772_mac_link_down(asix_class);
+ }
+ }
+ return 0;
+}
+
+void usbh_asix_rx_thread(void *argument)
+{
+ uint32_t g_asix_rx_length;
+ int ret;
+ uint16_t len;
+ uint16_t len_crc;
+
+ USB_LOG_INFO("Create asix rx thread\r\n");
+ // clang-format off
+find_class:
+ // clang-format on
+ g_asix_class.connect_status = false;
+ if (usbh_find_class_instance("/dev/asix") == NULL) {
+ goto delete;
+ }
+
+ while (g_asix_class.connect_status == false) {
+ ret = usbh_asix_get_connect_status(&g_asix_class);
+ if (ret < 0) {
+ usb_osal_msleep(100);
+ goto find_class;
+ }
+ }
+
+ g_asix_rx_length = 0;
+ while (1) {
+ usbh_bulk_urb_fill(&g_asix_class.bulkin_urb, g_asix_class.hport, g_asix_class.bulkin, &g_asix_rx_buffer[g_asix_rx_length], CONFIG_USBHOST_ASIX_ETH_MAX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_asix_class.bulkin_urb);
+ if (ret < 0) {
+ goto find_class;
+ }
+
+ g_asix_rx_length += g_asix_class.bulkin_urb.actual_length;
+
+ if (g_asix_rx_length % USB_GET_MAXPACKETSIZE(g_asix_class.bulkin->wMaxPacketSize)) {
+ len = ((uint16_t)g_asix_rx_buffer[0] | ((uint16_t)(g_asix_rx_buffer[1]) << 8)) & 0x7ff;
+ len_crc = g_asix_rx_buffer[2] | ((uint16_t)(g_asix_rx_buffer[3]) << 8);
+
+ if (len != (~len_crc & 0x7ff)) {
+ USB_LOG_ERR("asix rx header error\r\n");
+ continue;
+ }
+
+ USB_LOG_DBG("rxlen:%d\r\n", g_asix_rx_length);
+
+ uint8_t *buf = (uint8_t *)&g_asix_rx_buffer[4];
+ usbh_asix_eth_input(buf, len);
+ g_asix_rx_length = 0;
+ } else {
+ if (g_asix_rx_length > CONFIG_USBHOST_ASIX_ETH_MAX_SIZE) {
+ USB_LOG_ERR("Rx packet is overflow\r\n");
+ g_asix_rx_length = 0;
+ }
+ }
+ }
+ // clang-format off
+delete:
+ USB_LOG_INFO("Delete asix rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+int usbh_asix_eth_output(uint8_t *buf, uint32_t buflen)
+{
+ uint16_t actual_len;
+ uint8_t *buffer;
+
+ if (g_asix_class.connect_status == false) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ buffer = &g_asix_tx_buffer[4];
+ memcpy(buffer, buf, buflen);
+
+ g_asix_tx_buffer[0] = buflen & 0xff;
+ g_asix_tx_buffer[1] = (buflen >> 8) & 0xff;
+ g_asix_tx_buffer[2] = ~g_asix_tx_buffer[0];
+ g_asix_tx_buffer[3] = ~g_asix_tx_buffer[1];
+
+ if (!(buflen + 4) % USB_GET_MAXPACKETSIZE(g_asix_class.bulkout->wMaxPacketSize)) {
+ USB_LOG_DBG("txlen:%d\r\n", buflen + 8);
+ g_asix_tx_buffer[buflen + 4 + 0] = 0x00;
+ g_asix_tx_buffer[buflen + 4 + 1] = 0x00;
+ g_asix_tx_buffer[buflen + 4 + 2] = 0xff;
+ g_asix_tx_buffer[buflen + 4 + 3] = 0xff;
+ actual_len = buflen + 8;
+ } else {
+ USB_LOG_DBG("txlen:%d\r\n", buflen + 4);
+ actual_len = buflen + 4;
+ }
+
+ usbh_bulk_urb_fill(&g_asix_class.bulkout_urb, g_asix_class.hport, g_asix_class.bulkout, g_asix_tx_buffer, actual_len, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ return usbh_submit_urb(&g_asix_class.bulkout_urb);
+}
+
+__WEAK void usbh_asix_run(struct usbh_asix *asix_class)
+{
+}
+
+__WEAK void usbh_asix_stop(struct usbh_asix *asix_class)
+{
+}
+
+static const uint16_t asix_id_table[][2] = {
+ { 0x0B95, 0x772B },
+ { 0x0B95, 0x7720 },
+ { 0, 0 },
+};
+
+static const struct usbh_class_driver asix_class_driver = {
+ .driver_name = "asix",
+ .connect = usbh_asix_connect,
+ .disconnect = usbh_asix_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info asix_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = asix_id_table,
+ .class_driver = &asix_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.h b/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.h
new file mode 100644
index 0000000000..9f34e570a6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/net/usbh_asix.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_ASIX_H
+#define USBH_ASIX_H
+
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+#define AX_CMD_SET_SW_MII 0x06
+#define AX_CMD_READ_MII_REG 0x07
+#define AX_CMD_WRITE_MII_REG 0x08
+#define AX_CMD_STATMNGSTS_REG 0x09
+#define AX_CMD_SET_HW_MII 0x0a
+#define AX_CMD_READ_EEPROM 0x0b
+#define AX_CMD_WRITE_EEPROM 0x0c
+#define AX_CMD_WRITE_ENABLE 0x0d
+#define AX_CMD_WRITE_DISABLE 0x0e
+#define AX_CMD_READ_RX_CTL 0x0f
+#define AX_CMD_WRITE_RX_CTL 0x10
+#define AX_CMD_READ_IPG012 0x11
+#define AX_CMD_WRITE_IPG0 0x12
+#define AX_CMD_WRITE_IPG1 0x13
+#define AX_CMD_READ_NODE_ID 0x13
+#define AX_CMD_WRITE_NODE_ID 0x14
+#define AX_CMD_WRITE_IPG2 0x14
+#define AX_CMD_WRITE_MULTI_FILTER 0x16
+#define AX88172_CMD_READ_NODE_ID 0x17
+#define AX_CMD_READ_PHY_ID 0x19
+#define AX_CMD_READ_MEDIUM_STATUS 0x1a
+#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
+#define AX_CMD_READ_MONITOR_MODE 0x1c
+#define AX_CMD_WRITE_MONITOR_MODE 0x1d
+#define AX_CMD_READ_GPIOS 0x1e
+#define AX_CMD_WRITE_GPIOS 0x1f
+#define AX_CMD_SW_RESET 0x20
+#define AX_CMD_SW_PHY_STATUS 0x21
+#define AX_CMD_SW_PHY_SELECT 0x22
+#define AX_QCTCTRL 0x2A
+
+#define AX_CHIPCODE_MASK 0x70
+#define AX_AX88772_CHIPCODE 0x00
+#define AX_AX88772A_CHIPCODE 0x10
+#define AX_AX88772B_CHIPCODE 0x20
+#define AX_HOST_EN 0x01
+
+#define AX_PHYSEL_PSEL 0x01
+#define AX_PHYSEL_SSMII 0
+#define AX_PHYSEL_SSEN 0x10
+
+#define AX_PHY_SELECT_MASK (BIT(3) | BIT(2))
+#define AX_PHY_SELECT_INTERNAL 0
+#define AX_PHY_SELECT_EXTERNAL BIT(2)
+
+#define AX_MONITOR_MODE 0x01
+#define AX_MONITOR_LINK 0x02
+#define AX_MONITOR_MAGIC 0x04
+#define AX_MONITOR_HSFS 0x10
+
+/* AX88172 Medium Status Register values */
+#define AX88172_MEDIUM_FD 0x02
+#define AX88172_MEDIUM_TX 0x04
+#define AX88172_MEDIUM_FC 0x10
+#define AX88172_MEDIUM_DEFAULT \
+ (AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC)
+
+#define AX_MCAST_FILTER_SIZE 8
+#define AX_MAX_MCAST 64
+
+#define AX_SWRESET_CLEAR 0x00
+#define AX_SWRESET_RR 0x01
+#define AX_SWRESET_RT 0x02
+#define AX_SWRESET_PRTE 0x04
+#define AX_SWRESET_PRL 0x08
+#define AX_SWRESET_BZ 0x10
+#define AX_SWRESET_IPRL 0x20
+#define AX_SWRESET_IPPD 0x40
+
+#define AX88772_IPG0_DEFAULT 0x15
+#define AX88772_IPG1_DEFAULT 0x0c
+#define AX88772_IPG2_DEFAULT 0x12
+
+/* AX88772 & AX88178 Medium Mode Register */
+#define AX_MEDIUM_PF 0x0080
+#define AX_MEDIUM_JFE 0x0040
+#define AX_MEDIUM_TFC 0x0020
+#define AX_MEDIUM_RFC 0x0010
+#define AX_MEDIUM_ENCK 0x0008
+#define AX_MEDIUM_AC 0x0004
+#define AX_MEDIUM_FD 0x0002
+#define AX_MEDIUM_GM 0x0001
+#define AX_MEDIUM_SM 0x1000
+#define AX_MEDIUM_SBP 0x0800
+#define AX_MEDIUM_PS 0x0200
+#define AX_MEDIUM_RE 0x0100
+
+#define AX88178_MEDIUM_DEFAULT \
+ (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
+ AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
+ AX_MEDIUM_RE)
+
+#define AX88772_MEDIUM_DEFAULT \
+ (AX_MEDIUM_FD | AX_MEDIUM_PS | \
+ AX_MEDIUM_AC | AX_MEDIUM_RE)
+
+/* AX88772 & AX88178 RX_CTL values */
+#define AX_RX_CTL_SO 0x0080
+#define AX_RX_CTL_AP 0x0020
+#define AX_RX_CTL_AM 0x0010
+#define AX_RX_CTL_AB 0x0008
+#define AX_RX_CTL_SEP 0x0004
+#define AX_RX_CTL_AMALL 0x0002
+#define AX_RX_CTL_PRO 0x0001
+#define AX_RX_CTL_MFB_2048 0x0000
+#define AX_RX_CTL_MFB_4096 0x0100
+#define AX_RX_CTL_MFB_8192 0x0200
+#define AX_RX_CTL_MFB_16384 0x0300
+
+#define AX_DEFAULT_RX_CTL (AX_RX_CTL_SO | AX_RX_CTL_AB)
+
+/* GPIO 0 .. 2 toggles */
+#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */
+#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */
+#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */
+#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */
+#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */
+#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */
+#define AX_GPIO_RESERVED 0x40 /* Reserved */
+#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
+
+#define AX_EEPROM_MAGIC 0xdeadbeef
+#define AX_EEPROM_LEN 0x200
+
+#define AX_EMBD_PHY_ADDR 0x10
+
+struct usbh_asix {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+ struct usbh_urb intin_urb;
+
+ uint8_t intf;
+ char *name;
+ uint8_t phy_addr;
+ uint8_t embd_phy;
+ uint8_t chipcode;
+ uint16_t mac_capabilities;
+
+ bool connect_status;
+ uint8_t mac[6];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_asix_get_connect_status(struct usbh_asix *asix_class);
+
+void usbh_asix_run(struct usbh_asix *asix_class);
+void usbh_asix_stop(struct usbh_asix *asix_class);
+
+int usbh_asix_eth_output(uint8_t *buf, uint32_t buflen);
+void usbh_asix_eth_input(uint8_t *buf, uint32_t buflen);
+void usbh_asix_rx_thread(void *argument);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_ASIX_H */ \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c b/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c
new file mode 100644
index 0000000000..3bd68a1dfd
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.c
@@ -0,0 +1,2249 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_rtl8152.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "rtl8152"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/rtl8152"
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_rx_buffer[CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_tx_buffer[CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_inttx_buffer[2];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_buf[32];
+
+static struct usbh_rtl8152 g_rtl8152_class;
+
+#define RTL8152_REQ_GET_REGS 0x05
+#define RTL8152_REQ_SET_REGS 0x05
+
+#define BYTE_EN_DWORD 0xff
+#define BYTE_EN_WORD 0x33
+#define BYTE_EN_BYTE 0x11
+#define BYTE_EN_SIX_BYTES 0x3f
+#define BYTE_EN_START_MASK 0x0f
+#define BYTE_EN_END_MASK 0xf0
+
+#define MCU_TYPE_PLA 0x0100
+#define MCU_TYPE_USB 0x0000
+
+/* Define these values to match your device */
+#define VENDOR_ID_REALTEK 0x0bda
+#define VENDOR_ID_MICROSOFT 0x045e
+#define VENDOR_ID_SAMSUNG 0x04e8
+#define VENDOR_ID_LENOVO 0x17ef
+#define VENDOR_ID_LINKSYS 0x13b1
+#define VENDOR_ID_NVIDIA 0x0955
+#define VENDOR_ID_TPLINK 0x2357
+#define VENDOR_ID_DLINK 0x2001
+#define VENDOR_ID_ASUS 0x0b05
+
+#define R8152_PHY_ID 32
+
+#define PLA_IDR 0xc000
+#define PLA_RCR 0xc010
+#define PLA_RCR1 0xc012
+#define PLA_RMS 0xc016
+#define PLA_RXFIFO_CTRL0 0xc0a0
+#define PLA_RXFIFO_FULL 0xc0a2
+#define PLA_RXFIFO_CTRL1 0xc0a4
+#define PLA_RX_FIFO_FULL 0xc0a6
+#define PLA_RXFIFO_CTRL2 0xc0a8
+#define PLA_RX_FIFO_EMPTY 0xc0aa
+#define PLA_DMY_REG0 0xc0b0
+#define PLA_FMC 0xc0b4
+#define PLA_CFG_WOL 0xc0b6
+#define PLA_TEREDO_CFG 0xc0bc
+#define PLA_TEREDO_WAKE_BASE 0xc0c4
+#define PLA_MAR 0xcd00
+#define PLA_BACKUP 0xd000
+#define PLA_BDC_CR 0xd1a0
+#define PLA_TEREDO_TIMER 0xd2cc
+#define PLA_REALWOW_TIMER 0xd2e8
+#define PLA_UPHY_TIMER 0xd388
+#define PLA_SUSPEND_FLAG 0xd38a
+#define PLA_INDICATE_FALG 0xd38c
+#define PLA_MACDBG_PRE 0xd38c /* RTL_VER_04 only */
+#define PLA_MACDBG_POST 0xd38e /* RTL_VER_04 only */
+#define PLA_EXTRA_STATUS 0xd398
+#define PLA_GPHY_CTRL 0xd3ae
+#define PLA_POL_GPIO_CTRL 0xdc6a
+#define PLA_EFUSE_DATA 0xdd00
+#define PLA_EFUSE_CMD 0xdd02
+#define PLA_LEDSEL 0xdd90
+#define PLA_LED_FEATURE 0xdd92
+#define PLA_PHYAR 0xde00
+#define PLA_BOOT_CTRL 0xe004
+#define PLA_LWAKE_CTRL_REG 0xe007
+#define PLA_GPHY_INTR_IMR 0xe022
+#define PLA_EEE_CR 0xe040
+#define PLA_EEE_TXTWSYS 0xe04c
+#define PLA_EEE_TXTWSYS_2P5G 0xe058
+#define PLA_EEEP_CR 0xe080
+#define PLA_MAC_PWR_CTRL 0xe0c0
+#define PLA_MAC_PWR_CTRL2 0xe0ca
+#define PLA_MAC_PWR_CTRL3 0xe0cc
+#define PLA_MAC_PWR_CTRL4 0xe0ce
+#define PLA_WDT6_CTRL 0xe428
+#define PLA_TCR0 0xe610
+#define PLA_TCR1 0xe612
+#define PLA_MTPS 0xe615
+#define PLA_TXFIFO_CTRL 0xe618
+#define PLA_TXFIFO_FULL 0xe61a
+#define PLA_RSTTALLY 0xe800
+#define PLA_CR 0xe813
+#define PLA_CRWECR 0xe81c
+#define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */
+#define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */
+#define PLA_CONFIG5 0xe822
+#define PLA_PHY_PWR 0xe84c
+#define PLA_OOB_CTRL 0xe84f
+#define PLA_CPCR 0xe854
+#define PLA_MISC_0 0xe858
+#define PLA_MISC_1 0xe85a
+#define PLA_OCP_GPHY_BASE 0xe86c
+#define PLA_TALLYCNT 0xe890
+#define PLA_SFF_STS_7 0xe8de
+#define PLA_PHYSTATUS 0xe908
+#define PLA_CONFIG6 0xe90a /* CONFIG6 */
+#define PLA_USB_CFG 0xe952
+#define PLA_BP_BA 0xfc26
+#define PLA_BP_0 0xfc28
+#define PLA_BP_1 0xfc2a
+#define PLA_BP_2 0xfc2c
+#define PLA_BP_3 0xfc2e
+#define PLA_BP_4 0xfc30
+#define PLA_BP_5 0xfc32
+#define PLA_BP_6 0xfc34
+#define PLA_BP_7 0xfc36
+#define PLA_BP_EN 0xfc38
+
+#define USB_USB2PHY 0xb41e
+#define USB_SSPHYLINK1 0xb426
+#define USB_SSPHYLINK2 0xb428
+#define USB_L1_CTRL 0xb45e
+#define USB_U2P3_CTRL 0xb460
+#define USB_CSR_DUMMY1 0xb464
+#define USB_CSR_DUMMY2 0xb466
+#define USB_DEV_STAT 0xb808
+#define USB_CONNECT_TIMER 0xcbf8
+#define USB_MSC_TIMER 0xcbfc
+#define USB_BURST_SIZE 0xcfc0
+#define USB_FW_FIX_EN0 0xcfca
+#define USB_FW_FIX_EN1 0xcfcc
+#define USB_LPM_CONFIG 0xcfd8
+#define USB_ECM_OPTION 0xcfee
+#define USB_CSTMR 0xcfef /* RTL8153A */
+#define USB_MISC_2 0xcfff
+#define USB_ECM_OP 0xd26b
+#define USB_GPHY_CTRL 0xd284
+#define USB_SPEED_OPTION 0xd32a
+#define USB_FW_CTRL 0xd334 /* RTL8153B */
+#define USB_FC_TIMER 0xd340
+#define USB_USB_CTRL 0xd406
+#define USB_PHY_CTRL 0xd408
+#define USB_TX_AGG 0xd40a
+#define USB_RX_BUF_TH 0xd40c
+#define USB_USB_TIMER 0xd428
+#define USB_RX_EARLY_TIMEOUT 0xd42c
+#define USB_RX_EARLY_SIZE 0xd42e
+#define USB_PM_CTRL_STATUS 0xd432 /* RTL8153A */
+#define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */
+#define USB_TX_DMA 0xd434
+#define USB_UPT_RXDMA_OWN 0xd437
+#define USB_UPHY3_MDCMDIO 0xd480
+#define USB_TOLERANCE 0xd490
+#define USB_LPM_CTRL 0xd41a
+#define USB_BMU_RESET 0xd4b0
+#define USB_BMU_CONFIG 0xd4b4
+#define USB_U1U2_TIMER 0xd4da
+#define USB_FW_TASK 0xd4e8 /* RTL8153B */
+#define USB_RX_AGGR_NUM 0xd4ee
+#define USB_UPS_CTRL 0xd800
+#define USB_POWER_CUT 0xd80a
+#define USB_MISC_0 0xd81a
+#define USB_MISC_1 0xd81f
+#define USB_AFE_CTRL2 0xd824
+#define USB_UPHY_XTAL 0xd826
+#define USB_UPS_CFG 0xd842
+#define USB_UPS_FLAGS 0xd848
+#define USB_WDT1_CTRL 0xe404
+#define USB_WDT11_CTRL 0xe43c
+#define USB_BP_BA PLA_BP_BA
+#define USB_BP_0 PLA_BP_0
+#define USB_BP_1 PLA_BP_1
+#define USB_BP_2 PLA_BP_2
+#define USB_BP_3 PLA_BP_3
+#define USB_BP_4 PLA_BP_4
+#define USB_BP_5 PLA_BP_5
+#define USB_BP_6 PLA_BP_6
+#define USB_BP_7 PLA_BP_7
+#define USB_BP_EN PLA_BP_EN /* RTL8153A */
+#define USB_BP_8 0xfc38 /* RTL8153B */
+#define USB_BP_9 0xfc3a
+#define USB_BP_10 0xfc3c
+#define USB_BP_11 0xfc3e
+#define USB_BP_12 0xfc40
+#define USB_BP_13 0xfc42
+#define USB_BP_14 0xfc44
+#define USB_BP_15 0xfc46
+#define USB_BP2_EN 0xfc48
+
+/* OCP Registers */
+#define OCP_ALDPS_CONFIG 0x2010
+#define OCP_EEE_CONFIG1 0x2080
+#define OCP_EEE_CONFIG2 0x2092
+#define OCP_EEE_CONFIG3 0x2094
+#define OCP_BASE_MII 0xa400
+#define OCP_EEE_AR 0xa41a
+#define OCP_EEE_DATA 0xa41c
+#define OCP_PHY_STATUS 0xa420
+#define OCP_INTR_EN 0xa424
+#define OCP_NCTL_CFG 0xa42c
+#define OCP_POWER_CFG 0xa430
+#define OCP_EEE_CFG 0xa432
+#define OCP_SRAM_ADDR 0xa436
+#define OCP_SRAM_DATA 0xa438
+#define OCP_DOWN_SPEED 0xa442
+#define OCP_EEE_ABLE 0xa5c4
+#define OCP_EEE_ADV 0xa5d0
+#define OCP_EEE_LPABLE 0xa5d2
+#define OCP_10GBT_CTRL 0xa5d4
+#define OCP_10GBT_STAT 0xa5d6
+#define OCP_EEE_ADV2 0xa6d4
+#define OCP_PHY_STATE 0xa708 /* nway state for 8153 */
+#define OCP_PHY_PATCH_STAT 0xb800
+#define OCP_PHY_PATCH_CMD 0xb820
+#define OCP_PHY_LOCK 0xb82e
+#define OCP_ADC_IOFFSET 0xbcfc
+#define OCP_ADC_CFG 0xbc06
+#define OCP_SYSCLK_CFG 0xc416
+
+/* SRAM Register */
+#define SRAM_GREEN_CFG 0x8011
+#define SRAM_LPF_CFG 0x8012
+#define SRAM_GPHY_FW_VER 0x801e
+#define SRAM_10M_AMP1 0x8080
+#define SRAM_10M_AMP2 0x8082
+#define SRAM_IMPEDANCE 0x8084
+#define SRAM_PHY_LOCK 0xb82e
+
+/* PLA_RCR */
+#define RCR_AAP 0x00000001
+#define RCR_APM 0x00000002
+#define RCR_AM 0x00000004
+#define RCR_AB 0x00000008
+#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB)
+#define SLOT_EN BIT(11)
+
+/* PLA_RCR1 */
+#define OUTER_VLAN BIT(7)
+#define INNER_VLAN BIT(6)
+
+/* PLA_RXFIFO_CTRL0 */
+#define RXFIFO_THR1_NORMAL 0x00080002
+#define RXFIFO_THR1_OOB 0x01800003
+
+/* PLA_RXFIFO_FULL */
+#define RXFIFO_FULL_MASK 0xfff
+
+/* PLA_RXFIFO_CTRL1 */
+#define RXFIFO_THR2_FULL 0x00000060
+#define RXFIFO_THR2_HIGH 0x00000038
+#define RXFIFO_THR2_OOB 0x0000004a
+#define RXFIFO_THR2_NORMAL 0x00a0
+
+/* PLA_RXFIFO_CTRL2 */
+#define RXFIFO_THR3_FULL 0x00000078
+#define RXFIFO_THR3_HIGH 0x00000048
+#define RXFIFO_THR3_OOB 0x0000005a
+#define RXFIFO_THR3_NORMAL 0x0110
+
+/* PLA_TXFIFO_CTRL */
+#define TXFIFO_THR_NORMAL 0x00400008
+#define TXFIFO_THR_NORMAL2 0x01000008
+
+/* PLA_DMY_REG0 */
+#define ECM_ALDPS 0x0002
+
+/* PLA_FMC */
+#define FMC_FCR_MCU_EN 0x0001
+
+/* PLA_EEEP_CR */
+#define EEEP_CR_EEEP_TX 0x0002
+
+/* PLA_WDT6_CTRL */
+#define WDT6_SET_MODE 0x0010
+
+/* PLA_TCR0 */
+#define TCR0_TX_EMPTY 0x0800
+#define TCR0_AUTO_FIFO 0x0080
+
+/* PLA_TCR1 */
+#define VERSION_MASK 0x7cf0
+#define IFG_MASK (BIT(3) | BIT(9) | BIT(8))
+#define IFG_144NS BIT(9)
+#define IFG_96NS (BIT(9) | BIT(8))
+
+/* PLA_MTPS */
+#define MTPS_JUMBO (12 * 1024 / 64)
+#define MTPS_DEFAULT (6 * 1024 / 64)
+
+/* PLA_RSTTALLY */
+#define TALLY_RESET 0x0001
+
+/* PLA_CR */
+#define CR_RST 0x10
+#define CR_RE 0x08
+#define CR_TE 0x04
+
+/* PLA_CRWECR */
+#define CRWECR_NORAML 0x00
+#define CRWECR_CONFIG 0xc0
+
+/* PLA_OOB_CTRL */
+#define NOW_IS_OOB 0x80
+#define TXFIFO_EMPTY 0x20
+#define RXFIFO_EMPTY 0x10
+#define LINK_LIST_READY 0x02
+#define DIS_MCU_CLROOB 0x01
+#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY)
+
+/* PLA_MISC_1 */
+#define RXDY_GATED_EN 0x0008
+
+/* PLA_SFF_STS_7 */
+#define RE_INIT_LL 0x8000
+#define MCU_BORW_EN 0x4000
+
+/* PLA_CPCR */
+#define FLOW_CTRL_EN BIT(0)
+#define CPCR_RX_VLAN 0x0040
+
+/* PLA_CFG_WOL */
+#define MAGIC_EN 0x0001
+
+/* PLA_TEREDO_CFG */
+#define TEREDO_SEL 0x8000
+#define TEREDO_WAKE_MASK 0x7f00
+#define TEREDO_RS_EVENT_MASK 0x00fe
+#define OOB_TEREDO_EN 0x0001
+
+/* PLA_BDC_CR */
+#define ALDPS_PROXY_MODE 0x0001
+
+/* PLA_EFUSE_CMD */
+#define EFUSE_READ_CMD BIT(15)
+#define EFUSE_DATA_BIT16 BIT(7)
+
+/* PLA_CONFIG34 */
+#define LINK_ON_WAKE_EN 0x0010
+#define LINK_OFF_WAKE_EN 0x0008
+
+/* PLA_CONFIG6 */
+#define LANWAKE_CLR_EN BIT(0)
+
+/* PLA_USB_CFG */
+#define EN_XG_LIP BIT(1)
+#define EN_G_LIP BIT(2)
+
+/* PLA_CONFIG5 */
+#define BWF_EN 0x0040
+#define MWF_EN 0x0020
+#define UWF_EN 0x0010
+#define LAN_WAKE_EN 0x0002
+
+/* PLA_LED_FEATURE */
+#define LED_MODE_MASK 0x0700
+
+/* PLA_PHY_PWR */
+#define TX_10M_IDLE_EN 0x0080
+#define PFM_PWM_SWITCH 0x0040
+#define TEST_IO_OFF BIT(4)
+
+/* PLA_MAC_PWR_CTRL */
+#define D3_CLK_GATED_EN 0x00004000
+#define MCU_CLK_RATIO 0x07010f07
+#define MCU_CLK_RATIO_MASK 0x0f0f0f0f
+#define ALDPS_SPDWN_RATIO 0x0f87
+
+/* PLA_MAC_PWR_CTRL2 */
+#define EEE_SPDWN_RATIO 0x8007
+#define MAC_CLK_SPDWN_EN BIT(15)
+#define EEE_SPDWN_RATIO_MASK 0xff
+
+/* PLA_MAC_PWR_CTRL3 */
+#define PLA_MCU_SPDWN_EN BIT(14)
+#define PKT_AVAIL_SPDWN_EN 0x0100
+#define SUSPEND_SPDWN_EN 0x0004
+#define U1U2_SPDWN_EN 0x0002
+#define L1_SPDWN_EN 0x0001
+
+/* PLA_MAC_PWR_CTRL4 */
+#define PWRSAVE_SPDWN_EN 0x1000
+#define RXDV_SPDWN_EN 0x0800
+#define TX10MIDLE_EN 0x0100
+#define IDLE_SPDWN_EN BIT(6)
+#define TP100_SPDWN_EN 0x0020
+#define TP500_SPDWN_EN 0x0010
+#define TP1000_SPDWN_EN 0x0008
+#define EEE_SPDWN_EN 0x0001
+
+/* PLA_GPHY_INTR_IMR */
+#define GPHY_STS_MSK 0x0001
+#define SPEED_DOWN_MSK 0x0002
+#define SPDWN_RXDV_MSK 0x0004
+#define SPDWN_LINKCHG_MSK 0x0008
+
+/* PLA_PHYAR */
+#define PHYAR_FLAG 0x80000000
+
+/* PLA_EEE_CR */
+#define EEE_RX_EN 0x0001
+#define EEE_TX_EN 0x0002
+
+/* PLA_BOOT_CTRL */
+#define AUTOLOAD_DONE 0x0002
+
+/* PLA_LWAKE_CTRL_REG */
+#define LANWAKE_PIN BIT(7)
+
+/* PLA_SUSPEND_FLAG */
+#define LINK_CHG_EVENT BIT(0)
+
+/* PLA_INDICATE_FALG */
+#define UPCOMING_RUNTIME_D3 BIT(0)
+
+/* PLA_MACDBG_PRE and PLA_MACDBG_POST */
+#define DEBUG_OE BIT(0)
+#define DEBUG_LTSSM 0x0082
+
+/* PLA_EXTRA_STATUS */
+#define CUR_LINK_OK BIT(15)
+#define U3P3_CHECK_EN BIT(7) /* RTL_VER_05 only */
+#define LINK_CHANGE_FLAG BIT(8)
+#define POLL_LINK_CHG BIT(0)
+
+/* PLA_GPHY_CTRL */
+#define GPHY_FLASH BIT(1)
+
+/* PLA_POL_GPIO_CTRL */
+#define DACK_DET_EN BIT(15)
+#define POL_GPHY_PATCH BIT(4)
+
+/* USB_USB2PHY */
+#define USB2PHY_SUSPEND 0x0001
+#define USB2PHY_L1 0x0002
+
+/* USB_SSPHYLINK1 */
+#define DELAY_PHY_PWR_CHG BIT(1)
+
+/* USB_SSPHYLINK2 */
+#define pwd_dn_scale_mask 0x3ffe
+#define pwd_dn_scale(x) ((x) << 1)
+
+/* USB_CSR_DUMMY1 */
+#define DYNAMIC_BURST 0x0001
+
+/* USB_CSR_DUMMY2 */
+#define EP4_FULL_FC 0x0001
+
+/* USB_DEV_STAT */
+#define STAT_SPEED_MASK 0x0006
+#define STAT_SPEED_HIGH 0x0000
+#define STAT_SPEED_FULL 0x0002
+
+/* USB_FW_FIX_EN0 */
+#define FW_FIX_SUSPEND BIT(14)
+
+/* USB_FW_FIX_EN1 */
+#define FW_IP_RESET_EN BIT(9)
+
+/* USB_LPM_CONFIG */
+#define LPM_U1U2_EN BIT(0)
+
+/* USB_TX_AGG */
+#define TX_AGG_MAX_THRESHOLD 0x03
+
+/* USB_RX_BUF_TH */
+#define RX_THR_SUPPER 0x0c350180
+#define RX_THR_HIGH 0x7a120180
+#define RX_THR_SLOW 0xffff0180
+#define RX_THR_B 0x00010001
+
+/* USB_TX_DMA */
+#define TEST_MODE_DISABLE 0x00000001
+#define TX_SIZE_ADJUST1 0x00000100
+
+/* USB_BMU_RESET */
+#define BMU_RESET_EP_IN 0x01
+#define BMU_RESET_EP_OUT 0x02
+
+/* USB_BMU_CONFIG */
+#define ACT_ODMA BIT(1)
+
+/* USB_UPT_RXDMA_OWN */
+#define OWN_UPDATE BIT(0)
+#define OWN_CLEAR BIT(1)
+
+/* USB_FW_TASK */
+#define FC_PATCH_TASK BIT(1)
+
+/* USB_RX_AGGR_NUM */
+#define RX_AGGR_NUM_MASK 0x1ff
+
+/* USB_UPS_CTRL */
+#define POWER_CUT 0x0100
+
+/* USB_PM_CTRL_STATUS */
+#define RESUME_INDICATE 0x0001
+
+/* USB_ECM_OPTION */
+#define BYPASS_MAC_RESET BIT(5)
+
+/* USB_CSTMR */
+#define FORCE_SUPER BIT(0)
+
+/* USB_MISC_2 */
+#define UPS_FORCE_PWR_DOWN BIT(0)
+
+/* USB_ECM_OP */
+#define EN_ALL_SPEED BIT(0)
+
+/* USB_GPHY_CTRL */
+#define GPHY_PATCH_DONE BIT(2)
+#define BYPASS_FLASH BIT(5)
+#define BACKUP_RESTRORE BIT(6)
+
+/* USB_SPEED_OPTION */
+#define RG_PWRDN_EN BIT(8)
+#define ALL_SPEED_OFF BIT(9)
+
+/* USB_FW_CTRL */
+#define FLOW_CTRL_PATCH_OPT BIT(1)
+#define AUTO_SPEEDUP BIT(3)
+#define FLOW_CTRL_PATCH_2 BIT(8)
+
+/* USB_FC_TIMER */
+#define CTRL_TIMER_EN BIT(15)
+
+/* USB_USB_CTRL */
+#define CDC_ECM_EN BIT(3)
+#define RX_AGG_DISABLE 0x0010
+#define RX_ZERO_EN 0x0080
+
+/* USB_U2P3_CTRL */
+#define U2P3_ENABLE 0x0001
+#define RX_DETECT8 BIT(3)
+
+/* USB_POWER_CUT */
+#define PWR_EN 0x0001
+#define PHASE2_EN 0x0008
+#define UPS_EN BIT(4)
+#define USP_PREWAKE BIT(5)
+
+/* USB_MISC_0 */
+#define PCUT_STATUS 0x0001
+
+/* USB_RX_EARLY_TIMEOUT */
+#define COALESCE_SUPER 85000U
+#define COALESCE_HIGH 250000U
+#define COALESCE_SLOW 524280U
+
+/* USB_WDT1_CTRL */
+#define WTD1_EN BIT(0)
+
+/* USB_WDT11_CTRL */
+#define TIMER11_EN 0x0001
+
+/* USB_LPM_CTRL */
+/* bit 4 ~ 5: fifo empty boundary */
+#define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */
+/* bit 2 ~ 3: LMP timer */
+#define LPM_TIMER_MASK 0x0c
+#define LPM_TIMER_500MS 0x04 /* 500 ms */
+#define LPM_TIMER_500US 0x0c /* 500 us */
+#define ROK_EXIT_LPM 0x02
+
+/* USB_AFE_CTRL2 */
+#define SEN_VAL_MASK 0xf800
+#define SEN_VAL_NORMAL 0xa000
+#define SEL_RXIDLE 0x0100
+
+/* USB_UPHY_XTAL */
+#define OOBS_POLLING BIT(8)
+
+/* USB_UPS_CFG */
+#define SAW_CNT_1MS_MASK 0x0fff
+#define MID_REVERSE BIT(5) /* RTL8156A */
+
+/* USB_UPS_FLAGS */
+#define UPS_FLAGS_R_TUNE BIT(0)
+#define UPS_FLAGS_EN_10M_CKDIV BIT(1)
+#define UPS_FLAGS_250M_CKDIV BIT(2)
+#define UPS_FLAGS_EN_ALDPS BIT(3)
+#define UPS_FLAGS_CTAP_SHORT_DIS BIT(4)
+#define UPS_FLAGS_SPEED_MASK (0xf << 16)
+#define ups_flags_speed(x) ((x) << 16)
+#define UPS_FLAGS_EN_EEE BIT(20)
+#define UPS_FLAGS_EN_500M_EEE BIT(21)
+#define UPS_FLAGS_EN_EEE_CKDIV BIT(22)
+#define UPS_FLAGS_EEE_PLLOFF_100 BIT(23)
+#define UPS_FLAGS_EEE_PLLOFF_GIGA BIT(24)
+#define UPS_FLAGS_EEE_CMOD_LV_EN BIT(25)
+#define UPS_FLAGS_EN_GREEN BIT(26)
+#define UPS_FLAGS_EN_FLOW_CTR BIT(27)
+
+enum spd_duplex {
+ NWAY_10M_HALF,
+ NWAY_10M_FULL,
+ NWAY_100M_HALF,
+ NWAY_100M_FULL,
+ NWAY_1000M_FULL,
+ FORCE_10M_HALF,
+ FORCE_10M_FULL,
+ FORCE_100M_HALF,
+ FORCE_100M_FULL,
+ FORCE_1000M_FULL,
+ NWAY_2500M_FULL,
+};
+
+/* OCP_ALDPS_CONFIG */
+#define ENPWRSAVE 0x8000
+#define ENPDNPS 0x0200
+#define LINKENA 0x0100
+#define DIS_SDSAVE 0x0010
+
+/* OCP_PHY_STATUS */
+#define PHY_STAT_MASK 0x0007
+#define PHY_STAT_EXT_INIT 2
+#define PHY_STAT_LAN_ON 3
+#define PHY_STAT_PWRDN 5
+
+/* OCP_INTR_EN */
+#define INTR_SPEED_FORCE BIT(3)
+
+/* OCP_NCTL_CFG */
+#define PGA_RETURN_EN BIT(1)
+
+/* OCP_POWER_CFG */
+#define EEE_CLKDIV_EN 0x8000
+#define EN_ALDPS 0x0004
+#define EN_10M_PLLOFF 0x0001
+
+/* OCP_EEE_CONFIG1 */
+#define RG_TXLPI_MSK_HFDUP 0x8000
+#define RG_MATCLR_EN 0x4000
+#define EEE_10_CAP 0x2000
+#define EEE_NWAY_EN 0x1000
+#define TX_QUIET_EN 0x0200
+#define RX_QUIET_EN 0x0100
+#define sd_rise_time_mask 0x0070
+#define sd_rise_time(x) (MIN(x, 7) << 4) /* bit 4 ~ 6 */
+#define RG_RXLPI_MSK_HFDUP 0x0008
+#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */
+
+/* OCP_EEE_CONFIG2 */
+#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */
+#define RG_DACQUIET_EN 0x0400
+#define RG_LDVQUIET_EN 0x0200
+#define RG_CKRSEL 0x0020
+#define RG_EEEPRG_EN 0x0010
+
+/* OCP_EEE_CONFIG3 */
+#define fast_snr_mask 0xff80
+#define fast_snr(x) (MIN(x, 0x1ff) << 7) /* bit 7 ~ 15 */
+#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */
+#define MSK_PH 0x0006 /* bit 0 ~ 3 */
+
+/* OCP_EEE_AR */
+/* bit[15:14] function */
+#define FUN_ADDR 0x0000
+#define FUN_DATA 0x4000
+/* bit[4:0] device addr */
+
+/* OCP_EEE_CFG */
+#define CTAP_SHORT_EN 0x0040
+#define EEE10_EN 0x0010
+
+/* OCP_DOWN_SPEED */
+#define EN_EEE_CMODE BIT(14)
+#define EN_EEE_1000 BIT(13)
+#define EN_EEE_100 BIT(12)
+#define EN_10M_CLKDIV BIT(11)
+#define EN_10M_BGOFF 0x0080
+
+/* OCP_10GBT_CTRL */
+#define RTL_ADV2_5G_F_R BIT(5) /* Advertise 2.5GBASE-T fast-retrain */
+
+/* OCP_PHY_STATE */
+#define TXDIS_STATE 0x01
+#define ABD_STATE 0x02
+
+/* OCP_PHY_PATCH_STAT */
+#define PATCH_READY BIT(6)
+
+/* OCP_PHY_PATCH_CMD */
+#define PATCH_REQUEST BIT(4)
+
+/* OCP_PHY_LOCK */
+#define PATCH_LOCK BIT(0)
+
+/* OCP_ADC_CFG */
+#define CKADSEL_L 0x0100
+#define ADC_EN 0x0080
+#define EN_EMI_L 0x0040
+
+/* OCP_SYSCLK_CFG */
+#define sysclk_div_expo(x) (MIN(x, 5) << 8)
+#define clk_div_expo(x) (MIN(x, 5) << 4)
+
+/* SRAM_GREEN_CFG */
+#define GREEN_ETH_EN BIT(15)
+#define R_TUNE_EN BIT(11)
+
+/* SRAM_LPF_CFG */
+#define LPF_AUTO_TUNE 0x8000
+
+/* SRAM_10M_AMP1 */
+#define GDAC_IB_UPALL 0x0008
+
+/* SRAM_10M_AMP2 */
+#define AMP_DN 0x0200
+
+/* SRAM_IMPEDANCE */
+#define RX_DRIVING_MASK 0x6000
+
+/* SRAM_PHY_LOCK */
+#define PHY_PATCH_LOCK 0x0001
+
+/* MAC PASSTHRU */
+#define AD_MASK 0xfee0
+#define BND_MASK 0x0004
+#define BD_MASK 0x0001
+#define EFUSE 0xcfdb
+#define PASS_THRU_MASK 0x1
+
+#define BP4_SUPER_ONLY 0x1578 /* RTL_VER_04 only */
+
+enum rtl_register_content {
+ _2500bps = BIT(10),
+ _1250bps = BIT(9),
+ _500bps = BIT(8),
+ _tx_flow = BIT(6),
+ _rx_flow = BIT(5),
+ _1000bps = 0x10,
+ _100bps = 0x08,
+ _10bps = 0x04,
+ LINK_STATUS = 0x02,
+ FULL_DUP = 0x01,
+};
+
+#define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
+#define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))
+
+#define RTL8152_MAX_TX 4
+#define RTL8152_MAX_RX 10
+#define INTBUFSIZE 2
+#define TX_ALIGN 4
+#define RX_ALIGN 8
+
+#define RTL8152_RX_MAX_PENDING 4096
+#define RTL8152_RXFG_HEADSZ 256
+
+#define INTR_LINK 0x0004
+
+#define VLAN_ETH_HLEN 18
+#define ETH_FCS_LEN 4
+#define VLAN_ETH_FRAME_LEN 1514
+
+#define RTL8152_RMS (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN)
+#define RTL8153_RMS RTL8153_MAX_PACKET
+#define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN)
+#define size_to_mtu(s) ((s)-VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN)
+
+struct rx_desc {
+ uint32_t opts1;
+#define RD_CRC BIT(15)
+#define RX_LEN_MASK 0x7fff
+
+ uint32_t opts2;
+#define RD_UDP_CS BIT(23)
+#define RD_TCP_CS BIT(22)
+#define RD_IPV6_CS BIT(20)
+#define RD_IPV4_CS BIT(19)
+
+ uint32_t opts3;
+#define IPF BIT(23) /* IP checksum fail */
+#define UDPF BIT(22) /* UDP checksum fail */
+#define TCPF BIT(21) /* TCP checksum fail */
+#define RX_VLAN_TAG BIT(16)
+
+ uint32_t opts4;
+ uint32_t opts5;
+ uint32_t opts6;
+};
+
+struct tx_desc {
+ uint32_t opts1;
+#define TX_FS BIT(31) /* First segment of a packet */
+#define TX_LS BIT(30) /* Final segment of a packet */
+#define LGSEND BIT(29)
+#define GTSENDV4 BIT(28)
+#define GTSENDV6 BIT(27)
+#define GTTCPHO_SHIFT 18
+#define GTTCPHO_MAX 0x7fU
+#define TX_LEN_MAX 0x3ffffU
+
+ uint32_t opts2;
+#define UDP_CS BIT(31) /* Calculate UDP/IP checksum */
+#define TCP_CS BIT(30) /* Calculate TCP/IP checksum */
+#define IPV4_CS BIT(29) /* Calculate IPv4 checksum */
+#define IPV6_CS BIT(28) /* Calculate IPv6 checksum */
+#define MSS_SHIFT 17
+#define MSS_MAX 0x7ffU
+#define TCPHO_SHIFT 17
+#define TCPHO_MAX 0x7ffU
+#define TX_VLAN_TAG BIT(16)
+};
+
+enum rtl_version {
+ RTL_VER_UNKNOWN = 0,
+ RTL_VER_01,
+ RTL_VER_02,
+ RTL_VER_03,
+ RTL_VER_04,
+ RTL_VER_05,
+ RTL_VER_06,
+ RTL_VER_07,
+ RTL_VER_08,
+ RTL_VER_09,
+
+ RTL_TEST_01,
+ RTL_VER_10,
+ RTL_VER_11,
+ RTL_VER_12,
+ RTL_VER_13,
+ RTL_VER_14,
+ RTL_VER_15,
+
+ RTL_VER_MAX
+};
+
+/* mii.h */
+/* Generic MII registers. */
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_CTRL1000 0x09 /* 1000BASE-T control */
+#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */
+#define MII_MMD_DATA 0x0e /* MMD Access Data Register */
+#define MII_ESTATUS 0x0f /* Extended Status */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x003f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Isolate data paths from MII */
+#define BMCR_PDOWN 0x0800 /* Enable low power state */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset to default state */
+#define BMCR_SPEED10 0x0000 /* Select 10Mbps */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x00c0 /* Unused... */
+#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */
+#define BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */
+#define BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
+#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */
+#define ADVERTISE_RESV 0x1000 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* 1000BASE-T Control register */
+#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */
+#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */
+#define CTL1000_AS_MASTER 0x0800
+#define CTL1000_ENABLE_MASTER 0x1000
+
+/* ethtool.h */
+/* The forced speed, 10Mb, 100Mb, gigabit. */
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+
+#define SPEED_UNKNOWN -1
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_UNKNOWN 0xff
+
+/* Enable or disable autonegotiation. */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+static int usbh_rtl8152_read_regs(struct usbh_rtl8152 *rtl8152_class,
+ uint16_t value,
+ uint16_t index,
+ uint16_t size,
+ void *data)
+{
+ struct usb_setup_packet *setup = rtl8152_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = RTL8152_REQ_GET_REGS;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = size;
+
+ ret = usbh_control_transfer(rtl8152_class->hport, setup, g_rtl8152_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(data, g_rtl8152_buf, ret - 8);
+
+ return ret;
+}
+
+static int usbh_rtl8152_write_regs(struct usbh_rtl8152 *rtl8152_class,
+ uint16_t value,
+ uint16_t index,
+ uint16_t size,
+ void *data)
+{
+ struct usb_setup_packet *setup = rtl8152_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = RTL8152_REQ_SET_REGS;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = size;
+
+ memcpy(g_rtl8152_buf, data, size);
+ return usbh_control_transfer(rtl8152_class->hport, setup, g_rtl8152_buf);
+}
+
+static int generic_ocp_read(struct usbh_rtl8152 *tp, uint16_t index, uint16_t size,
+ void *data, uint16_t type)
+{
+ uint16_t limit = 64;
+ int ret = 0;
+
+ /* both size and indix must be 4 bytes align */
+ if ((size & 3) || !size || (index & 3) || !data)
+ return -USB_ERR_INVAL;
+
+ if ((uint32_t)index + (uint32_t)size > 0xffff)
+ return -USB_ERR_INVAL;
+
+ while (size) {
+ if (size > limit) {
+ ret = usbh_rtl8152_read_regs(tp, index, type, limit, data);
+ if (ret < 0)
+ break;
+
+ index += limit;
+ data += limit;
+ size -= limit;
+ } else {
+ ret = usbh_rtl8152_read_regs(tp, index, type, size, data);
+ if (ret < 0)
+ break;
+
+ index += size;
+ data += size;
+ size = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int generic_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t byteen,
+ uint16_t size, void *data, uint16_t type)
+{
+ int ret;
+ uint16_t byteen_start, byteen_end, byen;
+ uint16_t limit = 512;
+
+ /* both size and indix must be 4 bytes align */
+ if ((size & 3) || !size || (index & 3) || !data)
+ return -USB_ERR_INVAL;
+
+ if ((uint32_t)index + (uint32_t)size > 0xffff)
+ return -USB_ERR_INVAL;
+
+ byteen_start = byteen & BYTE_EN_START_MASK;
+ byteen_end = byteen & BYTE_EN_END_MASK;
+
+ byen = byteen_start | (byteen_start << 4);
+
+ /* Split the first DWORD if the byte_en is not 0xff */
+ if (byen != BYTE_EN_DWORD) {
+ ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, data);
+ if (ret < 0)
+ goto error1;
+
+ index += 4;
+ data += 4;
+ size -= 4;
+ }
+
+ if (size) {
+ byen = byteen_end | (byteen_end >> 4);
+
+ /* Split the last DWORD if the byte_en is not 0xff */
+ if (byen != BYTE_EN_DWORD)
+ size -= 4;
+
+ while (size) {
+ if (size > limit) {
+ ret = usbh_rtl8152_write_regs(tp, index,
+ type | BYTE_EN_DWORD,
+ limit, data);
+ if (ret < 0)
+ goto error1;
+
+ index += limit;
+ data += limit;
+ size -= limit;
+ } else {
+ ret = usbh_rtl8152_write_regs(tp, index,
+ type | BYTE_EN_DWORD,
+ size, data);
+ if (ret < 0)
+ goto error1;
+
+ index += size;
+ data += size;
+ size = 0;
+ break;
+ }
+ }
+
+ /* Set the last DWORD */
+ if (byen != BYTE_EN_DWORD)
+ ret = usbh_rtl8152_write_regs(tp, index, type | byen, 4, data);
+ }
+
+error1:
+
+ return ret;
+}
+
+static inline int pla_ocp_read(struct usbh_rtl8152 *tp, uint16_t index, uint16_t size, void *data)
+{
+ return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA);
+}
+
+static inline int pla_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t byteen, uint16_t size, void *data)
+{
+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA);
+}
+
+static inline int usb_ocp_write(struct usbh_rtl8152 *tp, uint16_t index, uint16_t byteen, uint16_t size, void *data)
+{
+ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB);
+}
+
+static uint32_t ocp_read_dword(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index)
+{
+ uint32_t data;
+
+ generic_ocp_read(tp, index, sizeof(data), &data, type);
+
+ return data;
+}
+
+static void ocp_write_dword(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index, uint32_t data)
+{
+ uint32_t tmp = data;
+
+ generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type);
+}
+
+static uint16_t ocp_read_word(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index)
+{
+ uint32_t data;
+ uint32_t tmp;
+ uint16_t byen = BYTE_EN_WORD;
+ uint8_t shift = index & 2;
+
+ index &= ~3;
+ byen <<= shift;
+
+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type | byen);
+
+ data = tmp;
+ data >>= (shift * 8);
+ data &= 0xffff;
+
+ return (uint16_t)data;
+}
+
+static void ocp_write_word(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index, uint32_t data)
+{
+ uint32_t mask = 0xffff;
+ uint32_t tmp;
+ uint16_t byen = BYTE_EN_WORD;
+ uint8_t shift = index & 2;
+
+ data &= mask;
+
+ if (index & 2) {
+ byen <<= shift;
+ mask <<= (shift * 8);
+ data <<= (shift * 8);
+ index &= ~3;
+ }
+
+ tmp = data;
+
+ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type);
+}
+
+static uint8_t ocp_read_byte(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index)
+{
+ uint32_t data;
+ uint32_t tmp;
+ uint8_t shift = index & 3;
+
+ index &= ~3;
+
+ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type);
+
+ data = tmp;
+ data >>= (shift * 8);
+ data &= 0xff;
+
+ return (uint8_t)data;
+}
+
+static void ocp_write_byte(struct usbh_rtl8152 *tp, uint16_t type, uint16_t index, uint32_t data)
+{
+ uint32_t mask = 0xff;
+ uint32_t tmp;
+ uint16_t byen = BYTE_EN_BYTE;
+ uint8_t shift = index & 3;
+
+ data &= mask;
+
+ if (index & 3) {
+ byen <<= shift;
+ mask <<= (shift * 8);
+ data <<= (shift * 8);
+ index &= ~3;
+ }
+
+ tmp = data;
+
+ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type);
+}
+
+static uint16_t ocp_reg_read(struct usbh_rtl8152 *tp, uint16_t addr)
+{
+ uint16_t ocp_base, ocp_index;
+
+ ocp_base = addr & 0xf000;
+ if (ocp_base != tp->ocp_base) {
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base);
+ tp->ocp_base = ocp_base;
+ }
+
+ ocp_index = (addr & 0x0fff) | 0xb000;
+ return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index);
+}
+
+static void ocp_reg_write(struct usbh_rtl8152 *tp, uint16_t addr, uint16_t data)
+{
+ uint16_t ocp_base, ocp_index;
+
+ ocp_base = addr & 0xf000;
+ if (ocp_base != tp->ocp_base) {
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base);
+ tp->ocp_base = ocp_base;
+ }
+
+ ocp_index = (addr & 0x0fff) | 0xb000;
+ ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data);
+}
+
+static inline void r8152_mdio_write(struct usbh_rtl8152 *tp, uint32_t reg_addr, uint32_t value)
+{
+ ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value);
+}
+
+static inline int r8152_mdio_read(struct usbh_rtl8152 *tp, uint32_t reg_addr)
+{
+ return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2);
+}
+
+static uint8_t usbh_rtl8152_get_version(struct usbh_rtl8152 *rtl8152_class)
+{
+ uint8_t version;
+ uint32_t temp;
+ uint32_t ocp_data;
+
+ usbh_rtl8152_read_regs(rtl8152_class, PLA_TCR0, MCU_TYPE_PLA, 4, &temp);
+
+ ocp_data = (temp >> 16) & VERSION_MASK;
+ switch (ocp_data) {
+ case 0x4c00:
+ version = RTL_VER_01;
+ break;
+ case 0x4c10:
+ version = RTL_VER_02;
+ break;
+ case 0x5c00:
+ version = RTL_VER_03;
+ break;
+ case 0x5c10:
+ version = RTL_VER_04;
+ break;
+ case 0x5c20:
+ version = RTL_VER_05;
+ break;
+ case 0x5c30:
+ version = RTL_VER_06;
+ break;
+ case 0x4800:
+ version = RTL_VER_07;
+ break;
+ case 0x6000:
+ version = RTL_VER_08;
+ break;
+ case 0x6010:
+ version = RTL_VER_09;
+ break;
+ case 0x7010:
+ version = RTL_TEST_01;
+ break;
+ case 0x7020:
+ version = RTL_VER_10;
+ break;
+ case 0x7030:
+ version = RTL_VER_11;
+ break;
+ case 0x7400:
+ version = RTL_VER_12;
+ break;
+ case 0x7410:
+ version = RTL_VER_13;
+ break;
+ case 0x6400:
+ version = RTL_VER_14;
+ break;
+ case 0x7420:
+ version = RTL_VER_15;
+ break;
+ default:
+ version = RTL_VER_UNKNOWN;
+ break;
+ }
+
+ return version;
+}
+
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+#define WAKE_FILTER (1 << 7)
+
+#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
+
+static uint32_t __rtl_get_wol(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ uint32_t wolopts = 0;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+ if (ocp_data & LINK_ON_WAKE_EN)
+ wolopts |= WAKE_PHY;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
+ if (ocp_data & UWF_EN)
+ wolopts |= WAKE_UCAST;
+ if (ocp_data & BWF_EN)
+ wolopts |= WAKE_BCAST;
+ if (ocp_data & MWF_EN)
+ wolopts |= WAKE_MCAST;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL);
+ if (ocp_data & MAGIC_EN)
+ wolopts |= WAKE_MAGIC;
+
+ return wolopts;
+}
+
+static void r8152_aldps_en(struct usbh_rtl8152 *tp, bool enable)
+{
+ if (enable) {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | LINKENA | DIS_SDSAVE);
+ } else {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE);
+ usb_osal_msleep(20);
+ }
+}
+
+static void r8152_power_cut_en(struct usbh_rtl8152 *tp, bool enable)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL);
+ if (enable)
+ ocp_data |= POWER_CUT;
+ else
+ ocp_data &= ~POWER_CUT;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS);
+ ocp_data &= ~RESUME_INDICATE;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data);
+}
+
+static void rtl_tally_reset(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY);
+ ocp_data |= TALLY_RESET;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data);
+}
+
+static void r8152b_reset_packet_filter(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC);
+ ocp_data &= ~FMC_FCR_MCU_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data);
+ ocp_data |= FMC_FCR_MCU_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data);
+}
+
+static inline void r8153b_rx_agg_chg_indicate(struct usbh_rtl8152 *tp)
+{
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN,
+ OWN_UPDATE | OWN_CLEAR);
+}
+
+static void rxdy_gated_en(struct usbh_rtl8152 *tp, bool enable)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1);
+ if (enable)
+ ocp_data |= RXDY_GATED_EN;
+ else
+ ocp_data &= ~RXDY_GATED_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data);
+}
+
+static inline uint16_t rtl8152_get_speed(struct usbh_rtl8152 *tp)
+{
+ return ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHYSTATUS);
+}
+
+static void rtl_eee_plus_en(struct usbh_rtl8152 *tp, bool enable)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR);
+ if (enable)
+ ocp_data |= EEEP_CR_EEEP_TX;
+ else
+ ocp_data &= ~EEEP_CR_EEEP_TX;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data);
+}
+
+static void rtl_set_eee_plus(struct usbh_rtl8152 *tp)
+{
+ if (rtl8152_get_speed(tp) & _10bps)
+ rtl_eee_plus_en(tp, true);
+ else
+ rtl_eee_plus_en(tp, false);
+}
+
+static void rtl8152_nic_reset(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ int i;
+
+ switch (tp->version) {
+ case RTL_TEST_01:
+ case RTL_VER_10:
+ case RTL_VER_11:
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+ ocp_data &= ~CR_TE;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
+ ocp_data &= ~BMU_RESET_EP_IN;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data |= CDC_ECM_EN;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+ ocp_data &= ~CR_RE;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET);
+ ocp_data |= BMU_RESET_EP_IN;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data &= ~CDC_ECM_EN;
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+ break;
+
+ default:
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST);
+
+ for (i = 0; i < 1000; i++) {
+ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST))
+ break;
+ usb_osal_msleep(400);
+ }
+ break;
+ }
+}
+
+static void rtl_disable(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ int i;
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+ rxdy_gated_en(tp, true);
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY)
+ break;
+ usb_osal_msleep(1);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY)
+ break;
+ usb_osal_msleep(1);
+ }
+
+ rtl8152_nic_reset(tp);
+}
+
+static void rtl_rx_vlan_en(struct usbh_rtl8152 *tp, bool enable)
+{
+ uint32_t ocp_data;
+
+ switch (tp->version) {
+ case RTL_VER_01:
+ case RTL_VER_02:
+ case RTL_VER_03:
+ case RTL_VER_04:
+ case RTL_VER_05:
+ case RTL_VER_06:
+ case RTL_VER_07:
+ case RTL_VER_08:
+ case RTL_VER_09:
+ case RTL_VER_14:
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR);
+ if (enable)
+ ocp_data |= CPCR_RX_VLAN;
+ else
+ ocp_data &= ~CPCR_RX_VLAN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data);
+ break;
+
+ case RTL_TEST_01:
+ case RTL_VER_10:
+ case RTL_VER_11:
+ case RTL_VER_12:
+ case RTL_VER_13:
+ case RTL_VER_15:
+ default:
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR1);
+ if (enable)
+ ocp_data |= OUTER_VLAN | INNER_VLAN;
+ else
+ ocp_data &= ~(OUTER_VLAN | INNER_VLAN);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR1, ocp_data);
+ break;
+ }
+}
+
+static void wait_oob_link_list_ready(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ if (ocp_data & LINK_LIST_READY)
+ break;
+ usb_osal_msleep(1);
+ }
+}
+
+static void r8153_teredo_off(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ switch (tp->version) {
+ case RTL_VER_01:
+ case RTL_VER_02:
+ case RTL_VER_03:
+ case RTL_VER_04:
+ case RTL_VER_05:
+ case RTL_VER_06:
+ case RTL_VER_07:
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
+ ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK |
+ OOB_TEREDO_EN);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
+ break;
+
+ case RTL_VER_08:
+ case RTL_VER_09:
+ case RTL_TEST_01:
+ case RTL_VER_10:
+ case RTL_VER_11:
+ case RTL_VER_12:
+ case RTL_VER_13:
+ case RTL_VER_14:
+ case RTL_VER_15:
+ default:
+ /* The bit 0 ~ 7 are relative with teredo settings. They are
+ * W1C (write 1 to clear), so set all 1 to disable it.
+ */
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff);
+ break;
+ }
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0);
+}
+
+static void r8152b_exit_oob(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+
+ rxdy_gated_en(tp, true);
+ r8153_teredo_off(tp);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data &= ~NOW_IS_OOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data &= ~MCU_BORW_EN;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ wait_oob_link_list_ready(tp);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data |= RE_INIT_LL;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ wait_oob_link_list_ready(tp);
+
+ rtl8152_nic_reset(tp);
+
+ /* rx share fifo credit full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL);
+
+ if (tp->hport->speed == USB_SPEED_FULL ||
+ tp->hport->speed == USB_SPEED_LOW) {
+ /* rx share fifo credit near full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1,
+ RXFIFO_THR2_FULL);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2,
+ RXFIFO_THR3_FULL);
+ } else {
+ /* rx share fifo credit near full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1,
+ RXFIFO_THR2_HIGH);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2,
+ RXFIFO_THR3_HIGH);
+ }
+
+ /* TX share fifo free credit full threshold */
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2);
+
+ ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH);
+ ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA,
+ TEST_MODE_DISABLE | TX_SIZE_ADJUST1);
+
+ rtl_rx_vlan_en(tp, true);
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0);
+ ocp_data |= TCR0_AUTO_FIFO;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data);
+}
+
+static void r8152b_enter_oob(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data &= ~NOW_IS_OOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB);
+
+ rtl_disable(tp);
+
+ wait_oob_link_list_ready(tp);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7);
+ ocp_data |= RE_INIT_LL;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data);
+
+ wait_oob_link_list_ready(tp);
+
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS);
+
+ rtl_rx_vlan_en(tp, true);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BDC_CR);
+ ocp_data |= ALDPS_PROXY_MODE;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_BDC_CR, ocp_data);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
+ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
+
+ rxdy_gated_en(tp, false);
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data |= RCR_APM | RCR_AM | RCR_AB;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+}
+
+static void r8152b_init(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ uint16_t data;
+
+ data = r8152_mdio_read(tp, MII_BMCR);
+ if (data & BMCR_PDOWN) {
+ data &= ~BMCR_PDOWN;
+ r8152_mdio_write(tp, MII_BMCR, data);
+ }
+
+ r8152_aldps_en(tp, false);
+
+ if (tp->version == RTL_VER_01) {
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
+ ocp_data &= ~LED_MODE_MASK;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data);
+ }
+
+ r8152_power_cut_en(tp, false);
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
+ ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL);
+ ocp_data &= ~MCU_CLK_RATIO_MASK;
+ ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data);
+ ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK |
+ SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK;
+ ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data);
+
+ rtl_tally_reset(tp);
+
+ /* enable rx aggregation */
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
+ ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
+ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
+}
+
+static int rtl_enable(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+
+ r8152b_reset_packet_filter(tp);
+
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR);
+ ocp_data |= CR_RE | CR_TE;
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data);
+
+ switch (tp->version) {
+ case RTL_VER_01:
+ case RTL_VER_02:
+ case RTL_VER_03:
+ case RTL_VER_04:
+ case RTL_VER_05:
+ case RTL_VER_06:
+ case RTL_VER_07:
+ break;
+ default:
+ r8153b_rx_agg_chg_indicate(tp);
+ break;
+ }
+
+ rxdy_gated_en(tp, false);
+
+ return 0;
+}
+
+static int rtl8152_enable(struct usbh_rtl8152 *tp)
+{
+ rtl_set_eee_plus(tp);
+
+ return rtl_enable(tp);
+}
+
+static void rtl8152_disable(struct usbh_rtl8152 *tp)
+{
+ r8152_aldps_en(tp, false);
+ rtl_disable(tp);
+ r8152_aldps_en(tp, true);
+}
+
+static void rtl8152_up(struct usbh_rtl8152 *tp)
+{
+ r8152_aldps_en(tp, false);
+ r8152b_exit_oob(tp);
+ r8152_aldps_en(tp, true);
+}
+
+static void rtl8152_down(struct usbh_rtl8152 *tp)
+{
+ r8152_power_cut_en(tp, false);
+ r8152_aldps_en(tp, false);
+ r8152b_enter_oob(tp);
+ r8152_aldps_en(tp, true);
+}
+
+static int rtl_ops_init(struct usbh_rtl8152 *tp)
+{
+ struct rtl_ops *ops = &tp->rtl_ops;
+ int ret = 0;
+
+ switch (tp->version) {
+ case RTL_VER_01:
+ case RTL_VER_02:
+ case RTL_VER_07:
+ ops->init = r8152b_init;
+ ops->enable = rtl8152_enable;
+ ops->disable = rtl8152_disable;
+ ops->up = rtl8152_up;
+ ops->down = rtl8152_down;
+ // ops->unload = rtl8152_unload;
+ // ops->eee_get = r8152_get_eee;
+ // ops->eee_set = r8152_set_eee;
+ // ops->in_nway = rtl8152_in_nway;
+ // ops->hw_phy_cfg = r8152b_hw_phy_cfg;
+ // ops->autosuspend_en = rtl_runtime_suspend_enable;
+ tp->rx_buf_sz = 16 * 1024;
+ tp->eee_en = true;
+ //tp->eee_adv = MDIO_EEE_100TX;
+ break;
+
+ // case RTL_VER_03:
+ // case RTL_VER_04:
+ // case RTL_VER_05:
+ // case RTL_VER_06:
+ // break;
+
+ // case RTL_VER_08:
+ // case RTL_VER_09:
+ // break;
+
+ // case RTL_VER_11:
+ // case RTL_VER_10:
+ // break;
+
+ // case RTL_VER_12:
+ // case RTL_VER_13:
+ // case RTL_VER_15:
+ // break;
+
+ // case RTL_VER_14:
+ // break;
+
+ default:
+ ret = -USB_ERR_NODEV;
+ USB_LOG_ERR("Unsupport rtl version:%d\r\n", tp->version);
+ break;
+ }
+
+ return ret;
+}
+
+static void rtl8152_set_rx_mode(struct usbh_rtl8152 *tp)
+{
+ uint32_t ocp_data;
+ uint32_t mc_filter[2];
+
+ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data &= ~RCR_ACPT_ALL;
+ ocp_data |= RCR_AB | RCR_APM;
+
+ ocp_data |= RCR_AM;
+ mc_filter[1] = 0xffffffff;
+ mc_filter[0] = 0xffffffff;
+
+ pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(mc_filter), mc_filter);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+}
+
+static int rtl8152_set_speed(struct usbh_rtl8152 *tp, uint8_t autoneg, uint16_t speed, uint8_t duplex)
+{
+ uint16_t bmcr, anar, gbcr;
+
+ anar = r8152_mdio_read(tp, MII_ADVERTISE);
+ anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_100FULL);
+ if (tp->supports_gmii) {
+ gbcr = r8152_mdio_read(tp, MII_CTRL1000);
+ gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ } else {
+ gbcr = 0;
+ }
+
+ if (autoneg == AUTONEG_DISABLE) {
+ if (speed == SPEED_10) {
+ bmcr = 0;
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ } else if (speed == SPEED_100) {
+ bmcr = BMCR_SPEED100;
+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+ } else if (speed == SPEED_1000 && tp->supports_gmii) {
+ bmcr = BMCR_SPEED1000;
+ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
+ } else {
+ return -USB_ERR_INVAL;
+ }
+
+ if (duplex == DUPLEX_FULL)
+ bmcr |= BMCR_FULLDPLX;
+ } else {
+ if (speed == SPEED_10) {
+ if (duplex == DUPLEX_FULL)
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ else
+ anar |= ADVERTISE_10HALF;
+ } else if (speed == SPEED_100) {
+ if (duplex == DUPLEX_FULL) {
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+ } else {
+ anar |= ADVERTISE_10HALF;
+ anar |= ADVERTISE_100HALF;
+ }
+ } else if (speed == SPEED_1000 && tp->supports_gmii) {
+ if (duplex == DUPLEX_FULL) {
+ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
+ } else {
+ anar |= ADVERTISE_10HALF;
+ anar |= ADVERTISE_100HALF;
+ gbcr |= ADVERTISE_1000HALF;
+ }
+ } else {
+ return -USB_ERR_INVAL;
+ }
+
+ bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET;
+ }
+
+ if (tp->supports_gmii)
+ r8152_mdio_write(tp, MII_CTRL1000, gbcr);
+
+ r8152_mdio_write(tp, MII_ADVERTISE, anar);
+ r8152_mdio_write(tp, MII_BMCR, bmcr);
+
+ return 0;
+}
+
+int r8152_write_hwaddr(struct usbh_rtl8152 *tp, unsigned char *mac)
+{
+ unsigned char enetaddr[8] = { 0 };
+ memcpy(enetaddr, mac, 6);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+ pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, enetaddr);
+ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+ return 0;
+}
+
+int usbh_rtl8152_get_connect_status(struct usbh_rtl8152 *rtl8152_class)
+{
+ int ret;
+
+ usbh_int_urb_fill(&rtl8152_class->intin_urb, rtl8152_class->hport, rtl8152_class->intin, g_rtl8152_inttx_buffer, 2, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&rtl8152_class->intin_urb);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (g_rtl8152_inttx_buffer[0] & INTR_LINK) {
+ rtl8152_class->connect_status = true;
+ } else {
+ rtl8152_class->connect_status = false;
+ }
+ return 0;
+}
+
+static int usbh_rtl8152_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ char mac_buffer[12];
+ int ret;
+
+ struct usbh_rtl8152 *rtl8152_class = &g_rtl8152_class;
+
+ memset(rtl8152_class, 0, sizeof(struct usbh_rtl8152));
+
+ rtl8152_class->hport = hport;
+ rtl8152_class->intf = intf;
+
+ hport->config.intf[intf].priv = rtl8152_class;
+
+ rtl8152_class->version = usbh_rtl8152_get_version(rtl8152_class);
+
+ if (rtl8152_class->version == RTL_VER_UNKNOWN) {
+ USB_LOG_ERR("Unknown version 0x%04x\r\n", rtl8152_class->version);
+ return -USB_ERR_NOTSUPP;
+ } else {
+ USB_LOG_INFO("rtl8152 version 0x%04x\r\n", rtl8152_class->version);
+ }
+
+ /* MTU range: 68 - 1500 or 9194 */
+ rtl8152_class->min_mtu = 68;
+ switch (rtl8152_class->version) {
+ case RTL_VER_03:
+ case RTL_VER_04:
+ case RTL_VER_05:
+ case RTL_VER_06:
+ case RTL_VER_08:
+ case RTL_VER_09:
+ case RTL_VER_14:
+ rtl8152_class->max_mtu = size_to_mtu(9 * 1024);
+ break;
+ case RTL_VER_10:
+ case RTL_VER_11:
+ rtl8152_class->max_mtu = size_to_mtu(15 * 1024);
+ break;
+ case RTL_VER_12:
+ case RTL_VER_13:
+ case RTL_VER_15:
+ rtl8152_class->max_mtu = size_to_mtu(16 * 1024);
+ break;
+ case RTL_VER_01:
+ case RTL_VER_02:
+ case RTL_VER_07:
+ default:
+ rtl8152_class->max_mtu = 1500;
+ break;
+ }
+
+ rtl8152_class->saved_wolopts = __rtl_get_wol(rtl8152_class);
+ if (rtl_ops_init(rtl8152_class) < 0) {
+ return -USB_ERR_NODEV;
+ }
+
+ rtl8152_class->rtl_ops.init(rtl8152_class);
+ rtl8152_class->rtl_ops.up(rtl8152_class);
+
+ if (rtl8152_class->rx_buf_sz > CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) {
+ USB_LOG_ERR("rx_buf_sz is overflow, default is %d\r\n", CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE);
+ return -USB_ERR_NOMEM;
+ }
+
+ memset(mac_buffer, 0, 12);
+ ret = usbh_get_string_desc(rtl8152_class->hport, 3, (uint8_t *)mac_buffer);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (int i = 0, j = 0; i < 12; i += 2, j++) {
+ char byte_str[3];
+ byte_str[0] = mac_buffer[i];
+ byte_str[1] = mac_buffer[i + 1];
+ byte_str[2] = '\0';
+
+ uint32_t byte = strtoul(byte_str, NULL, 16);
+ rtl8152_class->mac[j] = (unsigned char)byte;
+ }
+
+ r8152_write_hwaddr(rtl8152_class, rtl8152_class->mac);
+
+ USB_LOG_INFO("RTL8152 MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
+ rtl8152_class->mac[0],
+ rtl8152_class->mac[1],
+ rtl8152_class->mac[2],
+ rtl8152_class->mac[3],
+ rtl8152_class->mac[4],
+ rtl8152_class->mac[5]);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(rtl8152_class->intin, ep_desc);
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+ } else {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(rtl8152_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(rtl8152_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
+
+ USB_LOG_INFO("Register RTL8152 Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_rtl8152_run(rtl8152_class);
+ return 0;
+}
+
+static int usbh_rtl8152_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_rtl8152 *rtl8152_class = (struct usbh_rtl8152 *)hport->config.intf[intf].priv;
+
+ if (rtl8152_class) {
+ if (rtl8152_class->bulkin) {
+ usbh_kill_urb(&rtl8152_class->bulkin_urb);
+ }
+
+ if (rtl8152_class->bulkout) {
+ usbh_kill_urb(&rtl8152_class->bulkout_urb);
+ }
+
+ if (rtl8152_class->intin) {
+ usbh_kill_urb(&rtl8152_class->intin_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister rtl8152 Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_rtl8152_stop(rtl8152_class);
+ }
+
+ memset(rtl8152_class, 0, sizeof(struct usbh_rtl8152));
+ }
+
+ return ret;
+}
+
+void usbh_rtl8152_rx_thread(void *argument)
+{
+ uint32_t g_rtl8152_rx_length;
+ int ret;
+ uint16_t len;
+ uint16_t data_offset;
+
+ USB_LOG_INFO("Create rtl8152 rx thread\r\n");
+ // clang-format off
+find_class:
+ // clang-format on
+ g_rtl8152_class.connect_status = false;
+ if (usbh_find_class_instance("/dev/rtl8152") == NULL) {
+ goto delete;
+ }
+
+ while (g_rtl8152_class.connect_status == false) {
+ ret = usbh_rtl8152_get_connect_status(&g_rtl8152_class);
+ if (ret < 0) {
+ usb_osal_msleep(100);
+ goto find_class;
+ }
+ usb_osal_msleep(128);
+ }
+
+ if (g_rtl8152_class.rtl_ops.enable) {
+ g_rtl8152_class.rtl_ops.enable(&g_rtl8152_class);
+ } else {
+ goto delete;
+ }
+
+ rtl8152_set_rx_mode(&g_rtl8152_class);
+ rtl8152_set_speed(&g_rtl8152_class, AUTONEG_ENABLE, g_rtl8152_class.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL);
+
+ g_rtl8152_rx_length = 0;
+ while (1) {
+ usbh_bulk_urb_fill(&g_rtl8152_class.bulkin_urb, g_rtl8152_class.hport, g_rtl8152_class.bulkin, &g_rtl8152_rx_buffer[g_rtl8152_rx_length], (CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE > (16 * 1024)) ? (16 * 1024) : CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_rtl8152_class.bulkin_urb);
+ if (ret < 0) {
+ goto find_class;
+ }
+
+ g_rtl8152_rx_length += g_rtl8152_class.bulkin_urb.actual_length;
+
+ if (g_rtl8152_rx_length % USB_GET_MAXPACKETSIZE(g_rtl8152_class.bulkin->wMaxPacketSize)) {
+ data_offset = 0;
+
+ USB_LOG_DBG("rxlen:%d\r\n", g_rtl8152_rx_length);
+ while (g_rtl8152_rx_length > 0) {
+ struct rx_desc *rx_desc = (struct rx_desc *)&g_rtl8152_rx_buffer[data_offset];
+
+ len = rx_desc->opts1 & RX_LEN_MASK;
+
+ USB_LOG_DBG("data_offset:%d, eth len:%d\r\n", data_offset, len);
+
+ uint8_t *buf = (uint8_t *)&g_rtl8152_rx_buffer[data_offset + sizeof(struct rx_desc)];
+ usbh_rtl8152_eth_input(buf, len);
+
+ data_offset += (len + sizeof(struct rx_desc));
+ g_rtl8152_rx_length -= (len + sizeof(struct rx_desc));
+
+ if (len & (RX_ALIGN - 1)) {
+ data_offset += (RX_ALIGN - (len & (RX_ALIGN - 1)));
+ g_rtl8152_rx_length -= (RX_ALIGN - (len & (RX_ALIGN - 1)));
+ }
+ }
+ } else {
+ if (g_rtl8152_rx_length > CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) {
+ USB_LOG_ERR("Rx packet is overflow\r\n");
+ g_rtl8152_rx_length = 0;
+ }
+ }
+ }
+ // clang-format off
+delete:
+ USB_LOG_INFO("Delete rtl8152 rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+int usbh_rtl8152_eth_output(uint8_t *buf, uint32_t buflen)
+{
+ uint8_t *buffer;
+ struct tx_desc *tx_desc;
+
+ if (g_rtl8152_class.connect_status == false) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ tx_desc = (struct tx_desc *)g_rtl8152_tx_buffer;
+ tx_desc->opts1 = buflen | TX_FS | TX_LS;
+ tx_desc->opts2 = 0;
+
+ buffer = g_rtl8152_tx_buffer + sizeof(struct tx_desc);
+ memcpy(buffer, buf, buflen);
+
+ USB_LOG_DBG("txlen:%d\r\n", buflen + sizeof(struct tx_desc));
+
+ usbh_bulk_urb_fill(&g_rtl8152_class.bulkout_urb, g_rtl8152_class.hport, g_rtl8152_class.bulkout, g_rtl8152_tx_buffer, buflen + sizeof(struct tx_desc), USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ return usbh_submit_urb(&g_rtl8152_class.bulkout_urb);
+}
+
+__WEAK void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class)
+{
+}
+
+__WEAK void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class)
+{
+}
+
+static const uint16_t rtl_id_table[][2] = {
+ { 0x0BDA, 0x8152 },
+ { 0, 0 },
+};
+
+static const struct usbh_class_driver rtl8152_class_driver = {
+ .driver_name = "rtl8152",
+ .connect = usbh_rtl8152_connect,
+ .disconnect = usbh_rtl8152_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info rtl8152_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = rtl_id_table,
+ .class_driver = &rtl8152_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.h b/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.h
new file mode 100644
index 0000000000..a9aa794355
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/net/usbh_rtl8152.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_RTL8152_H
+#define USBH_RTL8152_H
+
+struct usbh_rtl8152 {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usb_endpoint_descriptor *intin; /* INTR IN endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+ struct usbh_urb intin_urb;
+
+ uint8_t intf;
+
+ uint8_t mac[6];
+ bool connect_status;
+ uint32_t speed[2];
+
+ uint8_t version;
+ uint8_t eee_adv;
+ uint8_t eee_en;
+ uint8_t supports_gmii;
+ uint16_t min_mtu;
+ uint16_t max_mtu;
+ uint16_t ocp_base;
+ uint32_t saved_wolopts;
+ uint32_t rx_buf_sz;
+
+ struct rtl_ops {
+ void (*init)(struct usbh_rtl8152 *tp);
+ int (*enable)(struct usbh_rtl8152 *tp);
+ void (*disable)(struct usbh_rtl8152 *tp);
+ void (*up)(struct usbh_rtl8152 *tp);
+ void (*down)(struct usbh_rtl8152 *tp);
+ void (*unload)(struct usbh_rtl8152 *tp);
+ bool (*in_nway)(struct usbh_rtl8152 *tp);
+ void (*hw_phy_cfg)(struct usbh_rtl8152 *tp);
+ void (*autosuspend_en)(struct usbh_rtl8152 *tp, bool enable);
+ void (*change_mtu)(struct usbh_rtl8152 *tp);
+ } rtl_ops;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_rtl8152_get_connect_status(struct usbh_rtl8152 *rtl8152_class);
+
+void usbh_rtl8152_run(struct usbh_rtl8152 *rtl8152_class);
+void usbh_rtl8152_stop(struct usbh_rtl8152 *rtl8152_class);
+
+int usbh_rtl8152_eth_output(uint8_t *buf, uint32_t buflen);
+void usbh_rtl8152_eth_input(uint8_t *buf, uint32_t buflen);
+void usbh_rtl8152_rx_thread(void *argument);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_RTL8152_H */ \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c
new file mode 100644
index 0000000000..6321d81339
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_ch34x.h"
+
+#define DEV_FORMAT "/dev/ttyUSB%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ch34x_buf[64];
+
+#define CONFIG_USBHOST_MAX_CP210X_CLASS 1
+
+static struct usbh_ch34x g_ch34x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_ch34x *usbh_ch34x_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_ch34x_class[devno], 0, sizeof(struct usbh_ch34x));
+ g_ch34x_class[devno].minor = devno;
+ return &g_ch34x_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_ch34x_class_free(struct usbh_ch34x *ch34x_class)
+{
+ int devno = ch34x_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(ch34x_class, 0, sizeof(struct usbh_ch34x));
+}
+
+static int usbh_ch34x_get_baudrate_div(uint32_t baudrate, uint8_t *factor, uint8_t *divisor)
+{
+ uint8_t a;
+ uint8_t b;
+ uint32_t c;
+
+ switch (baudrate) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baudrate > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baudrate > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baudrate > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (uint8_t)(c / baudrate);
+ if (a == 0 || a == 0xFF) {
+ return -USB_ERR_INVAL;
+ }
+ if ((c / a - baudrate) > (baudrate - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t)(256 - a);
+ break;
+ }
+
+ *factor = a;
+ *divisor = b;
+
+ return 0;
+}
+
+static int usbh_ch34x_get_version(struct usbh_ch34x *ch34x_class)
+{
+ struct usb_setup_packet *setup = ch34x_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = CH34X_READ_VERSION;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 2;
+
+ ret = usbh_control_transfer(ch34x_class->hport, setup, g_ch34x_buf);
+ if (ret < 0) {
+ return ret;
+ }
+
+ USB_LOG_INFO("Ch34x chip version %02x:%02x\r\n", g_ch34x_buf[0], g_ch34x_buf[1]);
+ return ret;
+}
+
+static int usbh_ch34x_flow_ctrl(struct usbh_ch34x *ch34x_class)
+{
+ struct usb_setup_packet *setup = ch34x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = CH34X_WRITE_REG;
+ setup->wValue = 0x2727;
+ setup->wIndex = 0;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ch34x_class->hport, setup, NULL);
+}
+
+int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup = ch34x_class->hport->setup;
+ uint16_t reg_value = 0;
+ uint16_t value = 0;
+ uint8_t factor = 0;
+ uint8_t divisor = 0;
+
+ memcpy((uint8_t *)&ch34x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
+
+ /* refer to https://github.com/WCHSoftGroup/ch341ser_linux/blob/main/driver/ch341.c */
+
+ switch (line_coding->bParityType) {
+ case 0:
+ break;
+ case 1:
+ reg_value |= CH341_L_PO;
+ break;
+ case 2:
+ reg_value |= CH341_L_PE;
+ break;
+ case 3:
+ reg_value |= CH341_L_PM;
+ break;
+ case 4:
+ reg_value |= CH341_L_PS;
+ break;
+ default:
+ return -USB_ERR_INVAL;
+ }
+
+ switch (line_coding->bDataBits) {
+ case 5:
+ reg_value |= CH341_L_D5;
+ break;
+ case 6:
+ reg_value |= CH341_L_D6;
+ break;
+ case 7:
+ reg_value |= CH341_L_D7;
+ break;
+ case 8:
+ reg_value |= CH341_L_D8;
+ break;
+ default:
+ return -USB_ERR_INVAL;
+ }
+
+ if (line_coding->bCharFormat == 2) {
+ reg_value |= CH341_L_SB;
+ }
+
+ reg_value |= 0xC0;
+
+ value |= 0x9c;
+ value |= reg_value << 8;
+
+ usbh_ch34x_get_baudrate_div(line_coding->dwDTERate, &factor, &divisor);
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = CH34X_SERIAL_INIT;
+ setup->wValue = value;
+ setup->wIndex = (factor << 8) | 0x80 | divisor;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ch34x_class->hport, setup, NULL);
+}
+
+int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding)
+{
+ memcpy(line_coding, (uint8_t *)&ch34x_class->line_coding, sizeof(struct cdc_line_coding));
+ return 0;
+}
+
+int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup = ch34x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = CH34X_MODEM_CTRL;
+ setup->wValue = 0x0f | (dtr << 5) | (rts << 6);
+ setup->wIndex = 0;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ch34x_class->hport, setup, NULL);
+}
+
+static int usbh_ch34x_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+
+ struct usbh_ch34x *ch34x_class = usbh_ch34x_class_alloc();
+ if (ch34x_class == NULL) {
+ USB_LOG_ERR("Fail to alloc ch34x_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ ch34x_class->hport = hport;
+ ch34x_class->intf = intf;
+
+ hport->config.intf[intf].priv = ch34x_class;
+
+ usbh_ch34x_get_version(ch34x_class);
+ usbh_ch34x_flow_ctrl(ch34x_class);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ continue;
+ } else {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(ch34x_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(ch34x_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ch34x_class->minor);
+
+ USB_LOG_INFO("Register CH34X Class:%s\r\n", hport->config.intf[intf].devname);
+
+#if 0
+ USB_LOG_INFO("Test ch34x rx and tx and rx for 5 times, baudrate is 115200\r\n");
+
+ struct cdc_line_coding linecoding;
+ uint8_t count = 5;
+
+ linecoding.dwDTERate = 115200;
+ linecoding.bDataBits = 8;
+ linecoding.bParityType = 0;
+ linecoding.bCharFormat = 0;
+ usbh_ch34x_set_line_coding(ch34x_class, &linecoding);
+ usbh_ch34x_set_line_state(ch34x_class, true, false);
+
+ memset(g_ch34x_buf, 'a', sizeof(g_ch34x_buf));
+ ret = usbh_ch34x_bulk_out_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
+ USB_LOG_RAW("out ret:%d\r\n", ret);
+ while (count--) {
+ ret = usbh_ch34x_bulk_in_transfer(ch34x_class, g_ch34x_buf, sizeof(g_ch34x_buf), 0xfffffff);
+ USB_LOG_RAW("in ret:%d\r\n", ret);
+ if (ret > 0) {
+ for (uint32_t i = 0; i < ret; i++) {
+ USB_LOG_RAW("%02x ", g_ch34x_buf[i]);
+ }
+ USB_LOG_RAW("\r\n");
+ }
+ }
+#endif
+ usbh_ch34x_run(ch34x_class);
+ return ret;
+}
+
+static int usbh_ch34x_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_ch34x *ch34x_class = (struct usbh_ch34x *)hport->config.intf[intf].priv;
+
+ if (ch34x_class) {
+ if (ch34x_class->bulkin) {
+ usbh_kill_urb(&ch34x_class->bulkin_urb);
+ }
+
+ if (ch34x_class->bulkout) {
+ usbh_kill_urb(&ch34x_class->bulkout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CH34X Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_ch34x_stop(ch34x_class);
+ }
+
+ usbh_ch34x_class_free(ch34x_class);
+ }
+
+ return ret;
+}
+
+int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &ch34x_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &ch34x_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, ch34x_class->hport, ch34x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+__WEAK void usbh_ch34x_run(struct usbh_ch34x *ch34x_class)
+{
+}
+
+__WEAK void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class)
+{
+}
+
+static const uint16_t ch34x_id_table[][2] = {
+ { 0x1A86, 0x7523 },
+ { 0, 0 },
+};
+
+const struct usbh_class_driver ch34x_class_driver = {
+ .driver_name = "ch34x",
+ .connect = usbh_ch34x_connect,
+ .disconnect = usbh_ch34x_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info ch34x_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = ch34x_id_table,
+ .class_driver = &ch34x_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.h b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.h
new file mode 100644
index 0000000000..c90bf9bda6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ch34x.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CH34X_H
+#define USBH_CH34X_H
+
+#include "usb_cdc.h"
+
+/* Requests */
+#define CH34X_READ_VERSION 0x5F
+#define CH34X_WRITE_REG 0x9A
+#define CH34X_READ_REG 0x95
+#define CH34X_SERIAL_INIT 0xA1
+#define CH34X_MODEM_CTRL 0xA4
+
+// modem control bits
+#define CH34X_BIT_RTS (1 << 6)
+#define CH34X_BIT_DTR (1 << 5)
+
+#define CH341_CTO_O 0x10
+#define CH341_CTO_D 0x20
+#define CH341_CTO_R 0x40
+#define CH341_CTI_C 0x01
+#define CH341_CTI_DS 0x02
+#define CH341_CTRL_RI 0x04
+#define CH341_CTI_DC 0x08
+#define CH341_CTI_ST 0x0f
+
+#define CH341_L_ER 0x80
+#define CH341_L_ET 0x40
+#define CH341_L_PS 0x38
+#define CH341_L_PM 0x28
+#define CH341_L_PE 0x18
+#define CH341_L_PO 0x08
+#define CH341_L_SB 0x04
+#define CH341_L_D8 0x03
+#define CH341_L_D7 0x02
+#define CH341_L_D6 0x01
+#define CH341_L_D5 0x00
+
+struct usbh_ch34x {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+
+ struct cdc_line_coding line_coding;
+
+ uint8_t intf;
+ uint8_t minor;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_ch34x_set_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
+int usbh_ch34x_get_line_coding(struct usbh_ch34x *ch34x_class, struct cdc_line_coding *line_coding);
+int usbh_ch34x_set_line_state(struct usbh_ch34x *ch34x_class, bool dtr, bool rts);
+
+int usbh_ch34x_bulk_in_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+int usbh_ch34x_bulk_out_transfer(struct usbh_ch34x *ch34x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+
+void usbh_ch34x_run(struct usbh_ch34x *ch34x_class);
+void usbh_ch34x_stop(struct usbh_ch34x *ch34x_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CH34X_H */
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c
new file mode 100644
index 0000000000..e27161bdec
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_cp210x.h"
+
+#define DEV_FORMAT "/dev/ttyUSB%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[64];
+
+#define CONFIG_USBHOST_MAX_CP210X_CLASS 4
+
+static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_cp210x *usbh_cp210x_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x));
+ g_cp210x_class[devno].minor = devno;
+ return &g_cp210x_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class)
+{
+ int devno = cp210x_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
+}
+
+static int usbh_cp210x_enable(struct usbh_cp210x *cp210x_class)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_IFC_ENABLE;
+ setup->wValue = 1;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cp210x_class->hport, setup, NULL);
+}
+
+static int usbh_cp210x_set_flow(struct usbh_cp210x *cp210x_class)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_FLOW;
+ setup->wValue = 0;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 16;
+
+ memset(g_cp210x_buf, 0, 16);
+ g_cp210x_buf[13] = 0x20;
+ return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
+}
+
+static int usbh_cp210x_set_chars(struct usbh_cp210x *cp210x_class)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_CHARS;
+ setup->wValue = 0;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 6;
+
+ memset(g_cp210x_buf, 0, 6);
+ g_cp210x_buf[0] = 0x80;
+ g_cp210x_buf[4] = 0x88;
+ g_cp210x_buf[5] = 0x28;
+ return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
+}
+
+static int usbh_cp210x_set_baudrate(struct usbh_cp210x *cp210x_class, uint32_t baudrate)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_BAUDRATE;
+ setup->wValue = 0;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 4;
+
+ memcpy(g_cp210x_buf, (uint8_t *)&baudrate, 4);
+ return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
+}
+
+static int usbh_cp210x_set_data_format(struct usbh_cp210x *cp210x_class, uint8_t databits, uint8_t parity, uint8_t stopbits)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+ uint16_t value;
+
+ value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_LINE_CTL;
+ setup->wValue = value;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cp210x_class->hport, setup, NULL);
+}
+
+static int usbh_cp210x_set_mhs(struct usbh_cp210x *cp210x_class, uint8_t dtr, uint8_t rts, uint8_t dtr_mask, uint8_t rts_mask)
+{
+ struct usb_setup_packet *setup = cp210x_class->hport->setup;
+ uint16_t value;
+
+ value = ((dtr & 0x01) << 0) | ((rts & 0x01) << 1) | ((dtr_mask & 0x01) << 8) | ((rts_mask & 0x01) << 9);
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CP210X_SET_MHS;
+ setup->wValue = value;
+ setup->wIndex = cp210x_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cp210x_class->hport, setup, NULL);
+}
+
+int usbh_cp210x_set_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
+{
+ memcpy((uint8_t *)&cp210x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
+ usbh_cp210x_set_baudrate(cp210x_class, line_coding->dwDTERate);
+ return usbh_cp210x_set_data_format(cp210x_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
+}
+
+int usbh_cp210x_get_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
+{
+ memcpy(line_coding, (uint8_t *)&cp210x_class->line_coding, sizeof(struct cdc_line_coding));
+ return 0;
+}
+
+int usbh_cp210x_set_line_state(struct usbh_cp210x *cp210x_class, bool dtr, bool rts)
+{
+ return usbh_cp210x_set_mhs(cp210x_class, dtr, rts, 1, 1);
+}
+
+static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+
+ struct usbh_cp210x *cp210x_class = usbh_cp210x_class_alloc();
+ if (cp210x_class == NULL) {
+ USB_LOG_ERR("Fail to alloc cp210x_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ cp210x_class->hport = hport;
+ cp210x_class->intf = intf;
+
+ hport->config.intf[intf].priv = cp210x_class;
+
+ usbh_cp210x_enable(cp210x_class);
+ usbh_cp210x_set_flow(cp210x_class);
+ usbh_cp210x_set_chars(cp210x_class);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(cp210x_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(cp210x_class->bulkout, ep_desc);
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cp210x_class->minor);
+
+ USB_LOG_INFO("Register CP210X Class:%s\r\n", hport->config.intf[intf].devname);
+
+#if 0
+ USB_LOG_INFO("Test cp2102 rx and tx and rx for 5 times, baudrate is 115200\r\n");
+
+ struct cdc_line_coding linecoding;
+ uint8_t count = 5;
+
+ linecoding.dwDTERate = 115200;
+ linecoding.bDataBits = 8;
+ linecoding.bParityType = 0;
+ linecoding.bCharFormat = 0;
+ usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
+ usbh_cp210x_set_line_state(cp210x_class, true, false);
+
+ memset(g_cp210x_buf, 'a', sizeof(g_cp210x_buf));
+ ret = usbh_cp210x_bulk_out_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
+ USB_LOG_RAW("out ret:%d\r\n", ret);
+ while (count--) {
+ ret = usbh_cp210x_bulk_in_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
+ USB_LOG_RAW("in ret:%d\r\n", ret);
+ if (ret > 0) {
+ for (uint32_t i = 0; i < ret; i++) {
+ USB_LOG_RAW("%02x ", g_cp210x_buf[i]);
+ }
+ USB_LOG_RAW("\r\n");
+ }
+ }
+#endif
+ usbh_cp210x_run(cp210x_class);
+ return ret;
+}
+
+static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)hport->config.intf[intf].priv;
+
+ if (cp210x_class) {
+ if (cp210x_class->bulkin) {
+ usbh_kill_urb(&cp210x_class->bulkin_urb);
+ }
+
+ if (cp210x_class->bulkout) {
+ usbh_kill_urb(&cp210x_class->bulkout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CP210X Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_cp210x_stop(cp210x_class);
+ }
+
+ usbh_cp210x_class_free(cp210x_class);
+ }
+
+ return ret;
+}
+
+int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &cp210x_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &cp210x_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+__WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
+{
+}
+
+__WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
+{
+}
+
+static const uint16_t cp210x_id_table[][2] = {
+ { 0x10C4, 0xEA60 },
+ { 0, 0 },
+};
+
+const struct usbh_class_driver cp210x_class_driver = {
+ .driver_name = "cp210x",
+ .connect = usbh_cp210x_connect,
+ .disconnect = usbh_cp210x_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = cp210x_id_table,
+ .class_driver = &cp210x_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.h b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.h
new file mode 100644
index 0000000000..d380c36c58
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_cp210x.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CP210X_H
+#define USBH_CP210X_H
+
+#include "usb_cdc.h"
+
+/* Requests */
+#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
+#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 // Set baudrate
+#define CP210X_VENDOR_SPECIFIC 0xFF
+
+struct usbh_cp210x {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+
+ struct cdc_line_coding line_coding;
+
+ uint8_t intf;
+ uint8_t minor;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_cp210x_set_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
+int usbh_cp210x_get_line_coding(struct usbh_cp210x *ftdi_class, struct cdc_line_coding *line_coding);
+int usbh_cp210x_set_line_state(struct usbh_cp210x *ftdi_class, bool dtr, bool rts);
+
+int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+
+void usbh_cp210x_run(struct usbh_cp210x *cp210x_class);
+void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CP210X_H */
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c
new file mode 100644
index 0000000000..c84d2d8c01
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_ftdi.h"
+
+#define DEV_FORMAT "/dev/ttyUSB%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[64];
+
+#define CONFIG_USBHOST_MAX_FTDI_CLASS 4
+
+static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_ftdi *usbh_ftdi_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi));
+ g_ftdi_class[devno].minor = devno;
+ return &g_ftdi_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class)
+{
+ int devno = ftdi_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
+}
+
+static void usbh_ftdi_caculate_baudrate(uint32_t *itdf_divisor, uint32_t actual_baudrate)
+{
+#define FTDI_USB_CLK 48000000
+ int baudrate;
+ uint8_t frac[] = { 0, 8, 4, 2, 6, 10, 12, 14 };
+
+ if (actual_baudrate == 2000000) {
+ *itdf_divisor = 0x01;
+ } else if (actual_baudrate == 3000000) {
+ *itdf_divisor = 0x00;
+ } else {
+ baudrate = actual_baudrate;
+ if (baudrate > 100000 && baudrate < 12000000) {
+ baudrate = (baudrate / 100000) + 100000;
+ }
+ int divisor = FTDI_USB_CLK / baudrate;
+ int frac_bits = 0;
+ for (int i = 0; i < sizeof(frac) / sizeof(frac[0]); i++) {
+ if ((divisor & 0xF) == frac[i]) {
+ frac_bits = i;
+ break;
+ }
+ }
+ divisor >>= 4;
+ divisor &= 0x3FFF;
+ *itdf_divisor = (divisor << 14) | (frac_bits << 8);
+ }
+}
+
+int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_RESET_REQUEST;
+ setup->wValue = 0;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_SET_MODEM_CTRL_REQUEST;
+ setup->wValue = value;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+ uint32_t itdf_divisor;
+ uint16_t value;
+ uint8_t baudrate_high;
+
+ usbh_ftdi_caculate_baudrate(&itdf_divisor, baudrate);
+ value = itdf_divisor & 0xFFFF;
+ baudrate_high = (itdf_divisor >> 16) & 0xff;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_SET_BAUDRATE_REQUEST;
+ setup->wValue = value;
+ setup->wIndex = (baudrate_high << 8) | ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_data_format(struct usbh_ftdi *ftdi_class, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
+{
+ /**
+ * D0-D7 databits BITS_7=7, BITS_8=8
+ * D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
+ * D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
+ * D14 BREAK_OFF=0, BREAK_ON=1
+ **/
+
+ uint16_t value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
+
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_SET_DATA_REQUEST;
+ setup->wValue = value;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_latency_timer(struct usbh_ftdi *ftdi_class, uint16_t value)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_SET_LATENCY_TIMER_REQUEST;
+ setup->wValue = value;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_SET_FLOW_CTRL_REQUEST;
+ setup->wValue = value;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(ftdi_class->hport, setup, NULL);
+}
+
+static int usbh_ftdi_read_modem_status(struct usbh_ftdi *ftdi_class)
+{
+ struct usb_setup_packet *setup = ftdi_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = SIO_POLL_MODEM_STATUS_REQUEST;
+ setup->wValue = 0x0000;
+ setup->wIndex = ftdi_class->intf;
+ setup->wLength = 2;
+
+ ret = usbh_control_transfer(ftdi_class->hport, setup, g_ftdi_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(ftdi_class->modem_status, g_ftdi_buf, 2);
+ return ret;
+}
+
+int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
+{
+ memcpy((uint8_t *)&ftdi_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
+ usbh_ftdi_set_baudrate(ftdi_class, line_coding->dwDTERate);
+ return usbh_ftdi_set_data_format(ftdi_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
+}
+
+int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
+{
+ memcpy(line_coding, (uint8_t *)&ftdi_class->line_coding, sizeof(struct cdc_line_coding));
+ return 0;
+}
+
+int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts)
+{
+ int ret;
+
+ if (dtr) {
+ usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_HIGH);
+ } else {
+ usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_LOW);
+ }
+
+ if (rts) {
+ ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_HIGH);
+ } else {
+ ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_LOW);
+ }
+
+ return ret;
+}
+
+static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+
+ struct usbh_ftdi *ftdi_class = usbh_ftdi_class_alloc();
+ if (ftdi_class == NULL) {
+ USB_LOG_ERR("Fail to alloc ftdi_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ ftdi_class->hport = hport;
+ ftdi_class->intf = intf;
+
+ hport->config.intf[intf].priv = ftdi_class;
+
+ usbh_ftdi_reset(ftdi_class);
+ usbh_ftdi_set_flow_ctrl(ftdi_class, SIO_DISABLE_FLOW_CTRL);
+ usbh_ftdi_set_latency_timer(ftdi_class, 0x10);
+ usbh_ftdi_read_modem_status(ftdi_class);
+ USB_LOG_INFO("modem status:%02x:%02x\r\n", ftdi_class->modem_status[0], ftdi_class->modem_status[1]);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(ftdi_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(ftdi_class->bulkout, ep_desc);
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ftdi_class->minor);
+
+ USB_LOG_INFO("Register FTDI Class:%s\r\n", hport->config.intf[intf].devname);
+
+#if 0
+ USB_LOG_INFO("Test ftdi rx and tx and rx for 5 times, baudrate is 115200\r\n");
+
+ struct cdc_line_coding linecoding;
+ uint8_t count = 5;
+
+ linecoding.dwDTERate = 115200;
+ linecoding.bDataBits = 8;
+ linecoding.bParityType = 0;
+ linecoding.bCharFormat = 0;
+ usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
+ usbh_ftdi_set_line_state(ftdi_class, true, false);
+
+ memset(g_ftdi_buf, 'a', sizeof(g_ftdi_buf));
+ ret = usbh_ftdi_bulk_out_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
+ USB_LOG_RAW("out ret:%d\r\n", ret);
+ while (count--) {
+ ret = usbh_ftdi_bulk_in_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
+ USB_LOG_RAW("in ret:%d\r\n", ret);
+ if (ret > 0) {
+ for (uint32_t i = 0; i < ret; i++) {
+ USB_LOG_RAW("%02x ", g_ftdi_buf[i]);
+ }
+ }
+ USB_LOG_RAW("\r\n");
+ }
+#endif
+ usbh_ftdi_run(ftdi_class);
+ return ret;
+}
+
+static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)hport->config.intf[intf].priv;
+
+ if (ftdi_class) {
+ if (ftdi_class->bulkin) {
+ usbh_kill_urb(&ftdi_class->bulkin_urb);
+ }
+
+ if (ftdi_class->bulkout) {
+ usbh_kill_urb(&ftdi_class->bulkout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister FTDI Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_ftdi_stop(ftdi_class);
+ }
+
+ usbh_ftdi_class_free(ftdi_class);
+ }
+
+ return ret;
+}
+
+int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &ftdi_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &ftdi_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+__WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
+{
+}
+
+__WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
+{
+}
+
+static const uint16_t ftdi_id_table[][2] = {
+ { 0x0403, 0x6001 },
+ { 0x0403, 0x6010 },
+ { 0, 0 },
+};
+
+const struct usbh_class_driver ftdi_class_driver = {
+ .driver_name = "ftdi",
+ .connect = usbh_ftdi_connect,
+ .disconnect = usbh_ftdi_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = ftdi_id_table,
+ .class_driver = &ftdi_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.h b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.h
new file mode 100644
index 0000000000..faaf52147f
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_ftdi.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_FTDI_H
+#define USBH_FTDI_H
+
+#include "usb_cdc.h"
+
+/* Requests */
+#define SIO_RESET_REQUEST 0x00 /* Reset the port */
+#define SIO_SET_MODEM_CTRL_REQUEST 0x01 /* Set the modem control register */
+#define SIO_SET_FLOW_CTRL_REQUEST 0x02 /* Set flow control register */
+#define SIO_SET_BAUDRATE_REQUEST 0x03 /* Set baud rate */
+#define SIO_SET_DATA_REQUEST 0x04 /* Set the data characteristics of the port */
+#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
+#define SIO_SET_EVENT_CHAR_REQUEST 0x06
+#define SIO_SET_ERROR_CHAR_REQUEST 0x07
+#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
+#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
+#define SIO_SET_BITMODE_REQUEST 0x0B
+#define SIO_READ_PINS_REQUEST 0x0C
+#define SIO_READ_EEPROM_REQUEST 0x90
+#define SIO_WRITE_EEPROM_REQUEST 0x91
+#define SIO_ERASE_EEPROM_REQUEST 0x92
+
+#define SIO_DISABLE_FLOW_CTRL 0x0
+#define SIO_RTS_CTS_HS (0x1 << 8)
+#define SIO_DTR_DSR_HS (0x2 << 8)
+#define SIO_XON_XOFF_HS (0x4 << 8)
+
+#define SIO_SET_DTR_MASK 0x1
+#define SIO_SET_DTR_HIGH (1 | (SIO_SET_DTR_MASK << 8))
+#define SIO_SET_DTR_LOW (0 | (SIO_SET_DTR_MASK << 8))
+#define SIO_SET_RTS_MASK 0x2
+#define SIO_SET_RTS_HIGH (2 | (SIO_SET_RTS_MASK << 8))
+#define SIO_SET_RTS_LOW (0 | (SIO_SET_RTS_MASK << 8))
+
+#define SIO_RTS_CTS_HS (0x1 << 8)
+
+struct usbh_ftdi {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+
+ struct cdc_line_coding line_coding;
+
+ uint8_t intf;
+ uint8_t minor;
+ uint8_t modem_status[2];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
+int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding);
+int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts);
+
+int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+
+void usbh_ftdi_run(struct usbh_ftdi *ftdi_class);
+void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_FTDI_H */
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c
new file mode 100644
index 0000000000..2305f44597
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ * Copyright (c) 2024, Derek Konigsberg
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_pl2303.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_pl2303"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/ttyUSB%d"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_pl2303_buf[64];
+
+#define CONFIG_USBHOST_MAX_PL2303_CLASS 4
+
+#define UT_WRITE_VENDOR_DEVICE (USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
+#define UT_READ_VENDOR_DEVICE (USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE)
+
+static struct usbh_pl2303 g_pl2303_class[CONFIG_USBHOST_MAX_PL2303_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_pl2303 *usbh_pl2303_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_PL2303_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_pl2303_class[devno], 0, sizeof(struct usbh_pl2303));
+ g_pl2303_class[devno].minor = devno;
+ return &g_pl2303_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_pl2303_class_free(struct usbh_pl2303 *pl2303_class)
+{
+ int devno = pl2303_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(pl2303_class, 0, sizeof(struct usbh_pl2303));
+}
+
+static int usbh_pl2303_get_chiptype(struct usbh_pl2303 *pl2303_class)
+{
+ int ret = 0;
+
+ switch (pl2303_class->hport->device_desc.bcdDevice) {
+ case 0x0300:
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
+ /* or TA, that is HX with external crystal */
+ break;
+ case 0x0400:
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
+ /* or EA, that is HXD with ESD protection */
+ /* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */
+ break;
+ case 0x0500:
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXD;
+ /* in fact it's TB, that is HXD with external crystal */
+ break;
+ default:
+ /* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud,
+ only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */
+ /* Determine the chip type. This algorithm is taken from Linux. */
+ if (pl2303_class->hport->device_desc.bDeviceClass == 0x02) {
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
+ } else if (pl2303_class->hport->device_desc.bMaxPacketSize0 == 0x40) {
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HX;
+ } else {
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303;
+ }
+ break;
+ }
+
+ /*
+ * The new chip revision PL2303HXN is only compatible with the new
+ * PLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command
+ * PLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX
+ * and PL2303HXN can be distinguished by issuing an old-style request
+ * (on a status register) to the new chip and checking the error.
+ */
+ if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX) {
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+
+ setup->bmRequestType = UT_READ_VENDOR_DEVICE;
+ setup->bRequest = PL2303_SET_REQUEST;
+ setup->wValue = PL2303_STATUS_REG_PL2303HX;
+ setup->wIndex = 0;
+ setup->wLength = 1;
+
+ ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
+ if (ret == -USB_ERR_STALL) {
+ pl2303_class->chiptype = USBH_PL2303_TYPE_PL2303HXN;
+ ret = 0;
+ } else if (ret < 0) {
+ USB_LOG_WRN("Error checking chip type: %d\r\n", ret);
+ return ret;
+ }
+ }
+
+ switch (pl2303_class->chiptype) {
+ case USBH_PL2303_TYPE_PL2303:
+ USB_LOG_INFO("chiptype = 2303\r\n");
+ break;
+ case USBH_PL2303_TYPE_PL2303HX:
+ USB_LOG_INFO("chiptype = 2303HX/TA\r\n");
+ break;
+ case USBH_PL2303_TYPE_PL2303HXN:
+ USB_LOG_INFO("chiptype = 2303HXN\r\n");
+ break;
+ case USBH_PL2303_TYPE_PL2303HXD:
+ USB_LOG_INFO("chiptype = 2303HXD/TB/RA/EA\r\n");
+ break;
+ default:
+ USB_LOG_INFO("chiptype = [%d]\r\n", pl2303_class->chiptype);
+ break;
+ }
+
+ return ret;
+}
+
+static int usbh_pl2303_do(struct usbh_pl2303 *pl2303_class,
+ uint8_t req_type, uint8_t request, uint16_t value, uint16_t index,
+ uint16_t length)
+{
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+
+ setup->bmRequestType = req_type;
+ setup->bRequest = request;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = length;
+
+ return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
+}
+
+int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = pl2303_class->intf;
+ setup->wLength = 7;
+
+ memcpy(g_pl2303_buf, line_coding, sizeof(struct cdc_line_coding));
+
+ return usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
+}
+
+int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding)
+{
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
+ setup->wValue = 0;
+ setup->wIndex = pl2303_class->intf;
+ setup->wLength = 7;
+
+ ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
+ if (ret < 0) {
+ return ret;
+ }
+ memcpy(line_coding, g_pl2303_buf, sizeof(struct cdc_line_coding));
+ return ret;
+}
+
+int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts)
+{
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
+ setup->wValue = (dtr << 0) | (rts << 1);
+ setup->wIndex = pl2303_class->intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(pl2303_class->hport, setup, NULL);
+}
+
+static int usbh_pl2303_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+
+ struct usbh_pl2303 *pl2303_class = usbh_pl2303_class_alloc();
+ if (pl2303_class == NULL) {
+ USB_LOG_ERR("Fail to alloc pl2303_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ pl2303_class->hport = hport;
+ pl2303_class->intf = intf;
+
+ hport->config.intf[intf].priv = pl2303_class;
+
+ do {
+ ret = usbh_pl2303_get_chiptype(pl2303_class);
+ if (ret < 0) {
+ break;
+ }
+
+ /* Startup reset sequence, if necessary for the chip type */
+ if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
+ struct usb_setup_packet *setup = pl2303_class->hport->setup;
+
+ setup->bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ setup->bRequest = PL2303_SET_REQUEST;
+ setup->wValue = 0;
+ setup->wIndex = pl2303_class->intf;
+ setup->wLength = 0;
+
+ ret = usbh_control_transfer(pl2303_class->hport, setup, g_pl2303_buf);
+ if (ret < 0) {
+ USB_LOG_WRN("Initialization reset failed: %d\r\n", ret);
+ break;
+ }
+ }
+
+ if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303) {
+ /* HX variants seem to lock up after a clear stall request. */
+ /*
+ * The FreeBSD code sets the stall flags on the in and out pipes
+ * here. Have no idea exactly how to do this, or if it is necessary.
+ * May just leave this code unwritten until test hardware is available.
+ */
+ } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HX || pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXD) {
+ /* Reset upstream data pipes */
+ ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 8, 0, 0);
+ if (ret < 0) {
+ USB_LOG_WRN("Could not reset upstream data pipes (8,0): %d\r\n", ret);
+ break;
+ }
+ ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 9, 0, 0);
+ if (ret < 0) {
+ USB_LOG_WRN("Could not reset upstream data pipes (9,0): %d\r\n", ret);
+ break;
+ }
+ } else if (pl2303_class->chiptype == USBH_PL2303_TYPE_PL2303HXN) {
+ /* Reset upstream data pipes */
+ ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0);
+ if (ret < 0) {
+ USB_LOG_WRN("Could not reset upstream data pipes (7,3): %d\r\n", ret);
+ break;
+ }
+ }
+
+ /* Final device initialization, if necessary for the chip type */
+ if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303HXN) {
+ if (usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 0, 0) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x0404, 1, 0) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8484, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_READ_VENDOR_DEVICE, PL2303_SET_REQUEST, 0x8383, 0, 1) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 0, 1, 0) < 0 ||
+ usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 1, 0, 0) < 0) {
+ USB_LOG_WRN("Could not complete init sequence\r\n");
+ ret = -USB_ERR_INVAL;
+ break;
+ }
+
+ if (pl2303_class->chiptype != USBH_PL2303_TYPE_PL2303) {
+ ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x44, 0);
+ } else {
+ ret = usbh_pl2303_do(pl2303_class, UT_WRITE_VENDOR_DEVICE, PL2303_SET_REQUEST, 2, 0x24, 0);
+ }
+ if (ret < 0) {
+ USB_LOG_WRN("Could not complete final init request: %d\r\n", ret);
+ break;
+ }
+ }
+ } while (0);
+
+ if (ret < 0) {
+ USB_LOG_ERR("Failed to initialize PL2303 device: %d\r\n", ret);
+ return ret;
+ }
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ continue;
+ } else {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(pl2303_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(pl2303_class->bulkout, ep_desc);
+ }
+ }
+ }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, pl2303_class->minor);
+
+ USB_LOG_INFO("Register PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
+
+#if 0
+ USB_LOG_INFO("Test pl2303 rx and tx and rx for 5 times, baudrate is 115200\r\n");
+
+ struct cdc_line_coding linecoding;
+ uint8_t count = 5;
+
+ linecoding.dwDTERate = 115200;
+ linecoding.bDataBits = 8;
+ linecoding.bParityType = 0;
+ linecoding.bCharFormat = 0;
+ usbh_pl2303_set_line_coding(pl2303_class, &linecoding);
+ usbh_pl2303_set_line_state(pl2303_class, true, false);
+
+ memset(g_pl2303_buf, 'a', sizeof(g_pl2303_buf));
+ ret = usbh_pl2303_bulk_out_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
+ USB_LOG_RAW("out ret:%d\r\n", ret);
+ while (count--) {
+ ret = usbh_pl2303_bulk_in_transfer(pl2303_class, g_pl2303_buf, sizeof(g_pl2303_buf), 0xfffffff);
+ USB_LOG_RAW("in ret:%d\r\n", ret);
+ if (ret > 0) {
+ for (uint32_t i = 0; i < ret; i++) {
+ USB_LOG_RAW("%02x ", g_pl2303_buf[i]);
+ }
+ }
+ USB_LOG_RAW("\r\n");
+ }
+#endif
+
+ usbh_pl2303_run(pl2303_class);
+ return ret;
+}
+
+static int usbh_pl2303_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_pl2303 *pl2303_class = (struct usbh_pl2303 *)hport->config.intf[intf].priv;
+
+ if (pl2303_class) {
+ if (pl2303_class->bulkin) {
+ usbh_kill_urb(&pl2303_class->bulkin_urb);
+ }
+
+ if (pl2303_class->bulkout) {
+ usbh_kill_urb(&pl2303_class->bulkout_urb);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister PL2303 Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_pl2303_stop(pl2303_class);
+ }
+
+ usbh_pl2303_class_free(pl2303_class);
+ }
+
+ return ret;
+}
+
+int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &pl2303_class->bulkin_urb;
+
+ usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkin, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
+{
+ int ret;
+ struct usbh_urb *urb = &pl2303_class->bulkout_urb;
+
+ usbh_bulk_urb_fill(urb, pl2303_class->hport, pl2303_class->bulkout, buffer, buflen, timeout, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+__WEAK void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class)
+{
+}
+
+__WEAK void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class)
+{
+}
+
+static const uint16_t pl2303_id_table[][2] = {
+ { 0x067B, 0x2303 }, // PL2303 Serial (ATEN/IOGEAR UC232A)
+ { 0x067B, 0x23A3 }, // PL2303HXN Serial, type GC
+ { 0x067B, 0x23B3 }, // PL2303HXN Serial, type GB
+ { 0x067B, 0x23C3 }, // PL2303HXN Serial, type GT
+ { 0x067B, 0x23D3 }, // PL2303HXN Serial, type GL
+ { 0x067B, 0x23E3 }, // PL2303HXN Serial, type GE
+ { 0x067B, 0x23F3 }, // PL2303HXN Serial, type GS
+ { 0, 0 },
+};
+
+const struct usbh_class_driver pl2303_class_driver = {
+ .driver_name = "pl2303",
+ .connect = usbh_pl2303_connect,
+ .disconnect = usbh_pl2303_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info pl2303_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = pl2303_id_table,
+ .class_driver = &pl2303_class_driver
+}; \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.h b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.h
new file mode 100644
index 0000000000..2b3d05f9e2
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/vendor/serial/usbh_pl2303.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_PL2303_H
+#define USBH_PL2303_H
+
+#include "usb_cdc.h"
+
+#define PL2303_SET_REQUEST 0x01
+#define PL2303_SET_REQUEST_PL2303HXN 0x80
+#define PL2303_SET_CRTSCTS 0x41
+#define PL2303_SET_CRTSCTS_PL2303X 0x61
+#define PL2303_SET_CRTSCTS_PL2303HXN 0xFA
+#define PL2303_CLEAR_CRTSCTS_PL2303HXN 0xFF
+#define PL2303_CRTSCTS_REG_PL2303HXN 0x0A
+#define PL2303_STATUS_REG_PL2303HX 0x8080
+
+/* Different PL2303 IC types */
+#define USBH_PL2303_TYPE_UNKNOWN 0
+#define USBH_PL2303_TYPE_PL2303 1
+#define USBH_PL2303_TYPE_PL2303HX 2
+#define USBH_PL2303_TYPE_PL2303HXD 3
+#define USBH_PL2303_TYPE_PL2303HXN 4
+
+struct usbh_pl2303 {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+
+ struct usbh_urb bulkout_urb;
+ struct usbh_urb bulkin_urb;
+
+ struct cdc_line_coding linecoding;
+
+ uint8_t intf;
+ uint8_t minor;
+ uint8_t chiptype;
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_pl2303_set_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
+int usbh_pl2303_get_line_coding(struct usbh_pl2303 *pl2303_class, struct cdc_line_coding *line_coding);
+int usbh_pl2303_set_line_state(struct usbh_pl2303 *pl2303_class, bool dtr, bool rts);
+
+int usbh_pl2303_bulk_in_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+int usbh_pl2303_bulk_out_transfer(struct usbh_pl2303 *pl2303_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout);
+
+void usbh_pl2303_run(struct usbh_pl2303 *pl2303_class);
+void usbh_pl2303_stop(struct usbh_pl2303 *pl2303_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_PL2303_H */
diff --git a/components/drivers/usb/cherryusb/class/video/usb_video.h b/components/drivers/usb/cherryusb/class/video/usb_video.h
new file mode 100644
index 0000000000..4997526baa
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/video/usb_video.h
@@ -0,0 +1,1198 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_VIDEO_H
+#define USB_VIDEO_H
+
+#define USB_DEVICE_VIDEO_CLASS_VERSION_1_5 0
+
+/*! @brief Video device subclass code */
+#define VIDEO_SC_UNDEFINED 0x00U
+#define VIDEO_SC_VIDEOCONTROL 0x01U
+#define VIDEO_SC_VIDEOSTREAMING 0x02U
+#define VIDEO_SC_VIDEO_INTERFACE_COLLECTION 0x03U
+
+/*! @brief Video device protocol code */
+#define VIDEO_PC_PROTOCOL_UNDEFINED 0x00U
+#define VIDEO_PC_PROTOCOL_15 0x01U
+
+/*! @brief Video device class-specific descriptor type */
+#define VIDEO_CS_UNDEFINED_DESCRIPTOR_TYPE 0x20U
+#define VIDEO_CS_DEVICE_DESCRIPTOR_TYPE 0x21U
+#define VIDEO_CS_CONFIGURATION_DESCRIPTOR_TYPE 0x22U
+#define VIDEO_CS_STRING_DESCRIPTOR_TYPE 0x23U
+#define VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE 0x24U
+#define VIDEO_CS_ENDPOINT_DESCRIPTOR_TYPE 0x25U
+
+/*! @brief Video device class-specific VC interface descriptor subtype */
+#define VIDEO_VC_DESCRIPTOR_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE 0x03U
+#define VIDEO_VC_SELECTOR_UNIT_DESCRIPTOR_SUBTYPE 0x04U
+#define VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE 0x05U
+#define VIDEO_VC_EXTENSION_UNIT_DESCRIPTOR_SUBTYPE 0x06U
+#define VIDEO_VC_ENCODING_UNIT_DESCRIPTOR_SUBTYPE 0x07U
+
+/*! @brief Video device class-specific VS interface descriptor subtype */
+#define VIDEO_VS_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_VS_OUTPUT_HEADER_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_VS_STILL_IMAGE_FRAME_DESCRIPTOR_SUBTYPE 0x03U
+#define VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_SUBTYPE 0x04U
+#define VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_SUBTYPE 0x05U
+#define VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_SUBTYPE 0x06U
+#define VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE 0x07U
+#define VIDEO_VS_FORMAT_MPEG2TS_DESCRIPTOR_SUBTYPE 0x0AU
+#define VIDEO_VS_FORMAT_DV_DESCRIPTOR_SUBTYPE 0x0CU
+#define VIDEO_VS_COLORFORMAT_DESCRIPTOR_SUBTYPE 0x0DU
+#define VIDEO_VS_FORMAT_FRAME_BASED_DESCRIPTOR_SUBTYPE 0x10U
+#define VIDEO_VS_FRAME_FRAME_BASED_DESCRIPTOR_SUBTYPE 0x11U
+#define VIDEO_VS_FORMAT_STREAM_BASED_DESCRIPTOR_SUBTYPE 0x12U
+#define VIDEO_VS_FORMAT_H264_DESCRIPTOR_SUBTYPE 0x13U
+#define VIDEO_VS_FRAME_H264_DESCRIPTOR_SUBTYPE 0x14U
+#define VIDEO_VS_FORMAT_H264_SIMULCAST_DESCRIPTOR_SUBTYPE 0x15U
+#define VIDEO_VS_FORMAT_VP8_DESCRIPTOR_SUBTYPE 0x16U
+#define VIDEO_VS_FRAME_VP8_DESCRIPTOR_SUBTYPE 0x17U
+#define VIDEO_VS_FORMAT_VP8_SIMULCAST_DESCRIPTOR_SUBTYPE 0x18U
+
+/*! @brief Video device class-specific VC endpoint descriptor subtype */
+#define VIDEO_EP_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_EP_GENERAL_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_EP_ENDPOINT_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_EP_INTERRUPT_DESCRIPTOR_SUBTYPE 0x03U
+
+/*! @brief Video device class-specific request code */
+#define VIDEO_REQUEST_UNDEFINED 0x00U
+#define VIDEO_REQUEST_SET_CUR 0x01U
+#define VIDEO_REQUEST_SET_CUR_ALL 0x11U
+#define VIDEO_REQUEST_GET_CUR 0x81U
+#define VIDEO_REQUEST_GET_MIN 0x82U
+#define VIDEO_REQUEST_GET_MAX 0x83U
+#define VIDEO_REQUEST_GET_RES 0x84U
+#define VIDEO_REQUEST_GET_LEN 0x85U
+#define VIDEO_REQUEST_GET_INFO 0x86U
+#define VIDEO_REQUEST_GET_DEF 0x87U
+#define VIDEO_REQUEST_GET_CUR_ALL 0x91U
+#define VIDEO_REQUEST_GET_MIN_ALL 0x92U
+#define VIDEO_REQUEST_GET_MAX_ALL 0x93U
+#define VIDEO_REQUEST_GET_RES_ALL 0x94U
+#define VIDEO_REQUEST_GET_DEF_ALL 0x97U
+
+/*! @brief Video device class-specific VideoControl interface control selector */
+#define VIDEO_VC_CONTROL_UNDEFINED 0x00U
+#define VIDEO_VC_VIDEO_POWER_MODE_CONTROL 0x01U
+#define VIDEO_VC_REQUEST_ERROR_CODE_CONTROL 0x02U
+
+/*! @brief Video device class-specific Terminal control selector */
+#define VIDEO_TE_CONTROL_UNDEFINED 0x00U
+
+/*! @brief Video device class-specific Selector Unit control selector */
+#define VIDEO_SU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_SU_INPUT_SELECT_CONTROL 0x01U
+
+/*! @brief Video device class-specific Camera Terminal control selector */
+#define VIDEO_CT_CONTROL_UNDEFINED 0x00U
+#define VIDEO_CT_SCANNING_MODE_CONTROL 0x01U
+#define VIDEO_CT_AE_MODE_CONTROL 0x02U
+#define VIDEO_CT_AE_PRIORITY_CONTROL 0x03U
+#define VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04U
+#define VIDEO_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05U
+#define VIDEO_CT_FOCUS_ABSOLUTE_CONTROL 0x06U
+#define VIDEO_CT_FOCUS_RELATIVE_CONTROL 0x07U
+#define VIDEO_CT_FOCUS_AUTO_CONTROL 0x08U
+#define VIDEO_CT_IRIS_ABSOLUTE_CONTROL 0x09U
+#define VIDEO_CT_IRIS_RELATIVE_CONTROL 0x0AU
+#define VIDEO_CT_ZOOM_ABSOLUTE_CONTROL 0x0BU
+#define VIDEO_CT_ZOOM_RELATIVE_CONTROL 0x0CU
+#define VIDEO_CT_PANTILT_ABSOLUTE_CONTROL 0x0DU
+#define VIDEO_CT_PANTILT_RELATIVE_CONTROL 0x0EU
+#define VIDEO_CT_ROLL_ABSOLUTE_CONTROL 0x0FU
+#define VIDEO_CT_ROLL_RELATIVE_CONTROL 0x10U
+#define VIDEO_CT_PRIVACY_CONTROL 0x11U
+#define VIDEO_CT_FOCUS_SIMPLE_CONTROL 0x12U
+#define VIDEO_CT_WINDOW_CONTROL 0x13U
+#define VIDEO_CT_REGION_OF_INTEREST_CONTROL 0x14U
+
+/*! @brief Video device class-specific Processing Unit control selector */
+#define VIDEO_PU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01U
+#define VIDEO_PU_BRIGHTNESS_CONTROL 0x02U
+#define VIDEO_PU_CONTRAST_CONTROL 0x03U
+#define VIDEO_PU_GAIN_CONTROL 0x04U
+#define VIDEO_PU_POWER_LINE_FREQUENCY_CONTROL 0x05U
+#define VIDEO_PU_HUE_CONTROL 0x06U
+#define VIDEO_PU_SATURATION_CONTROL 0x07U
+#define VIDEO_PU_SHARPNESS_CONTROL 0x08U
+#define VIDEO_PU_GAMMA_CONTROL 0x09U
+#define VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0AU
+#define VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0BU
+#define VIDEO_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0CU
+#define VIDEO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0DU
+#define VIDEO_PU_DIGITAL_MULTIPLIER_CONTROL 0x0EU
+#define VIDEO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0FU
+#define VIDEO_PU_HUE_AUTO_CONTROL 0x10U
+#define VIDEO_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11U
+#define VIDEO_PU_ANALOG_LOCK_STATUS_CONTROL 0x12U
+#define VIDEO_PU_CONTRAST_AUTO_CONTROL 0x13U
+
+/*! @brief Video device class-specific Encoding Unit control selector */
+#define VIDEO_EU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_EU_SELECT_LAYER_CONTROL 0x01U
+#define VIDEO_EU_PROFILE_TOOLSET_CONTROL 0x02U
+#define VIDEO_EU_VIDEO_RESOLUTION_CONTROL 0x03U
+#define VIDEO_EU_MIN_FRAME_INTERVAL_CONTROL 0x04U
+#define VIDEO_EU_SLICE_MODE_CONTROL 0x05U
+#define VIDEO_EU_RATE_CONTROL_MODE_CONTROL 0x06U
+#define VIDEO_EU_AVERAGE_BITRATE_CONTROL 0x07U
+#define VIDEO_EU_CPB_SIZE_CONTROL 0x08U
+#define VIDEO_EU_PEAK_BIT_RATE_CONTROL 0x09U
+#define VIDEO_EU_QUANTIZATION_PARAMS_CONTROL 0x0AU
+#define VIDEO_EU_SYNC_REF_FRAME_CONTROL 0x0BU
+#define VIDEO_EU_LTR_BUFFER_CONTROL 0x0CU
+#define VIDEO_EU_LTR_PICTURE_CONTROL 0x0DU
+#define VIDEO_EU_LTR_VALIDATION_CONTROL 0x0EU
+#define VIDEO_EU_LEVEL_IDC_LIMIT_CONTROL 0x0FU
+#define VIDEO_EU_SEI_PAYLOADTYPE_CONTROL 0x10U
+#define VIDEO_EU_QP_RANGE_CONTROL 0x11U
+#define VIDEO_EU_PRIORITY_CONTROL 0x12U
+#define VIDEO_EU_START_OR_STOP_LAYER_CONTROL 0x13U
+#define VIDEO_EU_ERROR_RESILIENCY_CONTROL 0x14U
+
+/*! @brief Video device class-specific Extension Unit control selector */
+#define VIDEO_XU_CONTROL_UNDEFINED 0x00U
+
+/*! @brief Video device class-specific VideoStreaming Interface control selector */
+#define VIDEO_VS_CONTROL_UNDEFINED 0x00U
+#define VIDEO_VS_PROBE_CONTROL 0x01U
+#define VIDEO_VS_COMMIT_CONTROL 0x02U
+#define VIDEO_VS_STILL_PROBE_CONTROL 0x03U
+#define VIDEO_VS_STILL_COMMIT_CONTROL 0x04U
+#define VIDEO_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05U
+#define VIDEO_VS_STREAM_ERROR_CODE_CONTROL 0x06U
+#define VIDEO_VS_GENERATE_KEY_FRAME_CONTROL 0x07U
+#define VIDEO_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08U
+#define VIDEO_VS_SYNCH_DELAY_CONTROL 0x09U
+
+/*! @}*/
+
+/*!
+ * @name USB Video class terminal types
+ * @{
+ */
+
+/*! @brief Video device USB terminal type */
+#define VIDEO_TT_VENDOR_SPECIFIC 0x0100U
+#define VIDEO_TT_STREAMING 0x0101U
+
+/*! @brief Video device input terminal type */
+#define VIDEO_ITT_VENDOR_SPECIFIC 0x0200U
+#define VIDEO_ITT_CAMERA 0x0201U
+#define VIDEO_ITT_MEDIA_TRANSPORT_INPUT 0x0202U
+
+/*! @brief Video device output terminal type */
+#define VIDEO_OTT_VENDOR_SPECIFIC 0x0300U
+#define VIDEO_OTT_DISPLAY 0x0301U
+#define VIDEO_OTT_MEDIA_TRANSPORT_OUTPUT 0x0302U
+
+/*! @brief Video device external terminal type */
+#define VIDEO_ET_VENDOR_SPECIFIC 0x0400U
+#define VIDEO_ET_COMPOSITE_CONNECTOR 0x0401U
+#define VIDEO_ET_SVIDEO_CONNECTOR 0x0402U
+#define VIDEO_ET_COMPONENT_CONNECTOR 0x0403U
+
+/*! @}*/
+
+/*!
+ * @name USB Video class setup request types
+ * @{
+ */
+
+/*! @brief Video device class setup request set type */
+#define VIDEO_SET_REQUEST_INTERFACE 0x21U
+#define VIDEO_SET_REQUEST_ENDPOINT 0x22U
+
+/*! @brief Video device class setup request get type */
+#define VIDEO_GET_REQUEST_INTERFACE 0xA1U
+#define VIDEO_GET_REQUEST_ENDPOINT 0xA2U
+
+/*! @}*/
+
+/*! @brief Video device still image trigger control */
+#define VIDEO_STILL_IMAGE_TRIGGER_NORMAL_OPERATION 0x00U
+#define VIDEO_STILL_IMAGE_TRIGGER_TRANSMIT_STILL_IMAGE 0x01U
+#define VIDEO_STILL_IMAGE_TRIGGER_TRANSMIT_STILL_IMAGE_VS_DEDICATED_BULK_PIPE 0x02U
+#define VIDEO_STILL_IMAGE_TRIGGER_ABORT_STILL_IMAGE_TRANSMISSION 0x03U
+
+/*!
+ * @name USB Video device class-specific request commands
+ * @{
+ */
+
+/*! @brief Video device class-specific request GET CUR COMMAND */
+#define VIDEO_GET_CUR_VC_POWER_MODE_CONTROL 0x8101U
+#define VIDEO_GET_CUR_VC_ERROR_CODE_CONTROL 0x8102U
+
+#define VIDEO_GET_CUR_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8121U
+#define VIDEO_GET_CUR_PU_BRIGHTNESS_CONTROL 0x8122U
+#define VIDEO_GET_CUR_PU_CONTRACT_CONTROL 0x8123U
+#define VIDEO_GET_CUR_PU_GAIN_CONTROL 0x8124U
+#define VIDEO_GET_CUR_PU_POWER_LINE_FREQUENCY_CONTROL 0x8125U
+#define VIDEO_GET_CUR_PU_HUE_CONTROL 0x8126U
+#define VIDEO_GET_CUR_PU_SATURATION_CONTROL 0x8127U
+#define VIDEO_GET_CUR_PU_SHARRNESS_CONTROL 0x8128U
+#define VIDEO_GET_CUR_PU_GAMMA_CONTROL 0x8129U
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x812AU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x812BU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x812CU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x812DU
+#define VIDEO_GET_CUR_PU_DIGITAL_MULTIPLIER_CONTROL 0x812EU
+#define VIDEO_GET_CUR_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x812FU
+#define VIDEO_GET_CUR_PU_HUE_AUTO_CONTROL 0x8130U
+#define VIDEO_GET_CUR_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x8131U
+#define VIDEO_GET_CUR_PU_ANALOG_LOCK_STATUS_CONTROL 0x8132U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_PU_CONTRAST_AUTO_CONTROL 0x8133U
+#endif
+
+#define VIDEO_GET_CUR_CT_SCANNING_MODE_CONTROL 0x8141U
+#define VIDEO_GET_CUR_CT_AE_MODE_CONTROL 0x8142U
+#define VIDEO_GET_CUR_CT_AE_PRIORITY_CONTROL 0x8143U
+#define VIDEO_GET_CUR_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8144U
+#define VIDEO_GET_CUR_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x8145U
+#define VIDEO_GET_CUR_CT_FOCUS_ABSOLUTE_CONTROL 0x8146U
+#define VIDEO_GET_CUR_CT_FOCUS_RELATIVE_CONTROL 0x8147U
+#define VIDEO_GET_CUR_CT_FOCUS_AUTO_CONTROL 0x8148U
+#define VIDEO_GET_CUR_CT_IRIS_ABSOLUTE_CONTROL 0x8149U
+#define VIDEO_GET_CUR_CT_IRIS_RELATIVE_CONTROL 0x814AU
+#define VIDEO_GET_CUR_CT_ZOOM_ABSOLUTE_CONTROL 0x814BU
+#define VIDEO_GET_CUR_CT_ZOOM_RELATIVE_CONTROL 0x814CU
+#define VIDEO_GET_CUR_CT_PANTILT_ABSOLUTE_CONTROL 0x814DU
+#define VIDEO_GET_CUR_CT_PANTILT_RELATIVE_CONTROL 0x814EU
+#define VIDEO_GET_CUR_CT_ROLL_ABSOLUTE_CONTROL 0x814FU
+#define VIDEO_GET_CUR_CT_ROLL_RELATIVE_CONTROL 0x8150U
+#define VIDEO_GET_CUR_CT_PRIVACY_CONTROL 0x8151U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_CT_FOCUS_SIMPLE_CONTROL 0x8152U
+#define VIDEO_GET_CUR_CT_DIGITAL_WINDOW_CONTROL 0x8153U
+#define VIDEO_GET_CUR_CT_REGION_OF_INTEREST_CONTROL 0x8154U
+#endif
+
+#define VIDEO_GET_CUR_VS_PROBE_CONTROL 0x8161U
+#define VIDEO_GET_CUR_VS_COMMIT_CONTROL 0x8162U
+#define VIDEO_GET_CUR_VS_STILL_PROBE_CONTROL 0x8163U
+#define VIDEO_GET_CUR_VS_STILL_COMMIT_CONTROL 0x8164U
+#define VIDEO_GET_CUR_VS_STILL_IMAGE_TRIGGER_CONTROL 0x8165U
+#define VIDEO_GET_CUR_VS_STREAM_ERROR_CODE_CONTROL 0x8166U
+#define VIDEO_GET_CUR_VS_GENERATE_KEY_FRAME_CONTROL 0x8167U
+#define VIDEO_GET_CUR_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8168U
+#define VIDEO_GET_CUR_VS_SYNCH_DELAY_CONTROL 0x8169U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_EU_SELECT_LAYER_CONTROL 0x8181U
+#define VIDEO_GET_CUR_EU_PROFILE_TOOLSET_CONTROL 0x8182U
+#define VIDEO_GET_CUR_EU_VIDEO_RESOLUTION_CONTROL 0x8183U
+#define VIDEO_GET_CUR_EU_MIN_FRAME_INTERVAL_CONTROL 0x8184U
+#define VIDEO_GET_CUR_EU_SLICE_MODE_CONTROL 0x8185U
+#define VIDEO_GET_CUR_EU_RATE_CONTROL_MODE_CONTROL 0x8186U
+#define VIDEO_GET_CUR_EU_AVERAGE_BITRATE_CONTROL 0x8187U
+#define VIDEO_GET_CUR_EU_CPB_SIZE_CONTROL 0x8188U
+#define VIDEO_GET_CUR_EU_PEAK_BIT_RATE_CONTROL 0x8189U
+#define VIDEO_GET_CUR_EU_QUANTIZATION_PARAMS_CONTROL 0x818AU
+#define VIDEO_GET_CUR_EU_SYNC_REF_FRAME_CONTROL 0x818BU
+#define VIDEO_GET_CUR_EU_LTR_BUFFER_CONTROL 0x818CU
+#define VIDEO_GET_CUR_EU_LTR_PICTURE_CONTROL 0x818DU
+#define VIDEO_GET_CUR_EU_LTR_VALIDATION_CONTROL 0x818EU
+#define VIDEO_GET_CUR_EU_LEVEL_IDC_LIMIT_CONTROL 0x818FU
+#define VIDEO_GET_CUR_EU_SEI_PAYLOADTYPE_CONTROL 0x8190U
+#define VIDEO_GET_CUR_EU_QP_RANGE_CONTROL 0x8191U
+#define VIDEO_GET_CUR_EU_PRIORITY_CONTROL 0x8192U
+#define VIDEO_GET_CUR_EU_START_OR_STOP_LAYER_CONTROL 0x8193U
+#define VIDEO_GET_CUR_EU_ERROR_RESILIENCY_CONTROL 0x8194U
+#endif
+
+/*! @brief Video device class-specific request GET MIN COMMAND */
+#define VIDEO_GET_MIN_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8221U
+#define VIDEO_GET_MIN_PU_BRIGHTNESS_CONTROL 0x8222U
+#define VIDEO_GET_MIN_PU_CONTRACT_CONTROL 0x8223U
+#define VIDEO_GET_MIN_PU_GAIN_CONTROL 0x8224U
+#define VIDEO_GET_MIN_PU_HUE_CONTROL 0x8226U
+#define VIDEO_GET_MIN_PU_SATURATION_CONTROL 0x8227U
+#define VIDEO_GET_MIN_PU_SHARRNESS_CONTROL 0x8228U
+#define VIDEO_GET_MIN_PU_GAMMA_CONTROL 0x8229U
+#define VIDEO_GET_MIN_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x822AU
+#define VIDEO_GET_MIN_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x822CU
+#define VIDEO_GET_MIN_PU_DIGITAL_MULTIPLIER_CONTROL 0x822EU
+#define VIDEO_GET_MIN_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x822FU
+
+#define VIDEO_GET_MIN_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8244U
+#define VIDEO_GET_MIN_CT_FOCUS_ABSOLUTE_CONTROL 0x8246U
+#define VIDEO_GET_MIN_CT_FOCUS_RELATIVE_CONTROL 0x8247U
+#define VIDEO_GET_MIN_CT_IRIS_ABSOLUTE_CONTROL 0x8249U
+#define VIDEO_GET_MIN_CT_ZOOM_ABSOLUTE_CONTROL 0x824BU
+#define VIDEO_GET_MIN_CT_ZOOM_RELATIVE_CONTROL 0x824CU
+#define VIDEO_GET_MIN_CT_PANTILT_ABSOLUTE_CONTROL 0x824DU
+#define VIDEO_GET_MIN_CT_PANTILT_RELATIVE_CONTROL 0x824EU
+#define VIDEO_GET_MIN_CT_ROLL_ABSOLUTE_CONTROL 0x824FU
+#define VIDEO_GET_MIN_CT_ROLL_RELATIVE_CONTROL 0x8250U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MIN_CT_DIGITAL_WINDOW_CONTROL 0x8251U
+#define VIDEO_GET_MIN_CT_REGION_OF_INTEREST_CONTROL 0x8252U
+#endif
+
+#define VIDEO_GET_MIN_VS_PROBE_CONTROL 0x8261U
+#define VIDEO_GET_MIN_VS_STILL_PROBE_CONTROL 0x8263U
+#define VIDEO_GET_MIN_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8268U
+#define VIDEO_GET_MIN_VS_SYNCH_DELAY_CONTROL 0x8269U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MIN_EU_VIDEO_RESOLUTION_CONTROL 0x8283U
+#define VIDEO_GET_MIN_EU_MIN_FRAME_INTERVAL_CONTROL 0x8284U
+#define VIDEO_GET_MIN_EU_SLICE_MODE_CONTROL 0x8285U
+#define VIDEO_GET_MIN_EU_AVERAGE_BITRATE_CONTROL 0x8287U
+#define VIDEO_GET_MIN_EU_CPB_SIZE_CONTROL 0x8288U
+#define VIDEO_GET_MIN_EU_PEAK_BIT_RATE_CONTROL 0x8289U
+#define VIDEO_GET_MIN_EU_QUANTIZATION_PARAMS_CONTROL 0x828AU
+#define VIDEO_GET_MIN_EU_SYNC_REF_FRAME_CONTROL 0x828BU
+#define VIDEO_GET_MIN_EU_LEVEL_IDC_LIMIT_CONTROL 0x828FU
+#define VIDEO_GET_MIN_EU_SEI_PAYLOADTYPE_CONTROL 0x8290U
+#define VIDEO_GET_MIN_EU_QP_RANGE_CONTROL 0x8291U
+#endif
+
+/*! @brief Video device class-specific request GET MAX COMMAND */
+#define VIDEO_GET_MAX_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8321U
+#define VIDEO_GET_MAX_PU_BRIGHTNESS_CONTROL 0x8322U
+#define VIDEO_GET_MAX_PU_CONTRACT_CONTROL 0x8323U
+#define VIDEO_GET_MAX_PU_GAIN_CONTROL 0x8324U
+#define VIDEO_GET_MAX_PU_HUE_CONTROL 0x8326U
+#define VIDEO_GET_MAX_PU_SATURATION_CONTROL 0x8327U
+#define VIDEO_GET_MAX_PU_SHARRNESS_CONTROL 0x8328U
+#define VIDEO_GET_MAX_PU_GAMMA_CONTROL 0x8329U
+#define VIDEO_GET_MAX_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x832AU
+#define VIDEO_GET_MAX_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x832CU
+#define VIDEO_GET_MAX_PU_DIGITAL_MULTIPLIER_CONTROL 0x832EU
+#define VIDEO_GET_MAX_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x832FU
+
+#define VIDEO_GET_MAX_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8344U
+#define VIDEO_GET_MAX_CT_FOCUS_ABSOLUTE_CONTROL 0x8346U
+#define VIDEO_GET_MAX_CT_FOCUS_RELATIVE_CONTROL 0x8347U
+#define VIDEO_GET_MAX_CT_IRIS_ABSOLUTE_CONTROL 0x8349U
+#define VIDEO_GET_MAX_CT_ZOOM_ABSOLUTE_CONTROL 0x834BU
+#define VIDEO_GET_MAX_CT_ZOOM_RELATIVE_CONTROL 0x834CU
+#define VIDEO_GET_MAX_CT_PANTILT_ABSOLUTE_CONTROL 0x834DU
+#define VIDEO_GET_MAX_CT_PANTILT_RELATIVE_CONTROL 0x834EU
+#define VIDEO_GET_MAX_CT_ROLL_ABSOLUTE_CONTROL 0x834FU
+#define VIDEO_GET_MAX_CT_ROLL_RELATIVE_CONTROL 0x8350U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MAX_CT_DIGITAL_WINDOW_CONTROL 0x8351U
+#define VIDEO_GET_MAX_CT_REGION_OF_INTEREST_CONTROL 0x8352U
+#endif
+
+#define VIDEO_GET_MAX_VS_PROBE_CONTROL 0x8361U
+#define VIDEO_GET_MAX_VS_STILL_PROBE_CONTROL 0x8363U
+#define VIDEO_GET_MAX_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8368U
+#define VIDEO_GET_MAX_VS_SYNCH_DELAY_CONTROL 0x8369U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MAX_EU_VIDEO_RESOLUTION_CONTROL 0x8383U
+#define VIDEO_GET_MAX_EU_MIN_FRAME_INTERVAL_CONTROL 0x8384U
+#define VIDEO_GET_MAX_EU_SLICE_MODE_CONTROL 0x8385U
+#define VIDEO_GET_MAX_EU_AVERAGE_BITRATE_CONTROL 0x8387U
+#define VIDEO_GET_MAX_EU_CPB_SIZE_CONTROL 0x8388U
+#define VIDEO_GET_MAX_EU_PEAK_BIT_RATE_CONTROL 0x8389U
+#define VIDEO_GET_MAX_EU_QUANTIZATION_PARAMS_CONTROL 0x838AU
+#define VIDEO_GET_MAX_EU_SYNC_REF_FRAME_CONTROL 0x838BU
+#define VIDEO_GET_MAX_EU_LTR_BUFFER_CONTROL 0x838CU
+#define VIDEO_GET_MAX_EU_LEVEL_IDC_LIMIT_CONTROL 0x838FU
+#define VIDEO_GET_MAX_EU_SEI_PAYLOADTYPE_CONTROL 0x8390U
+#define VIDEO_GET_MAX_EU_QP_RANGE_CONTROL 0x8391U
+#endif
+
+/*! @brief Video device class-specific request GET RES COMMAND */
+#define VIDEO_GET_RES_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8421U
+#define VIDEO_GET_RES_PU_BRIGHTNESS_CONTROL 0x8422U
+#define VIDEO_GET_RES_PU_CONTRACT_CONTROL 0x8423U
+#define VIDEO_GET_RES_PU_GAIN_CONTROL 0x8424U
+#define VIDEO_GET_RES_PU_HUE_CONTROL 0x8426U
+#define VIDEO_GET_RES_PU_SATURATION_CONTROL 0x8427U
+#define VIDEO_GET_RES_PU_SHARRNESS_CONTROL 0x8428U
+#define VIDEO_GET_RES_PU_GAMMA_CONTROL 0x8429U
+#define VIDEO_GET_RES_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x842AU
+#define VIDEO_GET_RES_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x842CU
+#define VIDEO_GET_RES_PU_DIGITAL_MULTIPLIER_CONTROL 0x842EU
+#define VIDEO_GET_RES_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x842FU
+
+#define VIDEO_GET_RES_CT_AE_MODE_CONTROL 0x8442U
+#define VIDEO_GET_RES_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8444U
+#define VIDEO_GET_RES_CT_FOCUS_ABSOLUTE_CONTROL 0x8446U
+#define VIDEO_GET_RES_CT_FOCUS_RELATIVE_CONTROL 0x8447U
+#define VIDEO_GET_RES_CT_IRIS_ABSOLUTE_CONTROL 0x8449U
+#define VIDEO_GET_RES_CT_ZOOM_ABSOLUTE_CONTROL 0x844BU
+#define VIDEO_GET_RES_CT_ZOOM_RELATIVE_CONTROL 0x844CU
+#define VIDEO_GET_RES_CT_PANTILT_ABSOLUTE_CONTROL 0x844DU
+#define VIDEO_GET_RES_CT_PANTILT_RELATIVE_CONTROL 0x844EU
+#define VIDEO_GET_RES_CT_ROLL_ABSOLUTE_CONTROL 0x844FU
+#define VIDEO_GET_RES_CT_ROLL_RELATIVE_CONTROL 0x8450U
+
+#define VIDEO_GET_RES_VS_PROBE_CONTROL 0x8461U
+#define VIDEO_GET_RES_VS_STILL_PROBE_CONTROL 0x8463U
+#define VIDEO_GET_RES_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8468U
+#define VIDEO_GET_RES_VS_SYNCH_DELAY_CONTROL 0x8469U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_RES_EU_AVERAGE_BITRATE_CONTROL 0x8487U
+#define VIDEO_GET_RES_EU_CPB_SIZE_CONTROL 0x8488U
+#define VIDEO_GET_RES_EU_PEAK_BIT_RATE_CONTROL 0x8489U
+#define VIDEO_GET_RES_EU_QUANTIZATION_PARAMS_CONTROL 0x848AU
+#define VIDEO_GET_RES_EU_ERROR_RESILIENCY_CONTROL 0x8494U
+#endif
+
+/*! @brief Video device class-specific request GET LEN COMMAND */
+
+#define VIDEO_GET_LEN_VS_PROBE_CONTROL 0x8561U
+#define VIDEO_GET_LEN_VS_COMMIT_CONTROL 0x8562U
+#define VIDEO_GET_LEN_VS_STILL_PROBE_CONTROL 0x8563U
+#define VIDEO_GET_LEN_VS_STILL_COMMIT_CONTROL 0x8564U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_LEN_EU_SELECT_LAYER_CONTROL 0x8581U
+#define VIDEO_GET_LEN_EU_PROFILE_TOOLSET_CONTROL 0x8582U
+#define VIDEO_GET_LEN_EU_VIDEO_RESOLUTION_CONTROL 0x8583U
+#define VIDEO_GET_LEN_EU_MIN_FRAME_INTERVAL_CONTROL 0x8584U
+#define VIDEO_GET_LEN_EU_SLICE_MODE_CONTROL 0x8585U
+#define VIDEO_GET_LEN_EU_RATE_CONTROL_MODE_CONTROL 0x8586U
+#define VIDEO_GET_LEN_EU_AVERAGE_BITRATE_CONTROL 0x8587U
+#define VIDEO_GET_LEN_EU_CPB_SIZE_CONTROL 0x8588U
+#define VIDEO_GET_LEN_EU_PEAK_BIT_RATE_CONTROL 0x8589U
+#define VIDEO_GET_LEN_EU_QUANTIZATION_PARAMS_CONTROL 0x858AU
+#define VIDEO_GET_LEN_EU_SYNC_REF_FRAME_CONTROL 0x858BU
+#define VIDEO_GET_LEN_EU_LTR_BUFFER_CONTROL 0x858CU
+#define VIDEO_GET_LEN_EU_LTR_PICTURE_CONTROL 0x858DU
+#define VIDEO_GET_LEN_EU_LTR_VALIDATION_CONTROL 0x858EU
+#define VIDEO_GET_LEN_EU_QP_RANGE_CONTROL 0x8591U
+#define VIDEO_GET_LEN_EU_PRIORITY_CONTROL 0x8592U
+#define VIDEO_GET_LEN_EU_START_OR_STOP_LAYER_CONTROL 0x8593U
+#endif
+
+/*! @brief Video device class-specific request GET INFO COMMAND */
+#define VIDEO_GET_INFO_VC_POWER_MODE_CONTROL 0x8601U
+#define VIDEO_GET_INFO_VC_ERROR_CODE_CONTROL 0x8602U
+
+#define VIDEO_GET_INFO_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8621U
+#define VIDEO_GET_INFO_PU_BRIGHTNESS_CONTROL 0x8622U
+#define VIDEO_GET_INFO_PU_CONTRACT_CONTROL 0x8623U
+#define VIDEO_GET_INFO_PU_GAIN_CONTROL 0x8624U
+#define VIDEO_GET_INFO_PU_POWER_LINE_FREQUENCY_CONTROL 0x8625U
+#define VIDEO_GET_INFO_PU_HUE_CONTROL 0x8626U
+#define VIDEO_GET_INFO_PU_SATURATION_CONTROL 0x8627U
+#define VIDEO_GET_INFO_PU_SHARRNESS_CONTROL 0x8628U
+#define VIDEO_GET_INFO_PU_GAMMA_CONTROL 0x8629U
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x862AU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x862BU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x862CU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x862DU
+#define VIDEO_GET_INFO_PU_DIGITAL_MULTIPLIER_CONTROL 0x862EU
+#define VIDEO_GET_INFO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x862FU
+#define VIDEO_GET_INFO_PU_HUE_AUTO_CONTROL 0x8630U
+#define VIDEO_GET_INFO_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x8631U
+#define VIDEO_GET_INFO_PU_ANALOG_LOCK_STATUS_CONTROL 0x8632U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_PU_CONTRAST_AUTO_CONTROL 0x8633U
+#endif
+
+#define VIDEO_GET_INFO_CT_SCANNING_MODE_CONTROL 0x8641U
+#define VIDEO_GET_INFO_CT_AE_MODE_CONTROL 0x8642U
+#define VIDEO_GET_INFO_CT_AE_PRIORITY_CONTROL 0x8643U
+#define VIDEO_GET_INFO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8644U
+#define VIDEO_GET_INFO_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x8645U
+#define VIDEO_GET_INFO_CT_FOCUS_ABSOLUTE_CONTROL 0x8646U
+#define VIDEO_GET_INFO_CT_FOCUS_RELATIVE_CONTROL 0x8647U
+#define VIDEO_GET_INFO_CT_FOCUS_AUTO_CONTROL 0x8648U
+#define VIDEO_GET_INFO_CT_IRIS_ABSOLUTE_CONTROL 0x8649U
+#define VIDEO_GET_INFO_CT_IRIS_RELATIVE_CONTROL 0x864AU
+#define VIDEO_GET_INFO_CT_ZOOM_ABSOLUTE_CONTROL 0x864BU
+#define VIDEO_GET_INFO_CT_ZOOM_RELATIVE_CONTROL 0x864CU
+#define VIDEO_GET_INFO_CT_PANTILT_ABSOLUTE_CONTROL 0x864DU
+#define VIDEO_GET_INFO_CT_PANTILT_RELATIVE_CONTROL 0x864EU
+#define VIDEO_GET_INFO_CT_ROLL_ABSOLUTE_CONTROL 0x864FU
+#define VIDEO_GET_INFO_CT_ROLL_RELATIVE_CONTROL 0x8650U
+#define VIDEO_GET_INFO_CT_PRIVACY_CONTROL 0x8651U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_CT_FOCUS_SIMPLE_CONTROL 0x8652U
+#endif
+
+#define VIDEO_GET_INFO_VS_PROBE_CONTROL 0x8661U
+#define VIDEO_GET_INFO_VS_COMMIT_CONTROL 0x8662U
+#define VIDEO_GET_INFO_VS_STILL_PROBE_CONTROL 0x8663U
+#define VIDEO_GET_INFO_VS_STILL_COMMIT_CONTROL 0x8664U
+#define VIDEO_GET_INFO_VS_STILL_IMAGE_TRIGGER_CONTROL 0x8665U
+#define VIDEO_GET_INFO_VS_STREAM_ERROR_CODE_CONTROL 0x8666U
+#define VIDEO_GET_INFO_VS_GENERATE_KEY_FRAME_CONTROL 0x8667U
+#define VIDEO_GET_INFO_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8668U
+#define VIDEO_GET_INFO_VS_SYNCH_DELAY_CONTROL 0x8669U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_EU_SELECT_LAYER_CONTROL 0x8681U
+#define VIDEO_GET_INFO_EU_PROFILE_TOOLSET_CONTROL 0x8682U
+#define VIDEO_GET_INFO_EU_VIDEO_RESOLUTION_CONTROL 0x8683U
+#define VIDEO_GET_INFO_EU_MIN_FRAME_INTERVAL_CONTROL 0x8684U
+#define VIDEO_GET_INFO_EU_SLICE_MODE_CONTROL 0x8685U
+#define VIDEO_GET_INFO_EU_RATE_CONTROL_MODE_CONTROL 0x8686U
+#define VIDEO_GET_INFO_EU_AVERAGE_BITRATE_CONTROL 0x8687U
+#define VIDEO_GET_INFO_EU_CPB_SIZE_CONTROL 0x8688U
+#define VIDEO_GET_INFO_EU_PEAK_BIT_RATE_CONTROL 0x8689U
+#define VIDEO_GET_INFO_EU_QUANTIZATION_PARAMS_CONTROL 0x868AU
+#define VIDEO_GET_INFO_EU_SYNC_REF_FRAME_CONTROL 0x868BU
+#define VIDEO_GET_INFO_EU_LTR_BUFFER_CONTROL 0x868CU
+#define VIDEO_GET_INFO_EU_LTR_PICTURE_CONTROL 0x868DU
+#define VIDEO_GET_INFO_EU_LTR_VALIDATION_CONTROL 0x868EU
+#define VIDEO_GET_INFO_EU_SEI_PAYLOADTYPE_CONTROL 0x8690U
+#define VIDEO_GET_INFO_EU_QP_RANGE_CONTROL 0x8691U
+#define VIDEO_GET_INFO_EU_PRIORITY_CONTROL 0x8692U
+#define VIDEO_GET_INFO_EU_START_OR_STOP_LAYER_CONTROL 0x8693U
+#endif
+
+/*! @brief Video device class-specific request GET DEF COMMAND */
+#define VIDEO_GET_DEF_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8721U
+#define VIDEO_GET_DEF_PU_BRIGHTNESS_CONTROL 0x8722U
+#define VIDEO_GET_DEF_PU_CONTRACT_CONTROL 0x8723U
+#define VIDEO_GET_DEF_PU_GAIN_CONTROL 0x8724U
+#define VIDEO_GET_DEF_PU_POWER_LINE_FREQUENCY_CONTROL 0x8725U
+#define VIDEO_GET_DEF_PU_HUE_CONTROL 0x8726U
+#define VIDEO_GET_DEF_PU_SATURATION_CONTROL 0x8727U
+#define VIDEO_GET_DEF_PU_SHARRNESS_CONTROL 0x8728U
+#define VIDEO_GET_DEF_PU_GAMMA_CONTROL 0x8729U
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x872AU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x872BU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x872CU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x872DU
+#define VIDEO_GET_DEF_PU_DIGITAL_MULTIPLIER_CONTROL 0x872EU
+#define VIDEO_GET_DEF_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x872FU
+#define VIDEO_GET_DEF_PU_HUE_AUTO_CONTROL 0x8730U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_PU_CONTRAST_AUTO_CONTROL 0x8731U
+#endif
+
+#define VIDEO_GET_DEF_CT_AE_MODE_CONTROL 0x8742U
+#define VIDEO_GET_DEF_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8744U
+#define VIDEO_GET_DEF_CT_FOCUS_ABSOLUTE_CONTROL 0x8746U
+#define VIDEO_GET_DEF_CT_FOCUS_RELATIVE_CONTROL 0x8747U
+#define VIDEO_GET_DEF_CT_FOCUS_AUTO_CONTROL 0x8748U
+#define VIDEO_GET_DEF_CT_IRIS_ABSOLUTE_CONTROL 0x8749U
+#define VIDEO_GET_DEF_CT_ZOOM_ABSOLUTE_CONTROL 0x874BU
+#define VIDEO_GET_DEF_CT_ZOOM_RELATIVE_CONTROL 0x874CU
+#define VIDEO_GET_DEF_CT_PANTILT_ABSOLUTE_CONTROL 0x874DU
+#define VIDEO_GET_DEF_CT_PANTILT_RELATIVE_CONTROL 0x874EU
+#define VIDEO_GET_DEF_CT_ROLL_ABSOLUTE_CONTROL 0x874FU
+#define VIDEO_GET_DEF_CT_ROLL_RELATIVE_CONTROL 0x8750U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_CT_FOCUS_SIMPLE_CONTROL 0x8751U
+#define VIDEO_GET_DEF_CT_DIGITAL_WINDOW_CONTROL 0x8752U
+#define VIDEO_GET_DEF_CT_REGION_OF_INTEREST_CONTROL 0x8753U
+#endif
+
+#define VIDEO_GET_DEF_VS_PROBE_CONTROL 0x8761U
+#define VIDEO_GET_DEF_VS_STILL_PROBE_CONTROL 0x8763U
+#define VIDEO_GET_DEF_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8768U
+#define VIDEO_GET_DEF_VS_SYNCH_DELAY_CONTROL 0x8769U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_EU_PROFILE_TOOLSET_CONTROL 0x8782U
+#define VIDEO_GET_DEF_EU_VIDEO_RESOLUTION_CONTROL 0x8783U
+#define VIDEO_GET_DEF_EU_MIN_FRAME_INTERVAL_CONTROL 0x8784U
+#define VIDEO_GET_DEF_EU_SLICE_MODE_CONTROL 0x8785U
+#define VIDEO_GET_DEF_EU_RATE_CONTROL_MODE_CONTROL 0x8786U
+#define VIDEO_GET_DEF_EU_AVERAGE_BITRATE_CONTROL 0x8787U
+#define VIDEO_GET_DEF_EU_CPB_SIZE_CONTROL 0x8788U
+#define VIDEO_GET_DEF_EU_PEAK_BIT_RATE_CONTROL 0x8789U
+#define VIDEO_GET_DEF_EU_QUANTIZATION_PARAMS_CONTROL 0x878AU
+#define VIDEO_GET_DEF_EU_LTR_BUFFER_CONTROL 0x878CU
+#define VIDEO_GET_DEF_EU_LTR_PICTURE_CONTROL 0x878DU
+#define VIDEO_GET_DEF_EU_LTR_VALIDATION_CONTROL 0x878EU
+#define VIDEO_GET_DEF_EU_LEVEL_IDC_LIMIT_CONTROL 0x878FU
+#define VIDEO_GET_DEF_EU_SEI_PAYLOADTYPE_CONTROL 0x8790U
+#define VIDEO_GET_DEF_EU_QP_RANGE_CONTROL 0x8791U
+#define VIDEO_GET_DEF_EU_ERROR_RESILIENCY_CONTROL 0x8794U
+#endif
+
+/*! @brief Video device class-specific request SET CUR COMMAND */
+#define VIDEO_SET_CUR_VC_POWER_MODE_CONTROL 0x0101U
+
+#define VIDEO_SET_CUR_PU_BACKLIGHT_COMPENSATION_CONTROL 0x0121U
+#define VIDEO_SET_CUR_PU_BRIGHTNESS_CONTROL 0x0122U
+#define VIDEO_SET_CUR_PU_CONTRACT_CONTROL 0x0123U
+#define VIDEO_SET_CUR_PU_GAIN_CONTROL 0x0124U
+#define VIDEO_SET_CUR_PU_POWER_LINE_FREQUENCY_CONTROL 0x0125U
+#define VIDEO_SET_CUR_PU_HUE_CONTROL 0x0126U
+#define VIDEO_SET_CUR_PU_SATURATION_CONTROL 0x0127U
+#define VIDEO_SET_CUR_PU_SHARRNESS_CONTROL 0x0128U
+#define VIDEO_SET_CUR_PU_GAMMA_CONTROL 0x0129U
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x012AU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x012BU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x012CU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x012DU
+#define VIDEO_SET_CUR_PU_DIGITAL_MULTIPLIER_CONTROL 0x012EU
+#define VIDEO_SET_CUR_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x012FU
+#define VIDEO_SET_CUR_PU_HUE_AUTO_CONTROL 0x0130U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_PU_CONTRAST_AUTO_CONTROL 0x0131U
+#endif
+
+#define VIDEO_SET_CUR_CT_SCANNING_MODE_CONTROL 0x0141U
+#define VIDEO_SET_CUR_CT_AE_MODE_CONTROL 0x0142U
+#define VIDEO_SET_CUR_CT_AE_PRIORITY_CONTROL 0x0143U
+#define VIDEO_SET_CUR_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x0144U
+#define VIDEO_SET_CUR_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x0145U
+#define VIDEO_SET_CUR_CT_FOCUS_ABSOLUTE_CONTROL 0x0146U
+#define VIDEO_SET_CUR_CT_FOCUS_RELATIVE_CONTROL 0x0147U
+#define VIDEO_SET_CUR_CT_FOCUS_AUTO_CONTROL 0x0148U
+#define VIDEO_SET_CUR_CT_IRIS_ABSOLUTE_CONTROL 0x0149U
+#define VIDEO_SET_CUR_CT_IRIS_RELATIVE_CONTROL 0x014AU
+#define VIDEO_SET_CUR_CT_ZOOM_ABSOLUTE_CONTROL 0x014BU
+#define VIDEO_SET_CUR_CT_ZOOM_RELATIVE_CONTROL 0x014CU
+#define VIDEO_SET_CUR_CT_PANTILT_ABSOLUTE_CONTROL 0x014DU
+#define VIDEO_SET_CUR_CT_PANTILT_RELATIVE_CONTROL 0x014EU
+#define VIDEO_SET_CUR_CT_ROLL_ABSOLUTE_CONTROL 0x014FU
+#define VIDEO_SET_CUR_CT_ROLL_RELATIVE_CONTROL 0x0150U
+#define VIDEO_SET_CUR_CT_PRIVACY_CONTROL 0x0151U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_CT_FOCUS_SIMPLE_CONTROL 0x0152U
+#define VIDEO_SET_CUR_CT_DIGITAL_WINDOW_CONTROL 0x0153U
+#define VIDEO_SET_CUR_CT_REGION_OF_INTEREST_CONTROL 0x0154U
+#endif
+
+#define VIDEO_SET_CUR_VS_PROBE_CONTROL 0x0161U
+#define VIDEO_SET_CUR_VS_COMMIT_CONTROL 0x0162U
+#define VIDEO_SET_CUR_VS_STILL_PROBE_CONTROL 0x0163U
+#define VIDEO_SET_CUR_VS_STILL_COMMIT_CONTROL 0x0164U
+#define VIDEO_SET_CUR_VS_STILL_IMAGE_TRIGGER_CONTROL 0x0165U
+#define VIDEO_SET_CUR_VS_STREAM_ERROR_CODE_CONTROL 0x0166U
+#define VIDEO_SET_CUR_VS_GENERATE_KEY_FRAME_CONTROL 0x0167U
+#define VIDEO_SET_CUR_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x0168U
+#define VIDEO_SET_CUR_VS_SYNCH_DELAY_CONTROL 0x0169U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_EU_SELECT_LAYER_CONTROL 0x0181U
+#define VIDEO_SET_CUR_EU_PROFILE_TOOLSET_CONTROL 0x0182U
+#define VIDEO_SET_CUR_EU_VIDEO_RESOLUTION_CONTROL 0x0183U
+#define VIDEO_SET_CUR_EU_MIN_FRAME_INTERVAL_CONTROL 0x0184U
+#define VIDEO_SET_CUR_EU_SLICE_MODE_CONTROL 0x0185U
+#define VIDEO_SET_CUR_EU_RATE_CONTROL_MODE_CONTROL 0x0186U
+#define VIDEO_SET_CUR_EU_AVERAGE_BITRATE_CONTROL 0x0187U
+#define VIDEO_SET_CUR_EU_CPB_SIZE_CONTROL 0x0188U
+#define VIDEO_SET_CUR_EU_PEAK_BIT_RATE_CONTROL 0x0189U
+#define VIDEO_SET_CUR_EU_QUANTIZATION_PARAMS_CONTROL 0x018AU
+#define VIDEO_SET_CUR_EU_SYNC_REF_FRAME_CONTROL 0x018BU
+#define VIDEO_SET_CUR_EU_LTR_BUFFER_CONTROL 0x018CU
+#define VIDEO_SET_CUR_EU_LTR_PICTURE_CONTROL 0x018DU
+#define VIDEO_SET_CUR_EU_LTR_VALIDATION_CONTROL 0x018EU
+#define VIDEO_SET_CUR_EU_LEVEL_IDC_LIMIT_CONTROL 0x018FU
+#define VIDEO_SET_CUR_EU_SEI_PAYLOADTYPE_CONTROL 0x0190U
+#define VIDEO_SET_CUR_EU_QP_RANGE_CONTROL 0x0191U
+#define VIDEO_SET_CUR_EU_PRIORITY_CONTROL 0x0192U
+#define VIDEO_SET_CUR_EU_START_OR_STOP_LAYER_CONTROL 0x0193U
+#define VIDEO_SET_CUR_EU_ERROR_RESILIENCY_CONTROL 0x0194U
+#endif
+
+/*! @brief The payload header structure. */
+struct video_payload_header {
+ uint8_t bHeaderLength; /*!< The payload header length. */
+ union {
+ uint8_t bmheaderInfo; /*!< The payload header bitmap field. */
+ struct
+ {
+ uint8_t frameIdentifier : 1U; /*!< Frame Identifier. This bit toggles at each frame start boundary and stays
+ constant for the rest of the frame.*/
+ uint8_t endOfFrame : 1U; /*!< End of Frame. This bit indicates the end of a video frame and is set in the
+ last video sample that belongs to a frame.*/
+ uint8_t
+ presentationTimeStamp : 1U; /*!< Presentation Time Stamp. This bit, when set, indicates the presence of
+ a PTS field.*/
+ uint8_t sourceClockReference : 1U; /*!< Source Clock Reference. This bit, when set, indicates the presence
+ of a SCR field.*/
+ uint8_t reserved : 1U; /*!< Reserved. Set to 0. */
+ uint8_t stillImage : 1U; /*!< Still Image. This bit, when set, identifies a video sample that belongs to a
+ still image.*/
+ uint8_t errorBit : 1U; /*!< Error Bit. This bit, when set, indicates an error in the device streaming.*/
+ uint8_t endOfHeader : 1U; /*!< End of Header. This bit, when set, indicates the end of the BFH fields.*/
+ } headerInfoBits;
+ struct
+ {
+ uint8_t FID : 1U; /*!< Frame Identifier. This bit toggles at each frame start boundary and stays constant
+ for the rest of the frame.*/
+ uint8_t EOI : 1U; /*!< End of Frame. This bit indicates the end of a video frame and is set in the last
+ video sample that belongs to a frame.*/
+ uint8_t PTS : 1U; /*!< Presentation Time Stamp. This bit, when set, indicates the presence of a PTS field.*/
+ uint8_t SCR : 1U; /*!< Source Clock Reference. This bit, when set, indicates the presence of a SCR field.*/
+ uint8_t RES : 1U; /*!< Reserved. Set to 0. */
+ uint8_t STI : 1U; /*!< Still Image. This bit, when set, identifies a video sample that belongs to a still
+ image.*/
+ uint8_t ERR : 1U; /*!< Error Bit. This bit, when set, indicates an error in the device streaming.*/
+ uint8_t EOH : 1U; /*!< End of Header. This bit, when set, indicates the end of the BFH fields.*/
+ } headerInfoBitmap;
+ } headerInfoUnion;
+ uint32_t dwPresentationTime; /*!< Presentation time stamp (PTS) field.*/
+ uint8_t bSourceClockReference[6]; /*!< Source clock reference (SCR) field.*/
+} __PACKED;
+
+/*! @brief The Video probe and commit controls structure.*/
+struct video_probe_and_commit_controls {
+ union {
+ uint8_t bmHint; /*!< Bit-field control indicating to the function what fields shall be kept fixed. */
+ struct
+ {
+ uint8_t dwFrameInterval : 1U; /*!< dwFrameInterval field.*/
+ uint8_t wKeyFrameRate : 1U; /*!< wKeyFrameRate field.*/
+ uint8_t wPFrameRate : 1U; /*!< wPFrameRate field.*/
+ uint8_t wCompQuality : 1U; /*!< wCompQuality field.*/
+ uint8_t wCompWindowSize : 1U; /*!< wCompWindowSize field.*/
+ uint8_t reserved : 3U; /*!< Reserved field.*/
+ } hintBitmap;
+ } hintUnion;
+ union {
+ uint8_t bmHint; /*!< Bit-field control indicating to the function what fields shall be kept fixed. */
+ struct
+ {
+ uint8_t reserved : 8U; /*!< Reserved field.*/
+ } hintBitmap;
+ } hintUnion1;
+ uint8_t bFormatIndex; /*!< Video format index from a format descriptor.*/
+ uint8_t bFrameIndex; /*!< Video frame index from a frame descriptor.*/
+ uint32_t dwFrameInterval; /*!< Frame interval in 100ns units.*/
+ uint16_t wKeyFrameRate; /*!< Key frame rate in key-frame per video-frame units.*/
+ uint16_t wPFrameRate; /*!< PFrame rate in PFrame/key frame units.*/
+ uint16_t wCompQuality; /*!< Compression quality control in abstract units 0U (lowest) to 10000U (highest).*/
+ uint16_t wCompWindowSize; /*!< Window size for average bit rate control.*/
+ uint16_t wDelay; /*!< Internal video streaming interface latency in ms from video data capture to presentation on
+ the USB.*/
+ uint32_t dwMaxVideoFrameSize; /*!< Maximum video frame or codec-specific segment size in bytes.*/
+ uint32_t dwMaxPayloadTransferSize; /*!< Specifies the maximum number of bytes that the device can transmit or
+ receive in a single payload transfer.*/
+ uint32_t dwClockFrequency; /*!< The device clock frequency in Hz for the specified format. This specifies the
+ units used for the time information fields in the Video Payload Headers in the data
+ stream.*/
+ uint8_t bmFramingInfo; /*!< Bit-field control supporting the following values: D0 Frame ID, D1 EOF.*/
+ uint8_t bPreferedVersion; /*!< The preferred payload format version supported by the host or device for the
+ specified bFormatIndex value.*/
+ uint8_t bMinVersion; /*!< The minimum payload format version supported by the device for the specified bFormatIndex
+ value.*/
+ uint8_t bMaxVersion; /*!< The maximum payload format version supported by the device for the specified bFormatIndex
+ value.*/
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+ uint8_t bUsage; /*!< This bitmap enables features reported by the bmUsages field of the Video Frame Descriptor.*/
+ uint8_t
+ bBitDepthLuma; /*!< Represents bit_depth_luma_minus8 + 8U, which must be the same as bit_depth_chroma_minus8 +
+ 8.*/
+ uint8_t bmSettings; /*!< A bitmap of flags that is used to discover and control specific features of a temporally
+ encoded video stream.*/
+ uint8_t bMaxNumberOfRefFramesPlus1; /*!< Host indicates the maximum number of frames stored for use as references.*/
+ uint16_t bmRateControlModes; /*!< This field contains 4U sub-fields, each of which is a 4U bit number.*/
+ uint64_t bmLayoutPerStream; /*!< This field contains 4U sub-fields, each of which is a 2U byte number.*/
+#endif
+} __PACKED;
+
+/*! @brief The Video still probe and still commit controls structure.*/
+struct video_still_probe_and_commit_controls {
+ uint8_t bFormatIndex; /*!< Video format index from a format descriptor.*/
+ uint8_t bFrameIndex; /*!< Video frame index from a frame descriptor.*/
+ uint8_t bCompressionIndex; /*!< Compression index from a frame descriptor.*/
+ uint32_t dwMaxVideoFrameSize; /*!< Maximum still image size in bytes.*/
+ uint32_t dwMaxPayloadTransferSize; /*!< Specifies the maximum number of bytes that the device can transmit or
+ receive in a single payload transfer.*/
+} __PACKED;
+
+struct video_cs_if_vc_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdVDC;
+ uint16_t wTotalLength;
+ uint32_t dwClockFrequency;
+ uint8_t bInCollection;
+ uint8_t baInterfaceNr[];
+} __PACKED;
+
+#define VIDEO_SIZEOF_VC_HEADER_DESC(n) (11 + n)
+
+struct video_cs_if_vc_input_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t iTerminal;
+ uint16_t wObjectiveFocalLenMin;
+ uint16_t wObjectiveFocalLenMax;
+ uint16_t wOcularFocalLength;
+ uint8_t bControlSize;
+ uint8_t bmaControls[];
+} __PACKED;
+
+#define VIDEO_SIZEOF_VC_INPUT_TERMINAL_DESC(n) (15 + n)
+
+struct video_cs_if_vc_processing_unit_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint16_t wMaxMultiplier;
+ uint8_t bControlSize;
+ // uint8_t bmaControls[];
+ uint8_t iProcessing;
+ uint8_t bmVideoStandards;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VC_PROCESSING_UNIT_DESC(n) (10 + n)
+
+struct video_cs_if_vc_output_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VC_OUTPUT_TERMINAL_DESC 9
+
+struct video_cs_ep_vc_ep_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint16_t wMaxTransferSize;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VC_EP_DESC 5
+
+struct video_cs_if_vs_input_header_descriptor {
+ 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 bControlSize;
+ uint8_t bmaControls[];
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_INPUT_HEADER_DESC(p, n) (13 + p * n)
+
+struct video_cs_if_vs_output_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bNumFormats;
+ uint16_t wTotalLength;
+ uint8_t bEndpointAddress;
+ uint8_t bTerminalLink;
+ uint8_t bControlSize;
+ uint8_t bmaControls[];
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_OUTPUT_TERMINAL_DESC(p, n) (9 + p * n)
+
+struct video_cs_if_vs_format_uncompressed_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+ uint8_t guidFormat[16];
+ uint8_t bBitsPerPixel;
+ uint8_t bDefaultFrameIndex;
+ uint8_t bAspectRatioX;
+ uint8_t bAspectRatioY;
+ uint8_t bmInterlaceFlags;
+ uint8_t bCopyProtect;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_FORMAT_UNCOMPRESSED_DESC (27)
+
+struct video_cs_if_vs_frame_uncompressed_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bmCapabilities;
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint32_t dwMinBitRate;
+ uint32_t dwMaxBitRate;
+ uint32_t dwMaxVideoFrameBufferSize;
+ uint32_t dwDefaultFrameInterval;
+ uint8_t bFrameIntervalType;
+ uint32_t dwFrameInterval;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_FRAME_UNCOMPRESSED_DESC 30
+
+struct video_cs_if_vs_format_mjpeg_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bNumFrameDescriptors;
+ uint8_t bmFlags;
+ uint8_t bDefaultFrameIndex;
+ uint8_t bAspectRatioX;
+ uint8_t bAspectRatioY;
+ uint8_t bmInterlaceFlags;
+ uint8_t bCopyProtect;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_FORMAT_MJPEG_DESC 11
+
+struct video_cs_if_vs_frame_mjpeg_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bFormatIndex;
+ uint8_t bmCapabilities;
+ uint16_t wWidth;
+ uint16_t wHeight;
+ uint32_t dwMinBitRate;
+ uint32_t dwMaxBitRate;
+ uint32_t dwMaxVideoFrameBufferSize;
+ uint32_t dwDefaultFrameInterval;
+ uint8_t bFrameIntervalType;
+ uint32_t dwFrameInterval1;
+ uint32_t dwFrameInterval2;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_FRAME_MJPEG_DESC(n) (26 + n)
+
+struct video_cs_if_vs_colorformat_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubType;
+ uint8_t bColorPrimaries;
+ uint8_t bXferCharacteristics;
+ uint8_t bMatrixCoefficients;
+} __PACKED;
+
+#define VIDEO_SIZEOF_VS_COLORFORMAT_DESC 6
+
+struct video_vc_input_terminal_bmcontrol_bitmap {
+ uint32_t scanning_mode : 1;
+ uint32_t auto_exposure_mode : 1;
+ uint32_t auto_exposure_priority : 1;
+ uint32_t exposure_time_absolute : 1;
+ uint32_t exposure_time_relative : 1;
+ uint32_t focus_absolute : 1;
+ uint32_t focus_relative : 1;
+ uint32_t iris_absolute : 1;
+ uint32_t iris_relative : 1;
+ uint32_t zoom_absolute : 1;
+ uint32_t zoom_relative : 1;
+ uint32_t pantilt_absolute : 1;
+ uint32_t pantilt_relative : 1;
+ uint32_t roll_absolute : 1;
+ uint32_t roll_relative : 1;
+ uint32_t reserved : 2;
+ uint32_t focus_auto : 1;
+ uint32_t pricvcy : 1;
+};
+
+struct video_vc_processing_unit_bmcontrol_bitmap {
+ uint16_t brightness : 1;
+ uint16_t contrast : 1;
+ uint16_t hue : 1;
+ uint16_t saturation : 1;
+ uint16_t sharpness : 1;
+ uint16_t gamma : 1;
+ uint16_t white_bal_temp : 1;
+ uint16_t white_bal_comp : 1;
+ uint16_t backlight_comp : 1;
+ uint16_t gain : 1;
+ uint16_t power_line_freq : 1;
+ uint16_t hue_auto : 1;
+ uint16_t white_bal_temp_auto : 1;
+ uint16_t white_bal_comp_auto : 1;
+ uint16_t digital_mult : 1;
+ uint16_t digital_mult_limit : 1;
+};
+
+struct video_camera_capabilities {
+ uint8_t support_get_request : 1;
+ uint8_t support_set_request : 1;
+ uint8_t disabled_by_automatic_mode : 1;
+ uint8_t auto_update_control : 1;
+ uint8_t async_control : 1;
+ uint8_t reserved : 3;
+};
+
+struct video_autoexposure_mode {
+ uint8_t manual_mode : 1;
+ uint8_t auto_mode : 1;
+ uint8_t shutter_priority_mode : 1;
+ uint8_t aperture_priority_mode : 1;
+ uint8_t reserved : 4;
+};
+
+#define VIDEO_GUID_YUY2 0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
+#define VIDEO_GUID_NV12 0x4E, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
+#define VIDEO_GUID_M420 0x4D, 0x34, 0x32, 0x30, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
+#define VIDEO_GUID_I420 0x49, 0x34, 0x32, 0x30, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
+
+// clang-format off
+#define VIDEO_VC_DESCRIPTOR_INIT(bFirstInterface, bNumEndpoints, bcdUVC, wTotalLength, dwClockFrequency, stridx) \
+ /* Interface Association Descriptor */ \
+ 0x08, \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, \
+ bFirstInterface, \
+ 0x02, \
+ USB_DEVICE_CLASS_VIDEO, \
+ VIDEO_SC_VIDEO_INTERFACE_COLLECTION, \
+ 0x00, \
+ 0x00, \
+ /* VideoControl Interface Descriptor */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ 0x00, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ bNumEndpoints, /* bNumEndpoints:1 endpoint (interrupt endpoint) */ \
+ USB_DEVICE_CLASS_VIDEO, /* bInterfaceClass : CC_VIDEO */ \
+ VIDEO_SC_VIDEOCONTROL, /* bInterfaceSubClass : SC_VIDEOCONTROL */ \
+ VIDEO_PC_PROTOCOL_UNDEFINED, /* bInterfaceProtocol : PC_PROTOCOL_UNDEFINED */ \
+ stridx, /* iInterface:Index to string descriptor that contains the string <Your Product Name> */ \
+ /*Class-specific VideoControl Interface Descriptor */ \
+ 0x0d, /* bLength */ \
+ 0x24, /* bDescriptorType : CS_INTERFACE */ \
+ VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE, /* bDescriptorSubType : VC_HEADER subtype */ \
+ WBVAL(bcdUVC), /* bcdUVC : Revision of class specification that this device is based upon.*/ \
+ WBVAL(wTotalLength), /* wTotalLength */ \
+ DBVAL(dwClockFrequency), /* dwClockFrequency : 0x005b8d80 -> 6,000,000 == 6MHz*/ \
+ 0x01, /* bInCollection : Number of streaming interfaces. */ \
+ (uint8_t)(bFirstInterface + 1), /* baInterfaceNr(0) : VideoStreaming interface 1 belongs to this VideoControl interface.*/ \
+ /* Input Terminal 1 -> Processing Unit 2 -> Output Terminal 3 */ \
+ 0x12, \
+ 0x24, \
+ VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE, \
+ 0x01, /* bTerminalID */ \
+ WBVAL(VIDEO_ITT_CAMERA), /* wTerminalType : 0x0201 Camera Sensor*/ \
+ 0x00, /* bAssocTerminal */ \
+ 0x00, /* iTerminal */ \
+ WBVAL(0x0000), /* wObjectiveFocalLengthMin */ \
+ WBVAL(0x0000), /* wObjectiveFocalLengthMax */ \
+ WBVAL(0x0000), /* wOcularFocalLength */ \
+ 0x03, /* bControlSize */ \
+ 0x00, 0x00, 0x00, /* bmControls */ \
+ 0x0c, \
+ 0x24, \
+ VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE, \
+ 0x02, /* bUnitID */ \
+ 0x01, /* bSourceID */ \
+ 0x00, 0x00, /* wMaxMultiplier */ \
+ 0x02, /* bControlSize */ \
+ 0x00, 0x00, /* bmControls */ \
+ 0x00, /* iProcessing */ \
+ 0x00, /* bmVideoStandards */ \
+ 0x09, \
+ 0x24, \
+ VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE, \
+ 0x03, /* bTerminalID */ \
+ WBVAL(VIDEO_TT_STREAMING), \
+ 0x00, /* bAssocTerminal */ \
+ 0x02, /* bSourceID */ \
+ 0x00 /* iTerminal */
+
+#define VIDEO_VS_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bNumEndpoints) \
+ /* Video Streaming (VS) Interface Descriptor */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType : INTERFACE */ \
+ bInterfaceNumber, /* bInterfaceNumber: Index of this interface */ \
+ bAlternateSetting, /* bAlternateSetting: Index of this alternate setting */ \
+ bNumEndpoints, /* bNumEndpoints : 0 endpoints – no bandwidth used*/ \
+ 0x0e, /* bInterfaceClass : CC_VIDEO */ \
+ 0x02, /* bInterfaceSubClass : SC_VIDEOSTREAMING */ \
+ 0x00, /* bInterfaceProtocol : PC_PROTOCOL_UNDEFINED */ \
+ 0x00 /* iInterface : unused */
+
+#define VIDEO_VS_HEADER_DESCRIPTOR_INIT(bNumFormats, wTotalLength, bEndpointAddress, ...) \
+ /*Class-specific VideoStream Header Descriptor (Input) */ \
+ 0x0d + PP_NARG(__VA_ARGS__), \
+ 0x24, \
+ VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE, \
+ bNumFormats, /* bNumFormats : One format descriptor follows. */ \
+ WBVAL(wTotalLength), \
+ bEndpointAddress, \
+ 0x00, /* bmInfo : No dynamic format change supported. */ \
+ 0x03, /* bTerminalLink : This VideoStreaming interface supplies terminal ID 2 (Output Terminal). */ \
+ 0x00, /* bStillCaptureMethod : Device supports still image capture method 0. */ \
+ 0x00, /* bTriggerSupport : Hardware trigger supported for still image capture */ \
+ 0x00, /* bTriggerUsage : Hardware trigger should initiate a still image capture. */ \
+ PP_NARG(__VA_ARGS__), /* bControlSize : Size of the bmaControls field */ \
+ __VA_ARGS__ /* bmaControls : No VideoStreaming specific controls are supported.*/
+
+#define VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_INIT(bFormatIndex, bNumFrameDescriptors, GUIDFormat) \
+ /*Payload Format(UNCOMPRESSED) Descriptor */ \
+ 0x1b, \
+ 0x24, \
+ VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_SUBTYPE, /* bDescriptorSubType : VS_FORMAT_UNCOMPRESSED subtype */ \
+ bFormatIndex, /* bFormatIndex : First (and only) format descriptor */ \
+ bNumFrameDescriptors, /* bNumFrameDescriptors : One frame descriptor for this format follows. */ \
+ GUIDFormat, /* GUID Format YUY2 {32595559-0000-0010-8000-00AA00389B71} */ \
+ 0x0c, /* bBitsPerPixel : Number of bits per pixel used to specify color in the decoded video frame - 16 for yuy2*/ \
+ 0x01, /* bDefaultFrameIndex : Default frame index is 1. */ \
+ 0x00, /* bAspectRatioX : Non-interlaced stream not required. */ \
+ 0x00, /* bAspectRatioY : Non-interlaced stream not required. */ \
+ 0x00, /* bmInterlaceFlags : Non-interlaced stream */ \
+ 0x00 /* bCopyProtect : No restrictions imposed on the duplication of this video stream. */
+
+#define VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_INIT(bFrameIndex, wWidth, wHeight, dwMinBitRate, dwMaxBitRate, \
+ dwMaxVideoFrameBufferSize, dwDefaultFrameInterval, dwFrameInterval) \
+ 0x1e, \
+ 0x24, \
+ VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_SUBTYPE, \
+ bFrameIndex, \
+ 0x00, \
+ WBVAL(wWidth), \
+ WBVAL(wHeight), \
+ DBVAL(dwMinBitRate), \
+ DBVAL(dwMaxBitRate), \
+ DBVAL(dwMaxVideoFrameBufferSize), \
+ DBVAL(dwDefaultFrameInterval), \
+ 0x01, \
+ DBVAL(dwFrameInterval)
+
+#define VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_INIT(bFormatIndex, bNumFrameDescriptors) \
+ /*Payload Format(MJPEG) Descriptor */ \
+ 0x0b, /* bLength */ \
+ 0x24, /* bDescriptorType : CS_INTERFACE */ \
+ 0x06, /* bDescriptorSubType : VS_FORMAT_MJPEG subtype */ \
+ bFormatIndex, /* bFormatIndex : First (and only) format descriptor */ \
+ bNumFrameDescriptors, /* bNumFrameDescriptors : One frame descriptor for this format follows. */ \
+ 0x00, /* bmFlags : Uses fixed size samples.. */ \
+ 0x01, /* bDefaultFrameIndex : Default frame index is 1. */ \
+ 0x00, /* bAspectRatioX : Non-interlaced stream – not required. */ \
+ 0x00, /* bAspectRatioY : Non-interlaced stream – not required. */ \
+ 0x00, /* bmInterlaceFlags : Non-interlaced stream */ \
+ 0x00 /* bCopyProtect : No restrictions imposed on the duplication of this video stream. */
+
+#define VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_INIT(bFrameIndex, wWidth, wHeight, dwMinBitRate, dwMaxBitRate, \
+ dwMaxVideoFrameBufferSize, dwDefaultFrameInterval, bFrameIntervalType, ...) \
+ 0x1a + PP_NARG(__VA_ARGS__), /* bLength */ \
+ 0x24, /* bDescriptorType : CS_INTERFACE */ \
+ VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE, /* bDescriptorSubType : VS_FRAME_MJPEG */ \
+ bFrameIndex, /* bFrameIndex : First (and only) frame descriptor */ \
+ 0x00, /* bmCapabilities : Still images using capture method 0 are supported at this frame setting.D1: Fixed frame-rate. */ \
+ WBVAL(wWidth), /* wWidth (2bytes): Width of frame is 128 pixels. */ \
+ WBVAL(wHeight), /* wHeight (2bytes): Height of frame is 64 pixels. */ \
+ DBVAL(dwMinBitRate), /* dwMinBitRate (4bytes): Min bit rate in bits/s */ \
+ DBVAL(dwMaxBitRate), /* dwMaxBitRate (4bytes): Max bit rate in bits/s */ \
+ DBVAL(dwMaxVideoFrameBufferSize), /* dwMaxVideoFrameBufSize (4bytes): Maximum video or still frame size, in bytes. */ \
+ dwDefaultFrameInterval, /* dwDefaultFrameInterval : 1,000,000 * 100ns -> 10 FPS */ \
+ bFrameIntervalType, /* bFrameIntervalType : Indicates how the frame interval can be programmed. 0: Continuous frame interval 1..255: The number of discrete frame */ \
+ __VA_ARGS__
+// clang-format on
+#endif /*USB_VIDEO_H */ \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/video/usbd_video.c b/components/drivers/usb/cherryusb/class/video/usbd_video.c
new file mode 100644
index 0000000000..07059ee0d7
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/video/usbd_video.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_video.h"
+
+struct video_entity_info {
+ uint8_t bDescriptorSubtype;
+ uint8_t bEntityId;
+ uint16_t wTerminalType;
+};
+
+struct usbd_video_priv {
+ struct video_probe_and_commit_controls probe;
+ struct video_probe_and_commit_controls commit;
+ uint8_t power_mode;
+ uint8_t error_code;
+ struct video_entity_info info[3];
+} g_usbd_video[CONFIG_USBDEV_MAX_BUS];
+
+static int usbd_video_control_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
+
+ switch (control_selector) {
+ case VIDEO_VC_VIDEO_POWER_MODE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_SET_CUR:
+ break;
+ case VIDEO_REQUEST_GET_CUR:
+ break;
+ case VIDEO_REQUEST_GET_INFO:
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ break;
+ case VIDEO_VC_REQUEST_ERROR_CODE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR:
+ (*data)[0] = 0x06;
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_INFO:
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int usbd_video_control_unit_terminal_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t entity_id = (uint8_t)(setup->wIndex >> 8);
+ uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
+
+ for (uint8_t i = 0; i < 3; i++) {
+ struct video_entity_info *entity_info = &g_usbd_video[busid].info[i];
+ if (entity_info->bEntityId == entity_id) {
+ switch (entity_info->bDescriptorSubtype) {
+ case VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
+ if (entity_info->wTerminalType == VIDEO_ITT_CAMERA) {
+ switch (control_selector) {
+ case VIDEO_CT_AE_MODE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR:
+ (*data)[0] = 0x08;
+ *len = 1;
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint32_t dwExposureTimeAbsolute = 2500;
+ memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
+ *len = 4;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint32_t dwExposureTimeAbsolute = 5; //0.0005sec
+ memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
+ *len = 4;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec
+ memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
+ *len = 4;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint32_t dwExposureTimeAbsolute = 5; //0.0005sec
+ memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
+ *len = 4;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec
+ memcpy(*data, (uint8_t *)&dwExposureTimeAbsolute, 4);
+ *len = 4;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_CT_FOCUS_ABSOLUTE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wFocusAbsolute = 0x0080;
+ memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wFocusAbsolute = 0;
+ memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wFocusAbsolute = 0x00ff;
+ memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wFocusAbsolute = 0x0001;
+ memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wFocusAbsolute = 0x0080;
+ memcpy(*data, (uint8_t *)&wFocusAbsolute, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_CT_ZOOM_ABSOLUTE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wObjectiveFocalLength = 0x0064;
+ memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wObjectiveFocalLength = 0x0064;
+ memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wObjectiveFocalLength = 0x00c8;
+ memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wObjectiveFocalLength = 0x0001;
+ memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wObjectiveFocalLength = 0x0064;
+ memcpy(*data, (uint8_t *)&wObjectiveFocalLength, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_CT_ROLL_ABSOLUTE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wRollAbsolute = 0x0000;
+ memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wRollAbsolute = 0x0000;
+ memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wRollAbsolute = 0x00ff;
+ memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wRollAbsolute = 0x0001;
+ memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wRollAbsolute = 0x0000;
+ memcpy(*data, (uint8_t *)&wRollAbsolute, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_CT_FOCUS_AUTO_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wFocusAuto = 0x0000;
+ memcpy(*data, (uint8_t *)&wFocusAuto, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class control selector 0x%02x\r\n", control_selector);
+ return -1;
+ }
+ } else {
+ USB_LOG_WRN("Unhandled Video Class wTerminalType 0x%02x\r\n", entity_info->wTerminalType);
+ return -2;
+ }
+ break;
+ case VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_SELECTOR_UNIT_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE:
+ switch (control_selector) {
+ case VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wBacklightCompensation = 0x0004;
+ memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wBacklightCompensation = 0;
+ memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wBacklightCompensation = 8;
+ memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wBacklightCompensation = 1;
+ memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wBacklightCompensation = 4;
+ memcpy(*data, (uint8_t *)&wBacklightCompensation, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_BRIGHTNESS_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_SET_CUR: {
+ uint16_t wBrightness = (uint16_t)(*data)[1] << 8 | (uint16_t)(*data)[0];
+ USB_LOG_INFO("Video set brightness:%d\r\n", wBrightness);
+ } break;
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wBrightness = 0x0080;
+ memcpy(*data, (uint8_t *)&wBrightness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wBrightness = 0x0001;
+ memcpy(*data, (uint8_t *)&wBrightness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wBrightness = 0x00ff;
+ memcpy(*data, (uint8_t *)&wBrightness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wBrightness = 0x0001;
+ memcpy(*data, (uint8_t *)&wBrightness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wBrightness = 0x0080;
+ memcpy(*data, (uint8_t *)&wBrightness, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_CONTRAST_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wContrast = 0x0080;
+ memcpy(*data, (uint8_t *)&wContrast, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wContrast = 0x0001;
+ memcpy(*data, (uint8_t *)&wContrast, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wContrast = 0x00ff;
+ memcpy(*data, (uint8_t *)&wContrast, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wContrast = 0x0001;
+ memcpy(*data, (uint8_t *)&wContrast, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wContrast = 0x0080;
+ memcpy(*data, (uint8_t *)&wContrast, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_HUE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wHue = 0x0080;
+ memcpy(*data, (uint8_t *)&wHue, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wHue = 0x0001;
+ memcpy(*data, (uint8_t *)&wHue, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wHue = 0x00ff;
+ memcpy(*data, (uint8_t *)&wHue, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wHue = 0x0001;
+ memcpy(*data, (uint8_t *)&wHue, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wHue = 0x0080;
+ memcpy(*data, (uint8_t *)&wHue, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_SATURATION_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wSaturation = 0x0001;
+ memcpy(*data, (uint8_t *)&wSaturation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wSaturation = 0x00ff;
+ memcpy(*data, (uint8_t *)&wSaturation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wSaturation = 0x0001;
+ memcpy(*data, (uint8_t *)&wSaturation, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wSaturation = 0x0080;
+ memcpy(*data, (uint8_t *)&wSaturation, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_SHARPNESS_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wSharpness = 0x0001;
+ memcpy(*data, (uint8_t *)&wSharpness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wSharpness = 0x00ff;
+ memcpy(*data, (uint8_t *)&wSharpness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wSharpness = 0x0001;
+ memcpy(*data, (uint8_t *)&wSharpness, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wSharpness = 0x0080;
+ memcpy(*data, (uint8_t *)&wSharpness, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_GAIN_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wGain = 0;
+ memcpy(*data, (uint8_t *)&wGain, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wGain = 255;
+ memcpy(*data, (uint8_t *)&wGain, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wGain = 1;
+ memcpy(*data, (uint8_t *)&wGain, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wGain = 255;
+ memcpy(*data, (uint8_t *)&wGain, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wWhiteBalance_Temprature = 417;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MIN: {
+ uint16_t wWhiteBalance_Temprature = 300;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_MAX: {
+ uint16_t wWhiteBalance_Temprature = 600;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_RES: {
+ uint16_t wWhiteBalance_Temprature = 1;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
+ *len = 2;
+ } break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03; //struct video_camera_capabilities
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_DEF: {
+ uint16_t wWhiteBalance_Temprature = 417;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature, 2);
+ *len = 2;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR: {
+ uint16_t wWhiteBalance_Temprature_Auto = 1;
+ memcpy(*data, (uint8_t *)&wWhiteBalance_Temprature_Auto, 1);
+ *len = 1;
+ } break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ default:
+ g_usbd_video[busid].error_code = 0x06;
+ USB_LOG_WRN("Unhandled Video Class control selector 0x%02x\r\n", control_selector);
+ return -1;
+ }
+ break;
+ case VIDEO_VC_EXTENSION_UNIT_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_ENCODING_UNIT_DESCRIPTOR_SUBTYPE:
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int usbd_video_stream_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t control_selector = (uint8_t)(setup->wValue >> 8);
+
+ switch (control_selector) {
+ case VIDEO_VS_PROBE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_SET_CUR:
+ //memcpy((uint8_t *)&g_usbd_video[busid].probe, *data, setup->wLength);
+ break;
+ case VIDEO_REQUEST_GET_CUR:
+ memcpy(*data, (uint8_t *)&g_usbd_video[busid].probe, setup->wLength);
+ *len = sizeof(struct video_probe_and_commit_controls);
+ break;
+
+ case VIDEO_REQUEST_GET_MIN:
+ case VIDEO_REQUEST_GET_MAX:
+ case VIDEO_REQUEST_GET_RES:
+ case VIDEO_REQUEST_GET_DEF:
+ memcpy(*data, (uint8_t *)&g_usbd_video[busid].probe, setup->wLength);
+ *len = sizeof(struct video_probe_and_commit_controls);
+ break;
+ case VIDEO_REQUEST_GET_LEN:
+ (*data)[0] = sizeof(struct video_probe_and_commit_controls);
+ *len = 1;
+ break;
+
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03;
+ *len = 1;
+ break;
+
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_VS_COMMIT_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_SET_CUR:
+ //memcpy((uint8_t *)&g_usbd_video[busid].commit, *data, setup->wLength);
+ break;
+ case VIDEO_REQUEST_GET_CUR:
+ memcpy(*data, (uint8_t *)&g_usbd_video[busid].commit, setup->wLength);
+ *len = sizeof(struct video_probe_and_commit_controls);
+ break;
+ case VIDEO_REQUEST_GET_MIN:
+ case VIDEO_REQUEST_GET_MAX:
+ case VIDEO_REQUEST_GET_RES:
+ case VIDEO_REQUEST_GET_DEF:
+ memcpy(*data, (uint8_t *)&g_usbd_video[busid].commit, setup->wLength);
+ *len = sizeof(struct video_probe_and_commit_controls);
+ break;
+
+ case VIDEO_REQUEST_GET_LEN:
+ (*data)[0] = sizeof(struct video_probe_and_commit_controls);
+ *len = 1;
+ break;
+
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x03;
+ *len = 1;
+ break;
+
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ case VIDEO_VS_STREAM_ERROR_CODE_CONTROL:
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_GET_CUR:
+ (*data)[0] = g_usbd_video[busid].error_code;
+ *len = 1;
+ break;
+ case VIDEO_REQUEST_GET_INFO:
+ (*data)[0] = 0x01;
+ *len = 1;
+ break;
+ default:
+ USB_LOG_WRN("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int video_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("Video Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ uint8_t intf_num = (uint8_t)setup->wIndex;
+ uint8_t entity_id = (uint8_t)(setup->wIndex >> 8);
+
+ if (intf_num == 0) { /* Video Control Interface */
+ if (entity_id == 0) {
+ return usbd_video_control_request_handler(busid, setup, data, len); /* Interface Control Requests */
+ } else {
+ return usbd_video_control_unit_terminal_request_handler(busid, setup, data, len); /* Unit and Terminal Requests */
+ }
+ } else if (intf_num == 1) { /* Video Stream Inteface */
+ return usbd_video_stream_request_handler(busid, setup, data, len); /* Interface Stream Requests */
+ }
+ return -1;
+}
+
+static void video_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ g_usbd_video[busid].error_code = 0;
+ g_usbd_video[busid].power_mode = 0;
+ break;
+
+ case USBD_EVENT_SET_INTERFACE: {
+ struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
+ if (intf->bAlternateSetting == 1) {
+ usbd_video_open(busid, intf->bInterfaceNumber);
+ } else {
+ usbd_video_close(busid, intf->bInterfaceNumber);
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+void usbd_video_probe_and_commit_controls_init(uint8_t busid, uint32_t dwFrameInterval, uint32_t dwMaxVideoFrameSize, uint32_t dwMaxPayloadTransferSize)
+{
+ g_usbd_video[busid].probe.hintUnion.bmHint = 0x01;
+ g_usbd_video[busid].probe.hintUnion1.bmHint = 0;
+ g_usbd_video[busid].probe.bFormatIndex = 1;
+ g_usbd_video[busid].probe.bFrameIndex = 1;
+ g_usbd_video[busid].probe.dwFrameInterval = dwFrameInterval;
+ g_usbd_video[busid].probe.wKeyFrameRate = 0;
+ g_usbd_video[busid].probe.wPFrameRate = 0;
+ g_usbd_video[busid].probe.wCompQuality = 0;
+ g_usbd_video[busid].probe.wCompWindowSize = 0;
+ g_usbd_video[busid].probe.wDelay = 0;
+ g_usbd_video[busid].probe.dwMaxVideoFrameSize = dwMaxVideoFrameSize;
+ g_usbd_video[busid].probe.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize;
+ g_usbd_video[busid].probe.dwClockFrequency = 0;
+ g_usbd_video[busid].probe.bmFramingInfo = 0;
+ g_usbd_video[busid].probe.bPreferedVersion = 0;
+ g_usbd_video[busid].probe.bMinVersion = 0;
+ g_usbd_video[busid].probe.bMaxVersion = 0;
+
+ g_usbd_video[busid].commit.hintUnion.bmHint = 0x01;
+ g_usbd_video[busid].commit.hintUnion1.bmHint = 0;
+ g_usbd_video[busid].commit.bFormatIndex = 1;
+ g_usbd_video[busid].commit.bFrameIndex = 1;
+ g_usbd_video[busid].commit.dwFrameInterval = dwFrameInterval;
+ g_usbd_video[busid].commit.wKeyFrameRate = 0;
+ g_usbd_video[busid].commit.wPFrameRate = 0;
+ g_usbd_video[busid].commit.wCompQuality = 0;
+ g_usbd_video[busid].commit.wCompWindowSize = 0;
+ g_usbd_video[busid].commit.wDelay = 0;
+ g_usbd_video[busid].commit.dwMaxVideoFrameSize = dwMaxVideoFrameSize;
+ g_usbd_video[busid].commit.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize;
+ g_usbd_video[busid].commit.dwClockFrequency = 0;
+ g_usbd_video[busid].commit.bmFramingInfo = 0;
+ g_usbd_video[busid].commit.bPreferedVersion = 0;
+ g_usbd_video[busid].commit.bMinVersion = 0;
+ g_usbd_video[busid].commit.bMaxVersion = 0;
+}
+
+struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf,
+ uint32_t dwFrameInterval,
+ uint32_t dwMaxVideoFrameSize,
+ uint32_t dwMaxPayloadTransferSize)
+{
+ intf->class_interface_handler = video_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = video_notify_handler;
+
+ g_usbd_video[busid].info[0].bDescriptorSubtype = VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE;
+ g_usbd_video[busid].info[0].bEntityId = 0x01;
+ g_usbd_video[busid].info[0].wTerminalType = VIDEO_ITT_CAMERA;
+ g_usbd_video[busid].info[1].bDescriptorSubtype = VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE;
+ g_usbd_video[busid].info[1].bEntityId = 0x03;
+ g_usbd_video[busid].info[1].wTerminalType = 0x00;
+ g_usbd_video[busid].info[2].bDescriptorSubtype = VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE;
+ g_usbd_video[busid].info[2].bEntityId = 0x02;
+ g_usbd_video[busid].info[2].wTerminalType = 0x00;
+
+ usbd_video_probe_and_commit_controls_init(busid, dwFrameInterval, dwMaxVideoFrameSize, dwMaxPayloadTransferSize);
+ return intf;
+}
+
+uint32_t usbd_video_mjpeg_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len)
+{
+ uint32_t packets;
+ uint32_t last_packet_size;
+ uint32_t picture_pos = 0;
+ static uint8_t uvc_header[2] = { 0x02, 0x80 };
+
+ packets = (input_len + (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2) ) / (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2);
+ last_packet_size = input_len - ((packets - 1) * (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2));
+
+ for (size_t i = 0; i < packets; i++) {
+ output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i] = uvc_header[0];
+ output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] = uvc_header[1];
+ if (i == (packets - 1)) {
+ memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], last_packet_size);
+ output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] |= (1 << 1);
+ } else {
+ memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2);
+ picture_pos += g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2;
+ }
+ }
+ uvc_header[1] ^= 1;
+ *out_len = (input_len + 2 * packets);
+ return packets;
+} \ No newline at end of file
diff --git a/components/drivers/usb/cherryusb/class/video/usbd_video.h b/components/drivers/usb/cherryusb/class/video/usbd_video.h
new file mode 100644
index 0000000000..5044af69bc
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/video/usbd_video.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_VIDEO_H
+#define USBD_VIDEO_H
+
+#include "usb_video.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init video interface driver */
+struct usbd_interface *usbd_video_init_intf(uint8_t busid, struct usbd_interface *intf,
+ uint32_t dwFrameInterval,
+ uint32_t dwMaxVideoFrameSize,
+ uint32_t dwMaxPayloadTransferSize);
+
+void usbd_video_open(uint8_t busid, uint8_t intf);
+void usbd_video_close(uint8_t busid, uint8_t intf);
+uint32_t usbd_video_mjpeg_payload_fill(uint8_t busid, uint8_t *input, uint32_t input_len, uint8_t *output, uint32_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_VIDEO_H */
diff --git a/components/drivers/usb/cherryusb/class/video/usbh_video.c b/components/drivers/usb/cherryusb/class/video/usbh_video.c
new file mode 100644
index 0000000000..0691066881
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/video/usbh_video.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_video.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_video"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/video%d"
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
+#define DESC_bNumFormats 3 /** Descriptor numformat offset */
+#define DESC_bNumFrameDescriptors 4 /** Descriptor numframe offset */
+#define DESC_bFormatIndex 3 /** Descriptor format index offset */
+#define DESC_bFrameIndex 3 /** Descriptor frame index offset */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_video_buf[128];
+
+static const char *format_type[] = { "uncompressed", "mjpeg" };
+
+static struct usbh_video g_video_class[CONFIG_USBHOST_MAX_VIDEO_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_video *usbh_video_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_VIDEO_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_video_class[devno], 0, sizeof(struct usbh_video));
+ g_video_class[devno].minor = devno;
+ return &g_video_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_video_class_free(struct usbh_video *video_class)
+{
+ int devno = video_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(video_class, 0, sizeof(struct usbh_video));
+}
+
+int usbh_video_get(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len)
+{
+ struct usb_setup_packet *setup = video_class->hport->setup;
+ int ret;
+ uint8_t retry;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = request;
+ setup->wValue = cs << 8;
+ setup->wIndex = (entity_id << 8) | intf;
+ setup->wLength = len;
+
+ retry = 0;
+ while (1) {
+ ret = usbh_control_transfer(video_class->hport, setup, g_video_buf);
+ if (ret > 0) {
+ break;
+ }
+ retry++;
+
+ if (retry == 3) {
+ return ret;
+ }
+ }
+
+ if (buf) {
+ memcpy(buf, g_video_buf, len);
+ }
+
+ return ret;
+}
+
+int usbh_video_set(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len)
+{
+ struct usb_setup_packet *setup = video_class->hport->setup;
+ int ret;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = request;
+ setup->wValue = cs << 8;
+ setup->wIndex = (entity_id << 8) | intf;
+ setup->wLength = len;
+
+ memcpy(g_video_buf, buf, len);
+
+ ret = usbh_control_transfer(video_class->hport, setup, g_video_buf);
+ usb_osal_msleep(50);
+ return ret;
+}
+
+int usbh_videostreaming_get_cur_probe(struct usbh_video *video_class)
+{
+ return usbh_video_get(video_class, VIDEO_REQUEST_GET_CUR, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, (uint8_t *)&video_class->probe, 26);
+}
+
+int usbh_videostreaming_set_cur_probe(struct usbh_video *video_class, uint8_t formatindex, uint8_t frameindex)
+{
+ video_class->probe.bFormatIndex = formatindex;
+ video_class->probe.bFrameIndex = frameindex;
+ video_class->probe.dwMaxPayloadTransferSize = 0;
+ video_class->probe.dwFrameInterval = 333333;
+ return usbh_video_set(video_class, VIDEO_REQUEST_SET_CUR, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, (uint8_t *)&video_class->probe, 26);
+}
+
+int usbh_videostreaming_set_cur_commit(struct usbh_video *video_class, uint8_t formatindex, uint8_t frameindex)
+{
+ memcpy(&video_class->commit, &video_class->probe, sizeof(struct video_probe_and_commit_controls));
+ video_class->commit.bFormatIndex = formatindex;
+ video_class->commit.bFrameIndex = frameindex;
+ video_class->commit.dwFrameInterval = 333333;
+ return usbh_video_set(video_class, VIDEO_REQUEST_SET_CUR, video_class->data_intf, 0x00, VIDEO_VS_COMMIT_CONTROL, (uint8_t *)&video_class->commit, 26);
+}
+
+int usbh_video_open(struct usbh_video *video_class,
+ uint8_t format_type,
+ uint16_t wWidth,
+ uint16_t wHeight,
+ uint8_t altsetting)
+{
+ struct usb_setup_packet *setup = video_class->hport->setup;
+ struct usb_endpoint_descriptor *ep_desc;
+ uint8_t mult;
+ uint16_t mps;
+ int ret;
+ bool found = false;
+ uint8_t formatidx = 0;
+ uint8_t frameidx = 0;
+ uint8_t step;
+
+ if (video_class->is_opened) {
+ return 0;
+ }
+
+ for (uint8_t i = 0; i < video_class->num_of_formats; i++) {
+ if (format_type == video_class->format[i].format_type) {
+ formatidx = i + 1;
+ for (uint8_t j = 0; j < video_class->format[i].num_of_frames; j++) {
+ if ((wWidth == video_class->format[i].frame[j].wWidth) &&
+ (wHeight == video_class->format[i].frame[j].wHeight)) {
+ frameidx = j + 1;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (found == false) {
+ return -USB_ERR_NODEV;
+ }
+
+ if (altsetting > (video_class->num_of_intf_altsettings - 1)) {
+ return -USB_ERR_INVAL;
+ }
+
+ /* Open video step:
+ * Get CUR request (probe)
+ * Set CUR request (probe)
+ * Get CUR request (probe)
+ * Get MAX request (probe)
+ * Get MIN request (probe)
+ * Get CUR request (probe)
+ * Set CUR request (commit)
+ *
+ */
+ step = 0;
+ ret = usbh_videostreaming_get_cur_probe(video_class);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 1;
+ ret = usbh_videostreaming_set_cur_probe(video_class, formatidx, frameidx);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 2;
+ ret = usbh_videostreaming_get_cur_probe(video_class);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 3;
+ ret = usbh_video_get(video_class, VIDEO_REQUEST_GET_MAX, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, NULL, 26);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 4;
+ ret = usbh_video_get(video_class, VIDEO_REQUEST_GET_MIN, video_class->data_intf, 0x00, VIDEO_VS_PROBE_CONTROL, NULL, 26);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 5;
+ ret = usbh_videostreaming_set_cur_probe(video_class, formatidx, frameidx);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 6;
+ ret = usbh_videostreaming_get_cur_probe(video_class);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 7;
+ ret = usbh_videostreaming_set_cur_commit(video_class, formatidx, frameidx);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ step = 8;
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_SET_INTERFACE;
+ setup->wValue = altsetting;
+ setup->wIndex = video_class->data_intf;
+ setup->wLength = 0;
+
+ ret = usbh_control_transfer(video_class->hport, setup, NULL);
+ if (ret < 0) {
+ goto errout;
+ }
+
+ ep_desc = &video_class->hport->config.intf[video_class->data_intf].altsetting[altsetting].ep[0].ep_desc;
+ mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
+ mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
+ if (ep_desc->bEndpointAddress & 0x80) {
+ video_class->isoin_mps = mps * (mult + 1);
+ USBH_EP_INIT(video_class->isoin, ep_desc);
+ } else {
+ video_class->isoout_mps = mps * (mult + 1);
+ USBH_EP_INIT(video_class->isoout, ep_desc);
+ }
+
+ USB_LOG_INFO("Open video and select formatidx:%u, frameidx:%u, altsetting:%u\r\n", formatidx, frameidx, altsetting);
+ video_class->is_opened = true;
+ video_class->current_format = format_type;
+ return ret;
+
+errout:
+ USB_LOG_ERR("Fail to open video in step %u\r\n", step);
+ return ret;
+}
+
+int usbh_video_close(struct usbh_video *video_class)
+{
+ struct usb_setup_packet *setup = video_class->hport->setup;
+ int ret = 0;
+
+ USB_LOG_INFO("Close video device\r\n");
+
+ video_class->is_opened = false;
+
+ if (video_class->isoin) {
+ video_class->isoin = NULL;
+ }
+
+ if (video_class->isoout) {
+ video_class->isoout = NULL;
+ }
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_SET_INTERFACE;
+ setup->wValue = 0;
+ setup->wIndex = video_class->data_intf;
+ setup->wLength = 0;
+
+ ret = usbh_control_transfer(video_class->hport, setup, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+ return ret;
+}
+
+void usbh_video_list_info(struct usbh_video *video_class)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ uint8_t mult;
+ uint16_t mps;
+
+ USB_LOG_INFO("============= Video device information ===================\r\n");
+ USB_LOG_RAW("bcdVDC:%04x\r\n", video_class->bcdVDC);
+ USB_LOG_RAW("Num of altsettings:%u\r\n", video_class->num_of_intf_altsettings);
+
+ for (uint8_t i = 0; i < video_class->num_of_intf_altsettings; i++) {
+ if (i == 0) {
+ USB_LOG_RAW("Ingore altsetting 0\r\n");
+ continue;
+ }
+ ep_desc = &video_class->hport->config.intf[video_class->data_intf].altsetting[i].ep[0].ep_desc;
+
+ mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT;
+ mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK;
+
+ USB_LOG_RAW("Altsetting:%u, Ep=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n",
+ i,
+ ep_desc->bEndpointAddress,
+ ep_desc->bmAttributes,
+ mps,
+ ep_desc->bInterval,
+ mult);
+ }
+
+ USB_LOG_RAW("bNumFormats:%u\r\n", video_class->num_of_formats);
+ for (uint8_t i = 0; i < video_class->num_of_formats; i++) {
+ USB_LOG_RAW(" FormatIndex:%u\r\n", i + 1);
+ USB_LOG_RAW(" FormatType:%s\r\n", format_type[video_class->format[i].format_type]);
+ USB_LOG_RAW(" bNumFrames:%u\r\n", video_class->format[i].num_of_frames);
+ USB_LOG_RAW(" Resolution:\r\n");
+ for (uint8_t j = 0; j < video_class->format[i].num_of_frames; j++) {
+ USB_LOG_RAW(" FrameIndex:%u\r\n", j + 1);
+ USB_LOG_RAW(" wWidth: %d, wHeight: %d\r\n",
+ video_class->format[i].frame[j].wWidth,
+ video_class->format[i].frame[j].wHeight);
+ }
+ }
+
+ USB_LOG_INFO("============= Video device information ===================\r\n");
+}
+
+static int usbh_video_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret;
+ uint8_t cur_iface = 0xff;
+ // uint8_t cur_alt_setting = 0xff;
+ uint8_t frame_index = 0xff;
+ uint8_t format_index = 0xff;
+ uint8_t num_of_frames = 0xff;
+ uint8_t *p;
+
+ struct usbh_video *video_class = usbh_video_class_alloc();
+ if (video_class == NULL) {
+ USB_LOG_ERR("Fail to alloc video_class\r\n");
+ return -USB_ERR_NOMEM;
+ }
+
+ video_class->hport = hport;
+ video_class->ctrl_intf = intf;
+ video_class->data_intf = intf + 1;
+ video_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
+
+ hport->config.intf[intf].priv = video_class;
+
+ ret = usbh_video_close(video_class);
+ if (ret < 0) {
+ USB_LOG_ERR("Fail to close video device\r\n");
+ return ret;
+ }
+
+ p = hport->raw_config_desc;
+ while (p[DESC_bLength]) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ cur_iface = p[INTF_DESC_bInterfaceNumber];
+ //cur_alt_setting = p[INTF_DESC_bAlternateSetting];
+ break;
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ //ep_desc = (struct usb_endpoint_descriptor *)p;
+ break;
+ case VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE:
+ if (cur_iface == video_class->ctrl_intf) {
+ switch (p[DESC_bDescriptorSubType]) {
+ case VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE:
+ video_class->bcdVDC = ((uint16_t)p[4] << 8) | (uint16_t)p[3];
+ break;
+ case VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE:
+ break;
+ case VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE:
+ break;
+
+ default:
+ break;
+ }
+ } else if (cur_iface == video_class->data_intf) {
+ switch (p[DESC_bDescriptorSubType]) {
+ case VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE:
+ video_class->num_of_formats = p[DESC_bNumFormats];
+ break;
+ case VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_SUBTYPE:
+ format_index = p[DESC_bFormatIndex];
+ num_of_frames = p[DESC_bNumFrameDescriptors];
+
+ video_class->format[format_index - 1].num_of_frames = num_of_frames;
+ video_class->format[format_index - 1].format_type = USBH_VIDEO_FORMAT_UNCOMPRESSED;
+ break;
+ case VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_SUBTYPE:
+ format_index = p[DESC_bFormatIndex];
+ num_of_frames = p[DESC_bNumFrameDescriptors];
+
+ video_class->format[format_index - 1].num_of_frames = num_of_frames;
+ video_class->format[format_index - 1].format_type = USBH_VIDEO_FORMAT_MJPEG;
+ break;
+ case VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_SUBTYPE:
+ frame_index = p[DESC_bFrameIndex];
+
+ video_class->format[format_index - 1].frame[frame_index - 1].wWidth = ((struct video_cs_if_vs_frame_uncompressed_descriptor *)p)->wWidth;
+ video_class->format[format_index - 1].frame[frame_index - 1].wHeight = ((struct video_cs_if_vs_frame_uncompressed_descriptor *)p)->wHeight;
+ break;
+ case VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE:
+ frame_index = p[DESC_bFrameIndex];
+
+ video_class->format[format_index - 1].frame[frame_index - 1].wWidth = ((struct video_cs_if_vs_frame_mjpeg_descriptor *)p)->wWidth;
+ video_class->format[format_index - 1].frame[frame_index - 1].wHeight = ((struct video_cs_if_vs_frame_mjpeg_descriptor *)p)->wHeight;
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ usbh_video_list_info(video_class);
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, video_class->minor);
+
+ USB_LOG_INFO("Register Video Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_video_run(video_class);
+ return ret;
+}
+
+static int usbh_video_ctrl_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_video *video_class = (struct usbh_video *)hport->config.intf[intf].priv;
+
+ if (video_class) {
+ if (video_class->isoin) {
+ }
+
+ if (video_class->isoout) {
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister Video Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_video_stop(video_class);
+ }
+
+ usbh_video_class_free(video_class);
+ }
+
+ return ret;
+}
+
+static int usbh_video_streaming_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+static int usbh_video_streaming_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ return 0;
+}
+
+__WEAK void usbh_video_run(struct usbh_video *video_class)
+{
+}
+
+__WEAK void usbh_video_stop(struct usbh_video *video_class)
+{
+}
+
+const struct usbh_class_driver video_ctrl_class_driver = {
+ .driver_name = "video_ctrl",
+ .connect = usbh_video_ctrl_connect,
+ .disconnect = usbh_video_ctrl_disconnect
+};
+
+const struct usbh_class_driver video_streaming_class_driver = {
+ .driver_name = "video_streaming",
+ .connect = usbh_video_streaming_connect,
+ .disconnect = usbh_video_streaming_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info video_ctrl_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_VIDEO,
+ .subclass = VIDEO_SC_VIDEOCONTROL,
+ .protocol = VIDEO_PC_PROTOCOL_UNDEFINED,
+ .id_table = NULL,
+ .class_driver = &video_ctrl_class_driver
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info video_streaming_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_VIDEO,
+ .subclass = VIDEO_SC_VIDEOSTREAMING,
+ .protocol = VIDEO_PC_PROTOCOL_UNDEFINED,
+ .id_table = NULL,
+ .class_driver = &video_streaming_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/video/usbh_video.h b/components/drivers/usb/cherryusb/class/video/usbh_video.h
new file mode 100644
index 0000000000..dac5792c14
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/video/usbh_video.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_VIDEO_H
+#define USBH_VIDEO_H
+
+#include "usb_video.h"
+
+#define USBH_VIDEO_FORMAT_UNCOMPRESSED 0
+#define USBH_VIDEO_FORMAT_MJPEG 1
+
+struct usbh_video_resolution {
+ uint16_t wWidth;
+ uint16_t wHeight;
+};
+
+struct usbh_video_format {
+ struct usbh_video_resolution frame[12];
+ uint8_t format_type;
+ uint8_t num_of_frames;
+};
+
+struct usbh_videoframe {
+ uint8_t *frame_buf;
+ uint32_t frame_bufsize;
+ uint32_t frame_format;
+ uint32_t frame_size;
+};
+
+struct usbh_videostreaming {
+ struct usbh_videoframe *frame;
+ uint32_t frame_format;
+ uint32_t bufoffset;
+ uint16_t width;
+ uint16_t height;
+};
+
+struct usbh_video {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *isoin; /* ISO IN endpoint */
+ struct usb_endpoint_descriptor *isoout; /* ISO OUT endpoint */
+
+ uint8_t ctrl_intf; /* interface number */
+ uint8_t data_intf; /* interface number */
+ uint8_t minor;
+ struct video_probe_and_commit_controls probe;
+ struct video_probe_and_commit_controls commit;
+ uint16_t isoin_mps;
+ uint16_t isoout_mps;
+ bool is_opened;
+ uint8_t current_format;
+ uint16_t bcdVDC;
+ uint8_t num_of_intf_altsettings;
+ uint8_t num_of_formats;
+ struct usbh_video_format format[3];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_video_get(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len);
+int usbh_video_set(struct usbh_video *video_class, uint8_t request, uint8_t intf, uint8_t entity_id, uint8_t cs, uint8_t *buf, uint16_t len);
+
+int usbh_video_open(struct usbh_video *video_class,
+ uint8_t format_type,
+ uint16_t wWidth,
+ uint16_t wHeight,
+ uint8_t altsetting);
+int usbh_video_close(struct usbh_video *video_class);
+
+void usbh_video_list_info(struct usbh_video *video_class);
+
+void usbh_video_run(struct usbh_video *video_class);
+void usbh_video_stop(struct usbh_video *video_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_VIDEO_H */
diff --git a/components/drivers/usb/cherryusb/class/wireless/ndis.h b/components/drivers/usb/cherryusb/class/wireless/ndis.h
new file mode 100644
index 0000000000..80a6e66968
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/ndis.h
@@ -0,0 +1,270 @@
+/* This file has been prepared for Doxygen automatic documentation generation.*/
+/*! \file ndis.h ***************************************************************
+ *
+ * \brief
+ * This file contains the possible external configuration of the USB.
+ *
+ * \addtogroup usbstick
+ *
+ *
+ ******************************************************************************/
+
+/**
+ \ingroup usbstick
+ \defgroup RNDIS RNDIS Support
+ @{
+ */
+
+/*
+ * ndis.h
+ *
+ * Modified by Colin O'Flynn <[email protected]>
+ * ntddndis.h modified by Benedikt Spranger <[email protected]>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <[email protected]>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
+#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
+#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
+
+/* from drivers/net/sk98lin/h/skgepnmi.h */
+#define OID_PNP_CAPABILITIES 0xFD010100
+#define OID_PNP_SET_POWER 0xFD010101
+#define OID_PNP_QUERY_POWER 0xFD010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
+#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
+
+enum NDIS_DEVICE_POWER_STATE {
+ NdisDeviceStateUnspecified = 0,
+ NdisDeviceStateD0,
+ NdisDeviceStateD1,
+ NdisDeviceStateD2,
+ NdisDeviceStateD3,
+ NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+ enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
+};
+
+/* NDIS_PNP_CAPABILITIES.Flags constants */
+#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
+#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
+#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
+
+/*
+struct NDIS_PNP_CAPABILITIES {
+ __le32 Flags;
+ struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+ __le32 Priority;
+ __le32 Reserved;
+ __le32 MaskSize;
+ __le32 PatternOffset;
+ __le32 PatternSize;
+ __le32 PatternFlags;
+};
+*/
+
+/* Required Object IDs (OIDs) */
+#define OID_GEN_SUPPORTED_LIST 0x00010101
+#define OID_GEN_HARDWARE_STATUS 0x00010102
+#define OID_GEN_MEDIA_SUPPORTED 0x00010103
+#define OID_GEN_MEDIA_IN_USE 0x00010104
+#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
+#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
+#define OID_GEN_LINK_SPEED 0x00010107
+#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
+#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
+#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
+#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
+#define OID_GEN_VENDOR_ID 0x0001010C
+#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
+#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
+#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
+#define OID_GEN_DRIVER_VERSION 0x00010110
+#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
+#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
+#define OID_GEN_MAC_OPTIONS 0x00010113
+#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
+#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
+#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
+#define OID_GEN_SUPPORTED_GUIDS 0x00010117
+#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
+#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
+#define OID_GEN_MACHINE_NAME 0x0001021A
+#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
+#define OID_GEN_VLAN_ID 0x0001021C
+
+/* Optional OIDs */
+#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
+#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
+
+/* Required statistics OIDs */
+#define OID_GEN_XMIT_OK 0x00020101
+#define OID_GEN_RCV_OK 0x00020102
+#define OID_GEN_XMIT_ERROR 0x00020103
+#define OID_GEN_RCV_ERROR 0x00020104
+#define OID_GEN_RCV_NO_BUFFER 0x00020105
+
+/* Optional statistics OIDs */
+#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
+#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
+#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
+#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
+#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
+#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
+#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
+#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
+#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
+#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
+#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
+#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
+#define OID_GEN_RCV_CRC_ERROR 0x0002020D
+#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
+#define OID_GEN_GET_TIME_CAPS 0x0002020F
+#define OID_GEN_GET_NETCARD_TIME 0x00020210
+#define OID_GEN_NETCARD_LOAD 0x00020211
+#define OID_GEN_DEVICE_PROFILE 0x00020212
+#define OID_GEN_INIT_TIME_MS 0x00020213
+#define OID_GEN_RESET_COUNTS 0x00020214
+#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
+#define OID_GEN_FRIENDLY_NAME 0x00020216
+#define OID_GEN_MINIPORT_INFO 0x00020217
+#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
+
+/* IEEE 802.3 (Ethernet) OIDs */
+#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
+
+#define OID_802_3_PERMANENT_ADDRESS 0x01010101
+#define OID_802_3_CURRENT_ADDRESS 0x01010102
+#define OID_802_3_MULTICAST_LIST 0x01010103
+#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
+#define OID_802_3_MAC_OPTIONS 0x01010105
+#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
+#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
+#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
+#define OID_802_3_XMIT_DEFERRED 0x01020201
+#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
+#define OID_802_3_RCV_OVERRUN 0x01020203
+#define OID_802_3_XMIT_UNDERRUN 0x01020204
+#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
+#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
+#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
+
+/* Wireless LAN OIDs */
+/* Mandatory */
+#define OID_802_11_BSSID 0x0D010101 /* Q S */
+#define OID_802_11_SSID 0x0D010102 /* Q S */
+#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 /* Q S */
+#define OID_802_11_RSSI 0x0D010206 /* Q I */
+#define OID_802_11_BSSID_LIST 0x0D010217 /* Q */
+#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A /* S */
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 /* Q S */
+#define OID_802_11_SUPPORTED_RATES 0x0D01020E /* Q */
+#define OID_802_11_CONFIGURATION 0x0D010211 /* Q S */
+#define OID_802_11_ADD_WEP 0x0D010113 /* S */
+#define OID_802_11_WEP_STATUS 0x0D01011B /* Q S */
+#define OID_802_11_REMOVE_WEP 0x0D010114 /* S */
+#define OID_802_11_DISASSOCIATE 0x0D010115 /* S */
+#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 /* Q S */
+#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C /* S */
+
+/* OID_GEN_MINIPORT_INFO constants */
+#define NDIS_MINIPORT_BUS_MASTER 0x00000001
+#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
+#define NDIS_MINIPORT_SG_LIST 0x00000004
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
+#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
+#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
+#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
+#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
+#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
+#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
+#define NDIS_MINIPORT_IS_CO 0x00000400
+#define NDIS_MINIPORT_DESERIALIZE 0x00000800
+#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
+#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
+#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
+#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
+#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
+#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
+#define NDIS_MINIPORT_HIDDEN 0x00040000
+#define NDIS_MINIPORT_SWENUM 0x00080000
+#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
+#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
+#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
+#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
+#define NDIS_MINIPORT_64BITS_DMA 0x01000000
+
+#define NDIS_MEDIUM_802_3 0x00000000
+#define NDIS_MEDIUM_802_5 0x00000001
+#define NDIS_MEDIUM_FDDI 0x00000002
+#define NDIS_MEDIUM_WAN 0x00000003
+#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
+#define NDIS_MEDIUM_DIX 0x00000005
+#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
+#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
+#define NDIS_MEDIUM_ATM 0x00000008
+#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
+#define NDIS_MEDIUM_IRDA 0x0000000A
+#define NDIS_MEDIUM_BPC 0x0000000B
+#define NDIS_MEDIUM_CO_WAN 0x0000000C
+#define NDIS_MEDIUM_1394 0x0000000D
+
+#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
+#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
+#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
+#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
+#define NDIS_PACKET_TYPE_SMT 0x00000040
+#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
+#define NDIS_PACKET_TYPE_GROUP 0x00000100
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
+#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
+#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
+
+#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
+#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
+#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
+#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
+#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
+#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
+#define NDIS_MAC_OPTION_RESERVED 0x80000000
+
+/** Hardware status of the underlying NIC */
+#define NDIS_HW_STS_READY 0x00000000UL
+#define NDIS_HW_STS_INITIALIZING 0x00000001UL
+#define NDIS_HW_STS_RESET 0x00000002UL
+#define NDIS_HW_STS_CLOSING 0x00000003UL
+#define NDIS_HW_STS_NOT_READY 0x00000004UL
+
+#endif /* _LINUX_NDIS_H */
+
+/** @} */
diff --git a/components/drivers/usb/cherryusb/class/wireless/rndis_protocol.h b/components/drivers/usb/cherryusb/class/wireless/rndis_protocol.h
new file mode 100644
index 0000000000..54b4ebf626
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/rndis_protocol.h
@@ -0,0 +1,302 @@
+/**
+ * \file rndis_protocol.h
+ * RNDIS Defines
+ *
+ * \author
+ * Colin O'Flynn <[email protected]>
+ *
+ * \addtogroup usbstick
+ */
+
+/* Copyright (c) 2008 Colin O'Flynn
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of the copyright holders nor the names of
+ 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 OWNER 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.
+*/
+
+#ifndef _RNDIS_H
+#define _RNDIS_H
+
+/**
+ \addtogroup RNDIS
+ @{
+ */
+
+#include <stdint.h>
+
+#define RNDIS_MAJOR_VERSION 1
+#define RNDIS_MINOR_VERSION 0
+
+#define RNDIS_STATUS_SUCCESS 0X00000000
+#define RNDIS_STATUS_FAILURE 0XC0000001
+#define RNDIS_STATUS_INVALID_DATA 0XC0010015
+#define RNDIS_STATUS_NOT_SUPPORTED 0XC00000BB
+#define RNDIS_STATUS_MEDIA_CONNECT 0X4001000B
+#define RNDIS_STATUS_MEDIA_DISCONNECT 0X4001000C
+
+/* Message set for Connectionless (802.3) Devices */
+#define REMOTE_NDIS_PACKET_MSG 0x00000001
+#define REMOTE_NDIS_INITIALIZE_MSG 0X00000002
+#define REMOTE_NDIS_HALT_MSG 0X00000003
+#define REMOTE_NDIS_QUERY_MSG 0X00000004
+#define REMOTE_NDIS_SET_MSG 0X00000005
+#define REMOTE_NDIS_RESET_MSG 0X00000006
+#define REMOTE_NDIS_INDICATE_STATUS_MSG 0X00000007
+#define REMOTE_NDIS_KEEPALIVE_MSG 0X00000008
+#define REMOTE_NDIS_INITIALIZE_CMPLT 0X80000002
+#define REMOTE_NDIS_QUERY_CMPLT 0X80000004
+#define REMOTE_NDIS_SET_CMPLT 0X80000005
+#define REMOTE_NDIS_RESET_CMPLT 0X80000006
+#define REMOTE_NDIS_KEEPALIVE_CMPLT 0X80000008
+
+typedef uint32_t rndis_MessageType_t;
+typedef uint32_t rndis_MessageLength_t;
+typedef uint32_t rndis_RequestId_t;
+typedef uint32_t rndis_MajorVersion_t;
+typedef uint32_t rndis_MinorVersion_t;
+typedef uint32_t rndis_MaxTransferSize_t;
+typedef uint32_t rndis_Status_t;
+
+/* Device Flags */
+#define RNDIS_DF_CONNECTIONLESS 0x00000001
+#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
+typedef uint32_t rndis_DeviceFlags_t;
+
+/* Mediums */
+#define RNDIS_MEDIUM_802_3 0x00000000
+typedef uint32_t rndis_Medium_t;
+
+typedef uint32_t rndis_MaxPacketsPerTransfer_t;
+typedef uint32_t rndis_PacketAlignmentFactor_t;
+typedef uint32_t rndis_AfListOffset_t;
+typedef uint32_t rndis_AfListSize_t;
+
+/*** Remote NDIS Generic Message type ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+} rndis_generic_msg_t;
+
+/*** Remote NDIS Initialize Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_MajorVersion_t MajorVersion;
+ rndis_MinorVersion_t MinorVersion;
+ rndis_MaxTransferSize_t MaxTransferSize;
+} rndis_initialize_msg_t;
+
+/* Response: */
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Status_t Status;
+ rndis_MajorVersion_t MajorVersion;
+ rndis_MinorVersion_t MinorVersion;
+ rndis_DeviceFlags_t DeviceFlags;
+ rndis_Medium_t Medium;
+ rndis_MaxPacketsPerTransfer_t MaxPacketsPerTransfer;
+ rndis_MaxTransferSize_t MaxTransferSize;
+ rndis_PacketAlignmentFactor_t PacketAlignmentFactor;
+ rndis_AfListOffset_t AfListOffset;
+ rndis_AfListSize_t AfListSize;
+} rndis_initialize_cmplt_t;
+
+/*** Remote NDIS Halt Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+} rndis_halt_msg_t;
+
+typedef uint32_t rndis_Oid_t;
+typedef uint32_t rndis_InformationBufferLength_t;
+typedef uint32_t rndis_InformationBufferOffset_t;
+typedef uint32_t rndis_DeviceVcHandle_t;
+
+/*** Remote NDIS Query Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Oid_t Oid;
+ rndis_InformationBufferLength_t InformationBufferLength;
+ rndis_InformationBufferOffset_t InformationBufferOffset;
+ rndis_DeviceVcHandle_t DeviceVcHandle;
+} rndis_query_msg_t;
+
+/* Response: */
+
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Status_t Status;
+ rndis_InformationBufferLength_t InformationBufferLength;
+ rndis_InformationBufferOffset_t InformationBufferOffset;
+} rndis_query_cmplt_t;
+
+/*** Remote NDIS Set Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Oid_t Oid;
+ rndis_InformationBufferLength_t InformationBufferLength;
+ rndis_InformationBufferOffset_t InformationBufferOffset;
+ rndis_DeviceVcHandle_t DeviceVcHandle;
+} rndis_set_msg_t;
+
+/* Response */
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Status_t Status;
+} rndis_set_cmplt_t;
+
+/* Information buffer layout for OID_GEN_RNDIS_CONFIG_PARAMETER */
+typedef uint32_t rndis_ParameterNameOffset_t;
+typedef uint32_t rndis_ParameterNameLength_t;
+typedef uint32_t rndis_ParameterType_t;
+typedef uint32_t rndis_ParameterValueOffset_t;
+typedef uint32_t rndis_ParameterValueLength_t;
+
+#define PARAMETER_TYPE_STRING 2
+#define PARAMETER_TYPE_NUMERICAL 0
+
+typedef struct {
+ rndis_ParameterNameOffset_t ParameterNameOffset;
+ rndis_ParameterNameLength_t ParameterNameLength;
+ rndis_ParameterType_t ParameterType;
+ rndis_ParameterValueOffset_t ParameterValueOffset;
+ rndis_ParameterValueLength_t ParameterValueLength;
+} rndis_config_parameter_t;
+
+typedef uint32_t rndis_Reserved_t;
+
+/*** Remote NDIS Soft Reset Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_Reserved_t Reserved;
+} rndis_reset_msg_t;
+
+typedef uint32_t rndis_AddressingReset_t;
+
+/* Response: */
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_Status_t Status;
+ rndis_AddressingReset_t AddressingReset;
+} rndis_reset_cmplt_t;
+
+/*** Remote NDIS Indicate Status Message ***/
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_Status_t Status;
+ rndis_Status_t StatusBufferLength;
+ rndis_Status_t StatusBufferOffset;
+} rndis_indicate_status_t;
+
+typedef uint32_t rndis_DiagStatus_t;
+typedef uint32_t rndis_ErrorOffset_t;
+
+typedef struct {
+ rndis_DiagStatus_t DiagStatus;
+ rndis_ErrorOffset_t ErrorOffset;
+} rndis_diagnostic_info_t;
+
+/*** Remote NDIS Keepalive Message */
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+} rndis_keepalive_msg_t;
+
+/* Response: */
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_RequestId_t RequestId;
+ rndis_Status_t Status;
+} rndis_keepalive_cmplt_t;
+
+/*** Remote NDIS Data Packet ***/
+
+typedef uint32_t rndis_DataOffset_t;
+typedef uint32_t rndis_DataLength_t;
+typedef uint32_t rndis_OOBDataOffset_t;
+typedef uint32_t rndis_OOBDataLength_t;
+typedef uint32_t rndis_NumOOBDataElements_t;
+typedef uint32_t rndis_PerPacketInfoOffset_t;
+typedef uint32_t rndis_PerPacketInfoLength_t;
+
+typedef struct {
+ rndis_MessageType_t MessageType;
+ rndis_MessageLength_t MessageLength;
+ rndis_DataOffset_t DataOffset;
+ rndis_DataLength_t DataLength;
+ rndis_OOBDataOffset_t OOBDataOffset;
+ rndis_OOBDataLength_t OOBDataLength;
+ rndis_NumOOBDataElements_t NumOOBDataElements;
+ rndis_PerPacketInfoOffset_t PerPacketInfoOffset;
+ rndis_PerPacketInfoLength_t PerPacketInfoLength;
+ rndis_DeviceVcHandle_t DeviceVcHandle;
+ rndis_Reserved_t Reserved;
+} rndis_data_packet_t;
+
+typedef uint32_t rndis_ClassInformationOffset_t;
+typedef uint32_t rndis_Size_t;
+typedef uint32_t rndis_Type_t;
+
+typedef struct {
+ rndis_Size_t Size;
+ rndis_Type_t Type;
+ rndis_ClassInformationOffset_t ClassInformationType;
+} rndis_OOB_packet_t;
+
+#include "ndis.h"
+
+typedef enum rnids_state_e {
+ rndis_uninitialized,
+ rndis_initialized,
+ rndis_data_initialized
+} rndis_state_t;
+
+typedef struct {
+ uint32_t txok;
+ uint32_t rxok;
+ uint32_t txbad;
+ uint32_t rxbad;
+} usb_eth_stat_t;
+
+#endif /* _RNDIS_H */
+
+/** @} */
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c b/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c
new file mode 100644
index 0000000000..aaea5431a4
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_rndis.h"
+#include "rndis_protocol.h"
+
+#define RNDIS_OUT_EP_IDX 0
+#define RNDIS_IN_EP_IDX 1
+#define RNDIS_INT_EP_IDX 2
+
+/* Describe EndPoints configuration */
+static struct usbd_endpoint rndis_ep_data[3];
+
+#define RNDIS_INQUIRY_PUT(src, len) (memcpy(infomation_buffer, src, len))
+#define RNDIS_INQUIRY_PUT_LE32(value) (*(uint32_t *)infomation_buffer = (value))
+
+/* Device data structure */
+struct usbd_rndis_priv {
+ uint32_t drv_version;
+ uint32_t link_status;
+ uint32_t net_filter;
+ usb_eth_stat_t eth_state;
+ rndis_state_t init_state;
+ uint8_t mac[6];
+} g_usbd_rndis;
+
+#if CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE < 140
+#undef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE
+#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156
+#endif
+
+#if CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE < 1580
+#undef CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE
+#define CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE 1580
+#endif
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_rx_buffer[CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_tx_buffer[CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE];
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t rndis_encapsulated_resp_buffer[CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t NOTIFY_RESPONSE_AVAILABLE[8];
+
+volatile uint8_t *g_rndis_rx_data_buffer;
+volatile uint32_t g_rndis_rx_data_length;
+volatile uint32_t g_rndis_tx_data_length;
+
+/* RNDIS options list */
+const uint32_t oid_supported_list[] = {
+ /* General OIDs */
+ OID_GEN_SUPPORTED_LIST,
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+
+ OID_GEN_PHYSICAL_MEDIUM,
+
+ /* General Statistic OIDs */
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_GEN_RCV_NO_BUFFER,
+
+ /* Please configure us */
+ OID_GEN_RNDIS_CONFIG_PARAMETER,
+
+ /* 802.3 OIDs */
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+
+ /* 802.3 Statistic OIDs */
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+
+ OID_802_3_MAC_OPTIONS,
+};
+
+static int rndis_encapsulated_cmd_handler(uint8_t *data, uint32_t len);
+
+static void rndis_notify_rsp(void)
+{
+ memset(NOTIFY_RESPONSE_AVAILABLE, 0, 8);
+ NOTIFY_RESPONSE_AVAILABLE[0] = 0x01;
+ usbd_ep_start_write(0, rndis_ep_data[RNDIS_INT_EP_IDX].ep_addr, NOTIFY_RESPONSE_AVAILABLE, 8);
+}
+
+static int rndis_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ switch (setup->bRequest) {
+ case CDC_REQUEST_SEND_ENCAPSULATED_COMMAND:
+ rndis_encapsulated_cmd_handler(*data, setup->wLength);
+ break;
+ case CDC_REQUEST_GET_ENCAPSULATED_RESPONSE:
+ *data = rndis_encapsulated_resp_buffer;
+ *len = ((rndis_generic_msg_t *)rndis_encapsulated_resp_buffer)->MessageLength;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rndis_init_cmd_handler(uint8_t *data, uint32_t len);
+static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len);
+static int rndis_query_cmd_handler(uint8_t *data, uint32_t len);
+static int rndis_set_cmd_handler(uint8_t *data, uint32_t len);
+static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len);
+static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len);
+
+static int rndis_encapsulated_cmd_handler(uint8_t *data, uint32_t len)
+{
+ switch (((rndis_generic_msg_t *)data)->MessageType) {
+ case REMOTE_NDIS_INITIALIZE_MSG:
+ return rndis_init_cmd_handler(data, len);
+ case REMOTE_NDIS_HALT_MSG:
+ return rndis_halt_cmd_handler(data, len);
+ case REMOTE_NDIS_QUERY_MSG:
+ return rndis_query_cmd_handler(data, len);
+ case REMOTE_NDIS_SET_MSG:
+ return rndis_set_cmd_handler(data, len);
+ case REMOTE_NDIS_RESET_MSG:
+ return rndis_reset_cmd_handler(data, len);
+ case REMOTE_NDIS_KEEPALIVE_MSG:
+ return rndis_keepalive_cmd_handler(data, len);
+
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int rndis_init_cmd_handler(uint8_t *data, uint32_t len)
+{
+ rndis_initialize_msg_t *cmd = (rndis_initialize_msg_t *)data;
+ rndis_initialize_cmplt_t *resp;
+
+ resp = ((rndis_initialize_cmplt_t *)rndis_encapsulated_resp_buffer);
+ resp->RequestId = cmd->RequestId;
+ resp->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
+ resp->MessageLength = sizeof(rndis_initialize_cmplt_t);
+ resp->MajorVersion = RNDIS_MAJOR_VERSION;
+ resp->MinorVersion = RNDIS_MINOR_VERSION;
+ resp->Status = RNDIS_STATUS_SUCCESS;
+ resp->DeviceFlags = RNDIS_DF_CONNECTIONLESS;
+ resp->Medium = RNDIS_MEDIUM_802_3;
+ resp->MaxPacketsPerTransfer = CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE / 1580;
+ resp->MaxTransferSize = CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE;
+ resp->PacketAlignmentFactor = 0;
+ resp->AfListOffset = 0;
+ resp->AfListSize = 0;
+
+ g_usbd_rndis.init_state = rndis_initialized;
+
+ rndis_notify_rsp();
+ return 0;
+}
+
+static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len)
+{
+ rndis_halt_msg_t *resp;
+
+ resp = ((rndis_halt_msg_t *)rndis_encapsulated_resp_buffer);
+ resp->MessageLength = 0;
+
+ g_usbd_rndis.init_state = rndis_uninitialized;
+
+ return 0;
+}
+
+static int rndis_query_cmd_handler(uint8_t *data, uint32_t len)
+{
+ rndis_query_msg_t *cmd = (rndis_query_msg_t *)data;
+ rndis_query_cmplt_t *resp;
+ uint8_t *infomation_buffer;
+ uint32_t infomation_len = 0;
+
+ resp = ((rndis_query_cmplt_t *)rndis_encapsulated_resp_buffer);
+ resp->MessageType = REMOTE_NDIS_QUERY_CMPLT;
+ resp->RequestId = cmd->RequestId;
+ resp->InformationBufferOffset = sizeof(rndis_query_cmplt_t) - sizeof(rndis_generic_msg_t);
+ resp->Status = RNDIS_STATUS_SUCCESS;
+
+ infomation_buffer = (uint8_t *)resp + sizeof(rndis_query_cmplt_t);
+
+ switch (cmd->Oid) {
+ case OID_GEN_SUPPORTED_LIST:
+ RNDIS_INQUIRY_PUT(oid_supported_list, sizeof(oid_supported_list));
+ infomation_len = sizeof(oid_supported_list);
+ break;
+ case OID_GEN_HARDWARE_STATUS:
+ RNDIS_INQUIRY_PUT_LE32(NDIS_HW_STS_READY);
+ infomation_len = 4;
+ break;
+ case OID_GEN_MEDIA_SUPPORTED:
+ case OID_GEN_MEDIA_IN_USE:
+ RNDIS_INQUIRY_PUT_LE32(NDIS_MEDIUM_802_3);
+ infomation_len = 4;
+ break;
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ RNDIS_INQUIRY_PUT_LE32(0x05DC);
+ infomation_len = 4;
+ break;
+ case OID_GEN_VENDOR_ID:
+ RNDIS_INQUIRY_PUT_LE32(CONFIG_USBDEV_RNDIS_VENDOR_ID);
+ infomation_len = 4;
+ break;
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ RNDIS_INQUIRY_PUT_LE32(0x0001);
+ infomation_len = 4;
+ break;
+ case OID_GEN_VENDOR_DESCRIPTION:
+ RNDIS_INQUIRY_PUT(CONFIG_USBDEV_RNDIS_VENDOR_DESC, strlen(CONFIG_USBDEV_RNDIS_VENDOR_DESC));
+ infomation_len = (strlen(CONFIG_USBDEV_RNDIS_VENDOR_DESC) + 1);
+ break;
+ case OID_802_3_CURRENT_ADDRESS:
+ case OID_802_3_PERMANENT_ADDRESS:
+ RNDIS_INQUIRY_PUT(g_usbd_rndis.mac, 6);
+ infomation_len = 6;
+ break;
+ case OID_GEN_PHYSICAL_MEDIUM:
+ RNDIS_INQUIRY_PUT_LE32(NDIS_MEDIUM_802_3);
+ infomation_len = 4;
+ break;
+ case OID_GEN_LINK_SPEED:
+ if (usbd_get_ep_mps(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr) > 64) {
+ RNDIS_INQUIRY_PUT_LE32(480000000 / 100);
+ } else {
+ RNDIS_INQUIRY_PUT_LE32(12000000 / 100);
+ }
+
+ infomation_len = 4;
+ break;
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.net_filter);
+ infomation_len = 4;
+ break;
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ RNDIS_INQUIRY_PUT_LE32(CONFIG_USBDEV_RNDIS_ETH_MAX_FRAME_SIZE + CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE);
+ infomation_len = 4;
+ break;
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.link_status);
+ infomation_len = 4;
+ break;
+ case OID_GEN_RNDIS_CONFIG_PARAMETER:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ RNDIS_INQUIRY_PUT_LE32(1); /* one address */
+ infomation_len = 4;
+ break;
+ case OID_802_3_MULTICAST_LIST:
+ //RNDIS_INQUIRY_PUT_LE32(0xE0000000); /* 224.0.0.0 */
+ resp->Status = RNDIS_STATUS_NOT_SUPPORTED;
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_802_3_MAC_OPTIONS:
+ // infomation_len = 0;
+ resp->Status = RNDIS_STATUS_NOT_SUPPORTED;
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_GEN_MAC_OPTIONS:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_802_3_XMIT_ONE_COLLISION:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ case OID_GEN_XMIT_OK:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.txok);
+ infomation_len = 4;
+ break;
+ case OID_GEN_RCV_OK:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.rxok);
+ infomation_len = 4;
+ break;
+ case OID_GEN_RCV_ERROR:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.rxbad);
+ infomation_len = 4;
+ break;
+ case OID_GEN_XMIT_ERROR:
+ RNDIS_INQUIRY_PUT_LE32(g_usbd_rndis.eth_state.txbad);
+ infomation_len = 4;
+ break;
+ case OID_GEN_RCV_NO_BUFFER:
+ RNDIS_INQUIRY_PUT_LE32(0);
+ infomation_len = 4;
+ break;
+ default:
+ resp->Status = RNDIS_STATUS_FAILURE;
+ infomation_len = 0;
+ USB_LOG_WRN("Unhandled query for Object ID 0x%x\r\n", cmd->Oid);
+ break;
+ }
+
+ resp->MessageLength = sizeof(rndis_query_cmplt_t) + infomation_len;
+ resp->InformationBufferLength = infomation_len;
+
+ rndis_notify_rsp();
+ return 0;
+}
+
+static int rndis_set_cmd_handler(uint8_t *data, uint32_t len)
+{
+ rndis_set_msg_t *cmd = (rndis_set_msg_t *)data;
+ rndis_set_cmplt_t *resp;
+ rndis_config_parameter_t *param;
+
+ resp = ((rndis_set_cmplt_t *)rndis_encapsulated_resp_buffer);
+ resp->RequestId = cmd->RequestId;
+ resp->MessageType = REMOTE_NDIS_SET_CMPLT;
+ resp->MessageLength = sizeof(rndis_set_cmplt_t);
+ resp->Status = RNDIS_STATUS_SUCCESS;
+
+ switch (cmd->Oid) {
+ case OID_GEN_RNDIS_CONFIG_PARAMETER:
+ param = (rndis_config_parameter_t *)((uint8_t *)&(cmd->RequestId) + cmd->InformationBufferOffset);
+ USB_LOG_WRN("RNDIS cfg param: NameOfs=%d, NameLen=%d, ValueOfs=%d, ValueLen=%d\r\n",
+ param->ParameterNameOffset, param->ParameterNameLength,
+ param->ParameterValueOffset, param->ParameterValueLength);
+ break;
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ if (cmd->InformationBufferLength < sizeof(g_usbd_rndis.net_filter)) {
+ USB_LOG_WRN("PACKET_FILTER!\r\n");
+ resp->Status = RNDIS_STATUS_INVALID_DATA;
+ } else {
+ uint32_t *filter;
+ /* Parameter starts at offset buf_offset of the req_id field */
+ filter = (uint32_t *)((uint8_t *)&(cmd->RequestId) + cmd->InformationBufferOffset);
+
+ //g_usbd_rndis.net_filter = param->ParameterNameOffset;
+ g_usbd_rndis.net_filter = *(uint32_t *)filter;
+ if (g_usbd_rndis.net_filter) {
+ g_usbd_rndis.init_state = rndis_data_initialized;
+ } else {
+ g_usbd_rndis.init_state = rndis_initialized;
+ }
+ }
+ break;
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ break;
+ case OID_GEN_PROTOCOL_OPTIONS:
+ break;
+ case OID_802_3_MULTICAST_LIST:
+ break;
+ case OID_PNP_ADD_WAKE_UP_PATTERN:
+ case OID_PNP_REMOVE_WAKE_UP_PATTERN:
+ case OID_PNP_ENABLE_WAKE_UP:
+ default:
+ resp->Status = RNDIS_STATUS_FAILURE;
+ USB_LOG_WRN("Unhandled query for Object ID 0x%x\r\n", cmd->Oid);
+ break;
+ }
+
+ rndis_notify_rsp();
+
+ return 0;
+}
+
+static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len)
+{
+ // rndis_reset_msg_t *cmd = (rndis_reset_msg_t *)data;
+ rndis_reset_cmplt_t *resp;
+
+ resp = ((rndis_reset_cmplt_t *)rndis_encapsulated_resp_buffer);
+ resp->MessageType = REMOTE_NDIS_RESET_CMPLT;
+ resp->MessageLength = sizeof(rndis_reset_cmplt_t);
+ resp->Status = RNDIS_STATUS_SUCCESS;
+ resp->AddressingReset = 1;
+
+ g_usbd_rndis.init_state = rndis_uninitialized;
+
+ rndis_notify_rsp();
+
+ return 0;
+}
+
+static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len)
+{
+ rndis_keepalive_msg_t *cmd = (rndis_keepalive_msg_t *)data;
+ rndis_keepalive_cmplt_t *resp;
+
+ resp = ((rndis_keepalive_cmplt_t *)rndis_encapsulated_resp_buffer);
+ resp->RequestId = cmd->RequestId;
+ resp->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
+ resp->MessageLength = sizeof(rndis_keepalive_cmplt_t);
+ resp->Status = RNDIS_STATUS_SUCCESS;
+
+ rndis_notify_rsp();
+
+ return 0;
+}
+
+static void rndis_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ g_usbd_rndis.link_status = NDIS_MEDIA_STATE_DISCONNECTED;
+ break;
+ case USBD_EVENT_CONFIGURED:
+ g_rndis_rx_data_length = 0;
+ g_rndis_tx_data_length = 0;
+ g_usbd_rndis.link_status = NDIS_MEDIA_STATE_CONNECTED;
+ usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void rndis_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ rndis_data_packet_t *hdr;
+
+ hdr = (rndis_data_packet_t *)g_rndis_rx_buffer;
+ g_rndis_rx_data_buffer = g_rndis_rx_buffer;
+ if ((hdr->MessageType != REMOTE_NDIS_PACKET_MSG) || (nbytes < hdr->MessageLength)) {
+ usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
+ return;
+ }
+
+ /* Point to the payload and update the message length */
+ g_rndis_rx_data_buffer += hdr->DataOffset + sizeof(rndis_generic_msg_t);
+ g_rndis_rx_data_length = hdr->DataLength;
+
+ usbd_rndis_data_recv_done();
+}
+
+void rndis_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
+ /* send zlp */
+ usbd_ep_start_write(0, ep, NULL, 0);
+ } else {
+ g_rndis_tx_data_length = 0;
+ }
+}
+
+void rndis_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ //USB_LOG_DBG("len:%d\r\n", nbytes);
+}
+
+#ifdef CONFIG_USBDEV_RNDIS_USING_LWIP
+#include <lwip/pbuf.h>
+
+struct pbuf *usbd_rndis_eth_rx(void)
+{
+ struct pbuf *p;
+
+ if (g_rndis_rx_data_length == 0) {
+ return NULL;
+ }
+ p = pbuf_alloc(PBUF_RAW, g_rndis_rx_data_length, PBUF_POOL);
+ if (p == NULL) {
+ return NULL;
+ }
+ memcpy(p->payload, (uint8_t *)g_rndis_rx_data_buffer, g_rndis_rx_data_length);
+ p->len = g_rndis_rx_data_length;
+
+ USB_LOG_DBG("rxlen:%d\r\n", g_rndis_rx_data_length);
+ g_rndis_rx_data_length = 0;
+ usbd_ep_start_read(0, rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr, g_rndis_rx_buffer, sizeof(g_rndis_rx_buffer));
+
+ return p;
+}
+
+int usbd_rndis_eth_tx(struct pbuf *p)
+{
+ struct pbuf *q;
+ uint8_t *buffer;
+ rndis_data_packet_t *hdr;
+
+ if (g_usbd_rndis.link_status == NDIS_MEDIA_STATE_DISCONNECTED) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ if (g_rndis_tx_data_length > 0) {
+ return -USB_ERR_BUSY;
+ }
+
+ if (p->tot_len > sizeof(g_rndis_tx_buffer)) {
+ p->tot_len = sizeof(g_rndis_tx_buffer);
+ }
+
+ buffer = (uint8_t *)(g_rndis_tx_buffer + sizeof(rndis_data_packet_t));
+ for (q = p; q != NULL; q = q->next) {
+ memcpy(buffer, q->payload, q->len);
+ buffer += q->len;
+ }
+
+ hdr = (rndis_data_packet_t *)g_rndis_tx_buffer;
+
+ memset(hdr, 0, sizeof(rndis_data_packet_t));
+ hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
+ hdr->MessageLength = sizeof(rndis_data_packet_t) + p->tot_len;
+ hdr->DataOffset = sizeof(rndis_data_packet_t) - sizeof(rndis_generic_msg_t);
+ hdr->DataLength = p->tot_len;
+
+ g_rndis_tx_data_length = sizeof(rndis_data_packet_t) + p->tot_len;
+
+ USB_LOG_DBG("txlen:%d\r\n", g_rndis_tx_data_length);
+ return usbd_ep_start_write(0, rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr, g_rndis_tx_buffer, g_rndis_tx_data_length);
+}
+#endif
+struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf,
+ const uint8_t out_ep,
+ const uint8_t in_ep,
+ const uint8_t int_ep, uint8_t mac[6])
+{
+ memcpy(g_usbd_rndis.mac, mac, 6);
+
+ g_usbd_rndis.drv_version = 0x0001;
+ g_usbd_rndis.link_status = NDIS_MEDIA_STATE_DISCONNECTED;
+
+ rndis_ep_data[RNDIS_OUT_EP_IDX].ep_addr = out_ep;
+ rndis_ep_data[RNDIS_OUT_EP_IDX].ep_cb = rndis_bulk_out;
+ rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr = in_ep;
+ rndis_ep_data[RNDIS_IN_EP_IDX].ep_cb = rndis_bulk_in;
+ rndis_ep_data[RNDIS_INT_EP_IDX].ep_addr = int_ep;
+ rndis_ep_data[RNDIS_INT_EP_IDX].ep_cb = rndis_int_in;
+
+ usbd_add_endpoint(0, &rndis_ep_data[RNDIS_OUT_EP_IDX]);
+ usbd_add_endpoint(0, &rndis_ep_data[RNDIS_IN_EP_IDX]);
+ usbd_add_endpoint(0, &rndis_ep_data[RNDIS_INT_EP_IDX]);
+
+ intf->class_interface_handler = rndis_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = rndis_notify_handler;
+
+ return intf;
+}
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h b/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h
new file mode 100644
index 0000000000..8a9581f68d
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbd_rndis.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_RNDIS_H
+#define USBD_RNDIS_H
+
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init rndis interface driver */
+struct usbd_interface *usbd_rndis_init_intf(struct usbd_interface *intf,
+ const uint8_t out_ep,
+ const uint8_t in_ep,
+ const uint8_t int_ep, uint8_t mac[6]);
+
+void usbd_rndis_data_recv_done(void);
+
+#ifdef CONFIG_USBDEV_RNDIS_USING_LWIP
+struct pbuf *usbd_rndis_eth_rx(void);
+int usbd_rndis_eth_tx(struct pbuf *p);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_RNDIS_H */
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c b/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c
new file mode 100644
index 0000000000..4c223dbfd6
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_bluetooth.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_bluetooth"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/bluetooth"
+
+static struct usbh_bluetooth g_bluetooth_class;
+
+#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_tx_buf[1 + CONFIG_USBHOST_BLUETOOTH_TX_SIZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_rx_buf[1 + CONFIG_USBHOST_BLUETOOTH_RX_SIZE];
+#else
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_cmd_buf[1 + 256];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_evt_buf[1 + 256];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_tx_buf[1 + CONFIG_USBHOST_BLUETOOTH_TX_SIZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bluetooth_rx_buf[1 + CONFIG_USBHOST_BLUETOOTH_RX_SIZE];
+#endif
+
+static int usbh_bluetooth_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret = 0;
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ uint8_t mult;
+ uint16_t mps;
+#endif
+
+ struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
+
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ if (intf != 0) {
+ return 0;
+ }
+#endif
+
+ memset(bluetooth_class, 0, sizeof(struct usbh_bluetooth));
+
+ bluetooth_class->hport = hport;
+ bluetooth_class->intf = intf;
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ bluetooth_class->num_of_intf_altsettings = hport->config.intf[intf + 1].altsetting_num;
+#endif
+ hport->config.intf[intf].priv = bluetooth_class;
+
+ for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ if (USB_GET_ENDPOINT_TYPE(ep_desc->bmAttributes) == USB_ENDPOINT_TYPE_INTERRUPT) {
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(bluetooth_class->intin, ep_desc);
+ } else {
+ return -USB_ERR_NOTSUPP;
+ }
+ } else {
+#endif
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(bluetooth_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(bluetooth_class->bulkout, ep_desc);
+ }
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ }
+#endif
+ }
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ USB_LOG_INFO("Num of altsettings:%u\r\n", bluetooth_class->num_of_intf_altsettings);
+
+ for (uint8_t i = 0; i < bluetooth_class->num_of_intf_altsettings; i++) {
+ USB_LOG_INFO("Altsetting:%u\r\n", i);
+ for (uint8_t j = 0; j < hport->config.intf[intf + 1].altsetting[i].intf_desc.bNumEndpoints; j++) {
+ ep_desc = &bluetooth_class->hport->config.intf[intf + 1].altsetting[i].ep[j].ep_desc;
+
+ mult = USB_GET_MULT(ep_desc->wMaxPacketSize);
+ mps = USB_GET_MAXPACKETSIZE(ep_desc->wMaxPacketSize);
+
+ USB_LOG_INFO("\tEp=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n",
+ ep_desc->bEndpointAddress,
+ ep_desc->bmAttributes,
+ mps,
+ ep_desc->bInterval,
+ mult);
+ }
+ }
+
+ ret = usbh_set_interface(hport, intf, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ USB_LOG_INFO("Bluetooth select altsetting 0\r\n");
+#endif
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT);
+ USB_LOG_INFO("Register Bluetooth Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_bluetooth_run(bluetooth_class);
+ return ret;
+}
+
+static int usbh_bluetooth_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_bluetooth *bluetooth_class = (struct usbh_bluetooth *)hport->config.intf[intf].priv;
+
+ if (hport->config.config_desc.bNumInterfaces == (intf + 1)) {
+ return 0;
+ }
+
+ if (bluetooth_class) {
+ if (bluetooth_class->bulkin) {
+ usbh_kill_urb(&bluetooth_class->bulkin_urb);
+ }
+
+ if (bluetooth_class->bulkout) {
+ usbh_kill_urb(&bluetooth_class->bulkout_urb);
+ }
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ if (bluetooth_class->intin) {
+ usbh_kill_urb(&bluetooth_class->intin_urb);
+ }
+
+ // if (bluetooth_class->isoin) {
+ // usbh_kill_urb(&bluetooth_class->isoin_urb);
+ // }
+
+ // if (bluetooth_class->isoin) {
+ // usbh_kill_urb(&bluetooth_class->isoinin_urb);
+ // }
+#endif
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister Bluetooth Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_bluetooth_stop(bluetooth_class);
+ }
+
+ memset(bluetooth_class, 0, sizeof(struct usbh_bluetooth));
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_LOG
+static void usbh_bluetooth_hci_dump(uint8_t *data, uint32_t len)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0) {
+ USB_LOG_RAW("\r\n");
+ }
+
+ USB_LOG_RAW("%02x ", data[i]);
+ }
+
+ USB_LOG_RAW("\r\n");
+}
+#else
+#define usbh_bluetooth_hci_dump(data, len)
+#endif
+
+static int usbh_bluetooth_hci_bulk_out(uint8_t *buffer, uint32_t buflen)
+{
+ struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
+ struct usbh_urb *urb = &bluetooth_class->bulkout_urb;
+ int ret;
+
+ usbh_bulk_urb_fill(urb, bluetooth_class->hport, bluetooth_class->bulkout, buffer, buflen, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(urb);
+ if (ret == 0) {
+ ret = urb->actual_length;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen)
+{
+ int ret;
+
+ g_bluetooth_tx_buf[0] = hci_type;
+ memcpy(&g_bluetooth_tx_buf[1], buffer, buflen);
+ usbh_bluetooth_hci_dump(g_bluetooth_tx_buf, buflen + 1);
+ ret = usbh_bluetooth_hci_bulk_out(g_bluetooth_tx_buf, buflen + 1);
+ return ret;
+}
+
+void usbh_bluetooth_hci_rx_thread(void *argument)
+{
+ int ret;
+ uint32_t ep_mps;
+ uint8_t retry = 0;
+ uint16_t actual_len = 0;
+
+ ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.bulkin->wMaxPacketSize);
+
+ USB_LOG_INFO("Create hc rx thread\r\n");
+ while (1) {
+ usbh_bulk_urb_fill(&g_bluetooth_class.bulkin_urb, g_bluetooth_class.hport, g_bluetooth_class.bulkin, &g_bluetooth_rx_buf[actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_bluetooth_class.bulkin_urb);
+ if (ret < 0) {
+ if (ret == -USB_ERR_SHUTDOWN) {
+ goto delete;
+ } else {
+ retry++;
+ if (retry == 3) {
+ retry = 0;
+ goto delete;
+ }
+ continue;
+ }
+ }
+ actual_len += g_bluetooth_class.bulkin_urb.actual_length;
+ if (g_bluetooth_class.bulkin_urb.actual_length != ep_mps) {
+ usbh_bluetooth_hci_dump(g_bluetooth_rx_buf, actual_len);
+ usbh_bluetooth_hci_read_callback(g_bluetooth_rx_buf, actual_len);
+ actual_len = 0;
+ } else {
+ /* read continue util read short packet */
+ }
+ }
+ // clang-format off
+delete :
+ USB_LOG_INFO("Delete hc acl rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+#else
+static int usbh_bluetooth_hci_cmd(uint8_t *buffer, uint32_t buflen)
+{
+ struct usbh_bluetooth *bluetooth_class = &g_bluetooth_class;
+ struct usb_setup_packet *setup = bluetooth_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE;
+ setup->bRequest = 0x00;
+ setup->wValue = 0;
+ setup->wIndex = bluetooth_class->intf;
+ setup->wLength = buflen;
+
+ return usbh_control_transfer(bluetooth_class->hport, setup, buffer);
+}
+
+int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen)
+{
+ int ret;
+
+ if (hci_type == USB_BLUETOOTH_HCI_CMD) {
+ g_bluetooth_cmd_buf[0] = USB_BLUETOOTH_HCI_CMD;
+ memcpy(&g_bluetooth_cmd_buf[1], buffer, buflen);
+ usbh_bluetooth_hci_dump(g_bluetooth_cmd_buf, buflen + 1);
+ ret = usbh_bluetooth_hci_cmd(&g_bluetooth_cmd_buf[1], buflen);
+ } else if (hci_type == USB_BLUETOOTH_HCI_ACL) {
+ g_bluetooth_tx_buf[0] = USB_BLUETOOTH_HCI_ACL;
+ memcpy(&g_bluetooth_tx_buf[1], buffer, buflen);
+ usbh_bluetooth_hci_dump(g_bluetooth_tx_buf, buflen + 1);
+ ret = usbh_bluetooth_hci_bulk_out(&g_bluetooth_tx_buf[1], buflen);
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+void usbh_bluetooth_hci_evt_rx_thread(void *argument)
+{
+ int ret;
+ uint32_t ep_mps;
+ uint32_t interval;
+ uint8_t retry = 0;
+ uint16_t actual_len = 0;
+
+ ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.intin->wMaxPacketSize);
+ interval = g_bluetooth_class.intin->bInterval;
+
+ USB_LOG_INFO("Create hc event rx thread\r\n");
+ while (1) {
+ usbh_int_urb_fill(&g_bluetooth_class.intin_urb, g_bluetooth_class.hport, g_bluetooth_class.intin, &g_bluetooth_evt_buf[1 + actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_bluetooth_class.intin_urb);
+ if (ret < 0) {
+ if (ret == -USB_ERR_SHUTDOWN) {
+ goto delete;
+ } else if (ret == -USB_ERR_NAK) {
+ usb_osal_msleep(interval);
+ continue;
+ } else {
+ retry++;
+ if (retry == 3) {
+ retry = 0;
+ goto delete;
+ }
+ usb_osal_msleep(interval);
+ continue;
+ }
+ }
+ actual_len += g_bluetooth_class.intin_urb.actual_length;
+ if (g_bluetooth_class.intin_urb.actual_length != ep_mps) {
+ g_bluetooth_evt_buf[0] = USB_BLUETOOTH_HCI_EVT;
+ usbh_bluetooth_hci_dump(g_bluetooth_evt_buf, actual_len + 1);
+ usbh_bluetooth_hci_read_callback(g_bluetooth_evt_buf, actual_len + 1);
+ actual_len = 0;
+ } else {
+ /* read continue util read short packet */
+ }
+ usb_osal_msleep(interval);
+ }
+ // clang-format off
+delete :
+ USB_LOG_INFO("Delete hc event rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+void usbh_bluetooth_hci_acl_rx_thread(void *argument)
+{
+ int ret;
+ uint32_t ep_mps;
+ uint8_t retry = 0;
+ uint16_t actual_len = 0;
+
+ ep_mps = USB_GET_MAXPACKETSIZE(g_bluetooth_class.bulkin->wMaxPacketSize);
+
+ USB_LOG_INFO("Create hc acl rx thread\r\n");
+ while (1) {
+ usbh_bulk_urb_fill(&g_bluetooth_class.bulkin_urb, g_bluetooth_class.hport, g_bluetooth_class.bulkin, &g_bluetooth_rx_buf[1 + actual_len], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_bluetooth_class.bulkin_urb);
+ if (ret < 0) {
+ if (ret == -USB_ERR_SHUTDOWN) {
+ goto delete;
+ } else {
+ retry++;
+ if (retry == 3) {
+ retry = 0;
+ goto delete;
+ }
+ continue;
+ }
+ }
+ actual_len += g_bluetooth_class.bulkin_urb.actual_length;
+ if (g_bluetooth_class.bulkin_urb.actual_length != ep_mps) {
+ g_bluetooth_rx_buf[0] = USB_BLUETOOTH_HCI_ACL;
+ usbh_bluetooth_hci_dump(g_bluetooth_rx_buf, actual_len + 1);
+ usbh_bluetooth_hci_read_callback(g_bluetooth_rx_buf, actual_len + 1);
+ actual_len = 0;
+ } else {
+ /* read continue util read short packet */
+ }
+ }
+ // clang-format off
+delete :
+ USB_LOG_INFO("Delete hc acl rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+#endif
+
+__WEAK void usbh_bluetooth_hci_read_callback(uint8_t *data, uint32_t len)
+{
+}
+
+__WEAK void usbh_bluetooth_run(struct usbh_bluetooth *bluetooth_class)
+{
+}
+
+__WEAK void usbh_bluetooth_stop(struct usbh_bluetooth *bluetooth_class)
+{
+}
+
+static const struct usbh_class_driver bluetooth_class_driver = {
+ .driver_name = "bluetooth",
+ .connect = usbh_bluetooth_connect,
+ .disconnect = usbh_bluetooth_disconnect
+};
+
+#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+static const uint16_t bluetooth_id_table[][2] = {
+ { 0x2fe3, 0x000c },
+ { 0, 0 },
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_h4_nrf_class_info = {
+ .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
+ .class = 0xff,
+ .subclass = 0x00,
+ .protocol = 0x00,
+ .id_table = bluetooth_id_table,
+ .class_driver = &bluetooth_class_driver
+};
+#else
+CLASS_INFO_DEFINE const struct usbh_class_info bluetooth_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_WIRELESS,
+ .subclass = 0x01,
+ .protocol = 0x01,
+ .id_table = NULL,
+ .class_driver = &bluetooth_class_driver
+};
+#endif
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.h b/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.h
new file mode 100644
index 0000000000..868ff2c2cc
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbh_bluetooth.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_BLUETOOTH_H
+#define USBH_BLUETOOTH_H
+
+#define USB_BLUETOOTH_HCI_NONE 0x00
+#define USB_BLUETOOTH_HCI_CMD 0x01
+#define USB_BLUETOOTH_HCI_ACL 0x02
+#define USB_BLUETOOTH_HCI_SCO 0x03
+#define USB_BLUETOOTH_HCI_EVT 0x04
+#define USB_BLUETOOTH_HCI_ISO 0x05
+
+struct usbh_bluetooth {
+ struct usbh_hubport *hport;
+ uint8_t intf;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usbh_urb bulkin_urb; /* Bulk IN urb */
+ struct usbh_urb bulkout_urb; /* Bulk OUT urb */
+#ifndef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+ struct usb_endpoint_descriptor *intin; /* INTR endpoint */
+ struct usb_endpoint_descriptor *isoin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *isoout; /* Bulk OUT endpoint */
+ struct usbh_urb intin_urb; /* INTR IN urb */
+ struct usbh_urb *isoin_urb; /* Bulk IN urb */
+ struct usbh_urb *isoout_urb; /* Bulk OUT urb */
+ uint8_t num_of_intf_altsettings;
+#endif
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_bluetooth_hci_write(uint8_t hci_type, uint8_t *buffer, uint32_t buflen);
+void usbh_bluetooth_hci_read_callback(uint8_t *data, uint32_t len);
+#ifdef CONFIG_USBHOST_BLUETOOTH_HCI_H4
+void usbh_bluetooth_hci_rx_thread(void *argument);
+#else
+void usbh_bluetooth_hci_evt_rx_thread(void *argument);
+void usbh_bluetooth_hci_acl_rx_thread(void *argument);
+#endif
+
+void usbh_bluetooth_run(struct usbh_bluetooth *bluetooth_class);
+void usbh_bluetooth_stop(struct usbh_bluetooth *bluetooth_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_BLUETOOTH_H */
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c b/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c
new file mode 100644
index 0000000000..969e6e53a5
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_rndis.h"
+#include "rndis_protocol.h"
+
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbh_rndis"
+#include "usb_log.h"
+
+#define DEV_FORMAT "/dev/rndis"
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[4096];
+
+#define CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE 1514
+#define CONFIG_USBHOST_RNDIS_ETH_MSG_SIZE (CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE + 44)
+
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_rx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_tx_buffer[CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE];
+// static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_inttx_buffer[16];
+
+static struct usbh_rndis g_rndis_class;
+
+static int usbh_rndis_get_notification(struct usbh_rndis *rndis_class)
+{
+ // int ret;
+ // struct usbh_urb *urb = &rndis_class->intin_urb;
+
+ // usbh_int_urb_fill(urb, rndis_class->hport, rndis_class->intin, g_rndis_inttx_buffer, rndis_class->intin->wMaxPacketSize, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ // ret = usbh_submit_urb(urb);
+ // if (ret == 0) {
+ // ret = urb->actual_length;
+ // }
+ // return ret;
+ return 0;
+}
+
+static int usbh_rndis_init_msg_transfer(struct usbh_rndis *rndis_class)
+{
+ struct usb_setup_packet *setup = rndis_class->hport->setup;
+ int ret = 0;
+ rndis_initialize_msg_t *cmd;
+ rndis_initialize_cmplt_t *resp;
+
+ cmd = (rndis_initialize_msg_t *)g_rndis_buf;
+
+ cmd->MessageType = REMOTE_NDIS_INITIALIZE_MSG;
+ cmd->MessageLength = sizeof(rndis_initialize_msg_t);
+ cmd->RequestId = rndis_class->request_id++;
+ cmd->MajorVersion = 1;
+ cmd->MinorVersion = 0;
+ cmd->MaxTransferSize = 0x4000;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = sizeof(rndis_initialize_msg_t);
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
+ if (ret < 0) {
+ USB_LOG_ERR("rndis_initialize_msg_t send error, ret: %d\r\n", ret);
+ return ret;
+ }
+
+ usbh_rndis_get_notification(rndis_class);
+
+ resp = (rndis_initialize_cmplt_t *)g_rndis_buf;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 4096;
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
+ if (ret < 0) {
+ USB_LOG_ERR("rndis_initialize_cmplt_t recv error, ret: %d\r\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint32_t query_len, uint8_t *info, uint32_t *info_len)
+{
+ struct usb_setup_packet *setup = rndis_class->hport->setup;
+ int ret = 0;
+ rndis_query_msg_t *cmd;
+ rndis_query_cmplt_t *resp;
+
+ cmd = (rndis_query_msg_t *)g_rndis_buf;
+
+ cmd->MessageType = REMOTE_NDIS_QUERY_MSG;
+ cmd->MessageLength = query_len + sizeof(rndis_query_msg_t);
+ cmd->RequestId = rndis_class->request_id++;
+ cmd->Oid = oid;
+ cmd->InformationBufferLength = query_len;
+ cmd->InformationBufferOffset = 20;
+ cmd->DeviceVcHandle = 0;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = query_len + sizeof(rndis_query_msg_t);
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
+ if (ret < 0) {
+ USB_LOG_ERR("oid:%08x send error, ret: %d\r\n", (unsigned int)oid, ret);
+ return ret;
+ }
+
+ usbh_rndis_get_notification(rndis_class);
+
+ resp = (rndis_query_cmplt_t *)g_rndis_buf;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 4096;
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
+ if (ret < 0) {
+ USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
+ return ret;
+ }
+
+ memcpy(info, ((uint8_t *)resp + sizeof(rndis_query_cmplt_t)), resp->InformationBufferLength);
+ *info_len = resp->InformationBufferLength;
+
+ return ret;
+}
+
+static int usbh_rndis_set_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint8_t *info, uint32_t info_len)
+{
+ struct usb_setup_packet *setup = rndis_class->hport->setup;
+ int ret = 0;
+ rndis_set_msg_t *cmd;
+ rndis_set_cmplt_t *resp;
+
+ cmd = (rndis_set_msg_t *)g_rndis_buf;
+
+ cmd->MessageType = REMOTE_NDIS_SET_MSG;
+ cmd->MessageLength = info_len + sizeof(rndis_set_msg_t);
+ cmd->RequestId = rndis_class->request_id++;
+ cmd->Oid = oid;
+ cmd->InformationBufferLength = info_len;
+ cmd->InformationBufferOffset = 20;
+ cmd->DeviceVcHandle = 0;
+
+ memcpy(((uint8_t *)cmd + sizeof(rndis_set_msg_t)), info, info_len);
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = info_len + sizeof(rndis_set_msg_t);
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
+ if (ret < 0) {
+ USB_LOG_ERR("oid:%08x send error, ret: %d\r\n", (unsigned int)oid, ret);
+ return ret;
+ }
+
+ usbh_rndis_get_notification(rndis_class);
+
+ resp = (rndis_set_cmplt_t *)g_rndis_buf;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 4096;
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
+ if (ret < 0) {
+ USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int usbh_rndis_get_connect_status(struct usbh_rndis *rndis_class)
+{
+ int ret;
+ uint8_t data[32];
+ uint32_t data_len;
+
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
+ if (ret < 0) {
+ return ret;
+ }
+ if (NDIS_MEDIA_STATE_CONNECTED == data[0]) {
+ rndis_class->connect_status = true;
+ } else {
+ rndis_class->connect_status = false;
+ }
+ return 0;
+}
+
+int usbh_rndis_keepalive(struct usbh_rndis *rndis_class)
+{
+ struct usb_setup_packet *setup = rndis_class->hport->setup;
+ int ret = 0;
+ rndis_keepalive_msg_t *cmd;
+ rndis_keepalive_cmplt_t *resp;
+
+ cmd = (rndis_keepalive_msg_t *)g_rndis_buf;
+
+ cmd->MessageType = REMOTE_NDIS_KEEPALIVE_MSG;
+ cmd->MessageLength = sizeof(rndis_keepalive_msg_t);
+ cmd->RequestId = rndis_class->request_id++;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SEND_ENCAPSULATED_COMMAND;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = sizeof(rndis_keepalive_msg_t);
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
+ if (ret < 0) {
+ USB_LOG_ERR("keepalive send error, ret: %d\r\n", ret);
+ return ret;
+ }
+
+ usbh_rndis_get_notification(rndis_class);
+
+ resp = (rndis_keepalive_cmplt_t *)g_rndis_buf;
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = 4096;
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
+ if (ret < 0) {
+ USB_LOG_ERR("keepalive recv error, ret: %d\r\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ uint32_t *oid_support_list;
+ unsigned int oid = 0;
+ unsigned int oid_num = 0;
+ uint32_t data_len;
+ uint8_t tmp_buffer[512];
+ uint8_t data[32];
+
+ struct usbh_rndis *rndis_class = &g_rndis_class;
+
+ memset(rndis_class, 0, sizeof(struct usbh_rndis));
+
+ rndis_class->hport = hport;
+ rndis_class->ctrl_intf = intf;
+ rndis_class->data_intf = intf + 1;
+
+ hport->config.intf[intf].priv = rndis_class;
+ hport->config.intf[intf + 1].priv = NULL;
+
+ // ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ // USBH_EP_INIT(rndis_class->intin, ep_desc);
+
+ for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
+ ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
+
+ if (ep_desc->bEndpointAddress & 0x80) {
+ USBH_EP_INIT(rndis_class->bulkin, ep_desc);
+ } else {
+ USBH_EP_INIT(rndis_class->bulkout, ep_desc);
+ }
+ }
+
+ ret = usbh_rndis_init_msg_transfer(rndis_class);
+ if (ret < 0) {
+ return ret;
+ }
+ USB_LOG_INFO("rndis init success\r\n");
+
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_SUPPORTED_LIST, 0, tmp_buffer, &data_len);
+ if (ret < 0) {
+ return ret;
+ }
+ oid_num = (data_len / 4);
+ USB_LOG_INFO("rndis query OID_GEN_SUPPORTED_LIST success,oid num :%d\r\n", oid_num);
+
+ oid_support_list = (uint32_t *)tmp_buffer;
+
+ for (uint8_t i = 0; i < oid_num; i++) {
+ oid = oid_support_list[i];
+ switch (oid) {
+ case OID_GEN_PHYSICAL_MEDIUM:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_PHYSICAL_MEDIUM, 4, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+ break;
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MAXIMUM_FRAME_SIZE, 4, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+ break;
+ case OID_GEN_LINK_SPEED:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_LINK_SPEED, 4, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+
+ memcpy(&rndis_class->link_speed, data, 4);
+ break;
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+ if (NDIS_MEDIA_STATE_CONNECTED == data[0]) {
+ rndis_class->connect_status = true;
+ } else {
+ rndis_class->connect_status = false;
+ }
+ break;
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_MAXIMUM_LIST_SIZE, 4, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+ break;
+ case OID_802_3_CURRENT_ADDRESS:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_CURRENT_ADDRESS, 6, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+
+ for (uint8_t j = 0; j < 6; j++) {
+ rndis_class->mac[j] = data[j];
+ }
+ break;
+ case OID_802_3_PERMANENT_ADDRESS:
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_PERMANENT_ADDRESS, 6, data, &data_len);
+ if (ret < 0) {
+ goto query_errorout;
+ }
+ break;
+ default:
+ USB_LOG_WRN("Ignore rndis query iod:%08x\r\n", oid);
+ continue;
+ }
+ USB_LOG_INFO("rndis query iod:%08x success\r\n", oid);
+ }
+
+ uint32_t packet_filter = 0x0f;
+ usbh_rndis_set_msg_transfer(rndis_class, OID_GEN_CURRENT_PACKET_FILTER, (uint8_t *)&packet_filter, 4);
+ if (ret < 0) {
+ return ret;
+ }
+ USB_LOG_INFO("rndis set OID_GEN_CURRENT_PACKET_FILTER success\r\n");
+
+ uint8_t multicast_list[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x01 };
+ usbh_rndis_set_msg_transfer(rndis_class, OID_802_3_MULTICAST_LIST, multicast_list, 6);
+ if (ret < 0) {
+ return ret;
+ }
+ USB_LOG_INFO("rndis set OID_802_3_MULTICAST_LIST success\r\n");
+
+ USB_LOG_INFO("rndis MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
+ rndis_class->mac[0],
+ rndis_class->mac[1],
+ rndis_class->mac[2],
+ rndis_class->mac[3],
+ rndis_class->mac[4],
+ rndis_class->mac[5]);
+
+ memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
+
+ USB_LOG_INFO("Register RNDIS Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_rndis_run(rndis_class);
+ return ret;
+query_errorout:
+ USB_LOG_ERR("rndis query iod:%08x error\r\n", oid);
+ return ret;
+}
+
+static int usbh_rndis_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_rndis *rndis_class = (struct usbh_rndis *)hport->config.intf[intf].priv;
+
+ if (rndis_class) {
+ if (rndis_class->bulkin) {
+ usbh_kill_urb(&rndis_class->bulkin_urb);
+ }
+
+ if (rndis_class->bulkout) {
+ usbh_kill_urb(&rndis_class->bulkout_urb);
+ }
+
+ // if (rndis_class->intin) {
+ // usbh_kill_urb(&rndis_class->intin_urb);
+ // }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister RNDIS Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_rndis_stop(rndis_class);
+ }
+
+ memset(rndis_class, 0, sizeof(struct usbh_rndis));
+ }
+
+ return ret;
+}
+
+void usbh_rndis_rx_thread(void *argument)
+{
+ uint32_t g_rndis_rx_length;
+ int ret;
+ uint32_t pmg_offset;
+ rndis_data_packet_t *pmsg;
+ rndis_data_packet_t temp;
+
+ USB_LOG_INFO("Create rndis rx thread\r\n");
+ // clang-format off
+find_class:
+ // clang-format on
+ g_rndis_class.connect_status = false;
+ if (usbh_find_class_instance("/dev/rndis") == NULL) {
+ goto delete;
+ }
+
+ while (g_rndis_class.connect_status == false) {
+ ret = usbh_rndis_get_connect_status(&g_rndis_class);
+ if (ret < 0) {
+ usb_osal_msleep(100);
+ goto find_class;
+ }
+ }
+
+ g_rndis_rx_length = 0;
+ while (1) {
+ usbh_bulk_urb_fill(&g_rndis_class.bulkin_urb, g_rndis_class.hport, g_rndis_class.bulkin, g_rndis_rx_buffer, (CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE > (16 * 1024)) ? (16 * 1024) : CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ ret = usbh_submit_urb(&g_rndis_class.bulkin_urb);
+ if (ret < 0) {
+ goto find_class;
+ }
+
+ g_rndis_rx_length += g_rndis_class.bulkin_urb.actual_length;
+ if (g_rndis_rx_length % USB_GET_MAXPACKETSIZE(g_rndis_class.bulkin->wMaxPacketSize)) {
+ pmg_offset = 0;
+
+ uint32_t total_len = g_rndis_rx_length;
+
+ while (g_rndis_rx_length > 0) {
+ USB_LOG_DBG("rxlen:%d\r\n", g_rndis_rx_length);
+
+ pmsg = (rndis_data_packet_t *)(g_rndis_rx_buffer + pmg_offset);
+
+ /* Not word-aligned case */
+ if (pmg_offset & 0x3) {
+ memcpy(&temp, pmsg, sizeof(rndis_data_packet_t));
+ pmsg = &temp;
+ }
+
+ if (pmsg->MessageType == REMOTE_NDIS_PACKET_MSG) {
+ uint8_t *buf = (uint8_t *)(g_rndis_rx_buffer + pmg_offset + sizeof(rndis_generic_msg_t) + pmsg->DataOffset);
+
+ usbh_rndis_eth_input(buf, pmsg->DataLength);
+ pmg_offset += pmsg->MessageLength;
+ g_rndis_rx_length -= pmsg->MessageLength;
+
+ /* drop the last dummy byte, it is a short packet to tell us we have received a multiple of wMaxPacketSize */
+ if (g_rndis_rx_length < 4) {
+ g_rndis_rx_length = 0;
+ }
+ } else {
+ USB_LOG_ERR("offset:%d,remain:%d,total:%d\r\n", pmg_offset, g_rndis_rx_length, total_len);
+ g_rndis_rx_length = 0;
+ USB_LOG_ERR("Error rndis packet message\r\n");
+ }
+ }
+ } else {
+ if (g_rndis_rx_length > CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) {
+ USB_LOG_ERR("Rx packet is overflow\r\n");
+ g_rndis_rx_length = 0;
+ }
+ }
+ }
+
+ // clang-format off
+delete:
+ USB_LOG_INFO("Delete rndis rx thread\r\n");
+ usb_osal_thread_delete(NULL);
+ // clang-format on
+}
+
+int usbh_rndis_eth_output(uint8_t *buf, uint32_t buflen)
+{
+ uint8_t *buffer;
+ rndis_data_packet_t *hdr;
+ uint32_t len;
+
+ if (g_rndis_class.connect_status == false) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ hdr = (rndis_data_packet_t *)g_rndis_tx_buffer;
+ memset(hdr, 0, sizeof(rndis_data_packet_t));
+
+ hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
+ hdr->MessageLength = sizeof(rndis_data_packet_t) + buflen;
+ hdr->DataOffset = sizeof(rndis_data_packet_t) - sizeof(rndis_generic_msg_t);
+ hdr->DataLength = buflen;
+
+ buffer = (uint8_t *)(g_rndis_tx_buffer + sizeof(rndis_data_packet_t));
+ memcpy(buffer, buf, buflen);
+
+ len = hdr->MessageLength;
+ /* if message length is the multiple of wMaxPacketSize, we should add a short packet to tell device transfer is over. */
+ if (!(hdr->MessageLength % g_rndis_class.bulkout->wMaxPacketSize)) {
+ len += 1;
+ }
+
+ USB_LOG_DBG("txlen:%d\r\n", len);
+
+ usbh_bulk_urb_fill(&g_rndis_class.bulkout_urb, g_rndis_class.hport, g_rndis_class.bulkout, g_rndis_tx_buffer, len, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ return usbh_submit_urb(&g_rndis_class.bulkout_urb);
+}
+
+__WEAK void usbh_rndis_run(struct usbh_rndis *rndis_class)
+{
+}
+
+__WEAK void usbh_rndis_stop(struct usbh_rndis *rndis_class)
+{
+}
+
+static const struct usbh_class_driver rndis_class_driver = {
+ .driver_name = "rndis",
+ .connect = usbh_rndis_connect,
+ .disconnect = usbh_rndis_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info rndis_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .class = USB_DEVICE_CLASS_WIRELESS,
+ .subclass = 0x01,
+ .protocol = 0x03,
+ .id_table = NULL,
+ .class_driver = &rndis_class_driver
+};
diff --git a/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.h b/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.h
new file mode 100644
index 0000000000..be56e4004e
--- /dev/null
+++ b/components/drivers/usb/cherryusb/class/wireless/usbh_rndis.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_RNDIS_H
+#define USBH_RNDIS_H
+
+#include "usb_cdc.h"
+
+struct usbh_rndis {
+ struct usbh_hubport *hport;
+ struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */
+ struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */
+ struct usb_endpoint_descriptor *intin; /* INTR endpoint */
+ struct usbh_urb bulkin_urb; /* Bulk IN urb */
+ struct usbh_urb bulkout_urb; /* Bulk OUT urb */
+ struct usbh_urb intin_urb; /* INTR IN urb */
+
+ uint8_t ctrl_intf; /* Control interface number */
+ uint8_t data_intf; /* Data interface number */
+ uint8_t minor;
+
+ uint32_t request_id;
+
+ uint32_t link_speed;
+ bool connect_status;
+ uint8_t mac[6];
+
+ void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_rndis_get_connect_status(struct usbh_rndis *rndis_class);
+int usbh_rndis_keepalive(struct usbh_rndis *rndis_class);
+
+void usbh_rndis_run(struct usbh_rndis *rndis_class);
+void usbh_rndis_stop(struct usbh_rndis *rndis_class);
+
+int usbh_rndis_eth_output(uint8_t *buf, uint32_t buflen);
+void usbh_rndis_eth_input(uint8_t *buf, uint32_t buflen);
+void usbh_rndis_rx_thread(void *argument);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_RNDIS_H */