M include/usb.h => include/usb.h +44 -13
  
@@ 1,8 1,16 @@
 #include <stdint.h>
+#include <stm32h747xx.h>
 
 #ifndef USB_H
 #define USB_H
 
+// NOTE: fields here have to be kept in same order,
+// and care must be taken the structs are not padded.
+// They are from the usb standard.
+
+#define USB_SUBLANG_ENGLISH_US 0x01
+#define USB_LANG_ENGLISH 0x09
+
 #pragma GCC diagnostic error "-Wpadded"
 typedef struct {
   uint8_t self_powered : 1;
@@ 127,7 135,7 @@ typedef struct {
   uint8_t reserved1 : 1;
   uint8_t self_powered : 1;
   uint8_t remote_wakeup : 1;
-  uint8_t reserved2: 5;
+  uint8_t reserved_zeros: 5;
 } usb_configuration_descriptor_attributes;
 
 typedef struct __attribute__((packed)) {
@@ 160,10 168,10 @@ typedef enum {
   USB_ENDPOINT_IN = 1,
 } usb_endpoint_direction_t;
 
-typedef struct {
+typedef struct __attribute__((packed)) {
   uint8_t endpoint_number : 4;
   uint8_t reserved : 3;
-  uint8_t direction : 1;
+  usb_endpoint_direction_t direction : 1;
 } usb_endpoint_address_t;
 
 typedef enum {
@@ 215,28 223,51 @@ typedef struct {
   uint8_t *bString;
 } usb_unicode_string_descriptor_t;
 
-void usb_generic_send(uint8_t *data, uint16_t size,
+// NOTE: End of structs from usb specification here
+
+typedef struct __attribute__((packed)) {
+  usb_device_descriptor_t device_descriptor;
+  usb_device_qualifier_t device_qualifier;
+  // NOTE: keep these three fields in this order!
+  usb_configuration_descriptor_t configuration_descriptor;
+  usb_interface_descriptor_t interface_descriptor;
+  usb_endpoint_descriptor_t endpoint_descriptors[8];
+  //
+  usb_string_descriptor_zero_t string_descriptor_zero;
+  usb_unicode_string_descriptor_t *string_descriptors;
+} usb_class_t;
+
+void usb_generic_send(USB_OTG_INEndpointTypeDef *endpoint,
+                      uint8_t *data,
+                      uint16_t size,
                       volatile uint32_t *fifo_tx_target);
-void usb_generic_read(uint8_t *data, uint16_t size,
+void usb_generic_read(uint8_t *data,
+                      uint16_t size,
                       volatile uint32_t *fifo_rx_source);
-void usb_send_descriptor(usb_descriptor_t *descriptor,
+void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                         usb_descriptor_t *descriptor,
                          volatile uint32_t *fifo_tx_target);
-void usb_send_device_descriptor(usb_device_descriptor_t *descriptor,
+void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                usb_device_descriptor_t *descriptor,
                                 volatile uint32_t *fifo_tx_target);
-void usb_send_device_qualifier_descriptor(usb_device_qualifier_t *descriptor,
+void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef* endpoint,usb_device_qualifier_t *descriptor,
                                           volatile uint32_t *fifo_tx_target);
 /* void usb_send_ack(uint32_t* fifo_tx_target); */
-void usb_send_configuration_descriptor(
-    usb_configuration_descriptor_t *descriptor,
-    volatile uint32_t *fifo_tx_target);
-void usb_send_interface_descriptor(usb_interface_descriptor_t *descriptor,
+void usb_send_configuration_descriptor(USB_OTG_INEndpointTypeDef* endpoint,
+                                       usb_class_t *class,
+                                       volatile uint32_t *fifo_tx_target);
+void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                   usb_interface_descriptor_t *descriptor,
                                    volatile uint32_t *fifo_tx_target);
-void usb_send_endpoint_descriptor(usb_endpoint_descriptor_t *descriptor,
+void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                  usb_endpoint_descriptor_t *descriptor,
                                   volatile uint32_t *fifo_tx_target);
 void usb_send_string_descriptor_zero(
+    USB_OTG_INEndpointTypeDef *endpoint,
     usb_string_descriptor_zero_t *string_descriptor,
     volatile uint32_t *fifo_tx_target);
 void usb_send_unicode_string_descriptor(
+    USB_OTG_INEndpointTypeDef *endpoint,
     usb_unicode_string_descriptor_t *string_descriptor,
     volatile uint32_t *fifo_tx_target);
 
 
M include/usb_device.h => include/usb_device.h +0 -8
  
@@ 24,14 24,6 @@ typedef struct {
 } usb_fifo_t;
 
 typedef struct {
-  usb_device_descriptor_t device_descriptor;
-  usb_configuration_descriptor_t configuration_descriptor;
-  usb_interface_descriptor_t interface_descriptor;
-  usb_device_qualifier_t device_qualifier;
-  usb_endpoint_descriptor_t endpoint_descriptors[8];
-} usb_class_t;
-
-typedef struct {
   USB_OTG_GlobalTypeDef *core;
   USB_OTG_DeviceTypeDef *device;
 
 
M src/usb.c => src/usb.c +64 -20
  
@@ 1,4 1,6 @@
 #include "usb.h"
+#include "usb_device.h"
+#include <stm32h747xx.h>
 
 typedef union {
   uint32_t word;
@@ 6,7 8,20 @@ typedef union {
   uint8_t bytes[4];
 } usb_data_t;
 
-void usb_generic_send(uint8_t* data, uint16_t size, volatile uint32_t *fifo_tx_target) {
+void 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);
+  }
+
+  // TODO: generic max packet size
+  uint16_t packet_count = size + 63 / 64;
+
+  endpoint->DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
+
   usb_data_t tx_data;
   uint8_t subWordBytes = size % 4;
   uint8_t wordCount = size / 4;
@@ 25,6 40,8 @@ void usb_generic_send(uint8_t* data, uint16_t size, volatile uint32_t *fifo_tx_t
     }
     *fifo_tx_target = tx_data.word;
   }
+
+  endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
 }
 
 void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_source) {
@@ 47,43 64,66 @@ void usb_generic_read(uint8_t *data, uint16_t size, volatile uint32_t *fifo_rx_s
   }
 }
 
-void usb_send_descriptor(usb_descriptor_t *descriptor,
+void usb_send_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                         usb_descriptor_t *descriptor,
                          volatile uint32_t *fifo_tx_target) {
-  usb_generic_send((uint8_t*)descriptor, descriptor->bLength, fifo_tx_target);
+  usb_generic_send(endpoint, (uint8_t*)descriptor, descriptor->bLength, fifo_tx_target);
 }
 
-void usb_send_configuration_descriptor(
-    usb_configuration_descriptor_t *descriptor, volatile uint32_t *fifo_tx_target) {
-  descriptor->header.bDescriptorType = DESCRIPTOR_CONFIGURATION;
-  descriptor->header.bLength = sizeof(usb_configuration_descriptor_t);
-  usb_send_descriptor(&descriptor->header, 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_device_descriptor_t *descriptor,
-                                   volatile uint32_t *fifo_tx_target) {
+void usb_send_device_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                usb_device_descriptor_t *descriptor,
+                                volatile uint32_t *fifo_tx_target) {
   descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE;
   descriptor->header.bLength = sizeof(usb_device_descriptor_t);
-  usb_send_descriptor(&descriptor->header, fifo_tx_target);
+  usb_send_descriptor(endpoint, &descriptor->header, fifo_tx_target);
 }
-void usb_send_device_qualifier_descriptor(usb_device_qualifier_t *descriptor,
+void usb_send_device_qualifier_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                          usb_device_qualifier_t *descriptor,
                                    volatile uint32_t *fifo_tx_target) {
   descriptor->header.bDescriptorType = DESCRIPTOR_DEVICE_QUALIFIER;
   descriptor->header.bLength = sizeof(usb_device_qualifier_t);
-  usb_send_descriptor(&descriptor->header, fifo_tx_target);
+  usb_send_descriptor(endpoint, &descriptor->header, fifo_tx_target);
 }
-void usb_send_interface_descriptor(usb_interface_descriptor_t *descriptor,
+void usb_send_interface_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                   usb_interface_descriptor_t *descriptor,
                                    volatile uint32_t *fifo_tx_target) {
   descriptor->header.bDescriptorType = DESCRIPTOR_INTERFACE;
   descriptor->header.bLength = sizeof(usb_interface_descriptor_t);
-  usb_send_descriptor(&descriptor->header, fifo_tx_target);
+  usb_send_descriptor(endpoint, &descriptor->header, fifo_tx_target);
 }
-void usb_send_endpoint_descriptor(usb_endpoint_descriptor_t *descriptor,
+void usb_send_endpoint_descriptor(USB_OTG_INEndpointTypeDef *endpoint,
+                                  usb_endpoint_descriptor_t *descriptor,
                                   volatile uint32_t *fifo_tx_target) {
   descriptor->header.bDescriptorType = DESCRIPTOR_ENDPOINT;
   descriptor->header.bLength = sizeof(usb_endpoint_descriptor_t);
-  usb_send_descriptor(&descriptor->header, fifo_tx_target);
+  usb_send_descriptor(endpoint, &descriptor->header, fifo_tx_target);
 }
 
-void usb_send_string_descriptor_zero(usb_string_descriptor_zero_t *string_descriptor,
+void usb_send_string_descriptor_zero(USB_OTG_INEndpointTypeDef *endpoint,
+                                     usb_string_descriptor_zero_t *string_descriptor,
                               volatile uint32_t *fifo_tx_target) {
   usb_data_t data;
   data.word = 0;
@@ 102,12 142,14 @@ void usb_send_string_descriptor_zero(usb_string_descriptor_zero_t *string_descri
 
   // TODO: Now the rest only from wLANGID[1..]
   if (string_descriptor->header.bLength > 4) {
-    usb_generic_send((uint8_t *)&string_descriptor->wLANGID[1],
+    usb_generic_send(endpoint,
+                     (uint8_t *)&string_descriptor->wLANGID[1],
                      string_descriptor->header.bLength - 4,
                      fifo_tx_target);
   }
 }
 void usb_send_unicode_string_descriptor(
+    USB_OTG_INEndpointTypeDef *endpoint,
     usb_unicode_string_descriptor_t *string_descriptor,
     volatile uint32_t *fifo_tx_target) {
   usb_data_t data = { .word = 0 };
@@ 121,6 163,8 @@ void usb_send_unicode_string_descriptor(
   *fifo_tx_target = data.word;
 
   if (string_descriptor->header.bLength > 4) {
-    usb_generic_send(&string_descriptor->bString[2], string_descriptor->header.bLength - 4, fifo_tx_target);
+    usb_generic_send(endpoint, &string_descriptor->bString[2],
+                     string_descriptor->header.bLength - 4,
+                     fifo_tx_target);
   }
 }
 
M src/usb_device.c => src/usb_device.c +28 -49
  
@@ 142,28 142,6 @@ typedef struct {
   uint8_t endpoint_num;
 } packet_info_t;
 
-void usb_prepare_send_packet(USB_OTG_INEndpointTypeDef* in, uint8_t packet_count, uint8_t packet_size) {
-  if (ep enabled) {
-    // this is bad! Can't send the packet
-  }
-
-  in->DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | (packet_size << USB_OTG_DIEPTSIZ_XFRSIZ_Pos);
-}
-
-void usb_control_send(usb_device_t *device, uint8_t value) {
-    usb_prepare_send_packet(&device->in[0],
-                            1, sizeof(uint8_t));
-    usb_generic_send(&value,
-                     sizeof(uint8_t), &device->fifos[0].data[0]);
-    device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
-}
-
-void usb_control_send_ack(usb_device_t* device) {
-    usb_prepare_send_packet(&device->in[0],
-                            1, sizeof(uint8_t));
-    device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
-}
-
 void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
   switch (cmd->bRequest) {
   case USB_SETUP_GET_STATUS: {
@@ 197,10 175,8 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
       break;
     }
 
-    usb_prepare_send_packet(device, 1, size);
-    usb_generic_send(packet, size,
+    usb_generic_send(device->in, packet, size,
                      &device->fifos[0].data[0]);
-    device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
   }
     break;
   case USB_SETUP_GET_DESCRIPTOR: {
@@ 209,30 185,30 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
 
     switch (descriptor_type) {
     case DESCRIPTOR_DEVICE:
-      usb_prepare_send_packet(&device->in[0],
-                              1, sizeof(usb_device_descriptor_t));
-      usb_send_device_descriptor(&device->class.device_descriptor, &device->fifos[0].data[0]);
-      device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
+      usb_send_device_descriptor(device->in, &device->class.device_descriptor, &device->fifos[0].data[0]);
       break;
     case DESCRIPTOR_CONFIGURATION:
-      usb_prepare_send_packet(&device->in[0],
-                              1, sizeof(usb_configuration_descriptor_t));
-      usb_send_configuration_descriptor(&device->class.configuration_descriptor, &device->fifos[0].data[0]);
-      device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
+      usb_send_configuration_descriptor(device->in, &device->class.configuration_descriptor, &device->fifos[0].data[0]);
       break;
-    case DESCRIPTOR_STRING:
+    case DESCRIPTOR_STRING: {
+      if (descriptor_index == 0) {
+        usb_send_string_descriptor_zero(device->in, &device->class.string_descriptor_zero, &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], &device->fifos[0].data[0]);
+      }
+    }
+      break;
+    case DESCRIPTOR_INTERFACE:
+      usb_send_interface_descriptor(device->in, &device->class.interface_descriptor, &device->fifos[0].data[0]);
       break;
     case DESCRIPTOR_ENDPOINT:
-      usb_prepare_send_packet(&device->in[0],
-                              1, sizeof(usb_endpoint_descriptor_t));
-      usb_send_endpoint_descriptor(&device->class.endpoint_descriptors[descriptor_index], &device->fifos[0].data[0]);
-      device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
+      usb_send_endpoint_descriptor(device->in, &device->class.endpoint_descriptors[descriptor_index], &device->fifos[0].data[0]);
       break;
     case DESCRIPTOR_DEVICE_QUALIFIER:
-      usb_prepare_send_packet(&device->in[0],
-                              1, sizeof(usb_device_qualifier_t));
-      usb_send_device_qualifier_descriptor(&device->class.device_qualifier, &device->fifos[0].data[0]);
-      device->in[0].DIEPCTL |= USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA;
+      usb_send_device_qualifier_descriptor(device->in, &device->class.device_qualifier, &device->fifos[0].data[0]);
       break;
     case DESCRIPTOR_OTHER_SPEED_CONFIGURATION:
     case DESCRIPTOR_INTERFACE_POWER:
@@ 247,11 223,14 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
     if (device->state != ENUMERATED) {
       value = 0;
     }
-    usb_control_send(device, value);
+    usb_generic_send(device->in, &value, sizeof(value), &device->fifos[0].data[0]);
   }
     break;
   case USB_SETUP_GET_INTERFACE: {
-    usb_control_send(device, device->class.interface_descriptor.bAlternateSetting);
+    usb_generic_send(device->in,
+                     &device->class.interface_descriptor.bAlternateSetting,
+                     sizeof(uint8_t),
+                     &device->fifos[0].data[0]);
   }
     break;
   case USB_SETUP_SET_ADDRESS:
@@ 259,18 238,18 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
                        cmd->wValue,
                        USB_OTG_DCFG_DAD_Pos,
                        0x7F);
-    usb_control_send_ack(device);
+    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
     device->state = SET_ADDRESS_RCVD;
     break;
   case USB_SETUP_SET_CONFIGURATION: {
     if (cmd->wValue == 0) {
       device->state = SET_ADDRESS_RCVD;
-      usb_control_send_ack(device);
+      usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
 
       // TODO disable endpoints
     } else if (cmd->wValue == device->class.configuration_descriptor.bConfigurationValue) {
       device->state = ENUMERATED;
-      usb_control_send_ack(device);
+      usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
 
       // TODO setup endpoints
     } else {
@@ 292,7 271,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
       break;
     }
 
-    usb_control_send_ack(device);
+    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
     break;
   case USB_SETUP_SET_FEATURE:
     switch (cmd->wValue) {
@@ 308,7 287,7 @@ void usb_handle_setup(usb_device_t *device, usb_setup_command_t* cmd) {
       break;
     }
 
-    usb_control_send_ack(device);
+    usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]);
     break;
   case USB_SETUP_SET_DESCRIPTOR:
   case USB_SETUP_SET_INTERFACE: