Refactored things to move ugly structs out of run.c. Experimental config structures.
This commit is contained in:
		
							parent
							
								
									f9e28ca4c7
								
							
						
					
					
						commit
						904a8afd9a
					
				|  | @ -6,6 +6,18 @@ | |||
| #include "pico/stdlib.h" | ||||
| #include "pico/usb_device.h" | ||||
| #include "configuration_manager.h" | ||||
| #include "configuration_types.h" | ||||
| 
 | ||||
| static const default_configuration default_config = { | ||||
|     .filters = { | ||||
|         .filter = { FILTER_CONFIGURATION, sizeof(default_config.filters) }, | ||||
|         .f1 = {PEAKING,     38,   -19,  0.9}, | ||||
|         .f2 = {LOWSHELF,    2900,   2,  0.7}, | ||||
|         .f3 = {PEAKING,     430,    3,  3.5}, | ||||
|         .f4 = {HIGHSHELF,   8400,   2,  0.7}, | ||||
|         .f5 = {PEAKING,     4800,   3,    5} | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static uint8_t config_buffer[2][512]; | ||||
| static uint8_t active_index = 0; | ||||
|  | @ -14,17 +26,65 @@ static uint16_t write_offset = 0; | |||
| static uint16_t read_offset = 0; | ||||
| static uint16_t config_length = 0; | ||||
| 
 | ||||
| typedef struct _TLVHeader { | ||||
|     uint16_t type; | ||||
|     uint16_t length; | ||||
| } TLVHeader; | ||||
| 
 | ||||
| typedef struct _ConfigHeaderTLV { | ||||
|     struct _TLVHeader header; | ||||
|     uint32_t config_magic; | ||||
|     uint16_t config_version; | ||||
|     uint16_t reserved; | ||||
| } ConfigHeaderTLV; | ||||
| bool validate_filter_configuration(filter_configuration_tlv *filters) | ||||
| { | ||||
|     if (filters->header.type != FILTER_CONFIGURATION) | ||||
|     { | ||||
|         printf("Error! Not a filter TLV (%x)..\n", filters->header.type); | ||||
|         return false; | ||||
|     } | ||||
|     uint8_t *ptr = (uint8_t *)filters->header.value; | ||||
|     const uint8_t *end = (uint8_t *)filters + filters->header.length; | ||||
|     while ((ptr + 4) < end) | ||||
|     { | ||||
|         uint32_t type = *(uint32_t *)ptr; | ||||
|         uint16_t remaining = (uint16_t)(end - ptr); | ||||
|         printf("Found Filter Type %d (%p rem: %d)..\n", type, ptr, remaining); | ||||
|         switch (type) | ||||
|         { | ||||
|         case LOWPASS: | ||||
|         case HIGHPASS: | ||||
|         case BANDPASSSKIRT: | ||||
|         case BANDPASSPEAK: | ||||
|         case NOTCH: | ||||
|         case ALLPASS: | ||||
|         { | ||||
|             if (remaining < sizeof(filter2)) | ||||
|             { | ||||
|                 printf("Error! Not enough data left for filter2 (%d)..\n", remaining); | ||||
|                 return false; | ||||
|             } | ||||
|             filter2 *args = (filter2 *)ptr; | ||||
|             ptr += sizeof(filter2); | ||||
|             break; | ||||
|         } | ||||
|         case PEAKING: | ||||
|         case LOWSHELF: | ||||
|         case HIGHSHELF: | ||||
|         { | ||||
|             if (remaining < sizeof(filter3)) | ||||
|             { | ||||
|                 printf("Error! Not enough data left for filter3 (%d)..\n", remaining); | ||||
|                 return false; | ||||
|             } | ||||
|             filter3 *args = (filter3 *)ptr; | ||||
|             printf("Args: %0.2f %0.2f, %0.2f\n", args->f0, args->dBgain, args->Q); | ||||
|             ptr += sizeof(filter3); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             printf("Unknown filter type\n"); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     if (ptr != end) | ||||
|     { | ||||
|         printf("Error! Did not consume the whole TLV (%p != %p)..\n", ptr, end); | ||||
|         return false; | ||||
|     } | ||||
|     printf("Config looks good..\n"); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void load_config() | ||||
| { | ||||
|  |  | |||
|  | @ -0,0 +1,81 @@ | |||
| #ifndef __CONFIGURATION_TYPES_H__ | ||||
| #define __CONFIGURATION_TYPES_H__ | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| enum structure_types { | ||||
|     // Commands/Responses, these are container TLVs. The Value will be a set of TLV structures.
 | ||||
|     OK = 0,                     // Standard response when a command was successful
 | ||||
|     NOK,                        // Standard error response
 | ||||
|     FLASH_HEADER,               // A special container for the config stored in flash. Hopefully there is some useful
 | ||||
|                                 // metadata in here to allow us to migrate an old config to a new version.
 | ||||
|     GET_VERSION,                // Returns the current config version, and the minimum supported version so clients
 | ||||
|                                 // can decide if they can talk to us or not.
 | ||||
|     SET_CONFIGURATION,          // Updates the active configuration with the supplied TLVs
 | ||||
|     GET_ACTIVE_CONFIGURATION,   // Retrieves the current active configuration TLVs from RAM
 | ||||
|     GET_STORED_CONFIGURATION,   // Retrieves the current stored configuration TLVs from Flash
 | ||||
|     SAVE_CONFIGURATION,         // Writes the active configuration to Flash
 | ||||
| 
 | ||||
|     // Configuration structures, these are returned in the body of a command/response
 | ||||
|     PREPROCESSING_CONFIGURATION = 0x200, | ||||
|     FILTER_CONFIGURATION, | ||||
|     PCM3060_CONFIGURATION, | ||||
| 
 | ||||
|     // Status structures, these are returned in the body of a command/response but they are
 | ||||
|     // not persisted as part of the configuration
 | ||||
|     VERSION_STATUS = 0x400, | ||||
| }; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _tlv_header { | ||||
|     uint16_t type; | ||||
|     uint16_t length; | ||||
|     const uint8_t value[0]; // Doesn't take up any space, just a convenient way of accessing the value
 | ||||
| } tlv_header; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _filter2 { | ||||
|     uint32_t type; | ||||
|     double f0; | ||||
|     double Q; | ||||
| } filter2; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _filter3 { | ||||
|     uint32_t type; | ||||
|     double f0; | ||||
|     double dBgain; | ||||
|     double Q; | ||||
| } filter3; | ||||
| 
 | ||||
| enum filter_type { | ||||
|     LOWPASS = 0, | ||||
|     HIGHPASS, | ||||
|     BANDPASSSKIRT, | ||||
|     BANDPASSPEAK, | ||||
|     NOTCH, | ||||
|     ALLPASS, | ||||
|     PEAKING, | ||||
|     LOWSHELF, | ||||
|     HIGHSHELF | ||||
| }; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _filter_configuration_tlv { | ||||
|     tlv_header header; | ||||
|     const uint8_t filters[]; | ||||
| } filter_configuration_tlv; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _version_status_tlv { | ||||
|     tlv_header header; | ||||
|     uint16_t current_version; | ||||
|     uint16_t minimum_supported_version; | ||||
| } version_status_tlv; | ||||
| 
 | ||||
| typedef struct __attribute__((__packed__)) _default_configuration { | ||||
|     const struct __attribute__((__packed__)) { | ||||
|         tlv_header filter; | ||||
|         filter3 f1; | ||||
|         filter3 f2; | ||||
|         filter3 f3; | ||||
|         filter3 f4; | ||||
|         filter3 f5; | ||||
|     } filters; | ||||
| } default_configuration; | ||||
| 
 | ||||
| #endif // __CONFIGURATION_TYPES_H__
 | ||||
|  | @ -0,0 +1,68 @@ | |||
| #ifndef __OS_DESCRIPTORS__ | ||||
| #define __OS_DESCRIPTORS__ | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #define TU_U16_HIGH(_u16)      ((uint8_t) (((_u16) >> 8) & 0x00ff)) | ||||
| #define TU_U16_LOW(_u16)       ((uint8_t) ((_u16)       & 0x00ff)) | ||||
| #define U16_TO_U8S_BE(_u16)    TU_U16_HIGH(_u16), TU_U16_LOW(_u16) | ||||
| #define U16_TO_U8S_LE(_u16)    TU_U16_LOW(_u16), TU_U16_HIGH(_u16) | ||||
| #define TU_U32_BYTE3(_u32)     ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB
 | ||||
| #define TU_U32_BYTE2(_u32)     ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) | ||||
| #define TU_U32_BYTE1(_u32)     ((uint8_t) ((((uint32_t) _u32) >>  8) & 0x000000ff)) | ||||
| #define TU_U32_BYTE0(_u32)     ((uint8_t) (((uint32_t)  _u32)        & 0x000000ff)) // LSB
 | ||||
| 
 | ||||
| #define U32_TO_U8S_BE(_u32)    TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32) | ||||
| #define U32_TO_U8S_LE(_u32)    TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32) | ||||
| 
 | ||||
| #define MS_OS_20_DESC_LEN  0xB2 | ||||
| 
 | ||||
| typedef enum { | ||||
|     MS_OS_20_SET_HEADER_DESCRIPTOR       = 0x00, | ||||
|     MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, | ||||
|     MS_OS_20_SUBSET_HEADER_FUNCTION      = 0x02, | ||||
|     MS_OS_20_FEATURE_COMPATBLE_ID        = 0x03, | ||||
|     MS_OS_20_FEATURE_REG_PROPERTY        = 0x04, | ||||
|     MS_OS_20_FEATURE_MIN_RESUME_TIME     = 0x05, | ||||
|     MS_OS_20_FEATURE_MODEL_ID            = 0x06, | ||||
|     MS_OS_20_FEATURE_CCGP_DEVICE         = 0x07, | ||||
|     MS_OS_20_FEATURE_VENDOR_REVISION     = 0x08 | ||||
| } microsoft_os_20_type_t; | ||||
| 
 | ||||
| 
 | ||||
| static __aligned(4) uint8_t ms_platform_capability_bos_descriptor[PICO_USBDEV_MAX_DESCRIPTOR_SIZE] = { | ||||
|     0x5, 0xF, 0x21, 0x0, 0x1, | ||||
|     0x1C, 0x10, 0x5, 0x00, | ||||
|     0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, | ||||
|     0x0, 0x0, 0x3, 0x6, | ||||
|     U16_TO_U8S_LE(MS_OS_20_DESC_LEN),  // TODO: len
 | ||||
|     0x1, | ||||
|     0x0 }; | ||||
| 
 | ||||
| uint8_t desc_ms_os_20[256] = { | ||||
|   // Set header: length, type, windows version, total length
 | ||||
|   U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), | ||||
| 
 | ||||
|   // Configuration subset header: length, type, configuration index, reserved, configuration total length
 | ||||
|   U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), | ||||
| 
 | ||||
|   // Function Subset header: length, type, first interface, reserved, subset length
 | ||||
|   U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 2  /*interface*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), | ||||
| 
 | ||||
|   // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
 | ||||
|   U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, | ||||
|   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
 | ||||
| 
 | ||||
|   // MS OS 2.0 Registry property descriptor: length, type
 | ||||
|   U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), | ||||
|   U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
 | ||||
|   'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, | ||||
|   'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, | ||||
|   U16_TO_U8S_LE(0x0050), // wPropertyDataLength
 | ||||
| 	//bPropertyData: “{E8379B1D-6AA3-F426-2EAE-83D18090CA79}”.
 | ||||
|   '{', 0x00, 'E', 0x00, '8', 0x00, '3', 0x00, '7', 0x00, '9', 0x00, 'B', 0x00, '1', 0x00, 'D', 0x00, '-', 0x00, | ||||
|   '6', 0x00, 'A', 0x00, 'A', 0x00, '3', 0x00, '-', 0x00, 'F', 0x00, '4', 0x00, '2', 0x00, '6', 0x00, '-', 0x00, | ||||
|   '2', 0x00, 'E', 0x00, 'A', 0x00, 'E', 0x00, '-', 0x00, '8', 0x00, '3', 0x00, 'D', 0x00, '1', 0x00, '8', 0x00, | ||||
|   '0', 0x00, '9', 0x00, '0', 0x00, 'C', 0x00, 'A', 0x00, '7', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 | ||||
| }; | ||||
| 
 | ||||
| #endif //__OS_DESCRIPTORS__
 | ||||
|  | @ -44,6 +44,7 @@ | |||
| #include "i2s.h" | ||||
| #include "bqf.h" | ||||
| #include "user.h" | ||||
| #include "os_descriptors.h" | ||||
| #include "configuration_manager.h" | ||||
| 
 | ||||
| i2s_obj_t i2s_write_obj; | ||||
|  | @ -761,62 +762,6 @@ static struct usb_stream_transfer_funcs control_stream_funcs = { | |||
|         .on_packet_complete = usb_stream_noop_on_packet_complete | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| #define TU_U16_HIGH(_u16)      ((uint8_t) (((_u16) >> 8) & 0x00ff)) | ||||
| #define TU_U16_LOW(_u16)       ((uint8_t) ((_u16)       & 0x00ff)) | ||||
| #define U16_TO_U8S_BE(_u16)    TU_U16_HIGH(_u16), TU_U16_LOW(_u16) | ||||
| #define U16_TO_U8S_LE(_u16)    TU_U16_LOW(_u16), TU_U16_HIGH(_u16) | ||||
| #define TU_U32_BYTE3(_u32)     ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB
 | ||||
| #define TU_U32_BYTE2(_u32)     ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff)) | ||||
| #define TU_U32_BYTE1(_u32)     ((uint8_t) ((((uint32_t) _u32) >>  8) & 0x000000ff)) | ||||
| #define TU_U32_BYTE0(_u32)     ((uint8_t) (((uint32_t)  _u32)        & 0x000000ff)) // LSB
 | ||||
| 
 | ||||
| #define U32_TO_U8S_BE(_u32)    TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32) | ||||
| #define U32_TO_U8S_LE(_u32)    TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32) | ||||
| 
 | ||||
| #define MS_OS_20_DESC_LEN  0xB2 | ||||
| 
 | ||||
| typedef enum | ||||
| { | ||||
|   MS_OS_20_SET_HEADER_DESCRIPTOR       = 0x00, | ||||
|   MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01, | ||||
|   MS_OS_20_SUBSET_HEADER_FUNCTION      = 0x02, | ||||
|   MS_OS_20_FEATURE_COMPATBLE_ID        = 0x03, | ||||
|   MS_OS_20_FEATURE_REG_PROPERTY        = 0x04, | ||||
|   MS_OS_20_FEATURE_MIN_RESUME_TIME     = 0x05, | ||||
|   MS_OS_20_FEATURE_MODEL_ID            = 0x06, | ||||
|   MS_OS_20_FEATURE_CCGP_DEVICE         = 0x07, | ||||
|   MS_OS_20_FEATURE_VENDOR_REVISION     = 0x08 | ||||
| } microsoft_os_20_type_t; | ||||
| 
 | ||||
| uint8_t desc_ms_os_20[256] = | ||||
| { | ||||
|   // Set header: length, type, windows version, total length
 | ||||
|   U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), | ||||
| 
 | ||||
|   // Configuration subset header: length, type, configuration index, reserved, configuration total length
 | ||||
|   U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), | ||||
| 
 | ||||
|   // Function Subset header: length, type, first interface, reserved, subset length
 | ||||
|   U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 2  /*interface*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), | ||||
| 
 | ||||
|   // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
 | ||||
|   U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, | ||||
|   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
 | ||||
| 
 | ||||
|   // MS OS 2.0 Registry property descriptor: length, type
 | ||||
|   U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), | ||||
|   U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
 | ||||
|   'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, | ||||
|   'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, | ||||
|   U16_TO_U8S_LE(0x0050), // wPropertyDataLength
 | ||||
| 	//bPropertyData: “{E8379B1D-6AA3-F426-2EAE-83D18090CA79}”.
 | ||||
|   '{', 0x00, 'E', 0x00, '8', 0x00, '3', 0x00, '7', 0x00, '9', 0x00, 'B', 0x00, '1', 0x00, 'D', 0x00, '-', 0x00, | ||||
|   '6', 0x00, 'A', 0x00, 'A', 0x00, '3', 0x00, '-', 0x00, 'F', 0x00, '4', 0x00, '2', 0x00, '6', 0x00, '-', 0x00, | ||||
|   '2', 0x00, 'E', 0x00, 'A', 0x00, 'E', 0x00, '-', 0x00, '8', 0x00, '3', 0x00, 'D', 0x00, '1', 0x00, '8', 0x00, | ||||
|   '0', 0x00, '9', 0x00, '0', 0x00, 'C', 0x00, 'A', 0x00, '7', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 | ||||
| }; | ||||
| 
 | ||||
| static bool ad_setup_request_handler(__unused struct usb_device *device, struct usb_setup_packet *setup) { | ||||
|     setup = __builtin_assume_aligned(setup, 4); | ||||
|     printf("ad_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); | ||||
|  | @ -828,14 +773,6 @@ static bool ad_setup_request_handler(__unused struct usb_device *device, struct | |||
|                 printf("Request BOS descriptor, idx %d\n", setup->wValue & 0xFF); | ||||
| 
 | ||||
|                 struct usb_endpoint *usb_control_in = usb_get_control_in_endpoint(); | ||||
|                 static __aligned(4) uint8_t descriptor_buf[PICO_USBDEV_MAX_DESCRIPTOR_SIZE] = { | ||||
|                     0x5, 0xF, 0x21, 0x0, 0x1, | ||||
|                     0x1C, 0x10, 0x5, 0x00, | ||||
|                     0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, | ||||
|                     0x0, 0x0, 0x3, 0x6, | ||||
|                     U16_TO_U8S_LE(MS_OS_20_DESC_LEN),  // TODO: len
 | ||||
|                     0x1, | ||||
|                     0x0 }; | ||||
|                 static struct usb_stream_transfer_funcs control_stream_funcs = { | ||||
|                         .on_chunk = usb_stream_noop_on_chunk, | ||||
|                         .on_packet_complete = usb_stream_noop_on_packet_complete | ||||
|  | @ -843,8 +780,8 @@ static bool ad_setup_request_handler(__unused struct usb_device *device, struct | |||
|                 int len = 0x21; | ||||
| 
 | ||||
|                 len = MIN(len, setup->wLength); | ||||
|                 usb_stream_setup_transfer(&_control_in_stream_transfer, &control_stream_funcs, descriptor_buf, | ||||
|                                             sizeof(descriptor_buf), len, _tf_send_control_in_ack); | ||||
|                 usb_stream_setup_transfer(&_control_in_stream_transfer, &control_stream_funcs, ms_platform_capability_bos_descriptor, | ||||
|                                             sizeof(ms_platform_capability_bos_descriptor), len, _tf_send_control_in_ack); | ||||
| 
 | ||||
|                 _control_in_stream_transfer.ep = usb_control_in; | ||||
|                 usb_start_transfer(usb_control_in, &_control_in_stream_transfer.core); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 George Norton
						George Norton