/**
 * Copyright 2022 Colin Lam, Ploopy Corporation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPECIAL THANKS TO:
 * @kilograham (github.com/kilograham)
 * for his exceptional work on Pico Playground's usb-sound-card, on which
 * a large portion of this work is based.
 */

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "hardware/vreg.h"
#include "hardware/pwm.h"
#include "hardware/i2c.h"
#include "hardware/sync.h"

#include "pico/stdlib.h"
#include "pico/usb_device.h"
#include "pico/multicore.h"
#include "pico/bootrom.h"
#include "AudioClassCommon.h"

#include "run.h"
#include "ringbuf.h"
#include "i2s.h"
#include "bqf.h"
#include "user.h"

i2s_obj_t i2s_write_obj;
static uint8_t *userbuf;

bqf_coeff_t bqf_filters_left[MAX_FILTER_STAGES];
bqf_coeff_t bqf_filters_right[MAX_FILTER_STAGES];
bqf_mem_t bqf_filters_mem_left[MAX_FILTER_STAGES];
bqf_mem_t bqf_filters_mem_right[MAX_FILTER_STAGES];

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 = {
    .freq = 48000,
};

enum vendor_cmds {
    REBOOT_BOOTLOADER = 0
};

int main(void) {
    setup();

    define_filters();

    // start second core (called "core 1" in the SDK)
    multicore_launch_core1(core1_entry);
    multicore_fifo_push_blocking((uintptr_t) userbuf);
    uint32_t ready = multicore_fifo_pop_blocking();
    if (ready != CORE1_READY) {
        //printf("core 1 startup sequence is hella borked")
        exit(1);
    }

    usb_sound_card_init();

    while (true)
        __wfi();
}

// Here's the meat. It's where the data buffer from USB gets transformed from
// PCM data into I2S data that gets shipped out to the PCM3060. It really
// belongs with the other USB-related code due to its utter indecipherability,
// but it's placed here to emphasize its importance.
static void _as_audio_packet(struct usb_endpoint *ep) {
    struct usb_buffer *usb_buffer = usb_current_out_packet_buffer(ep);
    int16_t *in = (int16_t *) usb_buffer->data;
    int32_t *out = (int32_t *) userbuf;
    int samples = usb_buffer->data_len / 2;

    for (int i = 0; i < samples; i++)
        out[i] = in[i];

    multicore_fifo_push_blocking(CORE0_READY);
    multicore_fifo_push_blocking(samples);

    for (int j = 0; j < filter_stages; j++) {
        // Left channel filter
        for (int i = 0; i < samples; i += 2) {
            fix16_t x_f16 = fix16_from_int((int16_t) out[i]);

            x_f16 = bqf_transform(x_f16, &bqf_filters_left[j],
                &bqf_filters_mem_left[j]);

            out[i] = (int32_t) fix16_to_int(x_f16);
        }
    }

    // Block until core 1 has finished transforming the data
    uint32_t ready = multicore_fifo_pop_blocking();

    i2s_stream_write(&i2s_write_obj, userbuf, samples * 4);

    // keep on truckin'
    usb_grow_transfer(ep->current_transfer, 1);
    usb_packet_done(ep);
}

static void update_volume()
{
    if (audio_state._volume != audio_state._target_volume) {
        // PCM3060 volume attenuation:
        //  0: 0db (default)
        //  55: -100db
        //  56..: Mute
        uint8_t buf[3];
        buf[0] = 65;    // register addr
        buf[1] = 255 + (audio_state.target_volume[0] / 128); // data left
        buf[2] = 255 + (audio_state.target_volume[1] / 128); // data right
        i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 3, false);

        audio_state._volume = audio_state._target_volume;
    }
}

void core1_entry() {
    uint8_t *userbuf = (uint8_t *) multicore_fifo_pop_blocking();
    int32_t *out = (int32_t *) userbuf;

    multicore_fifo_push_blocking(CORE1_READY);

    while (true) {
        // Block until the userbuf is filled with data
        uint32_t ready = multicore_fifo_pop_blocking();
        while (ready != CORE0_READY)
            ready = multicore_fifo_pop_blocking();
        
        uint32_t limit = multicore_fifo_pop_blocking();

        for (int j = 0; j < filter_stages; j++) {
            for (int i = 1; i < limit; i += 2) {
                fix16_t x_f16 = fix16_from_int((int16_t) out[i]);

                x_f16 = bqf_transform(x_f16, &bqf_filters_right[j],
                    &bqf_filters_mem_right[j]);

                out[i] = (int16_t) fix16_to_int(x_f16);
            }
        }

        // Signal to core 0 that the data has all been transformed
        multicore_fifo_push_blocking(CORE1_READY);

        // Update the volume if required. We do this from core1 as
        // core0 is more heavily loaded, doing this from core0 can
        // lead to audio crackling.
        update_volume();
    }
}

void setup() {
    set_sys_clock_khz(SYSTEM_FREQ / 1000, true);
    sleep_ms(100);

    userbuf = malloc(sizeof(uint8_t) * RINGBUF_LEN_IN_BYTES);
    
    // Configure DAC PWM
    gpio_set_function(PCM3060_SCKI2_PIN, GPIO_FUNC_PWM);
    uint slice_num_dac = pwm_gpio_to_slice_num(PCM3060_SCKI2_PIN);
    uint chan_num_dac = pwm_gpio_to_channel(PCM3060_SCKI2_PIN);
    pwm_set_phase_correct(slice_num_dac, false);
    pwm_set_wrap(slice_num_dac, (SYSTEM_FREQ / CODEC_FREQ) - 1);
    pwm_set_chan_level(slice_num_dac, chan_num_dac, (SYSTEM_FREQ / CODEC_FREQ / 2));
    pwm_set_enabled(slice_num_dac, true);

    gpio_init(AUDIO_POS_SUPPLY_EN_PIN);
    gpio_set_dir(AUDIO_POS_SUPPLY_EN_PIN, GPIO_OUT);
    gpio_put(AUDIO_POS_SUPPLY_EN_PIN, true);

    sleep_ms(100);

    configure_neg_switch_pwm();

    // After negative switching PWM is configured, take the PCM out of reset
    gpio_init(PCM3060_RST_PIN);
    gpio_set_dir(PCM3060_RST_PIN, GPIO_OUT);
    gpio_put(PCM3060_RST_PIN, true);

    // 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);
    gpio_set_function(PCM3060_SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(PCM3060_SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(PCM3060_SDA_PIN);
    gpio_pull_up(PCM3060_SCL_PIN);

    // Let the PCM stabilize before power-on
    sleep_ms(200);

    // Resynchronise clocks. Do not enable PCM yet.
    uint8_t buf[2];
    buf[0] = 64; // register addr
    buf[1] = 0xB0; // data
    i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);

    // Don't remove this. Don't do it.
    sleep_ms(200);

    // Set data format to 16 bit right justified, MSB first
    buf[0] = 67;   // register addr
    buf[1] = 0x03; // data
    i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);

    // Enable DAC
    buf[0] = 64; // register addr
    buf[1] = 0xE0; // data
    i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);

    // Same here, pal. Hands off.
    sleep_ms(100);

    i2s_write_obj.sck_pin = PCM3060_DAC_SCK_PIN;
    i2s_write_obj.ws_pin = PCM3060_DAC_WS_PIN;
    i2s_write_obj.sd_pin = PCM3060_DAC_SD_PIN;
    i2s_write_obj.sampling_rate = SAMPLING_FREQ;

    i2s_write_init(&i2s_write_obj);
}

/** **************************************************************************
 * DO. NOT. FUCKING. CHANGE. THIS. FUNCTION.                                 *
 * IF YOU DO, YOU COULD BLOW UP YOUR HARDWARE!                               *
 * YOU WERE WARNED!!!!!!!!!!!!!!!!                                           *
 ****************************************************************************/
void configure_neg_switch_pwm() {
    gpio_set_function(NEG_SWITCH_PWM_PIN, GPIO_FUNC_PWM);
    uint slice_num = pwm_gpio_to_slice_num(NEG_SWITCH_PWM_PIN);
    uint chan_num = pwm_gpio_to_channel(NEG_SWITCH_PWM_PIN);
    pwm_set_phase_correct(slice_num, false);

    uint16_t wrap = round((float) SYSTEM_FREQ / (float) NEG_SWITCH_FREQ);
    pwm_set_wrap(slice_num, wrap - 1);

    uint16_t target_level = round((float) SYSTEM_FREQ / (float) NEG_SWITCH_FREQ /
            (float) NEG_DUTY_DEN * (float) NEG_DUTY_NUM);
    pwm_set_chan_level(slice_num, chan_num, 0);
    pwm_set_enabled(slice_num, true);
    sleep_ms(10);

    // Ramp up the duty cycle.
    // Seriously, don't fuck with this. A spike on the negative voltage supply
    // because this isn't ramping correctly will destroy the hardware.
    size_t i;
    for(i = 0; i < 200; i++) {
        uint16_t current_level = round(i * ((float)target_level / 200.0));
        pwm_set_chan_level(slice_num, chan_num, current_level);
        sleep_ms(1);
    }
}




/*****************************************************************************
 * USB-related code begins here. It's a refactoring nightmare, so here it
 * shall lie for a thousand years.
 ****************************************************************************/

// todo noop when muted

static const audio_device_config ad_conf = {
    .descriptor = {
        .bLength = sizeof(ad_conf.descriptor),
        .bDescriptorType = DTYPE_Configuration,
        .wTotalLength = sizeof(ad_conf),
        .bNumInterfaces = 2,
        .bConfigurationValue = 0x01,
        .iConfiguration = 0x00,
        .bmAttributes = 0x80,
        .bMaxPower = 0xFA,
    },
    .ac_interface = {
        .bLength = sizeof(ad_conf.ac_interface),
        .bDescriptorType = DTYPE_Interface,
        .bInterfaceNumber = 0x00,
        .bAlternateSetting = 0x00,
        .bNumEndpoints = 0x00,
        .bInterfaceClass = AUDIO_CSCP_AudioClass,
        .bInterfaceSubClass = AUDIO_CSCP_ControlSubclass,
        .bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
        .iInterface = 0x00,
    },
    .ac_audio = {
        .core = {
            .bLength = sizeof(ad_conf.ac_audio.core),
            .bDescriptorType = AUDIO_DTYPE_CSInterface,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_Header,
            .bcdADC = VERSION_BCD(1, 0, 0),
            .wTotalLength = sizeof(ad_conf.ac_audio),
            .bInCollection = 1,
            .bInterfaceNumbers = 1,
        },
        .input_terminal = {
            .bLength = sizeof(ad_conf.ac_audio.input_terminal),
            .bDescriptorType = AUDIO_DTYPE_CSInterface,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_InputTerminal,
            .bTerminalID = 1,
            .wTerminalType = AUDIO_TERMINAL_STREAMING,
            .bAssocTerminal = 0,
            .bNrChannels = 2,
            .wChannelConfig = AUDIO_CHANNEL_LEFT_FRONT | AUDIO_CHANNEL_RIGHT_FRONT,
            .iChannelNames = 0,
            .iTerminal = 0,
        },
        .feature_unit = {
            .bLength = sizeof(ad_conf.ac_audio.feature_unit),
            .bDescriptorType = AUDIO_DTYPE_CSInterface,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_Feature,
            .bUnitID = 2,
            .bSourceID = 1,
            .bControlSize = 1,
            .bmaControls = {
                AUDIO_FEATURE_MUTE, // Master channel
                AUDIO_FEATURE_VOLUME, // Left channel
                AUDIO_FEATURE_VOLUME, // Right channel
            },
            .iFeature = 0,
        },
        .output_terminal = {
            .bLength = sizeof(ad_conf.ac_audio.output_terminal),
            .bDescriptorType = AUDIO_DTYPE_CSInterface,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,
            .bTerminalID = 3,
            .wTerminalType = AUDIO_TERMINAL_OUT_HEADPHONES,
            .bAssocTerminal = 0,
            .bSourceID = 2,
            .iTerminal = 0,
        },
    },
    .as_zero_interface = {
        .bLength = sizeof(ad_conf.as_zero_interface),
        .bDescriptorType = DTYPE_Interface,
        .bInterfaceNumber = 0x01,
        .bAlternateSetting = 0x00,
        .bNumEndpoints = 0x00,
        .bInterfaceClass = AUDIO_CSCP_AudioClass,
        .bInterfaceSubClass = AUDIO_CSCP_AudioStreamingSubclass,
        .bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
        .iInterface = 0x00,
    },
    .as_op_interface = {
        .bLength = sizeof(ad_conf.as_op_interface),
        .bDescriptorType = DTYPE_Interface,
        .bInterfaceNumber = 0x01,
        .bAlternateSetting = 0x01,
        .bNumEndpoints = 0x02,
        .bInterfaceClass = AUDIO_CSCP_AudioClass,
        .bInterfaceSubClass = AUDIO_CSCP_AudioStreamingSubclass,
        .bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
        .iInterface = 0x00,
    },
    .as_audio = {
        .streaming = {
            .bLength = sizeof(ad_conf.as_audio.streaming),
            .bDescriptorType = AUDIO_DTYPE_CSInterface,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_General,
            .bTerminalLink = 1,
            .bDelay = 1,
            .wFormatTag = 1, // PCM
        },
        .format = {
            .core = {
                .bLength = sizeof(ad_conf.as_audio.format),
                .bDescriptorType = AUDIO_DTYPE_CSInterface,
                .bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_FormatType,
                .bFormatType = 1,
                .bNrChannels = 2,
                .bSubFrameSize = 2,
                .bBitResolution = 16,
                .bSampleFrequencyType = 1,
            },
            .freqs = {
                0x80,
                0xBB,
                0x00
            },
        },
    },
    .ep1 = {
        .core = {
            .bLength = sizeof(ad_conf.ep1.core),
            .bDescriptorType = DTYPE_Endpoint,
            .bEndpointAddress = 0x01,
            .bmAttributes = 5,
            .wMaxPacketSize = (uint8_t) 0xC4,
            .bInterval = 1,
            .bRefresh = 0,
            .bSyncAddr = 0x82,
        },
        .audio = {
            .bLength = sizeof(ad_conf.ep1.audio),
            .bDescriptorType = AUDIO_DTYPE_CSEndpoint,
            .bDescriptorSubtype = AUDIO_DSUBTYPE_CSEndpoint_General,
            .bmAttributes = 1,
            .bLockDelayUnits = 0,
            .wLockDelay = 0,
        }
    },
    .ep2 = {
        .bLength = sizeof(ad_conf.ep2),
        .bDescriptorType = 0x05,
        .bEndpointAddress = 0x82,
        .bmAttributes = 0x11,
        .wMaxPacketSize = 3,
        .bInterval = 0x01,
        .bRefresh = 2,
        .bSyncAddr = 0,
    },
};

static struct usb_interface ac_interface;
static struct usb_interface as_op_interface;
static struct usb_endpoint ep_op_out, ep_op_sync;

static const struct usb_device_descriptor boot_device_descriptor = {
    .bLength            = 18,
    .bDescriptorType    = 0x01,
    .bcdUSB             = 0x0110,
    .bDeviceClass       = 0x00,
    .bDeviceSubClass    = 0x00,
    .bDeviceProtocol    = 0x00,
    .bMaxPacketSize0    = 0x40,
    .idVendor           = 0x2E8A,
    .idProduct          = 0xFEDD,
    .bcdDevice          = 0x0200,
    .iManufacturer      = 0x01,
    .iProduct           = 0x02,
    .iSerialNumber      = 0x03,
    .bNumConfigurations = 0x01,
};

const char *_get_descriptor_string(uint index) {
    if (index <= count_of(descriptor_strings)) {
        return descriptor_strings[index - 1];
    } else {
        return "";
    }
}

static void _as_sync_packet(struct usb_endpoint *ep) {
    assert(ep->current_transfer);
    struct usb_buffer *buffer = usb_current_in_packet_buffer(ep);
    assert(buffer->data_max >= 3);
    buffer->data_len = 3;

    ring_buf_t rb = i2s_write_obj.ring_buffer;
    uint32_t feedback;

    size_t lower_limit = (RINGBUF_LEN_IN_BYTES / 2) - (RINGBUF_LEN_IN_BYTES / 4);
    size_t upper_limit = (RINGBUF_LEN_IN_BYTES / 2) + (RINGBUF_LEN_IN_BYTES / 4);

    if (ringbuf_available_data(&rb) > upper_limit) {
        // slow down
        feedback = 47 << 14;
    } else if (ringbuf_available_data(&rb) < lower_limit) {
        // we need more data
        feedback = 49 << 14;
    } else
        feedback = 48 << 14;

    //double temp = rate * 0x00004000;
    //temp += (double)((temp >= 0) ? 0.5f : -0.5f);
    //uint32_t feedback = (uint32_t) temp;

    // todo lie thru our teeth for now
    //uint feedback = 48 << 14u;

    buffer->data[0] = feedback;
    buffer->data[1] = feedback >> 8u;
    buffer->data[2] = feedback >> 16u;

    // keep on truckin'
    usb_grow_transfer(ep->current_transfer, 1);
    usb_packet_done(ep);
}

static const struct usb_transfer_type as_transfer_type = {
    .on_packet = _as_audio_packet,
    .initial_packet_count = 1,
};

static const struct usb_transfer_type as_sync_transfer_type = {
    .on_packet = _as_sync_packet,
    .initial_packet_count = 1,
};

static struct usb_transfer as_transfer;
static struct usb_transfer as_sync_transfer;

static bool do_get_current(struct usb_setup_packet *setup) {
    if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
        switch (setup->wValue >> 8u) {
            case 1: { // mute
                usb_start_tiny_control_in_transfer(audio_state.mute, 1);
                return true;
            }
            case 2: { // volume
                /* Current volume. See UAC Spec 1.0 p.77 */
                const uint8_t cn = (uint8_t) setup->wValue;
                if (cn == AUDIO_CHANNEL_LEFT_FRONT) {
                    usb_start_tiny_control_in_transfer(audio_state.target_volume[0], 2);
                }
                else if (cn == AUDIO_CHANNEL_RIGHT_FRONT) {
                    usb_start_tiny_control_in_transfer(audio_state.target_volume[1], 2);
                }
                else {
                    return false;
                }
                return true;
            }
        }
    } else if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_ENDPOINT) {
        if ((setup->wValue >> 8u) == 1) { // endpoint frequency control
            /* Current frequency */
            usb_start_tiny_control_in_transfer(audio_state.freq, 3);
            return true;
        }
    }
    return false;
}

static bool do_get_minimum(struct usb_setup_packet *setup) {
    if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
        switch (setup->wValue >> 8u) {
            case 2: { // volume
                usb_start_tiny_control_in_transfer(MIN_VOLUME, 2);
                return true;
            }
        }
    }
    return false;
}

static bool do_get_maximum(struct usb_setup_packet *setup) {
    if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
        switch (setup->wValue >> 8u) {
            case 2: { // volume
                usb_start_tiny_control_in_transfer(MAX_VOLUME, 2);
                return true;
            }
        }
    }
    return false;
}

static bool do_get_resolution(struct usb_setup_packet *setup) {
    if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
        switch (setup->wValue >> 8u) {
            case 2: { // volume
                usb_start_tiny_control_in_transfer(VOLUME_RESOLUTION, 2);
                return true;
            }
        }
    }
    return false;
}

static struct audio_control_cmd {
    uint8_t cmd;
    uint8_t type;
    uint8_t cs;
    uint8_t cn;
    uint8_t unit;
    uint8_t len;
} audio_control_cmd_t;

static void _audio_reconfigure() {
    switch (audio_state.freq) {
        case 44100:
        case 48000:
            break;
        default:
            audio_state.freq = 48000;
    }
}

static void audio_set_volume(int8_t channel, int16_t volume) {
    // volume is in the range 127.9961dB (0x7FFF) .. -127.9961dB (0x8001). 0x8000 = mute
    // the old code reported a min..max volume of -90.9961dB (0xA500) .. 0dB (0x0)

    if (volume == 0x8000) {
        // Mute case
    }
    else if (volume > (int16_t) MAX_VOLUME) {
        volume = MAX_VOLUME;
    }
    else if (volume < (int16_t) MIN_VOLUME) {
        volume = MIN_VOLUME;
    }
    if (channel == AUDIO_CHANNEL_LEFT_FRONT || channel == 0) {
        audio_state.target_volume[0] = volume;
    }
    if (channel == AUDIO_CHANNEL_RIGHT_FRONT || channel == 0) {
        audio_state.target_volume[1] = volume;
    }
}

static void audio_cmd_packet(struct usb_endpoint *ep) {
    assert(audio_control_cmd_t.cmd == AUDIO_REQ_SetCurrent);
    struct usb_buffer *buffer = usb_current_out_packet_buffer(ep);

    // printf("%s: CMD: %u, Type: %u, CS: %u, CN: %u, Unit: %u, Len: %u\n", __PRETTY_FUNCTION__, audio_control_cmd_t.cmd, audio_control_cmd_t.type,
    //    audio_control_cmd_t.cs, audio_control_cmd_t.cn, audio_control_cmd_t.unit, audio_control_cmd_t.len);

    audio_control_cmd_t.cmd = 0;
    if (buffer->data_len >= audio_control_cmd_t.len) {
        if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
            switch (audio_control_cmd_t.cs) {
                case 1: { // mute
                    audio_state.mute = buffer->data[0];
                    uint8_t buf[2];
                    buf[0] = 68;   // register addr
                    buf[1] = buffer->data[0] ? 0x3 : 0x0; // data
                    i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);
                    break;
                }
                case 2: { // volume
                    audio_set_volume(audio_control_cmd_t.cn, *(int16_t *) buffer->data);
                    break;
                }
            }

        } else if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_ENDPOINT) {
            if (audio_control_cmd_t.cs == 1) { // endpoint frequency control
                uint32_t new_freq = (*(uint32_t *) buffer->data) & 0x00ffffffu;

                if (audio_state.freq != new_freq) {
                    audio_state.freq = new_freq;
                    _audio_reconfigure();
                }
            }
        }
    }
    usb_start_empty_control_in_transfer_null_completion();
    // todo is there error handling?
}


static const struct usb_transfer_type _audio_cmd_transfer_type = {
    .on_packet = audio_cmd_packet,
    .initial_packet_count = 1,
};

static bool as_set_alternate(struct usb_interface *interface, uint alt) {
    assert(interface == &as_op_interface);
    return alt < 2;
}

static bool do_set_current(struct usb_setup_packet *setup) {
    if (setup->wLength && setup->wLength < 64) {
        audio_control_cmd_t.cmd = AUDIO_REQ_SetCurrent;
        audio_control_cmd_t.type = setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK;
        audio_control_cmd_t.len = (uint8_t) setup->wLength;
        audio_control_cmd_t.unit = setup->wIndex >> 8u;
        audio_control_cmd_t.cs = setup->wValue >> 8u;
        audio_control_cmd_t.cn = (uint8_t) setup->wValue;
        usb_start_control_out_transfer(&_audio_cmd_transfer_type);
        return true;
    }
    return false;
}

static bool ad_setup_request_handler(__unused struct usb_device *device, struct usb_setup_packet *setup) {
    setup = __builtin_assume_aligned(setup, 4);
    if (USB_REQ_TYPE_TYPE_VENDOR == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
        // 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) {
            reset_usb_boot(0, 0);
            // reset_usb_boot does not return, so we will not respond to this command.
            return true;
        }
    }
    return false;
}

static bool ac_setup_request_handler(__unused struct usb_interface *interface, struct usb_setup_packet *setup) {
    setup = __builtin_assume_aligned(setup, 4);
    if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
        switch (setup->bRequest) {
            case AUDIO_REQ_SetCurrent:
                return do_set_current(setup);

            case AUDIO_REQ_GetCurrent:
                return do_get_current(setup);

            case AUDIO_REQ_GetMinimum:
                return do_get_minimum(setup);

            case AUDIO_REQ_GetMaximum:
                return do_get_maximum(setup);

            case AUDIO_REQ_GetResolution:
                return do_get_resolution(setup);

            default:
                break;
        }
    }
    return false;
}

bool _as_setup_request_handler(__unused struct usb_endpoint *ep, struct usb_setup_packet *setup) {
    setup = __builtin_assume_aligned(setup, 4);
    if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
        switch (setup->bRequest) {
            case AUDIO_REQ_SetCurrent:
                return do_set_current(setup);

            case AUDIO_REQ_GetCurrent:
                return do_get_current(setup);

            case AUDIO_REQ_GetMinimum:
                return do_get_minimum(setup);

            case AUDIO_REQ_GetMaximum:
                return do_get_maximum(setup);

            case AUDIO_REQ_GetResolution:
                return do_get_resolution(setup);

            default:
                break;
        }
    }
    return false;
}

void usb_sound_card_init() {
    usb_interface_init(&ac_interface, &ad_conf.ac_interface, NULL, 0, true);
    ac_interface.setup_request_handler = ac_setup_request_handler;

    static struct usb_endpoint *const op_endpoints[] = {
        &ep_op_out, &ep_op_sync
    };

    usb_interface_init(&as_op_interface, &ad_conf.as_op_interface, op_endpoints,
        count_of(op_endpoints), true);
    
    as_op_interface.set_alternate_handler = as_set_alternate;
    ep_op_out.setup_request_handler = _as_setup_request_handler;
    as_transfer.type = &as_transfer_type;
    usb_set_default_transfer(&ep_op_out, &as_transfer);
    as_sync_transfer.type = &as_sync_transfer_type;
    usb_set_default_transfer(&ep_op_sync, &as_sync_transfer);

    static struct usb_interface *const boot_device_interfaces[] = {
        &ac_interface,
        &as_op_interface,
    };

    __unused struct usb_device *device = usb_device_init(&boot_device_descriptor,
        &ad_conf.descriptor, boot_device_interfaces,
        count_of(boot_device_interfaces), _get_descriptor_string);

    assert(device);

    device->setup_request_handler = ad_setup_request_handler;
    audio_set_volume(0, DEFAULT_VOLUME);
    _audio_reconfigure();

    usb_device_start();
}

/*****************************************************************************
 * USB-related code ends here.
 ****************************************************************************/
