diff --git a/firmware/code/configuration_manager.c b/firmware/code/configuration_manager.c index 10346fc..5eb22c8 100644 --- a/firmware/code/configuration_manager.c +++ b/firmware/code/configuration_manager.c @@ -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() { diff --git a/firmware/code/configuration_types.h b/firmware/code/configuration_types.h new file mode 100644 index 0000000..89a562f --- /dev/null +++ b/firmware/code/configuration_types.h @@ -0,0 +1,81 @@ +#ifndef __CONFIGURATION_TYPES_H__ +#define __CONFIGURATION_TYPES_H__ +#include + +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__ \ No newline at end of file diff --git a/firmware/code/os_descriptors.h b/firmware/code/os_descriptors.h new file mode 100644 index 0000000..9431c8c --- /dev/null +++ b/firmware/code/os_descriptors.h @@ -0,0 +1,68 @@ +#ifndef __OS_DESCRIPTORS__ +#define __OS_DESCRIPTORS__ +#include + +#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__ \ No newline at end of file diff --git a/firmware/code/run.c b/firmware/code/run.c index 3536774..ae901b4 100644 --- a/firmware/code/run.c +++ b/firmware/code/run.c @@ -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);