~ruther/stm32h747i-disco-usb-image-viewer

079ff751c0e9b65d936aa656e697257faebbb871 — Rutherther 4 months ago 2831954
feat: implement cdc application layer, fix enumeration
M Makefile => Makefile +1 -1
@@ 22,7 22,7 @@ OBJS=$(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
DEPFILES=$(patsubst %.c,$(DEPDIR)/%.d,$(SRCS))

DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
CFLAGS=-I$(INCDIR) \
CFLAGS=-I$(INCDIR) -Wall \
				-I$(DEVICEDIR)/$(INCDIR) \
				-Ilibs/CMSIS_6/CMSIS/Core/Include \
				-Ilibs/cmsis_device_h7/Include \

M include/delay.h => include/delay.h +14 -8
@@ 1,26 1,32 @@
#ifndef DELAY_H
#define DELAY_H

#include <stdint.h>

// TODO: define system clock well
/* #define SYSTEM_CLOCK    ((uint32_t)360000000UL) */
#define SYSTEM_CLOCK    ((uint32_t)64000000UL)

#define SYSTICK_CALIB 0x3E8
#define SYSTICK_LOAD (SYSTEM_CLOCK/1000000UL)
/* #define SYSTICK_CALIB 0x3E8 */
// Underflow every 1 us
#define SYSTICK_LOAD (SYSTEM_CLOCK/1000UL-1)
#define SYSTICK_DELAY_CALIB (SYSTICK_LOAD >> 1)

extern uint32_t us_ticks;

#define DELAY_US(us) \
    do { \
         uint32_t start = SysTick->VAL; \
         uint32_t ticks = (us * SYSTICK_LOAD)-SYSTICK_DELAY_CALIB;  \
         while((start - SysTick->VAL) < ticks); \
      for (int i = 0; i < 500000; i++); \
         /* uint32_t start = us_ticks; \ */ \
         /* while((us_ticks - start) < us); \ */ \
    } while (0)

#define DELAY_MS(ms) \
    do { \
        for (uint32_t i = 0; i < ms; ++i) { \
            DELAY_US(1000); \
        } \
      for (int i = 0; i < 500000; i++); \
        /* for (uint32_t i = 0; i < ms; ++i) { \ */ \
            /* DELAY_US(1000); \ */ \
        /* } \ */ \
    } while (0)

void systick_configure();

M include/pin.h => include/pin.h +1 -1
@@ 39,7 39,7 @@ void pin_speed(pin_t* pin, pin_speedmode_t mode);

// Reads input, not output!
uint8_t pin_read(pin_t* pin);
uint8_t pin_write(pin_t* pin, uint8_t val);
void pin_write(pin_t* pin, uint8_t val);
void pin_toggle(pin_t* pin);
void pin_set(pin_t* pin);
void pin_reset(pin_t* pin);

M include/queue.h => include/queue.h +2 -0
@@ 75,4 75,6 @@ void* queue_peek(queue_t* queue);
 */
uint16_t queue_count(queue_t* queue);

uint16_t queue_space(queue_t* queue);

#endif // QUEUE_H

M include/usb_device.h => include/usb_device.h +8 -4
@@ 31,7 31,7 @@ typedef enum {
 * Every read value is popped, every written value is enqueued to the fifo.
 */
typedef struct {
  volatile uint32_t data[128]; /**< Data memory. Every address here is the same. */
  volatile uint32_t data[1024]; /**< Data memory. Every address here is the same. */
} usb_fifo_t;

struct usb_class_header_t;


@@ 141,11 141,11 @@ typedef struct {
  void (*txfifo_empty_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on rx fifo empty.
 * @brief Callback on rx fifo full.
 * @param[in,out] device The usb device.
 * @param[in] endpoint The number of the endpoint.
 */
  void (*rxfifo_empty_callback)(usb_device_t* device, uint8_t endpoint);
  void (*rxfifo_full_callback)(usb_device_t* device, uint8_t endpoint);

/**
 * @brief Callback on tx transmission done - all data were sent from configured size.


@@ 215,7 215,7 @@ typedef struct usb_class_header_t usb_class_header_t;
typedef enum {
  SETUP_STAGE_NONE,              /**< No setup command being handled. */
  SETUP_STAGE_RCVD_SETUP_PACKET, /**< Received a setup packet, saved to memory. */
  SETUP_STAGE_AWAITING_RESPONSE, /**< Handled a setup packet, waiting for data from the host. */
  SETUP_STAGE_AWAITING_DATA, /**< Handled a setup packet, waiting for data from the host. */
  SETUP_STAGE_SENDING_RESPONSE,  /**< Handling setup packet by sending response to host. */
  SETUP_STAGE_SENDING_ACK,       /**< Handling setup packet by sending acknowledge. */
  SETUP_STAGE_AWAITING_ACK,      /**< Handled a setup packet, sent data, waiting for acknowledge */


@@ 225,6 225,10 @@ typedef enum {
typedef struct {
  queue_t* received_setup_commands;

  uint8_t* rcvd_data;
  uint16_t rcvd_count;
  uint16_t rcvd_awaiting;

  usb_setup_command_stage_t stage;          /**< Current stage status. */

  uint8_t detected_setup_errors;                  /**< Number of errors when handling setup commands. This should be zero. */

M include/usb_device_cdc.h => include/usb_device_cdc.h +32 -14
@@ 1,4 1,6 @@
#include "usb_device.h"
#include <stdbool.h>
#include <stdatomic.h>

#ifndef USB_DEVICE_CDC_H
#define USB_DEVICE_CDC_H


@@ 94,27 96,35 @@ typedef struct {
} usb_cdc_call_management_functional_decriptor_t;
_Static_assert(sizeof(usb_cdc_call_management_functional_decriptor_t) == 5, "Size check");

typedef void (*cdc_receive_callback_t)(usb_device_t* device, uint16_t count);

/* typedef enum { */
/*   CDC_STATE_NONE, */
/*   CDC_STATE_GOT_DATA, */
/*   CDC_STATE_FULL_FIFO, */
/* } cdc_rx_state_t; */

typedef enum {
  CDC_TX_STATE_NONE,
  CDC_TX_STATE_SENDING,
  CDC_TX_STATE_,
} cdc_tx_state_t;

#pragma GCC diagnostic ignored "-Wpadded"
typedef struct {
  usb_class_header_t header;
  uint8_t functional_descriptors_count;
  usb_cdc_functional_descriptor_header_t** functional_descriptors;
} usb_device_cdc_t;

usb_class_header_t* usb_device_cdc_init(usb_device_t *device,
                uint16_t id_vendor, uint16_t id_product,
                char *vendor_name, char *product_name,
                uint16_t serial_number, char* serial_name);
  cdc_receive_callback_t callback;

task_result_t usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd);
  // Internal state.
  queue_t* rx_buffer;

task_result_t usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd);
void usb_device_cdc_enumeration_done_callback(usb_device_t* device);
void usb_device_cdc_txfifo_empty_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_rxfifo_empty_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_transmit_done_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nak_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nyet_callback(usb_device_t* device, uint8_t endpoint);
  cdc_tx_state_t tx_state;

  uint8_t got_setup;
} usb_device_cdc_t;

/**
 * @brief Configures cdc acm descriptors


@@ 122,10 132,18 @@ void usb_device_cdc_nyet_callback(usb_device_t* device, uint8_t endpoint);
 * That means that after call to this function, usb_device_setup can be safely
 * called, and all setup commands handled.
 * @param[in,out] device The usb device.
 * @param[in] rx_queue_size The size, in bytes, of the rx queue. When the queue is full, data cannot be received. There should not be any data lost, but it's not a 100% guarantee for now.
 */
void usb_device_cdc_acm_configure(usb_device_t *device);
void cdc_acm_configure(usb_device_t *device, uint16_t rx_queue_size);

void cdc_data_set_receive_callback(usb_device_t *device,
                                   cdc_receive_callback_t callback);

int32_t cdc_data_send_blocking(usb_device_t *device, uint8_t *data, uint16_t size);
int32_t cdc_data_send(usb_device_t* device, uint8_t* data, uint16_t size);

uint16_t cdc_data_receive(usb_device_t* device, uint8_t* buffer, uint16_t max_size);

bool cdc_got_params(usb_device_t* device);

#endif // USB_DEVICE_CDC_H

M src/delay.c => src/delay.c +9 -1
@@ 1,7 1,15 @@
#include "delay.h"
#include <stm32h7xx.h>

uint32_t us_ticks;

void systick_configure() {
  SysTick->LOAD = SYSTICK_LOAD;
  SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
  SysTick->VAL = 0;
  SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; //| SysTick_CTRL_TICKINT_Msk;
  us_ticks = 0;
}

void systick_handler() {
  us_ticks++;
}

M src/main.c => src/main.c +17 -2
@@ 164,10 164,25 @@ void main()
  void *usb_dev = usb_device_init(USB_OTG_HS1, &USB_CLASS_CDC_ACM,
                                  0x1234, 0x1111, NULL,
                                  NULL, 1, NULL);
  usb_device_cdc_acm_configure(usb_dev);
  cdc_acm_configure(usb_dev, 512);
  usb_device_setup(usb_dev);

  usb_device_wait_for_handshake(usb_dev);

  while(1) {}
  uint8_t data[64];
  while (1) {
    uint16_t received = cdc_data_receive(usb_dev, data, 64);

    for (uint16_t i = 0; i < received; i++) {
      data[i] = data[i] + 1;
    }

    if (received > 0) {
      cdc_data_send_blocking(usb_dev, data, received);
    }

    if (pin_read(&wkup)) {
      cdc_data_send_blocking(usb_dev, (uint8_t*)"Hello world!\r\n", 0);
    }
  }
}

M src/pin.c => src/pin.c +10 -5
@@ 1,5 1,6 @@
#include "pin.h"
#include "registers.h"
#include "stm32h747xx.h"
#include <stdlib.h>

void pin_init(pin_t* pin, GPIO_TypeDef *gpio, uint8_t pin_index) {


@@ 18,20 19,24 @@ void pin_speed(pin_t *pin, pin_speedmode_t mode) {
}

uint8_t pin_read(pin_t *pin) {
  reg_read_bits_pos(&pin->gpio->IDR, pin->pin, 1);
  return reg_read_bits_pos(&pin->gpio->IDR, pin->pin, 1);
}

uint8_t pin_write(pin_t *pin, uint8_t val) {
  reg_write_bits_pos(&pin->gpio->ODR, val, pin->pin, 1);
void pin_write(pin_t *pin, uint8_t val) {
  if (val) {
    pin_set(pin);
  } else {
    pin_reset(pin);
  }
}
void pin_toggle(pin_t *pin) {
  reg_toggle_bits_pos(&pin->gpio->ODR, pin->pin, 1);
}
void pin_set(pin_t *pin) {
  pin->gpio->ODR = 1 << pin->pin;
  pin->gpio->BSRR = 1 << pin->pin;
}
void pin_reset(pin_t *pin) {
  pin->gpio->ODR = 1 << (pin->pin + 15);
  pin->gpio->BSRR = 1 << (pin->pin + GPIO_BSRR_BR0_Pos);
}

void pin_into_output(pin_t *pin) {

M src/queue.c => src/queue.c +4 -0
@@ 77,3 77,7 @@ void *queue_peek(queue_t *queue) {
uint16_t queue_count(queue_t *queue) {
  return queue->length - queue->space;
}

uint16_t queue_space(queue_t *queue) {
  return queue->space;
}

M src/usb.c => src/usb.c +13 -7
@@ 106,8 106,12 @@ task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint,
    return RES_WOULD_BLOCK;
  }

  if (!usb_check_fifo_space(endpoint, size)) {
    return RES_WOULD_BLOCK;
  }

  if (size > 0) {
    endpoint->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_MULCNT_Pos) | (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
    endpoint->DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
  } else { // just one packet, of zero length
    endpoint->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos);
  }


@@ 125,10 129,12 @@ task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
    return result;
  }

  usb_generic_fill_fifo(endpoint, data, size, fifo_tx_target);
  if (size > 0) {
    usb_generic_fill_fifo(endpoint, data, size, fifo_tx_target);
    // After the fifo is filled...
    /* endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK; */
  }

  // After the fifo is filled...
  endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
  return RES_OK;
}



@@ 145,7 151,7 @@ task_result_t usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *

  // TODO: check where to put it... beginning, end...
  if (subWordBytes > 0) {
    rx_data.word = 0;
    rx_data.word = *fifo_rx_source;
    for (uint8_t i = 0; i < subWordBytes; i++) {
      *(data + i) = rx_data.bytes[i];
    }


@@ 227,7 233,7 @@ task_result_t usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoin
    usb_generic_fill_fifo(endpoint, (uint8_t*)&string_descriptor->wLANGID[1], string_descriptor->header.bLength - 4, fifo_tx_target);
  }

  endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
  /* endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK; */

  return RES_OK;
}


@@ 260,7 266,7 @@ task_result_t usb_send_unicode_string_descriptor(
    usb_generic_fill_fifo(endpoint, &string_descriptor->bString[2], string_descriptor->header.bLength - 4, fifo_tx_target);
  }

  endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
  /* endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK; */
  return RES_OK;
}


M src/usb_device.c => src/usb_device.c +245 -172
@@ 28,6 28,9 @@ void* usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class,
  device->fifos = (usb_fifo_t*)(((uint8_t*)device->core) + USB_OTG_FIFO_BASE);

  device->setup.received_setup_commands = queue_malloc(sizeof(usb_setup_command_t), MAX_SETUP_PACKETS);
  device->setup.rcvd_awaiting = 0;
  device->setup.rcvd_count = 0;
  device->setup.rcvd_data = NULL;
  queue_init(device->setup.received_setup_commands, sizeof(usb_setup_command_t), MAX_SETUP_PACKETS);
  device->vt = *class;



@@ 79,7 82,7 @@ void usb_device_setup(void* device_ptr) {
  usb_device_t* device = (usb_device_t*)device_ptr;

  device->device->DCTL |= USB_OTG_DCTL_SDIS;
  /* DELAY_US(3); */
  DELAY_US(3);

  PWR->CR3 |= PWR_CR3_USB33DEN;
  while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0);


@@ 89,7 92,7 @@ void usb_device_setup(void* device_ptr) {

  device->core->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD;

  /* DELAY_US(3); */
  DELAY_US(3);
  usb_device_reset(device_ptr);
  /* DELAY_US(3); */



@@ 248,16 251,23 @@ task_result_t usb_handle_setup_command(usb_device_t *device, usb_setup_command_t
    break;
  case USB_SETUP_SET_CONFIGURATION: {
    if (cmd->wValue == 0) {
      device->state = SET_ADDRESS_RCVD;
      usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
      device->vt.reset_endpoints(device);

      result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);

      // TODO disable endpoints
      if (result == RES_OK) {
        device->state = SET_ADDRESS_RCVD;
      }
    } else if (cmd->wValue == device->class->configuration_descriptor.bConfigurationValue) {
      device->state = ENUMERATED;
      usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
      device->setup.stage = SETUP_STAGE_SENDING_ACK;

      // TODO setup endpoints
      device->vt.setup_endpoints(device, cmd->wValue);

      result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);

      if (result == RES_OK) {
        device->setup.stage = SETUP_STAGE_SENDING_ACK;
        device->state = ENUMERATED;
      }
    } else {
      reg_set_bits(&device->in[0].DIEPCTL, USB_OTG_DIEPCTL_STALL);
    }


@@ 322,7 332,7 @@ typedef enum {
  USB_GOT_SETUP,
  USB_PROCESS_SETUP,
  USB_GOT_ACK,
  USB_GOT_RESPONSE,
  USB_GOT_DATA,
  USB_SENT_RESPONSE,
} usb_event_t;



@@ 334,6 344,22 @@ task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) {
    }

    usb_setup_command_t* command = queue_peek(device->setup.received_setup_commands);

    if (command == NULL) {
      device->setup.stage = SETUP_STAGE_NONE;
      break;
    }

    // pre-processing of data commands - first get the data, only then proceed with handle_setup_command
    if (command->bmRequestType.direction == USB_SETUP_HOST_TO_DEVICE && command->wLength > 0 && device->setup.rcvd_awaiting == 0) {
      device->setup.rcvd_awaiting = command->wLength;
      device->setup.rcvd_count = 0;
      device->setup.rcvd_data = (uint8_t*)malloc(device->setup.rcvd_count);

      device->setup.stage = SETUP_STAGE_AWAITING_DATA;
      break;
    }

    task_result_t result = usb_handle_setup_command(device, command);
    if (result == RES_OK) {
      queue_dequeue(device->setup.received_setup_commands);


@@ 341,6 367,14 @@ task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) {
      if (setup_packets_count == 0) {
        reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
      }

      if (device->setup.rcvd_data != NULL) {
        free(device->setup.rcvd_data);
        device->setup.rcvd_data = NULL;
      }

      device->setup.rcvd_awaiting = 0;
      device->setup.rcvd_count = 0;
    }
    return result;
  }


@@ 354,9 388,12 @@ task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) {
      }
    }
    break;
  case SETUP_STAGE_AWAITING_RESPONSE:
    // NOTE: Currently not really supported.
    return RES_ERROR;
  case SETUP_STAGE_AWAITING_DATA:
    if (event == USB_GOT_DATA) {
      if (device->setup.rcvd_count >= device->setup.rcvd_awaiting) {
        device->setup.stage = SETUP_STAGE_RCVD_SETUP_PACKET;
      }
    }
    break;
  case SETUP_STAGE_SENDING_RESPONSE:
    if (event == USB_SENT_RESPONSE) {


@@ 388,29 425,36 @@ void usb_handle_rxflvl_control_int(usb_device_t *device,
  volatile uint32_t *fifo = device->fifos[0].data;

  if (packet_info->packet_status == PACKET_SETUP_TRANSACTION_COMPLETED) {
    // Nothing do to.
    dummy = *fifo;
    usb_handle_setup(device, USB_PROCESS_SETUP);
    if (packet_info->byte_count > 0) {
      usb_generic_read(data, packet_info->byte_count, fifo);
    }
  } else if (packet_info->packet_status == PACKET_SETUP_DATA_PACKET_RECEIVED) {
    // SAVE data
    usb_setup_command_t command;
    usb_generic_read((uint8_t*)&command, 8, fifo);
    usb_handle_setup(device, USB_GOT_SETUP);

    if (command.wLength > 60000) {
      dummy = 0;
    }

    if (!queue_enqueue(device->setup.received_setup_commands, &command)) {
      // Got a problem, setup command lost!
      device->setup.detected_setup_errors++;
    }

    dummy = *fifo; // the last that will trigger another interrupt
    usb_handle_setup(device, USB_GOT_SETUP);
  } else if (packet_info->byte_count != 0) {
    usb_generic_read(data, packet_info->byte_count, fifo);
    if (device->setup.stage == SETUP_STAGE_AWAITING_DATA && device->setup.rcvd_data != NULL) {
      // TODO: check if still enough space
      usb_generic_read(&device->setup.rcvd_data[device->setup.rcvd_count],
                       packet_info->byte_count, fifo);
      device->setup.rcvd_count += packet_info->byte_count;
    }

    if (device->setup.stage == SETUP_STAGE_AWAITING_ACK) {
      // This is an error, since there is data in status phase...
      // TODO: How to handle?
      device->setup.detected_setup_errors++;
      usb_handle_setup(device, USB_GOT_RESPONSE);
    }
  }
}


@@ 440,10 484,6 @@ void usb_handle_rxflvl_int(usb_device_t *device) {
    }
  }

  device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA;

  // Read info about the endpoint etc.

  // Re-enable the interrupt
  reg_set_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_RXFLVLM);
}


@@ 458,77 498,80 @@ uint8_t usb_daint_get_endpoint_number(uint32_t endpoints) {
}

void usb_handle_endpoint_in_int(usb_device_t* device) {
  uint8_t ep_id = usb_daint_get_endpoint_number(reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_IEPINT_Pos, 0xFFFF));
  uint32_t daint = reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_IEPINT_Pos, 0xFFFF);
  uint8_t ep_id = 0;
  while (ep_id != 0xFF) {
    ep_id = usb_daint_get_endpoint_number(daint);
    daint &= ~(1 << ep_id);

  if (ep_id == 0xFF) {
    device->state = USB_DEV_ERROR;
    return;
  }
    if (ep_id == 0xFF) {
      return;
    }

  uint32_t interrupt_reg = device->in[ep_id].DIEPINT;
    uint32_t interrupt_reg = device->in[ep_id].DIEPINT;

  if (interrupt_reg & USB_OTG_DIEPINT_PKTDRPSTS) {
    // Not generated by interrupt! So if we get this one
    // here, it's not from the interrupt. Just clear it,
    // isochronous pipes can drop data from time to time
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_PKTDRPSTS);
  }
    if (interrupt_reg & USB_OTG_DIEPINT_PKTDRPSTS) {
      // Not generated by interrupt! So if we get this one
      // here, it's not from the interrupt. Just clear it,
      // isochronous pipes can drop data from time to time
      reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_PKTDRPSTS);
    }

  if (interrupt_reg & USB_OTG_DIEPINT_NAK) {
    // NOTE no need to do much. Hardware will resend
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_NAK);
    device->in[ep_id].DIEPCTL = USB_OTG_DIEPCTL_CNAK;
  }
  if (interrupt_reg & USB_OTG_DIEPINT_BNA) {
    // NOTE this should not be reached as DMA is not used
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_BNA);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_TXFIFOUDRN) {
    // NOTE this should not be reached as thresholding is not used
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_TXFIFOUDRN);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_TXFE) {
    if (device->state == ENUMERATED) {
      device->vt.txfifo_empty_callback(device, ep_id);
    if (interrupt_reg & USB_OTG_DIEPINT_NAK) {
      // NOTE no need to do much. Hardware will resend
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_NAK;
    }
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_TXFE);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_INEPNE) {
    // NAK effective. Okay, ack, go on.
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_INEPNE);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_INEPNM) {
    device->state = USB_DEV_ERROR; // data on top of TxFIFO belong to endpoint other
    // than the one for which in token was received.
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_INEPNM);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_ITTXFE) {
    // In token when no data. How to proceed? TODO
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_ITTXFE);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_TOC) {
    // Timeout condition. Skip? TODO how to proceed?
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_TOC);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_AHBERR) {
    // NOTE this should not be reached as thresholding is not used
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_AHBERR);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_EPDISD) {
    // Endpoint is disabled, per application's request. Okay.
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_EPDISD);
  }
  if (interrupt_reg & USB_OTG_DIEPINT_XFRC) {
    // Transfer is completed.
    if (device->state == ENUMERATED) {
      device->vt.rx_done_callback(device, ep_id);
    if (interrupt_reg & USB_OTG_DIEPINT_BNA) {
      // NOTE this should not be reached as DMA is not used
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_BNA;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_TXFIFOUDRN) {
      // NOTE this should not be reached as thresholding is not used
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TXFIFOUDRN;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_TXFE) {
      if (device->state == ENUMERATED) {
        device->vt.txfifo_empty_callback(device, ep_id);
      }
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TXFE;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_INEPNE) {
      // NAK effective. Okay, ack, go on.
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_INEPNE;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_INEPNM) {
      device->state = USB_DEV_ERROR; // data on top of TxFIFO belong to endpoint other
      // than the one for which in token was received.
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_INEPNM;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_ITTXFE) {
      // In token when no data. How to proceed? TODO
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_ITTXFE;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_TOC) {
      // Timeout condition. Skip? TODO how to proceed?
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_TOC;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_AHBERR) {
      // NOTE this should not be reached as thresholding is not used
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_AHBERR;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_EPDISD) {
      // Endpoint is disabled, per application's request. Okay.
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_EPDISD;
    }
    if (interrupt_reg & USB_OTG_DIEPINT_XFRC) {
      // Transfer is completed.
      if (device->state == ENUMERATED) {
        device->vt.rx_done_callback(device, ep_id);
      }

    if (ep_id == 0) {
      usb_handle_setup(device, USB_GOT_RESPONSE);
      usb_handle_setup(device, USB_PROCESS_SETUP);
      if (ep_id == 0) {
        usb_handle_setup(device, USB_SENT_RESPONSE);
        usb_handle_setup(device, USB_PROCESS_SETUP);
      }
      device->in[ep_id].DIEPINT = USB_OTG_DIEPINT_XFRC;
    }
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_XFRC);
  }
}



@@ 537,84 580,97 @@ void usb_handle_endpoint_in_int(usb_device_t* device) {
#define USB_OTG_DOEPINT_BNA                  USB_OTG_DOEPINT_B2BSTUP_Msk   /*!< Back-to-back SETUP packets received */

void usb_handle_endpoint_out_int(usb_device_t* device) {
  /* device->core->GRXSTSP; */
  /* device->core->GRXFSIZ; */

  uint8_t ep_id = usb_daint_get_endpoint_number(reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_OEPINT_Pos, 0xFFFF));

  if (ep_id == 0xFF) {
    device->state = USB_DEV_ERROR;
    return;
  }
  uint32_t daint = reg_read_bits_pos(&device->device->DAINT, USB_OTG_DAINT_OEPINT_Pos, 0xFFFF);
  uint8_t ep_id = 0;
  while (ep_id != 0xFF) {
    ep_id = usb_daint_get_endpoint_number(daint);
    daint &= ~(1 << ep_id);

    if (ep_id == 0xFF) {
      return;
    }

    uint32_t interrupt_reg = device->out[ep_id].DOEPINT;

    if (interrupt_reg & USB_OTG_DOEPINT_STPKTRX) {
      // NOTE This shouldn't be reached since DMA is not used
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_STPKTRX;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_NYET) {
      // We don't really care about this one for now
      device->vt.nyet_callback(device, ep_id);
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_NYET;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_NAK) {
      // We don't really care about this one for now
      device->vt.nak_callback(device, ep_id);
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_NAK;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_BERR) {
      // Uh? Babble much?
      device->state = CONTROL_ERROR;
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_BERR;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_BNA) {
      // NOTE This shoudln't be reached since DMA is not used
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_BNA;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_OUTPKTERR) {
      // NOTE thresholding not enabled, so this shouldn't be reached
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OUTPKTERR;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_B2BSTUP) {
      // TODO: this is a problem! we couldn't capture all the packets!
      device->state = CONTROL_ERROR;
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_B2BSTUP;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_OTEPSPR) {
      // TODO: is there someting to do? The data sending is usually handled prior
      //       to this, in handling of setup / data stages. Maybe it should be
      //       moved here instead, since it's possible other packet will be
      //       received in the meantime?
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OTEPSPR;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_OTEPDIS) {
      // NOTE: Can we handle this? a callback to application?
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_OTEPDIS;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_STUP) {
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_STUP;

      if (ep_id == 0) {
        usb_handle_setup(device, USB_PROCESS_SETUP);
        device->out[ep_id].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
      }
    }
    if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) {
      // NOTE This shoudln't be reached since DMA is not used
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_AHBERR;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_EPDISD) {
      // NOTE endpoint has been disabled, as was instructed. So this shoudln't
      // need handling?
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_EPDISD;
    }
    if (interrupt_reg & USB_OTG_DOEPINT_XFRC) {
      device->out[ep_id].DOEPINT = USB_OTG_DOEPINT_XFRC;

  uint32_t interrupt_reg = device->out[ep_id].DOEPINT;
      if (device->state == ENUMERATED) {
        device->vt.rx_done_callback(device, ep_id);
      }

  if (interrupt_reg & USB_OTG_DOEPINT_STPKTRX) {
    // NOTE This shouldn't be reached since DMA is not used
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_STPKTRX);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_NYET) {
    // We don't really care about this one for now
    device->vt.nyet_callback(device, ep_id);
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_NYET);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_NAK) {
    // We don't really care about this one for now
    device->vt.nak_callback(device, ep_id);
    device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA;
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_NAK);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_BERR) {
    // Uh? Babble much?
    device->state = CONTROL_ERROR;
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_BERR);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_BNA) {
    // NOTE This shoudln't be reached since DMA is not used
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_BNA);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_OUTPKTERR) {
    // NOTE thresholding not enabled, so this shouldn't be reached
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OUTPKTERR);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_B2BSTUP) {
    // TODO: this is a problem! we couldn't capture all the packets!
    device->state = CONTROL_ERROR;
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_B2BSTUP);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_OTEPSPR) {
    // TODO: is there someting to do? The data sending is usually handled prior
    //       to this, in handling of setup / data stages. Maybe it should be
    //       moved here instead, since it's possible other packet will be
    //       received in the meantime?
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OTEPSPR);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_OTEPDIS) {
    // NOTE: Can we handle this? a callback to application?
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_OTEPDIS);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_STUP) {
    usb_handle_setup(device, USB_PROCESS_SETUP);
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_STUP);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) {
    // NOTE This shoudln't be reached since DMA is not used
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_AHBERR);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_EPDISD) {
    // NOTE endpoint has been disabled, as was instructed. So this shoudln't
    // need handling?
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_EPDISD);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_XFRC) {
    device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA;
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_XFRC);
      if (ep_id == 0) {
        if (device->setup.rcvd_count > 0) {
          usb_handle_setup(device, USB_GOT_DATA);
        } else {
          usb_handle_setup(device, USB_GOT_ACK);
        }

    device->vt.rx_done_callback(device, ep_id);
        // Process next packet / data
        usb_handle_setup(device, USB_PROCESS_SETUP);

    if (ep_id == 0) {
      usb_handle_setup(device, USB_GOT_ACK);
      usb_handle_setup(device, USB_PROCESS_SETUP);
        device->out[ep_id].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
      }
    }
  }
}


@@ 625,15 681,25 @@ void otg_hs_handler(void) {
  usb_device_t* device = &usb_devices[USB_OTG_HS1];
  uint8_t handled = 0;

  // Start of frame
  if (device->core->GINTSTS & USB_OTG_GINTSTS_SOF) {
    device->core->GINTSTS = USB_OTG_GINTSTS_SOF;
    handled = 1;

    /* usb_handle_setup(device, USB_PROCESS_SETUP); */
  }

  // Reset detected
  if (device->core->GINTSTS & USB_OTG_GINTSTS_USBRST) {
    // clear it
    device->core->GINTSTS = USB_OTG_GINTSTS_USBRST;

    device->setup.stage = SETUP_STAGE_NONE;
    while (queue_dequeue(device->setup.received_setup_commands) != NULL);

    reg_set_bits(&device->core->GINTMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT);
    reg_set_bits(&device->device->DOEPMSK, USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT);

    // TODO: should this be done for ep 0 as well?
    for (int ep = 0; ep < 8; ep++) {
      USB_OTG_OUTEndpointTypeDef *out = device->out + ep;
      out->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;


@@ 644,8 710,8 @@ void otg_hs_handler(void) {
    device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_IEPM_Pos;
    device->device->DAINTMSK |= 1 << USB_OTG_DAINTMSK_OEPM_Pos;

    device->device->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_NAKM;
    device->device->DIEPMSK |= USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPEACHMSK1_NAKM | USB_OTG_DIEPEACHMSK1_INEPNEM;
    device->device->DOEPMSK |= USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM;
    device->device->DIEPMSK |= USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM;

    // 512 bytes
    device->core->GRXFSIZ = 1024 / 4;


@@ 695,26 761,33 @@ void otg_hs_handler(void) {
  // Setup, data...
  if (device->core->GINTSTS & USB_OTG_GINTSTS_OEPINT) {
    usb_handle_endpoint_out_int(device);
    return;
    handled = 1;
  }

  if (device->core->GINTSTS & USB_OTG_GINTSTS_IEPINT) {
    usb_handle_endpoint_in_int(device);
    return;
    handled = 1;
  }

  if (device->core->GINTSTS & (USB_OTG_GINTSTS_RXFLVL)) {
    usb_handle_rxflvl_int(device);
    return;
    handled = 1;
  }

  // Start of frame
  if (device->core->GINTSTS & USB_OTG_GINTSTS_SOF) {
    device->core->GINTSTS = USB_OTG_GINTSTS_SOF;
  if (device->core->GINTSTS & (USB_OTG_GINTSTS_RSTDET)) {
    // Soft reset, meaning the usb is likely not connected
    device->core->GINTSTS = USB_OTG_GINTSTS_RSTDET;
    device->setup.stage = SETUP_STAGE_NONE;
    while (queue_dequeue(device->setup.received_setup_commands) != NULL);
    handled = 1;
  }

    usb_handle_setup(device, USB_PROCESS_SETUP);
    return;
  if (device->core->GINTSTS & (USB_OTG_GINTSTS_ESUSP)) {
    // The usb has probably been disconnected
    device->core->GINTSTS = USB_OTG_GINTSTS_ESUSP;
    device->setup.stage = SETUP_STAGE_NONE;
    while (queue_dequeue(device->setup.received_setup_commands) != NULL);
    handled = 1;
  }

  if (handled == 0) {

M src/usb_device_cdc.c => src/usb_device_cdc.c +328 -18
@@ 1,10 1,34 @@
#include "generic.h"
#include "stm32h747xx.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_cdc.h"
#include "registers.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

usb_class_header_t* usb_device_cdc_init(usb_device_t *device,
                uint16_t id_vendor, uint16_t id_product,
                char *vendor_name, char *product_name,
                uint16_t serial_number, char* serial_name);

task_result_t usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd);
void usb_device_cdc_setup_endpoints(usb_device_t* device, uint8_t configuration);
void usb_device_cdc_reset_endpoints(usb_device_t* device);
void usb_device_cdc_reset_callback(usb_device_t* device);
task_result_t usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd);
void usb_device_cdc_enumeration_done_callback(usb_device_t* device);
void usb_device_cdc_txfifo_empty_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_rxfifo_full_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_rx_done_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_tx_done_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nak_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nyet_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_received_data_callback(usb_device_t* device, packet_info_t* packet);

#define MAX_PACKET_SIZE 512

uint16_t usb_cdc_lang_descriptors[] = {
  USB_LANG_ENGLISH | (USB_SUBLANG_ENGLISH_US << 10)
};


@@ 12,13 36,19 @@ uint16_t usb_cdc_lang_descriptors[] = {
usb_class_vtable_t USB_CLASS_CDC_ACM = {
  .init = usb_device_cdc_init,
  .send_configuration = usb_device_cdc_send_configuration,
  .setup_endpoints = usb_device_cdc_setup_endpoints,
  .reset_endpoints = usb_device_cdc_reset_endpoints,
  .reset_callback = usb_device_cdc_reset_callback,

  .setup_packet_callback = usb_device_cdc_setup_packet_callback,
  .enumeration_done_callback = usb_device_cdc_enumeration_done_callback,
  .txfifo_empty_callback = usb_device_cdc_txfifo_empty_callback,
  .rxfifo_empty_callback = usb_device_cdc_rxfifo_empty_callback,
  .transmit_done_callback = usb_device_cdc_transmit_done_callback,
  .rxfifo_full_callback = usb_device_cdc_rxfifo_full_callback,
  .rx_done_callback = usb_device_cdc_rx_done_callback,
  .tx_done_callback = usb_device_cdc_tx_done_callback,
  .nak_callback = usb_device_cdc_nak_callback,
  .nyet_callback = usb_device_cdc_nyet_callback,
  .received_data_callback = usb_device_cdc_received_data_callback,
};

usb_class_header_t *usb_device_cdc_init(usb_device_t *device, uint16_t id_vendor,


@@ 77,14 107,18 @@ usb_class_header_t *usb_device_cdc_init(usb_device_t *device, uint16_t id_vendor
  return class;
}

void usb_device_cdc_acm_configure(usb_device_t* device) {
void cdc_acm_configure(usb_device_t* device, uint16_t rx_queue_size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  usb_class_header_t* header = &cdc->header;

  queue_t* rx_queue = queue_malloc(1, rx_queue_size);
  cdc->rx_buffer = rx_queue;
  queue_init(rx_queue, 1, rx_queue_size);

  usb_device_descriptor_t device_descriptor =
    {
      .header = { .bDescriptorType = DESCRIPTOR_DEVICE, .bLength = sizeof(usb_device_descriptor_t) },
      .bcdUSB = 0x20,
      .bcdUSB = 0x200,
      .bDeviceClass = USB_CLASS_CDC_CODE,
      .bDeviceSubClass = 0x00,
      .bDeviceProtocol = 0x00,


@@ 174,28 208,28 @@ void usb_device_cdc_acm_configure(usb_device_t* device) {
       .usage_type = USB_ENDPOINT_USAGE_DATA,
       .transfer_type = USB_ENDPOINT_TYPE_BULK,
     },
     .wMaxPacketSize = 64,
     .wMaxPacketSize = MAX_PACKET_SIZE,
    },
    {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT,
                .bLength = sizeof(usb_endpoint_descriptor_t)},
     .bEndpointAddress = { .endpoint_number = 1, .direction = USB_ENDPOINT_OUT, .reserved = 0 },
     .bInterval = 16,
     .bEndpointAddress = { .endpoint_number = 2, .direction = USB_ENDPOINT_OUT, .reserved = 0 },
     .bInterval = 1,
     .bmAttributes = {
       .reserved_zeros = 0,
       .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC,
       .usage_type = USB_ENDPOINT_USAGE_DATA,
       .transfer_type = USB_ENDPOINT_TYPE_BULK,
     },
     .wMaxPacketSize = 64,
     .wMaxPacketSize = MAX_PACKET_SIZE,
    },
  };
  usb_interface_t data_interface = {
    .interface_descriptor = {
      .header = {.bDescriptorType = DESCRIPTOR_INTERFACE,
                 .bLength = sizeof(usb_interface_descriptor_t)},
      .bInterfaceNumber = 2,
      .bInterfaceNumber = 1,
      .bAlternateSetting = 0,
      .bNumEndpoints = 1,
      .bNumEndpoints = 2,
      .bInterfaceClass = USB_CLASS_DATA_CODE,
      .bInterfaceSubClass = 0x00,
      .bInterfaceProtocol = 0,


@@ 328,33 362,309 @@ task_result_t usb_device_cdc_send_configuration(usb_device_t *device,
  }

  // After the fifo is filled...
  enp0->DIEPCTL = USB_OTG_DIEPCTL_CNAK;
  return result;
}

void usb_device_cdc_setup_endpoints(usb_device_t *device,
                                    uint8_t configuration) {
  // NOTE: currently it is assumed one notification endpoint (1),
  //       and data endpoint (2). This is valid for acm, if other
  //       are implemented in the future, this will have to be changed

  // FIFOs sizes
  uint16_t fifo_zero_pos = 0;
  uint16_t fifo_zero_size = reg_read_bits_pos(&device->core->DIEPTXF0_HNPTXFSIZ, USB_OTG_DIEPTXF_INEPTXFD_Pos, 0xFFFF);
  uint16_t curr_pos = fifo_zero_pos + fifo_zero_size;
  uint16_t sizes = 512 / 4;
  device->core->DIEPTXF[1] = (curr_pos << USB_OTG_DIEPTXF_INEPTXSA_Pos) | (sizes << USB_OTG_DIEPTXF_INEPTXFD_Pos);
  curr_pos += sizes;
  device->core->DIEPTXF[2] = (curr_pos << USB_OTG_DIEPTXF_INEPTXSA_Pos) | (sizes << USB_OTG_DIEPTXF_INEPTXFD_Pos);

  // Enable endpoint 1 IN
  //   it's the notification endpoint
  // Configure the endpoint (size, fifo, type)
  reg_write_bits_pos(&device->in[1].DIEPCTL, MAX_PACKET_SIZE, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL);
  reg_write_bits_pos(&device->in[1].DIEPCTL, 1, USB_OTG_DIEPCTL_TXFNUM_Pos, 0xF);
  reg_write_bits_pos(&device->in[1].DIEPCTL, 3, USB_OTG_DIEPCTL_EPTYP_Pos, 0x3);
  // Enable interrupts
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_IEPM_Pos + 1);
  // The in endpoint should be enabled only if there is something to send,
  // so not enablling.
  reg_set_bits_pos(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Pos, 1);

  // Enable endpoint 2 IN
  // Enable endpoint 2 OUT
  //   Those are the data endpoints, used for transferring
  //   serial data, directly, no protocol is utilized.
  // Configure the endpoint (size, fifo, type)
  reg_write_bits_pos(&device->in[2].DIEPCTL, MAX_PACKET_SIZE, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL);
  reg_write_bits_pos(&device->in[2].DIEPCTL, 2, USB_OTG_DIEPCTL_TXFNUM_Pos, 0xF);
  reg_write_bits_pos(&device->in[2].DIEPCTL, 2, USB_OTG_DIEPCTL_EPTYP_Pos, 0x3);
  reg_write_bits_pos(&device->out[2].DOEPCTL, 2, USB_OTG_DOEPCTL_EPTYP_Pos, 0x3);
  reg_write_bits_pos(&device->out[2].DOEPCTL, MAX_PACKET_SIZE, USB_OTG_DOEPCTL_MPSIZ_Pos, 0x7FFUL);
  /* reg_write_bits_pos(&device->out[2].DOEPTSIZ, MAX_PACKET_SIZE, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF); */
  reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF);
  reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_PKTCNT_Pos, 0x3FF);
  // Enable interrupts
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_IEPM_Pos + 2);
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_OEPM_Pos + 2);
  // Enable the out endpoint
  reg_set_bits_pos(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Pos, 1);
  reg_set_bits_pos(&device->out[2].DOEPCTL, USB_OTG_DOEPCTL_USBAEP_Pos, 1);
  device->out[2].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
}

void usb_device_cdc_reset_endpoints(usb_device_t *device) {
  reg_set_bits(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_EPDIS_Msk);
  reg_set_bits(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_EPDIS_Msk);
  reg_set_bits(&device->out[2].DOEPCTL, USB_OTG_DOEPCTL_EPDIS_Msk);
  reg_clear_bits(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);
  reg_clear_bits(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);
  reg_clear_bits(&device->out[2].DOEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);

  reg_clear_bits(&device->device->DAINTMSK,
                 (USB_OTG_DAINTMSK_IEPM_Msk << 1) |
                 (USB_OTG_DAINTMSK_IEPM_Msk << 2) |
                 (USB_OTG_DAINTMSK_OEPM_Msk << 2));

  usb_flush_tx_fifo(device->core, 1);
  usb_flush_tx_fifo(device->core, 2);
}

void usb_device_cdc_reset_callback(usb_device_t *device) {

  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  while (queue_dequeue(cdc->rx_buffer) != NULL);

  // NOTE: endpoint deinitialization and such is handled
  // by the usb device itself.
}

task_result_t usb_device_cdc_setup_packet_callback(usb_device_t *device,
                                                   usb_setup_command_t *cmd) {
  // TODO - is there something to do? maybe just the multiplexed commands?
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  cdc->got_setup = 1;
  reg_set_bits(&device->out[0].DOEPCTL, USB_OTG_DOEPCTL_STALL);
  reg_set_bits(&device->in[0].DIEPCTL, USB_OTG_DIEPCTL_STALL);
  return RES_OK;
  /* // TODO: only for some commands. For now, we just ignore them. */
  /* if (cmd->bRequest == 34) { */
  /*   task_result_t result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); */
  /*   if (result == RES_OK) { */
  /*     device->setup.stage = SETUP_STAGE_SENDING_ACK; */
  /*   } */
  /*   return result; */
  /* } */

  /* return RES_OK; */
}

void usb_device_cdc_enumeration_done_callback(usb_device_t *device) {
  // TODO start the application somehow
  // Now receiving send, receive calls.
  // Nothing to do.
}

typedef enum {
  CDC_TX_FIFO_EMPTY,
  CDC_RX_FIFO_FULL,
  CDC_RX_DONE,
  CDC_TX_DONE,
  CDC_RX_DATA,
  CDC_TX_DATA,

  CDC_RX_REQ,
  CDC_TX_REQ,
} cdc_event_t;

#define DATA_ENDPOINT 2

task_result_t cdc_transmit(usb_device_t *device, uint8_t* data, uint16_t* size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;

  USB_OTG_INEndpointTypeDef* enp = &device->in[DATA_ENDPOINT];
  volatile uint32_t* fifo = device->fifos[DATA_ENDPOINT].data;

  uint32_t fifo_size = ((enp->DTXFSTS & (USB_OTG_DTXFSTS_INEPTFSAV_Msk)) >> USB_OTG_DTXFSTS_INEPTFSAV_Pos);

  if (fifo_size * 4 < *size) {
    *size = fifo_size * 4;
  }

  task_result_t result = usb_generic_setup_in_endpoint(enp, *size, MAX_PACKET_SIZE);

  if (result != RES_OK) {
    return result;
  }

  cdc->tx_state = CDC_TX_STATE_SENDING;

  usb_generic_fill_fifo(enp, data, *size, fifo);

  return RES_OK;
}

void cdc_process_tx(usb_device_t *device, cdc_event_t event) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  switch (event) {
  case CDC_TX_REQ:
    break;
  case CDC_TX_DONE:
  case CDC_TX_FIFO_EMPTY:
    // Try reading from the queue. If size is 0,
    // notify application?
    break;
  default:
    // Should not happen.
    break;
  }
}

void cdc_process_rx(usb_device_t *device, cdc_event_t event) {
  /* usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class; */
  /* queue_t* queue = cdc->rx_buffer; */
  /* switch (event) { */
  /* case CDC_RX_REQ: */
  /*   // TODO: read from peripheral fifo to queue */
  /*   if (queue_space(queue) > 0 && cdc->pending_rx_bytes > 0) { */
  /*     cdc_read(device); */
  /*   } */
  /*   break; */
  /* case CDC_RX_DONE: */
  /* case CDC_RX_FIFO_FULL: */
  /*   // Try reading from peripheral to the queue, */
  /*   // if there is space. Check for space first, */
  /*   // when data are read, we don't have where to */
  /*   // store them! */

  /*   if (queue_space(queue) > 0) { */
  /*     cdc_read(device); */
  /*   } */
  /*   break; */
  /*   break; */
  /* default: */
  /*   // Should not happen. */
  /*   break; */
  /* } */
}

void usb_device_cdc_txfifo_empty_callback(usb_device_t *device,
                                          uint8_t endpoint) {
// TODO the application
  cdc_process_tx(device, CDC_TX_FIFO_EMPTY);
}
void usb_device_cdc_rxfifo_empty_callback(usb_device_t *device,
void usb_device_cdc_rxfifo_full_callback(usb_device_t *device,
                                          uint8_t endpoint) {
// TODO the application
  cdc_process_rx(device, CDC_RX_FIFO_FULL);
}
void usb_device_cdc_transmit_done_callback(usb_device_t *device,
void usb_device_cdc_rx_done_callback(usb_device_t *device,
                                           uint8_t endpoint) {
  cdc_process_rx(device, CDC_RX_DONE);
}

void usb_device_cdc_tx_done_callback(usb_device_t *device,
                                           uint8_t endpoint) {
// TODO the application
  cdc_process_tx(device, CDC_TX_DONE);
}

void usb_device_cdc_received_data_callback(usb_device_t *device, packet_info_t *packet) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  uint8_t data[4*MAX_PACKET_SIZE];
  uint16_t len = packet->byte_count;

  volatile uint32_t* fifo = &device->fifos[packet->endpoint_num].data[0];

  if (packet->packet_status == PACKET_OUT_DATA_PACKET_RCVD) {
    // TODO: could be optimized until pointer has to wrap
    usb_generic_read(data, len, fifo);

    for (uint16_t i = 0; i < len; i++) {
      queue_enqueue(cdc->rx_buffer, (void*)&data[i]);
    }

    reg_write_bits_pos(&device->out[2].DOEPTSIZ, MAX_PACKET_SIZE, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF);
    reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_PKTCNT_Pos, 0x3FF);
    device->out[DATA_ENDPOINT].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
  } else if(packet->packet_status == PACKET_OUT_TRANSFER_COMPLETED) {
      if (queue_space(cdc->rx_buffer)) {
        reg_write_bits_pos(&device->out[2].DOEPTSIZ, MAX_PACKET_SIZE, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF);
        reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_PKTCNT_Pos, 0x3FF);
        device->out[DATA_ENDPOINT].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
      }
  } else {
    usb_generic_read(data, packet->byte_count, fifo);
  }
}

void usb_device_cdc_nak_callback(usb_device_t *device, uint8_t endpoint) {
  // Nothing to do for now
}
void usb_device_cdc_nyet_callback(usb_device_t *device, uint8_t endpoint) {
  // Nothing to do for now
}

void cdc_data_set_receive_callback(usb_device_t *device,
                                   cdc_receive_callback_t callback) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  cdc->callback = callback;
}

int32_t cdc_data_send_blocking(usb_device_t *device, uint8_t *data, uint16_t size) {
  int32_t sent = 0;
  if (size == 0) {
    size = strlen((char*)data);
  }

  while (size > 0) {
    int32_t res = cdc_data_send(device, data, size);

    if (res == -1) {
      return -1;
    }

    size -= res;
    data += res;
    sent += res;
  }

  return sent;
}

int32_t cdc_data_send(usb_device_t *device, uint8_t* data, uint16_t size) {
  if (size == 0) {
    size = strlen((char*)data);
  }

  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;

  task_result_t result = cdc_transmit(device, data, &size);

  // If would block, just send back nothing was sent
  // That means no transaction has begun.
  if (result == RES_WOULD_BLOCK) {
    size = 0;
  } else if (result == RES_ERROR) {
    size = -1;
  }

  return size;
}

uint16_t cdc_data_receive(usb_device_t *device, uint8_t* buffer,
                          uint16_t max_size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  uint16_t size = max_size;

  for (uint16_t i = 0; i < max_size; i++) {
    uint8_t element;
    if (!queue_dequeue_safely(cdc->rx_buffer, buffer + i)) {
      size = i;
      break;
    }
  }

  return size;
}

bool cdc_got_params(usb_device_t *device) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  return cdc->got_setup != 0;
}

Do not follow this link