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

9db48e9622188345ed867afec065b1f68690e20d — Rutherther 4 months ago 5d5e3d6
feat: use task_result to indicate blocking

There should be no spin loops inside of the
interrupts, so this change removes them,
and instead uses a result system that has
WOULD_BLOCK result. When this result is returned,
it is expected there was no side-effect and the
function can safely be reran later.
A include/generic.h => include/generic.h +10 -0
@@ 0,0 1,10 @@
#ifndef GENERIC_H
#define GENERIC_H

typedef enum {
  RES_OK,
  RES_ERROR,
  RES_WOULD_BLOCK
} task_result_t;

#endif // GENERIC_H

A include/queue.h => include/queue.h +25 -0
@@ 0,0 1,25 @@
#include <stdint.h>
#include <stdbool.h>

#ifndef QUEUE_H
#define QUEUE_H

typedef struct {
  uint16_t curr_read_ptr;
  uint16_t curr_write_ptr;

  uint16_t element_size;

  uint16_t length;
  uint16_t space;

  uint8_t elements[0];
} queue_t;

queue_t* queue_malloc(uint16_t element_size, uint16_t length);
void queue_init(queue_t* queue, uint16_t element_size, uint16_t length);
bool queue_enqueue(queue_t* queue, void* element);
void* queue_dequeue(queue_t* queue);
void* queue_peek(queue_t* queue);

#endif // QUEUE_H

M include/usb.h => include/usb.h +28 -14
@@ 1,5 1,7 @@
#include <stdint.h>
#include <stm32h747xx.h>
#include "generic.h"
#include <stdbool.h>

#ifndef USB_H
#define USB_H


@@ 297,6 299,15 @@ void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint,
                      uint32_t *sub_word_data,
                      uint8_t *sub_word_bytes);

void usb_generic_fill_fifo(USB_OTG_INEndpointTypeDef *endpoint,
                      uint8_t *data,
                      uint16_t size,
                      volatile uint32_t *fifo_tx_target);

task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint,
                                   uint16_t size,
                                   uint16_t max_packet_size);

/**
 * @brief Send data through usb
 * @details Configures the endpoint with given data count, fills fifos with those, and enabled the endpoint.


@@ 305,11 316,14 @@ void usb_generic_fill_fifo_words(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[in] size Number of bytes to send.
 * @param[out] fifo_tx_target Address of the fifo. All data is written to this exact address, no offsets are made.
 */
void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
                      uint8_t *data,
                      uint16_t size,
                      volatile uint32_t *fifo_tx_target);

bool usb_is_inendpoint_ready(USB_OTG_INEndpointTypeDef *endpoint);
bool usb_check_fifo_space(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size);

/**
 * @brief Read data from the usb fifo.
 * @details The data are read from the fifo pointer, without any offsets, and put into data array


@@ 317,9 331,9 @@ void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[in] size Number of bytes to store in the array.
 * @param[out] fifo_rx_source Address of the fifo. All data are read from this exact address, no offsets are made.
 */
void usb_generic_read(uint8_t *data,
                      uint16_t size,
                      volatile uint32_t *fifo_rx_source);
task_result_t usb_generic_read(uint8_t *data,
                               uint16_t size,
                               volatile uint32_t *fifo_rx_source);

/**
 * @brief Sends descriptor


@@ 328,7 342,7 @@ void usb_generic_read(uint8_t *data,
 * @param[out] descriptor The descriptor to send. The wlength has to be set.
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                         usb_descriptor_t *descriptor,
                         uint16_t max_size,
                         volatile uint32_t *fifo_tx_target);


@@ 340,7 354,7 @@ void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced.
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                usb_device_descriptor_t *descriptor,
                                uint16_t max_size,
                                volatile uint32_t *fifo_tx_target);


@@ 352,7 366,7 @@ void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced.
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor,
task_result_t usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor,
                                          uint16_t max_size,
                                          volatile uint32_t *fifo_tx_target);



@@ 363,7 377,7 @@ void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,us
 * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced.
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                   usb_interface_descriptor_t *descriptor,
                                   uint16_t max_size,
                                   volatile uint32_t *fifo_tx_target);


@@ 375,10 389,10 @@ void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[out] descriptor The descriptor to send. The wLength in header can be wrong, it will get replaced.
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                  usb_endpoint_descriptor_t *descriptor,
                                  uint16_t max_size,
                                  volatile uint32_t *fifo_tx_target);
task_result_t usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                           usb_endpoint_descriptor_t *descriptor,
                                           uint16_t max_size,
                                           volatile uint32_t *fifo_tx_target);

/**
 * @brief Sends string descriptor zero.


@@ 387,7 401,7 @@ void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
 * @param[out] string_descriptor The descriptor to send. The wLength NEEDS to be correct!
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_string_descriptor_zero(
task_result_t usb_send_string_descriptor_zero(
    USB_OTG_INEndpointTypeDef *endpoint,
    usb_string_descriptor_zero_t *string_descriptor,
    uint16_t max_size,


@@ 400,7 414,7 @@ void usb_send_string_descriptor_zero(
 * @param[out] string_descriptor The descriptor to send. The wLength NEEDS to be correct!
 * @param[out] fifo_tx_target Address of the fifo. All data are written to this exact address, no offsets are made.
 */
void usb_send_unicode_string_descriptor(
task_result_t usb_send_unicode_string_descriptor(
    USB_OTG_INEndpointTypeDef *endpoint,
    usb_unicode_string_descriptor_t *string_descriptor,
    uint16_t max_size,

M include/usb_device.h => include/usb_device.h +15 -9
@@ 1,5 1,7 @@
#include <stm32h747xx.h>
#include "usb.h"
#include "generic.h"
#include "queue.h"

#ifndef USB_DEVICE_H
#define USB_DEVICE_H


@@ 70,7 72,7 @@ typedef struct {
 * @param[in,out] device The usb device.
 * @param[out] cmd The command that has been received, GET_DESCRIPTOR of type CONFIGURATION
 */
  void (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd);
  task_result_t (*send_configuration)(usb_device_t* device, usb_setup_command_t* cmd);

/**
 * @brief Set all endpoints except for endpoint 0 based on the configuration.


@@ 98,9 100,9 @@ typedef struct {
 * @details Got a setup packet that cannot be handled by the device handler. That likely means a class specific setup command.
 * @param[in,out] device The usb device.
 * @param[out] cmd The command that was received.
 * @return Zero if packet was handled, other if there is an issue.
 * @return A result of the callback, successfull or error.
 */
  uint8_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd);
  task_result_t (*setup_packet_callback)(usb_device_t* device, usb_setup_command_t* cmd);

/**
 * @brief Received SET_CONFIGURATION, Endpoints set up.


@@ 186,6 188,15 @@ typedef enum {
  SETUP_STAGE_AWAITING_ACK,      /**< Handled a setup packet, sent data, waiting for acknowledge */
} usb_setup_command_stage_t;

#define MAX_SETUP_PACKETS 3
typedef struct {
  queue_t* received_setup_commands;

  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. */
} usb_device_setup_t;

/**
 * @struct usb_device_t
 * Struct representing usb device.


@@ 204,12 215,7 @@ struct usb_device_t {

  usb_device_state_t state;                       /**< The current status of the device. */

  // TODO: is this count field required?          /**< Description */
  uint8_t received_setup_commands_count;          /**< Number of setup commands received, max 3, wrapped. */
  uint8_t received_setup_commands_index;          /**< Currently handled setup command index. */
  usb_setup_command_stage_t setup_stage;          /**< Current stage status. */
  uint8_t detected_setup_errors;                  /**< Number of errors when handling setup commands. This should be zero. */
  usb_setup_command_t received_setup_commands[3]; /**< The setup commands reseived so far. */
  usb_device_setup_t setup;
};

// has configuration etc

M include/usb_device_cdc.h => include/usb_device_cdc.h +2 -2
@@ 106,9 106,9 @@ usb_class_header_t* usb_device_cdc_init(usb_device_t *device,
                char *vendor_name, char *product_name,
                uint16_t serial_number, char* serial_name);

void usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd);
task_result_t usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd);

uint8_t usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd);
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);

A src/queue.c => src/queue.c +49 -0
@@ 0,0 1,49 @@
#include <stdlib.h>
#include "queue.h"

queue_t *queue_malloc(uint16_t element_size, uint16_t length) {
  return (queue_t*)malloc(sizeof(queue_t) + element_size * length);
}

void queue_init(queue_t* queue, uint16_t element_size, uint16_t length) {
  queue->curr_write_ptr = 0;
  queue->curr_read_ptr = 0;
  queue->element_size = element_size;
  queue->length = length;
  queue->space = length;
}

bool queue_enqueue(queue_t *queue, void *element) {
  if (queue->space == 0) {
    return false;
  }

  for (uint16_t i = 0; i < queue->element_size; i++) {
    queue->elements[queue->curr_write_ptr + i] = *(uint8_t*)(element + i);
  }

  queue->space--;
  queue->curr_write_ptr += queue->element_size;
  queue->curr_write_ptr %= queue->length * queue->element_size;
  return true;
}

void *queue_dequeue(queue_t *queue) {
  void* element = queue_peek(queue);

  if (element != NULL) {
    queue->curr_read_ptr += queue->element_size;
    queue->curr_read_ptr %= queue->length * queue->element_size;
    queue->space++;
  }

  return element;
}

void *queue_peek(queue_t *queue) {
  if (queue->space == queue->length) {
    return NULL;
  }

  return &queue->elements[queue->curr_read_ptr];
}

M src/usb.c => src/usb.c +64 -80
@@ 77,21 77,32 @@ void usb_generic_fill_fifo(USB_OTG_INEndpointTypeDef *endpoint,
                      uint8_t *data,
                      uint16_t size,
                      volatile uint32_t *fifo_tx_target) {
  size = ((size + 3) / 4) * 4;
  usb_data_t txData;
  uint8_t subWordBytes = size % 4;
  uint8_t wordCount = size / 4;

  for (uint8_t i = 0; i < wordCount; i++) {
    txData.word = *((uint32_t*)data);
    *fifo_tx_target = txData.word;
    data += 4;
  }

  uint32_t subWordData;
  uint8_t subWordCount;
  if (subWordBytes > 0) {
    txData.word = 0;
    for (int i = 0; i < subWordBytes; i++) {
      txData.bytes[i] = *(data++);
      size--;
    }

  usb_generic_fill_fifo_words(endpoint, data, size, fifo_tx_target, &subWordData, &subWordCount);
    *fifo_tx_target = txData.word;
  }
}

void usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size, uint16_t max_packet_size) {
task_result_t usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size, uint16_t max_packet_size) {
  uint16_t packet_count = (size + max_packet_size - 1) / max_packet_size;

  if (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) {
    // this is bad! Can't send the packet, this shouldn't get here, ever.
    while (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA);
    /* __nop(); */
  if (usb_is_inendpoint_ready(endpoint)) {
    return RES_WOULD_BLOCK;
  }

  if (size > 0) {


@@ 100,50 111,27 @@ void usb_generic_setup_in_endpoint(USB_OTG_INEndpointTypeDef *endpoint, uint16_t
    endpoint->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos);
  }
  endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;

  return RES_OK;
}

void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
                      uint8_t *data,
                      uint16_t size,
                      volatile uint32_t *fifo_tx_target) {
  if (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) {
    // this is bad! Can't send the packet, this shouldn't get here, ever.
    while (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA);
    /* __nop(); */
  }

  uint32_t fifo_size;
  while ((fifo_size = endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) < (size + 3) / 4) {
    /* __nop(); */
  task_result_t result = usb_generic_setup_in_endpoint(endpoint, size, 64);
  if (result != RES_OK) {
    return result;
  }

  // TODO: generic max packet size
  usb_generic_setup_in_endpoint(endpoint, size, 64);

  usb_data_t tx_data;
  uint8_t subWordBytes = size % 4;
  uint8_t wordCount = size / 4;

  for (uint8_t i = 0; i < wordCount; i++) {
    tx_data.word = *((uint32_t*)data);
    *fifo_tx_target = tx_data.word;
    data += 4;
  }

  // TODO: check where to put it... beginning, end...
  if (subWordBytes != 0) {
    tx_data.word = 0;
    for (uint8_t i = 0; i < subWordBytes; i++) {
      tx_data.bytes[i] = *(data + i);
    }
    *fifo_tx_target = tx_data.word;
  }
  usb_generic_fill_fifo(endpoint, data, size, fifo_tx_target);

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

void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_source) {
task_result_t usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_source) {
  usb_data_t rx_data;
  uint8_t subWordBytes = size % 4;
  uint8_t wordCount = size / 4;


@@ 161,9 149,11 @@ void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_s
      *(data + i) = rx_data.bytes[i];
    }
  }

  return RES_OK;
}

void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                         usb_descriptor_t *descriptor,
                         uint16_t max_size,
                         volatile uint32_t *fifo_tx_target) {


@@ 172,81 162,60 @@ void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
    size = max_size;
  }

  usb_generic_send(endpoint, (uint8_t*)descriptor, size, fifo_tx_target);
  return usb_generic_send(endpoint, (uint8_t*)descriptor, size, fifo_tx_target);
}

/* void usb_send_configuration_descriptor(USB_OTG_INEndpointTypeDef* endpoint, */
/*                                        usb_class_t *class, */
/*                                        volatile uint32_t *fifo_tx_target) { */
/*   class->configuration_descriptor.header.bDescriptorType = DESCRIPTOR_CONFIGURATION; */
/*   class->configuration_descriptor.header.bLength = sizeof(usb_configuration_descriptor_t); */
/*   class->interface_descriptor.header.bDescriptorType = DESCRIPTOR_INTERFACE; */
/*   class->interface_descriptor.header.bLength = sizeof(usb_interface_descriptor_t); */
/*   uint16_t total_length = */
/*     sizeof(usb_configuration_descriptor_t) + */
/*     sizeof(usb_interface_descriptor_t) + */
/*     class->interface_descriptor.bNumEndpoints * sizeof(usb_endpoint_descriptor_t); */

/*   for (int i = 0; i < class->interface_descriptor.bNumEndpoints; i++) { */
/*     class->endpoint_descriptors[i].header.bDescriptorType = DESCRIPTOR_ENDPOINT; */
/*     class->endpoint_descriptors[i].header.bLength = sizeof(usb_endpoint_descriptor_t); */
/*   } */

/*   // NOTE: since the memory layout is: configuration, interface, endpoints, */
/*   // we can send directly like this. */
/*   usb_generic_send(endpoint, */
/*                    (uint8_t*)&class->configuration_descriptor, */
/*                    total_length, fifo_tx_target); */
/* } */

void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                usb_device_descriptor_t *descriptor,
                                uint16_t max_size,
                                volatile uint32_t *fifo_tx_target) {
  descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE;
  descriptor->header.bLength = sizeof(usb_device_descriptor_t);
  usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
  return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
}
void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                          usb_device_qualifier_t *descriptor,
                                          uint16_t max_size,
                                   volatile uint32_t *fifo_tx_target) {
  descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE_QUALIFIER;
  descriptor->header.bLength = sizeof(usb_device_qualifier_t);
  usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
  return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
}
void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                   usb_interface_descriptor_t *descriptor,
                                   uint16_t max_size,
                                   volatile uint32_t *fifo_tx_target) {
  descriptor->header.bDescriptorType = DESCRIPTOR_INTERFACE;
  descriptor->header.bLength = sizeof(usb_interface_descriptor_t);
  usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
  return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
}
void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
                                  usb_endpoint_descriptor_t *descriptor,
                                   uint16_t max_size,
                                  volatile uint32_t *fifo_tx_target) {
  descriptor->header.bDescriptorType = DESCRIPTOR_ENDPOINT;
  descriptor->header.bLength = sizeof(usb_endpoint_descriptor_t);
  usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
  return usb_send_descriptor(endpoint, &descriptor->header, max_size, fifo_tx_target);
}

void usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint,
task_result_t usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint,
                                     usb_string_descriptor_zero_t *string_descriptor,
                                     uint16_t max_size,
                              volatile uint32_t *fifo_tx_target) {
  usb_data_t data;
  data.word = 0;

  usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64);
  task_result_t result = usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64);
  if (result != RES_OK) {
    return result;
  }

  data.bytes[0] = string_descriptor->header.bLength;
  data.bytes[1] = string_descriptor->header.bDescriptorType;

  if (string_descriptor->header.bLength == 2) {
    usb_generic_fill_fifo(endpoint, &data.bytes[0], 2, fifo_tx_target);
    return;
    return RES_OK;
  }

  data.halfwords[1] = string_descriptor->wLANGID[0];


@@ 258,14 227,20 @@ void usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint,
  }

  endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK;

  return RES_OK;
}
void usb_send_unicode_string_descriptor(

task_result_t usb_send_unicode_string_descriptor(
    USB_OTG_INEndpointTypeDef *endpoint,
    usb_unicode_string_descriptor_t *string_descriptor,
    uint16_t max_size,
    volatile uint32_t *fifo_tx_target) {

  usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64);
  task_result_t result = usb_generic_setup_in_endpoint(endpoint, string_descriptor->header.bLength, 64);
  if (result != RES_OK) {
    return result;
  }

  usb_data_t data = { .word = 0 };



@@ 285,4 260,13 @@ void usb_send_unicode_string_descriptor(
  }

  endpoint->DIEPCTL = USB_OTG_DIEPCTL_CNAK;
  return RES_OK;
}

bool usb_is_inendpoint_ready(USB_OTG_INEndpointTypeDef *endpoint) {
  return (endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPENA) != 0;
}

bool usb_check_fifo_space(USB_OTG_INEndpointTypeDef *endpoint, uint16_t size) {
  return ((endpoint->DTXFSTS & (USB_OTG_DTXFSTS_INEPTFSAV_Msk)) >> USB_OTG_DTXFSTS_INEPTFSAV_Pos) >= (size + 3) / 4;
}

M src/usb_device.c => src/usb_device.c +134 -94
@@ 20,14 20,15 @@ void* usb_device_init(usb_device_slot_t slot, usb_class_vtable_t *class,
  void* peripheral_address = usb_periph_addresses[slot];

  device->state = INIT;
  device->detected_setup_errors = 0;
  device->setup.detected_setup_errors = 0;
  device->core = peripheral_address + USB_OTG_GLOBAL_BASE;
  device->device = peripheral_address + USB_OTG_DEVICE_BASE;
  device->out = peripheral_address + USB_OTG_OUT_ENDPOINT_BASE;
  device->in = peripheral_address + USB_OTG_IN_ENDPOINT_BASE;
  device->fifos = (usb_fifo_t*)(((uint8_t*)device->core) + USB_OTG_FIFO_BASE);
  device->received_setup_commands_count = 0;
  device->received_setup_commands_index = 0;

  device->setup.received_setup_commands = queue_malloc(sizeof(usb_setup_command_t), MAX_SETUP_PACKETS);
  queue_init(device->setup.received_setup_commands, sizeof(usb_setup_command_t), MAX_SETUP_PACKETS);
  device->vt = *class;

  device->class = device->


@@ 152,10 153,8 @@ typedef struct {
  uint8_t status_phase_start;
} packet_info_t;

void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
  if (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) {
    device->detected_setup_errors++;
  }
task_result_t usb_handle_setup_command(usb_device_t *device, usb_setup_command_t* cmd) {
  task_result_t result = RES_ERROR;

  switch (cmd->bRequest) {
  case USB_SETUP_GET_STATUS: {


@@ 189,9 188,11 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
      break;
    }

    usb_generic_send(device->in, packet, size,
                     &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
    result = usb_generic_send(device->in, packet, size,
                              &device->fifos[0].data[0]);
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_RESPONSE;
    }
  }
    break;
  case USB_SETUP_GET_DESCRIPTOR: {


@@ 200,44 201,40 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {

    switch (descriptor_type) {
    case DESCRIPTOR_DEVICE:
      usb_send_device_descriptor(device->in, &device->class->device_descriptor, cmd->wLength, &device->fifos[0].data[0]);
      device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
      result = usb_send_device_descriptor(device->in, &device->class->device_descriptor, cmd->wLength, &device->fifos[0].data[0]);
      break;
    case DESCRIPTOR_CONFIGURATION:
      device->vt.send_configuration(device, cmd);
      device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
      result = device->vt.send_configuration(device, cmd);
      break;
    case DESCRIPTOR_STRING: {
      if (descriptor_index == 0) {
        usb_send_string_descriptor_zero(device->in, &device->class->string_descriptor_zero, cmd->wLength, &device->fifos[0].data[0]);
        result = usb_send_string_descriptor_zero(device->in, &device->class->string_descriptor_zero, cmd->wLength, &device->fifos[0].data[0]);
      } else {
        uint8_t index = descriptor_index - 1;
        // NOTE: the user could potentially read different memory part!!
        // This has to be fixed, the length has to be stored somewhere.
        usb_send_unicode_string_descriptor(device->in, &device->class->string_descriptors[index], cmd->wLength, &device->fifos[0].data[0]);
        result = usb_send_unicode_string_descriptor(device->in, &device->class->string_descriptors[index], cmd->wLength, &device->fifos[0].data[0]);
      }
    }
      break;
    case DESCRIPTOR_INTERFACE:
      usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, cmd->wLength, &device->fifos[0].data[0]);
      device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
      result = usb_send_interface_descriptor(device->in, &device->class->interfaces[descriptor_index].interface_descriptor, cmd->wLength, &device->fifos[0].data[0]);
      break;
    case DESCRIPTOR_ENDPOINT:
      // TODO: how to match the interface to the descriptor index?
      usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], cmd->wLength, &device->fifos[0].data[0]);
      device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
      result = usb_send_endpoint_descriptor(device->in, &device->class->interfaces[0].endpoint_descriptors[descriptor_index], cmd->wLength, &device->fifos[0].data[0]);
      break;
    case DESCRIPTOR_DEVICE_QUALIFIER:
      usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, cmd->wLength, &device->fifos[0].data[0]);
      device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
      result = usb_send_device_qualifier_descriptor(device->in, &device->class->device_qualifier, cmd->wLength, &device->fifos[0].data[0]);
      break;
    case DESCRIPTOR_OTHER_SPEED_CONFIGURATION:
    case DESCRIPTOR_INTERFACE_POWER:
      reg_set_bits(&device->out[0].DOEPCTL, USB_OTG_DOEPCTL_STALL);
      device->setup_stage = SETUP_STAGE_NONE;
      break;
    }
    // TODO
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_RESPONSE;
    }
  }
    break;
  case USB_SETUP_GET_CONFIGURATION: {


@@ 245,16 242,20 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
    if (device->state != ENUMERATED) {
      value = 0;
    }
    usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
    result = usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]);
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_RESPONSE;
    }
  }
    break;
  case USB_SETUP_GET_INTERFACE: {
    usb_generic_send(device->in,
                     &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting,
                     sizeof(uint8_t),
                     &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_RESPONSE;
    result = usb_generic_send(device->in,
                              &device->class->interfaces[cmd->wIndex].interface_descriptor.bAlternateSetting,
                              sizeof(uint8_t),
                              &device->fifos[0].data[0]);
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_RESPONSE;
    }
  }
    break;
  case USB_SETUP_SET_ADDRESS:


@@ 262,9 263,11 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
                       cmd->wValue,
                       USB_OTG_DCFG_DAD_Pos,
                       0x7F);
    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_ACK;
    device->state = SET_ADDRESS_RCVD;
    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 = SET_ADDRESS_RCVD;
    }
    break;
  case USB_SETUP_SET_CONFIGURATION: {
    if (cmd->wValue == 0) {


@@ 275,7 278,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
    } 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;
      device->setup.stage = SETUP_STAGE_SENDING_ACK;

      // TODO setup endpoints
    } else {


@@ 297,8 300,10 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
      break;
    }

    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_ACK;
    result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_ACK;
    }
    break;
  case USB_SETUP_SET_FEATURE:
    switch (cmd->wValue) {


@@ 314,8 319,10 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
      break;
    }

    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
    device->setup_stage = SETUP_STAGE_SENDING_ACK;
    result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
    if (result == RES_OK) {
      device->setup.stage = SETUP_STAGE_SENDING_ACK;
    }
    break;
  case USB_SETUP_SET_DESCRIPTOR:
  case USB_SETUP_SET_INTERFACE:


@@ 326,13 333,77 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
  case RESERVED1:
  case RESERVED2:
  default:
    device->vt.setup_packet_callback(device, cmd);
    return device->vt.setup_packet_callback(device, cmd);
    break;
  }

  device->out[0].DOEPCTL = USB_OTG_DOEPCTL_CNAK;
  return result;
}

typedef enum {
  USB_UNKNOWN,
  USB_GOT_SETUP,
  USB_PROCESS_SETUP,
  USB_GOT_ACK,
  USB_GOT_RESPONSE,
  USB_SENT_RESPONSE,
} usb_event_t;

task_result_t usb_handle_setup(usb_device_t *device, usb_event_t event) {
  switch (device->setup.stage) {
  case SETUP_STAGE_RCVD_SETUP_PACKET: {
    if (event == USB_GOT_SETUP) {
      return RES_OK;
    }

    usb_setup_command_t* command = queue_peek(device->setup.received_setup_commands);
    task_result_t result = usb_handle_setup_command(device, command);
    if (result == RES_OK) {
      queue_dequeue(device->setup.received_setup_commands);
      uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
      if (setup_packets_count == 0) {
        reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
      }
    }
    return result;
  }
    break;
  case SETUP_STAGE_NONE:
    if (queue_peek(device->setup.received_setup_commands) != NULL) {
      device->setup.stage = SETUP_STAGE_RCVD_SETUP_PACKET;

      if (event == USB_PROCESS_SETUP) {
        usb_handle_setup(device, event);
      }
    }
    break;
  case SETUP_STAGE_AWAITING_RESPONSE:
    // NOTE: Currently not really supported.
    return RES_ERROR;
    break;
  case SETUP_STAGE_SENDING_RESPONSE:
    if (event == USB_SENT_RESPONSE) {
      device->setup.stage = SETUP_STAGE_AWAITING_ACK;
    }
    break;
  case SETUP_STAGE_SENDING_ACK:
    if (event == USB_SENT_RESPONSE) {
      device->setup.stage = SETUP_STAGE_NONE;
      return usb_handle_setup(device, event);
    }
    break;
  case SETUP_STAGE_AWAITING_ACK:
    if (event == USB_GOT_ACK) {
      device->setup.stage = SETUP_STAGE_NONE;
      return usb_handle_setup(device, event);
    }
    break;
  }

  return RES_OK;
}


void usb_handle_rxflvl_control_int(usb_device_t *device,
                                   packet_info_t *packet_info) {
  uint32_t dummy;


@@ 342,44 413,27 @@ void usb_handle_rxflvl_control_int(usb_device_t *device,
  if (packet_info->packet_status == PACKET_SETUP_TRANSACTION_COMPLETED) {
    // Nothing do to.
    dummy = *fifo;

    if (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) {
      // something went wrong. Let's continue, but this isn't looking good.
      device->detected_setup_errors++;
    }

    device->received_setup_commands_index %= 3;
    uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
    usb_setup_command_t* command = &device->received_setup_commands[device->received_setup_commands_index++];
    usb_handle_setup(device, command);

    if (setup_packets_count == 0) {
      reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
    }
    usb_handle_setup(device, USB_PROCESS_SETUP);
  } else if (packet_info->packet_status == PACKET_SETUP_DATA_PACKET_RECEIVED) {
    // SAVE data
    uint8_t setup_packets_count = reg_read_bits_pos(&device->out[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3);
    usb_setup_command_t* command = &device
      ->received_setup_commands[device->received_setup_commands_count++];
    device->received_setup_commands_count %= 3;

    usb_generic_read(command, 8, fifo);
    usb_setup_command_t command;
    usb_generic_read((uint8_t*)&command, 8, fifo);
    usb_handle_setup(device, USB_GOT_SETUP);

    device->setup_stage = SETUP_STAGE_RCVD_SETUP_PACKET;

    /* if (setup_packets_count == 0) { */
    /*   reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); */
    /* } */
    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
  } else if (packet_info->byte_count != 0) {
    usb_generic_read(data, packet_info->byte_count, fifo);

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


@@ 494,11 548,8 @@ void usb_handle_endpoint_in_int(usb_device_t* device) {
    }

    if (ep_id == 0) {
      if (device->setup_stage == SETUP_STAGE_SENDING_RESPONSE) {
        device->setup_stage = SETUP_STAGE_AWAITING_ACK;
      } else if (device->setup_stage == SETUP_STAGE_SENDING_ACK) {
        device->setup_stage = SETUP_STAGE_NONE;
      }
      usb_handle_setup(device, USB_GOT_RESPONSE);
      usb_handle_setup(device, USB_PROCESS_SETUP);
    }
    reg_clear_bits(&device->in[ep_id].DIEPINT, USB_OTG_DIEPINT_XFRC);
  }


@@ 566,20 617,8 @@ void usb_handle_endpoint_out_int(usb_device_t* device) {
    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 (device->setup_stage != SETUP_STAGE_RCVD_SETUP_PACKET) { */
    /*   // something went wrong. Let's continue, but this isn't looking good. */
    /*   device->detected_setup_errors++; */
    /* } */

    /* device->received_setup_commands_index %= 3; */
    /* uint8_t setup_packets_count = reg_read_bits_pos(&device->out[ep_id].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); */
    /* usb_setup_command_t* command = &device->received_setup_commands[device->received_setup_commands_index++]; */
    /* usb_handle_setup(device, command); */

    /* if (setup_packets_count == 0) { */
    /*   reg_write_bits_pos(&device->out[0].DOEPTSIZ, 3, USB_OTG_DOEPTSIZ_STUPCNT_Pos, 3); */
    /* } */
  }
  if (interrupt_reg & USB_OTG_DOEPINT_AHBERR) {
    // NOTE This shoudln't be reached since DMA is not used


@@ 591,15 630,15 @@ void usb_handle_endpoint_out_int(usb_device_t* device) {
    reg_clear_bits(&device->out[ep_id].DOEPINT, USB_OTG_DOEPINT_EPDISD);
  }
  if (interrupt_reg & USB_OTG_DOEPINT_XFRC) {
    // TODO: handle data? - callback to device data handle?
    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 && device->setup_stage == SETUP_STAGE_AWAITING_ACK) {
      device->setup_stage = SETUP_STAGE_NONE;
    }
    // TODO: handle data? - callback to device data handle?

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



@@ 696,9 735,10 @@ void otg_hs_handler(void) {

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

    usb_handle_setup(device, USB_PROCESS_SETUP);
    return;
  }


M src/usb_device_cdc.c => src/usb_device_cdc.c +11 -15
@@ 248,19 248,14 @@ uint16_t get_size(uint16_t size_to_send, uint16_t* remaining_size) {
  return size;
}

void usb_device_cdc_send_configuration(usb_device_t *device,
                                       usb_setup_command_t *cmd) {
task_result_t usb_device_cdc_send_configuration(usb_device_t *device,
                                                usb_setup_command_t *cmd) {
  usb_device_cdc_t* dev = (usb_device_cdc_t*)device->class;
  USB_OTG_INEndpointTypeDef* enp0 = &device->in[0];
  volatile uint32_t* enp0fifo = &device->fifos[0].data[0];
  uint32_t sub_word_data;
  uint8_t  sub_word_count = 0;

  if (enp0->DIEPCTL & USB_OTG_DIEPCTL_EPENA) {
    // this is bad! Can't send the packet, this shouldn't get here, ever.
    while (enp0->DIEPCTL & USB_OTG_DIEPCTL_EPENA);
  }

  // first configure the size
  uint16_t size =
      sizeof(usb_configuration_descriptor_t) +


@@ 285,11 280,11 @@ void usb_device_cdc_send_configuration(usb_device_t *device,
  // TODO: what if there is not enough space for this?
  // I mean there should be... but that case should probably be handled to,
  // it depends a lot on how many functions and interfaces we do have...
  uint16_t packet_count = (size + 63) / 64;
  // configure size
  enp0->DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_MULCNT_Pos) | (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
  // enable endpoint
  enp0->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
  task_result_t result = usb_generic_setup_in_endpoint(enp0, size, 64);

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

  // fill fifo with all configuration
  usb_generic_fill_fifo_words(enp0,


@@ 334,12 329,13 @@ void usb_device_cdc_send_configuration(usb_device_t *device,

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

uint8_t usb_device_cdc_setup_packet_callback(usb_device_t *device,
                                          usb_setup_command_t *cmd) {
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?
  return 1;
  return RES_OK;
}
void usb_device_cdc_enumeration_done_callback(usb_device_t *device) {
  // TODO start the application somehow

Do not follow this link