diff options
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/audio/audio.h | 200 | ||||
| -rw-r--r-- | src/class/cdc/cdc.h | 810 | ||||
| -rw-r--r-- | src/class/cdc/cdc_device.c | 839 | ||||
| -rw-r--r-- | src/class/cdc/cdc_device.h | 426 | ||||
| -rw-r--r-- | src/class/cdc/cdc_host.c | 450 | ||||
| -rw-r--r-- | src/class/cdc/cdc_host.h | 246 | ||||
| -rw-r--r-- | src/class/hid/hid.h | 1746 | ||||
| -rw-r--r-- | src/class/hid/hid_device.h | 630 | ||||
| -rw-r--r-- | src/class/hid/hid_host.c | 578 | ||||
| -rw-r--r-- | src/class/hid/hid_host.h | 430 | ||||
| -rw-r--r-- | src/class/midi/midi.h | 334 | ||||
| -rw-r--r-- | src/class/midi/midi_device.c | 722 | ||||
| -rw-r--r-- | src/class/midi/midi_device.h | 276 | ||||
| -rw-r--r-- | src/class/msc/msc.h | 784 | ||||
| -rw-r--r-- | src/class/msc/msc_device.c | 1400 | ||||
| -rw-r--r-- | src/class/msc/msc_device.h | 330 | ||||
| -rw-r--r-- | src/class/msc/msc_host.c | 826 | ||||
| -rw-r--r-- | src/class/msc/msc_host.h | 412 | ||||
| -rw-r--r-- | src/class/net/net_device.c | 345 | ||||
| -rw-r--r-- | src/class/net/net_device.h | 86 |
20 files changed, 6155 insertions, 5715 deletions
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index 83b8906d2..5bec14d88 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -1,100 +1,100 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \ingroup group_class
- * \defgroup ClassDriver_Audio Audio
- * Currently only MIDI subclass is supported
- * @{ */
-
-#ifndef _TUSB_AUDIO_H__
-#define _TUSB_AUDIO_H__
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/// Audio Interface Subclass Codes
-typedef enum
-{
- AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control
- AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming
- AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming
-} audio_subclass_type_t;
-
-/// Audio Protocol Codes
-typedef enum
-{
- AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0
- AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0
- AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0
-} audio_protocol_type_t;
-
-/// Audio Function Category Codes
-typedef enum
-{
- AUDIO_FUNC_DESKTOP_SPEAKER = 0x01,
- AUDIO_FUNC_HOME_THEATER = 0x02,
- AUDIO_FUNC_MICROPHONE = 0x03,
- AUDIO_FUNC_HEADSET = 0x04,
- AUDIO_FUNC_TELEPHONE = 0x05,
- AUDIO_FUNC_CONVERTER = 0x06,
- AUDIO_FUNC_SOUND_RECODER = 0x07,
- AUDIO_FUNC_IO_BOX = 0x08,
- AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09,
- AUDIO_FUNC_PRO_AUDIO = 0x0A,
- AUDIO_FUNC_AUDIO_VIDEO = 0x0B,
- AUDIO_FUNC_CONTROL_PANEL = 0x0C
-} audio_function_t;
-
-/// Audio Class-Specific AC Interface Descriptor Subtypes
-typedef enum
-{
- AUDIO_CS_INTERFACE_HEADER = 0x01,
- AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02,
- AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03,
- AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04,
- AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05,
- AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06,
- AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07,
- AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08,
- AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09,
- AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A,
- AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B,
- AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C,
- AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D,
-} audio_cs_interface_subtype_t;
-
-/** @} */
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif
-
-/** @} */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_Audio Audio + * Currently only MIDI subclass is supported + * @{ */ + +#ifndef _TUSB_AUDIO_H__ +#define _TUSB_AUDIO_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/// Audio Interface Subclass Codes +typedef enum +{ + AUDIO_SUBCLASS_CONTROL = 0x01 , ///< Audio Control + AUDIO_SUBCLASS_STREAMING , ///< Audio Streaming + AUDIO_SUBCLASS_MIDI_STREAMING , ///< MIDI Streaming +} audio_subclass_type_t; + +/// Audio Protocol Codes +typedef enum +{ + AUDIO_PROTOCOL_V1 = 0x00, ///< Version 1.0 + AUDIO_PROTOCOL_V2 = 0x20, ///< Version 2.0 + AUDIO_PROTOCOL_V3 = 0x30, ///< Version 3.0 +} audio_protocol_type_t; + +/// Audio Function Category Codes +typedef enum +{ + AUDIO_FUNC_DESKTOP_SPEAKER = 0x01, + AUDIO_FUNC_HOME_THEATER = 0x02, + AUDIO_FUNC_MICROPHONE = 0x03, + AUDIO_FUNC_HEADSET = 0x04, + AUDIO_FUNC_TELEPHONE = 0x05, + AUDIO_FUNC_CONVERTER = 0x06, + AUDIO_FUNC_SOUND_RECODER = 0x07, + AUDIO_FUNC_IO_BOX = 0x08, + AUDIO_FUNC_MUSICAL_INSTRUMENT = 0x09, + AUDIO_FUNC_PRO_AUDIO = 0x0A, + AUDIO_FUNC_AUDIO_VIDEO = 0x0B, + AUDIO_FUNC_CONTROL_PANEL = 0x0C +} audio_function_t; + +/// Audio Class-Specific AC Interface Descriptor Subtypes +typedef enum +{ + AUDIO_CS_INTERFACE_HEADER = 0x01, + AUDIO_CS_INTERFACE_INPUT_TERMINAL = 0x02, + AUDIO_CS_INTERFACE_OUTPUT_TERMINAL = 0x03, + AUDIO_CS_INTERFACE_MIXER_UNIT = 0x04, + AUDIO_CS_INTERFACE_SELECTOR_UNIT = 0x05, + AUDIO_CS_INTERFACE_FEATURE_UNIT = 0x06, + AUDIO_CS_INTERFACE_EFFECT_UNIT = 0x07, + AUDIO_CS_INTERFACE_PROCESSING_UNIT = 0x08, + AUDIO_CS_INTERFACE_EXTENSION_UNIT = 0x09, + AUDIO_CS_INTERFACE_CLOCK_SOURCE = 0x0A, + AUDIO_CS_INTERFACE_CLOCK_SELECTOR = 0x0B, + AUDIO_CS_INTERFACE_CLOCK_MULTIPLIER = 0x0C, + AUDIO_CS_INTERFACE_SAMPLE_RATE_CONVERTER = 0x0D, +} audio_cs_interface_subtype_t; + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index 9cc8b82e3..f0488acbb 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -1,405 +1,405 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \ingroup group_class
- * \defgroup ClassDriver_CDC Communication Device Class (CDC)
- * Currently only Abstract Control Model subclass is supported
- * @{ */
-
-#ifndef _TUSB_CDC_H__
-#define _TUSB_CDC_H__
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/** \defgroup ClassDriver_CDC_Common Common Definitions
- * @{ */
-
-// TODO remove
-/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In)
-typedef enum
-{
- CDC_PIPE_NOTIFICATION , ///< Notification pipe
- CDC_PIPE_DATA_IN , ///< Data in pipe
- CDC_PIPE_DATA_OUT , ///< Data out pipe
- CDC_PIPE_ERROR , ///< Invalid Pipe ID
-}cdc_pipeid_t;
-
-//--------------------------------------------------------------------+
-// CDC Communication Interface Class
-//--------------------------------------------------------------------+
-
-/// Communication Interface Subclass Codes
-typedef enum
-{
- CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2]
- CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2]
- CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2]
- CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2]
- CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1]
- CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1]
- CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1]
- CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1]
- CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0]
-} cdc_comm_sublcass_type_t;
-
-/// Communication Interface Protocol Codes
-typedef enum
-{
- CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol
- CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc
- CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101
- CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O
- CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07
- CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007
- CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA
- CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model
-} cdc_comm_protocol_type_t;
-
-//------------- SubType Descriptor in COMM Functional Descriptor -------------//
-/// Communication Interface SubType Descriptor
-typedef enum
-{
- CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface.
- CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor.
- CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor.
- CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor.
- CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor.
- CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor.
- CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor
- CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor
- CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor
- CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor
- CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor
- CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor
- CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor
- CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor
- CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor
- CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor
- CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor
- CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor
- CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor
- CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor
- CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor
- CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor
- CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor
- CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor
- CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor
- CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor
-}cdc_func_desc_type_t;
-
-//--------------------------------------------------------------------+
-// CDC Data Interface Class
-//--------------------------------------------------------------------+
-
-// SUBCLASS code of Data Interface is not used and should/must be zero
-/// Data Interface Protocol Codes
-typedef enum{
- CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI
- CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC
- CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent
- CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol
- CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931
- CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol
- CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures
- CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control
- CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN
- CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands
- CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack.
- CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface
-}cdc_data_protocol_type_t;
-
-//--------------------------------------------------------------------+
-// Management Element Request (Control Endpoint)
-//--------------------------------------------------------------------+
-
-/// Communication Interface Management Element Request Codes
-typedef enum
-{
- CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
- CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
-
- CDC_REQUEST_SET_COMM_FEATURE = 0x02,
- CDC_REQUEST_GET_COMM_FEATURE = 0x03,
- CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04,
-
- CDC_REQUEST_SET_AUX_LINE_STATE = 0x10,
- CDC_REQUEST_SET_HOOK_STATE = 0x11,
- CDC_REQUEST_PULSE_SETUP = 0x12,
- CDC_REQUEST_SEND_PULSE = 0x13,
- CDC_REQUEST_SET_PULSE_TIME = 0x14,
- CDC_REQUEST_RING_AUX_JACK = 0x15,
-
- CDC_REQUEST_SET_LINE_CODING = 0x20,
- CDC_REQUEST_GET_LINE_CODING = 0x21,
- CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22,
- CDC_REQUEST_SEND_BREAK = 0x23,
-
- CDC_REQUEST_SET_RINGER_PARMS = 0x30,
- CDC_REQUEST_GET_RINGER_PARMS = 0x31,
- CDC_REQUEST_SET_OPERATION_PARMS = 0x32,
- CDC_REQUEST_GET_OPERATION_PARMS = 0x33,
- CDC_REQUEST_SET_LINE_PARMS = 0x34,
- CDC_REQUEST_GET_LINE_PARMS = 0x35,
- CDC_REQUEST_DIAL_DIGITS = 0x36,
- CDC_REQUEST_SET_UNIT_PARAMETER = 0x37,
- CDC_REQUEST_GET_UNIT_PARAMETER = 0x38,
- CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39,
- CDC_REQUEST_GET_PROFILE = 0x3A,
-
- CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40,
- CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
- CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
- CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43,
- CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44,
-
- CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50,
- CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51,
- CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52,
- CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
-
- CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
-}cdc_management_request_t;
-
-//--------------------------------------------------------------------+
-// Management Elemenent Notification (Notification Endpoint)
-//--------------------------------------------------------------------+
-
-/// Communication Interface Management Element Notification Codes
-typedef enum
-{
- NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
- RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
-
- AUX_JACK_HOOK_STATE = 0x08,
- RING_DETECT = 0x09,
-
- SERIAL_STATE = 0x20,
-
- CALL_STATE_CHANGE = 0x28,
- LINE_STATE_CHANGE = 0x29,
- CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
- MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40,
-}cdc_notification_request_t;
-
-//--------------------------------------------------------------------+
-// Class Specific Functional Descriptor (Communication Interface)
-//--------------------------------------------------------------------+
-
-/// Header Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_
- uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal
-}cdc_desc_func_header_t;
-
-/// Union Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- uint8_t bControlInterface ; ///< Interface number of Communication Interface
- uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface
-}cdc_desc_func_union_t;
-
-#define cdc_desc_func_union_n_t(no_slave)\
- struct TU_ATTR_PACKED { \
- uint8_t bLength ;\
- uint8_t bDescriptorType ;\
- uint8_t bDescriptorSubType ;\
- uint8_t bControlInterface ;\
- uint8_t bSubordinateInterface[no_slave] ;\
-}
-
-/// Country Selection Functional Descriptor (Communication Interface)
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes.
- uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country.
-}cdc_desc_func_country_selection_t;
-
-#define cdc_desc_func_country_selection_n_t(no_country) \
- struct TU_ATTR_PACKED {\
- uint8_t bLength ;\
- uint8_t bDescriptorType ;\
- uint8_t bDescriptorSubType ;\
- uint8_t iCountryCodeRelDate ;\
- uint16_t wCountryCode[no_country] ;\
-}
-
-//--------------------------------------------------------------------+
-// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS
-//--------------------------------------------------------------------+
-
-/// \brief Call Management Functional Descriptor
-/// \details This functional descriptor describes the processing of calls for the Communications Class interface.
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
-
- struct {
- uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface.
- uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself.
- uint8_t : 0;
- } bmCapabilities;
-
- uint8_t bDataInterface;
-}cdc_desc_func_call_management_t;
-
-
-typedef struct TU_ATTR_PACKED
-{
- uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature.
- uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State.
- uint8_t support_send_break : 1; ///< Device supports the request Send_Break
- uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection.
- uint8_t : 0;
-}cdc_acm_capability_t;
-
-TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler");
-
-/// \brief Abstract Control Management Functional Descriptor
-/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- cdc_acm_capability_t bmCapabilities ;
-}cdc_desc_func_acm_t;
-
-/// \brief Direct Line Management Functional Descriptor
-/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- struct {
- uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit.
- uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State.
- uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time.
- uint8_t : 0;
- } bmCapabilities;
-}cdc_desc_func_direct_line_management_t;
-
-/// \brief Telephone Ringer Functional Descriptor
-/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface,
-/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- uint8_t bRingerVolSteps ;
- uint8_t bNumRingerPatterns ;
-}cdc_desc_func_telephone_ringer_t;
-
-/// \brief Telephone Operational Modes Functional Descriptor
-/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by
-/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- struct {
- uint8_t simple_mode : 1;
- uint8_t standalone_mode : 1;
- uint8_t computer_centric_mode : 1;
- uint8_t : 0;
- } bmCapabilities;
-}cdc_desc_func_telephone_operational_modes_t;
-
-/// \brief Telephone Call and Line State Reporting Capabilities Descriptor
-/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a
-/// telephone device to report optional call and line states.
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_
- struct {
- uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone
- uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states.
- uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information.
- uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns.
- uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line.
- uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification
- uint32_t : 0;
- } bmCapabilities;
-}cdc_desc_func_telephone_call_state_reporting_capabilities_t;
-
-static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc)
-{
- return p_desc[2];
-}
-
-//--------------------------------------------------------------------+
-// Requests
-//--------------------------------------------------------------------+
-typedef struct TU_ATTR_PACKED
-{
- uint32_t bit_rate;
- uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
- uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
- uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
-} cdc_line_coding_t;
-
-TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct");
-
-typedef struct TU_ATTR_PACKED
-{
- uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR.
- uint16_t half_duplex_carrier_control : 1;
- uint16_t : 14;
-} cdc_line_control_state_t;
-
-TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");
-
-/** @} */
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif
-
-/** @} */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CDC Communication Device Class (CDC) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_CDC_H__ +#define _TUSB_CDC_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** \defgroup ClassDriver_CDC_Common Common Definitions + * @{ */ + +// TODO remove +/// CDC Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In) +typedef enum +{ + CDC_PIPE_NOTIFICATION , ///< Notification pipe + CDC_PIPE_DATA_IN , ///< Data in pipe + CDC_PIPE_DATA_OUT , ///< Data out pipe + CDC_PIPE_ERROR , ///< Invalid Pipe ID +}cdc_pipeid_t; + +//--------------------------------------------------------------------+ +// CDC Communication Interface Class +//--------------------------------------------------------------------+ + +/// Communication Interface Subclass Codes +typedef enum +{ + CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2] + CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2] + CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2] + CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2] + CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1] + CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1] + CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1] + CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1] + CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0] +} cdc_comm_sublcass_type_t; + +/// Communication Interface Protocol Codes +typedef enum +{ + CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol + CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101 + CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O + CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07 + CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007 + CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA + CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model +} cdc_comm_protocol_type_t; + +//------------- SubType Descriptor in COMM Functional Descriptor -------------// +/// Communication Interface SubType Descriptor +typedef enum +{ + CDC_FUNC_DESC_HEADER = 0x00 , ///< Header Functional Descriptor, which marks the beginning of the concatenated set of functional descriptors for the interface. + CDC_FUNC_DESC_CALL_MANAGEMENT = 0x01 , ///< Call Management Functional Descriptor. + CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 , ///< Abstract Control Management Functional Descriptor. + CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT = 0x03 , ///< Direct Line Management Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_RINGER = 0x04 , ///< Telephone Ringer Functional Descriptor. + CDC_FUNC_DESC_TELEPHONE_CALL_AND_LINE_STATE_REPORTING_CAPACITY = 0x05 , ///< Telephone Call and Line State Reporting Capabilities Functional Descriptor. + CDC_FUNC_DESC_UNION = 0x06 , ///< Union Functional Descriptor + CDC_FUNC_DESC_COUNTRY_SELECTION = 0x07 , ///< Country Selection Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES = 0x08 , ///< Telephone Operational ModesFunctional Descriptor + CDC_FUNC_DESC_USB_TERMINAL = 0x09 , ///< USB Terminal Functional Descriptor + CDC_FUNC_DESC_NETWORK_CHANNEL_TERMINAL = 0x0A , ///< Network Channel Terminal Descriptor + CDC_FUNC_DESC_PROTOCOL_UNIT = 0x0B , ///< Protocol Unit Functional Descriptor + CDC_FUNC_DESC_EXTENSION_UNIT = 0x0C , ///< Extension Unit Functional Descriptor + CDC_FUNC_DESC_MULTICHANEL_MANAGEMENT = 0x0D , ///< Multi-Channel Management Functional Descriptor + CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT = 0x0E , ///< CAPI Control Management Functional Descriptor + CDC_FUNC_DESC_ETHERNET_NETWORKING = 0x0F , ///< Ethernet Networking Functional Descriptor + CDC_FUNC_DESC_ATM_NETWORKING = 0x10 , ///< ATM Networking Functional Descriptor + CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL = 0x11 , ///< Wireless Handset Control Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL = 0x12 , ///< Mobile Direct Line Model Functional Descriptor + CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL = 0x13 , ///< MDLM Detail Functional Descriptor + CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL = 0x14 , ///< Device Management Model Functional Descriptor + CDC_FUNC_DESC_OBEX = 0x15 , ///< OBEX Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor + CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor + CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor + CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor +}cdc_func_desc_type_t; + +//--------------------------------------------------------------------+ +// CDC Data Interface Class +//--------------------------------------------------------------------+ + +// SUBCLASS code of Data Interface is not used and should/must be zero +/// Data Interface Protocol Codes +typedef enum{ + CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI + CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC + CDC_DATA_PROTOCOL_TRANSPARENT = 0x32, ///< Transparent + CDC_DATA_PROTOCOL_Q921_MANAGEMENT = 0x50, ///< Management protocol for Q.921 data link protocol + CDC_DATA_PROTOCOL_Q921_DATA_LINK = 0x51, ///< Data link protocol for Q.931 + CDC_DATA_PROTOCOL_Q921_TEI_MULTIPLEXOR = 0x52, ///< TEI-multiplexor for Q.921 data link protocol + CDC_DATA_PROTOCOL_V42BIS_DATA_COMPRESSION = 0x90, ///< Data compression procedures + CDC_DATA_PROTOCOL_EURO_ISDN = 0x91, ///< Euro-ISDN protocol control + CDC_DATA_PROTOCOL_V24_RATE_ADAPTION_TO_ISDN = 0x92, ///< V.24 rate adaptation to ISDN + CDC_DATA_PROTOCOL_CAPI_COMMAND = 0x93, ///< CAPI Commands + CDC_DATA_PROTOCOL_HOST_BASED_DRIVER = 0xFD, ///< Host based driver. Note: This protocol code should only be used in messages between host and device to identify the host driver portion of a protocol stack. + CDC_DATA_PROTOCOL_IN_PROTOCOL_UNIT_FUNCTIONAL_DESCRIPTOR = 0xFE ///< The protocol(s) are described using a ProtocolUnit Functional Descriptors on Communications Class Interface +}cdc_data_protocol_type_t; + +//--------------------------------------------------------------------+ +// Management Element Request (Control Endpoint) +//--------------------------------------------------------------------+ + +/// Communication Interface Management Element Request Codes +typedef enum +{ + CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface + CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface. + + CDC_REQUEST_SET_COMM_FEATURE = 0x02, + CDC_REQUEST_GET_COMM_FEATURE = 0x03, + CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04, + + CDC_REQUEST_SET_AUX_LINE_STATE = 0x10, + CDC_REQUEST_SET_HOOK_STATE = 0x11, + CDC_REQUEST_PULSE_SETUP = 0x12, + CDC_REQUEST_SEND_PULSE = 0x13, + CDC_REQUEST_SET_PULSE_TIME = 0x14, + CDC_REQUEST_RING_AUX_JACK = 0x15, + + CDC_REQUEST_SET_LINE_CODING = 0x20, + CDC_REQUEST_GET_LINE_CODING = 0x21, + CDC_REQUEST_SET_CONTROL_LINE_STATE = 0x22, + CDC_REQUEST_SEND_BREAK = 0x23, + + CDC_REQUEST_SET_RINGER_PARMS = 0x30, + CDC_REQUEST_GET_RINGER_PARMS = 0x31, + CDC_REQUEST_SET_OPERATION_PARMS = 0x32, + CDC_REQUEST_GET_OPERATION_PARMS = 0x33, + CDC_REQUEST_SET_LINE_PARMS = 0x34, + CDC_REQUEST_GET_LINE_PARMS = 0x35, + CDC_REQUEST_DIAL_DIGITS = 0x36, + CDC_REQUEST_SET_UNIT_PARAMETER = 0x37, + CDC_REQUEST_GET_UNIT_PARAMETER = 0x38, + CDC_REQUEST_CLEAR_UNIT_PARAMETER = 0x39, + CDC_REQUEST_GET_PROFILE = 0x3A, + + CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS = 0x40, + CDC_REQUEST_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, + CDC_REQUEST_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, + CDC_REQUEST_SET_ETHERNET_PACKET_FILTER = 0x43, + CDC_REQUEST_GET_ETHERNET_STATISTIC = 0x44, + + CDC_REQUEST_SET_ATM_DATA_FORMAT = 0x50, + CDC_REQUEST_GET_ATM_DEVICE_STATISTICS = 0x51, + CDC_REQUEST_SET_ATM_DEFAULT_VC = 0x52, + CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53, + + CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60, +}cdc_management_request_t; + +//--------------------------------------------------------------------+ +// Management Elemenent Notification (Notification Endpoint) +//--------------------------------------------------------------------+ + +/// Communication Interface Management Element Notification Codes +typedef enum +{ + NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status. + RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request. + + AUX_JACK_HOOK_STATE = 0x08, + RING_DETECT = 0x09, + + SERIAL_STATE = 0x20, + + CALL_STATE_CHANGE = 0x28, + LINE_STATE_CHANGE = 0x29, + CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred + MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40, +}cdc_notification_request_t; + +//--------------------------------------------------------------------+ +// Class Specific Functional Descriptor (Communication Interface) +//--------------------------------------------------------------------+ + +/// Header Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUNC_DESC_ + uint16_t bcdCDC ; ///< CDC release number in Binary-Coded Decimal +}cdc_desc_func_header_t; + +/// Union Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bControlInterface ; ///< Interface number of Communication Interface + uint8_t bSubordinateInterface ; ///< Array of Interface number of Data Interface +}cdc_desc_func_union_t; + +#define cdc_desc_func_union_n_t(no_slave)\ + struct TU_ATTR_PACKED { \ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t bControlInterface ;\ + uint8_t bSubordinateInterface[no_slave] ;\ +} + +/// Country Selection Functional Descriptor (Communication Interface) +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t iCountryCodeRelDate ; ///< Index of a string giving the release date for the implemented ISO 3166 Country Codes. + uint16_t wCountryCode ; ///< Country code in the format as defined in [ISO3166], release date as specified inoffset 3 for the first supported country. +}cdc_desc_func_country_selection_t; + +#define cdc_desc_func_country_selection_n_t(no_country) \ + struct TU_ATTR_PACKED {\ + uint8_t bLength ;\ + uint8_t bDescriptorType ;\ + uint8_t bDescriptorSubType ;\ + uint8_t iCountryCodeRelDate ;\ + uint16_t wCountryCode[no_country] ;\ +} + +//--------------------------------------------------------------------+ +// PUBLIC SWITCHED TELEPHONE NETWORK (PSTN) SUBCLASS +//--------------------------------------------------------------------+ + +/// \brief Call Management Functional Descriptor +/// \details This functional descriptor describes the processing of calls for the Communications Class interface. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + + struct { + uint8_t handle_call : 1; ///< 0 - Device sends/receives call management information only over the Communications Class interface. 1 - Device can send/receive call management information over a Data Class interface. + uint8_t send_recv_call : 1; ///< 0 - Device does not handle call management itself. 1 - Device handles call management itself. + uint8_t : 0; + } bmCapabilities; + + uint8_t bDataInterface; +}cdc_desc_func_call_management_t; + + +typedef struct TU_ATTR_PACKED +{ + uint8_t support_comm_request : 1; ///< Device supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature. + uint8_t support_line_request : 1; ///< Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State. + uint8_t support_send_break : 1; ///< Device supports the request Send_Break + uint8_t support_notification_network_connection : 1; ///< Device supports the notification Network_Connection. + uint8_t : 0; +}cdc_acm_capability_t; + +TU_VERIFY_STATIC(sizeof(cdc_acm_capability_t) == 1, "mostly problem with compiler"); + +/// \brief Abstract Control Management Functional Descriptor +/// \details This functional descriptor describes the commands supported by by the Communications Class interface with SubClass code of \ref CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + cdc_acm_capability_t bmCapabilities ; +}cdc_desc_func_acm_t; + +/// \brief Direct Line Management Functional Descriptor +/// \details This functional descriptor describes the commands supported by the Communications Class interface with SubClass code of \ref CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t require_pulse_setup : 1; ///< Device requires extra Pulse_Setup request during pulse dialing sequence to disengage holding circuit. + uint8_t support_aux_request : 1; ///< Device supports the request combination of Set_Aux_Line_State, Ring_Aux_Jack, and notification Aux_Jack_Hook_State. + uint8_t support_pulse_request : 1; ///< Device supports the request combination of Pulse_Setup, Send_Pulse, and Set_Pulse_Time. + uint8_t : 0; + } bmCapabilities; +}cdc_desc_func_direct_line_management_t; + +/// \brief Telephone Ringer Functional Descriptor +/// \details The Telephone Ringer functional descriptor describes the ringer capabilities supported by the Communications Class interface, +/// with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + uint8_t bRingerVolSteps ; + uint8_t bNumRingerPatterns ; +}cdc_desc_func_telephone_ringer_t; + +/// \brief Telephone Operational Modes Functional Descriptor +/// \details The Telephone Operational Modes functional descriptor describes the operational modes supported by +/// the Communications Class interface, with the SubClass code of \ref CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint8_t simple_mode : 1; + uint8_t standalone_mode : 1; + uint8_t computer_centric_mode : 1; + uint8_t : 0; + } bmCapabilities; +}cdc_desc_func_telephone_operational_modes_t; + +/// \brief Telephone Call and Line State Reporting Capabilities Descriptor +/// \details The Telephone Call and Line State Reporting Capabilities functional descriptor describes the abilities of a +/// telephone device to report optional call and line states. +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType one of above CDC_FUCN_DESC_ + struct { + uint32_t interrupted_dialtone : 1; ///< 0 : Reports only dialtone (does not differentiate between normal and interrupted dialtone). 1 : Reports interrupted dialtone in addition to normal dialtone + uint32_t ringback_busy_fastbusy : 1; ///< 0 : Reports only dialing state. 1 : Reports ringback, busy, and fast busy states. + uint32_t caller_id : 1; ///< 0 : Does not report caller ID. 1 : Reports caller ID information. + uint32_t incoming_distinctive : 1; ///< 0 : Reports only incoming ringing. 1 : Reports incoming distinctive ringing patterns. + uint32_t dual_tone_multi_freq : 1; ///< 0 : Cannot report dual tone multi-frequency (DTMF) digits input remotely over the telephone line. 1 : Can report DTMF digits input remotely over the telephone line. + uint32_t line_state_change : 1; ///< 0 : Does not support line state change notification. 1 : Does support line state change notification + uint32_t : 0; + } bmCapabilities; +}cdc_desc_func_telephone_call_state_reporting_capabilities_t; + +static inline uint8_t cdc_functional_desc_typeof(uint8_t const * p_desc) +{ + return p_desc[2]; +} + +//--------------------------------------------------------------------+ +// Requests +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + uint32_t bit_rate; + uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits + uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space + uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16 +} cdc_line_coding_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint16_t dte_is_present : 1; ///< Indicates to DCE if DTE is presentor not. This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR. + uint16_t half_duplex_carrier_control : 1; + uint16_t : 14; +} cdc_line_control_state_t; + +TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 015c1c6fd..73eafab07 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -1,415 +1,424 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC)
-
-#include "cdc_device.h"
-#include "device/usbd_pvt.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-typedef struct
-{
- uint8_t itf_num;
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
-
- // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
- uint8_t line_state;
-
- /*------------- From this point, data is not cleared by bus reset -------------*/
- char wanted_char;
- cdc_line_coding_t line_coding;
-
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
-
- uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE];
-
-#if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
-#endif
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE];
-
-}cdcd_interface_t;
-
-#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
-
-static void _prep_out_transaction (uint8_t itf)
-{
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
-
- // skip if previous transfer not complete
- if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
-
- // Prepare for incoming data but only allow what we can store in the ring buffer.
- uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
- if ( max_read >= TU_ARRAY_SIZE(p_cdc->epout_buf) )
- {
- usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, TU_ARRAY_SIZE(p_cdc->epout_buf));
- }
-}
-
-//--------------------------------------------------------------------+
-// APPLICATION API
-//--------------------------------------------------------------------+
-bool tud_cdc_n_connected(uint8_t itf)
-{
- // DTR (bit 0) active is considered as connected
- return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
-}
-
-uint8_t tud_cdc_n_get_line_state (uint8_t itf)
-{
- return _cdcd_itf[itf].line_state;
-}
-
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
-{
- (*coding) = _cdcd_itf[itf].line_coding;
-}
-
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
-{
- _cdcd_itf[itf].wanted_char = wanted;
-}
-
-
-//--------------------------------------------------------------------+
-// READ API
-//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_available(uint8_t itf)
-{
- return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
-}
-
-uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
-{
- uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize);
- _prep_out_transaction(itf);
- return num_read;
-}
-
-bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
-{
- return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr);
-}
-
-void tud_cdc_n_read_flush (uint8_t itf)
-{
- tu_fifo_clear(&_cdcd_itf[itf].rx_ff);
- _prep_out_transaction(itf);
-}
-
-//--------------------------------------------------------------------+
-// WRITE API
-//--------------------------------------------------------------------+
-uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
-{
- uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize);
-
-#if 0 // TODO issue with circuitpython's REPL
- // flush if queue more than endpoint size
- if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE )
- {
- tud_cdc_n_write_flush(itf);
- }
-#endif
-
- return ret;
-}
-
-bool tud_cdc_n_write_flush (uint8_t itf)
-{
- cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete
-
- uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, TU_ARRAY_SIZE(p_cdc->epin_buf));
- if ( count )
- {
- TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) );
- }
-
- return true;
-}
-
-uint32_t tud_cdc_n_write_available (uint8_t itf)
-{
- return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
-}
-
-
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
-void cdcd_init(void)
-{
- tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
-
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
- cdcd_interface_t* p_cdc = &_cdcd_itf[i];
-
- p_cdc->wanted_char = -1;
-
- // default line coding is : stop bit = 1, parity = none, data bits = 8
- p_cdc->line_coding.bit_rate = 115200;
- p_cdc->line_coding.stop_bits = 0;
- p_cdc->line_coding.parity = 0;
- p_cdc->line_coding.data_bits = 8;
-
- // config fifo
- tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
- tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, false);
-
-#if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
- tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
-#endif
- }
-}
-
-void cdcd_reset(uint8_t rhport)
-{
- (void) rhport;
-
- for(uint8_t i=0; i<CFG_TUD_CDC; i++)
- {
- tu_memclr(&_cdcd_itf[i], ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&_cdcd_itf[i].rx_ff);
- tu_fifo_clear(&_cdcd_itf[i].tx_ff);
- }
-}
-
-bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
-{
- // Only support ACM subclass
- TU_ASSERT ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
-
- // Only support AT commands, no protocol and vendor specific commands.
- TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
- itf_desc->bInterfaceProtocol == 0xff);
-
- // Find available interface
- cdcd_interface_t * p_cdc = NULL;
- uint8_t cdc_id;
- for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
- {
- if ( _cdcd_itf[cdc_id].ep_in == 0 )
- {
- p_cdc = &_cdcd_itf[cdc_id];
- break;
- }
- }
- TU_ASSERT(p_cdc);
-
- //------------- Control Interface -------------//
- p_cdc->itf_num = itf_desc->bInterfaceNumber;
-
- uint8_t const * p_desc = tu_desc_next( itf_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
-
- // Communication Functional Descriptors
- while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) )
- {
- (*p_length) += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
- }
-
- if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
- {
- // notification endpoint if any
- TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) );
-
- p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
-
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- }
-
- //------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
- {
- // next to endpoint descriptor
- p_desc = tu_desc_next(p_desc);
-
- // Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) );
-
- (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
- }
-
- // Prepare for incoming data
- _prep_out_transaction(cdc_id);
-
- return true;
-}
-
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
-
- //------------- Class Specific Request -------------//
- TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
-
- // Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if ( p_cdc->itf_num == request->wIndex ) break;
- }
-
- // Invoke callback
- if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest )
- {
- if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
- }
-
- return true;
-}
-
-// Handle class control request
-// return false to stall control endpoint (e.g unsupported request)
-bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request)
-{
- // Handle class request only
- TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
-
- // Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if ( p_cdc->itf_num == request->wIndex ) break;
- }
-
- switch ( request->bRequest )
- {
- case CDC_REQUEST_SET_LINE_CODING:
- tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
- break;
-
- case CDC_REQUEST_GET_LINE_CODING:
- tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
- break;
-
- case CDC_REQUEST_SET_CONTROL_LINE_STATE:
- // CDC PSTN v1.2 section 6.3.12
- // Bit 0: Indicates if DTE is present or not.
- // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
- // Bit 1: Carrier control for half-duplex modems.
- // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send)
- p_cdc->line_state = (uint8_t) request->wValue;
-
- tud_control_status(rhport, request);
-
- // Invoke callback
- if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, tu_bit_test(request->wValue, 0), tu_bit_test(request->wValue, 1));
- break;
-
- default: return false; // stall unsupported request
- }
-
- return true;
-}
-
-bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) rhport;
- (void) result;
-
- uint8_t itf = 0;
- cdcd_interface_t* p_cdc = _cdcd_itf;
-
- // Identify which interface to use
- for ( ; ; itf++, p_cdc++)
- {
- if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
-
- if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
- }
-
- // Received new data
- if ( ep_addr == p_cdc->ep_out )
- {
- for(uint32_t i=0; i<xferred_bytes; i++)
- {
- tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]);
-
- // Check for wanted char and invoke callback if needed
- if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) )
- {
- tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
- }
- }
-
- // invoke receive callback (if there is still data)
- if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
-
- // prepare for OUT transaction
- _prep_out_transaction(itf);
- }
-
- // Data sent to host, we could continue to fetch data tx fifo to send.
- // But it will cause incorrect baudrate set in line coding.
- // Though maybe the baudrate is not really important !!!
- if ( ep_addr == p_cdc->ep_in )
- {
- if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EPSIZE)) ) usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, NULL, 0);
- }
-
- // nothing to do with notif endpoint for now
-
- return true;
-}
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CDC) + +#include "cdc_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) + uint8_t line_state; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + char wanted_char; + cdc_line_coding_t line_coding; + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; + +#if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; +#endif + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EPSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EPSIZE]; + +}cdcd_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; + +static void _prep_out_transaction (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + + // skip if previous transfer not complete + if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return; + + // Prepare for incoming data but only allow what we can store in the ring buffer. + uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff); + if ( max_read >= TU_ARRAY_SIZE(p_cdc->epout_buf) ) + { + usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, TU_ARRAY_SIZE(p_cdc->epout_buf)); + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected(uint8_t itf) +{ + // DTR (bit 0) active is considered as connected + return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); +} + +uint8_t tud_cdc_n_get_line_state (uint8_t itf) +{ + return _cdcd_itf[itf].line_state; +} + +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) +{ + (*coding) = _cdcd_itf[itf].line_coding; +} + +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) +{ + _cdcd_itf[itf].wanted_char = wanted; +} + + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_available(uint8_t itf) +{ + return tu_fifo_count(&_cdcd_itf[itf].rx_ff); +} + +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +{ + uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize); + _prep_out_transaction(itf); + return num_read; +} + +bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr) +{ + return tu_fifo_peek_at(&_cdcd_itf[itf].rx_ff, pos, chr); +} + +void tud_cdc_n_read_flush (uint8_t itf) +{ + tu_fifo_clear(&_cdcd_itf[itf].rx_ff); + _prep_out_transaction(itf); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +{ + uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize); + +#if 0 // TODO issue with circuitpython's REPL + // flush if queue more than endpoint size + if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EPSIZE ) + { + tud_cdc_n_write_flush(itf); + } +#endif + + return ret; +} + +bool tud_cdc_n_write_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in) ); // skip if previous transfer not complete + + uint16_t count = tu_fifo_read_n(&_cdcd_itf[itf].tx_ff, p_cdc->epin_buf, TU_ARRAY_SIZE(p_cdc->epin_buf)); + if ( count ) + { + TU_VERIFY( tud_cdc_n_connected(itf) ); // fifo is empty if not connected + TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count) ); + } + + return true; +} + +uint32_t tud_cdc_n_write_available (uint8_t itf) +{ + return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); +} + + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void cdcd_init(void) +{ + tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); + + for(uint8_t i=0; i<CFG_TUD_CDC; i++) + { + cdcd_interface_t* p_cdc = &_cdcd_itf[i]; + + p_cdc->wanted_char = -1; + + // default line coding is : stop bit = 1, parity = none, data bits = 8 + p_cdc->line_coding.bit_rate = 115200; + p_cdc->line_coding.stop_bits = 0; + p_cdc->line_coding.parity = 0; + p_cdc->line_coding.data_bits = 8; + + // config fifo + tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false); + tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, false); + +#if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex)); + tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex)); +#endif + } +} + +void cdcd_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; i<CFG_TUD_CDC; i++) + { + tu_memclr(&_cdcd_itf[i], ITF_MEM_RESET_SIZE); + tu_fifo_clear(&_cdcd_itf[i].rx_ff); + tu_fifo_clear(&_cdcd_itf[i].tx_ff); + } +} + +bool cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length) +{ + // Only support ACM subclass + TU_ASSERT ( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass); + + // Only support AT commands, no protocol and vendor specific commands. + TU_ASSERT(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || + itf_desc->bInterfaceProtocol == 0xff); + + // Find available interface + cdcd_interface_t * p_cdc = NULL; + uint8_t cdc_id; + for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++) + { + if ( _cdcd_itf[cdc_id].ep_in == 0 ) + { + p_cdc = &_cdcd_itf[cdc_id]; + break; + } + } + TU_ASSERT(p_cdc); + + //------------- Control Interface -------------// + p_cdc->itf_num = itf_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) ) + { + (*p_length) += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + // notification endpoint if any + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) ); + + p_cdc->ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in) ); + + (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + } + + // Prepare for incoming data + _prep_out_transaction(cdc_id); + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send non-sense DATA) +bool cdcd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + //------------- Class Specific Request -------------// + TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + // Invoke callback + if ( CDC_REQUEST_SET_LINE_CODING == request->bRequest ) + { + if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); + } + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool cdcd_control_request(uint8_t rhport, tusb_control_request_t const * request) +{ + // Handle class request only + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + switch ( request->bRequest ) + { + case CDC_REQUEST_SET_LINE_CODING: + TU_LOG2(" Set Line Coding\n"); + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + break; + + case CDC_REQUEST_GET_LINE_CODING: + TU_LOG2(" Get Line Coding\n"); + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + break; + + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + { + // CDC PSTN v1.2 section 6.3.12 + // Bit 0: Indicates if DTE is present or not. + // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready) + // Bit 1: Carrier control for half-duplex modems. + // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send) + bool const dtr = tu_bit_test(request->wValue, 0); + bool const rts = tu_bit_test(request->wValue, 1); + + p_cdc->line_state = (uint8_t) request->wValue; + + TU_LOG2(" Set Control Line State: DTR = %d, RTS = %d\n", dtr, rts); + + tud_control_status(rhport, request); + + // Invoke callback + if ( tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts); + } + break; + + default: return false; // stall unsupported request + } + + return true; +} + +bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) result; + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + } + + // Received new data + if ( ep_addr == p_cdc->ep_out ) + { + for(uint32_t i=0; i<xferred_bytes; i++) + { + tu_fifo_write(&p_cdc->rx_ff, &p_cdc->epout_buf[i]); + + // Check for wanted char and invoke callback if needed + if ( tud_cdc_rx_wanted_cb && ( ((signed char) p_cdc->wanted_char) != -1 ) && ( p_cdc->wanted_char == p_cdc->epout_buf[i] ) ) + { + tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); + } + } + + // invoke receive callback (if there is still data) + if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); + + // prepare for OUT transaction + _prep_out_transaction(itf); + } + + // Data sent to host, we could continue to fetch data tx fifo to send. + // But it will cause incorrect baudrate set in line coding. + // Though maybe the baudrate is not really important !!! + if ( ep_addr == p_cdc->ep_in ) + { + if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EPSIZE)) ) usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, NULL, 0); + } + + // nothing to do with notif endpoint for now + + return true; +} + +#endif diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index b8b3c864d..969ec7f99 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -1,213 +1,213 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_CDC_DEVICE_H_
-#define _TUSB_CDC_DEVICE_H_
-
-#include "common/tusb_common.h"
-#include "device/usbd.h"
-#include "cdc.h"
-
-//--------------------------------------------------------------------+
-// Class Driver Configuration
-//--------------------------------------------------------------------+
-#ifndef CFG_TUD_CDC_EPSIZE
-#define CFG_TUD_CDC_EPSIZE 64
-#endif
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/** \addtogroup CDC_Serial Serial
- * @{
- * \defgroup CDC_Serial_Device Device
- * @{ */
-
-//--------------------------------------------------------------------+
-// Application API (Multiple Interfaces)
-// CFG_TUD_CDC > 1
-//--------------------------------------------------------------------+
-bool tud_cdc_n_connected (uint8_t itf);
-uint8_t tud_cdc_n_get_line_state (uint8_t itf);
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
-
-uint32_t tud_cdc_n_available (uint8_t itf);
-uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
-void tud_cdc_n_read_flush (uint8_t itf);
-bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8);
-static inline int32_t tud_cdc_n_read_char (uint8_t itf);
-
-uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
-bool tud_cdc_n_write_flush (uint8_t itf);
-uint32_t tud_cdc_n_write_available (uint8_t itf);
-static inline uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
-
-//--------------------------------------------------------------------+
-// Application API (Interface0)
-//--------------------------------------------------------------------+
-static inline bool tud_cdc_connected (void);
-static inline uint8_t tud_cdc_get_line_state (void);
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding);
-static inline void tud_cdc_set_wanted_char (char wanted);
-
-static inline uint32_t tud_cdc_available (void);
-static inline int32_t tud_cdc_read_char (void);
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize);
-static inline void tud_cdc_read_flush (void);
-static inline bool tud_cdc_peek (int pos, uint8_t* u8);
-
-static inline uint32_t tud_cdc_write_char (char ch);
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_cdc_write_str (char const* str);
-static inline bool tud_cdc_write_flush (void);
-static inline uint32_t tud_cdc_write_available (void);
-
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
-
-// Invoked when received new data
-TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf);
-
-// Invoked when received `wanted_char`
-TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
-
-// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
-TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
-
-// Invoked when line coding is change via SET_LINE_CODING
-TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline int32_t tud_cdc_n_read_char (uint8_t itf)
-{
- uint8_t ch;
- return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
-}
-
-static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch)
-{
- return tud_cdc_n_write(itf, &ch, 1);
-}
-
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str)
-{
- return tud_cdc_n_write(itf, str, strlen(str));
-}
-
-static inline bool tud_cdc_connected (void)
-{
- return tud_cdc_n_connected(0);
-}
-
-static inline uint8_t tud_cdc_get_line_state (void)
-{
- return tud_cdc_n_get_line_state(0);
-}
-
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding)
-{
- tud_cdc_n_get_line_coding(0, coding);
-}
-
-static inline void tud_cdc_set_wanted_char (char wanted)
-{
- tud_cdc_n_set_wanted_char(0, wanted);
-}
-
-static inline uint32_t tud_cdc_available (void)
-{
- return tud_cdc_n_available(0);
-}
-
-static inline int32_t tud_cdc_read_char (void)
-{
- return tud_cdc_n_read_char(0);
-}
-
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize)
-{
- return tud_cdc_n_read(0, buffer, bufsize);
-}
-
-static inline void tud_cdc_read_flush (void)
-{
- tud_cdc_n_read_flush(0);
-}
-
-static inline bool tud_cdc_peek (int pos, uint8_t* u8)
-{
- return tud_cdc_n_peek(0, pos, u8);
-}
-
-static inline uint32_t tud_cdc_write_char (char ch)
-{
- return tud_cdc_n_write_char(0, ch);
-}
-
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize)
-{
- return tud_cdc_n_write(0, buffer, bufsize);
-}
-
-static inline uint32_t tud_cdc_write_str (char const* str)
-{
- return tud_cdc_n_write_str(0, str);
-}
-
-static inline bool tud_cdc_write_flush (void)
-{
- return tud_cdc_n_write_flush(0);
-}
-
-static inline uint32_t tud_cdc_write_available(void)
-{
- return tud_cdc_n_write_available(0);
-}
-
-/** @} */
-/** @} */
-
-//--------------------------------------------------------------------+
-// INTERNAL USBD-CLASS DRIVER API
-//--------------------------------------------------------------------+
-void cdcd_init (void);
-void cdcd_reset (uint8_t rhport);
-bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
-bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_CDC_DEVICE_H_ */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_DEVICE_H_ +#define _TUSB_CDC_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "cdc.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_CDC_EPSIZE +#define CFG_TUD_CDC_EPSIZE 64 +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup CDC_Serial Serial + * @{ + * \defgroup CDC_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Interfaces) +// CFG_TUD_CDC > 1 +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected (uint8_t itf); +uint8_t tud_cdc_n_get_line_state (uint8_t itf); +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); + +uint32_t tud_cdc_n_available (uint8_t itf); +uint32_t tud_cdc_n_read (uint8_t itf, void* buffer, uint32_t bufsize); +void tud_cdc_n_read_flush (uint8_t itf); +bool tud_cdc_n_peek (uint8_t itf, int pos, uint8_t* u8); +static inline int32_t tud_cdc_n_read_char (uint8_t itf); + +uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); +bool tud_cdc_n_write_flush (uint8_t itf); +uint32_t tud_cdc_n_write_available (uint8_t itf); +static inline uint32_t tud_cdc_n_write_char (uint8_t itf, char ch); +static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str); + +//--------------------------------------------------------------------+ +// Application API (Interface0) +//--------------------------------------------------------------------+ +static inline bool tud_cdc_connected (void); +static inline uint8_t tud_cdc_get_line_state (void); +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); +static inline void tud_cdc_set_wanted_char (char wanted); + +static inline uint32_t tud_cdc_available (void); +static inline int32_t tud_cdc_read_char (void); +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize); +static inline void tud_cdc_read_flush (void); +static inline bool tud_cdc_peek (int pos, uint8_t* u8); + +static inline uint32_t tud_cdc_write_char (char ch); +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize); +static inline uint32_t tud_cdc_write_str (char const* str); +static inline bool tud_cdc_write_flush (void); +static inline uint32_t tud_cdc_write_available (void); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_cdc_rx_cb(uint8_t itf); + +// Invoked when received `wanted_char` +TU_ATTR_WEAK void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char); + +// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE +TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); + +// Invoked when line coding is change via SET_LINE_CODING +TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ +static inline int32_t tud_cdc_n_read_char (uint8_t itf) +{ + uint8_t ch; + return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1; +} + +static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) +{ + return tud_cdc_n_write(itf, &ch, 1); +} + +static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str) +{ + return tud_cdc_n_write(itf, str, strlen(str)); +} + +static inline bool tud_cdc_connected (void) +{ + return tud_cdc_n_connected(0); +} + +static inline uint8_t tud_cdc_get_line_state (void) +{ + return tud_cdc_n_get_line_state(0); +} + +static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) +{ + tud_cdc_n_get_line_coding(0, coding); +} + +static inline void tud_cdc_set_wanted_char (char wanted) +{ + tud_cdc_n_set_wanted_char(0, wanted); +} + +static inline uint32_t tud_cdc_available (void) +{ + return tud_cdc_n_available(0); +} + +static inline int32_t tud_cdc_read_char (void) +{ + return tud_cdc_n_read_char(0); +} + +static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize) +{ + return tud_cdc_n_read(0, buffer, bufsize); +} + +static inline void tud_cdc_read_flush (void) +{ + tud_cdc_n_read_flush(0); +} + +static inline bool tud_cdc_peek (int pos, uint8_t* u8) +{ + return tud_cdc_n_peek(0, pos, u8); +} + +static inline uint32_t tud_cdc_write_char (char ch) +{ + return tud_cdc_n_write_char(0, ch); +} + +static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize) +{ + return tud_cdc_n_write(0, buffer, bufsize); +} + +static inline uint32_t tud_cdc_write_str (char const* str) +{ + return tud_cdc_n_write_str(0, str); +} + +static inline bool tud_cdc_write_flush (void) +{ + return tud_cdc_n_write_flush(0); +} + +static inline uint32_t tud_cdc_write_available(void) +{ + return tud_cdc_n_write_available(0); +} + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void cdcd_init (void); +void cdcd_reset (uint8_t rhport); +bool cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool cdcd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool cdcd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool cdcd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_DEVICE_H_ */ diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 6a6f508b3..595e7fd62 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1,225 +1,225 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC)
-
-#include "common/tusb_common.h"
-#include "cdc_host.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-typedef struct {
- uint8_t itf_num;
- uint8_t itf_protocol;
-
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
-
- cdc_acm_capability_t acm_capability;
-
-} cdch_data_t;
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX];
-
-bool tuh_cdc_mounted(uint8_t dev_addr)
-{
- cdch_data_t* cdc = &cdch_data[dev_addr-1];
- return cdc->ep_in && cdc->ep_out;
-}
-
-bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid)
-{
- if ( !tuh_cdc_mounted(dev_addr) ) return false;
-
- cdch_data_t const * p_cdc = &cdch_data[dev_addr-1];
-
- switch (pipeid)
- {
- case CDC_PIPE_NOTIFICATION:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_notif );
-
- case CDC_PIPE_DATA_IN:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_in );
-
- case CDC_PIPE_DATA_OUT:
- return hcd_edpt_busy(dev_addr, p_cdc->ep_out );
-
- default:
- return false;
- }
-}
-
-//--------------------------------------------------------------------+
-// APPLICATION API (parameter validation needed)
-//--------------------------------------------------------------------+
-bool tuh_cdc_serial_is_mounted(uint8_t dev_addr)
-{
- // TODO consider all AT Command as serial candidate
- return tuh_cdc_mounted(dev_addr) &&
- (CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) &&
- (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA);
-}
-
-bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify)
-{
- TU_VERIFY( tuh_cdc_mounted(dev_addr) );
- TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA);
-
- uint8_t const ep_out = cdch_data[dev_addr-1].ep_out;
- if ( hcd_edpt_busy(dev_addr, ep_out) ) return false;
-
- return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify);
-}
-
-bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify)
-{
- TU_VERIFY( tuh_cdc_mounted(dev_addr) );
- TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA);
-
- uint8_t const ep_in = cdch_data[dev_addr-1].ep_in;
- if ( hcd_edpt_busy(dev_addr, ep_in) ) return false;
-
- return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify);
-}
-
-//--------------------------------------------------------------------+
-// USBH-CLASS DRIVER API
-//--------------------------------------------------------------------+
-void cdch_init(void)
-{
- tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX);
-}
-
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
-{
- // Only support ACM
- TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass);
-
- // Only support AT commands, no protocol and vendor specific commands.
- TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) ||
- 0xff == itf_desc->bInterfaceProtocol);
-
- uint8_t const * p_desc;
- cdch_data_t * p_cdc;
-
- p_desc = tu_desc_next(itf_desc);
- p_cdc = &cdch_data[dev_addr-1];
-
- p_cdc->itf_num = itf_desc->bInterfaceNumber;
- p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com
-
- //------------- Communication Interface -------------//
- (*p_length) = sizeof(tusb_desc_interface_t);
-
- // Communication Functional Descriptors
- while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] )
- {
- if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) )
- {
- // save ACM bmCapabilities
- p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities;
- }
-
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- }
-
- if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
- {
- // notification endpoint
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc;
-
- TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) );
- p_cdc->ep_notif = ep_desc->bEndpointAddress;
-
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- }
-
- //------------- Data Interface (if any) -------------//
- if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) &&
- (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
- {
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
-
- // data endpoints expected to be in pairs
- for(uint32_t i=0; i<2; i++)
- {
- tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
- TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
-
- TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
-
- if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
- {
- p_cdc->ep_in = ep_desc->bEndpointAddress;
- }else
- {
- p_cdc->ep_out = ep_desc->bEndpointAddress;
- }
-
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next( p_desc );
- }
- }
-
- // FIXME move to seperate API : connect
- tusb_control_request_t request =
- {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
- .wValue = 0x03, // dtr on, cst on
- .wIndex = p_cdc->itf_num,
- .wLength = 0
- };
-
- TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) );
-
- return true;
-}
-
-void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
- (void) ep_addr;
- tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes );
-}
-
-void cdch_close(uint8_t dev_addr)
-{
- cdch_data_t * p_cdc = &cdch_data[dev_addr-1];
- tu_memclr(p_cdc, sizeof(cdch_data_t));
-}
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_CDC) + +#include "common/tusb_common.h" +#include "cdc_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct { + uint8_t itf_num; + uint8_t itf_protocol; + + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + cdc_acm_capability_t acm_capability; + +} cdch_data_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +static cdch_data_t cdch_data[CFG_TUSB_HOST_DEVICE_MAX]; + +bool tuh_cdc_mounted(uint8_t dev_addr) +{ + cdch_data_t* cdc = &cdch_data[dev_addr-1]; + return cdc->ep_in && cdc->ep_out; +} + +bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid) +{ + if ( !tuh_cdc_mounted(dev_addr) ) return false; + + cdch_data_t const * p_cdc = &cdch_data[dev_addr-1]; + + switch (pipeid) + { + case CDC_PIPE_NOTIFICATION: + return hcd_edpt_busy(dev_addr, p_cdc->ep_notif ); + + case CDC_PIPE_DATA_IN: + return hcd_edpt_busy(dev_addr, p_cdc->ep_in ); + + case CDC_PIPE_DATA_OUT: + return hcd_edpt_busy(dev_addr, p_cdc->ep_out ); + + default: + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API (parameter validation needed) +//--------------------------------------------------------------------+ +bool tuh_cdc_serial_is_mounted(uint8_t dev_addr) +{ + // TODO consider all AT Command as serial candidate + return tuh_cdc_mounted(dev_addr) && + (CDC_COMM_PROTOCOL_ATCOMMAND <= cdch_data[dev_addr-1].itf_protocol) && + (cdch_data[dev_addr-1].itf_protocol <= CDC_COMM_PROTOCOL_ATCOMMAND_CDMA); +} + +bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify) +{ + TU_VERIFY( tuh_cdc_mounted(dev_addr) ); + TU_VERIFY( p_data != NULL && length, TUSB_ERROR_INVALID_PARA); + + uint8_t const ep_out = cdch_data[dev_addr-1].ep_out; + if ( hcd_edpt_busy(dev_addr, ep_out) ) return false; + + return hcd_pipe_xfer(dev_addr, ep_out, (void *) p_data, length, is_notify); +} + +bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify) +{ + TU_VERIFY( tuh_cdc_mounted(dev_addr) ); + TU_VERIFY( p_buffer != NULL && length, TUSB_ERROR_INVALID_PARA); + + uint8_t const ep_in = cdch_data[dev_addr-1].ep_in; + if ( hcd_edpt_busy(dev_addr, ep_in) ) return false; + + return hcd_pipe_xfer(dev_addr, ep_in, p_buffer, length, is_notify); +} + +//--------------------------------------------------------------------+ +// USBH-CLASS DRIVER API +//--------------------------------------------------------------------+ +void cdch_init(void) +{ + tu_memclr(cdch_data, sizeof(cdch_data_t)*CFG_TUSB_HOST_DEVICE_MAX); +} + +bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +{ + // Only support ACM + TU_VERIFY( CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass); + + // Only support AT commands, no protocol and vendor specific commands. + TU_VERIFY(tu_within(CDC_COMM_PROTOCOL_NONE, itf_desc->bInterfaceProtocol, CDC_COMM_PROTOCOL_ATCOMMAND_CDMA) || + 0xff == itf_desc->bInterfaceProtocol); + + uint8_t const * p_desc; + cdch_data_t * p_cdc; + + p_desc = tu_desc_next(itf_desc); + p_cdc = &cdch_data[dev_addr-1]; + + p_cdc->itf_num = itf_desc->bInterfaceNumber; + p_cdc->itf_protocol = itf_desc->bInterfaceProtocol; // TODO 0xff is consider as rndis candidate, other is virtual Com + + //------------- Communication Interface -------------// + (*p_length) = sizeof(tusb_desc_interface_t); + + // Communication Functional Descriptors + while( TUSB_DESC_CS_INTERFACE == p_desc[DESC_OFFSET_TYPE] ) + { + if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) + { + // save ACM bmCapabilities + p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + // notification endpoint + tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) p_desc; + + TU_ASSERT( hcd_edpt_open(rhport, dev_addr, ep_desc) ); + p_cdc->ep_notif = ep_desc->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + + // data endpoints expected to be in pairs + for(uint32_t i=0; i<2; i++) + { + tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *) p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); + TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + + TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); + + if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) + { + p_cdc->ep_in = ep_desc->bEndpointAddress; + }else + { + p_cdc->ep_out = ep_desc->bEndpointAddress; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next( p_desc ); + } + } + + // FIXME move to seperate API : connect + tusb_control_request_t request = + { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = 0x03, // dtr on, cst on + .wIndex = p_cdc->itf_num, + .wLength = 0 + }; + + TU_ASSERT( usbh_control_xfer(dev_addr, &request, NULL) ); + + return true; +} + +void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + (void) ep_addr; + tuh_cdc_xfer_isr( dev_addr, event, 0, xferred_bytes ); +} + +void cdch_close(uint8_t dev_addr) +{ + cdch_data_t * p_cdc = &cdch_data[dev_addr-1]; + tu_memclr(p_cdc, sizeof(cdch_data_t)); +} + +#endif diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 59ad2bfa8..306420ce4 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -1,123 +1,123 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_CDC_HOST_H_
-#define _TUSB_CDC_HOST_H_
-
-#include "common/tusb_common.h"
-#include "host/usbh.h"
-#include "cdc.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// CDC APPLICATION PUBLIC API
-//--------------------------------------------------------------------+
-/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
- * \addtogroup CDC_Serial Serial
- * @{
- * \defgroup CDC_Serial_Host Host
- * @{ */
-
-/** \brief Check if device support CDC Serial interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports
- * \retval false if device does not support or is not mounted
- */
-bool tuh_cdc_serial_is_mounted(uint8_t dev_addr);
-
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe.
- * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device
- * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
- * can be scheduled. User needs to make sure the corresponding interface is mounted
- * (by \ref tuh_cdc_serial_is_mounted) before calling this function.
- */
-bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid);
-
-/** \brief Perform USB OUT transfer to device
- * \param[in] dev_addr device address
- * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] length Number of bytes to be transferred via USB bus
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
- * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
- */
-bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify);
-
-/** \brief Perform USB IN transfer to get data from device
- * \param[in] dev_addr device address
- * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] length Number of bytes to be transferred via USB bus
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the
- * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION.
- */
-bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify);
-
-//--------------------------------------------------------------------+
-// CDC APPLICATION CALLBACKS
-//--------------------------------------------------------------------+
-
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe
- * \param[in] xferred_bytes Number of bytes transferred via USB bus
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note
- */
-void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes);
-
-/// @} // group CDC_Serial_Host
-/// @}
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-void cdch_init(void);
-bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void cdch_close(uint8_t dev_addr);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_CDC_HOST_H_ */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_HOST_H_ +#define _TUSB_CDC_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// CDC APPLICATION PUBLIC API +//--------------------------------------------------------------------+ +/** \ingroup ClassDriver_CDC Communication Device Class (CDC) + * \addtogroup CDC_Serial Serial + * @{ + * \defgroup CDC_Serial_Host Host + * @{ */ + +/** \brief Check if device support CDC Serial interface or not + * \param[in] dev_addr device address + * \retval true if device supports + * \retval false if device does not support or is not mounted + */ +bool tuh_cdc_serial_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \param[in] pipeid value from \ref cdc_pipeid_t to indicate target pipe. + * \retval true if the interface is busy, meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy, meaning the stack successfully transferred data from/to device + * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer + * can be scheduled. User needs to make sure the corresponding interface is mounted + * (by \ref tuh_cdc_serial_is_mounted) before calling this function. + */ +bool tuh_cdc_is_busy(uint8_t dev_addr, cdc_pipeid_t pipeid); + +/** \brief Perform USB OUT transfer to device + * \param[in] dev_addr device address + * \param[in] p_data Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] length Number of bytes to be transferred via USB bus + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the + * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. + */ +bool tuh_cdc_send(uint8_t dev_addr, void const * p_data, uint32_t length, bool is_notify); + +/** \brief Perform USB IN transfer to get data from device + * \param[in] dev_addr device address + * \param[in] p_buffer Buffer containing received data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] length Number of bytes to be transferred via USB bus + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the + * interface's callback function. \a p_data must be declared with \ref CFG_TUSB_MEM_SECTION. + */ +bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is_notify); + +//--------------------------------------------------------------------+ +// CDC APPLICATION CALLBACKS +//--------------------------------------------------------------------+ + +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \param[in] pipe_id value from \ref cdc_pipeid_t indicate the pipe + * \param[in] xferred_bytes Number of bytes transferred via USB bus + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note + */ +void tuh_cdc_xfer_isr(uint8_t dev_addr, xfer_result_t event, cdc_pipeid_t pipe_id, uint32_t xferred_bytes); + +/// @} // group CDC_Serial_Host +/// @} + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void cdch_init(void); +bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); +void cdch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_HOST_H_ */ diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h index 7fa4aa8ab..848274037 100644 --- a/src/class/hid/hid.h +++ b/src/class/hid/hid.h @@ -1,873 +1,873 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \ingroup group_class
- * \defgroup ClassDriver_HID Human Interface Device (HID)
- * @{ */
-
-#ifndef _TUSB_HID_H_
-#define _TUSB_HID_H_
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Common Definitions
-//--------------------------------------------------------------------+
-/** \defgroup ClassDriver_HID_Common Common Definitions
- * @{ */
-
- /// USB HID Descriptor
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */
- uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */
-
- uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */
- uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */
- uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */
-
- uint8_t bReportType; /**< Type of HID class report. */
- uint16_t wReportLength; /**< the total size of the Report descriptor. */
-} tusb_hid_descriptor_hid_t;
-
-/// HID Subclass
-typedef enum
-{
- HID_SUBCLASS_NONE = 0, ///< No Subclass
- HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass
-}hid_subclass_type_t;
-
-/// HID Protocol
-typedef enum
-{
- HID_PROTOCOL_NONE = 0, ///< None
- HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard
- HID_PROTOCOL_MOUSE = 2 ///< Mouse
-}hid_protocol_type_t;
-
-/// HID Descriptor Type
-typedef enum
-{
- HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor
- HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor
- HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor
-}hid_descriptor_type_t;
-
-/// HID Request Report Type
-typedef enum
-{
- HID_REPORT_TYPE_INVALID = 0,
- HID_REPORT_TYPE_INPUT, ///< Input
- HID_REPORT_TYPE_OUTPUT, ///< Output
- HID_REPORT_TYPE_FEATURE ///< Feature
-}hid_report_type_t;
-
-/// HID Class Specific Control Request
-typedef enum
-{
- HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report
- HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle
- HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol
- HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report
- HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle
- HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol
-}hid_request_type_t;
-
-/// HID Country Code
-typedef enum
-{
- HID_LOCAL_NotSupported = 0 , ///< NotSupported
- HID_LOCAL_Arabic , ///< Arabic
- HID_LOCAL_Belgian , ///< Belgian
- HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual
- HID_LOCAL_Canadian_French , ///< Canadian_French
- HID_LOCAL_Czech_Republic , ///< Czech_Republic
- HID_LOCAL_Danish , ///< Danish
- HID_LOCAL_Finnish , ///< Finnish
- HID_LOCAL_French , ///< French
- HID_LOCAL_German , ///< German
- HID_LOCAL_Greek , ///< Greek
- HID_LOCAL_Hebrew , ///< Hebrew
- HID_LOCAL_Hungary , ///< Hungary
- HID_LOCAL_International , ///< International
- HID_LOCAL_Italian , ///< Italian
- HID_LOCAL_Japan_Katakana , ///< Japan_Katakana
- HID_LOCAL_Korean , ///< Korean
- HID_LOCAL_Latin_American , ///< Latin_American
- HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch
- HID_LOCAL_Norwegian , ///< Norwegian
- HID_LOCAL_Persian_Farsi , ///< Persian (Farsi)
- HID_LOCAL_Poland , ///< Poland
- HID_LOCAL_Portuguese , ///< Portuguese
- HID_LOCAL_Russia , ///< Russia
- HID_LOCAL_Slovakia , ///< Slovakia
- HID_LOCAL_Spanish , ///< Spanish
- HID_LOCAL_Swedish , ///< Swedish
- HID_LOCAL_Swiss_French , ///< Swiss/French
- HID_LOCAL_Swiss_German , ///< Swiss/German
- HID_LOCAL_Switzerland , ///< Switzerland
- HID_LOCAL_Taiwan , ///< Taiwan
- HID_LOCAL_Turkish_Q , ///< Turkish-Q
- HID_LOCAL_UK , ///< UK
- HID_LOCAL_US , ///< US
- HID_LOCAL_Yugoslavia , ///< Yugoslavia
- HID_LOCAL_Turkish_F ///< Turkish-F
-} hid_country_code_t;
-
-/** @} */
-
-//--------------------------------------------------------------------+
-// MOUSE
-//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Mouse Mouse
- * @{ */
-
-/// Standard HID Boot Protocol Mouse Report.
-typedef struct TU_ATTR_PACKED
-{
- uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
- int8_t x; /**< Current delta x movement of the mouse. */
- int8_t y; /**< Current delta y movement on the mouse. */
- int8_t wheel; /**< Current delta wheel movement on the mouse. */
- int8_t pan; // using AC Pan
-} hid_mouse_report_t;
-
-/// Standard Mouse Buttons Bitmap
-typedef enum
-{
- MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button
- MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button
- MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button
- MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button,
- MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button,
-}hid_mouse_button_bm_t;
-
-/// @}
-
-//--------------------------------------------------------------------+
-// Keyboard
-//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Keyboard Keyboard
- * @{ */
-
-/// Standard HID Boot Protocol Keyboard Report.
-typedef struct TU_ATTR_PACKED
-{
- uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */
- uint8_t reserved; /**< Reserved for OEM use, always set to 0. */
- uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */
-} hid_keyboard_report_t;
-
-/// Keyboard modifier codes bitmap
-typedef enum
-{
- KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control
- KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift
- KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt
- KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window
- KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control
- KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift
- KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt
- KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window
-}hid_keyboard_modifier_bm_t;
-
-typedef enum
-{
- KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED
- KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED
- KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED
- KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode
- KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode
-}hid_keyboard_led_bm_t;
-
-/// @}
-
-//--------------------------------------------------------------------+
-// HID KEYCODE
-//--------------------------------------------------------------------+
-#define HID_KEY_NONE 0x00
-#define HID_KEY_A 0x04
-#define HID_KEY_B 0x05
-#define HID_KEY_C 0x06
-#define HID_KEY_D 0x07
-#define HID_KEY_E 0x08
-#define HID_KEY_F 0x09
-#define HID_KEY_G 0x0A
-#define HID_KEY_H 0x0B
-#define HID_KEY_I 0x0C
-#define HID_KEY_J 0x0D
-#define HID_KEY_K 0x0E
-#define HID_KEY_L 0x0F
-#define HID_KEY_M 0x10
-#define HID_KEY_N 0x11
-#define HID_KEY_O 0x12
-#define HID_KEY_P 0x13
-#define HID_KEY_Q 0x14
-#define HID_KEY_R 0x15
-#define HID_KEY_S 0x16
-#define HID_KEY_T 0x17
-#define HID_KEY_U 0x18
-#define HID_KEY_V 0x19
-#define HID_KEY_W 0x1A
-#define HID_KEY_X 0x1B
-#define HID_KEY_Y 0x1C
-#define HID_KEY_Z 0x1D
-#define HID_KEY_1 0x1E
-#define HID_KEY_2 0x1F
-#define HID_KEY_3 0x20
-#define HID_KEY_4 0x21
-#define HID_KEY_5 0x22
-#define HID_KEY_6 0x23
-#define HID_KEY_7 0x24
-#define HID_KEY_8 0x25
-#define HID_KEY_9 0x26
-#define HID_KEY_0 0x27
-#define HID_KEY_RETURN 0x28
-#define HID_KEY_ESCAPE 0x29
-#define HID_KEY_BACKSPACE 0x2A
-#define HID_KEY_TAB 0x2B
-#define HID_KEY_SPACE 0x2C
-#define HID_KEY_MINUS 0x2D
-#define HID_KEY_EQUAL 0x2E
-#define HID_KEY_BRACKET_LEFT 0x2F
-#define HID_KEY_BRACKET_RIGHT 0x30
-#define HID_KEY_BACKSLASH 0x31
-#define HID_KEY_EUROPE_1 0x32
-#define HID_KEY_SEMICOLON 0x33
-#define HID_KEY_APOSTROPHE 0x34
-#define HID_KEY_GRAVE 0x35
-#define HID_KEY_COMMA 0x36
-#define HID_KEY_PERIOD 0x37
-#define HID_KEY_SLASH 0x38
-#define HID_KEY_CAPS_LOCK 0x39
-#define HID_KEY_F1 0x3A
-#define HID_KEY_F2 0x3B
-#define HID_KEY_F3 0x3C
-#define HID_KEY_F4 0x3D
-#define HID_KEY_F5 0x3E
-#define HID_KEY_F6 0x3F
-#define HID_KEY_F7 0x40
-#define HID_KEY_F8 0x41
-#define HID_KEY_F9 0x42
-#define HID_KEY_F10 0x43
-#define HID_KEY_F11 0x44
-#define HID_KEY_F12 0x45
-#define HID_KEY_PRINT_SCREEN 0x46
-#define HID_KEY_SCROLL_LOCK 0x47
-#define HID_KEY_PAUSE 0x48
-#define HID_KEY_INSERT 0x49
-#define HID_KEY_HOME 0x4A
-#define HID_KEY_PAGE_UP 0x4B
-#define HID_KEY_DELETE 0x4C
-#define HID_KEY_END 0x4D
-#define HID_KEY_PAGE_DOWN 0x4E
-#define HID_KEY_ARROW_RIGHT 0x4F
-#define HID_KEY_ARROW_LEFT 0x50
-#define HID_KEY_ARROW_DOWN 0x51
-#define HID_KEY_ARROW_UP 0x52
-#define HID_KEY_NUM_LOCK 0x53
-#define HID_KEY_KEYPAD_DIVIDE 0x54
-#define HID_KEY_KEYPAD_MULTIPLY 0x55
-#define HID_KEY_KEYPAD_SUBTRACT 0x56
-#define HID_KEY_KEYPAD_ADD 0x57
-#define HID_KEY_KEYPAD_ENTER 0x58
-#define HID_KEY_KEYPAD_1 0x59
-#define HID_KEY_KEYPAD_2 0x5A
-#define HID_KEY_KEYPAD_3 0x5B
-#define HID_KEY_KEYPAD_4 0x5C
-#define HID_KEY_KEYPAD_5 0x5D
-#define HID_KEY_KEYPAD_6 0x5E
-#define HID_KEY_KEYPAD_7 0x5F
-#define HID_KEY_KEYPAD_8 0x60
-#define HID_KEY_KEYPAD_9 0x61
-#define HID_KEY_KEYPAD_0 0x62
-#define HID_KEY_KEYPAD_DECIMAL 0x63
-#define HID_KEY_EUROPE_2 0x64
-#define HID_KEY_APPLICATION 0x65
-#define HID_KEY_POWER 0x66
-#define HID_KEY_KEYPAD_EQUAL 0x67
-#define HID_KEY_F13 0x68
-#define HID_KEY_F14 0x69
-#define HID_KEY_F15 0x6A
-#define HID_KEY_CONTROL_LEFT 0xE0
-#define HID_KEY_SHIFT_LEFT 0xE1
-#define HID_KEY_ALT_LEFT 0xE2
-#define HID_KEY_GUI_LEFT 0xE3
-#define HID_KEY_CONTROL_RIGHT 0xE4
-#define HID_KEY_SHIFT_RIGHT 0xE5
-#define HID_KEY_ALT_RIGHT 0xE6
-#define HID_KEY_GUI_RIGHT 0xE7
-
-
-//--------------------------------------------------------------------+
-// REPORT DESCRIPTOR
-//--------------------------------------------------------------------+
-//------------- ITEM & TAG -------------//
-#define HID_REPORT_DATA_0(data)
-#define HID_REPORT_DATA_1(data) , data
-#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data)
-#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data)
-
-#define HID_REPORT_ITEM(data, tag, type, size) \
- (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data)
-
-#define RI_TYPE_MAIN 0
-#define RI_TYPE_GLOBAL 1
-#define RI_TYPE_LOCAL 2
-
-//------------- MAIN ITEMS 6.2.2.4 -------------//
-#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1)
-#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1)
-#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1)
-#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1)
-#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0)
-
-//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------//
-#define HID_DATA (0<<0)
-#define HID_CONSTANT (1<<0)
-
-#define HID_ARRAY (0<<1)
-#define HID_VARIABLE (1<<1)
-
-#define HID_ABSOLUTE (0<<2)
-#define HID_RELATIVE (1<<2)
-
-#define HID_WRAP_NO (0<<3)
-#define HID_WRAP (1<<3)
-
-#define HID_LINEAR (0<<4)
-#define HID_NONLINEAR (1<<4)
-
-#define HID_PREFERRED_STATE (0<<5)
-#define HID_PREFERRED_NO (1<<5)
-
-#define HID_NO_NULL_POSITION (0<<6)
-#define HID_NULL_STATE (1<<6)
-
-#define HID_NON_VOLATILE (0<<7)
-#define HID_VOLATILE (1<<7)
-
-#define HID_BITFIELD (0<<8)
-#define HID_BUFFERED_BYTES (1<<8)
-
-//------------- COLLECTION ITEM 6.2.2.6 -------------//
-enum {
- HID_COLLECTION_PHYSICAL = 0,
- HID_COLLECTION_APPLICATION,
- HID_COLLECTION_LOGICAL,
- HID_COLLECTION_REPORT,
- HID_COLLECTION_NAMED_ARRAY,
- HID_COLLECTION_USAGE_SWITCH,
- HID_COLLECTION_USAGE_MODIFIER
-};
-
-//------------- GLOBAL ITEMS 6.2.2.7 -------------//
-#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1)
-#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n)
-
-#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1)
-#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n)
-
-#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1)
-#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n)
-
-#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1)
-#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n)
-
-#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1)
-#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n)
-
-#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1)
-#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n)
-
-#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1)
-#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n)
-
-#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n)
-
-#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n)
-
-#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1)
-#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n)
-
-#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0)
-#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0)
-
-//------------- LOCAL ITEMS 6.2.2.8 -------------//
-#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n)
-
-#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n)
-
-#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1)
-#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n)
-
-//--------------------------------------------------------------------+
-// Usage Table
-//--------------------------------------------------------------------+
-
-/// HID Usage Table - Table 1: Usage Page Summary
-enum {
- HID_USAGE_PAGE_DESKTOP = 0x01,
- HID_USAGE_PAGE_SIMULATE = 0x02,
- HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
- HID_USAGE_PAGE_SPORT = 0x04,
- HID_USAGE_PAGE_GAME = 0x05,
- HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
- HID_USAGE_PAGE_KEYBOARD = 0x07,
- HID_USAGE_PAGE_LED = 0x08,
- HID_USAGE_PAGE_BUTTON = 0x09,
- HID_USAGE_PAGE_ORDINAL = 0x0a,
- HID_USAGE_PAGE_TELEPHONY = 0x0b,
- HID_USAGE_PAGE_CONSUMER = 0x0c,
- HID_USAGE_PAGE_DIGITIZER = 0x0d,
- HID_USAGE_PAGE_PID = 0x0f,
- HID_USAGE_PAGE_UNICODE = 0x10,
- HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
- HID_USAGE_PAGE_MEDICAL = 0x40,
- HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
- HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
- HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
- HID_USAGE_PAGE_SCALE = 0x8d,
- HID_USAGE_PAGE_MSR = 0x8e,
- HID_USAGE_PAGE_CAMERA = 0x90,
- HID_USAGE_PAGE_ARCADE = 0x91,
- HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
-};
-
-/// HID Usage Table - Table 6: Generic Desktop Page
-enum {
- HID_USAGE_DESKTOP_POINTER = 0x01,
- HID_USAGE_DESKTOP_MOUSE = 0x02,
- HID_USAGE_DESKTOP_JOYSTICK = 0x04,
- HID_USAGE_DESKTOP_GAMEPAD = 0x05,
- HID_USAGE_DESKTOP_KEYBOARD = 0x06,
- HID_USAGE_DESKTOP_KEYPAD = 0x07,
- HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08,
- HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09,
- HID_USAGE_DESKTOP_X = 0x30,
- HID_USAGE_DESKTOP_Y = 0x31,
- HID_USAGE_DESKTOP_Z = 0x32,
- HID_USAGE_DESKTOP_RX = 0x33,
- HID_USAGE_DESKTOP_RY = 0x34,
- HID_USAGE_DESKTOP_RZ = 0x35,
- HID_USAGE_DESKTOP_SLIDER = 0x36,
- HID_USAGE_DESKTOP_DIAL = 0x37,
- HID_USAGE_DESKTOP_WHEEL = 0x38,
- HID_USAGE_DESKTOP_HAT_SWITCH = 0x39,
- HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a,
- HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b,
- HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c,
- HID_USAGE_DESKTOP_START = 0x3d,
- HID_USAGE_DESKTOP_SELECT = 0x3e,
- HID_USAGE_DESKTOP_VX = 0x40,
- HID_USAGE_DESKTOP_VY = 0x41,
- HID_USAGE_DESKTOP_VZ = 0x42,
- HID_USAGE_DESKTOP_VBRX = 0x43,
- HID_USAGE_DESKTOP_VBRY = 0x44,
- HID_USAGE_DESKTOP_VBRZ = 0x45,
- HID_USAGE_DESKTOP_VNO = 0x46,
- HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47,
- HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48,
- HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80,
- HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81,
- HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82,
- HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83,
- HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84,
- HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85,
- HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86,
- HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87,
- HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88,
- HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89,
- HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A,
- HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B,
- HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C,
- HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D,
- HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E,
- HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F,
- HID_USAGE_DESKTOP_DPAD_UP = 0x90,
- HID_USAGE_DESKTOP_DPAD_DOWN = 0x91,
- HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92,
- HID_USAGE_DESKTOP_DPAD_LEFT = 0x93,
- HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0,
- HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1,
- HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2,
- HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3,
- HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4,
- HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5,
- HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6,
- HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7,
- HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6,
- HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7
-};
-
-
-/// HID Usage Table: Consumer Page (0x0C)
-/// Only contains controls that supported by Windows (whole list is too long)
-enum
-{
- // Generic Control
- HID_USAGE_CONSUMER_CONTROL = 0x0001,
-
- // Power Control
- HID_USAGE_CONSUMER_POWER = 0x0030,
- HID_USAGE_CONSUMER_RESET = 0x0031,
- HID_USAGE_CONSUMER_SLEEP = 0x0032,
-
- // Screen Brightness
- HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F,
- HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070,
-
- // These HID usages operate only on mobile systems (battery powered) and
- // require Windows 8 (build 8302 or greater).
- HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C,
- HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6,
- HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7,
- HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8,
-
- // Media Control
- HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD,
- HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5,
- HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6,
- HID_USAGE_CONSUMER_STOP = 0x00B7,
- HID_USAGE_CONSUMER_VOLUME = 0x00E0,
- HID_USAGE_CONSUMER_MUTE = 0x00E2,
- HID_USAGE_CONSUMER_BASS = 0x00E3,
- HID_USAGE_CONSUMER_TREBLE = 0x00E4,
- HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5,
- HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9,
- HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA,
- HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152,
- HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153,
- HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154,
- HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155,
-
- // Application Launcher
- HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183,
- HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A,
- HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192,
- HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194,
-
- // Browser/Explorer Specific
- HID_USAGE_CONSUMER_AC_SEARCH = 0x0221,
- HID_USAGE_CONSUMER_AC_HOME = 0x0223,
- HID_USAGE_CONSUMER_AC_BACK = 0x0224,
- HID_USAGE_CONSUMER_AC_FORWARD = 0x0225,
- HID_USAGE_CONSUMER_AC_STOP = 0x0226,
- HID_USAGE_CONSUMER_AC_REFRESH = 0x0227,
- HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A,
-
- // Mouse Horizontal scroll
- HID_USAGE_CONSUMER_AC_PAN = 0x0238,
-};
-
-/*--------------------------------------------------------------------
- * ASCII to KEYCODE Conversion
- * Expand to array of [128][2] (shift, keycode)
- *
- * Usage: example to convert input chr into keyboard report (modifier + keycode)
- *
- * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE };
- *
- * uint8_t keycode[6] = { 0 };
- * uint8_t modifier = 0;
- *
- * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT;
- * keycode[0] = conv_table[chr][1];
- * tud_hid_keyboard_report(report_id, modifier, keycode);
- *
- *--------------------------------------------------------------------*/
-#define HID_ASCII_TO_KEYCODE \
- {0, 0 }, /* 0x00 Null */ \
- {0, 0 }, /* 0x01 */ \
- {0, 0 }, /* 0x02 */ \
- {0, 0 }, /* 0x03 */ \
- {0, 0 }, /* 0x04 */ \
- {0, 0 }, /* 0x05 */ \
- {0, 0 }, /* 0x06 */ \
- {0, 0 }, /* 0x07 */ \
- {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \
- {0, HID_KEY_TAB }, /* 0x09 Tab */ \
- {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \
- {0, 0 }, /* 0x0B */ \
- {0, 0 }, /* 0x0C */ \
- {0, HID_KEY_RETURN }, /* 0x0D CR */ \
- {0, 0 }, /* 0x0E */ \
- {0, 0 }, /* 0x0F */ \
- {0, 0 }, /* 0x10 */ \
- {0, 0 }, /* 0x11 */ \
- {0, 0 }, /* 0x12 */ \
- {0, 0 }, /* 0x13 */ \
- {0, 0 }, /* 0x14 */ \
- {0, 0 }, /* 0x15 */ \
- {0, 0 }, /* 0x16 */ \
- {0, 0 }, /* 0x17 */ \
- {0, 0 }, /* 0x18 */ \
- {0, 0 }, /* 0x19 */ \
- {0, 0 }, /* 0x1A */ \
- {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \
- {0, 0 }, /* 0x1C */ \
- {0, 0 }, /* 0x1D */ \
- {0, 0 }, /* 0x1E */ \
- {0, 0 }, /* 0x1F */ \
- \
- {0, HID_KEY_SPACE }, /* 0x20 */ \
- {1, HID_KEY_1 }, /* 0x21 ! */ \
- {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \
- {1, HID_KEY_3 }, /* 0x23 # */ \
- {1, HID_KEY_4 }, /* 0x24 $ */ \
- {1, HID_KEY_5 }, /* 0x25 % */ \
- {1, HID_KEY_7 }, /* 0x26 & */ \
- {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \
- {1, HID_KEY_9 }, /* 0x28 ( */ \
- {1, HID_KEY_0 }, /* 0x29 ) */ \
- {1, HID_KEY_8 }, /* 0x2A * */ \
- {1, HID_KEY_EQUAL }, /* 0x2B + */ \
- {0, HID_KEY_COMMA }, /* 0x2C , */ \
- {0, HID_KEY_MINUS }, /* 0x2D - */ \
- {0, HID_KEY_PERIOD }, /* 0x2E . */ \
- {0, HID_KEY_SLASH }, /* 0x2F / */ \
- {0, HID_KEY_0 }, /* 0x30 0 */ \
- {0, HID_KEY_1 }, /* 0x31 1 */ \
- {0, HID_KEY_2 }, /* 0x32 2 */ \
- {0, HID_KEY_3 }, /* 0x33 3 */ \
- {0, HID_KEY_4 }, /* 0x34 4 */ \
- {0, HID_KEY_5 }, /* 0x35 5 */ \
- {0, HID_KEY_6 }, /* 0x36 6 */ \
- {0, HID_KEY_7 }, /* 0x37 7 */ \
- {0, HID_KEY_8 }, /* 0x38 8 */ \
- {0, HID_KEY_9 }, /* 0x39 9 */ \
- {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \
- {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \
- {1, HID_KEY_COMMA }, /* 0x3C < */ \
- {0, HID_KEY_EQUAL }, /* 0x3D = */ \
- {1, HID_KEY_PERIOD }, /* 0x3E > */ \
- {1, HID_KEY_SLASH }, /* 0x3F ? */ \
- \
- {1, HID_KEY_2 }, /* 0x40 @ */ \
- {1, HID_KEY_A }, /* 0x41 A */ \
- {1, HID_KEY_B }, /* 0x42 B */ \
- {1, HID_KEY_C }, /* 0x43 C */ \
- {1, HID_KEY_D }, /* 0x44 D */ \
- {1, HID_KEY_E }, /* 0x45 E */ \
- {1, HID_KEY_F }, /* 0x46 F */ \
- {1, HID_KEY_G }, /* 0x47 G */ \
- {1, HID_KEY_H }, /* 0x48 H */ \
- {1, HID_KEY_I }, /* 0x49 I */ \
- {1, HID_KEY_J }, /* 0x4A J */ \
- {1, HID_KEY_K }, /* 0x4B K */ \
- {1, HID_KEY_L }, /* 0x4C L */ \
- {1, HID_KEY_M }, /* 0x4D M */ \
- {1, HID_KEY_N }, /* 0x4E N */ \
- {1, HID_KEY_O }, /* 0x4F O */ \
- {1, HID_KEY_P }, /* 0x50 P */ \
- {1, HID_KEY_Q }, /* 0x51 Q */ \
- {1, HID_KEY_R }, /* 0x52 R */ \
- {1, HID_KEY_S }, /* 0x53 S */ \
- {1, HID_KEY_T }, /* 0x55 T */ \
- {1, HID_KEY_U }, /* 0x55 U */ \
- {1, HID_KEY_V }, /* 0x56 V */ \
- {1, HID_KEY_W }, /* 0x57 W */ \
- {1, HID_KEY_X }, /* 0x58 X */ \
- {1, HID_KEY_Y }, /* 0x59 Y */ \
- {1, HID_KEY_Z }, /* 0x5A Z */ \
- {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \
- {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \
- {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \
- {1, HID_KEY_6 }, /* 0x5E ^ */ \
- {1, HID_KEY_MINUS }, /* 0x5F _ */ \
- \
- {0, HID_KEY_GRAVE }, /* 0x60 ` */ \
- {0, HID_KEY_A }, /* 0x61 a */ \
- {0, HID_KEY_B }, /* 0x62 b */ \
- {0, HID_KEY_C }, /* 0x63 c */ \
- {0, HID_KEY_D }, /* 0x66 d */ \
- {0, HID_KEY_E }, /* 0x65 e */ \
- {0, HID_KEY_F }, /* 0x66 f */ \
- {0, HID_KEY_G }, /* 0x67 g */ \
- {0, HID_KEY_H }, /* 0x68 h */ \
- {0, HID_KEY_I }, /* 0x69 i */ \
- {0, HID_KEY_J }, /* 0x6A j */ \
- {0, HID_KEY_K }, /* 0x6B k */ \
- {0, HID_KEY_L }, /* 0x6C l */ \
- {0, HID_KEY_M }, /* 0x6D m */ \
- {0, HID_KEY_N }, /* 0x6E n */ \
- {0, HID_KEY_O }, /* 0x6F o */ \
- {0, HID_KEY_P }, /* 0x70 p */ \
- {0, HID_KEY_Q }, /* 0x71 q */ \
- {0, HID_KEY_R }, /* 0x72 r */ \
- {0, HID_KEY_S }, /* 0x73 s */ \
- {0, HID_KEY_T }, /* 0x75 t */ \
- {0, HID_KEY_U }, /* 0x75 u */ \
- {0, HID_KEY_V }, /* 0x76 v */ \
- {0, HID_KEY_W }, /* 0x77 w */ \
- {0, HID_KEY_X }, /* 0x78 x */ \
- {0, HID_KEY_Y }, /* 0x79 y */ \
- {0, HID_KEY_Z }, /* 0x7A z */ \
- {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \
- {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \
- {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \
- {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \
- {0, HID_KEY_DELETE } /* 0x7F Delete */ \
-
-/*--------------------------------------------------------------------
- * KEYCODE to Ascii Conversion
- * Expand to array of [128][2] (ascii without shift, ascii with shift)
- *
- * Usage: example to convert ascii from keycode (key) and shift modifier (shift).
- * Here we assume key < 128 ( printable )
- *
- * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII };
- * char ch = shift ? conv_table[chr][1] : conv_table[chr][0];
- *
- *--------------------------------------------------------------------*/
-#define HID_KEYCODE_TO_ASCII \
- {0 , 0 }, /* 0x00 */ \
- {0 , 0 }, /* 0x01 */ \
- {0 , 0 }, /* 0x02 */ \
- {0 , 0 }, /* 0x03 */ \
- {'a' , 'A' }, /* 0x04 */ \
- {'b' , 'B' }, /* 0x05 */ \
- {'c' , 'C' }, /* 0x06 */ \
- {'d' , 'D' }, /* 0x07 */ \
- {'e' , 'E' }, /* 0x08 */ \
- {'f' , 'F' }, /* 0x09 */ \
- {'g' , 'G' }, /* 0x0a */ \
- {'h' , 'H' }, /* 0x0b */ \
- {'i' , 'I' }, /* 0x0c */ \
- {'j' , 'J' }, /* 0x0d */ \
- {'k' , 'K' }, /* 0x0e */ \
- {'l' , 'L' }, /* 0x0f */ \
- {'m' , 'M' }, /* 0x10 */ \
- {'n' , 'N' }, /* 0x11 */ \
- {'o' , 'O' }, /* 0x12 */ \
- {'p' , 'P' }, /* 0x13 */ \
- {'q' , 'Q' }, /* 0x14 */ \
- {'r' , 'R' }, /* 0x15 */ \
- {'s' , 'S' }, /* 0x16 */ \
- {'t' , 'T' }, /* 0x17 */ \
- {'u' , 'U' }, /* 0x18 */ \
- {'v' , 'V' }, /* 0x19 */ \
- {'w' , 'W' }, /* 0x1a */ \
- {'x' , 'X' }, /* 0x1b */ \
- {'y' , 'Y' }, /* 0x1c */ \
- {'z' , 'Z' }, /* 0x1d */ \
- {'1' , '!' }, /* 0x1e */ \
- {'2' , '@' }, /* 0x1f */ \
- {'3' , '#' }, /* 0x20 */ \
- {'4' , '$' }, /* 0x21 */ \
- {'5' , '%' }, /* 0x22 */ \
- {'6' , '^' }, /* 0x23 */ \
- {'7' , '&' }, /* 0x24 */ \
- {'8' , '*' }, /* 0x25 */ \
- {'9' , '(' }, /* 0x26 */ \
- {'0' , ')' }, /* 0x27 */ \
- {'\r' , '\r' }, /* 0x28 */ \
- {'\x1b', '\x1b' }, /* 0x29 */ \
- {'\b' , '\b' }, /* 0x2a */ \
- {'\t' , '\t' }, /* 0x2b */ \
- {' ' , ' ' }, /* 0x2c */ \
- {'-' , '_' }, /* 0x2d */ \
- {'=' , '+' }, /* 0x2e */ \
- {'[' , '{' }, /* 0x2f */ \
- {']' , '}' }, /* 0x30 */ \
- {'\\' , '|' }, /* 0x31 */ \
- {'#' , '~' }, /* 0x32 */ \
- {';' , ':' }, /* 0x33 */ \
- {'\'' , '\"' }, /* 0x34 */ \
- {'`' , '~' }, /* 0x35 */ \
- {',' , '<' }, /* 0x36 */ \
- {'.' , '>' }, /* 0x37 */ \
- {'/' , '?' }, /* 0x38 */ \
- \
- {0 , 0 }, /* 0x39 */ \
- {0 , 0 }, /* 0x3a */ \
- {0 , 0 }, /* 0x3b */ \
- {0 , 0 }, /* 0x3c */ \
- {0 , 0 }, /* 0x3d */ \
- {0 , 0 }, /* 0x3e */ \
- {0 , 0 }, /* 0x3f */ \
- {0 , 0 }, /* 0x40 */ \
- {0 , 0 }, /* 0x41 */ \
- {0 , 0 }, /* 0x42 */ \
- {0 , 0 }, /* 0x43 */ \
- {0 , 0 }, /* 0x44 */ \
- {0 , 0 }, /* 0x45 */ \
- {0 , 0 }, /* 0x46 */ \
- {0 , 0 }, /* 0x47 */ \
- {0 , 0 }, /* 0x48 */ \
- {0 , 0 }, /* 0x49 */ \
- {0 , 0 }, /* 0x4a */ \
- {0 , 0 }, /* 0x4b */ \
- {0 , 0 }, /* 0x4c */ \
- {0 , 0 }, /* 0x4d */ \
- {0 , 0 }, /* 0x4e */ \
- {0 , 0 }, /* 0x4f */ \
- {0 , 0 }, /* 0x50 */ \
- {0 , 0 }, /* 0x51 */ \
- {0 , 0 }, /* 0x52 */ \
- {0 , 0 }, /* 0x53 */ \
- \
- {'/' , '/' }, /* 0x54 */ \
- {'*' , '*' }, /* 0x55 */ \
- {'-' , '-' }, /* 0x56 */ \
- {'+' , '+' }, /* 0x57 */ \
- {'\r' , '\r' }, /* 0x58 */ \
- {'1' , 0 }, /* 0x59 */ \
- {'2' , 0 }, /* 0x5a */ \
- {'3' , 0 }, /* 0x5b */ \
- {'4' , 0 }, /* 0x5c */ \
- {'5' , '5' }, /* 0x5d */ \
- {'6' , 0 }, /* 0x5e */ \
- {'7' , 0 }, /* 0x5f */ \
- {'8' , 0 }, /* 0x60 */ \
- {'9' , 0 }, /* 0x61 */ \
- {'0' , 0 }, /* 0x62 */ \
- {'0' , 0 }, /* 0x63 */ \
- {'=' , '=' }, /* 0x67 */ \
-
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_HID_H__ */
-
-/// @}
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_HID Human Interface Device (HID) + * @{ */ + +#ifndef _TUSB_HID_H_ +#define _TUSB_HID_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Common Definitions +//--------------------------------------------------------------------+ +/** \defgroup ClassDriver_HID_Common Common Definitions + * @{ */ + + /// USB HID Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */ + uint8_t bDescriptorType; /**< Constant name specifying type of HID descriptor. */ + + uint16_t bcdHID; /**< Numeric expression identifying the HID Class Specification release */ + uint8_t bCountryCode; /**< Numeric expression identifying country code of the localized hardware. */ + uint8_t bNumDescriptors; /**< Numeric expression specifying the number of class descriptors */ + + uint8_t bReportType; /**< Type of HID class report. */ + uint16_t wReportLength; /**< the total size of the Report descriptor. */ +} tusb_hid_descriptor_hid_t; + +/// HID Subclass +typedef enum +{ + HID_SUBCLASS_NONE = 0, ///< No Subclass + HID_SUBCLASS_BOOT = 1 ///< Boot Interface Subclass +}hid_subclass_type_t; + +/// HID Protocol +typedef enum +{ + HID_PROTOCOL_NONE = 0, ///< None + HID_PROTOCOL_KEYBOARD = 1, ///< Keyboard + HID_PROTOCOL_MOUSE = 2 ///< Mouse +}hid_protocol_type_t; + +/// HID Descriptor Type +typedef enum +{ + HID_DESC_TYPE_HID = 0x21, ///< HID Descriptor + HID_DESC_TYPE_REPORT = 0x22, ///< Report Descriptor + HID_DESC_TYPE_PHYSICAL = 0x23 ///< Physical Descriptor +}hid_descriptor_type_t; + +/// HID Request Report Type +typedef enum +{ + HID_REPORT_TYPE_INVALID = 0, + HID_REPORT_TYPE_INPUT, ///< Input + HID_REPORT_TYPE_OUTPUT, ///< Output + HID_REPORT_TYPE_FEATURE ///< Feature +}hid_report_type_t; + +/// HID Class Specific Control Request +typedef enum +{ + HID_REQ_CONTROL_GET_REPORT = 0x01, ///< Get Report + HID_REQ_CONTROL_GET_IDLE = 0x02, ///< Get Idle + HID_REQ_CONTROL_GET_PROTOCOL = 0x03, ///< Get Protocol + HID_REQ_CONTROL_SET_REPORT = 0x09, ///< Set Report + HID_REQ_CONTROL_SET_IDLE = 0x0a, ///< Set Idle + HID_REQ_CONTROL_SET_PROTOCOL = 0x0b ///< Set Protocol +}hid_request_type_t; + +/// HID Country Code +typedef enum +{ + HID_LOCAL_NotSupported = 0 , ///< NotSupported + HID_LOCAL_Arabic , ///< Arabic + HID_LOCAL_Belgian , ///< Belgian + HID_LOCAL_Canadian_Bilingual , ///< Canadian_Bilingual + HID_LOCAL_Canadian_French , ///< Canadian_French + HID_LOCAL_Czech_Republic , ///< Czech_Republic + HID_LOCAL_Danish , ///< Danish + HID_LOCAL_Finnish , ///< Finnish + HID_LOCAL_French , ///< French + HID_LOCAL_German , ///< German + HID_LOCAL_Greek , ///< Greek + HID_LOCAL_Hebrew , ///< Hebrew + HID_LOCAL_Hungary , ///< Hungary + HID_LOCAL_International , ///< International + HID_LOCAL_Italian , ///< Italian + HID_LOCAL_Japan_Katakana , ///< Japan_Katakana + HID_LOCAL_Korean , ///< Korean + HID_LOCAL_Latin_American , ///< Latin_American + HID_LOCAL_Netherlands_Dutch , ///< Netherlands/Dutch + HID_LOCAL_Norwegian , ///< Norwegian + HID_LOCAL_Persian_Farsi , ///< Persian (Farsi) + HID_LOCAL_Poland , ///< Poland + HID_LOCAL_Portuguese , ///< Portuguese + HID_LOCAL_Russia , ///< Russia + HID_LOCAL_Slovakia , ///< Slovakia + HID_LOCAL_Spanish , ///< Spanish + HID_LOCAL_Swedish , ///< Swedish + HID_LOCAL_Swiss_French , ///< Swiss/French + HID_LOCAL_Swiss_German , ///< Swiss/German + HID_LOCAL_Switzerland , ///< Switzerland + HID_LOCAL_Taiwan , ///< Taiwan + HID_LOCAL_Turkish_Q , ///< Turkish-Q + HID_LOCAL_UK , ///< UK + HID_LOCAL_US , ///< US + HID_LOCAL_Yugoslavia , ///< Yugoslavia + HID_LOCAL_Turkish_F ///< Turkish-F +} hid_country_code_t; + +/** @} */ + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Mouse Mouse + * @{ */ + +/// Standard HID Boot Protocol Mouse Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */ + int8_t x; /**< Current delta x movement of the mouse. */ + int8_t y; /**< Current delta y movement on the mouse. */ + int8_t wheel; /**< Current delta wheel movement on the mouse. */ + int8_t pan; // using AC Pan +} hid_mouse_report_t; + +/// Standard Mouse Buttons Bitmap +typedef enum +{ + MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button + MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button + MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button + MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button, + MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button, +}hid_mouse_button_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// Keyboard +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Keyboard Keyboard + * @{ */ + +/// Standard HID Boot Protocol Keyboard Report. +typedef struct TU_ATTR_PACKED +{ + uint8_t modifier; /**< Keyboard modifier (KEYBOARD_MODIFIER_* masks). */ + uint8_t reserved; /**< Reserved for OEM use, always set to 0. */ + uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */ +} hid_keyboard_report_t; + +/// Keyboard modifier codes bitmap +typedef enum +{ + KEYBOARD_MODIFIER_LEFTCTRL = TU_BIT(0), ///< Left Control + KEYBOARD_MODIFIER_LEFTSHIFT = TU_BIT(1), ///< Left Shift + KEYBOARD_MODIFIER_LEFTALT = TU_BIT(2), ///< Left Alt + KEYBOARD_MODIFIER_LEFTGUI = TU_BIT(3), ///< Left Window + KEYBOARD_MODIFIER_RIGHTCTRL = TU_BIT(4), ///< Right Control + KEYBOARD_MODIFIER_RIGHTSHIFT = TU_BIT(5), ///< Right Shift + KEYBOARD_MODIFIER_RIGHTALT = TU_BIT(6), ///< Right Alt + KEYBOARD_MODIFIER_RIGHTGUI = TU_BIT(7) ///< Right Window +}hid_keyboard_modifier_bm_t; + +typedef enum +{ + KEYBOARD_LED_NUMLOCK = TU_BIT(0), ///< Num Lock LED + KEYBOARD_LED_CAPSLOCK = TU_BIT(1), ///< Caps Lock LED + KEYBOARD_LED_SCROLLLOCK = TU_BIT(2), ///< Scroll Lock LED + KEYBOARD_LED_COMPOSE = TU_BIT(3), ///< Composition Mode + KEYBOARD_LED_KANA = TU_BIT(4) ///< Kana mode +}hid_keyboard_led_bm_t; + +/// @} + +//--------------------------------------------------------------------+ +// HID KEYCODE +//--------------------------------------------------------------------+ +#define HID_KEY_NONE 0x00 +#define HID_KEY_A 0x04 +#define HID_KEY_B 0x05 +#define HID_KEY_C 0x06 +#define HID_KEY_D 0x07 +#define HID_KEY_E 0x08 +#define HID_KEY_F 0x09 +#define HID_KEY_G 0x0A +#define HID_KEY_H 0x0B +#define HID_KEY_I 0x0C +#define HID_KEY_J 0x0D +#define HID_KEY_K 0x0E +#define HID_KEY_L 0x0F +#define HID_KEY_M 0x10 +#define HID_KEY_N 0x11 +#define HID_KEY_O 0x12 +#define HID_KEY_P 0x13 +#define HID_KEY_Q 0x14 +#define HID_KEY_R 0x15 +#define HID_KEY_S 0x16 +#define HID_KEY_T 0x17 +#define HID_KEY_U 0x18 +#define HID_KEY_V 0x19 +#define HID_KEY_W 0x1A +#define HID_KEY_X 0x1B +#define HID_KEY_Y 0x1C +#define HID_KEY_Z 0x1D +#define HID_KEY_1 0x1E +#define HID_KEY_2 0x1F +#define HID_KEY_3 0x20 +#define HID_KEY_4 0x21 +#define HID_KEY_5 0x22 +#define HID_KEY_6 0x23 +#define HID_KEY_7 0x24 +#define HID_KEY_8 0x25 +#define HID_KEY_9 0x26 +#define HID_KEY_0 0x27 +#define HID_KEY_RETURN 0x28 +#define HID_KEY_ESCAPE 0x29 +#define HID_KEY_BACKSPACE 0x2A +#define HID_KEY_TAB 0x2B +#define HID_KEY_SPACE 0x2C +#define HID_KEY_MINUS 0x2D +#define HID_KEY_EQUAL 0x2E +#define HID_KEY_BRACKET_LEFT 0x2F +#define HID_KEY_BRACKET_RIGHT 0x30 +#define HID_KEY_BACKSLASH 0x31 +#define HID_KEY_EUROPE_1 0x32 +#define HID_KEY_SEMICOLON 0x33 +#define HID_KEY_APOSTROPHE 0x34 +#define HID_KEY_GRAVE 0x35 +#define HID_KEY_COMMA 0x36 +#define HID_KEY_PERIOD 0x37 +#define HID_KEY_SLASH 0x38 +#define HID_KEY_CAPS_LOCK 0x39 +#define HID_KEY_F1 0x3A +#define HID_KEY_F2 0x3B +#define HID_KEY_F3 0x3C +#define HID_KEY_F4 0x3D +#define HID_KEY_F5 0x3E +#define HID_KEY_F6 0x3F +#define HID_KEY_F7 0x40 +#define HID_KEY_F8 0x41 +#define HID_KEY_F9 0x42 +#define HID_KEY_F10 0x43 +#define HID_KEY_F11 0x44 +#define HID_KEY_F12 0x45 +#define HID_KEY_PRINT_SCREEN 0x46 +#define HID_KEY_SCROLL_LOCK 0x47 +#define HID_KEY_PAUSE 0x48 +#define HID_KEY_INSERT 0x49 +#define HID_KEY_HOME 0x4A +#define HID_KEY_PAGE_UP 0x4B +#define HID_KEY_DELETE 0x4C +#define HID_KEY_END 0x4D +#define HID_KEY_PAGE_DOWN 0x4E +#define HID_KEY_ARROW_RIGHT 0x4F +#define HID_KEY_ARROW_LEFT 0x50 +#define HID_KEY_ARROW_DOWN 0x51 +#define HID_KEY_ARROW_UP 0x52 +#define HID_KEY_NUM_LOCK 0x53 +#define HID_KEY_KEYPAD_DIVIDE 0x54 +#define HID_KEY_KEYPAD_MULTIPLY 0x55 +#define HID_KEY_KEYPAD_SUBTRACT 0x56 +#define HID_KEY_KEYPAD_ADD 0x57 +#define HID_KEY_KEYPAD_ENTER 0x58 +#define HID_KEY_KEYPAD_1 0x59 +#define HID_KEY_KEYPAD_2 0x5A +#define HID_KEY_KEYPAD_3 0x5B +#define HID_KEY_KEYPAD_4 0x5C +#define HID_KEY_KEYPAD_5 0x5D +#define HID_KEY_KEYPAD_6 0x5E +#define HID_KEY_KEYPAD_7 0x5F +#define HID_KEY_KEYPAD_8 0x60 +#define HID_KEY_KEYPAD_9 0x61 +#define HID_KEY_KEYPAD_0 0x62 +#define HID_KEY_KEYPAD_DECIMAL 0x63 +#define HID_KEY_EUROPE_2 0x64 +#define HID_KEY_APPLICATION 0x65 +#define HID_KEY_POWER 0x66 +#define HID_KEY_KEYPAD_EQUAL 0x67 +#define HID_KEY_F13 0x68 +#define HID_KEY_F14 0x69 +#define HID_KEY_F15 0x6A +#define HID_KEY_CONTROL_LEFT 0xE0 +#define HID_KEY_SHIFT_LEFT 0xE1 +#define HID_KEY_ALT_LEFT 0xE2 +#define HID_KEY_GUI_LEFT 0xE3 +#define HID_KEY_CONTROL_RIGHT 0xE4 +#define HID_KEY_SHIFT_RIGHT 0xE5 +#define HID_KEY_ALT_RIGHT 0xE6 +#define HID_KEY_GUI_RIGHT 0xE7 + + +//--------------------------------------------------------------------+ +// REPORT DESCRIPTOR +//--------------------------------------------------------------------+ +//------------- ITEM & TAG -------------// +#define HID_REPORT_DATA_0(data) +#define HID_REPORT_DATA_1(data) , data +#define HID_REPORT_DATA_2(data) , U16_TO_U8S_LE(data) +#define HID_REPORT_DATA_3(data) , U32_TO_U8S_LE(data) + +#define HID_REPORT_ITEM(data, tag, type, size) \ + (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data) + +#define RI_TYPE_MAIN 0 +#define RI_TYPE_GLOBAL 1 +#define RI_TYPE_LOCAL 2 + +//------------- MAIN ITEMS 6.2.2.4 -------------// +#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1) +#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1) +#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1) +#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1) +#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0) + +//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------// +#define HID_DATA (0<<0) +#define HID_CONSTANT (1<<0) + +#define HID_ARRAY (0<<1) +#define HID_VARIABLE (1<<1) + +#define HID_ABSOLUTE (0<<2) +#define HID_RELATIVE (1<<2) + +#define HID_WRAP_NO (0<<3) +#define HID_WRAP (1<<3) + +#define HID_LINEAR (0<<4) +#define HID_NONLINEAR (1<<4) + +#define HID_PREFERRED_STATE (0<<5) +#define HID_PREFERRED_NO (1<<5) + +#define HID_NO_NULL_POSITION (0<<6) +#define HID_NULL_STATE (1<<6) + +#define HID_NON_VOLATILE (0<<7) +#define HID_VOLATILE (1<<7) + +#define HID_BITFIELD (0<<8) +#define HID_BUFFERED_BYTES (1<<8) + +//------------- COLLECTION ITEM 6.2.2.6 -------------// +enum { + HID_COLLECTION_PHYSICAL = 0, + HID_COLLECTION_APPLICATION, + HID_COLLECTION_LOGICAL, + HID_COLLECTION_REPORT, + HID_COLLECTION_NAMED_ARRAY, + HID_COLLECTION_USAGE_SWITCH, + HID_COLLECTION_USAGE_MODIFIER +}; + +//------------- GLOBAL ITEMS 6.2.2.7 -------------// +#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1) +#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n) + +#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n) + +#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n) + +#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0) +#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0) + +//------------- LOCAL ITEMS 6.2.2.8 -------------// +#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1) +#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n) + +//--------------------------------------------------------------------+ +// Usage Table +//--------------------------------------------------------------------+ + +/// HID Usage Table - Table 1: Usage Page Summary +enum { + HID_USAGE_PAGE_DESKTOP = 0x01, + HID_USAGE_PAGE_SIMULATE = 0x02, + HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03, + HID_USAGE_PAGE_SPORT = 0x04, + HID_USAGE_PAGE_GAME = 0x05, + HID_USAGE_PAGE_GENERIC_DEVICE = 0x06, + HID_USAGE_PAGE_KEYBOARD = 0x07, + HID_USAGE_PAGE_LED = 0x08, + HID_USAGE_PAGE_BUTTON = 0x09, + HID_USAGE_PAGE_ORDINAL = 0x0a, + HID_USAGE_PAGE_TELEPHONY = 0x0b, + HID_USAGE_PAGE_CONSUMER = 0x0c, + HID_USAGE_PAGE_DIGITIZER = 0x0d, + HID_USAGE_PAGE_PID = 0x0f, + HID_USAGE_PAGE_UNICODE = 0x10, + HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, + HID_USAGE_PAGE_MEDICAL = 0x40, + HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 + HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 + HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, + HID_USAGE_PAGE_SCALE = 0x8d, + HID_USAGE_PAGE_MSR = 0x8e, + HID_USAGE_PAGE_CAMERA = 0x90, + HID_USAGE_PAGE_ARCADE = 0x91, + HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF +}; + +/// HID Usage Table - Table 6: Generic Desktop Page +enum { + HID_USAGE_DESKTOP_POINTER = 0x01, + HID_USAGE_DESKTOP_MOUSE = 0x02, + HID_USAGE_DESKTOP_JOYSTICK = 0x04, + HID_USAGE_DESKTOP_GAMEPAD = 0x05, + HID_USAGE_DESKTOP_KEYBOARD = 0x06, + HID_USAGE_DESKTOP_KEYPAD = 0x07, + HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08, + HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09, + HID_USAGE_DESKTOP_X = 0x30, + HID_USAGE_DESKTOP_Y = 0x31, + HID_USAGE_DESKTOP_Z = 0x32, + HID_USAGE_DESKTOP_RX = 0x33, + HID_USAGE_DESKTOP_RY = 0x34, + HID_USAGE_DESKTOP_RZ = 0x35, + HID_USAGE_DESKTOP_SLIDER = 0x36, + HID_USAGE_DESKTOP_DIAL = 0x37, + HID_USAGE_DESKTOP_WHEEL = 0x38, + HID_USAGE_DESKTOP_HAT_SWITCH = 0x39, + HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a, + HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b, + HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c, + HID_USAGE_DESKTOP_START = 0x3d, + HID_USAGE_DESKTOP_SELECT = 0x3e, + HID_USAGE_DESKTOP_VX = 0x40, + HID_USAGE_DESKTOP_VY = 0x41, + HID_USAGE_DESKTOP_VZ = 0x42, + HID_USAGE_DESKTOP_VBRX = 0x43, + HID_USAGE_DESKTOP_VBRY = 0x44, + HID_USAGE_DESKTOP_VBRZ = 0x45, + HID_USAGE_DESKTOP_VNO = 0x46, + HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47, + HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48, + HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80, + HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81, + HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82, + HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83, + HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84, + HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85, + HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86, + HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87, + HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88, + HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89, + HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A, + HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B, + HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C, + HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D, + HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E, + HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F, + HID_USAGE_DESKTOP_DPAD_UP = 0x90, + HID_USAGE_DESKTOP_DPAD_DOWN = 0x91, + HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92, + HID_USAGE_DESKTOP_DPAD_LEFT = 0x93, + HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0, + HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1, + HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2, + HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3, + HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4, + HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5, + HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6, + HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7, + HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7 +}; + + +/// HID Usage Table: Consumer Page (0x0C) +/// Only contains controls that supported by Windows (whole list is too long) +enum +{ + // Generic Control + HID_USAGE_CONSUMER_CONTROL = 0x0001, + + // Power Control + HID_USAGE_CONSUMER_POWER = 0x0030, + HID_USAGE_CONSUMER_RESET = 0x0031, + HID_USAGE_CONSUMER_SLEEP = 0x0032, + + // Screen Brightness + HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, + HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, + + // These HID usages operate only on mobile systems (battery powered) and + // require Windows 8 (build 8302 or greater). + HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C, + HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6, + HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, + HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, + + // Media Control + HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, + HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, + HID_USAGE_CONSUMER_STOP = 0x00B7, + HID_USAGE_CONSUMER_VOLUME = 0x00E0, + HID_USAGE_CONSUMER_MUTE = 0x00E2, + HID_USAGE_CONSUMER_BASS = 0x00E3, + HID_USAGE_CONSUMER_TREBLE = 0x00E4, + HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, + HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, + HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, + HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, + HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, + HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, + HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, + + // Application Launcher + HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, + HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, + HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, + HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, + + // Browser/Explorer Specific + HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, + HID_USAGE_CONSUMER_AC_HOME = 0x0223, + HID_USAGE_CONSUMER_AC_BACK = 0x0224, + HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, + HID_USAGE_CONSUMER_AC_STOP = 0x0226, + HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, + HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, + + // Mouse Horizontal scroll + HID_USAGE_CONSUMER_AC_PAN = 0x0238, +}; + +/*-------------------------------------------------------------------- + * ASCII to KEYCODE Conversion + * Expand to array of [128][2] (shift, keycode) + * + * Usage: example to convert input chr into keyboard report (modifier + keycode) + * + * uint8_t const conv_table[128][2] = { HID_ASCII_TO_KEYCODE }; + * + * uint8_t keycode[6] = { 0 }; + * uint8_t modifier = 0; + * + * if ( conv_table[chr][0] ) modifier = KEYBOARD_MODIFIER_LEFTSHIFT; + * keycode[0] = conv_table[chr][1]; + * tud_hid_keyboard_report(report_id, modifier, keycode); + * + *--------------------------------------------------------------------*/ +#define HID_ASCII_TO_KEYCODE \ + {0, 0 }, /* 0x00 Null */ \ + {0, 0 }, /* 0x01 */ \ + {0, 0 }, /* 0x02 */ \ + {0, 0 }, /* 0x03 */ \ + {0, 0 }, /* 0x04 */ \ + {0, 0 }, /* 0x05 */ \ + {0, 0 }, /* 0x06 */ \ + {0, 0 }, /* 0x07 */ \ + {0, HID_KEY_BACKSPACE }, /* 0x08 Backspace */ \ + {0, HID_KEY_TAB }, /* 0x09 Tab */ \ + {0, HID_KEY_RETURN }, /* 0x0A Line Feed */ \ + {0, 0 }, /* 0x0B */ \ + {0, 0 }, /* 0x0C */ \ + {0, HID_KEY_RETURN }, /* 0x0D CR */ \ + {0, 0 }, /* 0x0E */ \ + {0, 0 }, /* 0x0F */ \ + {0, 0 }, /* 0x10 */ \ + {0, 0 }, /* 0x11 */ \ + {0, 0 }, /* 0x12 */ \ + {0, 0 }, /* 0x13 */ \ + {0, 0 }, /* 0x14 */ \ + {0, 0 }, /* 0x15 */ \ + {0, 0 }, /* 0x16 */ \ + {0, 0 }, /* 0x17 */ \ + {0, 0 }, /* 0x18 */ \ + {0, 0 }, /* 0x19 */ \ + {0, 0 }, /* 0x1A */ \ + {0, HID_KEY_ESCAPE }, /* 0x1B Escape */ \ + {0, 0 }, /* 0x1C */ \ + {0, 0 }, /* 0x1D */ \ + {0, 0 }, /* 0x1E */ \ + {0, 0 }, /* 0x1F */ \ + \ + {0, HID_KEY_SPACE }, /* 0x20 */ \ + {1, HID_KEY_1 }, /* 0x21 ! */ \ + {1, HID_KEY_APOSTROPHE }, /* 0x22 " */ \ + {1, HID_KEY_3 }, /* 0x23 # */ \ + {1, HID_KEY_4 }, /* 0x24 $ */ \ + {1, HID_KEY_5 }, /* 0x25 % */ \ + {1, HID_KEY_7 }, /* 0x26 & */ \ + {0, HID_KEY_APOSTROPHE }, /* 0x27 ' */ \ + {1, HID_KEY_9 }, /* 0x28 ( */ \ + {1, HID_KEY_0 }, /* 0x29 ) */ \ + {1, HID_KEY_8 }, /* 0x2A * */ \ + {1, HID_KEY_EQUAL }, /* 0x2B + */ \ + {0, HID_KEY_COMMA }, /* 0x2C , */ \ + {0, HID_KEY_MINUS }, /* 0x2D - */ \ + {0, HID_KEY_PERIOD }, /* 0x2E . */ \ + {0, HID_KEY_SLASH }, /* 0x2F / */ \ + {0, HID_KEY_0 }, /* 0x30 0 */ \ + {0, HID_KEY_1 }, /* 0x31 1 */ \ + {0, HID_KEY_2 }, /* 0x32 2 */ \ + {0, HID_KEY_3 }, /* 0x33 3 */ \ + {0, HID_KEY_4 }, /* 0x34 4 */ \ + {0, HID_KEY_5 }, /* 0x35 5 */ \ + {0, HID_KEY_6 }, /* 0x36 6 */ \ + {0, HID_KEY_7 }, /* 0x37 7 */ \ + {0, HID_KEY_8 }, /* 0x38 8 */ \ + {0, HID_KEY_9 }, /* 0x39 9 */ \ + {1, HID_KEY_SEMICOLON }, /* 0x3A : */ \ + {0, HID_KEY_SEMICOLON }, /* 0x3B ; */ \ + {1, HID_KEY_COMMA }, /* 0x3C < */ \ + {0, HID_KEY_EQUAL }, /* 0x3D = */ \ + {1, HID_KEY_PERIOD }, /* 0x3E > */ \ + {1, HID_KEY_SLASH }, /* 0x3F ? */ \ + \ + {1, HID_KEY_2 }, /* 0x40 @ */ \ + {1, HID_KEY_A }, /* 0x41 A */ \ + {1, HID_KEY_B }, /* 0x42 B */ \ + {1, HID_KEY_C }, /* 0x43 C */ \ + {1, HID_KEY_D }, /* 0x44 D */ \ + {1, HID_KEY_E }, /* 0x45 E */ \ + {1, HID_KEY_F }, /* 0x46 F */ \ + {1, HID_KEY_G }, /* 0x47 G */ \ + {1, HID_KEY_H }, /* 0x48 H */ \ + {1, HID_KEY_I }, /* 0x49 I */ \ + {1, HID_KEY_J }, /* 0x4A J */ \ + {1, HID_KEY_K }, /* 0x4B K */ \ + {1, HID_KEY_L }, /* 0x4C L */ \ + {1, HID_KEY_M }, /* 0x4D M */ \ + {1, HID_KEY_N }, /* 0x4E N */ \ + {1, HID_KEY_O }, /* 0x4F O */ \ + {1, HID_KEY_P }, /* 0x50 P */ \ + {1, HID_KEY_Q }, /* 0x51 Q */ \ + {1, HID_KEY_R }, /* 0x52 R */ \ + {1, HID_KEY_S }, /* 0x53 S */ \ + {1, HID_KEY_T }, /* 0x55 T */ \ + {1, HID_KEY_U }, /* 0x55 U */ \ + {1, HID_KEY_V }, /* 0x56 V */ \ + {1, HID_KEY_W }, /* 0x57 W */ \ + {1, HID_KEY_X }, /* 0x58 X */ \ + {1, HID_KEY_Y }, /* 0x59 Y */ \ + {1, HID_KEY_Z }, /* 0x5A Z */ \ + {0, HID_KEY_BRACKET_LEFT }, /* 0x5B [ */ \ + {0, HID_KEY_BACKSLASH }, /* 0x5C '\' */ \ + {0, HID_KEY_BRACKET_RIGHT }, /* 0x5D ] */ \ + {1, HID_KEY_6 }, /* 0x5E ^ */ \ + {1, HID_KEY_MINUS }, /* 0x5F _ */ \ + \ + {0, HID_KEY_GRAVE }, /* 0x60 ` */ \ + {0, HID_KEY_A }, /* 0x61 a */ \ + {0, HID_KEY_B }, /* 0x62 b */ \ + {0, HID_KEY_C }, /* 0x63 c */ \ + {0, HID_KEY_D }, /* 0x66 d */ \ + {0, HID_KEY_E }, /* 0x65 e */ \ + {0, HID_KEY_F }, /* 0x66 f */ \ + {0, HID_KEY_G }, /* 0x67 g */ \ + {0, HID_KEY_H }, /* 0x68 h */ \ + {0, HID_KEY_I }, /* 0x69 i */ \ + {0, HID_KEY_J }, /* 0x6A j */ \ + {0, HID_KEY_K }, /* 0x6B k */ \ + {0, HID_KEY_L }, /* 0x6C l */ \ + {0, HID_KEY_M }, /* 0x6D m */ \ + {0, HID_KEY_N }, /* 0x6E n */ \ + {0, HID_KEY_O }, /* 0x6F o */ \ + {0, HID_KEY_P }, /* 0x70 p */ \ + {0, HID_KEY_Q }, /* 0x71 q */ \ + {0, HID_KEY_R }, /* 0x72 r */ \ + {0, HID_KEY_S }, /* 0x73 s */ \ + {0, HID_KEY_T }, /* 0x75 t */ \ + {0, HID_KEY_U }, /* 0x75 u */ \ + {0, HID_KEY_V }, /* 0x76 v */ \ + {0, HID_KEY_W }, /* 0x77 w */ \ + {0, HID_KEY_X }, /* 0x78 x */ \ + {0, HID_KEY_Y }, /* 0x79 y */ \ + {0, HID_KEY_Z }, /* 0x7A z */ \ + {1, HID_KEY_BRACKET_LEFT }, /* 0x7B { */ \ + {1, HID_KEY_BACKSLASH }, /* 0x7C | */ \ + {1, HID_KEY_BRACKET_RIGHT }, /* 0x7D } */ \ + {1, HID_KEY_GRAVE }, /* 0x7E ~ */ \ + {0, HID_KEY_DELETE } /* 0x7F Delete */ \ + +/*-------------------------------------------------------------------- + * KEYCODE to Ascii Conversion + * Expand to array of [128][2] (ascii without shift, ascii with shift) + * + * Usage: example to convert ascii from keycode (key) and shift modifier (shift). + * Here we assume key < 128 ( printable ) + * + * uint8_t const conv_table[128][2] = { HID_KEYCODE_TO_ASCII }; + * char ch = shift ? conv_table[chr][1] : conv_table[chr][0]; + * + *--------------------------------------------------------------------*/ +#define HID_KEYCODE_TO_ASCII \ + {0 , 0 }, /* 0x00 */ \ + {0 , 0 }, /* 0x01 */ \ + {0 , 0 }, /* 0x02 */ \ + {0 , 0 }, /* 0x03 */ \ + {'a' , 'A' }, /* 0x04 */ \ + {'b' , 'B' }, /* 0x05 */ \ + {'c' , 'C' }, /* 0x06 */ \ + {'d' , 'D' }, /* 0x07 */ \ + {'e' , 'E' }, /* 0x08 */ \ + {'f' , 'F' }, /* 0x09 */ \ + {'g' , 'G' }, /* 0x0a */ \ + {'h' , 'H' }, /* 0x0b */ \ + {'i' , 'I' }, /* 0x0c */ \ + {'j' , 'J' }, /* 0x0d */ \ + {'k' , 'K' }, /* 0x0e */ \ + {'l' , 'L' }, /* 0x0f */ \ + {'m' , 'M' }, /* 0x10 */ \ + {'n' , 'N' }, /* 0x11 */ \ + {'o' , 'O' }, /* 0x12 */ \ + {'p' , 'P' }, /* 0x13 */ \ + {'q' , 'Q' }, /* 0x14 */ \ + {'r' , 'R' }, /* 0x15 */ \ + {'s' , 'S' }, /* 0x16 */ \ + {'t' , 'T' }, /* 0x17 */ \ + {'u' , 'U' }, /* 0x18 */ \ + {'v' , 'V' }, /* 0x19 */ \ + {'w' , 'W' }, /* 0x1a */ \ + {'x' , 'X' }, /* 0x1b */ \ + {'y' , 'Y' }, /* 0x1c */ \ + {'z' , 'Z' }, /* 0x1d */ \ + {'1' , '!' }, /* 0x1e */ \ + {'2' , '@' }, /* 0x1f */ \ + {'3' , '#' }, /* 0x20 */ \ + {'4' , '$' }, /* 0x21 */ \ + {'5' , '%' }, /* 0x22 */ \ + {'6' , '^' }, /* 0x23 */ \ + {'7' , '&' }, /* 0x24 */ \ + {'8' , '*' }, /* 0x25 */ \ + {'9' , '(' }, /* 0x26 */ \ + {'0' , ')' }, /* 0x27 */ \ + {'\r' , '\r' }, /* 0x28 */ \ + {'\x1b', '\x1b' }, /* 0x29 */ \ + {'\b' , '\b' }, /* 0x2a */ \ + {'\t' , '\t' }, /* 0x2b */ \ + {' ' , ' ' }, /* 0x2c */ \ + {'-' , '_' }, /* 0x2d */ \ + {'=' , '+' }, /* 0x2e */ \ + {'[' , '{' }, /* 0x2f */ \ + {']' , '}' }, /* 0x30 */ \ + {'\\' , '|' }, /* 0x31 */ \ + {'#' , '~' }, /* 0x32 */ \ + {';' , ':' }, /* 0x33 */ \ + {'\'' , '\"' }, /* 0x34 */ \ + {'`' , '~' }, /* 0x35 */ \ + {',' , '<' }, /* 0x36 */ \ + {'.' , '>' }, /* 0x37 */ \ + {'/' , '?' }, /* 0x38 */ \ + \ + {0 , 0 }, /* 0x39 */ \ + {0 , 0 }, /* 0x3a */ \ + {0 , 0 }, /* 0x3b */ \ + {0 , 0 }, /* 0x3c */ \ + {0 , 0 }, /* 0x3d */ \ + {0 , 0 }, /* 0x3e */ \ + {0 , 0 }, /* 0x3f */ \ + {0 , 0 }, /* 0x40 */ \ + {0 , 0 }, /* 0x41 */ \ + {0 , 0 }, /* 0x42 */ \ + {0 , 0 }, /* 0x43 */ \ + {0 , 0 }, /* 0x44 */ \ + {0 , 0 }, /* 0x45 */ \ + {0 , 0 }, /* 0x46 */ \ + {0 , 0 }, /* 0x47 */ \ + {0 , 0 }, /* 0x48 */ \ + {0 , 0 }, /* 0x49 */ \ + {0 , 0 }, /* 0x4a */ \ + {0 , 0 }, /* 0x4b */ \ + {0 , 0 }, /* 0x4c */ \ + {0 , 0 }, /* 0x4d */ \ + {0 , 0 }, /* 0x4e */ \ + {0 , 0 }, /* 0x4f */ \ + {0 , 0 }, /* 0x50 */ \ + {0 , 0 }, /* 0x51 */ \ + {0 , 0 }, /* 0x52 */ \ + {0 , 0 }, /* 0x53 */ \ + \ + {'/' , '/' }, /* 0x54 */ \ + {'*' , '*' }, /* 0x55 */ \ + {'-' , '-' }, /* 0x56 */ \ + {'+' , '+' }, /* 0x57 */ \ + {'\r' , '\r' }, /* 0x58 */ \ + {'1' , 0 }, /* 0x59 */ \ + {'2' , 0 }, /* 0x5a */ \ + {'3' , 0 }, /* 0x5b */ \ + {'4' , 0 }, /* 0x5c */ \ + {'5' , '5' }, /* 0x5d */ \ + {'6' , 0 }, /* 0x5e */ \ + {'7' , 0 }, /* 0x5f */ \ + {'8' , 0 }, /* 0x60 */ \ + {'9' , 0 }, /* 0x61 */ \ + {'0' , 0 }, /* 0x62 */ \ + {'0' , 0 }, /* 0x63 */ \ + {'=' , '=' }, /* 0x67 */ \ + + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HID_H__ */ + +/// @} diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h index 577b9ca8e..f5e29d8a2 100644 --- a/src/class/hid/hid_device.h +++ b/src/class/hid/hid_device.h @@ -1,315 +1,315 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_HID_DEVICE_H_
-#define _TUSB_HID_DEVICE_H_
-
-#include "common/tusb_common.h"
-#include "device/usbd.h"
-#include "hid.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Class Driver Default Configure & Validation
-//--------------------------------------------------------------------+
-
-#ifndef CFG_TUD_HID_BUFSIZE
-#define CFG_TUD_HID_BUFSIZE 16
-#endif
-
-//--------------------------------------------------------------------+
-// Application API
-//--------------------------------------------------------------------+
-
-// Check if the interface is ready to use
-bool tud_hid_ready(void);
-
-// Check if current mode is Boot (true) or Report (false)
-bool tud_hid_boot_mode(void);
-
-// Send report to host
-bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len);
-
-// KEYBOARD: convenient helper to send keyboard report if application
-// use template layout report as defined by hid_keyboard_report_t
-bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
-
-// MOUSE: convenient helper to send mouse report if application
-// use template layout report as defined by hid_mouse_report_t
-bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
-
-//--------------------------------------------------------------------+
-// Callbacks (Weak is optional)
-//--------------------------------------------------------------------+
-
-// Invoked when received GET HID REPORT DESCRIPTOR request
-// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint8_t const * tud_hid_descriptor_report_cb(void);
-
-// Invoked when received GET_REPORT control request
-// Application must fill buffer report's content and return its length.
-// Return zero will cause the stack to STALL request
-uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
-
-// Invoked when received SET_REPORT control request or
-// received data on OUT endpoint ( Report ID = 0, Type = 0 )
-void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
-
-// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report )
-TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode);
-
-// Invoked when received SET_IDLE request. return false will stall the request
-// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
-// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
-
-/* --------------------------------------------------------------------+
- * HID Report Descriptor Template
- *
- * Convenient for declaring popular HID device (keyboard, mouse, consumer,
- * gamepad etc...). Templates take "HID_REPORT_ID(n)," as input, leave
- * empty if multiple reports is not used
- *
- * - Only 1 report: no parameter
- * uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() };
- *
- * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template
- * uint8_t const report_desc[] =
- * {
- * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) ,
- * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2), )
- * };
- *--------------------------------------------------------------------*/
-
-// Keyboard Report Descriptor Template
-#define TUD_HID_REPORT_DESC_KEYBOARD(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
- /* Report ID if any */\
- __VA_ARGS__ \
- /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
- HID_USAGE_MIN ( 224 ) ,\
- HID_USAGE_MAX ( 231 ) ,\
- HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 1 ) ,\
- HID_REPORT_COUNT ( 8 ) ,\
- HID_REPORT_SIZE ( 1 ) ,\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
- /* 8 bit reserved */ \
- HID_REPORT_COUNT ( 1 ) ,\
- HID_REPORT_SIZE ( 8 ) ,\
- HID_INPUT ( HID_CONSTANT ) ,\
- /* 6-byte Keycodes */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
- HID_USAGE_MIN ( 0 ) ,\
- HID_USAGE_MAX ( 255 ) ,\
- HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 255 ) ,\
- HID_REPORT_COUNT ( 6 ) ,\
- HID_REPORT_SIZE ( 8 ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
- /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
- HID_USAGE_MIN ( 1 ) ,\
- HID_USAGE_MAX ( 5 ) ,\
- HID_REPORT_COUNT ( 5 ) ,\
- HID_REPORT_SIZE ( 1 ) ,\
- HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
- /* led padding */ \
- HID_REPORT_COUNT ( 1 ) ,\
- HID_REPORT_SIZE ( 3 ) ,\
- HID_OUTPUT ( HID_CONSTANT ) ,\
- HID_COLLECTION_END \
-
-// Mouse Report Descriptor Template
-#define TUD_HID_REPORT_DESC_MOUSE(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
- /* Report ID if any */\
- __VA_ARGS__ \
- HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
- HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
- HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
- HID_USAGE_MIN ( 1 ) ,\
- HID_USAGE_MAX ( 5 ) ,\
- HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 1 ) ,\
- /* Left, Right, Middle, Backward, Forward buttons */ \
- HID_REPORT_COUNT( 5 ) ,\
- HID_REPORT_SIZE ( 1 ) ,\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
- /* 3 bit padding */ \
- HID_REPORT_COUNT( 1 ) ,\
- HID_REPORT_SIZE ( 3 ) ,\
- HID_INPUT ( HID_CONSTANT ) ,\
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- /* X, Y position [-127, 127] */ \
- HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
- HID_LOGICAL_MIN ( 0x81 ) ,\
- HID_LOGICAL_MAX ( 0x7f ) ,\
- HID_REPORT_COUNT( 2 ) ,\
- HID_REPORT_SIZE ( 8 ) ,\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
- /* Verital wheel scroll [-127, 127] */ \
- HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
- HID_LOGICAL_MIN ( 0x81 ) ,\
- HID_LOGICAL_MAX ( 0x7f ) ,\
- HID_REPORT_COUNT( 1 ) ,\
- HID_REPORT_SIZE ( 8 ) ,\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
- HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
- /* Horizontal wheel scroll [-127, 127] */ \
- HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
- HID_LOGICAL_MIN ( 0x81 ), \
- HID_LOGICAL_MAX ( 0x7f ), \
- HID_REPORT_COUNT( 1 ), \
- HID_REPORT_SIZE ( 8 ), \
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
- HID_COLLECTION_END , \
- HID_COLLECTION_END \
-
-// Consumer Control Report Descriptor Template
-#define TUD_HID_REPORT_DESC_CONSUMER(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
- HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
- /* Report ID if any */\
- __VA_ARGS__ \
- HID_LOGICAL_MIN ( 0x00 ) ,\
- HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\
- HID_USAGE_MIN ( 0x00 ) ,\
- HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\
- HID_REPORT_COUNT ( 1 ) ,\
- HID_REPORT_SIZE ( 16 ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
- HID_COLLECTION_END \
-
-/* System Control Report Descriptor Template
- * 0x00 - do nothing
- * 0x01 - Power Off
- * 0x02 - Standby
- * 0x04 - Wake Host
- */
-#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
- /* Report ID if any */\
- __VA_ARGS__ \
- /* 2 bit system power control */ \
- HID_LOGICAL_MIN ( 1 ) ,\
- HID_LOGICAL_MAX ( 3 ) ,\
- HID_REPORT_COUNT ( 1 ) ,\
- HID_REPORT_SIZE ( 2 ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
- /* 6 bit padding */ \
- HID_REPORT_COUNT ( 1 ) ,\
- HID_REPORT_SIZE ( 6 ) ,\
- HID_INPUT ( HID_CONSTANT ) ,\
- HID_COLLECTION_END \
-
-// Gamepad Report Descriptor Template
-// with 16 buttons and 2 joysticks with following layout
-// | Button Map (2 bytes) | X | Y | Z | Rz
-#define TUD_HID_REPORT_DESC_GAMEPAD(...) \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
- /* Report ID if any */\
- __VA_ARGS__ \
- /* 16 bit Button Map */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
- HID_USAGE_MIN ( 1 ) ,\
- HID_USAGE_MAX ( 16 ) ,\
- HID_LOGICAL_MIN ( 0 ) ,\
- HID_LOGICAL_MAX ( 1 ) ,\
- HID_REPORT_COUNT ( 16 ) ,\
- HID_REPORT_SIZE ( 1 ) ,\
- HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
- /* X, Y, Z, Rz (min -127, max 127 ) */ \
- HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
- HID_LOGICAL_MIN ( 0x81 ) ,\
- HID_LOGICAL_MAX ( 0x7f ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\
- HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\
- HID_REPORT_COUNT ( 4 ) ,\
- HID_REPORT_SIZE ( 8 ) ,\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
- HID_COLLECTION_END \
-
-// HID Generic Input & Output
-// - 1st parameter is report size (mandatory)
-// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
-#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \
- HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
- HID_USAGE ( 0x01 ),\
- HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
- /* Report ID if any */\
- __VA_ARGS__ \
- /* Input */ \
- HID_USAGE ( 0x02 ),\
- HID_LOGICAL_MIN ( 0x00 ),\
- HID_LOGICAL_MAX ( 0xff ),\
- HID_REPORT_SIZE ( 8 ),\
- HID_REPORT_COUNT( report_size ),\
- HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
- /* Output */ \
- HID_USAGE ( 0x03 ),\
- HID_LOGICAL_MIN ( 0x00 ),\
- HID_LOGICAL_MAX ( 0xff ),\
- HID_REPORT_SIZE ( 8 ),\
- HID_REPORT_COUNT( report_size ),\
- HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
- HID_COLLECTION_END \
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-void hidd_init (void);
-void hidd_reset (uint8_t rhport);
-bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
-bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_HID_DEVICE_H_ */
-
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_HID_DEVICE_H_ +#define _TUSB_HID_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "hid.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Default Configure & Validation +//--------------------------------------------------------------------+ + +#ifndef CFG_TUD_HID_BUFSIZE +#define CFG_TUD_HID_BUFSIZE 16 +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Check if the interface is ready to use +bool tud_hid_ready(void); + +// Check if current mode is Boot (true) or Report (false) +bool tud_hid_boot_mode(void); + +// Send report to host +bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len); + +// KEYBOARD: convenient helper to send keyboard report if application +// use template layout report as defined by hid_keyboard_report_t +bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]); + +// MOUSE: convenient helper to send mouse report if application +// use template layout report as defined by hid_mouse_report_t +bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal); + +//--------------------------------------------------------------------+ +// Callbacks (Weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received GET HID REPORT DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(void); + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen); + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize); + +// Invoked when received SET_PROTOCOL request ( mode switch Boot <-> Report ) +TU_ATTR_WEAK void tud_hid_boot_mode_cb(uint8_t boot_mode); + +// Invoked when received SET_IDLE request. return false will stall the request +// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication +// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms). +TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate); + +/* --------------------------------------------------------------------+ + * HID Report Descriptor Template + * + * Convenient for declaring popular HID device (keyboard, mouse, consumer, + * gamepad etc...). Templates take "HID_REPORT_ID(n)," as input, leave + * empty if multiple reports is not used + * + * - Only 1 report: no parameter + * uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() }; + * + * - Multiple Reports: "HID_REPORT_ID(ID)," must be passed to template + * uint8_t const report_desc[] = + * { + * TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1), ) , + * TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2), ) + * }; + *--------------------------------------------------------------------*/ + +// Keyboard Report Descriptor Template +#define TUD_HID_REPORT_DESC_KEYBOARD(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 8 bits Modifier Keys (Shfit, Control, Alt) */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ + HID_USAGE_MIN ( 224 ) ,\ + HID_USAGE_MAX ( 231 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + HID_REPORT_COUNT ( 8 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* 8 bit reserved */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + /* 6-byte Keycodes */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\ + HID_USAGE_MIN ( 0 ) ,\ + HID_USAGE_MAX ( 255 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 255 ) ,\ + HID_REPORT_COUNT ( 6 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 5 ) ,\ + HID_REPORT_COUNT ( 5 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* led padding */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 3 ) ,\ + HID_OUTPUT ( HID_CONSTANT ) ,\ + HID_COLLECTION_END \ + +// Mouse Report Descriptor Template +#define TUD_HID_REPORT_DESC_MOUSE(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\ + HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 5 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + /* Left, Right, Middle, Backward, Forward buttons */ \ + HID_REPORT_COUNT( 5 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + /* 3 bit padding */ \ + HID_REPORT_COUNT( 1 ) ,\ + HID_REPORT_SIZE ( 3 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + /* X, Y position [-127, 127] */ \ + HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_REPORT_COUNT( 2 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ + /* Verital wheel scroll [-127, 127] */ \ + HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_REPORT_COUNT( 1 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \ + /* Horizontal wheel scroll [-127, 127] */ \ + HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \ + HID_LOGICAL_MIN ( 0x81 ), \ + HID_LOGICAL_MAX ( 0x7f ), \ + HID_REPORT_COUNT( 1 ), \ + HID_REPORT_SIZE ( 8 ), \ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \ + HID_COLLECTION_END , \ + HID_COLLECTION_END \ + +// Consumer Control Report Descriptor Template +#define TUD_HID_REPORT_DESC_CONSUMER(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\ + HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + HID_LOGICAL_MIN ( 0x00 ) ,\ + HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\ + HID_USAGE_MIN ( 0x00 ) ,\ + HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 16 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + +/* System Control Report Descriptor Template + * 0x00 - do nothing + * 0x01 - Power Off + * 0x02 - Standby + * 0x04 - Wake Host + */ +#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 2 bit system power control */ \ + HID_LOGICAL_MIN ( 1 ) ,\ + HID_LOGICAL_MAX ( 3 ) ,\ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 2 ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* 6 bit padding */ \ + HID_REPORT_COUNT ( 1 ) ,\ + HID_REPORT_SIZE ( 6 ) ,\ + HID_INPUT ( HID_CONSTANT ) ,\ + HID_COLLECTION_END \ + +// Gamepad Report Descriptor Template +// with 16 buttons and 2 joysticks with following layout +// | Button Map (2 bytes) | X | Y | Z | Rz +#define TUD_HID_REPORT_DESC_GAMEPAD(...) \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* 16 bit Button Map */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\ + HID_USAGE_MIN ( 1 ) ,\ + HID_USAGE_MAX ( 16 ) ,\ + HID_LOGICAL_MIN ( 0 ) ,\ + HID_LOGICAL_MAX ( 1 ) ,\ + HID_REPORT_COUNT ( 16 ) ,\ + HID_REPORT_SIZE ( 1 ) ,\ + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ + /* X, Y, Z, Rz (min -127, max 127 ) */ \ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ + HID_LOGICAL_MIN ( 0x81 ) ,\ + HID_LOGICAL_MAX ( 0x7f ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\ + HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\ + HID_REPORT_COUNT ( 4 ) ,\ + HID_REPORT_SIZE ( 8 ) ,\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ + HID_COLLECTION_END \ + +// HID Generic Input & Output +// - 1st parameter is report size (mandatory) +// - 2nd parameter is report id HID_REPORT_ID(n) (optional) +#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \ + HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ + HID_USAGE ( 0x01 ),\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* Input */ \ + HID_USAGE ( 0x02 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + /* Output */ \ + HID_USAGE ( 0x03 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + HID_COLLECTION_END \ + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void hidd_init (void); +void hidd_reset (uint8_t rhport); +bool hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool hidd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool hidd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_HID_DEVICE_H_ */ + diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c index 91a441af2..69c49b012 100644 --- a/src/class/hid/hid_host.c +++ b/src/class/hid/hid_host.c @@ -1,289 +1,289 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if (TUSB_OPT_HOST_ENABLED && HOST_CLASS_HID)
-
-#include "common/tusb_common.h"
-#include "hid_host.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-
-//--------------------------------------------------------------------+
-// HID Interface common functions
-//--------------------------------------------------------------------+
-static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid)
-{
- p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID);
- p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
- p_hid->interface_number = interface_number;
-
- TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl));
-
- return true;
-}
-
-static inline void hidh_interface_close(hidh_interface_info_t *p_hid)
-{
- tu_memclr(p_hid, sizeof(hidh_interface_info_t));
-}
-
-// called from public API need to validate parameters
-tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_info_t *p_hid)
-{
- //------------- parameters validation -------------//
- // TODO change to use is configured function
- TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY);
- TU_VERIFY (report, TUSB_ERROR_INVALID_PARA);
- TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY);
-
- TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ;
-
- return TUSB_ERROR_NONE;
-}
-
-//--------------------------------------------------------------------+
-// KEYBOARD
-//--------------------------------------------------------------------+
-#if CFG_TUH_HID_KEYBOARD
-
-#if 0
-#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \
- [0][keycode] = ascii,\
- [1][keycode] = shift_modified,\
-
-// TODO size of table should be a macro for application to check boundary
-uint8_t const hid_keycode_to_ascii_tbl[2][128] =
-{
- HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII)
-};
-#endif
-
-static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
-
-//------------- KEYBOARD PUBLIC API (parameter validation required) -------------//
-bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr)
-{
- return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboardh_data[dev_addr-1].pipe_hdl);
-}
-
-tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report)
-{
- return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]);
-}
-
-bool tuh_hid_keyboard_is_busy(uint8_t dev_addr)
-{
- return tuh_hid_keyboard_is_mounted(dev_addr) &&
- hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl );
-}
-
-#endif
-
-//--------------------------------------------------------------------+
-// MOUSE
-//--------------------------------------------------------------------+
-#if CFG_TUH_HID_MOUSE
-
-static hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1
-
-//------------- Public API -------------//
-bool tuh_hid_mouse_is_mounted(uint8_t dev_addr)
-{
- return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(mouseh_data[dev_addr-1].pipe_hdl);
-}
-
-bool tuh_hid_mouse_is_busy(uint8_t dev_addr)
-{
- return tuh_hid_mouse_is_mounted(dev_addr) &&
- hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl );
-}
-
-tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report)
-{
- return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]);
-}
-
-#endif
-
-//--------------------------------------------------------------------+
-// GENERIC
-//--------------------------------------------------------------------+
-#if CFG_TUSB_HOST_HID_GENERIC
-
-//STATIC_ struct {
-// hidh_interface_info_t
-//} generic_data[CFG_TUSB_HOST_DEVICE_MAX];
-
-#endif
-
-//--------------------------------------------------------------------+
-// CLASS-USBH API (don't require to verify parameters)
-//--------------------------------------------------------------------+
-void hidh_init(void)
-{
-#if CFG_TUH_HID_KEYBOARD
- tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
-#endif
-
-#if CFG_TUH_HID_MOUSE
- tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX);
-#endif
-
-#if CFG_TUSB_HOST_HID_GENERIC
- hidh_generic_init();
-#endif
-}
-
-#if 0
-CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256];
-#endif
-
-bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
-{
- uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
-
- //------------- HID descriptor -------------//
- p_desc += p_desc[DESC_OFFSET_LEN];
- tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
- TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA);
-
- //------------- Endpoint Descriptor -------------//
- p_desc += p_desc[DESC_OFFSET_LEN];
- tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc;
- TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA);
-
- //------------- SET IDLE (0) request -------------//
- tusb_control_request_t request = {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = HID_REQ_CONTROL_SET_IDLE,
- .wValue = 0, // idle_rate = 0
- .wIndex = p_interface_desc->bInterfaceNumber,
- .wLength = 0
- };
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
-
-#if 0
- //------------- Get Report Descriptor TODO HID parser -------------//
- if ( p_desc_hid->bNumDescriptors )
- {
- STASK_INVOKE(
- usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE),
- TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0,
- p_desc_hid->wReportLength, report_descriptor ),
- error
- );
- (void) error; // if error in getting report descriptor --> treating like there is none
- }
-#endif
-
- if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass )
- {
- #if CFG_TUH_HID_KEYBOARD
- if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol)
- {
- TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) );
- tuh_hid_keyboard_mounted_cb(dev_addr);
- } else
- #endif
-
- #if CFG_TUH_HID_MOUSE
- if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol)
- {
- TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) );
- tuh_hid_mouse_mounted_cb(dev_addr);
- } else
- #endif
-
- {
- // Not supported protocol
- return false;
- }
- }else
- {
- // Not supported subclass
- return false;
- }
-
- *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t);
-
- return true;
-}
-
-void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
-{
- (void) xferred_bytes; // TODO may need to use this para later
-
-#if CFG_TUH_HID_KEYBOARD
- if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
- {
- tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event);
- return;
- }
-#endif
-
-#if CFG_TUH_HID_MOUSE
- if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) )
- {
- tuh_hid_mouse_isr(pipe_hdl.dev_addr, event);
- return;
- }
-#endif
-
-#if CFG_TUSB_HOST_HID_GENERIC
-
-#endif
-}
-
-void hidh_close(uint8_t dev_addr)
-{
-#if CFG_TUH_HID_KEYBOARD
- if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) )
- {
- hidh_interface_close(&keyboardh_data[dev_addr-1]);
- tuh_hid_keyboard_unmounted_cb(dev_addr);
- }
-#endif
-
-#if CFG_TUH_HID_MOUSE
- if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) )
- {
- hidh_interface_close(&mouseh_data[dev_addr-1]);
- tuh_hid_mouse_unmounted_cb( dev_addr );
- }
-#endif
-
-#if CFG_TUSB_HOST_HID_GENERIC
- hidh_generic_close(dev_addr);
-#endif
-}
-
-
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_HOST_ENABLED && HOST_CLASS_HID) + +#include "common/tusb_common.h" +#include "hid_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// HID Interface common functions +//--------------------------------------------------------------------+ +static inline bool hidh_interface_open(uint8_t dev_addr, uint8_t interface_number, tusb_desc_endpoint_t const *p_endpoint_desc, hidh_interface_info_t *p_hid) +{ + p_hid->pipe_hdl = hcd_edpt_open(dev_addr, p_endpoint_desc, TUSB_CLASS_HID); + p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor + p_hid->interface_number = interface_number; + + TU_ASSERT (pipehandle_is_valid(p_hid->pipe_hdl)); + + return true; +} + +static inline void hidh_interface_close(hidh_interface_info_t *p_hid) +{ + tu_memclr(p_hid, sizeof(hidh_interface_info_t)); +} + +// called from public API need to validate parameters +tusb_error_t hidh_interface_get_report(uint8_t dev_addr, void * report, hidh_interface_info_t *p_hid) +{ + //------------- parameters validation -------------// + // TODO change to use is configured function + TU_ASSERT (TUSB_DEVICE_STATE_CONFIGURED == tuh_device_get_state(dev_addr), TUSB_ERROR_DEVICE_NOT_READY); + TU_VERIFY (report, TUSB_ERROR_INVALID_PARA); + TU_ASSERT (!hcd_edpt_busy(p_hid->pipe_hdl), TUSB_ERROR_INTERFACE_IS_BUSY); + + TU_ASSERT_ERR( hcd_pipe_xfer(p_hid->pipe_hdl, report, p_hid->report_size, true) ) ; + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// KEYBOARD +//--------------------------------------------------------------------+ +#if CFG_TUH_HID_KEYBOARD + +#if 0 +#define EXPAND_KEYCODE_TO_ASCII(keycode, ascii, shift_modified) \ + [0][keycode] = ascii,\ + [1][keycode] = shift_modified,\ + +// TODO size of table should be a macro for application to check boundary +uint8_t const hid_keycode_to_ascii_tbl[2][128] = +{ + HID_KEYCODE_TABLE(EXPAND_KEYCODE_TO_ASCII) +}; +#endif + +static hidh_interface_info_t keyboardh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 + +//------------- KEYBOARD PUBLIC API (parameter validation required) -------------// +bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(keyboardh_data[dev_addr-1].pipe_hdl); +} + +tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void* p_report) +{ + return hidh_interface_get_report(dev_addr, p_report, &keyboardh_data[dev_addr-1]); +} + +bool tuh_hid_keyboard_is_busy(uint8_t dev_addr) +{ + return tuh_hid_keyboard_is_mounted(dev_addr) && + hcd_edpt_busy( keyboardh_data[dev_addr-1].pipe_hdl ); +} + +#endif + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ +#if CFG_TUH_HID_MOUSE + +static hidh_interface_info_t mouseh_data[CFG_TUSB_HOST_DEVICE_MAX]; // does not have addr0, index = dev_address-1 + +//------------- Public API -------------// +bool tuh_hid_mouse_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && pipehandle_is_valid(mouseh_data[dev_addr-1].pipe_hdl); +} + +bool tuh_hid_mouse_is_busy(uint8_t dev_addr) +{ + return tuh_hid_mouse_is_mounted(dev_addr) && + hcd_edpt_busy( mouseh_data[dev_addr-1].pipe_hdl ); +} + +tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void * report) +{ + return hidh_interface_get_report(dev_addr, report, &mouseh_data[dev_addr-1]); +} + +#endif + +//--------------------------------------------------------------------+ +// GENERIC +//--------------------------------------------------------------------+ +#if CFG_TUSB_HOST_HID_GENERIC + +//STATIC_ struct { +// hidh_interface_info_t +//} generic_data[CFG_TUSB_HOST_DEVICE_MAX]; + +#endif + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void hidh_init(void) +{ +#if CFG_TUH_HID_KEYBOARD + tu_memclr(&keyboardh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); +#endif + +#if CFG_TUH_HID_MOUSE + tu_memclr(&mouseh_data, sizeof(hidh_interface_info_t)*CFG_TUSB_HOST_DEVICE_MAX); +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + hidh_generic_init(); +#endif +} + +#if 0 +CFG_TUSB_MEM_SECTION uint8_t report_descriptor[256]; +#endif + +bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) +{ + uint8_t const *p_desc = (uint8_t const *) p_interface_desc; + + //------------- HID descriptor -------------// + p_desc += p_desc[DESC_OFFSET_LEN]; + tusb_hid_descriptor_hid_t const *p_desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc; + TU_ASSERT(HID_DESC_TYPE_HID == p_desc_hid->bDescriptorType, TUSB_ERROR_INVALID_PARA); + + //------------- Endpoint Descriptor -------------// + p_desc += p_desc[DESC_OFFSET_LEN]; + tusb_desc_endpoint_t const * p_endpoint_desc = (tusb_desc_endpoint_t const *) p_desc; + TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint_desc->bDescriptorType, TUSB_ERROR_INVALID_PARA); + + //------------- SET IDLE (0) request -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = HID_REQ_CONTROL_SET_IDLE, + .wValue = 0, // idle_rate = 0 + .wIndex = p_interface_desc->bInterfaceNumber, + .wLength = 0 + }; + TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); + +#if 0 + //------------- Get Report Descriptor TODO HID parser -------------// + if ( p_desc_hid->bNumDescriptors ) + { + STASK_INVOKE( + usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_INTERFACE), + TUSB_REQ_GET_DESCRIPTOR, (p_desc_hid->bReportType << 8), 0, + p_desc_hid->wReportLength, report_descriptor ), + error + ); + (void) error; // if error in getting report descriptor --> treating like there is none + } +#endif + + if ( HID_SUBCLASS_BOOT == p_interface_desc->bInterfaceSubClass ) + { + #if CFG_TUH_HID_KEYBOARD + if ( HID_PROTOCOL_KEYBOARD == p_interface_desc->bInterfaceProtocol) + { + TU_ASSERT( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &keyboardh_data[dev_addr-1]) ); + tuh_hid_keyboard_mounted_cb(dev_addr); + } else + #endif + + #if CFG_TUH_HID_MOUSE + if ( HID_PROTOCOL_MOUSE == p_interface_desc->bInterfaceProtocol) + { + TU_ASSERT ( hidh_interface_open(dev_addr, p_interface_desc->bInterfaceNumber, p_endpoint_desc, &mouseh_data[dev_addr-1]) ); + tuh_hid_mouse_mounted_cb(dev_addr); + } else + #endif + + { + // Not supported protocol + return false; + } + }else + { + // Not supported subclass + return false; + } + + *p_length = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + sizeof(tusb_desc_endpoint_t); + + return true; +} + +void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes) +{ + (void) xferred_bytes; // TODO may need to use this para later + +#if CFG_TUH_HID_KEYBOARD + if ( pipehandle_is_equal(pipe_hdl, keyboardh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) + { + tuh_hid_keyboard_isr(pipe_hdl.dev_addr, event); + return; + } +#endif + +#if CFG_TUH_HID_MOUSE + if ( pipehandle_is_equal(pipe_hdl, mouseh_data[pipe_hdl.dev_addr-1].pipe_hdl) ) + { + tuh_hid_mouse_isr(pipe_hdl.dev_addr, event); + return; + } +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + +#endif +} + +void hidh_close(uint8_t dev_addr) +{ +#if CFG_TUH_HID_KEYBOARD + if ( pipehandle_is_valid( keyboardh_data[dev_addr-1].pipe_hdl ) ) + { + hidh_interface_close(&keyboardh_data[dev_addr-1]); + tuh_hid_keyboard_unmounted_cb(dev_addr); + } +#endif + +#if CFG_TUH_HID_MOUSE + if( pipehandle_is_valid( mouseh_data[dev_addr-1].pipe_hdl ) ) + { + hidh_interface_close(&mouseh_data[dev_addr-1]); + tuh_hid_mouse_unmounted_cb( dev_addr ); + } +#endif + +#if CFG_TUSB_HOST_HID_GENERIC + hidh_generic_close(dev_addr); +#endif +} + + + +#endif diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h index 1c619c570..a3b614cd7 100644 --- a/src/class/hid/hid_host.h +++ b/src/class/hid/hid_host.h @@ -1,215 +1,215 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \addtogroup ClassDriver_HID
- * @{ */
-
-#ifndef _TUSB_HID_HOST_H_
-#define _TUSB_HID_HOST_H_
-
-#include "common/tusb_common.h"
-#include "host/usbh.h"
-#include "hid.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// KEYBOARD Application API
-//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Keyboard Keyboard
- * @{ */
-
-/** \defgroup Keyboard_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
-
-extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled
-
-/** \brief Check if device supports Keyboard interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports Keyboard interface
- * \retval false if device does not support Keyboard interface or is not mounted
- */
-bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr);
-
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report.
- * Alternatively, asynchronous event API can be used
- */
-bool tuh_hid_keyboard_is_busy(uint8_t dev_addr);
-
-/** \brief Perform a get report from Keyboard interface
- * \param[in] dev_addr device address
- * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
- * \returns \ref tusb_error_t type to indicate success or error condition.
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report);
-
-//------------- Application Callback -------------//
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback
- */
-void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event);
-
-/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr);
-
-/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr);
-
-/** @} */ // Keyboard_Host
-/** @} */ // ClassDriver_HID_Keyboard
-
-//--------------------------------------------------------------------+
-// MOUSE Application API
-//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Mouse Mouse
- * @{ */
-
-/** \defgroup Mouse_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
-
-/** \brief Check if device supports Mouse interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports Mouse interface
- * \retval false if device does not support Mouse interface or is not mounted
- */
-bool tuh_hid_mouse_is_mounted(uint8_t dev_addr);
-
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report.
- * Alternatively, asynchronous event API can be used
- */
-bool tuh_hid_mouse_is_busy(uint8_t dev_addr);
-
-/** \brief Perform a get report from Mouse interface
- * \param[in] dev_addr device address
- * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION)
- * \returns \ref tusb_error_t type to indicate success or error condition.
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report);
-
-//------------- Application Callback -------------//
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback
- */
-void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event);
-
-/** \brief Callback function that will be invoked when a device with Mouse interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_hid_mouse_mounted_cb(uint8_t dev_addr);
-
-/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr);
-
-/** @} */ // Mouse_Host
-/** @} */ // ClassDriver_HID_Mouse
-
-//--------------------------------------------------------------------+
-// GENERIC Application API
-//--------------------------------------------------------------------+
-/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet)
- * @{ */
-
-/** \defgroup Generic_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
-
-bool tuh_hid_generic_is_mounted(uint8_t dev_addr);
-tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
-tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete);
-tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr);
-tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr);
-
-//------------- Application Callback -------------//
-void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event);
-
-/** @} */ // Generic_Host
-/** @} */ // ClassDriver_HID_Generic
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-typedef struct {
- pipe_handle_t pipe_hdl;
- uint16_t report_size;
- uint8_t interface_number;
-}hidh_interface_info_t;
-
-void hidh_init(void);
-bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
-void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
-void hidh_close(uint8_t dev_addr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _TUSB_HID_HOST_H_ */
-
-/** @} */ // ClassDriver_HID
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \addtogroup ClassDriver_HID + * @{ */ + +#ifndef _TUSB_HID_HOST_H_ +#define _TUSB_HID_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "hid.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// KEYBOARD Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Keyboard Keyboard + * @{ */ + +/** \defgroup Keyboard_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +extern uint8_t const hid_keycode_to_ascii_tbl[2][128]; // TODO used weak attr if build failed without KEYBOARD enabled + +/** \brief Check if device supports Keyboard interface or not + * \param[in] dev_addr device address + * \retval true if device supports Keyboard interface + * \retval false if device does not support Keyboard interface or is not mounted + */ +bool tuh_hid_keyboard_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is primarily used for polling/waiting result after \ref tuh_hid_keyboard_get_report. + * Alternatively, asynchronous event API can be used + */ +bool tuh_hid_keyboard_is_busy(uint8_t dev_addr); + +/** \brief Perform a get report from Keyboard interface + * \param[in] dev_addr device address + * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) + * \returns \ref tusb_error_t type to indicate success or error condition. + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_hid_keyboard_get_report(uint8_t dev_addr, void * p_report); + +//------------- Application Callback -------------// +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note Application should schedule the next report by calling \ref tuh_hid_keyboard_get_report within this callback + */ +void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event); + +/** \brief Callback function that will be invoked when a device with Keyboard interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with Keyboard interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr); + +/** @} */ // Keyboard_Host +/** @} */ // ClassDriver_HID_Keyboard + +//--------------------------------------------------------------------+ +// MOUSE Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Mouse Mouse + * @{ */ + +/** \defgroup Mouse_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +/** \brief Check if device supports Mouse interface or not + * \param[in] dev_addr device address + * \retval true if device supports Mouse interface + * \retval false if device does not support Mouse interface or is not mounted + */ +bool tuh_hid_mouse_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is primarily used for polling/waiting result after \ref tuh_hid_mouse_get_report. + * Alternatively, asynchronous event API can be used + */ +bool tuh_hid_mouse_is_busy(uint8_t dev_addr); + +/** \brief Perform a get report from Mouse interface + * \param[in] dev_addr device address + * \param[in,out] p_report address that is used to store data from device. Must be accessible by usb controller (see \ref CFG_TUSB_MEM_SECTION) + * \returns \ref tusb_error_t type to indicate success or error condition. + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of usb transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_hid_mouse_get_report(uint8_t dev_addr, void* p_report); + +//------------- Application Callback -------------// +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note Application should schedule the next report by calling \ref tuh_hid_mouse_get_report within this callback + */ +void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event); + +/** \brief Callback function that will be invoked when a device with Mouse interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_hid_mouse_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with Mouse interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr); + +/** @} */ // Mouse_Host +/** @} */ // ClassDriver_HID_Mouse + +//--------------------------------------------------------------------+ +// GENERIC Application API +//--------------------------------------------------------------------+ +/** \addtogroup ClassDriver_HID_Generic Generic (not supported yet) + * @{ */ + +/** \defgroup Generic_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ + +bool tuh_hid_generic_is_mounted(uint8_t dev_addr); +tusb_error_t tuh_hid_generic_get_report(uint8_t dev_addr, void* p_report, bool int_on_complete); +tusb_error_t tuh_hid_generic_set_report(uint8_t dev_addr, void* p_report, bool int_on_complete); +tusb_interface_status_t tuh_hid_generic_get_status(uint8_t dev_addr); +tusb_interface_status_t tuh_hid_generic_set_status(uint8_t dev_addr); + +//------------- Application Callback -------------// +void tuh_hid_generic_isr(uint8_t dev_addr, xfer_result_t event); + +/** @} */ // Generic_Host +/** @} */ // ClassDriver_HID_Generic + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +typedef struct { + pipe_handle_t pipe_hdl; + uint16_t report_size; + uint8_t interface_number; +}hidh_interface_info_t; + +void hidh_init(void); +bool hidh_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length); +void hidh_isr(pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes); +void hidh_close(uint8_t dev_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_HID_HOST_H_ */ + +/** @} */ // ClassDriver_HID diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h index b3cc17af9..f758b6e1c 100644 --- a/src/class/midi/midi.h +++ b/src/class/midi/midi.h @@ -1,167 +1,167 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \ingroup group_class
- * \defgroup ClassDriver_CDC Communication Device Class (CDC)
- * Currently only Abstract Control Model subclass is supported
- * @{ */
-
-#ifndef _TUSB_MIDI_H__
-#define _TUSB_MIDI_H__
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Class Specific Descriptor
-//--------------------------------------------------------------------+
-
-typedef enum
-{
- MIDI_CS_INTERFACE_HEADER = 0x01,
- MIDI_CS_INTERFACE_IN_JACK = 0x02,
- MIDI_CS_INTERFACE_OUT_JACK = 0x03,
- MIDI_CS_INTERFACE_ELEMENT = 0x04,
-} midi_cs_interface_subtype_t;
-
-typedef enum
-{
- MIDI_CS_ENDPOINT_GENERAL = 0x01
-} midi_cs_endpoint_subtype_t;
-
-typedef enum
-{
- MIDI_JACK_EMBEDDED = 0x01,
- MIDI_JACK_EXTERNAL = 0x02
-} midi_jack_type_t;
-
-/// MIDI Interface Header Descriptor
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType
- uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
- uint16_t wTotalLength ;
-} midi_desc_header_t;
-
-/// MIDI In Jack Descriptor
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType
- uint8_t bJackType ; ///< Embedded or External
- uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
- uint8_t iJack ; ///< string descriptor
-} midi_desc_in_jack_t;
-
-
-/// MIDI Out Jack Descriptor with single pin
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType
- uint8_t bJackType ; ///< Embedded or External
- uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
- uint8_t bNrInputPins;
-
- uint8_t baSourceID;
- uint8_t baSourcePin;
-
- uint8_t iJack ; ///< string descriptor
-} midi_desc_out_jack_t ;
-
-/// MIDI Out Jack Descriptor with multiple pins
-#define midi_desc_out_jack_n_t(input_num) \
- struct TU_ATTR_PACKED { \
- uint8_t bLength ; \
- uint8_t bDescriptorType ; \
- uint8_t bDescriptorSubType ; \
- uint8_t bJackType ; \
- uint8_t bJackID ; \
- uint8_t bNrInputPins ; \
- struct TU_ATTR_PACKED { \
- uint8_t baSourceID; \
- uint8_t baSourcePin; \
- } pins[input_num]; \
- uint8_t iJack ; \
- }
-
-/// MIDI Element Descriptor
-typedef struct TU_ATTR_PACKED
-{
- uint8_t bLength ; ///< Size of this descriptor in bytes.
- uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
- uint8_t bDescriptorSubType ; ///< Descriptor SubType
- uint8_t bElementID;
-
- uint8_t bNrInputPins;
- uint8_t baSourceID;
- uint8_t baSourcePin;
-
- uint8_t bNrOutputPins;
- uint8_t bInTerminalLink;
- uint8_t bOutTerminalLink;
- uint8_t bElCapsSize;
-
- uint16_t bmElementCaps;
- uint8_t iElement;
-} midi_desc_element_t;
-
-/// MIDI Element Descriptor with multiple pins
-#define midi_desc_element_n_t(input_num) \
- struct TU_ATTR_PACKED { \
- uint8_t bLength; \
- uint8_t bDescriptorType; \
- uint8_t bDescriptorSubType; \
- uint8_t bElementID; \
- uint8_t bNrInputPins; \
- struct TU_ATTR_PACKED { \
- uint8_t baSourceID; \
- uint8_t baSourcePin; \
- } pins[input_num]; \
- uint8_t bNrOutputPins; \
- uint8_t bInTerminalLink; \
- uint8_t bOutTerminalLink; \
- uint8_t bElCapsSize; \
- uint16_t bmElementCaps; \
- uint8_t iElement; \
- }
-
-/** @} */
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif
-
-/** @} */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CDC Communication Device Class (CDC) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_MIDI_H__ +#define _TUSB_MIDI_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Specific Descriptor +//--------------------------------------------------------------------+ + +typedef enum +{ + MIDI_CS_INTERFACE_HEADER = 0x01, + MIDI_CS_INTERFACE_IN_JACK = 0x02, + MIDI_CS_INTERFACE_OUT_JACK = 0x03, + MIDI_CS_INTERFACE_ELEMENT = 0x04, +} midi_cs_interface_subtype_t; + +typedef enum +{ + MIDI_CS_ENDPOINT_GENERAL = 0x01 +} midi_cs_endpoint_subtype_t; + +typedef enum +{ + MIDI_JACK_EMBEDDED = 0x01, + MIDI_JACK_EXTERNAL = 0x02 +} midi_jack_type_t; + +/// MIDI Interface Header Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal + uint16_t wTotalLength ; +} midi_desc_header_t; + +/// MIDI In Jack Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bJackType ; ///< Embedded or External + uint8_t bJackID ; ///< Unique ID for MIDI IN Jack + uint8_t iJack ; ///< string descriptor +} midi_desc_in_jack_t; + + +/// MIDI Out Jack Descriptor with single pin +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bJackType ; ///< Embedded or External + uint8_t bJackID ; ///< Unique ID for MIDI IN Jack + uint8_t bNrInputPins; + + uint8_t baSourceID; + uint8_t baSourcePin; + + uint8_t iJack ; ///< string descriptor +} midi_desc_out_jack_t ; + +/// MIDI Out Jack Descriptor with multiple pins +#define midi_desc_out_jack_n_t(input_num) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength ; \ + uint8_t bDescriptorType ; \ + uint8_t bDescriptorSubType ; \ + uint8_t bJackType ; \ + uint8_t bJackID ; \ + uint8_t bNrInputPins ; \ + struct TU_ATTR_PACKED { \ + uint8_t baSourceID; \ + uint8_t baSourcePin; \ + } pins[input_num]; \ + uint8_t iJack ; \ + } + +/// MIDI Element Descriptor +typedef struct TU_ATTR_PACKED +{ + uint8_t bLength ; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific + uint8_t bDescriptorSubType ; ///< Descriptor SubType + uint8_t bElementID; + + uint8_t bNrInputPins; + uint8_t baSourceID; + uint8_t baSourcePin; + + uint8_t bNrOutputPins; + uint8_t bInTerminalLink; + uint8_t bOutTerminalLink; + uint8_t bElCapsSize; + + uint16_t bmElementCaps; + uint8_t iElement; +} midi_desc_element_t; + +/// MIDI Element Descriptor with multiple pins +#define midi_desc_element_n_t(input_num) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint8_t bDescriptorSubType; \ + uint8_t bElementID; \ + uint8_t bNrInputPins; \ + struct TU_ATTR_PACKED { \ + uint8_t baSourceID; \ + uint8_t baSourcePin; \ + } pins[input_num]; \ + uint8_t bNrOutputPins; \ + uint8_t bInTerminalLink; \ + uint8_t bOutTerminalLink; \ + uint8_t bElCapsSize; \ + uint16_t bmElementCaps; \ + uint8_t iElement; \ + } + +/** @} */ + +#ifdef __cplusplus + } +#endif + +#endif + +/** @} */ diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index 0c68f8e71..ae6e3afe1 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -1,361 +1,361 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MIDI)
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "midi_device.h"
-#include "class/audio/audio.h"
-#include "device/usbd_pvt.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-typedef struct
-{
- uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
-
- /*------------- From this point, data is not cleared by bus reset -------------*/
- // FIFO
- tu_fifo_t rx_ff;
- tu_fifo_t tx_ff;
- uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
- uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
-
- #if CFG_FIFO_MUTEX
- osal_mutex_def_t rx_ff_mutex;
- osal_mutex_def_t tx_ff_mutex;
- #endif
-
- // We need to pack messages into words before queueing their transmission so buffer across write
- // calls.
- uint8_t message_buffer[4];
- uint8_t message_buffer_length;
- uint8_t message_target_length;
-
- // Endpoint Transfer buffer
- CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE];
- CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE];
-
-} midid_interface_t;
-
-#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
-
-bool tud_midi_n_mounted (uint8_t itf)
-{
- midid_interface_t* midi = &_midid_itf[itf];
- return midi->ep_in && midi->ep_out;
-}
-
-//--------------------------------------------------------------------+
-// READ API
-//--------------------------------------------------------------------+
-uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id)
-{
- (void) jack_id;
- return tu_fifo_count(&_midid_itf[itf].rx_ff);
-}
-
-uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize)
-{
- (void) jack_id;
- return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize);
-}
-
-void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id)
-{
- (void) jack_id;
- tu_fifo_clear(&_midid_itf[itf].rx_ff);
-}
-
-void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) {
- if (bufsize % 4 != 0) {
- return;
- }
-
- for(uint32_t i=0; i<bufsize; i += 4) {
- uint8_t header = buffer[i];
- // uint8_t cable_number = (header & 0xf0) >> 4;
- uint8_t code_index = header & 0x0f;
- // We always copy over the first byte.
- uint8_t count = 1;
- // Ignore subsequent bytes based on the code.
- if (code_index != 0x5 && code_index != 0xf) {
- count = 2;
- if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) {
- count = 3;
- }
- }
- tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count);
- }
-}
-
-
-//--------------------------------------------------------------------+
-// WRITE API
-//--------------------------------------------------------------------+
-
-static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index)
-{
- (void) itf_index;
-
- // skip if previous transfer not complete
- TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) );
-
- uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE);
- if (count > 0)
- {
- TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
- }
- return true;
-}
-
-uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
-{
- midid_interface_t* midi = &_midid_itf[itf];
- if (midi->itf_num == 0) {
- return 0;
- }
-
- uint32_t i = 0;
- while (i < bufsize) {
- uint8_t data = buffer[i];
- if (midi->message_buffer_length == 0) {
- uint8_t msg = data >> 4;
- midi->message_buffer[1] = data;
- midi->message_buffer_length = 2;
- // Check to see if we're still in a SysEx transmit.
- if (midi->message_buffer[0] == 0x4) {
- if (data == 0xf7) {
- midi->message_buffer[0] = 0x5;
- } else {
- midi->message_buffer_length = 4;
- }
- } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
- midi->message_buffer[0] = jack_id << 4 | msg;
- midi->message_target_length = 4;
- } else if (msg == 0xC || msg == 0xD) {
- midi->message_buffer[0] = jack_id << 4 | msg;
- midi->message_target_length = 3;
- } else if (msg == 0xf) {
- if (data == 0xf0) {
- midi->message_buffer[0] = 0x4;
- midi->message_target_length = 4;
- } else if (data == 0xf1 || data == 0xf3) {
- midi->message_buffer[0] = 0x2;
- midi->message_target_length = 3;
- } else if (data == 0xf2) {
- midi->message_buffer[0] = 0x3;
- midi->message_target_length = 4;
- } else {
- midi->message_buffer[0] = 0x5;
- midi->message_target_length = 2;
- }
- } else {
- // Pack individual bytes if we don't support packing them into words.
- midi->message_buffer[0] = jack_id << 4 | 0xf;
- midi->message_buffer[2] = 0;
- midi->message_buffer[3] = 0;
- midi->message_buffer_length = 2;
- midi->message_target_length = 2;
- }
- } else {
- midi->message_buffer[midi->message_buffer_length] = data;
- midi->message_buffer_length += 1;
- // See if this byte ends a SysEx.
- if (midi->message_buffer[0] == 0x4 && data == 0xf7) {
- midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1);
- midi->message_target_length = midi->message_buffer_length;
- }
- }
-
- if (midi->message_buffer_length == midi->message_target_length) {
- uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4);
- if (written < 4) {
- TU_ASSERT( written == 0 );
- break;
- }
- midi->message_buffer_length = 0;
- }
- i++;
- }
- maybe_transmit(midi, itf);
-
- return i;
-}
-
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
-void midid_init(void)
-{
- tu_memclr(_midid_itf, sizeof(_midid_itf));
-
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- midid_interface_t* midi = &_midid_itf[i];
-
- // config fifo
- tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true);
- tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true);
-
- #if CFG_FIFO_MUTEX
- tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
- tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
- #endif
- }
-}
-
-void midid_reset(uint8_t rhport)
-{
- (void) rhport;
-
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- midid_interface_t* midi = &_midid_itf[i];
- tu_memclr(midi, ITF_MEM_RESET_SIZE);
- tu_fifo_clear(&midi->rx_ff);
- tu_fifo_clear(&midi->tx_ff);
- }
-}
-
-bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length)
-{
- // For now handle the audio control interface as well.
- if ( AUDIO_SUBCLASS_CONTROL == p_interface_desc->bInterfaceSubClass) {
- uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
-
- // Skip over the class specific descriptor.
- (*p_length) += tu_desc_len(p_desc);
- p_desc = tu_desc_next(p_desc);
- return true;
- }
-
- TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == p_interface_desc->bInterfaceSubClass &&
- AUDIO_PROTOCOL_V1 == p_interface_desc->bInterfaceProtocol );
-
- // Find available interface
- midid_interface_t * p_midi = NULL;
- for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
- {
- if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
- {
- p_midi = &_midid_itf[i];
- break;
- }
- }
-
- p_midi->itf_num = p_interface_desc->bInterfaceNumber;
-
- uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc );
- (*p_length) = sizeof(tusb_desc_interface_t);
-
- uint8_t found_endpoints = 0;
- while (found_endpoints < p_interface_desc->bNumEndpoints)
- {
- if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
- {
- TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false);
- uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- p_midi->ep_in = ep_addr;
- } else {
- p_midi->ep_out = ep_addr;
- }
-
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- found_endpoints += 1;
- }
- (*p_length) += p_desc[DESC_OFFSET_LEN];
- p_desc = tu_desc_next(p_desc);
- }
-
- // Prepare for incoming data
- TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false);
-
- return true;
-}
-
-bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- (void) rhport;
- (void) p_request;
- return true;
-}
-
-bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- (void) rhport;
- (void) p_request;
-
- // driver doesn't support any request yet
- return false;
-}
-
-bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
- (void) result;
-
- uint8_t itf = 0;
- midid_interface_t* p_midi = _midid_itf;
-
- for ( ; ; itf++, p_midi++)
- {
- if (itf >= TU_ARRAY_SIZE(_midid_itf)) return false;
-
- if ( ep_addr == p_midi->ep_out ) break;
- }
-
- // receive new data
- if ( ep_addr == p_midi->ep_out )
- {
- midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes);
-
- // prepare for next
- TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false );
- } else if ( ep_addr == p_midi->ep_in ) {
- maybe_transmit(p_midi, itf);
- }
-
- // nothing to do with in and notif endpoint
-
- return TUSB_ERROR_NONE;
-}
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MIDI) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "midi_device.h" +#include "class/audio/audio.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE]; + + #if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; + #endif + + // We need to pack messages into words before queueing their transmission so buffer across write + // calls. + uint8_t message_buffer[4]; + uint8_t message_buffer_length; + uint8_t message_target_length; + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EPSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EPSIZE]; + +} midid_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI]; + +bool tud_midi_n_mounted (uint8_t itf) +{ + midid_interface_t* midi = &_midid_itf[itf]; + return midi->ep_in && midi->ep_out; +} + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_midi_n_available(uint8_t itf, uint8_t jack_id) +{ + (void) jack_id; + return tu_fifo_count(&_midid_itf[itf].rx_ff); +} + +uint32_t tud_midi_n_read(uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize) +{ + (void) jack_id; + return tu_fifo_read_n(&_midid_itf[itf].rx_ff, buffer, bufsize); +} + +void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id) +{ + (void) jack_id; + tu_fifo_clear(&_midid_itf[itf].rx_ff); +} + +void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bufsize) { + if (bufsize % 4 != 0) { + return; + } + + for(uint32_t i=0; i<bufsize; i += 4) { + uint8_t header = buffer[i]; + // uint8_t cable_number = (header & 0xf0) >> 4; + uint8_t code_index = header & 0x0f; + // We always copy over the first byte. + uint8_t count = 1; + // Ignore subsequent bytes based on the code. + if (code_index != 0x5 && code_index != 0xf) { + count = 2; + if (code_index != 0x2 && code_index != 0x6 && code_index != 0xc && code_index != 0xd) { + count = 3; + } + } + tu_fifo_write_n(&midi->rx_ff, &buffer[i + 1], count); + } +} + + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ + +static bool maybe_transmit(midid_interface_t* midi, uint8_t itf_index) +{ + (void) itf_index; + + // skip if previous transfer not complete + TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) ); + + uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EPSIZE); + if (count > 0) + { + TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) ); + } + return true; +} + +uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) +{ + midid_interface_t* midi = &_midid_itf[itf]; + if (midi->itf_num == 0) { + return 0; + } + + uint32_t i = 0; + while (i < bufsize) { + uint8_t data = buffer[i]; + if (midi->message_buffer_length == 0) { + uint8_t msg = data >> 4; + midi->message_buffer[1] = data; + midi->message_buffer_length = 2; + // Check to see if we're still in a SysEx transmit. + if (midi->message_buffer[0] == 0x4) { + if (data == 0xf7) { + midi->message_buffer[0] = 0x5; + } else { + midi->message_buffer_length = 4; + } + } else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) { + midi->message_buffer[0] = jack_id << 4 | msg; + midi->message_target_length = 4; + } else if (msg == 0xC || msg == 0xD) { + midi->message_buffer[0] = jack_id << 4 | msg; + midi->message_target_length = 3; + } else if (msg == 0xf) { + if (data == 0xf0) { + midi->message_buffer[0] = 0x4; + midi->message_target_length = 4; + } else if (data == 0xf1 || data == 0xf3) { + midi->message_buffer[0] = 0x2; + midi->message_target_length = 3; + } else if (data == 0xf2) { + midi->message_buffer[0] = 0x3; + midi->message_target_length = 4; + } else { + midi->message_buffer[0] = 0x5; + midi->message_target_length = 2; + } + } else { + // Pack individual bytes if we don't support packing them into words. + midi->message_buffer[0] = jack_id << 4 | 0xf; + midi->message_buffer[2] = 0; + midi->message_buffer[3] = 0; + midi->message_buffer_length = 2; + midi->message_target_length = 2; + } + } else { + midi->message_buffer[midi->message_buffer_length] = data; + midi->message_buffer_length += 1; + // See if this byte ends a SysEx. + if (midi->message_buffer[0] == 0x4 && data == 0xf7) { + midi->message_buffer[0] = 0x4 + (midi->message_buffer_length - 1); + midi->message_target_length = midi->message_buffer_length; + } + } + + if (midi->message_buffer_length == midi->message_target_length) { + uint16_t written = tu_fifo_write_n(&midi->tx_ff, midi->message_buffer, 4); + if (written < 4) { + TU_ASSERT( written == 0 ); + break; + } + midi->message_buffer_length = 0; + } + i++; + } + maybe_transmit(midi, itf); + + return i; +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void midid_init(void) +{ + tu_memclr(_midid_itf, sizeof(_midid_itf)); + + for(uint8_t i=0; i<CFG_TUD_MIDI; i++) + { + midid_interface_t* midi = &_midid_itf[i]; + + // config fifo + tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, true); + tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, true); + + #if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex)); + tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex)); + #endif + } +} + +void midid_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; i<CFG_TUD_MIDI; i++) + { + midid_interface_t* midi = &_midid_itf[i]; + tu_memclr(midi, ITF_MEM_RESET_SIZE); + tu_fifo_clear(&midi->rx_ff); + tu_fifo_clear(&midi->tx_ff); + } +} + +bool midid_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length) +{ + // For now handle the audio control interface as well. + if ( AUDIO_SUBCLASS_CONTROL == p_interface_desc->bInterfaceSubClass) { + uint8_t const * p_desc = tu_desc_next ( (uint8_t const *) p_interface_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + // Skip over the class specific descriptor. + (*p_length) += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + return true; + } + + TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == p_interface_desc->bInterfaceSubClass && + AUDIO_PROTOCOL_V1 == p_interface_desc->bInterfaceProtocol ); + + // Find available interface + midid_interface_t * p_midi = NULL; + for(uint8_t i=0; i<CFG_TUD_MIDI; i++) + { + if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 ) + { + p_midi = &_midid_itf[i]; + break; + } + } + + p_midi->itf_num = p_interface_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( (uint8_t const *) p_interface_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + + uint8_t found_endpoints = 0; + while (found_endpoints < p_interface_desc->bNumEndpoints) + { + if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), false); + uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) { + p_midi->ep_in = ep_addr; + } else { + p_midi->ep_out = ep_addr; + } + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + found_endpoints += 1; + } + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + // Prepare for incoming data + TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false); + + return true; +} + +bool midid_control_complete(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void) rhport; + (void) p_request; + return true; +} + +bool midid_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void) rhport; + (void) p_request; + + // driver doesn't support any request yet + return false; +} + +bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t itf = 0; + midid_interface_t* p_midi = _midid_itf; + + for ( ; ; itf++, p_midi++) + { + if (itf >= TU_ARRAY_SIZE(_midid_itf)) return false; + + if ( ep_addr == p_midi->ep_out ) break; + } + + // receive new data + if ( ep_addr == p_midi->ep_out ) + { + midi_rx_done_cb(p_midi, p_midi->epout_buf, xferred_bytes); + + // prepare for next + TU_ASSERT( usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EPSIZE), false ); + } else if ( ep_addr == p_midi->ep_in ) { + maybe_transmit(p_midi, itf); + } + + // nothing to do with in and notif endpoint + + return true; +} + +#endif diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h index 6d9b292a3..fbf4bce0b 100644 --- a/src/class/midi/midi_device.h +++ b/src/class/midi/midi_device.h @@ -1,138 +1,138 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_MIDI_DEVICE_H_
-#define _TUSB_MIDI_DEVICE_H_
-
-#include "common/tusb_common.h"
-#include "device/usbd.h"
-
-#include "class/audio/audio.h"
-#include "midi.h"
-
-//--------------------------------------------------------------------+
-// Class Driver Configuration
-//--------------------------------------------------------------------+
-#ifndef CFG_TUD_MIDI_EPSIZE
-#define CFG_TUD_MIDI_EPSIZE 64
-#endif
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/** \addtogroup MIDI_Serial Serial
- * @{
- * \defgroup MIDI_Serial_Device Device
- * @{ */
-
-//--------------------------------------------------------------------+
-// Application API (Multiple Interfaces)
-// CFG_TUD_MIDI > 1
-//--------------------------------------------------------------------+
-bool tud_midi_n_mounted (uint8_t itf);
-uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id);
-uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize);
-void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id);
-uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
-
-static inline
-uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3);
-
-//--------------------------------------------------------------------+
-// Application API (Interface0)
-//--------------------------------------------------------------------+
-static inline bool tud_midi_mounted (void);
-static inline uint32_t tud_midi_available (void);
-static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize);
-static inline void tud_midi_read_flush (void);
-static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize);
-static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3);
-
-//--------------------------------------------------------------------+
-// Application Callback API (weak is optional)
-//--------------------------------------------------------------------+
-TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-
-static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3)
-{
- uint8_t msg[3] = { b1, b2, b3 };
- return tud_midi_n_write(itf, jack_id, msg, 3);
-}
-
-static inline bool tud_midi_mounted (void)
-{
- return tud_midi_n_mounted(0);
-}
-
-static inline uint32_t tud_midi_available (void)
-{
- return tud_midi_n_available(0, 0);
-}
-
-static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize)
-{
- return tud_midi_n_read(0, 0, buffer, bufsize);
-}
-
-static inline void tud_midi_read_flush (void)
-{
- tud_midi_n_read_flush(0, 0);
-}
-
-static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
-{
- return tud_midi_n_write(0, jack_id, buffer, bufsize);
-}
-
-static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3)
-{
- uint8_t msg[3] = { b1, b2, b3 };
- return tud_midi_write(jack_id, msg, 3);
-}
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-void midid_init (void);
-void midid_reset (uint8_t rhport);
-bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request);
-bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request);
-bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_MIDI_DEVICE_H_ */
-
-/** @} */
-/** @} */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_MIDI_DEVICE_H_ +#define _TUSB_MIDI_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" + +#include "class/audio/audio.h" +#include "midi.h" + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#ifndef CFG_TUD_MIDI_EPSIZE +#define CFG_TUD_MIDI_EPSIZE 64 +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup MIDI_Serial Serial + * @{ + * \defgroup MIDI_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Interfaces) +// CFG_TUD_MIDI > 1 +//--------------------------------------------------------------------+ +bool tud_midi_n_mounted (uint8_t itf); +uint32_t tud_midi_n_available (uint8_t itf, uint8_t jack_id); +uint32_t tud_midi_n_read (uint8_t itf, uint8_t jack_id, void* buffer, uint32_t bufsize); +void tud_midi_n_read_flush (uint8_t itf, uint8_t jack_id); +uint32_t tud_midi_n_write (uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); + +static inline +uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); + +//--------------------------------------------------------------------+ +// Application API (Interface0) +//--------------------------------------------------------------------+ +static inline bool tud_midi_mounted (void); +static inline uint32_t tud_midi_available (void); +static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize); +static inline void tud_midi_read_flush (void); +static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize); +static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ +TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ + +static inline uint32_t tud_midi_n_write24 (uint8_t itf, uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) +{ + uint8_t msg[3] = { b1, b2, b3 }; + return tud_midi_n_write(itf, jack_id, msg, 3); +} + +static inline bool tud_midi_mounted (void) +{ + return tud_midi_n_mounted(0); +} + +static inline uint32_t tud_midi_available (void) +{ + return tud_midi_n_available(0, 0); +} + +static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize) +{ + return tud_midi_n_read(0, 0, buffer, bufsize); +} + +static inline void tud_midi_read_flush (void) +{ + tud_midi_n_read_flush(0, 0); +} + +static inline uint32_t tud_midi_write (uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize) +{ + return tud_midi_n_write(0, jack_id, buffer, bufsize); +} + +static inline uint32_t tudi_midi_write24 (uint8_t jack_id, uint8_t b1, uint8_t b2, uint8_t b3) +{ + uint8_t msg[3] = { b1, b2, b3 }; + return tud_midi_write(jack_id, msg, 3); +} + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void midid_init (void); +void midid_reset (uint8_t rhport); +bool midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool midid_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool midid_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MIDI_DEVICE_H_ */ + +/** @} */ +/** @} */ diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h index d461513fc..cba8066b7 100644 --- a/src/class/msc/msc.h +++ b/src/class/msc/msc.h @@ -1,392 +1,392 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-/** \ingroup group_class
- * \defgroup ClassDriver_MSC MassStorage (MSC)
- * @{ */
-
-/** \defgroup ClassDriver_MSC_Common Common Definitions
- * @{ */
-
-#ifndef _TUSB_MSC_H_
-#define _TUSB_MSC_H_
-
-#include "common/tusb_common.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Mass Storage Class Constant
-//--------------------------------------------------------------------+
-/// MassStorage Subclass
-typedef enum
-{
- MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D
- MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device
- MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device
- MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device
- MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device
- MSC_SUBCLASS_SCSI ///< SCSI transparent command set
-}msc_subclass_type_t;
-
-enum {
- MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian)
- MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian)
-};
-
-/// \brief MassStorage Protocol.
-/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy
-typedef enum
-{
- MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
- MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt)
- MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport
-}msc_protocol_type_t;
-
-/// MassStorage Class-Specific Control Request
-typedef enum
-{
- MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15
- MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
-}msc_request_type_t;
-
-/// \brief Command Block Status Values
-/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed
-/// successfully. A non-zero value shall indicate a failure during command execution according to the following
-typedef enum
-{
- MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED
- MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED
- MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR
-}msc_csw_status_t;
-
-/// Command Block Wrapper
-typedef struct TU_ATTR_PACKED
-{
- uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
- uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
- uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags.
- uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host.
- uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero.
- uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16
- uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block
-}msc_cbw_t;
-
-TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
-
-/// Command Status Wrapper
-typedef struct TU_ATTR_PACKED
-{
- uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
- uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
- uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
- uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
-}msc_csw_t;
-
-TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
-
-//--------------------------------------------------------------------+
-// SCSI Constant
-//--------------------------------------------------------------------+
-
-/// SCSI Command Operation Code
-typedef enum
-{
- SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation.
- SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device.
- SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters.
- SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command.
- SCSI_CMD_START_STOP_UNIT = 0x1B,
- SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
- SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device.
- SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
- SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
- SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
- SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them.
-}scsi_cmd_type_t;
-
-/// SCSI Sense Key
-typedef enum
-{
- SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
- SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive.
- SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
- SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
- SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
- SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters
- SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset.
- SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed.
- SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key.
- SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
- SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
- SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
- SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium.
-}scsi_sense_key_type_t;
-
-//--------------------------------------------------------------------+
-// SCSI Primary Command (SPC-4)
-//--------------------------------------------------------------------+
-
-/// SCSI Test Unit Ready Command
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
- uint8_t lun ; ///< Logical Unit
- uint8_t reserved[3] ;
- uint8_t control ;
-} scsi_test_unit_ready_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
-
-/// SCSI Inquiry Command
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
- uint8_t reserved1 ;
- uint8_t page_code ;
- uint8_t reserved2 ;
- uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
- uint8_t control ;
-} scsi_inquiry_t, scsi_request_sense_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
-
-/// SCSI Inquiry Response Data
-typedef struct TU_ATTR_PACKED
-{
- uint8_t peripheral_device_type : 5;
- uint8_t peripheral_qualifier : 3;
-
- uint8_t : 7;
- uint8_t is_removable : 1;
-
- uint8_t version;
-
- uint8_t response_data_format : 4;
- uint8_t hierarchical_support : 1;
- uint8_t normal_aca : 1;
- uint8_t : 2;
-
- uint8_t additional_length;
-
- uint8_t protect : 1;
- uint8_t : 2;
- uint8_t third_party_copy : 1;
- uint8_t target_port_group_support : 2;
- uint8_t access_control_coordinator : 1;
- uint8_t scc_support : 1;
-
- uint8_t addr16 : 1;
- uint8_t : 3;
- uint8_t multi_port : 1;
- uint8_t : 1; // vendor specific
- uint8_t enclosure_service : 1;
- uint8_t : 1;
-
- uint8_t : 1; // vendor specific
- uint8_t cmd_que : 1;
- uint8_t : 2;
- uint8_t sync : 1;
- uint8_t wbus16 : 1;
- uint8_t : 2;
-
- uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product.
- uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor.
- uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor.
-} scsi_inquiry_resp_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct");
-
-
-typedef struct TU_ATTR_PACKED
-{
- uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
- uint8_t valid : 1;
-
- uint8_t reserved;
-
- uint8_t sense_key : 4;
- uint8_t : 1;
- uint8_t ili : 1; ///< Incorrect length indicator
- uint8_t end_of_medium : 1;
- uint8_t filemark : 1;
-
- uint32_t information;
- uint8_t add_sense_len;
- uint32_t command_specific_info;
- uint8_t add_sense_code;
- uint8_t add_sense_qualifier;
- uint8_t field_replaceable_unit_code;
-
- uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
-
-} scsi_sense_fixed_resp_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct");
-
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
-
- uint8_t : 3;
- uint8_t disable_block_descriptor : 1;
- uint8_t : 0;
-
- uint8_t page_code : 6;
- uint8_t page_control : 2;
-
- uint8_t subpage_code;
- uint8_t alloc_length;
- uint8_t control;
-} scsi_mode_sense6_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
-
-// This is only a Mode parameter header(6).
-typedef struct TU_ATTR_PACKED
-{
- uint8_t data_len;
- uint8_t medium_type;
-
- uint8_t reserved : 7;
- bool write_protected : 1;
-
- uint8_t block_descriptor_len;
-} scsi_mode_sense6_resp_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct");
-
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
- uint8_t reserved[3];
- uint8_t prohibit_removal;
- uint8_t control;
-} scsi_prevent_allow_medium_removal_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
-
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code;
-
- uint8_t immded : 1;
- uint8_t : 7;
-
- uint8_t TU_RESERVED;
-
- uint8_t power_condition_mod : 4;
- uint8_t : 4;
-
- uint8_t start : 1;
- uint8_t load_eject : 1;
- uint8_t no_flush : 1;
- uint8_t : 1;
- uint8_t power_condition : 4;
-
- uint8_t control;
-} scsi_start_stop_unit_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct");
-
-//--------------------------------------------------------------------+
-// SCSI MMC
-//--------------------------------------------------------------------+
-/// SCSI Read Format Capacity: Write Capacity
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code;
- uint8_t reserved[6];
- uint16_t alloc_length;
- uint8_t control;
-} scsi_read_format_capacity_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
-
-typedef struct TU_ATTR_PACKED{
- uint8_t reserved[3];
- uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
-
- uint32_t block_num; /// Number of Logical Blocks
- uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
-
- uint8_t reserved2;
- uint16_t block_size_u16;
-
-} scsi_read_format_capacity_data_t;
-
-TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct");
-
-//--------------------------------------------------------------------+
-// SCSI Block Command (SBC-3)
-// NOTE: All data in SCSI command are in Big Endian
-//--------------------------------------------------------------------+
-
-/// SCSI Read Capacity 10 Command: Read Capacity
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
- uint8_t reserved1 ;
- uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
- uint16_t reserved2 ;
- uint8_t partial_medium_indicator ;
- uint8_t control ;
-} scsi_read_capacity10_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct");
-
-/// SCSI Read Capacity 10 Response Data
-typedef struct {
- uint32_t last_lba ; ///< The last Logical Block Address of the device
- uint32_t block_size ; ///< Block size in bytes
-} scsi_read_capacity10_resp_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct");
-
-/// SCSI Read 10 Command
-typedef struct TU_ATTR_PACKED
-{
- uint8_t cmd_code ; ///< SCSI OpCode
- uint8_t reserved ; // has LUN according to wiki
- uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
- uint8_t reserved2 ;
- uint16_t block_count ; ///< Number of Blocks used by this command
- uint8_t control ;
-} scsi_read10_t, scsi_write10_t;
-
-TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
-TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_MSC_H_ */
-
-/// @}
-/// @}
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_MSC MassStorage (MSC) + * @{ */ + +/** \defgroup ClassDriver_MSC_Common Common Definitions + * @{ */ + +#ifndef _TUSB_MSC_H_ +#define _TUSB_MSC_H_ + +#include "common/tusb_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Mass Storage Class Constant +//--------------------------------------------------------------------+ +/// MassStorage Subclass +typedef enum +{ + MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D + MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device + MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device + MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device + MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device + MSC_SUBCLASS_SCSI ///< SCSI transparent command set +}msc_subclass_type_t; + +enum { + MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian) + MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian) +}; + +/// \brief MassStorage Protocol. +/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy +typedef enum +{ + MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt) + MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt) + MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport +}msc_protocol_type_t; + +/// MassStorage Class-Specific Control Request +typedef enum +{ + MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15 + MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host. +}msc_request_type_t; + +/// \brief Command Block Status Values +/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed +/// successfully. A non-zero value shall indicate a failure during command execution according to the following +typedef enum +{ + MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED + MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED + MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR +}msc_csw_status_t; + +/// Command Block Wrapper +typedef struct TU_ATTR_PACKED +{ + uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW. + uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW. + uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags. + uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host. + uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero. + uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16 + uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block +}msc_cbw_t; + +TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct"); + +/// Command Status Wrapper +typedef struct TU_ATTR_PACKED +{ + uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW. + uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW. + uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device + uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t +}msc_csw_t; + +TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI Constant +//--------------------------------------------------------------------+ + +/// SCSI Command Operation Code +typedef enum +{ + SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation. + SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device. + SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters. + SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command. + SCSI_CMD_START_STOP_UNIT = 0x1B, + SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E, + SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device. + SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device. + SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed + SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer. + SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them. +}scsi_cmd_type_t; + +/// SCSI Sense Key +typedef enum +{ + SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command + SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive. + SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed. + SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition. + SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test. + SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters + SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset. + SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed. + SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key. + SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command. + SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison. + SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium. + SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium. +}scsi_sense_key_type_t; + +//--------------------------------------------------------------------+ +// SCSI Primary Command (SPC-4) +//--------------------------------------------------------------------+ + +/// SCSI Test Unit Ready Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY + uint8_t lun ; ///< Logical Unit + uint8_t reserved[3] ; + uint8_t control ; +} scsi_test_unit_ready_t; + +TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct"); + +/// SCSI Inquiry Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY + uint8_t reserved1 ; + uint8_t page_code ; + uint8_t reserved2 ; + uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred. + uint8_t control ; +} scsi_inquiry_t, scsi_request_sense_t; + +TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct"); + +/// SCSI Inquiry Response Data +typedef struct TU_ATTR_PACKED +{ + uint8_t peripheral_device_type : 5; + uint8_t peripheral_qualifier : 3; + + uint8_t : 7; + uint8_t is_removable : 1; + + uint8_t version; + + uint8_t response_data_format : 4; + uint8_t hierarchical_support : 1; + uint8_t normal_aca : 1; + uint8_t : 2; + + uint8_t additional_length; + + uint8_t protect : 1; + uint8_t : 2; + uint8_t third_party_copy : 1; + uint8_t target_port_group_support : 2; + uint8_t access_control_coordinator : 1; + uint8_t scc_support : 1; + + uint8_t addr16 : 1; + uint8_t : 3; + uint8_t multi_port : 1; + uint8_t : 1; // vendor specific + uint8_t enclosure_service : 1; + uint8_t : 1; + + uint8_t : 1; // vendor specific + uint8_t cmd_que : 1; + uint8_t : 2; + uint8_t sync : 1; + uint8_t wbus16 : 1; + uint8_t : 2; + + uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product. + uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor. + uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor. +} scsi_inquiry_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct"); + + +typedef struct TU_ATTR_PACKED +{ + uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format + uint8_t valid : 1; + + uint8_t reserved; + + uint8_t sense_key : 4; + uint8_t : 1; + uint8_t ili : 1; ///< Incorrect length indicator + uint8_t end_of_medium : 1; + uint8_t filemark : 1; + + uint32_t information; + uint8_t add_sense_len; + uint32_t command_specific_info; + uint8_t add_sense_code; + uint8_t add_sense_qualifier; + uint8_t field_replaceable_unit_code; + + uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout + +} scsi_sense_fixed_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6 + + uint8_t : 3; + uint8_t disable_block_descriptor : 1; + uint8_t : 0; + + uint8_t page_code : 6; + uint8_t page_control : 2; + + uint8_t subpage_code; + uint8_t alloc_length; + uint8_t control; +} scsi_mode_sense6_t; + +TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct"); + +// This is only a Mode parameter header(6). +typedef struct TU_ATTR_PACKED +{ + uint8_t data_len; + uint8_t medium_type; + + uint8_t reserved : 7; + bool write_protected : 1; + + uint8_t block_descriptor_len; +} scsi_mode_sense6_resp_t; + +TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL + uint8_t reserved[3]; + uint8_t prohibit_removal; + uint8_t control; +} scsi_prevent_allow_medium_removal_t; + +TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct"); + +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; + + uint8_t immded : 1; + uint8_t : 7; + + uint8_t TU_RESERVED; + + uint8_t power_condition_mod : 4; + uint8_t : 4; + + uint8_t start : 1; + uint8_t load_eject : 1; + uint8_t no_flush : 1; + uint8_t : 1; + uint8_t power_condition : 4; + + uint8_t control; +} scsi_start_stop_unit_t; + +TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI MMC +//--------------------------------------------------------------------+ +/// SCSI Read Format Capacity: Write Capacity +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code; + uint8_t reserved[6]; + uint16_t alloc_length; + uint8_t control; +} scsi_read_format_capacity_t; + +TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct"); + +typedef struct TU_ATTR_PACKED{ + uint8_t reserved[3]; + uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it. + + uint32_t block_num; /// Number of Logical Blocks + uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present + + uint8_t reserved2; + uint16_t block_size_u16; + +} scsi_read_format_capacity_data_t; + +TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct"); + +//--------------------------------------------------------------------+ +// SCSI Block Command (SBC-3) +// NOTE: All data in SCSI command are in Big Endian +//--------------------------------------------------------------------+ + +/// SCSI Read Capacity 10 Command: Read Capacity +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10 + uint8_t reserved1 ; + uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command + uint16_t reserved2 ; + uint8_t partial_medium_indicator ; + uint8_t control ; +} scsi_read_capacity10_t; + +TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct"); + +/// SCSI Read Capacity 10 Response Data +typedef struct { + uint32_t last_lba ; ///< The last Logical Block Address of the device + uint32_t block_size ; ///< Block size in bytes +} scsi_read_capacity10_resp_t; + +TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct"); + +/// SCSI Read 10 Command +typedef struct TU_ATTR_PACKED +{ + uint8_t cmd_code ; ///< SCSI OpCode + uint8_t reserved ; // has LUN according to wiki + uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command + uint8_t reserved2 ; + uint16_t block_count ; ///< Number of Blocks used by this command + uint8_t control ; +} scsi_read10_t, scsi_write10_t; + +TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct"); +TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct"); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_H_ */ + +/// @} +/// @} diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index bef5d599b..3ff0eba92 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -1,700 +1,700 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC)
-
-#include "common/tusb_common.h"
-#include "msc_device.h"
-#include "device/usbd_pvt.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-enum
-{
- MSC_STAGE_CMD = 0,
- MSC_STAGE_DATA,
- MSC_STAGE_STATUS,
- MSC_STAGE_STATUS_SENT
-};
-
-typedef struct
-{
- // TODO optimize alignment
- CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
- CFG_TUSB_MEM_ALIGN msc_csw_t csw;
-
- uint8_t itf_num;
- uint8_t ep_in;
- uint8_t ep_out;
-
- // Bulk Only Transfer (BOT) Protocol
- uint8_t stage;
- uint32_t total_len;
- uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
-
- // Sense Response Data
- uint8_t sense_key;
- uint8_t add_sense_code;
- uint8_t add_sense_qualifier;
-}mscd_interface_t;
-
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
-
-static inline uint32_t rdwr10_get_lba(uint8_t const command[])
-{
- // read10 & write10 has the same format
- scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
-
- // copy first to prevent mis-aligned access
- uint32_t lba;
- memcpy(&lba, &p_rdwr10->lba, 4);
-
- // lba is in Big Endian format
- return tu_ntohl(lba);
-}
-
-static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
-{
- // read10 & write10 has the same format
- scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
-
- // copy first to prevent mis-aligned access
- uint16_t block_count;
- memcpy(&block_count, &p_rdwr10->block_count, 2);
-
- return tu_ntohs(block_count);
-}
-
-//--------------------------------------------------------------------+
-// Debug
-//--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= 2
-
-static lookup_entry_t const _msc_scsi_cmd_lookup[] =
-{
- { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
- { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
- { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
- { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
- { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
- { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" },
- { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
- { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
- { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
- { .key = SCSI_CMD_READ_10 , .data = "Read10" },
- { .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
-};
-
-static lookup_table_t const _msc_scsi_cmd_table =
-{
- .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
- .items = _msc_scsi_cmd_lookup
-};
-
-#endif
-
-//--------------------------------------------------------------------+
-// APPLICATION API
-//--------------------------------------------------------------------+
-bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
-{
- (void) lun;
-
- _mscd_itf.sense_key = sense_key;
- _mscd_itf.add_sense_code = add_sense_code;
- _mscd_itf.add_sense_qualifier = add_sense_qualifier;
-
- return true;
-}
-
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
-void mscd_init(void)
-{
- tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
-}
-
-void mscd_reset(uint8_t rhport)
-{
- (void) rhport;
- tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
-}
-
-bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
-{
- // only support SCSI's BOT protocol
- TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
-
- mscd_interface_t * p_msc = &_mscd_itf;
-
- // Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
-
- p_msc->itf_num = itf_desc->bInterfaceNumber;
- (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
-
- // Prepare for Command Block Wrapper
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
-
- return true;
-}
-
-// Handle class control request
-// return false to stall control endpoint (e.g unsupported request)
-bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- // Handle class request only
- TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
- switch ( p_request->bRequest )
- {
- case MSC_REQ_RESET:
- // TODO: Actually reset interface.
- tud_control_status(rhport, p_request);
- break;
-
- case MSC_REQ_GET_MAX_LUN:
- {
- uint8_t maxlun = 1;
- if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
- TU_VERIFY(maxlun);
-
- // MAX LUN is minus 1 by specs
- maxlun--;
-
- tud_control_xfer(rhport, p_request, &maxlun, 1);
- }
- break;
-
- default: return false; // stall unsupported request
- }
-
- return true;
-}
-
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
- (void) request;
-
- // nothing to do
- return true;
-}
-
-// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
-// In case of a failed status, sense key must be set for reason of failure
-int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
- (void) bufsize; // TODO refractor later
- int32_t resplen;
-
- switch ( scsi_cmd[0] )
- {
- case SCSI_CMD_TEST_UNIT_READY:
- resplen = 0;
- if ( !tud_msc_test_unit_ready_cb(lun) )
- {
- // Failed status response
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- break;
-
- case SCSI_CMD_START_STOP_UNIT:
- resplen = 0;
-
- if (tud_msc_start_stop_cb)
- {
- scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
- if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
- {
- // Failed status response
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- }
- break;
-
- case SCSI_CMD_READ_CAPACITY_10:
- {
- uint32_t block_count;
- uint32_t block_size;
- uint16_t block_size_u16;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
- block_size = (uint32_t) block_size_u16;
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- scsi_read_capacity10_resp_t read_capa10;
-
- read_capa10.last_lba = tu_htonl(block_count-1);
- read_capa10.block_size = tu_htonl(block_size);
-
- resplen = sizeof(read_capa10);
- memcpy(buffer, &read_capa10, resplen);
- }
- }
- break;
-
- case SCSI_CMD_READ_FORMAT_CAPACITY:
- {
- scsi_read_format_capacity_data_t read_fmt_capa =
- {
- .list_length = 8,
- .block_num = 0,
- .descriptor_type = 2, // formatted media
- .block_size_u16 = 0
- };
-
- uint32_t block_count;
- uint16_t block_size;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size);
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- read_fmt_capa.block_num = tu_htonl(block_count);
- read_fmt_capa.block_size_u16 = tu_htons(block_size);
-
- resplen = sizeof(read_fmt_capa);
- memcpy(buffer, &read_fmt_capa, resplen);
- }
- }
- break;
-
- case SCSI_CMD_INQUIRY:
- {
- scsi_inquiry_resp_t inquiry_rsp =
- {
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- };
-
- // vendor_id, product_id, product_rev is space padded string
- memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
- memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
- memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
-
- tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
-
- resplen = sizeof(inquiry_rsp);
- memcpy(buffer, &inquiry_rsp, resplen);
- }
- break;
-
- case SCSI_CMD_MODE_SENSE_6:
- {
- scsi_mode_sense6_resp_t mode_resp =
- {
- .data_len = 3,
- .medium_type = 0,
- .write_protected = false,
- .reserved = 0,
- .block_descriptor_len = 0 // no block descriptor are included
- };
-
- bool writable = true;
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(lun);
- }
- mode_resp.write_protected = !writable;
-
- resplen = sizeof(mode_resp);
- memcpy(buffer, &mode_resp, resplen);
- }
- break;
-
- case SCSI_CMD_REQUEST_SENSE:
- {
- scsi_sense_fixed_resp_t sense_rsp =
- {
- .response_code = 0x70,
- .valid = 1
- };
-
- sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
-
- sense_rsp.sense_key = _mscd_itf.sense_key;
- sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
- sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
-
- resplen = sizeof(sense_rsp);
- memcpy(buffer, &sense_rsp, resplen);
-
- // Clear sense data after copy
- tud_msc_set_sense(lun, 0, 0, 0);
- }
- break;
-
- default: resplen = -1; break;
- }
-
- return resplen;
-}
-
-bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
- mscd_interface_t* p_msc = &_mscd_itf;
- msc_cbw_t const * p_cbw = &p_msc->cbw;
- msc_csw_t * p_csw = &p_msc->csw;
-
- switch (p_msc->stage)
- {
- case MSC_STAGE_CMD:
- //------------- new CBW received -------------//
- // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
- if(ep_addr != p_msc->ep_out) return true;
-
- TU_ASSERT( event == XFER_RESULT_SUCCESS &&
- xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
-
- TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
- // TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
-
- p_csw->signature = MSC_CSW_SIGNATURE;
- p_csw->tag = p_cbw->tag;
- p_csw->data_residue = 0;
-
- /*------------- Parse command and prepare DATA -------------*/
- p_msc->stage = MSC_STAGE_DATA;
- p_msc->total_len = p_cbw->total_bytes;
- p_msc->xferred_len = 0;
-
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
- proc_read10_cmd(rhport, p_msc);
- }
- else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
- {
- proc_write10_cmd(rhport, p_msc);
- }
- else
- {
- // For other SCSI commands
- // 1. OUT : queue transfer (invoke app callback after done)
- // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
- if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) )
- {
- // queue transfer
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
- }else
- {
- int32_t resplen;
-
- // First process if it is a built-in commands
- resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
-
- // Not built-in, invoke user callback
- if ( (resplen < 0) && (p_msc->sense_key == 0) )
- {
- resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
- }
-
- if ( resplen < 0 )
- {
- p_msc->total_len = 0;
- p_csw->status = MSC_CSW_STATUS_FAILED;
- p_msc->stage = MSC_STAGE_STATUS;
-
- // failed but senskey is not set: default to Illegal Request
- if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
-
- // Stall bulk In if needed
- if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
- }
- else
- {
- p_msc->total_len = (uint32_t) resplen;
- p_csw->status = MSC_CSW_STATUS_PASSED;
-
- if (p_msc->total_len)
- {
- TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
- }else
- {
- p_msc->stage = MSC_STAGE_STATUS;
- }
- }
- }
- }
- break;
-
- case MSC_STAGE_DATA:
- TU_LOG2(" SCSI Data\n");
- //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2);
-
- // OUT transfer, invoke callback if needed
- if ( !tu_bit_test(p_cbw->dir, 7) )
- {
- if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
- {
- int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
-
- if ( cb_result < 0 )
- {
- p_csw->status = MSC_CSW_STATUS_FAILED;
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
- }else
- {
- p_csw->status = MSC_CSW_STATUS_PASSED;
- }
- }
- else
- {
- uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command);
-
- // Adjust lba with transferred bytes
- uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
-
- // Application can consume smaller bytes
- int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes);
-
- if ( nbytes < 0 )
- {
- // negative means error -> skip to status phase, status in CSW set to failed
- p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
- p_csw->status = MSC_CSW_STATUS_FAILED;
- p_msc->stage = MSC_STAGE_STATUS;
-
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
- break;
- }else
- {
- // Application consume less than what we got (including zero)
- if ( nbytes < (int32_t) xferred_bytes )
- {
- if ( nbytes > 0 )
- {
- p_msc->xferred_len += nbytes;
- memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
- }
-
- // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
- dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
-
- return true; // skip the rest
- }
- else
- {
- // Application consume all bytes in our buffer. Nothing to do, process with normal flow
- }
- }
- }
- }
-
- // Accumulate data so far
- p_msc->xferred_len += xferred_bytes;
-
- if ( p_msc->xferred_len >= p_msc->total_len )
- {
- // Data Stage is complete
- p_msc->stage = MSC_STAGE_STATUS;
- }
- else
- {
- // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
- // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE
- if (SCSI_CMD_READ_10 == p_cbw->command[0])
- {
- proc_read10_cmd(rhport, p_msc);
- }
- else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
- {
- proc_write10_cmd(rhport, p_msc);
- }else
- {
- // No other command take more than one transfer yet -> unlikely error
- TU_BREAKPOINT();
- }
- }
- break;
-
- case MSC_STAGE_STATUS:
- // processed immediately after this switch, supposedly to be empty
- break;
-
- case MSC_STAGE_STATUS_SENT:
- // Wait for the Status phase to complete
- if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
- {
- TU_LOG2(" SCSI Status: %u\n", p_csw->status);
- // TU_LOG2_MEM(p_csw, xferred_bytes, 2);
-
- // Move to default CMD stage
- p_msc->stage = MSC_STAGE_CMD;
-
- // Queue for the next CBW
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
- }
- break;
-
- default : break;
- }
-
- if ( p_msc->stage == MSC_STAGE_STATUS )
- {
- // Either endpoints is stalled, need to wait until it is cleared by host
- if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) )
- {
- // simulate an transfer complete with adjusted parameters --> this driver callback will fired again
- // and response with status phase after halted endpoints are cleared.
- // note: use ep_out to prevent confusing with STATUS complete
- dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false);
- }
- else
- {
- // Invoke complete callback if defined
- switch(p_cbw->command[0])
- {
- case SCSI_CMD_READ_10:
- if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
- break;
-
- case SCSI_CMD_WRITE_10:
- if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
- break;
-
- default:
- if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
- break;
- }
-
- // Move to Status Sent stage
- p_msc->stage = MSC_STAGE_STATUS_SENT;
-
- // Send SCSI Status
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
- }
- }
-
- return true;
-}
-
-/*------------------------------------------------------------------*/
-/* SCSI Command Process
- *------------------------------------------------------------------*/
-static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
- msc_csw_t * p_csw = &p_msc->csw;
-
- uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command);
- TU_ASSERT(block_cnt, ); // prevent div by zero
-
- uint16_t const block_sz = p_cbw->total_bytes / block_cnt;
- TU_ASSERT(block_sz, ); // prevent div by zero
-
- // Adjust lba with transferred bytes
- uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
-
- // remaining bytes capped at class buffer
- int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
-
- // Application can consume smaller bytes
- nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes);
-
- if ( nbytes < 0 )
- {
- // negative means error -> pipe is stalled & status in CSW set to failed
- p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len;
- p_csw->status = MSC_CSW_STATUS_FAILED;
-
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
- usbd_edpt_stall(rhport, p_msc->ep_in);
- }
- else if ( nbytes == 0 )
- {
- // zero means not ready -> simulate an transfer complete so that this driver callback will fired again
- dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
- }
- else
- {
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
- }
-}
-
-static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
-{
- msc_cbw_t const * p_cbw = &p_msc->cbw;
- bool writable = true;
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(p_cbw->lun);
- }
- if (!writable) {
- msc_csw_t* p_csw = &p_msc->csw;
- p_csw->data_residue = p_cbw->total_bytes;
- p_csw->status = MSC_CSW_STATUS_FAILED;
-
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected
- usbd_edpt_stall(rhport, p_msc->ep_out);
- return;
- }
-
- // remaining bytes capped at class buffer
- int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
-
- // Write10 callback will be called later when usb transfer complete
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
-}
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC) + +#include "common/tusb_common.h" +#include "msc_device.h" +#include "device/usbd_pvt.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +enum +{ + MSC_STAGE_CMD = 0, + MSC_STAGE_DATA, + MSC_STAGE_STATUS, + MSC_STAGE_STATUS_SENT +}; + +typedef struct +{ + // TODO optimize alignment + CFG_TUSB_MEM_ALIGN msc_cbw_t cbw; + CFG_TUSB_MEM_ALIGN msc_csw_t csw; + + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; + + // Bulk Only Transfer (BOT) Protocol + uint8_t stage; + uint32_t total_len; + uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage + + // Sense Response Data + uint8_t sense_key; + uint8_t add_sense_code; + uint8_t add_sense_qualifier; +}mscd_interface_t; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE]; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc); +static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc); + +static inline uint32_t rdwr10_get_lba(uint8_t const command[]) +{ + // read10 & write10 has the same format + scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; + + // copy first to prevent mis-aligned access + uint32_t lba; + memcpy(&lba, &p_rdwr10->lba, 4); + + // lba is in Big Endian format + return tu_ntohl(lba); +} + +static inline uint16_t rdwr10_get_blockcount(uint8_t const command[]) +{ + // read10 & write10 has the same format + scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command; + + // copy first to prevent mis-aligned access + uint16_t block_count; + memcpy(&block_count, &p_rdwr10->block_count, 2); + + return tu_ntohs(block_count); +} + +//--------------------------------------------------------------------+ +// Debug +//--------------------------------------------------------------------+ +#if CFG_TUSB_DEBUG >= 2 + +static lookup_entry_t const _msc_scsi_cmd_lookup[] = +{ + { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" }, + { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" }, + { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" }, + { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" }, + { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" }, + { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" }, + { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" }, + { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" }, + { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" }, + { .key = SCSI_CMD_READ_10 , .data = "Read10" }, + { .key = SCSI_CMD_WRITE_10 , .data = "Write10" } +}; + +static lookup_table_t const _msc_scsi_cmd_table = +{ + .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup), + .items = _msc_scsi_cmd_lookup +}; + +#endif + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier) +{ + (void) lun; + + _mscd_itf.sense_key = sense_key; + _mscd_itf.add_sense_code = add_sense_code; + _mscd_itf.add_sense_qualifier = add_sense_qualifier; + + return true; +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void mscd_init(void) +{ + tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); +} + +void mscd_reset(uint8_t rhport) +{ + (void) rhport; + tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); +} + +bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len) +{ + // only support SCSI's BOT protocol + TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && + MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); + + mscd_interface_t * p_msc = &_mscd_itf; + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) ); + + p_msc->itf_num = itf_desc->bInterfaceNumber; + (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + + // Prepare for Command Block Wrapper + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request) +{ + // Handle class request only + TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + switch ( p_request->bRequest ) + { + case MSC_REQ_RESET: + // TODO: Actually reset interface. + tud_control_status(rhport, p_request); + break; + + case MSC_REQ_GET_MAX_LUN: + { + uint8_t maxlun = 1; + if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb(); + TU_VERIFY(maxlun); + + // MAX LUN is minus 1 by specs + maxlun--; + + tud_control_xfer(rhport, p_request, &maxlun, 1); + } + break; + + default: return false; // stall unsupported request + } + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send non-sense DATA) +bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + (void) request; + + // nothing to do + return true; +} + +// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW) +// In case of a failed status, sense key must be set for reason of failure +int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize) +{ + (void) bufsize; // TODO refractor later + int32_t resplen; + + switch ( scsi_cmd[0] ) + { + case SCSI_CMD_TEST_UNIT_READY: + resplen = 0; + if ( !tud_msc_test_unit_ready_cb(lun) ) + { + // Failed status response + resplen = - 1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + } + break; + + case SCSI_CMD_START_STOP_UNIT: + resplen = 0; + + if (tud_msc_start_stop_cb) + { + scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd; + if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) ) + { + // Failed status response + resplen = - 1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + } + } + break; + + case SCSI_CMD_READ_CAPACITY_10: + { + uint32_t block_count; + uint32_t block_size; + uint16_t block_size_u16; + + tud_msc_capacity_cb(lun, &block_count, &block_size_u16); + block_size = (uint32_t) block_size_u16; + + // Invalid block size/count from callback, possibly unit is not ready + // stall this request, set sense key to NOT READY + if (block_count == 0 || block_size == 0) + { + resplen = -1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + }else + { + scsi_read_capacity10_resp_t read_capa10; + + read_capa10.last_lba = tu_htonl(block_count-1); + read_capa10.block_size = tu_htonl(block_size); + + resplen = sizeof(read_capa10); + memcpy(buffer, &read_capa10, resplen); + } + } + break; + + case SCSI_CMD_READ_FORMAT_CAPACITY: + { + scsi_read_format_capacity_data_t read_fmt_capa = + { + .list_length = 8, + .block_num = 0, + .descriptor_type = 2, // formatted media + .block_size_u16 = 0 + }; + + uint32_t block_count; + uint16_t block_size; + + tud_msc_capacity_cb(lun, &block_count, &block_size); + + // Invalid block size/count from callback, possibly unit is not ready + // stall this request, set sense key to NOT READY + if (block_count == 0 || block_size == 0) + { + resplen = -1; + + // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable + if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00); + }else + { + read_fmt_capa.block_num = tu_htonl(block_count); + read_fmt_capa.block_size_u16 = tu_htons(block_size); + + resplen = sizeof(read_fmt_capa); + memcpy(buffer, &read_fmt_capa, resplen); + } + } + break; + + case SCSI_CMD_INQUIRY: + { + scsi_inquiry_resp_t inquiry_rsp = + { + .is_removable = 1, + .version = 2, + .response_data_format = 2, + }; + + // vendor_id, product_id, product_rev is space padded string + memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id)); + memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id)); + memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev)); + + tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev); + + resplen = sizeof(inquiry_rsp); + memcpy(buffer, &inquiry_rsp, resplen); + } + break; + + case SCSI_CMD_MODE_SENSE_6: + { + scsi_mode_sense6_resp_t mode_resp = + { + .data_len = 3, + .medium_type = 0, + .write_protected = false, + .reserved = 0, + .block_descriptor_len = 0 // no block descriptor are included + }; + + bool writable = true; + if (tud_msc_is_writable_cb) { + writable = tud_msc_is_writable_cb(lun); + } + mode_resp.write_protected = !writable; + + resplen = sizeof(mode_resp); + memcpy(buffer, &mode_resp, resplen); + } + break; + + case SCSI_CMD_REQUEST_SENSE: + { + scsi_sense_fixed_resp_t sense_rsp = + { + .response_code = 0x70, + .valid = 1 + }; + + sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8; + + sense_rsp.sense_key = _mscd_itf.sense_key; + sense_rsp.add_sense_code = _mscd_itf.add_sense_code; + sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier; + + resplen = sizeof(sense_rsp); + memcpy(buffer, &sense_rsp, resplen); + + // Clear sense data after copy + tud_msc_set_sense(lun, 0, 0, 0); + } + break; + + default: resplen = -1; break; + } + + return resplen; +} + +bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + mscd_interface_t* p_msc = &_mscd_itf; + msc_cbw_t const * p_cbw = &p_msc->cbw; + msc_csw_t * p_csw = &p_msc->csw; + + switch (p_msc->stage) + { + case MSC_STAGE_CMD: + //------------- new CBW received -------------// + // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it + if(ep_addr != p_msc->ep_out) return true; + + TU_ASSERT( event == XFER_RESULT_SUCCESS && + xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE ); + + TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0])); + // TU_LOG2_MEM(p_cbw, xferred_bytes, 2); + + p_csw->signature = MSC_CSW_SIGNATURE; + p_csw->tag = p_cbw->tag; + p_csw->data_residue = 0; + + /*------------- Parse command and prepare DATA -------------*/ + p_msc->stage = MSC_STAGE_DATA; + p_msc->total_len = p_cbw->total_bytes; + p_msc->xferred_len = 0; + + if (SCSI_CMD_READ_10 == p_cbw->command[0]) + { + proc_read10_cmd(rhport, p_msc); + } + else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) + { + proc_write10_cmd(rhport, p_msc); + } + else + { + // For other SCSI commands + // 1. OUT : queue transfer (invoke app callback after done) + // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length + if ( (p_cbw->total_bytes > 0 ) && !tu_bit_test(p_cbw->dir, 7) ) + { + // queue transfer + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) ); + }else + { + int32_t resplen; + + // First process if it is a built-in commands + resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf)); + + // Not built-in, invoke user callback + if ( (resplen < 0) && (p_msc->sense_key == 0) ) + { + resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); + } + + if ( resplen < 0 ) + { + p_msc->total_len = 0; + p_csw->status = MSC_CSW_STATUS_FAILED; + p_msc->stage = MSC_STAGE_STATUS; + + // failed but senskey is not set: default to Illegal Request + if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // Stall bulk In if needed + if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in); + } + else + { + p_msc->total_len = (uint32_t) resplen; + p_csw->status = MSC_CSW_STATUS_PASSED; + + if (p_msc->total_len) + { + TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) ); + }else + { + p_msc->stage = MSC_STAGE_STATUS; + } + } + } + } + break; + + case MSC_STAGE_DATA: + TU_LOG2(" SCSI Data\n"); + //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2); + + // OUT transfer, invoke callback if needed + if ( !tu_bit_test(p_cbw->dir, 7) ) + { + if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] ) + { + int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len); + + if ( cb_result < 0 ) + { + p_csw->status = MSC_CSW_STATUS_FAILED; + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + }else + { + p_csw->status = MSC_CSW_STATUS_PASSED; + } + } + else + { + uint16_t const block_sz = p_cbw->total_bytes / rdwr10_get_blockcount(p_cbw->command); + + // Adjust lba with transferred bytes + uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); + + // Application can consume smaller bytes + int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, xferred_bytes); + + if ( nbytes < 0 ) + { + // negative means error -> skip to status phase, status in CSW set to failed + p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; + p_csw->status = MSC_CSW_STATUS_FAILED; + p_msc->stage = MSC_STAGE_STATUS; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + break; + }else + { + // Application consume less than what we got (including zero) + if ( nbytes < (int32_t) xferred_bytes ) + { + if ( nbytes > 0 ) + { + p_msc->xferred_len += nbytes; + memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes); + } + + // simulate an transfer complete with adjusted parameters --> this driver callback will fired again + dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false); + + return true; // skip the rest + } + else + { + // Application consume all bytes in our buffer. Nothing to do, process with normal flow + } + } + } + } + + // Accumulate data so far + p_msc->xferred_len += xferred_bytes; + + if ( p_msc->xferred_len >= p_msc->total_len ) + { + // Data Stage is complete + p_msc->stage = MSC_STAGE_STATUS; + } + else + { + // READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write) + // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE + if (SCSI_CMD_READ_10 == p_cbw->command[0]) + { + proc_read10_cmd(rhport, p_msc); + } + else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) + { + proc_write10_cmd(rhport, p_msc); + }else + { + // No other command take more than one transfer yet -> unlikely error + TU_BREAKPOINT(); + } + } + break; + + case MSC_STAGE_STATUS: + // processed immediately after this switch, supposedly to be empty + break; + + case MSC_STAGE_STATUS_SENT: + // Wait for the Status phase to complete + if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) ) + { + TU_LOG2(" SCSI Status: %u\n", p_csw->status); + // TU_LOG2_MEM(p_csw, xferred_bytes, 2); + + // Move to default CMD stage + p_msc->stage = MSC_STAGE_CMD; + + // Queue for the next CBW + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) ); + } + break; + + default : break; + } + + if ( p_msc->stage == MSC_STAGE_STATUS ) + { + // Either endpoints is stalled, need to wait until it is cleared by host + if ( usbd_edpt_stalled(rhport, p_msc->ep_in) || usbd_edpt_stalled(rhport, p_msc->ep_out) ) + { + // simulate an transfer complete with adjusted parameters --> this driver callback will fired again + // and response with status phase after halted endpoints are cleared. + // note: use ep_out to prevent confusing with STATUS complete + dcd_event_xfer_complete(rhport, p_msc->ep_out, 0, XFER_RESULT_SUCCESS, false); + } + else + { + // Invoke complete callback if defined + switch(p_cbw->command[0]) + { + case SCSI_CMD_READ_10: + if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun); + break; + + case SCSI_CMD_WRITE_10: + if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun); + break; + + default: + if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command); + break; + } + + // Move to Status Sent stage + p_msc->stage = MSC_STAGE_STATUS_SENT; + + // Send SCSI Status + TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t))); + } + } + + return true; +} + +/*------------------------------------------------------------------*/ +/* SCSI Command Process + *------------------------------------------------------------------*/ +static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) +{ + msc_cbw_t const * p_cbw = &p_msc->cbw; + msc_csw_t * p_csw = &p_msc->csw; + + uint16_t const block_cnt = rdwr10_get_blockcount(p_cbw->command); + TU_ASSERT(block_cnt, ); // prevent div by zero + + uint16_t const block_sz = p_cbw->total_bytes / block_cnt; + TU_ASSERT(block_sz, ); // prevent div by zero + + // Adjust lba with transferred bytes + uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); + + // remaining bytes capped at class buffer + int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); + + // Application can consume smaller bytes + nbytes = tud_msc_read10_cb(p_cbw->lun, lba, p_msc->xferred_len % block_sz, _mscd_buf, (uint32_t) nbytes); + + if ( nbytes < 0 ) + { + // negative means error -> pipe is stalled & status in CSW set to failed + p_csw->data_residue = p_cbw->total_bytes - p_msc->xferred_len; + p_csw->status = MSC_CSW_STATUS_FAILED; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation + usbd_edpt_stall(rhport, p_msc->ep_in); + } + else if ( nbytes == 0 ) + { + // zero means not ready -> simulate an transfer complete so that this driver callback will fired again + dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); + } + else + { + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), ); + } +} + +static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) +{ + msc_cbw_t const * p_cbw = &p_msc->cbw; + bool writable = true; + if (tud_msc_is_writable_cb) { + writable = tud_msc_is_writable_cb(p_cbw->lun); + } + if (!writable) { + msc_csw_t* p_csw = &p_msc->csw; + p_csw->data_residue = p_cbw->total_bytes; + p_csw->status = MSC_CSW_STATUS_FAILED; + + tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); // Sense = Write protected + usbd_edpt_stall(rhport, p_msc->ep_out); + return; + } + + // remaining bytes capped at class buffer + int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len); + + // Write10 callback will be called later when usb transfer complete + TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), ); +} + +#endif diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 8da9cfaa3..66180777e 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -1,165 +1,165 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_MSC_DEVICE_H_
-#define _TUSB_MSC_DEVICE_H_
-
-#include "common/tusb_common.h"
-#include "device/usbd.h"
-#include "msc.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-//--------------------------------------------------------------------+
-// Class Driver Configuration
-//--------------------------------------------------------------------+
-TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
-
-#ifndef CFG_TUD_MSC_BUFSIZE
- #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
-#endif
-
-/** \addtogroup ClassDriver_MSC
- * @{
- * \defgroup MSC_Device Device
- * @{ */
-
-bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
-
-//--------------------------------------------------------------------+
-// Application Callbacks (WEAK is optional)
-//--------------------------------------------------------------------+
-
-/**
- * Invoked when received \ref SCSI_CMD_READ_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be read
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which application need to update with the response data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer
- * this amount first and invoked this again for remaining data.
- *
- * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete.
- * tinyusb will invoke this callback with the same parameters again some time later.
- *
- * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
-int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
-
-/**
- * Invoked when received \ref SCSI_CMD_WRITE_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be write
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which holds written data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with
- * other work and invoked this again with adjusted parameters.
- *
- * \retval zero Indicate application is not ready yet e.g disk I/O is not complete.
- * Tinyusb will invoke this callback with the same parameters again some time later.
- *
- * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
-int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
-
-// Invoked when received SCSI_CMD_INQUIRY
-// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
-void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
-
-// Invoked when received Test Unit Ready command.
-// return true allowing host to read/write this LUN e.g SD card inserted
-bool tud_msc_test_unit_ready_cb(uint8_t lun);
-
-// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
-// Application update block count and block size
-void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
-
-/**
- * Invoked when received an SCSI command not in built-in list below.
- * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
- * - READ10 and WRITE10 has their own callbacks
- *
- * \param[in] lun Logical unit number
- * \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
- * \param[out] buffer Buffer for SCSI Data Stage.
- * - For INPUT: application must fill this with response.
- * - For OUTPUT it holds the Data from host
- * \param[in] bufsize Buffer's length.
- *
- * \return Actual bytes processed, can be zero for no-data command.
- * \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
-int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
-
-/*------------- Optional callbacks -------------*/
-
-// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
-TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
-
-// Invoked when received Start Stop Unit command
-// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
-// - Start = 1 : active mode, if load_eject = 1 : load disk storage
-TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
-
-// Invoked when Read10 command is complete
-TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
-
-// Invoke when Write10 command is complete, can be used to flush flash caching
-TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
-
-// Invoked when command in tud_msc_scsi_cb is complete
-TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
-
-// Hook to make a mass storage device read-only. TODO remove
-TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
-
-/** @} */
-/** @} */
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-void mscd_init (void);
-void mscd_reset (uint8_t rhport);
-bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_MSC_DEVICE_H_ */
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_MSC_DEVICE_H_ +#define _TUSB_MSC_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "msc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct"); + +#ifndef CFG_TUD_MSC_BUFSIZE + #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better +#endif + +/** \addtogroup ClassDriver_MSC + * @{ + * \defgroup MSC_Device Device + * @{ */ + +bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +/** + * Invoked when received \ref SCSI_CMD_READ_10 command + * \param[in] lun Logical unit number + * \param[in] lba Logical Block Address to be read + * \param[in] offset Byte offset from LBA + * \param[out] buffer Buffer which application need to update with the response data. + * \param[in] bufsize Requested bytes + * + * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer + * this amount first and invoked this again for remaining data. + * + * \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete. + * tinyusb will invoke this callback with the same parameters again some time later. + * + * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); + +/** + * Invoked when received \ref SCSI_CMD_WRITE_10 command + * \param[in] lun Logical unit number + * \param[in] lba Logical Block Address to be write + * \param[in] offset Byte offset from LBA + * \param[out] buffer Buffer which holds written data. + * \param[in] bufsize Requested bytes + * + * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with + * other work and invoked this again with adjusted parameters. + * + * \retval zero Indicate application is not ready yet e.g disk I/O is not complete. + * Tinyusb will invoke this callback with the same parameters again some time later. + * + * \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize); + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]); + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun); + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); + +/** + * Invoked when received an SCSI command not in built-in list below. + * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE + * - READ10 and WRITE10 has their own callbacks + * + * \param[in] lun Logical unit number + * \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly + * \param[out] buffer Buffer for SCSI Data Stage. + * - For INPUT: application must fill this with response. + * - For OUTPUT it holds the Data from host + * \param[in] bufsize Buffer's length. + * + * \return Actual bytes processed, can be zero for no-data command. + * \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding + * endpoint and return failed status in command status wrapper phase. + */ +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); + +/*------------- Optional callbacks -------------*/ + +// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation +TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void); + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject); + +// Invoked when Read10 command is complete +TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun); + +// Invoke when Write10 command is complete, can be used to flush flash caching +TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun); + +// Invoked when command in tud_msc_scsi_cb is complete +TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]); + +// Hook to make a mass storage device read-only. TODO remove +TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun); + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void mscd_init (void); +void mscd_reset (uint8_t rhport); +bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request); +bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request); +bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_DEVICE_H_ */ diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c index 0507c5f6d..e20969f57 100644 --- a/src/class/msc/msc_host.c +++ b/src/class/msc/msc_host.c @@ -1,413 +1,413 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "tusb_option.h"
-
-#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC
-
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
-#include "msc_host.h"
-
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
-
-//------------- Initalization Data -------------//
-static osal_semaphore_def_t msch_sem_def;
-static osal_semaphore_t msch_sem_hdl;
-
-// buffer used to read scsi information when mounted, largest response data currently is inquiry
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
-
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
-
-//--------------------------------------------------------------------+
-// PUBLIC API
-//--------------------------------------------------------------------+
-bool tuh_msc_is_mounted(uint8_t dev_addr)
-{
- return tuh_device_is_configured(dev_addr) && // is configured can be omitted
- msch_data[dev_addr-1].is_initialized;
-}
-
-bool tuh_msc_is_busy(uint8_t dev_addr)
-{
- return msch_data[dev_addr-1].is_initialized &&
- hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in);
-}
-
-uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr)
-{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL;
-}
-
-uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr)
-{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL;
-}
-
-tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size)
-{
- if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED;
- TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA);
-
- (*p_last_lba) = msch_data[dev_addr-1].last_lba;
- (*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size;
-
- return TUSB_ERROR_NONE;
-}
-
-//--------------------------------------------------------------------+
-// PUBLIC API: SCSI COMMAND
-//--------------------------------------------------------------------+
-static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun)
-{
- p_cbw->signature = MSC_CBW_SIGNATURE;
- p_cbw->tag = 0xCAFECAFE;
- p_cbw->lun = lun;
-}
-
-static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer)
-{
- if ( NULL != p_buffer)
- { // there is data phase
- if (p_msch->cbw.dir & TUSB_DIR_IN_MASK)
- {
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED );
- }else
- {
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED );
- }
- }
-
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED);
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t);
-
- //------------- SCSI command -------------//
- scsi_inquiry_t cmd_inquiry =
- {
- .cmd_code = SCSI_CMD_INQUIRY,
- .alloc_length = sizeof(scsi_inquiry_resp_t)
- };
-
- memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t);
-
- //------------- SCSI command -------------//
- scsi_read_capacity10_t cmd_read_capacity10 =
- {
- .cmd_code = SCSI_CMD_READ_CAPACITY_10,
- .lba = 0,
- .partial_medium_indicator = 0
- };
-
- memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
-{
- (void) lun; // TODO [MSCH] multiple lun support
-
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- p_msch->cbw.total_bytes = 18;
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t);
-
- //------------- SCSI command -------------//
- scsi_request_sense_t cmd_request_sense =
- {
- .cmd_code = SCSI_CMD_REQUEST_SENSE,
- .alloc_length = 18
- };
-
- memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
-
- p_msch->cbw.total_bytes = 0; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_OUT;
- p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
-
- //------------- SCSI command -------------//
- scsi_test_unit_ready_t cmd_test_unit_ready =
- {
- .cmd_code = SCSI_CMD_TEST_UNIT_READY,
- .lun = lun // according to wiki
- };
-
- memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len);
-
- // TODO MSCH refractor test uinit ready
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED );
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
-
- p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_read10_t);
-
- //------------- SCSI command -------------//
- scsi_read10_t cmd_read10 =
- {
- .cmd_code = SCSI_CMD_READ_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
- };
-
- memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer));
-
- return TUSB_ERROR_NONE;
-}
-
-tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count)
-{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
-
- p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_OUT;
- p_msch->cbw.cmd_len = sizeof(scsi_write10_t);
-
- //------------- SCSI command -------------//
- scsi_write10_t cmd_write10 =
- {
- .cmd_code = SCSI_CMD_WRITE_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
- };
-
- memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len);
-
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer));
-
- return TUSB_ERROR_NONE;
-}
-
-//--------------------------------------------------------------------+
-// CLASS-USBH API (don't require to verify parameters)
-//--------------------------------------------------------------------+
-void msch_init(void)
-{
- tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
- msch_sem_hdl = osal_semaphore_create(&msch_sem_def);
-}
-
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
-{
- TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
-
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
-
- //------------- Open Data Pipe -------------//
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
-
- for(uint32_t i=0; i<2; i++)
- {
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
- TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
-
- TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
-
- if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
- {
- p_msc->ep_in = ep_desc->bEndpointAddress;
- }else
- {
- p_msc->ep_out = ep_desc->bEndpointAddress;
- }
-
- ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
- }
-
- p_msc->itf_numr = itf_desc->bInterfaceNumber;
- (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
-
- //------------- Get Max Lun -------------//
- tusb_control_request_t request = {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
- .bRequest = MSC_REQ_GET_MAX_LUN,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 1
- };
- // TODO STALL means zero
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) );
- p_msc->max_lun = msch_buffer[0];
-
-#if 0
- //------------- Reset -------------//
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = MSC_REQ_RESET,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 0
- };
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
-#endif
-
- enum { SCSI_XFER_TIMEOUT = 2000 };
- //------------- SCSI Inquiry -------------//
- tusbh_msc_inquiry(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) );
-
- memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8);
- memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16);
-
- //------------- SCSI Read Capacity 10 -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
-
- // NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence
- // Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work
- if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) )
- {
- // clear stall TODO abstract clear stall function
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT },
- .bRequest = TUSB_REQ_CLEAR_FEATURE,
- .wValue = 0,
- .wIndex = p_msc->ep_in,
- .wLength = 0
- };
-
- TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL ));
-
- hcd_edpt_clear_stall(dev_addr, p_msc->ep_in);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status
-
- //------------- SCSI Request Sense -------------//
- (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
-
- //------------- Re-read SCSI Read Capactity -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
- }
-
- p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba );
- p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size );
-
- p_msc->is_initialized = true;
-
- tuh_msc_mounted_cb(dev_addr);
-
- return true;
-}
-
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
-{
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
- if ( ep_addr == p_msc->ep_in )
- {
- if (p_msc->is_initialized)
- {
- tuh_msc_isr(dev_addr, event, xferred_bytes);
- }else
- { // still initializing under open subtask
- osal_semaphore_post(msch_sem_hdl, true);
- }
- }
-}
-
-void msch_close(uint8_t dev_addr)
-{
- tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t));
- osal_semaphore_reset(msch_sem_hdl);
-
- tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback
-}
-
-//--------------------------------------------------------------------+
-// INTERNAL & HELPER
-//--------------------------------------------------------------------+
-
-
-#endif
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "common/tusb_common.h" +#include "msc_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX]; + +//------------- Initalization Data -------------// +static osal_semaphore_def_t msch_sem_def; +static osal_semaphore_t msch_sem_hdl; + +// buffer used to read scsi information when mounted, largest response data currently is inquiry +CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)]; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// PUBLIC API +//--------------------------------------------------------------------+ +bool tuh_msc_is_mounted(uint8_t dev_addr) +{ + return tuh_device_is_configured(dev_addr) && // is configured can be omitted + msch_data[dev_addr-1].is_initialized; +} + +bool tuh_msc_is_busy(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized && + hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in); +} + +uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL; +} + +uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr) +{ + return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL; +} + +tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size) +{ + if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED; + TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA); + + (*p_last_lba) = msch_data[dev_addr-1].last_lba; + (*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size; + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// PUBLIC API: SCSI COMMAND +//--------------------------------------------------------------------+ +static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun) +{ + p_cbw->signature = MSC_CBW_SIGNATURE; + p_cbw->tag = 0xCAFECAFE; + p_cbw->lun = lun; +} + +static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer) +{ + if ( NULL != p_buffer) + { // there is data phase + if (p_msch->cbw.dir & TUSB_DIR_IN_MASK) + { + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED ); + }else + { + TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED ); + } + } + + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t); + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t); + + //------------- SCSI command -------------// + scsi_inquiry_t cmd_inquiry = + { + .cmd_code = SCSI_CMD_INQUIRY, + .alloc_length = sizeof(scsi_inquiry_resp_t) + }; + + memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t); + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t); + + //------------- SCSI command -------------// + scsi_read_capacity10_t cmd_read_capacity10 = + { + .cmd_code = SCSI_CMD_READ_CAPACITY_10, + .lba = 0, + .partial_medium_indicator = 0 + }; + + memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data) +{ + (void) lun; // TODO [MSCH] multiple lun support + + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + p_msch->cbw.total_bytes = 18; + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t); + + //------------- SCSI command -------------// + scsi_request_sense_t cmd_request_sense = + { + .cmd_code = SCSI_CMD_REQUEST_SENSE, + .alloc_length = 18 + }; + + memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = 0; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_OUT; + p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t); + + //------------- SCSI command -------------// + scsi_test_unit_ready_t cmd_test_unit_ready = + { + .cmd_code = SCSI_CMD_TEST_UNIT_READY, + .lun = lun // according to wiki + }; + + memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len); + + // TODO MSCH refractor test uinit ready + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED ); + TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED ); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_IN_MASK; + p_msch->cbw.cmd_len = sizeof(scsi_read10_t); + + //------------- SCSI command -------------// + scsi_read10_t cmd_read10 = + { + .cmd_code = SCSI_CMD_READ_10, + .lba = tu_htonl(lba), + .block_count = tu_htons(block_count) + }; + + memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer)); + + return TUSB_ERROR_NONE; +} + +tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count) +{ + msch_interface_t* p_msch = &msch_data[dev_addr-1]; + + //------------- Command Block Wrapper -------------// + msc_cbw_add_signature(&p_msch->cbw, lun); + + p_msch->cbw.total_bytes = p_msch->block_size*block_count; // Number of bytes + p_msch->cbw.dir = TUSB_DIR_OUT; + p_msch->cbw.cmd_len = sizeof(scsi_write10_t); + + //------------- SCSI command -------------// + scsi_write10_t cmd_write10 = + { + .cmd_code = SCSI_CMD_WRITE_10, + .lba = tu_htonl(lba), + .block_count = tu_htons(block_count) + }; + + memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len); + + TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer)); + + return TUSB_ERROR_NONE; +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API (don't require to verify parameters) +//--------------------------------------------------------------------+ +void msch_init(void) +{ + tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX); + msch_sem_hdl = osal_semaphore_create(&msch_sem_def); +} + +bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length) +{ + TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass && + MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol); + + msch_interface_t* p_msc = &msch_data[dev_addr-1]; + + //------------- Open Data Pipe -------------// + tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + for(uint32_t i=0; i<2; i++) + { + TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType); + TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer); + + TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc)); + + if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN ) + { + p_msc->ep_in = ep_desc->bEndpointAddress; + }else + { + p_msc->ep_out = ep_desc->bEndpointAddress; + } + + ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc); + } + + p_msc->itf_numr = itf_desc->bInterfaceNumber; + (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t); + + //------------- Get Max Lun -------------// + tusb_control_request_t request = { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN }, + .bRequest = MSC_REQ_GET_MAX_LUN, + .wValue = 0, + .wIndex = p_msc->itf_numr, + .wLength = 1 + }; + // TODO STALL means zero + TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) ); + p_msc->max_lun = msch_buffer[0]; + +#if 0 + //------------- Reset -------------// + request = (tusb_control_request_t) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT }, + .bRequest = MSC_REQ_RESET, + .wValue = 0, + .wIndex = p_msc->itf_numr, + .wLength = 0 + }; + TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) ); +#endif + + enum { SCSI_XFER_TIMEOUT = 2000 }; + //------------- SCSI Inquiry -------------// + tusbh_msc_inquiry(dev_addr, 0, msch_buffer); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); + + memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8); + memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16); + + //------------- SCSI Read Capacity 10 -------------// + tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + + // NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence + // Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work + if ( hcd_edpt_stalled(dev_addr, p_msc->ep_in) ) + { + // clear stall TODO abstract clear stall function + request = (tusb_control_request_t) { + .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT }, + .bRequest = TUSB_REQ_CLEAR_FEATURE, + .wValue = 0, + .wIndex = p_msc->ep_in, + .wLength = 0 + }; + + TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL )); + + hcd_edpt_clear_stall(dev_addr, p_msc->ep_in); + TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status + + //------------- SCSI Request Sense -------------// + (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer); + TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + + //------------- Re-read SCSI Read Capactity -------------// + tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer); + TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT)); + } + + p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba ); + p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size ); + + p_msc->is_initialized = true; + + tuh_msc_mounted_cb(dev_addr); + + return true; +} + +void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) +{ + msch_interface_t* p_msc = &msch_data[dev_addr-1]; + if ( ep_addr == p_msc->ep_in ) + { + if (p_msc->is_initialized) + { + tuh_msc_isr(dev_addr, event, xferred_bytes); + }else + { // still initializing under open subtask + osal_semaphore_post(msch_sem_hdl, true); + } + } +} + +void msch_close(uint8_t dev_addr) +{ + tu_memclr(&msch_data[dev_addr-1], sizeof(msch_interface_t)); + osal_semaphore_reset(msch_sem_hdl); + + tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback +} + +//--------------------------------------------------------------------+ +// INTERNAL & HELPER +//--------------------------------------------------------------------+ + + +#endif diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h index da817fbe2..76cf86853 100644 --- a/src/class/msc/msc_host.h +++ b/src/class/msc/msc_host.h @@ -1,206 +1,206 @@ -/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#ifndef _TUSB_MSC_HOST_H_
-#define _TUSB_MSC_HOST_H_
-
-#include "common/tusb_common.h"
-#include "host/usbh.h"
-#include "msc.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/** \addtogroup ClassDriver_MSC
- * @{
- * \defgroup MSC_Host Host
- * The interface API includes status checking function, data transferring function and callback functions
- * @{ */
-//--------------------------------------------------------------------+
-// MASS STORAGE Application API
-//--------------------------------------------------------------------+
-/** \brief Check if device supports MassStorage interface or not
- * \param[in] dev_addr device address
- * \retval true if device supports
- * \retval false if device does not support or is not mounted
- */
-bool tuh_msc_is_mounted(uint8_t dev_addr);
-
-/** \brief Check if the interface is currently busy or not
- * \param[in] dev_addr device address
- * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
- * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
- * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
- * can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted)
- * before calling this function
- */
-bool tuh_msc_is_busy(uint8_t dev_addr);
-
-/** \brief Get SCSI vendor's name of MassStorage device
- * \param[in] dev_addr device address
- * \return pointer to vendor's name or NULL if specified device does not support MassStorage
- * \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
- * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
- * command or allocate buffer for this.
- */
-uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr);
-
-/** \brief Get SCSI product's name of MassStorage device
- * \param[in] dev_addr device address
- * \return pointer to product's name or NULL if specified device does not support MassStorage
- * \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
- * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
- * command or allocate buffer for this.
- */
-uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr);
-
-/** \brief Get SCSI Capacity of MassStorage device
- * \param[in] dev_addr device address
- * \param[out] p_last_lba Last Logical Block Address of device
- * \param[out] p_block_size Block Size of device in bytes
- * \retval pointer to product's name or NULL if specified device does not support MassStorage
- * \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already
- * retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application
- * to re-send SCSI READ CAPACITY 10 command
- */
-tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size);
-
-/** \brief Perform SCSI READ 10 command to read data from MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] lba Starting Logical Block Address to be read
- * \param[in] block_count Number of Block to be read
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count);
-
-/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \param[in] lba Starting Logical Block Address to be written
- * \param[in] block_count Number of Block to be written
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count);
-
-/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION)
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data);
-
-/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready
- * \param[in] dev_addr device address
- * \param[in] lun Targeted Logical Unit
- * \retval TUSB_ERROR_NONE on success
- * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
- * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
- * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
- * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
- */
-tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor
-
-//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in,
-// uint8_t const * p_command, uint8_t cmd_len,
-// uint8_t * p_response, uint32_t resp_len);
-
-//------------- Application Callback -------------//
-/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted
- * \param[in] dev_addr Address of newly mounted device
- * \note This callback should be used by Application to set-up interface-related data
- */
-void tuh_msc_mounted_cb(uint8_t dev_addr);
-
-/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted
- * \param[in] dev_addr Address of newly unmounted device
- * \note This callback should be used by Application to tear-down interface-related data
- */
-void tuh_msc_unmounted_cb(uint8_t dev_addr);
-
-/** \brief Callback function that is invoked when an transferring event occurred
- * \param[in] dev_addr Address of device
- * \param[in] event an value from \ref xfer_result_t
- * \param[in] xferred_bytes Number of bytes transferred via USB bus
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note
- */
-void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes);
-
-
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-typedef struct
-{
- uint8_t itf_numr;
- uint8_t ep_in;
- uint8_t ep_out;
-
- uint8_t max_lun;
- uint16_t block_size;
- uint32_t last_lba; // last logical block address
-
- volatile bool is_initialized;
- uint8_t vendor_id[8];
- uint8_t product_id[16];
-
- msc_cbw_t cbw;
- msc_csw_t csw;
-}msch_interface_t;
-
-void msch_init(void);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void msch_close(uint8_t dev_addr);
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* _TUSB_MSC_HOST_H_ */
-
-/// @}
-/// @}
+/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_MSC_HOST_H_ +#define _TUSB_MSC_HOST_H_ + +#include "common/tusb_common.h" +#include "host/usbh.h" +#include "msc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup ClassDriver_MSC + * @{ + * \defgroup MSC_Host Host + * The interface API includes status checking function, data transferring function and callback functions + * @{ */ +//--------------------------------------------------------------------+ +// MASS STORAGE Application API +//--------------------------------------------------------------------+ +/** \brief Check if device supports MassStorage interface or not + * \param[in] dev_addr device address + * \retval true if device supports + * \retval false if device does not support or is not mounted + */ +bool tuh_msc_is_mounted(uint8_t dev_addr); + +/** \brief Check if the interface is currently busy or not + * \param[in] dev_addr device address + * \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device + * \retval false if the interface is not busy meaning the stack successfully transferred data from/to device + * \note This function is used to check if previous transfer is complete (success or error), so that the next transfer + * can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted) + * before calling this function + */ +bool tuh_msc_is_busy(uint8_t dev_addr); + +/** \brief Get SCSI vendor's name of MassStorage device + * \param[in] dev_addr device address + * \return pointer to vendor's name or NULL if specified device does not support MassStorage + * \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already + * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY + * command or allocate buffer for this. + */ +uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr); + +/** \brief Get SCSI product's name of MassStorage device + * \param[in] dev_addr device address + * \return pointer to product's name or NULL if specified device does not support MassStorage + * \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already + * retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY + * command or allocate buffer for this. + */ +uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr); + +/** \brief Get SCSI Capacity of MassStorage device + * \param[in] dev_addr device address + * \param[out] p_last_lba Last Logical Block Address of device + * \param[out] p_block_size Block Size of device in bytes + * \retval pointer to product's name or NULL if specified device does not support MassStorage + * \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already + * retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application + * to re-send SCSI READ CAPACITY 10 command + */ +tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size); + +/** \brief Perform SCSI READ 10 command to read data from MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] lba Starting Logical Block Address to be read + * \param[in] block_count Number of Block to be read + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count); + +/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \param[in] lba Starting Logical Block Address to be written + * \param[in] block_count Number of Block to be written + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count); + +/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_MEM_SECTION) + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data); + +/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready + * \param[in] dev_addr device address + * \param[in] lun Targeted Logical Unit + * \retval TUSB_ERROR_NONE on success + * \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device + * \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request) + * \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct + * \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function + */ +tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw); // TODO to be refractor + +//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in, +// uint8_t const * p_command, uint8_t cmd_len, +// uint8_t * p_response, uint32_t resp_len); + +//------------- Application Callback -------------// +/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted + * \param[in] dev_addr Address of newly mounted device + * \note This callback should be used by Application to set-up interface-related data + */ +void tuh_msc_mounted_cb(uint8_t dev_addr); + +/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted + * \param[in] dev_addr Address of newly unmounted device + * \note This callback should be used by Application to tear-down interface-related data + */ +void tuh_msc_unmounted_cb(uint8_t dev_addr); + +/** \brief Callback function that is invoked when an transferring event occurred + * \param[in] dev_addr Address of device + * \param[in] event an value from \ref xfer_result_t + * \param[in] xferred_bytes Number of bytes transferred via USB bus + * \note event can be one of following + * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully. + * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error. + * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device. + * \note + */ +void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes); + + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_numr; + uint8_t ep_in; + uint8_t ep_out; + + uint8_t max_lun; + uint16_t block_size; + uint32_t last_lba; // last logical block address + + volatile bool is_initialized; + uint8_t vendor_id[8]; + uint8_t product_id[16]; + + msc_cbw_t cbw; + msc_csw_t csw; +}msch_interface_t; + +void msch_init(void); +bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length); +void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void msch_close(uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_MSC_HOST_H_ */ + +/// @} +/// @} diff --git a/src/class/net/net_device.c b/src/class/net/net_device.c new file mode 100644 index 000000000..c918e5874 --- /dev/null +++ b/src/class/net/net_device.c @@ -0,0 +1,345 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if ( TUSB_OPT_DEVICE_ENABLED && (CFG_TUD_NET != OPT_NET_NONE) ) + +#include "net_device.h" +#include "device/usbd_pvt.h" +#include "rndis_protocol.h" + +void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */ + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; +} netd_interface_t; + +#if CFG_TUD_NET == OPT_NET_ECM + #define CFG_TUD_NET_PACKET_PREFIX_LEN 0 + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 0 + #define CFG_TUD_NET_INTERFACESUBCLASS CDC_COMM_SUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL +#elif CFG_TUD_NET == OPT_NET_RNDIS + #define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t) + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 0 + #define CFG_TUD_NET_INTERFACESUBCLASS TUD_RNDIS_ITF_SUBCLASS +#elif CFG_TUD_NET == OPT_NET_EEM + #define CFG_TUD_NET_PACKET_PREFIX_LEN 2 + #define CFG_TUD_NET_PACKET_SUFFIX_LEN 4 + #define CFG_TUD_NET_INTERFACESUBCLASS CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL +#endif + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN]; + +#if CFG_TUD_NET == OPT_NET_RNDIS + CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t rndis_buf[128]; +#endif + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf; + +static bool can_xmit; + +void tud_network_recv_renew(void) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received)); +} + +static void do_in_xfer(uint8_t *buf, uint16_t len) +{ + can_xmit = false; + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len); +} + +void netd_report(uint8_t *buf, uint16_t len) +{ + usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void netd_init(void) +{ + tu_memclr(&_netd_itf, sizeof(_netd_itf)); +} + +void netd_reset(uint8_t rhport) +{ + (void) rhport; + + netd_init(); +} + +bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length) +{ + // sanity check the descriptor + TU_ASSERT (CFG_TUD_NET_INTERFACESUBCLASS == itf_desc->bInterfaceSubClass); + + // confirm interface hasn't already been allocated + TU_ASSERT(0 == _netd_itf.ep_in); + + //------------- first Interface -------------// + _netd_itf.itf_num = itf_desc->bInterfaceNumber; + + uint8_t const * p_desc = tu_desc_next( itf_desc ); + (*p_length) = sizeof(tusb_desc_interface_t); + +#if CFG_TUD_NET != OPT_NET_EEM + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) ) + { + (*p_length) += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // notification endpoint (if any) + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + TU_ASSERT( dcd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc) ); + + _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress; + + (*p_length) += p_desc[DESC_OFFSET_LEN]; + p_desc = tu_desc_next(p_desc); + } + + //------------- second Interface -------------// + if ( (TUSB_DESC_INTERFACE == p_desc[DESC_OFFSET_TYPE]) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + (*p_length) += sizeof(tusb_desc_interface_t); + } +#endif + + if (TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE]) + { + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) ); + + (*p_length) += 2*sizeof(tusb_desc_endpoint_t); + } + + tud_network_init_cb(); + + // we are ready to transmit a packet + can_xmit = true; + + // prepare for incoming packets + tud_network_recv_renew(); + + return true; +} + +// Invoked when class request DATA stage is finished. +// return false to stall control endpoint (e.g Host send nonsense DATA) +bool netd_control_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + // Handle class request only + TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + TU_VERIFY (_netd_itf.itf_num == request->wIndex); + +#if CFG_TUD_NET == OPT_NET_RNDIS + if (request->bmRequestType_bit.direction == TUSB_DIR_OUT) + { + rndis_class_set_handler(rndis_buf, request->wLength); + } +#endif + + return true; +} + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool netd_control_request(uint8_t rhport, tusb_control_request_t const * request) +{ + // Handle class request only + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + TU_VERIFY (_netd_itf.itf_num == request->wIndex); + +#if CFG_TUD_NET == OPT_NET_RNDIS + tud_control_xfer(rhport, request, rndis_buf, sizeof(rndis_buf)); +#else + (void)rhport; +#endif + + return true; +} + +struct cdc_eem_packet_header +{ + uint16_t length:14; + uint16_t bmCRC:1; + uint16_t bmType:1; +}; + +static void handle_incoming_packet(uint32_t len) +{ + uint8_t *pnt = received; + uint32_t size = 0; + +#if CFG_TUD_NET == OPT_NET_ECM + size = len; +#elif CFG_TUD_NET == OPT_NET_RNDIS + rndis_data_packet_t *r = (rndis_data_packet_t *)pnt; + if (len >= sizeof(rndis_data_packet_t)) + if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len)) + if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len) + { + pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)]; + size = r->DataLength; + } +#elif CFG_TUD_NET == OPT_NET_EEM + struct cdc_eem_packet_header *hdr = (struct cdc_eem_packet_header *)pnt; + + (void)len; + + if (hdr->bmType) + { + /* EEM Control Packet: discard it */ + tud_network_recv_renew(); + } + else + { + /* EEM Data Packet */ + pnt += CFG_TUD_NET_PACKET_PREFIX_LEN; + size = hdr->length - 4; /* discard the unused CRC-32 */ + } +#endif + + if (size) + { + struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); + bool accepted = true; + + if (p) + { + memcpy(p->payload, pnt, size); + p->len = size; + accepted = tud_network_recv_cb(p); + } + + if (!p || !accepted) + { + /* if a buffer couldn't be allocated or accepted by the callback, we must discard this packet */ + tud_network_recv_renew(); + } + } +} + +bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) rhport; + (void) result; + + /* new packet received */ + if ( ep_addr == _netd_itf.ep_out ) + { + handle_incoming_packet(xferred_bytes); + } + + /* data transmission finished */ + if ( ep_addr == _netd_itf.ep_in ) + { + /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */ + + if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) ) + { + do_in_xfer(NULL, 0); /* a ZLP is needed */ + } + else + { + /* we're finally finished */ + can_xmit = true; + } + } + + return true; +} + +bool tud_network_can_xmit(void) +{ + return can_xmit; +} + +void tud_network_xmit(struct pbuf *p) +{ + struct pbuf *q; + uint8_t *data; + uint16_t len; + + if (!can_xmit) + return; + + len = CFG_TUD_NET_PACKET_PREFIX_LEN; + data = transmitted + len; + + for(q = p; q != NULL; q = q->next) + { + memcpy(data, (char *)q->payload, q->len); + data += q->len; + len += q->len; + } + +#if CFG_TUD_NET == OPT_NET_RNDIS + rndis_data_packet_t *hdr = (rndis_data_packet_t *)transmitted; + memset(hdr, 0, sizeof(rndis_data_packet_t)); + hdr->MessageType = REMOTE_NDIS_PACKET_MSG; + hdr->MessageLength = len; + hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset); + hdr->DataLength = len - sizeof(rndis_data_packet_t); +#elif CFG_TUD_NET == OPT_NET_EEM + struct cdc_eem_packet_header *hdr = (struct cdc_eem_packet_header *)transmitted; + /* append a fake CRC-32; the standard allows 0xDEADBEEF, which takes less CPU time */ + data[0] = 0xDE; data[1] = 0xAD; data[2] = 0xBE; data[3] = 0xEF; + /* adjust length to reflect added fake CRC-32 */ + len += 4; + hdr->bmType = 0; /* EEM Data Packet */ + hdr->length = len - sizeof(struct cdc_eem_packet_header); + hdr->bmCRC = 0; /* Ethernet Frame CRC-32 set to 0xDEADBEEF */ +#endif + + do_in_xfer(transmitted, len); +} + +#endif diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h new file mode 100644 index 000000000..71483364d --- /dev/null +++ b/src/class/net/net_device.h @@ -0,0 +1,86 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Peter Lawrence + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_NET_DEVICE_H_ +#define _TUSB_NET_DEVICE_H_ + +#include "common/tusb_common.h" +#include "device/usbd.h" +#include "class/cdc/cdc.h" + +// TODO should not include external files +#include "lwip/pbuf.h" +#include "netif/ethernet.h" + +/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */ +#define CFG_TUD_NET_ENDPOINT_SIZE ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64) + +/* Maximum Tranmission Unit (in bytes) of the network, including Ethernet header */ +#define CFG_TUD_NET_MTU (1500 + SIZEOF_ETH_HDR) + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// client must provide this: initialize any network state back to the beginning +void tud_network_init_cb(void); + +// client must provide this: return false if the packet buffer was not accepted +bool tud_network_recv_cb(struct pbuf *p); + +// client must provide this: 48-bit MAC address +extern const uint8_t tud_network_mac_address[6]; + +// indicate to network driver that client has finished with the packet provided to network_recv_cb() +void tud_network_recv_renew(void); + +// poll network driver for its ability to accept another packet to transmit +bool tud_network_can_xmit(void); + +// if network_can_xmit() returns true, network_xmit() can be called once +void tud_network_xmit(struct pbuf *p); + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void netd_init (void); +void netd_reset (uint8_t rhport); +bool netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length); +bool netd_control_request (uint8_t rhport, tusb_control_request_t const * request); +bool netd_control_complete (uint8_t rhport, tusb_control_request_t const * request); +bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void netd_report (uint8_t *buf, uint16_t len); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_NET_DEVICE_H_ */ |
