Merge remote-tracking branch 'hathach/master' into cdc_without_dtr
This commit is contained in:
		
							
								
								
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAMD11
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAMD11
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAME5X
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAME5X
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAMG
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								examples/device/audio_test/.skip.MCU_SAMG
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										12
									
								
								examples/device/audio_test/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/device/audio_test/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| include ../../../tools/top.mk | ||||
| include ../../make.mk | ||||
|  | ||||
| INC += \ | ||||
|   src \ | ||||
|   $(TOP)/hw \ | ||||
|  | ||||
| # Example source | ||||
| EXAMPLE_SOURCE += $(wildcard src/*.c) | ||||
| SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) | ||||
|  | ||||
| include ../../rules.mk | ||||
							
								
								
									
										422
									
								
								examples/device/audio_test/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										422
									
								
								examples/device/audio_test/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,422 @@ | ||||
| /*  | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Reinhard Panhuber | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.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; | ||||
|  | ||||
| // Audio controls | ||||
| // Current states | ||||
| bool mute[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; 						// +1 for master channel 0 | ||||
| uint16_t volume[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; 					// +1 for master channel 0 | ||||
| uint32_t sampFreq; | ||||
| uint8_t clkValid; | ||||
|  | ||||
| // Range states | ||||
| audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_N_CHANNELS_TX+1]; 			// Volume range state | ||||
| audio_control_range_4_n_t(1) sampleFreqRng; 						// Sample frequency range state | ||||
|  | ||||
| // Audio test data | ||||
| uint16_t test_buffer_audio[CFG_TUD_AUDIO_TX_FIFO_SIZE/2]; | ||||
| uint16_t startVal = 0; | ||||
|  | ||||
| void led_blinking_task(void); | ||||
| void audio_task(void); | ||||
|  | ||||
| /*------------- MAIN -------------*/ | ||||
| int main(void) | ||||
| { | ||||
|   board_init(); | ||||
|  | ||||
|   tusb_init(); | ||||
|  | ||||
|   // Init values | ||||
|   sampFreq = 44100; | ||||
|   clkValid = 1; | ||||
|  | ||||
|   sampleFreqRng.wNumSubRanges = 1; | ||||
|   sampleFreqRng.subrange[0].bMin = 44100; | ||||
|   sampleFreqRng.subrange[0].bMax = 44100; | ||||
|   sampleFreqRng.subrange[0].bRes = 0; | ||||
|  | ||||
|   while (1) | ||||
|   { | ||||
|     tud_task(); // tinyusb device task | ||||
|     led_blinking_task(); | ||||
|     audio_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; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // AUDIO Task | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| void audio_task(void) | ||||
| { | ||||
|   // Yet to be filled - e.g. put meas data into TX FIFOs etc. | ||||
|   asm("nop"); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Application Callback API Implementations | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Invoked when audio class specific set request received for an EP | ||||
| bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) pBuff; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t ep = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) ep; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific set request received for an interface | ||||
| bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) pBuff; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) itf; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // 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 *pBuff) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|   uint8_t entityID = TU_U16_HIGH(p_request->wIndex); | ||||
|  | ||||
|   (void) itf; | ||||
|  | ||||
|   // We do not support any set range requests here, only current value requests | ||||
|   TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); | ||||
|  | ||||
|   // If request is for our feature unit | ||||
|   if ( entityID == 2 ) | ||||
|   { | ||||
|     switch ( ctrlSel ) | ||||
|     { | ||||
|       case AUDIO_FU_CTRL_MUTE: | ||||
|         // Request uses format layout 1 | ||||
|         TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); | ||||
|  | ||||
|         mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; | ||||
|  | ||||
|         TU_LOG2("    Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); | ||||
|  | ||||
|       return true; | ||||
|  | ||||
|       case AUDIO_FU_CTRL_VOLUME: | ||||
|         // Request uses format layout 2 | ||||
|         TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); | ||||
|  | ||||
|         volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur; | ||||
|  | ||||
|         TU_LOG2("    Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); | ||||
|  | ||||
|      return true; | ||||
|  | ||||
|         // Unknown/Unsupported control | ||||
|       default: | ||||
|         TU_BREAKPOINT(); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   return false;    // Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific get request received for an EP | ||||
| bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t ep = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) ep; | ||||
|  | ||||
|   //	return tud_control_xfer(rhport, p_request, &tmp, 1); | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // Invoked when audio class specific get request received for an interface | ||||
| bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   uint8_t itf = TU_U16_LOW(p_request->wIndex); | ||||
|  | ||||
|   (void) channelNum; (void) ctrlSel; (void) itf; | ||||
|  | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| // 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) | ||||
| { | ||||
|   (void) rhport; | ||||
|  | ||||
|   // Page 91 in UAC2 specification | ||||
|   uint8_t channelNum = TU_U16_LOW(p_request->wValue); | ||||
|   uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); | ||||
|   // uint8_t itf = TU_U16_LOW(p_request->wIndex); 			// Since we have only one audio function implemented, we do not need the itf value | ||||
|   uint8_t entityID = TU_U16_HIGH(p_request->wIndex); | ||||
|  | ||||
|   // Input terminal (Microphone input) | ||||
|   if (entityID == 1) | ||||
|   { | ||||
|     switch (ctrlSel) | ||||
|     { | ||||
|       case AUDIO_TE_CTRL_CONNECTOR:; | ||||
|       // The terminal connector control only has a get request with only the CUR attribute. | ||||
|  | ||||
|       audio_desc_channel_cluster_t ret; | ||||
|  | ||||
|       // Those are dummy values for now | ||||
|       ret.bNrChannels = 1; | ||||
|       ret.bmChannelConfig = 0; | ||||
|       ret.iChannelNames = 0; | ||||
|  | ||||
|       TU_LOG2("    Get terminal connector\r\n"); | ||||
|  | ||||
|       return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret)); | ||||
|  | ||||
|       // Unknown/Unsupported control selector | ||||
|       default: TU_BREAKPOINT(); return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Feature unit | ||||
|   if (entityID == 2) | ||||
|   { | ||||
|     switch (ctrlSel) | ||||
|     { | ||||
|       case AUDIO_FU_CTRL_MUTE: | ||||
| 	// Audio control mute cur parameter block consists of only one byte - we thus can send it right away | ||||
| 	// There does not exist a range parameter block for mute | ||||
| 	TU_LOG2("    Get Mute of channel: %u\r\n", channelNum); | ||||
| 	return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); | ||||
|  | ||||
|       case AUDIO_FU_CTRL_VOLUME: | ||||
|  | ||||
| 	switch (p_request->bRequest) | ||||
| 	{ | ||||
| 	  case AUDIO_CS_REQ_CUR: | ||||
| 	    TU_LOG2("    Get Volume of channel: %u\r\n", channelNum); | ||||
| 	    return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); | ||||
| 	  case AUDIO_CS_REQ_RANGE: | ||||
| 	    TU_LOG2("    Get Volume range of channel: %u\r\n", channelNum); | ||||
|  | ||||
| 	    // Copy values - only for testing - better is version below | ||||
| 	    audio_control_range_2_n_t(1) ret; | ||||
|  | ||||
| 	    ret.wNumSubRanges = 1; | ||||
| 	    ret.subrange[0].bMin = -90; 	// -90 dB | ||||
| 	    ret.subrange[0].bMax = 90;		// +90 dB | ||||
| 	    ret.subrange[0].bRes = 1; 		// 1 dB steps | ||||
|  | ||||
| 	    return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret)); | ||||
|  | ||||
| 	    // Unknown/Unsupported control | ||||
| 	  default: TU_BREAKPOINT(); return false; | ||||
| 	} | ||||
|  | ||||
| 	// Unknown/Unsupported control | ||||
| 	  default: TU_BREAKPOINT(); return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Clock Source unit | ||||
|   if (entityID == 4) | ||||
|   { | ||||
|     switch (ctrlSel) | ||||
|     { | ||||
|       case AUDIO_CS_CTRL_SAM_FREQ: | ||||
|  | ||||
| 	// channelNum is always zero in this case | ||||
|  | ||||
| 	switch (p_request->bRequest) | ||||
| 	{ | ||||
| 	  case AUDIO_CS_REQ_CUR: | ||||
| 	    TU_LOG2("    Get Sample Freq.\r\n"); | ||||
| 	    return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); | ||||
| 	  case AUDIO_CS_REQ_RANGE: | ||||
| 	    TU_LOG2("    Get Sample Freq. range\r\n"); | ||||
| 	    return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); | ||||
|  | ||||
| 	    // Unknown/Unsupported control | ||||
| 	  default: TU_BREAKPOINT(); return false; | ||||
| 	} | ||||
|  | ||||
| 	  case AUDIO_CS_CTRL_CLK_VALID: | ||||
| 	    // Only cur attribute exists for this request | ||||
| 	    TU_LOG2("    Get Sample Freq. valid\r\n"); | ||||
| 	    return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); | ||||
|  | ||||
| 	    // Unknown/Unsupported control | ||||
| 	  default: TU_BREAKPOINT(); return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   TU_LOG2("  Unsupported entity: %d\r\n", entityID); | ||||
|   return false; 	// Yet not implemented | ||||
| } | ||||
|  | ||||
| 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; | ||||
|  | ||||
|   tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_TX_FIFO_SIZE); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) n_bytes_copied; | ||||
|   (void) itf; | ||||
|   (void) ep_in; | ||||
|   (void) cur_alt_setting; | ||||
|  | ||||
|   for (size_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_SIZE/2; cnt++) | ||||
|   { | ||||
|     test_buffer_audio[cnt] = startVal++; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) | ||||
| { | ||||
|   (void) rhport; | ||||
|   (void) p_request; | ||||
|   startVal = 0; | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // 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 | ||||
| } | ||||
							
								
								
									
										116
									
								
								examples/device/audio_test/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								examples/device/audio_test/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* | ||||
|  * 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_DEVICE | OPT_MODE_HIGH_SPEED) | ||||
| #else | ||||
| #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_DEVICE | ||||
| #endif | ||||
|  | ||||
| #define CFG_TUSB_OS                 OPT_OS_NONE | ||||
|  | ||||
| #ifndef CFG_TUSB_DEBUG | ||||
| #define CFG_TUSB_DEBUG              0 | ||||
| #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               0 | ||||
| #define CFG_TUD_MIDI              0 | ||||
| #define CFG_TUD_AUDIO             1 | ||||
| #define CFG_TUD_VENDOR            0 | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // AUDIO CLASS DRIVER CONFIGURATION | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| // Audio format type | ||||
| #define CFG_TUD_AUDIO_USE_TX_FIFO 				1 | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_TX 				AUDIO_FORMAT_TYPE_I | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_RX 				AUDIO_FORMAT_TYPE_UNDEFINED | ||||
|  | ||||
| // Audio format type I specifications | ||||
| #define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX 				AUDIO_DATA_FORMAT_TYPE_I_PCM | ||||
| #define CFG_TUD_AUDIO_N_CHANNELS_TX 				1 | ||||
| #define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX			2 | ||||
|  | ||||
| // 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                                       48*CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX*CFG_TUD_AUDIO_N_CHANNELS_TX    // 48 Samples (48 kHz) x 2 Bytes/Sample x 1 Channels | ||||
| #define CFG_TUD_AUDIO_TX_FIFO_SIZE                                    48*2                                                                  // 48 Samples (48 kHz) x 2 Bytes/Sample (1/2 word) | ||||
|  | ||||
| // 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_ */ | ||||
							
								
								
									
										167
									
								
								examples/device/audio_test/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								examples/device/audio_test/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| /*  | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #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]     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 | ||||
| //--------------------------------------------------------------------+ | ||||
| enum | ||||
| { | ||||
|   ITF_NUM_AUDIO_CONTROL = 0, | ||||
|   ITF_NUM_AUDIO_STREAMING, | ||||
|   ITF_NUM_TOTAL | ||||
| }; | ||||
|  | ||||
| #define CONFIG_TOTAL_LEN    	(TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_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 | ||||
| #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 - unfortunately this is not possible to determine otherwise | ||||
| const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_MIC_DESC_LEN}; | ||||
|  | ||||
| // TAKE CARE - THE NUMBER OF AUDIO STREAMING INTERFACES PER AUDIO FUNCTION MUST NOT EXCEED CFG_TUD_AUDIO_N_AS_INT - IF IT DOES INCREASE CFG_TUD_AUDIO_N_AS_INT IN tusb_config.h! | ||||
|  | ||||
| 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_MIC_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ 3, /*_nBitsUsedPerSample*/ 24, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ 48*4) | ||||
| }; | ||||
|  | ||||
| // 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) | ||||
|     "PaniRCorp",                   	// 1: Manufacturer | ||||
|     "MicNode",              		// 2: Product | ||||
|     "123456",                      	// 3: Serials, should use chip ID | ||||
|     "UAC2",                 	 	// 4: 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; | ||||
| } | ||||
							
								
								
									
										12
									
								
								examples/device/hid_multiple_interface/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/device/hid_multiple_interface/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| include ../../../tools/top.mk | ||||
| include ../../make.mk | ||||
|  | ||||
| INC += \ | ||||
| 	src \ | ||||
| 	$(TOP)/hw \ | ||||
|  | ||||
| # Example source | ||||
| EXAMPLE_SOURCE += $(wildcard src/*.c) | ||||
| SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) | ||||
|  | ||||
| include ../../rules.mk | ||||
							
								
								
									
										207
									
								
								examples/device/hid_multiple_interface/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								examples/device/hid_multiple_interface/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| /* | ||||
|  * 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" | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Interface index depends on the order in configuration descriptor | ||||
| enum { | ||||
|   ITF_KEYBOARD = 0, | ||||
|   ITF_MOUSE = 1 | ||||
| }; | ||||
|  | ||||
| /* 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 = board_button_read(); | ||||
|  | ||||
|   // 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(); | ||||
|   } | ||||
|  | ||||
|   /*------------- Keyboard -------------*/ | ||||
|   if ( tud_hid_n_ready(ITF_KEYBOARD) ) | ||||
|   { | ||||
|     // use to avoid send multiple consecutive zero report for keyboard | ||||
|     static bool has_key = false; | ||||
|  | ||||
|     if ( btn ) | ||||
|     { | ||||
|       uint8_t keycode[6] = { 0 }; | ||||
|       keycode[0] = HID_KEY_A; | ||||
|  | ||||
|       tud_hid_n_keyboard_report(ITF_KEYBOARD, 0, 0, keycode); | ||||
|  | ||||
|       has_key = true; | ||||
|     }else | ||||
|     { | ||||
|       // send empty key report if previously has key pressed | ||||
|       if (has_key) tud_hid_n_keyboard_report(ITF_KEYBOARD, 0, 0, NULL); | ||||
|       has_key = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /*------------- Mouse -------------*/ | ||||
|   if ( tud_hid_n_ready(ITF_MOUSE) ) | ||||
|   { | ||||
|     if ( btn ) | ||||
|     { | ||||
|       int8_t const delta = 5; | ||||
|  | ||||
|       // no button, right + down, no scroll pan | ||||
|       tud_hid_n_mouse_report(ITF_MOUSE, 0, 0x00, delta, delta, 0, 0); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| // 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 itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) | ||||
| { | ||||
|   // TODO not Implemented | ||||
|   (void) itf; | ||||
|   (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 itf, 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) itf; | ||||
|   (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 | ||||
| } | ||||
							
								
								
									
										110
									
								
								examples/device/hid_multiple_interface/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								examples/device/hid_multiple_interface/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| /* | ||||
|  * 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 board.mk | ||||
| #ifndef CFG_TUSB_MCU | ||||
|   #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
|  | ||||
| // RHPort number used for device can be defined by board.mk, default to port 0 | ||||
| #ifndef BOARD_DEVICE_RHPORT_NUM | ||||
|   #define BOARD_DEVICE_RHPORT_NUM     0 | ||||
| #endif | ||||
|  | ||||
| // RHPort max operational speed can defined by board.mk | ||||
| // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed | ||||
| #ifndef BOARD_DEVICE_RHPORT_SPEED | ||||
|   #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 BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_HIGH_SPEED | ||||
|   #else | ||||
|     #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| // Device mode with rhport and speed defined by board.mk | ||||
| #if   BOARD_DEVICE_RHPORT_NUM == 0 | ||||
|   #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) | ||||
| #elif BOARD_DEVICE_RHPORT_NUM == 1 | ||||
|   #define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) | ||||
| #else | ||||
|   #error "Incorrect RHPort configuration" | ||||
| #endif | ||||
|  | ||||
| // This example doesn't use an RTOS | ||||
| #define CFG_TUSB_OS               OPT_OS_NONE | ||||
|  | ||||
| // 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               2 | ||||
| #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_EP_BUFSIZE    8 | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  } | ||||
| #endif | ||||
|  | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
							
								
								
									
										188
									
								
								examples/device/hid_multiple_interface/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								examples/device/hid_multiple_interface/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| /* | ||||
|  * 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_report1[] = | ||||
| { | ||||
|   TUD_HID_REPORT_DESC_KEYBOARD() | ||||
| }; | ||||
|  | ||||
| uint8_t const desc_hid_report2[] = | ||||
| { | ||||
|   TUD_HID_REPORT_DESC_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(uint8_t itf) | ||||
| { | ||||
|   if (itf == 0) | ||||
|   { | ||||
|     return desc_hid_report1; | ||||
|   } | ||||
|   else if (itf == 1) | ||||
|   { | ||||
|     return desc_hid_report2; | ||||
|   } | ||||
|  | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Configuration Descriptor | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| enum | ||||
| { | ||||
|   ITF_NUM_HID1, | ||||
|   ITF_NUM_HID2, | ||||
|   ITF_NUM_TOTAL | ||||
| }; | ||||
|  | ||||
| #define  CONFIG_TOTAL_LEN  (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_HID_DESC_LEN) | ||||
|  | ||||
| #define EPNUM_HID1   0x81 | ||||
| #define EPNUM_HID2   0x82 | ||||
|  | ||||
| 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_HID1, 4, HID_PROTOCOL_NONE, sizeof(desc_hid_report1), EPNUM_HID1, CFG_TUD_HID_EP_BUFSIZE, 10), | ||||
|   TUD_HID_DESCRIPTOR(ITF_NUM_HID2, 5, HID_PROTOCOL_NONE, sizeof(desc_hid_report2), EPNUM_HID2, CFG_TUD_HID_EP_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 | ||||
|   "Keyboard Interface",             // 4: Interface 1 String | ||||
|   "Mouse Interface",                 // 5: Interface 2 String | ||||
| }; | ||||
|  | ||||
| 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 | ||||
|   { | ||||
|     // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. | ||||
|     // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors | ||||
|  | ||||
|     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; | ||||
|  | ||||
|     // Convert ASCII string into UTF-16 | ||||
|     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; | ||||
| } | ||||
| @@ -35,7 +35,7 @@ | ||||
| /* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ | ||||
| #define NO_SYS                          1 | ||||
| #define MEM_ALIGNMENT                   4 | ||||
| #define LWIP_RAW                        1 | ||||
| #define LWIP_RAW                        0 | ||||
| #define LWIP_NETCONN                    0 | ||||
| #define LWIP_SOCKET                     0 | ||||
| #define LWIP_DHCP                       0 | ||||
| @@ -54,4 +54,6 @@ | ||||
| #define LWIP_HTTPD_SSI                  0 | ||||
| #define LWIP_HTTPD_SSI_INCLUDE_TAG      0 | ||||
|  | ||||
| #define LWIP_SINGLE_NETIF               1 | ||||
|  | ||||
| #endif /* __LWIPOPTS_H__ */ | ||||
|   | ||||
| @@ -32,6 +32,16 @@ RNDIS should be valid on Linux and Windows hosts, and CDC-ECM should be valid on | ||||
|  | ||||
| The MCU appears to the host as IP address 192.168.7.1, and provides a DHCP server, DNS server, and web server. | ||||
| */ | ||||
| /* | ||||
| Some smartphones *may* work with this implementation as well, but likely have limited (broken) drivers, | ||||
| and likely their manufacturer has not tested such functionality.  Some code workarounds could be tried: | ||||
|  | ||||
| The smartphone may only have an ECM driver, but refuse to automatically pick ECM (unlike the OSes above); | ||||
| try modifying ./examples/devices/net_lwip_webserver/usb_descriptors.c so that CONFIG_ID_ECM is default. | ||||
|  | ||||
| The smartphone may be artificially picky about which Ethernet MAC address to recognize; if this happens,  | ||||
| try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00 (clearing bit 1). | ||||
| */ | ||||
|  | ||||
| #include "bsp/board.h" | ||||
| #include "tusb.h" | ||||
| @@ -50,7 +60,7 @@ static struct pbuf *received_frame; | ||||
|  | ||||
| /* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ | ||||
| /* ideally speaking, this should be generated from the hardware's unique ID (if available) */ | ||||
| /* it is suggested that the first two bytes are 0x02,0x02 to indicate a link-local address */ | ||||
| /* it is suggested that the first byte is 0x02 to indicate a link-local address */ | ||||
| const uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00}; | ||||
|  | ||||
| /* network parameters of this MCU */ | ||||
| @@ -61,22 +71,21 @@ static const ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); | ||||
| /* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */ | ||||
| static dhcp_entry_t entries[] = | ||||
| { | ||||
|   /* mac    ip address        subnet mask        lease time */ | ||||
|   { {0}, {192, 168, 7, 2}, {255, 255, 255, 0}, 24 * 60 * 60 }, | ||||
|   { {0}, {192, 168, 7, 3}, {255, 255, 255, 0}, 24 * 60 * 60 }, | ||||
|   { {0}, {192, 168, 7, 4}, {255, 255, 255, 0}, 24 * 60 * 60 } | ||||
|     /* mac ip address                          lease time */ | ||||
|     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 2), 24 * 60 * 60 }, | ||||
|     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 3), 24 * 60 * 60 }, | ||||
|     { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 4), 24 * 60 * 60 }, | ||||
| }; | ||||
|  | ||||
| /* DHCP configuration parameters, leveraging "entries" above */ | ||||
| static const dhcp_config_t dhcp_config = | ||||
| { | ||||
|   {192, 168, 7, 1}, 67,    /* server address (self), port */ | ||||
|   {192, 168, 7, 1},        /* dns server (self) */ | ||||
|   "usb",                   /* dns suffix */ | ||||
|   TU_ARRAY_SIZE(entries),  /* number of entries */ | ||||
|   entries                  /* pointer to entries */ | ||||
|     .router = IPADDR4_INIT_BYTES(0, 0, 0, 0),  /* router address (if any) */ | ||||
|     .port = 67,                                /* listen port */ | ||||
|     .dns = IPADDR4_INIT_BYTES(192, 168, 7, 1), /* dns server (if any) */ | ||||
|     "usb",                                     /* dns suffix */ | ||||
|     TU_ARRAY_SIZE(entries),                    /* num entry */ | ||||
|     entries                                    /* entries */ | ||||
| }; | ||||
|  | ||||
| static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) | ||||
| { | ||||
|   (void)netif; | ||||
| @@ -90,7 +99,7 @@ static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) | ||||
|     /* if the network driver can accept another packet, we make it happen */ | ||||
|     if (tud_network_can_xmit()) | ||||
|     { | ||||
|       tud_network_xmit(p); | ||||
|       tud_network_xmit(p, 0 /* unused for this example */); | ||||
|       return ERR_OK; | ||||
|     } | ||||
|  | ||||
| @@ -143,17 +152,49 @@ bool dns_query_proc(const char *name, ip_addr_t *addr) | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool tud_network_recv_cb(struct pbuf *p) | ||||
| bool tud_network_recv_cb(const uint8_t *src, uint16_t size) | ||||
| { | ||||
|   /* this shouldn't happen, but if we get another packet before  | ||||
|   parsing the previous, we must signal our inability to accept it */ | ||||
|   if (received_frame) return false; | ||||
|  | ||||
|   /* store away the pointer for service_traffic() to later handle */ | ||||
|   received_frame = p; | ||||
|   if (size) | ||||
|   { | ||||
|     struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); | ||||
|  | ||||
|     if (p) | ||||
|     { | ||||
|       /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ | ||||
|       memcpy(p->payload, src, size); | ||||
|  | ||||
|       /* store away the pointer for service_traffic() to later handle */ | ||||
|       received_frame = p; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) | ||||
| { | ||||
|   struct pbuf *p = (struct pbuf *)ref; | ||||
|   struct pbuf *q; | ||||
|   uint16_t len = 0; | ||||
|  | ||||
|   (void)arg; /* unused for this example */ | ||||
|  | ||||
|   /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ | ||||
|   for(q = p; q != NULL; q = q->next) | ||||
|   { | ||||
|     memcpy(dst, (char *)q->payload, q->len); | ||||
|     dst += q->len; | ||||
|     len += q->len; | ||||
|     if (q->len == q->tot_len) break; | ||||
|   } | ||||
|  | ||||
|   return len; | ||||
| } | ||||
|  | ||||
| static void service_traffic(void) | ||||
| { | ||||
|   /* handle any packet received by tud_network_recv_cb() */ | ||||
|   | ||||
							
								
								
									
										12
									
								
								examples/device/uac2_headset/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/device/uac2_headset/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| include ../../../tools/top.mk | ||||
| include ../../make.mk | ||||
|  | ||||
| INC += \ | ||||
|   src \ | ||||
|   $(TOP)/hw \ | ||||
|  | ||||
| # Example source | ||||
| EXAMPLE_SOURCE += $(wildcard src/*.c) | ||||
| SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) | ||||
|  | ||||
| include ../../rules.mk | ||||
							
								
								
									
										391
									
								
								examples/device/uac2_headset/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								examples/device/uac2_headset/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | ||||
| /*  | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										134
									
								
								examples/device/uac2_headset/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								examples/device/uac2_headset/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  | ||||
| #define CFG_TUSB_OS                 OPT_OS_NONE | ||||
|  | ||||
| #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              0 | ||||
| #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_ */ | ||||
							
								
								
									
										165
									
								
								examples/device/uac2_headset/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								examples/device/uac2_headset/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| /*  | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										120
									
								
								examples/device/uac2_headset/src/usb_descriptors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								examples/device/uac2_headset/src/usb_descriptors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| /*  | ||||
|  * 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 | ||||
		Reference in New Issue
	
	Block a user
	 Jan Dümpelmann
					Jan Dümpelmann