diff options
| author | sakumisu <[email protected]> | 2024-06-02 11:20:13 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-06-02 11:20:13 +0800 |
| commit | 62d321caa028dee04bf0b43fc0abb68b40e80ddd (patch) | |
| tree | 31fa88156af25b434a194398154dc8027d6b413d /components/drivers/usb/cherryusb/class | |
| parent | 658813213e5ad1dc81739746f6001099c6cc9e41 (diff) | |
refactor(drivers/usb):replace rtt usb stack with cherryusb (#8799)
Diffstat (limited to 'components/drivers/usb/cherryusb/class')
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 */ |
