#include "usb_descriptors.h"

const USB_DeviceDescriptor_t USB_DeviceDescriptor =
{
    .bLength = sizeof(USB_DeviceDescriptor_t),
    .bDescriptorType = USB_DEVICE_DESCRIPTOR,
    .bcdUSB = 0x0200,
    .bDeviceClass = 0x02,       // CDC
    .bDeviceSubClass = 0x00,    // Subclass per interface
    .bDeviceProtocol = 0x00,    // Protocol per interface
    .bMaxPacketSize0 = 64,
    .idVendor = 0x16c0,
    .idProduct = 0x05dc,
    .bcdDevice = 0x0200,
    .iManufacturer = 1,
    .iProduct = 2,
    .iSerialNumber = 3,
    .bNumConfigurations = 1
};

const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor =
{
    .configuration = (USB_ConfigurationDescriptor_t)
    {
        .bLength = sizeof(USB_ConfigurationDescriptor_t),
        .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
        .wTotalLength = sizeof(USB_WholeDescriptor_t),
        .bNumInterfaces = 2,
        .bConfigurationValue = 1,
        .iConfiguration = 0,
        .bmAttributes = 0x80,
        .bMaxPower = 100
    },

    // Interface 0: Control
    .control_interface = (USB_InterfaceDescriptor_t)
    {
        .bLength = sizeof(USB_InterfaceDescriptor_t),
        .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
        .bInterfaceNumber = 0,
        .bAlternateSetting = 0,
        .bNumEndpoints = 1,
        .bInterfaceClass = 0x02,    // CDC
        .bInterfaceSubClass = 0x02, // ACM
        .bInterfaceProtocol = 0x00, // No command protocol
        .iInterface = 0
    },
    .control_interface_functional_header = (USB_HeaderFunctionalDescriptor_t)
    {
        .bFunctionLength = sizeof(USB_HeaderFunctionalDescriptor_t),
        .bDescriptorType = USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR,
        .bDescriptorSubtype = 0x00, // Header subtype
        .bcdCDC = 0x0120            // TODO: Check differences between 0x0110
                                    // and 0x0120 (CDC 1.1 vs. CDC 1.2)
    },
    .control_interface_functional_acm = (USB_ACMFunctionalDescriptor_t)
    {
        .bFunctionLength = sizeof(USB_ACMFunctionalDescriptor_t),
        .bDescriptorType = USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR,
        .bDescriptorSubtype = 0x02, // ACM
        .bmCapabilities = 0x02      // Device supports Set_Line_Coding,
                                    // Set_Control_Line_State, Get_Line_Coding,
                                    // and the notification Serial_State
    },
    .control_interface_functional_unit = (USB_UnionFunctionalDescriptor_t)
    {
        .bFunctionLength = sizeof(USB_UnionFunctionalDescriptor_t),
        .bDescriptorType = USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR,
        .bDescriptorSubtype = 0x06, // Union descriptor
        .bControlInterface = 0,     // Current interface
        .bSubordinateInterface0 = 1 // Next interface for the actual data
    },
    // Endpoint 1: Notification (in)
    .notification_endpoint = (USB_EndpointDescriptor_t)
    {
        .bLength = sizeof(USB_EndpointDescriptor_t),
        .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
        .bEndpointAddress = USB_ENDPOINT_IN | 1,
        .bmAttributes = USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_NO_SYNCHRONIZATION
            | USB_ENDPOINT_DATA,
        .wMaxPacketSize = 8,
        .bInterval = 0xff
    },

    // Interface 1: Data
    .data_interface = (USB_InterfaceDescriptor_t)
    {
        .bLength = sizeof(USB_InterfaceDescriptor_t),
        .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
        .bInterfaceNumber = 1,
        .bAlternateSetting = 0,
        .bNumEndpoints = 2,
        .bInterfaceClass = 0x0a,    // CDC data class
        .bInterfaceSubClass = 0x00, // No subclass
        .bInterfaceProtocol = 0x00, // No data protocol
        .iInterface = 0
    },
    // Endpoint 3: Data out
    .data_out_endpoint = (USB_EndpointDescriptor_t)
    {
        .bLength = sizeof(USB_EndpointDescriptor_t),
        .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
        .bEndpointAddress = USB_ENDPOINT_OUT | 3,
        .bmAttributes = USB_ENDPOINT_BULK | USB_ENDPOINT_NO_SYNCHRONIZATION
            | USB_ENDPOINT_DATA,
        .wMaxPacketSize = 64,
        .bInterval = 0x05
    },
    // Endpoint 2: Data in
    .data_in_endpoint = (USB_EndpointDescriptor_t)
    {
        .bLength = sizeof(USB_EndpointDescriptor_t),
        .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
        .bEndpointAddress = USB_ENDPOINT_IN | 2,
        .bmAttributes = USB_ENDPOINT_BULK | USB_ENDPOINT_NO_SYNCHRONIZATION
            | USB_ENDPOINT_DATA,
        .wMaxPacketSize = 64,
        .bInterval = 0x05
    },
};

#define USB_STRING_LANGID                       0x0409
#define USB_STRING_VENDOR                       \
    '2', '5', '1', '2', '0'
#define USB_STRING_PRODUCT                      \
    'H', 'P', 'G', 'L', ' ', 'X', 'Y'

const uint16_t USB_StringDescriptor_LangID[] =
    USB_BUILD_STRING_DESCRIPTOR(USB_STRING_LANGID);
const uint16_t USB_StringDescriptor_Vendor[] =
    USB_BUILD_STRING_DESCRIPTOR(USB_STRING_VENDOR);
const uint16_t USB_StringDescriptor_Product[] =
    USB_BUILD_STRING_DESCRIPTOR(USB_STRING_PRODUCT);

void USB_HandleGetDescriptor(USB_DescriptorType_t descriptor_type,
    int descriptor_index, const void **reply_data, int *reply_length,
    uint8_t *reply_response)
{
    switch(descriptor_type)
    {
        case USB_DEVICE_DESCRIPTOR:
            *reply_data = &USB_DeviceDescriptor;
            *reply_length = USB_DeviceDescriptor.bLength;
            break;

        case USB_CONFIGURATION_DESCRIPTOR:
            *reply_data = &USB_ConfigurationInterfaceDescriptor;
            if(*reply_length < USB_ConfigurationInterfaceDescriptor
                .configuration.wTotalLength)
            {
                *reply_length = USB_ConfigurationInterfaceDescriptor
                    .configuration.bLength;
            }
            else
            {
                *reply_length = USB_ConfigurationInterfaceDescriptor
                    .configuration.wTotalLength;
            }
            break;

        case USB_STRING_DESCRIPTOR:
            switch(descriptor_index)
            {
                case 0:
                    *reply_data = (uint8_t*)USB_StringDescriptor_LangID;
                    *reply_length = (uint8_t)*USB_StringDescriptor_LangID;
                    break;
                case 1:
                    *reply_data = (uint8_t*)USB_StringDescriptor_Vendor;
                    *reply_length = (uint8_t)*USB_StringDescriptor_Vendor;
                    break;
                case 2:
                    *reply_data = (uint8_t*)USB_StringDescriptor_Product;
                    *reply_length = (uint8_t)*USB_StringDescriptor_Product;
                    break;
                case 3:;
                    // Serial string
                    // The first two bytes are occupied by descriptor length and
                    // descriptor type (3 = string descriptor). String
                    // descriptors are 16 bits per char and are not zero-
                    // terminated, so we're reserving a buffer for 24 characters
                    // here.
                    static uint16_t buff[25];

                    // The first byte is the total length in bytes, the second
                    // byte is the descriptor type (3)
                    buff[0] = sizeof(buff) | (USB_STRING_DESCRIPTOR << 8);

                    // The unique device ID is 96 bits = 12 bytes long
                    for(int i = 0; i < 12; i++)
                    {
                        uint8_t uid_byte = *((uint8_t*)UID_BASE + i);
                        // We're using one of the first 16 letters of the
                        // alphabet for each nibble, just like in the bootloader
                        buff[1 + 2 * i] = 'A' + (uid_byte & 0x0f);
                        buff[2 + 2 * i] = 'A' + (uid_byte >> 4);
                    }

                    *reply_data = (uint8_t*)buff;
                    *reply_length = (uint8_t)*buff;
                    break;
                default:
                    __asm__ volatile("bkpt");
            }
            break;

        case USB_INTERFACE_DESCRIPTOR:
            switch(descriptor_index)
            {
                case 0:
                    *reply_data = &USB_ConfigurationInterfaceDescriptor
                            .control_interface;
                    *reply_length = USB_ConfigurationInterfaceDescriptor
                            .control_interface.bLength;
                    break;
                case 1:
                    *reply_data = &USB_ConfigurationInterfaceDescriptor
                            .data_interface;
                    *reply_length = USB_ConfigurationInterfaceDescriptor
                            .data_interface.bLength;
                    break;
                default:
                    __asm__ volatile("bkpt");
            }
            break;

        case USB_DEVICE_QUALIFIER_DESCRIPTOR:
            // Device is full-speed only, so it must return a request error
            *reply_response = USB_EP_TX_STALL;
            *reply_data = NULL;
            break;

        case USB_ENDPOINT_DESCRIPTOR:
        case USB_OTHER_DESCRIPTOR:
        case USB_INTERFACE_POWER_DESCRIPTOR:
        case USB_INTERFACE_ASSOCIATION_DESCRIPTOR:
        case USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR:
        case USB_CLASS_SPECIFIC_ENDPOINT_DESCRIPTOR:
            // Not implemented
            __asm__ volatile("bkpt");
            break;
    }
}

