just use device/host examples direct from tinyusb
This commit is contained in:
		
							parent
							
								
									7e4e3290d5
								
							
						
					
					
						commit
						40b07ac921
					
				| @ -1,4 +1,6 @@ | ||||
| add_subdirectory(dev_audio_headset) | ||||
| add_subdirectory(dev_hid_composite) | ||||
| add_subdirectory(dev_hid_generic_inout) | ||||
| set(FAMILY rp2040) | ||||
| set(BOARD pico_sdk) | ||||
| set(TINYUSB_FAMILY_PROJECT_NAME_PREFIX "tinyusb_dev_") | ||||
| add_subdirectory(${PICO_TINYUSB_PATH}/examples/device tinyusb_device_examples) | ||||
| 
 | ||||
| add_subdirectory(dev_lowlevel) | ||||
|  | ||||
| @ -1,14 +0,0 @@ | ||||
| set(CMAKE_C_FLAGS_DEBUG "-O0 -g") | ||||
| 
 | ||||
| add_executable(dev_audio_headset | ||||
|         dev_audio_headset.c | ||||
|         usb_descriptors.c | ||||
|         ) | ||||
| 
 | ||||
| target_include_directories(dev_audio_headset PRIVATE ${CMAKE_CURRENT_LIST_DIR}) | ||||
| 
 | ||||
| target_link_libraries(dev_audio_headset PRIVATE pico_stdlib tinyusb_device tinyusb_board) | ||||
| pico_add_extra_outputs(dev_audio_headset) | ||||
| 
 | ||||
| # add url via pico_set_program_url | ||||
| example_auto_set_url(dev_audio_headset) | ||||
| @ -1,358 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Jerzy Kasenberg | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
| #include "usb_descriptors.h" | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // MACRO CONSTANT TYPEDEF PROTOTYPES
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| /* Blink pattern
 | ||||
|  * - 25 ms   : streaming data | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum { | ||||
|     BLINK_STREAMING = 25, | ||||
|     BLINK_NOT_MOUNTED = 250, | ||||
|     BLINK_MOUNTED = 1000, | ||||
|     BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
|     VOLUME_CTRL_0_DB = 0, | ||||
|     VOLUME_CTRL_10_DB = 2560, | ||||
|     VOLUME_CTRL_20_DB = 5120, | ||||
|     VOLUME_CTRL_30_DB = 7680, | ||||
|     VOLUME_CTRL_40_DB = 10240, | ||||
|     VOLUME_CTRL_50_DB = 12800, | ||||
|     VOLUME_CTRL_60_DB = 15360, | ||||
|     VOLUME_CTRL_70_DB = 17920, | ||||
|     VOLUME_CTRL_80_DB = 20480, | ||||
|     VOLUME_CTRL_90_DB = 23040, | ||||
|     VOLUME_CTRL_100_DB = 25600, | ||||
|     VOLUME_CTRL_SILENCE = 0x8000, | ||||
| }; | ||||
| 
 | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| 
 | ||||
| // Audio controls
 | ||||
| // Current states
 | ||||
| int8_t mute[CFG_TUD_AUDIO_N_CHANNELS_TX + 1];       // +1 for master channel 0
 | ||||
| int16_t volume[CFG_TUD_AUDIO_N_CHANNELS_TX + 1];    // +1 for master channel 0
 | ||||
| 
 | ||||
| // Buffer for microphone data
 | ||||
| int16_t mic_buf[1000]; | ||||
| // Buffer for speaker data
 | ||||
| int16_t spk_buf[1000]; | ||||
| // Speaker data size received in the last frame
 | ||||
| int spk_data_size; | ||||
| 
 | ||||
| void led_blinking_task(void); | ||||
| void audio_task(void); | ||||
| 
 | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) { | ||||
|     board_init(); | ||||
| 
 | ||||
|     tusb_init(); | ||||
| 
 | ||||
|     TU_LOG1("Headset running\r\n"); | ||||
| 
 | ||||
|     while (1) { | ||||
|         tud_task(); // TinyUSB device task
 | ||||
|         audio_task(); | ||||
|         led_blinking_task(); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device callbacks
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // Invoked when device is mounted
 | ||||
| void tud_mount_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when device is unmounted
 | ||||
| void tud_umount_cb(void) { | ||||
|     blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is suspended
 | ||||
| // remote_wakeup_en : if host allow us  to perform remote wakeup
 | ||||
| // Within 7ms, device must draw an average of current less than 2.5 mA from bus
 | ||||
| void tud_suspend_cb(bool remote_wakeup_en) { | ||||
|     (void) remote_wakeup_en; | ||||
|     blink_interval_ms = BLINK_SUSPENDED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is resumed
 | ||||
| void tud_resume_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|     union { | ||||
|         struct TU_ATTR_PACKED { | ||||
|             uint8_t recipient: 5; ///< Recipient type tusb_request_recipient_t.
 | ||||
|             uint8_t type: 2; ///< Request type tusb_request_type_t.
 | ||||
|             uint8_t direction: 1; ///< Direction type. tusb_dir_t
 | ||||
|         } bmRequestType_bit; | ||||
| 
 | ||||
|         uint8_t bmRequestType; | ||||
|     }; | ||||
| 
 | ||||
|     audio_cs_req_t bRequest; | ||||
|     uint8_t bChannelNumber; | ||||
|     uint8_t bControlSelector; | ||||
|     union { | ||||
|         uint8_t bInterface; | ||||
|         uint8_t bEndpoint; | ||||
|     }; | ||||
|     uint8_t bEntityID; | ||||
|     uint16_t wLength; | ||||
| } audio_control_request_t; | ||||
| 
 | ||||
| // Helper for clock get requests
 | ||||
| static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) { | ||||
|     TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); | ||||
| 
 | ||||
|     // Example supports only single frequency, same value will be used for current value and range
 | ||||
|     if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { | ||||
|         if (request->bRequest == AUDIO_CS_REQ_CUR) { | ||||
|             TU_LOG2("Clock get current freq %u\r\n", AUDIO_SAMPLE_RATE); | ||||
| 
 | ||||
|             audio_control_cur_4_t curf = {tu_htole32(AUDIO_SAMPLE_RATE)}; | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, | ||||
|                                                               sizeof(curf)); | ||||
|         } else if (request->bRequest == AUDIO_CS_REQ_RANGE) { | ||||
|             audio_control_range_4_n_t(1) rangef = | ||||
|                     { | ||||
|                             .wNumSubRanges = tu_htole16(1), | ||||
|                             .subrange[0] = {tu_htole32(AUDIO_SAMPLE_RATE), tu_htole32(AUDIO_SAMPLE_RATE), 0} | ||||
|                     }; | ||||
|             TU_LOG2("Clock get freq range (%d, %d, %d)\r\n", (int) rangef.subrange[0].bMin, | ||||
|                     (int) rangef.subrange[0].bMax, (int) rangef.subrange[0].bRes); | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, | ||||
|                                                               sizeof(rangef)); | ||||
|         } | ||||
|     } else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && | ||||
|                request->bRequest == AUDIO_CS_REQ_CUR) { | ||||
|         audio_control_cur_1_t cur_valid = {.bCur = 1}; | ||||
|         TU_LOG2("Clock get is valid %u\r\n", cur_valid.bCur); | ||||
|         return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, | ||||
|                                                           sizeof(cur_valid)); | ||||
|     } | ||||
|     TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", | ||||
|             request->bEntityID, request->bControlSelector, request->bRequest); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Helper for feature unit get requests
 | ||||
| static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) { | ||||
|     TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); | ||||
| 
 | ||||
|     if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) { | ||||
|         audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]}; | ||||
|         TU_LOG2("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); | ||||
|         return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, | ||||
|                                                           sizeof(mute1)); | ||||
|     } else if (UAC2_ENTITY_SPK_FEATURE_UNIT && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { | ||||
|         if (request->bRequest == AUDIO_CS_REQ_RANGE) { | ||||
|             audio_control_range_2_n_t(1) range_vol = { | ||||
|                     .wNumSubRanges = tu_htole16(1), | ||||
|                     .subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), | ||||
|                                                                             tu_htole16(256)} | ||||
|             }; | ||||
|             TU_LOG2("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, | ||||
|                     range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, | ||||
|                     range_vol.subrange[0].bRes / 256); | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, | ||||
|                                                               &range_vol, sizeof(range_vol)); | ||||
|         } else if (request->bRequest == AUDIO_CS_REQ_CUR) { | ||||
|             audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])}; | ||||
|             TU_LOG2("Get channel %u volume %u dB\r\n", request->bChannelNumber, cur_vol.bCur); | ||||
|             return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, | ||||
|                                                               &cur_vol, sizeof(cur_vol)); | ||||
|         } | ||||
|     } | ||||
|     TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", | ||||
|             request->bEntityID, request->bControlSelector, request->bRequest); | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Helper for feature unit set requests
 | ||||
| static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, | ||||
|                                                uint8_t const *buf) { | ||||
|     (void) rhport; | ||||
| 
 | ||||
|     TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); | ||||
|     TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); | ||||
| 
 | ||||
|     if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) { | ||||
|         TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); | ||||
| 
 | ||||
|         mute[request->bChannelNumber] = ((audio_control_cur_1_t *) buf)->bCur; | ||||
| 
 | ||||
|         TU_LOG2("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); | ||||
| 
 | ||||
|         return true; | ||||
|     } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { | ||||
|         TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); | ||||
| 
 | ||||
|         volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur; | ||||
| 
 | ||||
|         TU_LOG2("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); | ||||
| 
 | ||||
|         return true; | ||||
|     } else { | ||||
|         TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", | ||||
|                 request->bEntityID, request->bControlSelector, request->bRequest); | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Application Callback API Implementations
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // Invoked when audio class specific get request received for an entity
 | ||||
| bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { | ||||
|     audio_control_request_t *request = (audio_control_request_t *) p_request; | ||||
| 
 | ||||
|     if (request->bEntityID == UAC2_ENTITY_CLOCK) | ||||
|         return tud_audio_clock_get_request(rhport, request); | ||||
|     if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) | ||||
|         return tud_audio_feature_unit_get_request(rhport, request); | ||||
|     else { | ||||
|         TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", | ||||
|                 request->bEntityID, request->bControlSelector, request->bRequest); | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // Invoked when audio class specific set request received for an entity
 | ||||
| bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) { | ||||
|     audio_control_request_t const *request = (audio_control_request_t const *) p_request; | ||||
| 
 | ||||
|     if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) | ||||
|         return tud_audio_feature_unit_set_request(rhport, request, buf); | ||||
| 
 | ||||
|     TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", | ||||
|             request->bEntityID, request->bControlSelector, request->bRequest); | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) { | ||||
|     (void) rhport; | ||||
| 
 | ||||
|     uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); | ||||
|     uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); | ||||
| 
 | ||||
|     if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) | ||||
|         blink_interval_ms = BLINK_MOUNTED; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { | ||||
|     (void) rhport; | ||||
|     uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); | ||||
|     uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); | ||||
| 
 | ||||
|     TU_LOG2("Set interface %d alt %d\r\n", itf, alt); | ||||
|     if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) | ||||
|         blink_interval_ms = BLINK_STREAMING; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool tud_audio_rx_done_cb(uint8_t rhport, uint8_t *buffer, uint16_t buf_size) { | ||||
|     (void) rhport; | ||||
| 
 | ||||
|     spk_data_size = buf_size; | ||||
|     memcpy(spk_buf, buffer, buf_size); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) { | ||||
|     (void) rhport; | ||||
|     (void) itf; | ||||
|     (void) ep_in; | ||||
|     (void) cur_alt_setting; | ||||
| 
 | ||||
|     // This callback could be used to fill microphone data separately
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // AUDIO Task
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| void audio_task(void) { | ||||
|     // When new data arrived, copy data from speaker buffer, to microphone buffer
 | ||||
|     // and send it over
 | ||||
|     if (spk_data_size) { | ||||
|         int16_t *src = spk_buf; | ||||
|         int16_t *limit = spk_buf + spk_data_size / 2; | ||||
|         int16_t *dst = mic_buf; | ||||
|         while (src < limit) { | ||||
|             // Combine two channels into one
 | ||||
|             int32_t left = *src++; | ||||
|             int32_t right = *src++; | ||||
|             *dst++ = (int16_t) ((left + right) / 2); | ||||
|         } | ||||
|         tud_audio_write((uint8_t *) mic_buf, spk_data_size / 2); | ||||
|         spk_data_size = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // BLINKING TASK
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| void led_blinking_task(void) { | ||||
|     static uint32_t start_ms = 0; | ||||
|     static bool led_state = false; | ||||
| 
 | ||||
|     // Blink every interval ms
 | ||||
|     if (board_millis() - start_ms < blink_interval_ms) return; | ||||
|     start_ms += blink_interval_ms; | ||||
| 
 | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; | ||||
| } | ||||
| @ -1,132 +0,0 @@ | ||||
| /*
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Ha Thach (tinyusb.org) | ||||
|  * Copyright (c) 2020 Jerzy Kasenberg | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // COMMON CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| // defined by compiler flags for flexibility
 | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX | ||||
| #define CFG_TUSB_RHPORT0_MODE       (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) | ||||
| #else | ||||
| #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_DEVICE | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_DEBUG | ||||
| // Can be set during compilation i.e.: make LOG=<value for CFG_TUSB_DEBUG> BOARD=<bsp>
 | ||||
| // Keep in mind that enabling logs when data is streaming can disrupt data flow.
 | ||||
| // It can be very helpful though when audio unit requests are tested/debugged.
 | ||||
| #define CFG_TUSB_DEBUG              2 | ||||
| #endif | ||||
| 
 | ||||
| /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
 | ||||
|  * Tinyusb use follows macros to declare transferring memory so that they can be put | ||||
|  * into those specific section. | ||||
|  * e.g | ||||
|  * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) | ||||
|  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4))) | ||||
|  */ | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4))) | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // DEVICE CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| #ifndef CFG_TUD_ENDPOINT0_SIZE | ||||
| #define CFG_TUD_ENDPOINT0_SIZE    64 | ||||
| #endif | ||||
| 
 | ||||
| //------------- CLASS -------------//
 | ||||
| #define CFG_TUD_CDC               0 | ||||
| #define CFG_TUD_MSC               0 | ||||
| #define CFG_TUD_HID               0 | ||||
| #define CFG_TUD_MIDI              0 | ||||
| #define CFG_TUD_AUDIO             1 | ||||
| #define CFG_TUD_VENDOR            0 | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // AUDIO CLASS DRIVER CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| #ifndef AUDIO_SAMPLE_RATE | ||||
| #define AUDIO_SAMPLE_RATE                   48000 | ||||
| #endif | ||||
| 
 | ||||
| #define CFG_TUD_AUDIO_IN_PATH               (CFG_TUD_AUDIO) | ||||
| #define CFG_TUD_AUDIO_OUT_PATH              (CFG_TUD_AUDIO) | ||||
| 
 | ||||
| // Audio format type
 | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_TX        AUDIO_FORMAT_TYPE_I | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_RX        AUDIO_FORMAT_TYPE_I | ||||
| 
 | ||||
| // Audio format type I specifications
 | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX      AUDIO_DATA_FORMAT_TYPE_I_PCM | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX      AUDIO_DATA_FORMAT_TYPE_I_PCM | ||||
| #define CFG_TUD_AUDIO_N_CHANNELS_TX         1 | ||||
| #define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 2 | ||||
| #define CFG_TUD_AUDIO_N_CHANNELS_RX         2 | ||||
| #define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 2 | ||||
| #define CFG_TUD_AUDIO_RX_ITEMSIZE           2 | ||||
| #define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP    0 | ||||
| 
 | ||||
| // EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
 | ||||
| #define CFG_TUD_AUDIO_EPSIZE_IN           (CFG_TUD_AUDIO_IN_PATH * (48 + 1) * (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX) * (CFG_TUD_AUDIO_N_CHANNELS_TX)) // 48 Samples (48 kHz) x 2 Bytes/Sample x n Channels
 | ||||
| #define CFG_TUD_AUDIO_TX_FIFO_COUNT       (CFG_TUD_AUDIO_IN_PATH * 1) | ||||
| #define CFG_TUD_AUDIO_TX_FIFO_SIZE        (CFG_TUD_AUDIO_IN_PATH ? ((CFG_TUD_AUDIO_EPSIZE_IN)) : 0) | ||||
| 
 | ||||
| // EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
 | ||||
| #define CFG_TUD_AUDIO_EPSIZE_OUT          (CFG_TUD_AUDIO_OUT_PATH * ((48 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX) * (CFG_TUD_AUDIO_N_CHANNELS_RX))) // N Samples (N kHz) x 2 Bytes/Sample x n Channels
 | ||||
| #define CFG_TUD_AUDIO_RX_FIFO_COUNT       (CFG_TUD_AUDIO_OUT_PATH * 1) | ||||
| #define CFG_TUD_AUDIO_RX_FIFO_SIZE        (CFG_TUD_AUDIO_OUT_PATH ? (3 * (CFG_TUD_AUDIO_EPSIZE_OUT / CFG_TUD_AUDIO_RX_FIFO_COUNT)) : 0) | ||||
| 
 | ||||
| // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
 | ||||
| #define CFG_TUD_AUDIO_N_AS_INT                    1 | ||||
| 
 | ||||
| // Size of control request buffer
 | ||||
| #define CFG_TUD_AUDIO_CTRL_BUF_SIZE                64 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
| @ -1,159 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Ha Thach (tinyusb.org) | ||||
|  * Copyright (c) 2020 Jerzy Kasenberg | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "tusb.h" | ||||
| #include "usb_descriptors.h" | ||||
| 
 | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
 | ||||
|  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. | ||||
|  * | ||||
|  * Auto ProductID layout's Bitmap: | ||||
|  *   [MSB]     AUDIO | MIDI | HID | MSC | CDC          [LSB] | ||||
|  */ | ||||
| #define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) ) | ||||
| #define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ | ||||
|     _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| tusb_desc_device_t const desc_device = | ||||
|         { | ||||
|                 .bLength            = sizeof(tusb_desc_device_t), | ||||
|                 .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|                 .bcdUSB             = 0x0200, | ||||
| 
 | ||||
|                 // Use Interface Association Descriptor (IAD) for CDC
 | ||||
|                 // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
 | ||||
|                 .bDeviceClass       = TUSB_CLASS_MISC, | ||||
|                 .bDeviceSubClass    = MISC_SUBCLASS_COMMON, | ||||
|                 .bDeviceProtocol    = MISC_PROTOCOL_IAD, | ||||
|                 .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
| 
 | ||||
|                 .idVendor           = 0xCafe, | ||||
|                 .idProduct          = USB_PID, | ||||
|                 .bcdDevice          = 0x0100, | ||||
| 
 | ||||
|                 .iManufacturer      = 0x01, | ||||
|                 .iProduct           = 0x02, | ||||
|                 .iSerialNumber      = 0x03, | ||||
| 
 | ||||
|                 .bNumConfigurations = 0x01 | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET DEVICE DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| uint8_t const *tud_descriptor_device_cb(void) { | ||||
|     return (uint8_t const *) &desc_device; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Configuration Descriptor
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| #define CONFIG_TOTAL_LEN        (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_HEADSET_STEREO_DESC_LEN) | ||||
| 
 | ||||
| #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX | ||||
| // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
 | ||||
| // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
 | ||||
| #define EPNUM_AUDIO   0x03 | ||||
| #elif CFG_TUSB_MCU == OPT_MCU_NRF5X | ||||
| // ISO endpoints for NRF5x are fixed to 0x08 (0x88)
 | ||||
| #define EPNUM_AUDIO   0x08 | ||||
| #else | ||||
| #define EPNUM_AUDIO   0x01 | ||||
| #endif | ||||
| 
 | ||||
| // These variables are required by the audio driver in audio_device.c
 | ||||
| 
 | ||||
| // List of audio descriptor lengths which is required by audio driver - you need as many entries as CFG_TUD_AUDIO
 | ||||
| const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_HEADSET_STEREO_DESC_LEN}; | ||||
| 
 | ||||
| uint8_t const desc_configuration[] = | ||||
|         { | ||||
|                 // Interface count, string index, total length, attribute, power in mA
 | ||||
|                 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), | ||||
| 
 | ||||
|                 // Interface number, string index, EP Out & EP In address, EP size
 | ||||
|                 TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, 2, 16, EPNUM_AUDIO, CFG_TUD_AUDIO_EPSIZE_OUT, EPNUM_AUDIO | 0x80, | ||||
|                                                     CFG_TUD_AUDIO_EPSIZE_IN) | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET CONFIGURATION DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| // Descriptor contents must exist long enough for transfer to complete
 | ||||
| uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { | ||||
|     (void) index; // for multiple configurations
 | ||||
|     return desc_configuration; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // String Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // array of pointer to string descriptors
 | ||||
| char const *string_desc_arr[] = | ||||
|         { | ||||
|                 (const char[]) {0x09, 0x04},  // 0: is supported language is English (0x0409)
 | ||||
|                 "TinyUSB",                      // 1: Manufacturer
 | ||||
|                 "TinyUSB headset",              // 2: Product
 | ||||
|                 "000001",                       // 3: Serials, should use chip ID
 | ||||
|                 "TinyUSB Speakers",             // 4: Audio Interface
 | ||||
|                 "TinyUSB Microphone",           // 5: Audio Interface
 | ||||
|         }; | ||||
| 
 | ||||
| static uint16_t _desc_str[32]; | ||||
| 
 | ||||
| // Invoked when received GET STRING DESCRIPTOR request
 | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 | ||||
| uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { | ||||
|     (void) langid; | ||||
| 
 | ||||
|     uint8_t chr_count; | ||||
| 
 | ||||
|     if (index == 0) { | ||||
|         memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|         chr_count = 1; | ||||
|     } else { | ||||
|         // Convert ASCII string into UTF-16
 | ||||
| 
 | ||||
|         if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; | ||||
| 
 | ||||
|         const char *str = string_desc_arr[index]; | ||||
| 
 | ||||
|         // Cap at max char
 | ||||
|         chr_count = strlen(str); | ||||
|         if (chr_count > 31) chr_count = 31; | ||||
| 
 | ||||
|         for (uint8_t i = 0; i < chr_count; i++) { | ||||
|             _desc_str[1 + i] = str[i]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // first byte is length (including header), second byte is string type
 | ||||
|     _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); | ||||
| 
 | ||||
|     return _desc_str; | ||||
| } | ||||
| @ -1,119 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Jerzy Kasenbreg | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _USB_DESCRIPTORS_H_ | ||||
| #define _USB_DESCRIPTORS_H_ | ||||
| 
 | ||||
| #include "tusb.h" | ||||
| 
 | ||||
| // Unit numbers are arbitrary selected
 | ||||
| #define UAC2_ENTITY_CLOCK               0x04 | ||||
| // Speaker path
 | ||||
| #define UAC2_ENTITY_SPK_INPUT_TERMINAL  0x01 | ||||
| #define UAC2_ENTITY_SPK_FEATURE_UNIT    0x02 | ||||
| #define UAC2_ENTITY_SPK_OUTPUT_TERMINAL 0x03 | ||||
| // Microphone path
 | ||||
| #define UAC2_ENTITY_MIC_INPUT_TERMINAL  0x11 | ||||
| #define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13 | ||||
| 
 | ||||
| enum { | ||||
|     ITF_NUM_AUDIO_CONTROL = 0, | ||||
|     ITF_NUM_AUDIO_STREAMING_SPK, | ||||
|     ITF_NUM_AUDIO_STREAMING_MIC, | ||||
|     ITF_NUM_TOTAL | ||||
| }; | ||||
| 
 | ||||
| #define TUD_AUDIO_HEADSET_STEREO_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AC_LEN\ | ||||
|     + TUD_AUDIO_DESC_CS_AC_LEN\ | ||||
|     + TUD_AUDIO_DESC_CLK_SRC_LEN\ | ||||
|     + TUD_AUDIO_DESC_INPUT_TERM_LEN\ | ||||
|     + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN\ | ||||
|     + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ | ||||
|     + TUD_AUDIO_DESC_INPUT_TERM_LEN\ | ||||
|     + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_CS_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ | ||||
|     + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_CS_AS_INT_LEN\ | ||||
|     + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN\ | ||||
|     + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\ | ||||
|     + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) | ||||
| 
 | ||||
| 
 | ||||
| #define TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epoutsize, _epin, _epinsize) \ | ||||
|     /* Standard Interface Association Descriptor (IAD) */\ | ||||
|     TUD_AUDIO_DESC_IAD(/*_firstitfs*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ ITF_NUM_TOTAL, /*_stridx*/ 0x00),\ | ||||
|     /* Standard AC Interface Descriptor(4.7.1) */\ | ||||
|     TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ | ||||
|     /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ | ||||
|     TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ | ||||
|     /* Clock Source Descriptor(4.7.2.1) */\ | ||||
|     TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 5, /*_assocTerm*/ 0x00,  /*_stridx*/ 0x00),    \ | ||||
|     /* Input Terminal Descriptor(4.7.2.4) */\ | ||||
|     TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ | ||||
|     /* Feature Unit Descriptor(4.7.2.8) */\ | ||||
|     TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch1*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_ctrlch2*/ (AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), /*_stridx*/ 0x00),\ | ||||
|     /* Output Terminal Descriptor(4.7.2.5) */\ | ||||
|     TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ | ||||
|     /* Input Terminal Descriptor(4.7.2.4) */\ | ||||
|     TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ | ||||
|     /* Output Terminal Descriptor(4.7.2.5) */\ | ||||
|     TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ | ||||
|     /* Standard AS Interface Descriptor(4.9.1) */\ | ||||
|     /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x05),\ | ||||
|     /* Standard AS Interface Descriptor(4.9.1) */\ | ||||
|     /* Interface 1, Alternate 1 - alternate interface for data streaming */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\ | ||||
|     /* Class-Specific AS Interface Descriptor(4.9.2) */\ | ||||
|     TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ | ||||
|     /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ | ||||
|     TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ | ||||
|     /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ADAPTIVE | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epoutsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01),\ | ||||
|     /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ | ||||
|     TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_MILLISEC, /*_lockdelay*/ 0x0001),\ | ||||
|     /* Standard AS Interface Descriptor(4.9.1) */\ | ||||
|     /* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\ | ||||
|     /* Standard AS Interface Descriptor(4.9.1) */\ | ||||
|     /* Interface 1, Alternate 1 - alternate interface for data streaming */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\ | ||||
|     /* Class-Specific AS Interface Descriptor(4.9.2) */\ | ||||
|     TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ | ||||
|     /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ | ||||
|     TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\ | ||||
|     /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ | ||||
|     TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epinsize, /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01),\ | ||||
|     /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\ | ||||
|     TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)\ | ||||
| 
 | ||||
| #endif | ||||
| @ -1,12 +0,0 @@ | ||||
| add_executable(dev_hid_composite | ||||
|         dev_hid_composite.c | ||||
|         usb_descriptors.c | ||||
|         ) | ||||
| 
 | ||||
| target_include_directories(dev_hid_composite PRIVATE ${CMAKE_CURRENT_LIST_DIR}) | ||||
| 
 | ||||
| target_link_libraries(dev_hid_composite PRIVATE pico_stdlib tinyusb_device tinyusb_board) | ||||
| pico_add_extra_outputs(dev_hid_composite) | ||||
| 
 | ||||
| # add url via pico_set_program_url | ||||
| example_auto_set_url(dev_hid_composite) | ||||
| @ -1,189 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
| 
 | ||||
| #include "usb_descriptors.h" | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| /* Blink pattern
 | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum { | ||||
|     BLINK_NOT_MOUNTED = 250, | ||||
|     BLINK_MOUNTED = 1000, | ||||
|     BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
| 
 | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| 
 | ||||
| void led_blinking_task(void); | ||||
| void hid_task(void); | ||||
| 
 | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) { | ||||
|     board_init(); | ||||
|     tusb_init(); | ||||
| 
 | ||||
|     while (1) { | ||||
|         tud_task(); // tinyusb device task
 | ||||
|         led_blinking_task(); | ||||
| 
 | ||||
|         hid_task(); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device callbacks
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // Invoked when device is mounted
 | ||||
| void tud_mount_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when device is unmounted
 | ||||
| void tud_umount_cb(void) { | ||||
|     blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is suspended
 | ||||
| // remote_wakeup_en : if host allow us  to perform remote wakeup
 | ||||
| // Within 7ms, device must draw an average of current less than 2.5 mA from bus
 | ||||
| void tud_suspend_cb(bool remote_wakeup_en) { | ||||
|     (void) remote_wakeup_en; | ||||
|     blink_interval_ms = BLINK_SUSPENDED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is resumed
 | ||||
| void tud_resume_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // USB HID
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| void hid_task(void) { | ||||
|     // Poll every 10ms
 | ||||
|     const uint32_t interval_ms = 10; | ||||
|     static uint32_t start_ms = 0; | ||||
| 
 | ||||
|     if (board_millis() - start_ms < interval_ms) return; // not enough time
 | ||||
|     start_ms += interval_ms; | ||||
| 
 | ||||
|     uint32_t const btn = 1; | ||||
| 
 | ||||
|     // Remote wakeup
 | ||||
|     if (tud_suspended() && btn) { | ||||
|         // Wake up host if we are in suspend mode
 | ||||
|         // and REMOTE_WAKEUP feature is enabled by host
 | ||||
|         tud_remote_wakeup(); | ||||
|     } | ||||
| 
 | ||||
|     /*------------- Mouse -------------*/ | ||||
|     if (tud_hid_ready()) { | ||||
|         if (btn) { | ||||
|             int8_t const delta = 5; | ||||
| 
 | ||||
|             // no button, right + down, no scroll pan
 | ||||
|             tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, delta, delta, 0, 0); | ||||
| 
 | ||||
|             // delay a bit before attempt to send keyboard report
 | ||||
|             board_delay(10); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /*------------- Keyboard -------------*/ | ||||
|     if (tud_hid_ready()) { | ||||
|         // use to avoid send multiple consecutive zero report for keyboard
 | ||||
|         static bool has_key = false; | ||||
| 
 | ||||
|         static bool toggle = false; | ||||
|         if ((toggle = !toggle)) { | ||||
|             uint8_t keycode[6] = {0}; | ||||
|             keycode[0] = HID_KEY_A; | ||||
| 
 | ||||
|             tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode); | ||||
| 
 | ||||
|             has_key = true; | ||||
|         } else { | ||||
|             // send empty key report if previously has key pressed
 | ||||
|             if (has_key) tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL); | ||||
|             has_key = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // 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) { | ||||
|     // TODO not Implemented
 | ||||
|     (void) report_id; | ||||
|     (void) report_type; | ||||
|     (void) buffer; | ||||
|     (void) reqlen; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // 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) { | ||||
|     // TODO set LED based on CAPLOCK, NUMLOCK etc...
 | ||||
|     (void) report_id; | ||||
|     (void) report_type; | ||||
|     (void) buffer; | ||||
|     (void) bufsize; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // BLINKING TASK
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| void led_blinking_task(void) { | ||||
|     static uint32_t start_ms = 0; | ||||
|     static bool led_state = false; | ||||
| 
 | ||||
|     // Blink every interval ms
 | ||||
|     if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
 | ||||
|     start_ms += blink_interval_ms; | ||||
| 
 | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; // toggle
 | ||||
| } | ||||
| @ -1,93 +0,0 @@ | ||||
| /*
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // COMMON CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| // defined by compiler flags for flexibility
 | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ | ||||
|     CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 | ||||
| #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) | ||||
| #else | ||||
| #define CFG_TUSB_RHPORT0_MODE     OPT_MODE_DEVICE | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_OS | ||||
| #define CFG_TUSB_OS                 OPT_OS_PICO | ||||
| #endif | ||||
| 
 | ||||
| // CFG_TUSB_DEBUG is defined by compiler in DEBUG build
 | ||||
| // #define CFG_TUSB_DEBUG           0
 | ||||
| 
 | ||||
| /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
 | ||||
|  * Tinyusb use follows macros to declare transferring memory so that they can be put | ||||
|  * into those specific section. | ||||
|  * e.g | ||||
|  * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) | ||||
|  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4))) | ||||
|  */ | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4))) | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // DEVICE CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| #ifndef CFG_TUD_ENDPOINT0_SIZE | ||||
| #define CFG_TUD_ENDPOINT0_SIZE    64 | ||||
| #endif | ||||
| 
 | ||||
| //------------- CLASS -------------//
 | ||||
| #define CFG_TUD_HID             1 | ||||
| #define CFG_TUD_CDC             0 | ||||
| #define CFG_TUD_MSC             0 | ||||
| #define CFG_TUD_MIDI            0 | ||||
| #define CFG_TUD_VENDOR          0 | ||||
| 
 | ||||
| // HID buffer size Should be sufficient to hold ID (if any) + Data
 | ||||
| #define CFG_TUD_HID_BUFSIZE     16 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
| @ -1,162 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "tusb.h" | ||||
| #include "usb_descriptors.h" | ||||
| 
 | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
 | ||||
|  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. | ||||
|  * | ||||
|  * Auto ProductID layout's Bitmap: | ||||
|  *   [MSB]         HID | MSC | CDC          [LSB] | ||||
|  */ | ||||
| #define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) ) | ||||
| #define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ | ||||
|                            _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| tusb_desc_device_t const desc_device = | ||||
|         { | ||||
|                 .bLength            = sizeof(tusb_desc_device_t), | ||||
|                 .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|                 .bcdUSB             = 0x0200, | ||||
|                 .bDeviceClass       = 0x00, | ||||
|                 .bDeviceSubClass    = 0x00, | ||||
|                 .bDeviceProtocol    = 0x00, | ||||
|                 .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
| 
 | ||||
|                 .idVendor           = 0xCafe, | ||||
|                 .idProduct          = USB_PID, | ||||
|                 .bcdDevice          = 0x0100, | ||||
| 
 | ||||
|                 .iManufacturer      = 0x01, | ||||
|                 .iProduct           = 0x02, | ||||
|                 .iSerialNumber      = 0x03, | ||||
| 
 | ||||
|                 .bNumConfigurations = 0x01 | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET DEVICE DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| uint8_t const *tud_descriptor_device_cb(void) { | ||||
|     return (uint8_t const *) &desc_device; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // HID Report Descriptor
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| uint8_t const desc_hid_report[] = | ||||
|         { | ||||
|                 TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)), | ||||
|                 TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE)) | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET HID REPORT DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| // Descriptor contents must exist long enough for transfer to complete
 | ||||
| uint8_t const *tud_hid_descriptor_report_cb(void) { | ||||
|     return desc_hid_report; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Configuration Descriptor
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| enum { | ||||
|     ITF_NUM_HID, | ||||
|     ITF_NUM_TOTAL | ||||
| }; | ||||
| 
 | ||||
| #define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN) | ||||
| 
 | ||||
| #define EPNUM_HID   0x81 | ||||
| 
 | ||||
| uint8_t const desc_configuration[] = | ||||
|         { | ||||
|                 // Config number, interface count, string index, total length, attribute, power in mA
 | ||||
|                 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), | ||||
| 
 | ||||
|                 // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
 | ||||
|                 TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, | ||||
|                                    CFG_TUD_HID_BUFSIZE, 10) | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET CONFIGURATION DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| // Descriptor contents must exist long enough for transfer to complete
 | ||||
| uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { | ||||
|     (void) index; // for multiple configurations
 | ||||
|     return desc_configuration; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // String Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // array of pointer to string descriptors
 | ||||
| char const *string_desc_arr[] = | ||||
|         { | ||||
|                 (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
 | ||||
|                 "TinyUSB",                     // 1: Manufacturer
 | ||||
|                 "TinyUSB Device",              // 2: Product
 | ||||
|                 "123456",                      // 3: Serials, should use chip ID
 | ||||
|         }; | ||||
| 
 | ||||
| static uint16_t _desc_str[32]; | ||||
| 
 | ||||
| // Invoked when received GET STRING DESCRIPTOR request
 | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 | ||||
| uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { | ||||
|     (void) langid; | ||||
| 
 | ||||
|     uint8_t chr_count; | ||||
| 
 | ||||
|     if (index == 0) { | ||||
|         memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|         chr_count = 1; | ||||
|     } else { | ||||
|         // Convert ASCII string into UTF-16
 | ||||
| 
 | ||||
|         if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; | ||||
| 
 | ||||
|         const char *str = string_desc_arr[index]; | ||||
| 
 | ||||
|         // Cap at max char
 | ||||
|         chr_count = strlen(str); | ||||
|         if (chr_count > 31) chr_count = 31; | ||||
| 
 | ||||
|         for (uint8_t i = 0; i < chr_count; i++) { | ||||
|             _desc_str[1 + i] = str[i]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // first byte is length (including header), second byte is string type
 | ||||
|     _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); | ||||
| 
 | ||||
|     return _desc_str; | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef USB_DESCRIPTORS_H_ | ||||
| #define USB_DESCRIPTORS_H_ | ||||
| 
 | ||||
| enum { | ||||
|     REPORT_ID_KEYBOARD = 1, | ||||
|     REPORT_ID_MOUSE | ||||
| }; | ||||
| 
 | ||||
| #endif /* USB_DESCRIPTORS_H_ */ | ||||
| @ -1,12 +0,0 @@ | ||||
| add_executable(dev_hid_generic_inout | ||||
|         dev_hid_generic_inout.c | ||||
|         usb_descriptors.c | ||||
|         ) | ||||
| 
 | ||||
| target_include_directories(dev_hid_generic_inout PRIVATE ${CMAKE_CURRENT_LIST_DIR}) | ||||
| 
 | ||||
| target_link_libraries(dev_hid_generic_inout PRIVATE pico_stdlib tinyusb_device tinyusb_board) | ||||
| pico_add_extra_outputs(dev_hid_generic_inout) | ||||
| 
 | ||||
| # add url via pico_set_program_url | ||||
| example_auto_set_url(dev_hid_generic_inout) | ||||
| @ -1,152 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
| 
 | ||||
| /* This example demonstrate HID Generic raw Input & Output.
 | ||||
|  * It will receive data from Host (In endpoint) and echo back (Out endpoint). | ||||
|  * HID Report descriptor use vendor for usage page (using template TUD_HID_REPORT_DESC_GENERIC_INOUT) | ||||
|  * | ||||
|  * There are 2 ways to test the sketch | ||||
|  * 1. Using nodejs | ||||
|  *    - Install nodejs and npm to your PC | ||||
|  *    - Install excellent node-hid (https://github.com/node-hid/node-hid) by
 | ||||
|  *      $ npm install node-hid | ||||
|  *    - Run provided hid test script | ||||
|  *      $ node hid_test.js | ||||
|  * | ||||
|  * 2. Using python hidRun | ||||
|  *    - Python and `hid` package is required, for installation please follow https://pypi.org/project/hid/
 | ||||
|  *    - Run provided hid test script to send and receive data to this device. | ||||
|  *      $ python3 hid_test.py | ||||
|  */ | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| /* Blink pattern
 | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum { | ||||
|     BLINK_NOT_MOUNTED = 250, | ||||
|     BLINK_MOUNTED = 1000, | ||||
|     BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
| 
 | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| 
 | ||||
| void led_blinking_task(void); | ||||
| 
 | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) { | ||||
|     board_init(); | ||||
| 
 | ||||
|     tusb_init(); | ||||
| 
 | ||||
|     while (1) { | ||||
|         tud_task(); // tinyusb device task
 | ||||
|         led_blinking_task(); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device callbacks
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // Invoked when device is mounted
 | ||||
| void tud_mount_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when device is unmounted
 | ||||
| void tud_umount_cb(void) { | ||||
|     blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is suspended
 | ||||
| // remote_wakeup_en : if host allow us  to perform remote wakeup
 | ||||
| // Within 7ms, device must draw an average of current less than 2.5 mA from bus
 | ||||
| void tud_suspend_cb(bool remote_wakeup_en) { | ||||
|     (void) remote_wakeup_en; | ||||
|     blink_interval_ms = BLINK_SUSPENDED; | ||||
| } | ||||
| 
 | ||||
| // Invoked when usb bus is resumed
 | ||||
| void tud_resume_cb(void) { | ||||
|     blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // USB HID
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // 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) { | ||||
|     // TODO not Implemented
 | ||||
|     (void) report_id; | ||||
|     (void) report_type; | ||||
|     (void) buffer; | ||||
|     (void) reqlen; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // 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) { | ||||
|     // This example doesn't use multiple report and report ID
 | ||||
|     (void) report_id; | ||||
|     (void) report_type; | ||||
| 
 | ||||
|     // echo back anything we received from host
 | ||||
|     tud_hid_report(0, buffer, bufsize); | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // BLINKING TASK
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| void led_blinking_task(void) { | ||||
|     static uint32_t start_ms = 0; | ||||
|     static bool led_state = false; | ||||
| 
 | ||||
|     // Blink every interval ms
 | ||||
|     if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
 | ||||
|     start_ms += blink_interval_ms; | ||||
| 
 | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; // toggle
 | ||||
| } | ||||
| @ -1,93 +0,0 @@ | ||||
| /*
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // COMMON CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| // defined by compiler flags for flexibility
 | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ | ||||
|     CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 | ||||
| #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) | ||||
| #else | ||||
| #define CFG_TUSB_RHPORT0_MODE     OPT_MODE_DEVICE | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_OS | ||||
| #define CFG_TUSB_OS                 OPT_OS_PICO | ||||
| #endif | ||||
| 
 | ||||
| // CFG_TUSB_DEBUG is defined by compiler in DEBUG build
 | ||||
| // #define CFG_TUSB_DEBUG           0
 | ||||
| 
 | ||||
| /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
 | ||||
|  * Tinyusb use follows macros to declare transferring memory so that they can be put | ||||
|  * into those specific section. | ||||
|  * e.g | ||||
|  * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) | ||||
|  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4))) | ||||
|  */ | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4))) | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // DEVICE CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| #ifndef CFG_TUD_ENDPOINT0_SIZE | ||||
| #define CFG_TUD_ENDPOINT0_SIZE    64 | ||||
| #endif | ||||
| 
 | ||||
| //------------- CLASS -------------//
 | ||||
| #define CFG_TUD_CDC             0 | ||||
| #define CFG_TUD_MSC             0 | ||||
| #define CFG_TUD_HID             1 | ||||
| #define CFG_TUD_MIDI            0 | ||||
| #define CFG_TUD_VENDOR          0 | ||||
| 
 | ||||
| // HID buffer size Should be sufficient to hold ID (if any) + Data
 | ||||
| #define CFG_TUD_HID_BUFSIZE     64 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
| @ -1,160 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "tusb.h" | ||||
| 
 | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
 | ||||
|  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. | ||||
|  * | ||||
|  * Auto ProductID layout's Bitmap: | ||||
|  *   [MSB]         HID | MSC | CDC          [LSB] | ||||
|  */ | ||||
| #define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) ) | ||||
| #define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ | ||||
|                            _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Device Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| tusb_desc_device_t const desc_device = | ||||
|         { | ||||
|                 .bLength            = sizeof(tusb_desc_device_t), | ||||
|                 .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|                 .bcdUSB             = 0x0200, | ||||
|                 .bDeviceClass       = 0x00, | ||||
|                 .bDeviceSubClass    = 0x00, | ||||
|                 .bDeviceProtocol    = 0x00, | ||||
|                 .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
| 
 | ||||
|                 .idVendor           = 0xCafe, | ||||
|                 .idProduct          = USB_PID, | ||||
|                 .bcdDevice          = 0x0100, | ||||
| 
 | ||||
|                 .iManufacturer      = 0x01, | ||||
|                 .iProduct           = 0x02, | ||||
|                 .iSerialNumber      = 0x03, | ||||
| 
 | ||||
|                 .bNumConfigurations = 0x01 | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET DEVICE DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| uint8_t const *tud_descriptor_device_cb(void) { | ||||
|     return (uint8_t const *) &desc_device; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // HID Report Descriptor
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| uint8_t const desc_hid_report[] = | ||||
|         { | ||||
|                 TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_BUFSIZE) | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET HID REPORT DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| // Descriptor contents must exist long enough for transfer to complete
 | ||||
| uint8_t const *tud_hid_descriptor_report_cb(void) { | ||||
|     return desc_hid_report; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // Configuration Descriptor
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| enum { | ||||
|     ITF_NUM_HID, | ||||
|     ITF_NUM_TOTAL | ||||
| }; | ||||
| 
 | ||||
| #define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) | ||||
| 
 | ||||
| #define EPNUM_HID   0x01 | ||||
| 
 | ||||
| uint8_t const desc_configuration[] = | ||||
|         { | ||||
|                 // Config number, interface count, string index, total length, attribute, power in mA
 | ||||
|                 TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), | ||||
| 
 | ||||
|                 // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
 | ||||
|                 TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, | ||||
|                                          0x80 | EPNUM_HID, CFG_TUD_HID_BUFSIZE, 10) | ||||
|         }; | ||||
| 
 | ||||
| // Invoked when received GET CONFIGURATION DESCRIPTOR
 | ||||
| // Application return pointer to descriptor
 | ||||
| // Descriptor contents must exist long enough for transfer to complete
 | ||||
| uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { | ||||
|     (void) index; // for multiple configurations
 | ||||
|     return desc_configuration; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // String Descriptors
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| 
 | ||||
| // array of pointer to string descriptors
 | ||||
| char const *string_desc_arr[] = | ||||
|         { | ||||
|                 (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
 | ||||
|                 "TinyUSB",                     // 1: Manufacturer
 | ||||
|                 "TinyUSB Device",              // 2: Product
 | ||||
|                 "123456",                      // 3: Serials, should use chip ID
 | ||||
|         }; | ||||
| 
 | ||||
| static uint16_t _desc_str[32]; | ||||
| 
 | ||||
| // Invoked when received GET STRING DESCRIPTOR request
 | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 | ||||
| uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { | ||||
|     (void) langid; | ||||
| 
 | ||||
|     uint8_t chr_count; | ||||
| 
 | ||||
|     if (index == 0) { | ||||
|         memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|         chr_count = 1; | ||||
|     } else { | ||||
|         // Convert ASCII string into UTF-16
 | ||||
| 
 | ||||
|         if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; | ||||
| 
 | ||||
|         const char *str = string_desc_arr[index]; | ||||
| 
 | ||||
|         // Cap at max char
 | ||||
|         chr_count = strlen(str); | ||||
|         if (chr_count > 31) chr_count = 31; | ||||
| 
 | ||||
|         for (uint8_t i = 0; i < chr_count; i++) { | ||||
|             _desc_str[1 + i] = str[i]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // first byte is length (including header), second byte is string type
 | ||||
|     _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); | ||||
| 
 | ||||
|     return _desc_str; | ||||
| } | ||||
| @ -1 +1,4 @@ | ||||
| add_subdirectory(host_hid) | ||||
| set(FAMILY rp2040) | ||||
| set(BOARD pico_sdk) | ||||
| set(TINYUSB_FAMILY_PROJECT_NAME_PREFIX "tinyusb_host_") | ||||
| add_subdirectory(${PICO_TINYUSB_PATH}/examples/host tinyusb_host_examples) | ||||
|  | ||||
| @ -1,10 +0,0 @@ | ||||
| add_executable(host_hid | ||||
|         host_hid.c | ||||
|         ) | ||||
| 
 | ||||
| target_include_directories(host_hid PRIVATE ${CMAKE_CURRENT_LIST_DIR}) | ||||
| target_link_libraries(host_hid PRIVATE pico_stdlib tinyusb_host tinyusb_board) | ||||
| pico_add_extra_outputs(host_hid) | ||||
| 
 | ||||
| # add url via pico_set_program_url | ||||
| example_auto_set_url(host_hid) | ||||
| @ -1,228 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
| 
 | ||||
| void print_greeting(void); | ||||
| void led_blinking_task(void); | ||||
| extern void hid_task(void); | ||||
| 
 | ||||
| int main(void) { | ||||
|     board_init(); | ||||
|     print_greeting(); | ||||
| 
 | ||||
|     tusb_init(); | ||||
| 
 | ||||
|     while (1) { | ||||
|         // tinyusb host task
 | ||||
|         tuh_task(); | ||||
|         led_blinking_task(); | ||||
| 
 | ||||
| #if CFG_TUH_HID_KEYBOARD || CFG_TUH_HID_MOUSE | ||||
|         hid_task(); | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // USB HID
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| #if CFG_TUH_HID_KEYBOARD | ||||
| 
 | ||||
| CFG_TUSB_MEM_SECTION static hid_keyboard_report_t usb_keyboard_report; | ||||
| uint8_t const keycode2ascii[128][2] = {HID_KEYCODE_TO_ASCII}; | ||||
| 
 | ||||
| // look up new key in previous keys
 | ||||
| static inline bool find_key_in_report(hid_keyboard_report_t const *p_report, uint8_t keycode) { | ||||
|     for (uint8_t i = 0; i < 6; i++) { | ||||
|         if (p_report->keycode[i] == keycode) return true; | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static inline void process_kbd_report(hid_keyboard_report_t const *p_new_report) { | ||||
|     static hid_keyboard_report_t prev_report = {0, 0, {0}}; // previous report to check key released
 | ||||
| 
 | ||||
|     //------------- example code ignore control (non-printable) key affects -------------//
 | ||||
|     for (uint8_t i = 0; i < 6; i++) { | ||||
|         if (p_new_report->keycode[i]) { | ||||
|             if (find_key_in_report(&prev_report, p_new_report->keycode[i])) { | ||||
|                 // exist in previous report means the current key is holding
 | ||||
|             } else { | ||||
|                 // not existed in previous report means the current key is pressed
 | ||||
|                 bool const is_shift = | ||||
|                         p_new_report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); | ||||
|                 uint8_t ch = keycode2ascii[p_new_report->keycode[i]][is_shift ? 1 : 0]; | ||||
|                 putchar(ch); | ||||
|                 if (ch == '\r') putchar('\n'); // added new line for enter key
 | ||||
| 
 | ||||
|                 fflush(stdout); // flush right away, else nanolib will wait for newline
 | ||||
|             } | ||||
|         } | ||||
|         // TODO example skips key released
 | ||||
|     } | ||||
| 
 | ||||
|     prev_report = *p_new_report; | ||||
| } | ||||
| 
 | ||||
| void tuh_hid_keyboard_mounted_cb(uint8_t dev_addr) { | ||||
|     // application set-up
 | ||||
|     printf("A Keyboard device (address %d) is mounted\r\n", dev_addr); | ||||
| 
 | ||||
|     tuh_hid_keyboard_get_report(dev_addr, &usb_keyboard_report); | ||||
| } | ||||
| 
 | ||||
| void tuh_hid_keyboard_unmounted_cb(uint8_t dev_addr) { | ||||
|     // application tear-down
 | ||||
|     printf("A Keyboard device (address %d) is unmounted\r\n", dev_addr); | ||||
| } | ||||
| 
 | ||||
| // invoked ISR context
 | ||||
| void tuh_hid_keyboard_isr(uint8_t dev_addr, xfer_result_t event) { | ||||
|     (void) dev_addr; | ||||
|     (void) event; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUH_HID_MOUSE | ||||
| 
 | ||||
| CFG_TUSB_MEM_SECTION static hid_mouse_report_t usb_mouse_report; | ||||
| 
 | ||||
| void cursor_movement(int8_t x, int8_t y, int8_t wheel) { | ||||
|     //------------- X -------------//
 | ||||
|     if (x < 0) { | ||||
|         printf(ANSI_CURSOR_BACKWARD(%d), (-x)); // move left
 | ||||
|     } else if (x > 0) { | ||||
|         printf(ANSI_CURSOR_FORWARD(%d), x); // move right
 | ||||
|     } else {} | ||||
| 
 | ||||
|     //------------- Y -------------//
 | ||||
|     if (y < 0) { | ||||
|         printf(ANSI_CURSOR_UP(%d), (-y)); // move up
 | ||||
|     } else if (y > 0) { | ||||
|         printf(ANSI_CURSOR_DOWN(%d), y); // move down
 | ||||
|     } else {} | ||||
| 
 | ||||
|     //------------- wheel -------------//
 | ||||
|     if (wheel < 0) { | ||||
|         printf(ANSI_SCROLL_UP(%d), (-wheel)); // scroll up
 | ||||
|     } else if (wheel > 0) { | ||||
|         printf(ANSI_SCROLL_DOWN(%d), wheel); // scroll down
 | ||||
|     } else {} | ||||
| } | ||||
| 
 | ||||
| static inline void process_mouse_report(hid_mouse_report_t const *p_report) { | ||||
|     static hid_mouse_report_t prev_report = {0}; | ||||
| 
 | ||||
|     //------------- button state  -------------//
 | ||||
|     uint8_t button_changed_mask = p_report->buttons ^prev_report.buttons; | ||||
|     if (button_changed_mask & p_report->buttons) { | ||||
|         printf(" %c%c%c ", | ||||
|                p_report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-', | ||||
|                p_report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-', | ||||
|                p_report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-'); | ||||
|     } | ||||
| 
 | ||||
|     //------------- cursor movement -------------//
 | ||||
|     cursor_movement(p_report->x, p_report->y, p_report->wheel); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void tuh_hid_mouse_mounted_cb(uint8_t dev_addr) { | ||||
|     // application set-up
 | ||||
|     printf("A Mouse device (address %d) is mounted\r\n", dev_addr); | ||||
| } | ||||
| 
 | ||||
| void tuh_hid_mouse_unmounted_cb(uint8_t dev_addr) { | ||||
|     // application tear-down
 | ||||
|     printf("A Mouse device (address %d) is unmounted\r\n", dev_addr); | ||||
| } | ||||
| 
 | ||||
| // invoked ISR context
 | ||||
| void tuh_hid_mouse_isr(uint8_t dev_addr, xfer_result_t event) { | ||||
|     (void) dev_addr; | ||||
|     (void) event; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| void hid_task(void) { | ||||
|     uint8_t const addr = 1; | ||||
| 
 | ||||
| #if CFG_TUH_HID_KEYBOARD | ||||
|     if (tuh_hid_keyboard_is_mounted(addr)) { | ||||
|         if (!tuh_hid_keyboard_is_busy(addr)) { | ||||
|             process_kbd_report(&usb_keyboard_report); | ||||
|             tuh_hid_keyboard_get_report(addr, &usb_keyboard_report); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUH_HID_MOUSE | ||||
|     if (tuh_hid_mouse_is_mounted(addr)) { | ||||
|         if (!tuh_hid_mouse_is_busy(addr)) { | ||||
|             process_mouse_report(&usb_mouse_report); | ||||
|             tuh_hid_mouse_get_report(addr, &usb_mouse_report); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // BLINKING TASK
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| void led_blinking_task(void) { | ||||
|     const uint32_t interval_ms = 250; | ||||
|     static uint32_t start_ms = 0; | ||||
| 
 | ||||
|     static bool led_state = false; | ||||
| 
 | ||||
|     // Blink every interval ms
 | ||||
|     if (board_millis() - start_ms < interval_ms) return; // not enough time
 | ||||
|     start_ms += interval_ms; | ||||
| 
 | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; // toggle
 | ||||
| } | ||||
| 
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| // HELPER FUNCTION
 | ||||
| //--------------------------------------------------------------------+
 | ||||
| void print_greeting(void) { | ||||
|     printf("This Host demo is configured to support:\n"); | ||||
|     if (CFG_TUH_HID_KEYBOARD) puts("  - HID Keyboard"); | ||||
|     if (CFG_TUH_HID_MOUSE) puts("  - HID Mouse"); | ||||
| } | ||||
| @ -1,73 +0,0 @@ | ||||
| /* 
 | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // COMMON CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| // defined by compiler flags for flexibility
 | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
| 
 | ||||
| #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX | ||||
| #define CFG_TUSB_RHPORT0_MODE       (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) | ||||
| #else | ||||
| #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_HOST | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
| 
 | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4))) | ||||
| #endif | ||||
| 
 | ||||
| //--------------------------------------------------------------------
 | ||||
| // CONFIGURATION
 | ||||
| //--------------------------------------------------------------------
 | ||||
| 
 | ||||
| #define CFG_TUH_HUB                 1 | ||||
| #define CFG_TUH_HID_KEYBOARD        1 | ||||
| #define CFG_TUH_HID_MOUSE           1 | ||||
| #define CFG_TUSB_HOST_HID_GENERIC   0 // (not yet supported)
 | ||||
| #define CFG_TUH_MSC                 0 | ||||
| #define CFG_TUH_CDC                 0 | ||||
| 
 | ||||
| #define CFG_TUSB_HOST_DEVICE_MAX    (CFG_TUH_HUB ? 5 : 1) // normal hub has 4 ports
 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
		Reference in New Issue
	
	Block a user