#include "project.h"

/* Define this to include the DFU APP interface. */
const struct usb_device_descriptor dev = {
  .bLength = USB_DT_DEVICE_SIZE,
  .bDescriptorType = USB_DT_DEVICE,
  .bcdUSB = 0x0200,
  .bDeviceClass = 0,
  .bDeviceSubClass = 0,
  .bDeviceProtocol = 0,
  .bMaxPacketSize0 = 64,
  .idVendor = 0x1d6b,
  .idProduct = 0x0ee3,
  .bcdDevice = 0x0200,
  .iManufacturer = 1,
  .iProduct = 2,
  .iSerialNumber = 3,
  .bNumConfigurations = 1,
};

const struct usb_interface ifaces[] = {
  {
   .num_altsetting = 1,
   .altsetting = &keyboard_iface,
   },
  {
   .num_altsetting = 1,
   .altsetting = &mouse_iface,
   },
  {
   .num_altsetting = 1,
   .altsetting = &tablet_iface,
   },
#ifdef INCLUDE_DFU_INTERFACE
  {
   .num_altsetting = 1,
   .altsetting = &dfu_iface,
#endif
   }
};

const struct usb_config_descriptor config = {
  .bLength = USB_DT_CONFIGURATION_SIZE,
  .bDescriptorType = USB_DT_CONFIGURATION,
  .wTotalLength = 0,
#ifdef INCLUDE_DFU_INTERFACE
  .bNumInterfaces = 4,
#else
  .bNumInterfaces = 3,
#endif
  .bConfigurationValue = 1,
  .iConfiguration = 0,
  .bmAttributes = 0xC0,
  .bMaxPower = 0x32,

  .interface = ifaces,
};

static const char *usb_strings[] = {
  "Cabbages are good for you",
  "fish",
  "soup",
};



usbd_device *usbd_dev;


static int
usb_control_request (usbd_device * usbd_dev, struct usb_setup_data *req,
                     uint8_t ** buf, uint16_t * len,
                     void (**complete) (usbd_device * usbd_dev,
                                        struct usb_setup_data * req))
{

  (void) complete;
  (void) usbd_dev;

  if ((req->bmRequestType != 0x81) ||
      (req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200))
    return 0;

  switch (req->wIndex)
    {
    case 0:
      keyboard_get_descriptor (buf, len);
      return 1;
    case 1:
      mouse_get_descriptor (buf, len);
      return 1;
    case 2:
      tablet_get_descriptor (buf, len);
      return 1;
    }

  *len = 0;
  return 0;
}


void
usb_set_config (usbd_device * usbd_dev, uint16_t wValue)
{
  (void) wValue;
  (void) usbd_dev;

  usbd_ep_setup (usbd_dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 4, NULL);
  usbd_ep_setup (usbd_dev, 0x82, USB_ENDPOINT_ATTR_INTERRUPT, 4, NULL);
  usbd_ep_setup (usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 4, NULL);

  usbd_register_control_callback (usbd_dev,
                                  USB_REQ_TYPE_STANDARD |
                                  USB_REQ_TYPE_INTERFACE,
                                  USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
                                  usb_control_request);

#ifdef INCLUDE_DFU_INTERFACE
  usbd_register_control_callback (usbd_dev,
                                  USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
                                  USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
                                  dfu_control_request);
#endif
}

/* Buffer to be used for control requests. */
static uint8_t usbd_control_buffer[128];

void
usb_init (void)
{

  usbd_dev =
    usbd_init (&stm32f103_usb_driver, &dev, &config, usb_strings, 3,
               usbd_control_buffer, sizeof (usbd_control_buffer));

  usbd_register_set_config_callback (usbd_dev, usb_set_config);

}

void
usb_run (void)
{
  while (1)
    usbd_poll (usbd_dev);
}