Fixes and minor features.
This commit is contained in:
parent
5fd063a9af
commit
9beeb23c89
|
@ -23,9 +23,12 @@
|
|||
#include "bqf.h"
|
||||
#include "run.h"
|
||||
#ifndef TEST_TARGET
|
||||
#include "pico/multicore.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/usb_device.h"
|
||||
#include "hardware/flash.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/i2c.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -59,7 +62,7 @@ static const default_configuration default_config = {
|
|||
// Grab the last 4k page of flash for our configuration strutures.
|
||||
#ifndef TEST_TARGET
|
||||
static const size_t USER_CONFIGURATION_OFFSET = PICO_FLASH_SIZE_BYTES - 0x1000;
|
||||
static const uint8_t *user_configuration = (const uint8_t *) (XIP_BASE + USER_CONFIGURATION_OFFSET);
|
||||
const uint8_t *user_configuration = (const uint8_t *) (XIP_BASE + USER_CONFIGURATION_OFFSET);
|
||||
#endif
|
||||
/**
|
||||
* TODO: For now, assume we always get a complete configuration but maybe we
|
||||
|
@ -71,6 +74,8 @@ static uint8_t inactive_working_configuration = 0;
|
|||
static uint8_t result_buffer[256] = { U16_TO_U8S_LE(NOK), U16_TO_U8S_LE(4) };
|
||||
|
||||
static bool reload_config = false;
|
||||
static bool save_config = false;
|
||||
static bool factory_reset = false;
|
||||
static uint16_t write_offset = 0;
|
||||
static uint16_t read_offset = 0;
|
||||
|
||||
|
@ -87,7 +92,7 @@ bool validate_filter_configuration(filter_configuration_tlv *filters)
|
|||
const uint32_t type = *(uint32_t *)ptr;
|
||||
const uint16_t remaining = (uint16_t)(end - ptr);
|
||||
if (count++ > MAX_FILTER_STAGES) {
|
||||
printf("Error! Too many filters defined.\n");
|
||||
printf("Error! Too many filters defined. (%d)\n", count);
|
||||
return false;
|
||||
}
|
||||
switch (type) {
|
||||
|
@ -103,6 +108,7 @@ bool validate_filter_configuration(filter_configuration_tlv *filters)
|
|||
printf("Error! Not enough data left for filter2 (%d)..\n", remaining);
|
||||
return false;
|
||||
}
|
||||
ptr += sizeof(filter2);
|
||||
break;
|
||||
}
|
||||
case PEAKING:
|
||||
|
@ -167,7 +173,20 @@ bool validate_configuration(tlv_header *config) {
|
|||
ptr = (uint8_t *) config->value;
|
||||
break;
|
||||
case FLASH_HEADER: {
|
||||
ptr = (uint8_t *) ((flash_header_tlv*) config)->tlvs;
|
||||
flash_header_tlv* header = (flash_header_tlv*) config;
|
||||
if (header->magic != FLASH_MAGIC) {
|
||||
printf("Unexpected magic word (%x)\n", header->magic);
|
||||
return false;
|
||||
}
|
||||
if (header->version > CONFIG_VERSION) {
|
||||
printf("Config is too new (%d > %d)\n", header->version, CONFIG_VERSION);
|
||||
return false;
|
||||
}
|
||||
if (header->version < MINIMUM_CONFIG_VERSION) {
|
||||
printf("Config is too old (%d > %d)\n", header->version, MINIMUM_CONFIG_VERSION);
|
||||
return false;
|
||||
}
|
||||
ptr = (uint8_t *) header->tlvs;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -194,7 +213,8 @@ bool validate_configuration(tlv_header *config) {
|
|||
printf("Preprocessing size missmatch: %u != %zu\n", tlv->length, sizeof(preprocessing_configuration_tlv));
|
||||
return false;
|
||||
}
|
||||
break;}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unknown TLVs are not invalid, just ignored.
|
||||
break;
|
||||
|
@ -243,6 +263,7 @@ bool apply_configuration(tlv_header *config) {
|
|||
|
||||
void load_config() {
|
||||
#ifndef TEST_TARGET
|
||||
flash_header_tlv* hdr = (flash_header_tlv*) user_configuration;
|
||||
// Try to load data from flash
|
||||
if (validate_configuration((tlv_header*) user_configuration)) {
|
||||
apply_configuration((tlv_header*) user_configuration);
|
||||
|
@ -254,40 +275,84 @@ void load_config() {
|
|||
}
|
||||
|
||||
#ifndef TEST_TARGET
|
||||
bool save_config() {
|
||||
// Must be in RAM
|
||||
uint8_t flash_buffer[FLASH_PAGE_SIZE];
|
||||
bool __no_inline_not_in_flash_func(save_configuration)() {
|
||||
const uint8_t active_configuration = inactive_working_configuration ? 0 : 1;
|
||||
tlv_header* config = (tlv_header*) working_configuration[active_configuration];
|
||||
|
||||
if (validate_configuration(config)) {
|
||||
const size_t config_length = config->length - (size_t)((size_t)config->value - (size_t)config);
|
||||
// Write data to flash
|
||||
flash_header_tlv flash_header;
|
||||
flash_header.header.type = FLASH_HEADER;
|
||||
flash_header.header.length = sizeof(flash_header) + config_length;
|
||||
flash_header.magic = FLASH_MAGIC;
|
||||
flash_header.version = CONFIG_VERSION;
|
||||
flash_range_program(USER_CONFIGURATION_OFFSET, (const uint8_t *) &flash_header, sizeof(flash_header));
|
||||
flash_range_program(USER_CONFIGURATION_OFFSET + sizeof(flash_header), config->value, config_length);
|
||||
flash_header_tlv* flash_header = (flash_header_tlv*) flash_buffer;
|
||||
flash_header->header.type = FLASH_HEADER;
|
||||
flash_header->header.length = sizeof(flash_header) + config_length;
|
||||
flash_header->magic = FLASH_MAGIC;
|
||||
flash_header->version = CONFIG_VERSION;
|
||||
memcpy((void*)(flash_header->tlvs), config->value, config_length);
|
||||
|
||||
power_down_dac();
|
||||
|
||||
uint32_t ints = save_and_disable_interrupts();
|
||||
flash_range_erase(USER_CONFIGURATION_OFFSET, FLASH_SECTOR_SIZE);
|
||||
flash_range_program(USER_CONFIGURATION_OFFSET, flash_buffer, FLASH_PAGE_SIZE);
|
||||
restore_interrupts(ints);
|
||||
|
||||
power_up_dac();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process_cmd(tlv_header* cmd) {
|
||||
tlv_header* result = ((tlv_header*) result_buffer);
|
||||
switch (cmd->type) {
|
||||
case SET_CONFIGURATION:
|
||||
if (validate_configuration(cmd)) {
|
||||
inactive_working_configuration = (inactive_working_configuration ? 0 : 1);
|
||||
((tlv_header*) working_configuration[inactive_working_configuration])->length = 0;
|
||||
inactive_working_configuration = inactive_working_configuration ? 0 : 1;
|
||||
reload_config = true;
|
||||
result->type = OK;
|
||||
result->length = 4;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case SAVE_CONFIGURATION: {
|
||||
if (cmd->length == 4) {
|
||||
return save_config();
|
||||
save_config = true;
|
||||
result->type = OK;
|
||||
result->length = 4;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FACTORY_RESET: {
|
||||
if (cmd->length == 4) {
|
||||
factory_reset = true;
|
||||
flash_header_tlv flash_header = { 0 };
|
||||
result->type = OK;
|
||||
result->length = 4;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GET_VERSION: {
|
||||
if (cmd->length == 4) {
|
||||
result->type = OK;
|
||||
result->length = 4 + sizeof(version_status_tlv);
|
||||
version_status_tlv* version = ((version_status_tlv*) result->value);
|
||||
version->header.type = VERSION_STATUS;
|
||||
version->header.length = sizeof(version_status_tlv);
|
||||
version->current_version = CONFIG_VERSION;
|
||||
version->minimum_supported_version = MINIMUM_CONFIG_VERSION;
|
||||
version->reserved = 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
result->type = NOK;
|
||||
result->length = 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -306,20 +371,10 @@ void config_out_packet(struct usb_endpoint *ep) {
|
|||
write_offset += buffer->data_len;
|
||||
|
||||
const uint16_t transfer_length = ((tlv_header*) working_configuration[inactive_working_configuration])->length;
|
||||
//printf("config_length %d %d\n", transfer_length, write_offset);
|
||||
if (transfer_length && write_offset >= transfer_length) {
|
||||
// Command complete, fill the result buffer
|
||||
tlv_header* result = ((tlv_header*) result_buffer);
|
||||
write_offset = 0;
|
||||
|
||||
if (process_cmd((tlv_header*) working_configuration[inactive_working_configuration])) {
|
||||
result->type = OK;
|
||||
result->length = 4;
|
||||
}
|
||||
else {
|
||||
result->type = NOK;
|
||||
result->length = 4;
|
||||
}
|
||||
process_cmd((tlv_header*) working_configuration[inactive_working_configuration]);
|
||||
}
|
||||
|
||||
usb_grow_transfer(ep->current_transfer, 1);
|
||||
|
@ -357,6 +412,18 @@ void config_in_packet(struct usb_endpoint *ep) {
|
|||
}
|
||||
|
||||
void apply_core0_config() {
|
||||
if (save_config) {
|
||||
save_config = false;
|
||||
save_configuration();
|
||||
}
|
||||
if (factory_reset) {
|
||||
factory_reset = false;
|
||||
power_down_dac();
|
||||
uint32_t ints = save_and_disable_interrupts();
|
||||
flash_range_erase(USER_CONFIGURATION_OFFSET, FLASH_SECTOR_SIZE);
|
||||
restore_interrupts(ints);
|
||||
power_up_dac();
|
||||
}
|
||||
}
|
||||
|
||||
void apply_core1_config() {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#define FLASH_MAGIC 0x2E8AFEDD
|
||||
#define CONFIG_VERSION 1
|
||||
#define MINIMUM_CONFIG_VERSION 1
|
||||
|
||||
enum structure_types {
|
||||
// Commands/Responses, these are container TLVs. The Value will be a set of TLV structures.
|
||||
|
@ -31,6 +32,7 @@ enum structure_types {
|
|||
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
|
||||
FACTORY_RESET, // Invalidates the flash memory
|
||||
|
||||
// Configuration structures, these are returned in the body of a command/response
|
||||
PREPROCESSING_CONFIGURATION = 0x200,
|
||||
|
@ -98,6 +100,7 @@ typedef struct __attribute__((__packed__)) _version_status_tlv {
|
|||
tlv_header header;
|
||||
uint16_t current_version;
|
||||
uint16_t minimum_supported_version;
|
||||
uint32_t reserved;
|
||||
} version_status_tlv;
|
||||
|
||||
typedef struct __attribute__((__packed__)) _default_configuration {
|
||||
|
|
|
@ -49,18 +49,7 @@
|
|||
i2s_obj_t i2s_write_obj;
|
||||
static uint8_t *userbuf;
|
||||
|
||||
static struct {
|
||||
uint32_t freq;
|
||||
union {
|
||||
int16_t volume[2];
|
||||
int32_t _volume;
|
||||
};
|
||||
union {
|
||||
int16_t target_volume[2];
|
||||
int32_t _target_volume;
|
||||
};
|
||||
bool mute;
|
||||
} audio_state = {
|
||||
audio_state_config audio_state = {
|
||||
.freq = 48000,
|
||||
};
|
||||
|
||||
|
@ -108,6 +97,9 @@ static void _as_audio_packet(struct usb_endpoint *ep) {
|
|||
int32_t *out = (int32_t *) userbuf;
|
||||
int samples = usb_buffer->data_len / 2;
|
||||
|
||||
// Update filters if required
|
||||
apply_core0_config();
|
||||
|
||||
if (preprocessing.reverse_stereo) {
|
||||
for (int i = 0; i < samples; i+=2) {
|
||||
out[i] = in[i+1];
|
||||
|
@ -143,9 +135,6 @@ static void _as_audio_packet(struct usb_endpoint *ep) {
|
|||
// keep on truckin'
|
||||
usb_grow_transfer(ep->current_transfer, 1);
|
||||
usb_packet_done(ep);
|
||||
|
||||
// Update filters if required
|
||||
apply_core0_config();
|
||||
}
|
||||
|
||||
static void update_volume()
|
||||
|
@ -245,7 +234,7 @@ void setup() {
|
|||
// The PCM3060 supports standard mode (100kbps) or fast mode (400kbps)
|
||||
// we run in fast mode so we dont block the core for too long while
|
||||
// updating the volume.
|
||||
i2c_init(i2c0, 400000);
|
||||
i2c_init(i2c0, 50000);
|
||||
gpio_set_function(PCM3060_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PCM3060_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PCM3060_SDA_PIN);
|
||||
|
@ -786,13 +775,11 @@ static struct usb_stream_transfer_funcs control_stream_funcs = {
|
|||
|
||||
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);
|
||||
//("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);
|
||||
|
||||
if (setup->bmRequestType & USB_DIR_IN) {
|
||||
if (USB_REQ_TYPE_RECIPIENT_DEVICE == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
|
||||
printf("Device request %x\n", (setup->wValue >> 8));
|
||||
if ((setup->bRequest == USB_REQUEST_GET_DESCRIPTOR) && ((setup->wValue >> 8) == 0xF /* BOS */)) {
|
||||
printf("Request BOS descriptor, idx %d\n", setup->wValue & 0xFF);
|
||||
|
||||
struct usb_endpoint *usb_control_in = usb_get_control_in_endpoint();
|
||||
static struct usb_stream_transfer_funcs control_stream_funcs = {
|
||||
|
@ -815,6 +802,7 @@ static bool ad_setup_request_handler(__unused struct usb_device *device, struct
|
|||
// To prevent badly behaving software from accidentally triggering a reboot, e expect
|
||||
// the wValue to be equal to the Ploopy vendor id.
|
||||
if (setup->bRequest == REBOOT_BOOTLOADER && setup->wValue == 0x2E8A) {
|
||||
power_down_dac();
|
||||
reset_usb_boot(0, 0);
|
||||
// reset_usb_boot does not return, so we will not respond to this command.
|
||||
return true;
|
||||
|
@ -822,9 +810,7 @@ static bool ad_setup_request_handler(__unused struct usb_device *device, struct
|
|||
else if (USB_REQ_TYPE_RECIPIENT_DEVICE == (setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) && setup->bRequest == MICROSOFT_COMPATIBLE_ID_FEATURE_DESRIPTOR && setup->wIndex == 0x7)
|
||||
{
|
||||
const int length = MIN(MS_OS_20_DESC_LEN, setup->wLength);
|
||||
|
||||
printf("Sending %u bytes (%u %u)\n", length, MS_OS_20_DESC_LEN, sizeof(desc_ms_os_20));
|
||||
|
||||
//printf("Sending %u bytes (%u %u)\n", length, MS_OS_20_DESC_LEN, sizeof(desc_ms_os_20));
|
||||
struct usb_endpoint *usb_control_in = usb_get_control_in_endpoint();
|
||||
usb_stream_setup_transfer(&_control_in_stream_transfer, &control_stream_funcs, desc_ms_os_20,
|
||||
sizeof(desc_ms_os_20), length, _tf_send_control_in_ack);
|
||||
|
@ -841,13 +827,13 @@ static bool ad_setup_request_handler(__unused struct usb_device *device, struct
|
|||
static struct usb_stream_transfer _config_in_stream_transfer;
|
||||
static bool configuration_interface_setup_request_handler(__unused struct usb_interface *interface, struct usb_setup_packet *setup) {
|
||||
setup = __builtin_assume_aligned(setup, 4);
|
||||
printf("configuration_interface_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
//printf("configuration_interface_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ac_setup_request_handler(__unused struct usb_interface *interface, struct usb_setup_packet *setup) {
|
||||
setup = __builtin_assume_aligned(setup, 4);
|
||||
printf("ac_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
//printf("ac_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
|
||||
if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
|
||||
switch (setup->bRequest) {
|
||||
|
@ -875,7 +861,7 @@ static bool ac_setup_request_handler(__unused struct usb_interface *interface, s
|
|||
|
||||
bool _as_setup_request_handler(__unused struct usb_endpoint *ep, struct usb_setup_packet *setup) {
|
||||
setup = __builtin_assume_aligned(setup, 4);
|
||||
printf("as_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
//printf("as_setup_request_handler: Type %u, Request %u, Value %u, Index %u, Length %u\n", setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
|
||||
if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
|
||||
switch (setup->bRequest) {
|
||||
|
@ -950,6 +936,22 @@ void usb_sound_card_init() {
|
|||
usb_device_start();
|
||||
}
|
||||
|
||||
// Some operations will cause popping on the audio output, temporarily
|
||||
// disabling the DAC sounds much better.
|
||||
void power_down_dac() {
|
||||
uint8_t buf[2];
|
||||
buf[0] = 64; // register addr
|
||||
buf[1] = 0xF0; // DAC low power mode
|
||||
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
void power_up_dac() {
|
||||
uint8_t buf[2];
|
||||
buf[0] = 64; // register addr
|
||||
buf[1] = 0xE0; // DAC normal mode
|
||||
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* USB-related code ends here.
|
||||
****************************************************************************/
|
||||
|
|
|
@ -46,6 +46,20 @@
|
|||
#define MAX_VOLUME ENCODE_DB(0)
|
||||
#define VOLUME_RESOLUTION ENCODE_DB(0.5f)
|
||||
|
||||
typedef struct _audio_state_config {
|
||||
uint32_t freq;
|
||||
union {
|
||||
int16_t volume[2];
|
||||
int32_t _volume;
|
||||
};
|
||||
union {
|
||||
int16_t target_volume[2];
|
||||
int32_t _target_volume;
|
||||
};
|
||||
bool mute;
|
||||
} audio_state_config;
|
||||
extern audio_state_config audio_state;
|
||||
|
||||
typedef struct _audio_device_config {
|
||||
struct usb_configuration_descriptor descriptor;
|
||||
struct usb_interface_descriptor ac_interface;
|
||||
|
@ -144,5 +158,6 @@ static bool do_set_current(struct usb_setup_packet *);
|
|||
static bool ac_setup_request_handler(__unused struct usb_interface *, struct usb_setup_packet *);
|
||||
bool _as_setup_request_handler(__unused struct usb_endpoint *, struct usb_setup_packet *);
|
||||
void usb_sound_card_init(void);
|
||||
|
||||
extern void power_down_dac();
|
||||
extern void power_up_dac();
|
||||
#endif
|
Loading…
Reference in New Issue