]> mj.ucw.cz Git - home-hw.git/commitdiff
First bits of my own high-level USB stack
authorMartin Mares <mj@ucw.cz>
Sat, 23 Jun 2018 22:41:19 +0000 (00:41 +0200)
committerMartin Mares <mj@ucw.cz>
Sat, 23 Jun 2018 22:41:19 +0000 (00:41 +0200)
Inc/usb.h [new file with mode: 0644]
Makefile
Src/main.c
Src/usb.c [new file with mode: 0644]

diff --git a/Inc/usb.h b/Inc/usb.h
new file mode 100644 (file)
index 0000000..ec7cc23
--- /dev/null
+++ b/Inc/usb.h
@@ -0,0 +1,167 @@
+#define USB_SELF_POWERED
+
+typedef unsigned int uint;
+typedef uint8_t byte;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+/*** USB state structure ***/
+
+struct usb_endpoint {
+  u32 max_packet_size;
+  u32 total_length;
+  u32 remaining_length;
+};
+
+struct usb {
+  PCD_HandleTypeDef *hpcd;
+  byte state;                  // USB_STATE_xxx
+  byte pre_suspend_state;
+  byte address;
+  byte speed;                  // PCD_SPEED_xxx
+  byte config;
+  byte remote_wakeup;
+  byte ep0_state;              // USB_EP0_xxx
+  byte ep0_data_len;
+  struct usb_endpoint ep_in[15];
+  struct usb_endpoint ep_out[15];
+  byte status_buf[2];
+};
+
+void usb_init(struct usb *usb, PCD_HandleTypeDef *hpcd);
+
+enum usb_device_state {
+  USB_STATE_DEFAULT,
+  USB_STATE_ADDRESSED,
+  USB_STATE_CONFIGURED,
+  USB_STATE_SUSPENDED,
+};
+
+enum usb_ep0_state {
+  USB_EP0_IDLE,
+  USB_EP0_SETUP,
+  USB_EP0_DATA_IN,
+  USB_EP0_DATA_OUT,
+  USB_EP0_STATUS_IN,
+  USB_EP0_STATUS_OUT,
+  USB_EP0_STALL,
+};
+
+/*** Constants from USB specs ***/
+
+#define USB_REQ_DIRECTION 0x80
+
+enum usb_req_type {
+  USB_REQ_TYPE_STANDARD = 0x00,
+  USB_REQ_TYPE_CLASS = 0x20,
+  USB_REQ_TYPE_VENDOR = 0x40,
+  USB_REQ_TYPE_MASK = 0x60,
+};
+
+enum usb_req_recipient {
+  USB_REQ_RECIPIENT_DEVICE = 0x00,
+  USB_REQ_RECIPIENT_INTERFACE = 0x01,
+  USB_REQ_RECIPIENT_ENDPOINT = 0x02,
+  USB_REQ_RECIPIENT_MASK = 0x1f,
+};
+
+enum usb_req_standard {
+  USB_REQ_GET_STATUS = 0x00,
+  USB_REQ_CLEAR_FEATURE = 0x01,
+  USB_REQ_SET_FEATURE = 0x03,
+  USB_REQ_SET_ADDRESS = 0x05,
+  USB_REQ_GET_DESCRIPTOR = 0x06,
+  USB_REQ_SET_DESCRIPTOR = 0x07,
+  USB_REQ_GET_CONFIGURATION = 0x08,
+  USB_REQ_SET_CONFIGURATION = 0x09,
+  USB_REQ_GET_INTERFACE = 0x0A,
+  USB_REQ_SET_INTERFACE = 0x0B,
+  USB_REQ_SYNCH_FRAME = 0x0C,
+};
+
+enum usb_desc_type {
+  USB_DESC_TYPE_DEVICE = 1,
+  USB_DESC_TYPE_CONFIGURATION = 2,
+  USB_DESC_TYPE_STRING = 3,
+  USB_DESC_TYPE_INTERFACE = 4,
+  USB_DESC_TYPE_ENDPOINT = 5,
+  USB_DESC_TYPE_DEVICE_QUALIFIER = 6,
+  USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION = 7,
+  USB_DESC_TYPE_BOS = 0x0F,
+};
+
+enum usb_dev_status {
+  USB_DEV_STATUS_REMOTE_WAKEUP = 2,
+  USB_DEV_STATUS_SELF_POWERED = 1,
+};
+
+enum usb_feature {
+  USB_FEATURE_EP_HALT = 0,
+  USB_FEATURE_REMOTE_WAKEUP = 1,
+  USB_FEATURE_TEST_MODE = 2,
+};
+
+#define USB_HS_MAX_PACKET_SIZE 512
+#define USB_FS_MAX_PACKET_SIZE 64
+#define USB_MAX_EP0_SIZE 64
+
+enum usb_ep_type {
+  USB_EP_TYPE_CTRL = 0,
+  USB_EP_TYPE_ISOC = 1,
+  USB_EP_TYPE_BULK = 2,
+  USB_EP_TYPE_INTR = 3,
+};
+
+/*** Wrappers around HAL routines ***/
+
+// Wrappers return HAL_OK / HAL_ERROR / HAL_BUSY / HAL_TIMEOUT
+
+static inline HAL_StatusTypeDef usb_ep_open(struct usb *usb, byte ep_addr, byte ep_type, byte ep_max_size)
+{
+  return HAL_PCD_EP_Open(usb->hpcd, ep_addr, ep_max_size, ep_type);
+}
+
+static inline HAL_StatusTypeDef usb_ep_close(struct usb *usb, byte ep_addr)
+{
+  return HAL_PCD_EP_Close(usb->hpcd, ep_addr);
+}
+
+static inline HAL_StatusTypeDef usb_ep_flush(struct usb *usb, byte ep_addr)
+{
+  return HAL_PCD_EP_Flush(usb->hpcd, ep_addr);
+}
+
+static inline HAL_StatusTypeDef usb_ep_stall(struct usb *usb, byte ep_addr)
+{
+  return HAL_PCD_EP_SetStall(usb->hpcd, ep_addr);
+}
+
+static inline HAL_StatusTypeDef usb_ep_unstall(struct usb *usb, byte ep_addr)
+{
+  return HAL_PCD_EP_ClrStall(usb->hpcd, ep_addr);
+}
+
+static inline int usb_ep_is_stalled(struct usb *usb, byte ep_addr)
+{
+  return ((ep_addr & 0x80) ? usb->hpcd->IN_ep : usb->hpcd->OUT_ep) [ep_addr & 0x7f].is_stall;
+}
+
+static inline HAL_StatusTypeDef usb_ep_transmit(struct usb *usb, byte ep_addr, byte *buf, u32 size)
+{
+  return HAL_PCD_EP_Transmit(usb->hpcd, ep_addr, buf, size);
+}
+
+static inline HAL_StatusTypeDef usb_ep_receive(struct usb *usb, byte ep_addr, byte *buf, u32 size)
+{
+  return HAL_PCD_EP_Receive(usb->hpcd, ep_addr, buf, size);
+}
+
+static inline u32 usb_ep_received_size(struct usb *usb, byte ep_addr)
+{
+  return HAL_PCD_EP_GetRxCount(usb->hpcd, ep_addr);
+}
index 5696535c5b52c8393e9b7ac6588aa655256e8938..7033be37c04df1df485351440be9dae11036e9dd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,7 @@ BUILD_DIR = build
 C_SOURCES =  \
 /aux/misc/stm/F1-package/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_exti.c \
 Src/main.c \
+Src/usb.c \
 /aux/misc/stm/F1-package/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_ll_rcc.c \
 /Src/system_stm32f1xx.c \
 Src/stm32f1xx_it.c \
index 22395ad86d356881ca7fa3e3b3ef1de5dc3ec1b4..9d3dbb8905792a7ba4634cfea9b5e7177b88bb3f 100644 (file)
@@ -39,6 +39,7 @@
 /* Includes ------------------------------------------------------------------*/
 #include "main.h"
 #include "stm32f1xx_hal.h"
+#include "usb.h"
 
 /* USER CODE BEGIN Includes */
 
@@ -50,7 +51,7 @@ PCD_HandleTypeDef hpcd_USB_FS;
 
 /* USER CODE BEGIN PV */
 /* Private variables ---------------------------------------------------------*/
-USBD_HandleTypeDef USBD_Device;
+struct usb usb;
 
 /* USER CODE END PV */
 
@@ -68,6 +69,168 @@ static void MX_USB_PCD_Init(void);
 
 /* USER CODE BEGIN 0 */
 
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
+
+void semi_put_char(char c)
+{
+  // This is tricky, we need to work around GCC bugs
+  volatile char cc = c;
+  asm volatile (
+    "mov r0, #0x03\n"   /* SYS_WRITEC */
+    "mov r1, %[msg]\n"
+    "bkpt #0xAB\n"
+    :
+    : [msg] "r" (&cc)
+    : "r0", "r1"
+  );
+}
+
+void semi_write_string(char *c)
+{
+  asm volatile (
+    "mov r0, #0x04\n"   /* SYS_WRITE0 */
+    "mov r1, %[msg]\n"
+    "bkpt #0xAB\n"
+    :
+    : [msg] "r" (c)
+    : "r0", "r1"
+  );
+}
+
+void debug_putc(int c)
+{
+  static char debug_buf[16];
+  static int debug_i;
+  debug_buf[debug_i++] = c;
+  if (c == '\n' || debug_i >= sizeof(debug_buf) - 1)
+    {
+      debug_buf[debug_i] = 0;
+      semi_write_string(debug_buf);
+      debug_i = 0;
+    }
+}
+
+void debug_puts(const char *s)
+{
+  while (*s)
+    debug_putc(*s++);
+}
+
+enum printf_flags {
+  PF_ZERO_PAD = 1,
+  PF_SIGNED = 2,
+  PF_NEGATIVE = 4,
+  PF_UPPERCASE = 8,
+  PF_LEFT = 16,
+};
+
+static void printf_string(const char *s, uint width, uint flags)
+{
+  uint len = strlen(s);
+  uint pad = (len < width) ? width - len : 0;
+  char pad_char = (flags & PF_ZERO_PAD) ? '0' : ' ';
+
+  if (flags & PF_LEFT)
+    debug_puts(s);
+  while (pad--)
+    debug_putc(pad_char);
+  if (!(flags & PF_LEFT))
+    debug_puts(s);
+}
+
+static void printf_number(uint i, uint width, uint flags, uint base)
+{
+  char buf[16];
+  char *w = buf + sizeof(buf);
+
+  if (flags & PF_SIGNED)
+    {
+      if ((int) i < 0)
+       {
+         i = - (int) i;
+         flags |= PF_NEGATIVE;
+       }
+    }
+
+  *--w = 0;
+  do
+    {
+      uint digit = i % base;
+      if (digit < 10)
+       *--w = '0' + digit;
+      else
+       *--w = ((flags & PF_UPPERCASE) ? 'A' : 'a') + digit - 10;
+      i /= base;
+    }
+  while (i);
+
+  if (flags & PF_NEGATIVE)
+    *--w = '-';
+
+  printf_string(w, width, flags);
+}
+
+void debug_printf(const char *fmt, ...)
+{
+  va_list args;
+  va_start(args, fmt);
+
+  while (*fmt)
+    {
+      int c = *fmt++;
+      if (c != '%')
+       {
+         debug_putc(c);
+         continue;
+       }
+
+      uint width = 0;
+      uint flags = 0;
+
+      if (*fmt == '-')
+       {
+         fmt++;
+         flags |= PF_LEFT;
+       }
+
+      if (*fmt == '0')
+       {
+         fmt++;
+         flags |= PF_ZERO_PAD;
+       }
+
+      while (*fmt >= '0' && *fmt <= '9')
+       width = 10*width + *fmt++ - '0';
+
+      c = *fmt++;
+      switch (c)
+       {
+       case 'd':
+         printf_number(va_arg(args, int), width, flags | PF_SIGNED, 10);
+         break;
+       case 'u':
+         printf_number(va_arg(args, int), width, flags, 10);
+         break;
+       case 'X':
+         flags |= PF_UPPERCASE;
+         // fall-thru
+       case 'x':
+         printf_number(va_arg(args, int), width, flags, 16);
+         break;
+       case 's':
+         printf_string(va_arg(args, char *), width, flags);
+         break;
+       default:
+         debug_putc(c);
+         continue;
+       }
+    }
+
+  va_end(args);
+}
+
 /* USER CODE END 0 */
 
 /**
@@ -103,17 +266,20 @@ int main(void)
   MX_I2C2_Init();
   MX_USB_PCD_Init();
   /* USER CODE BEGIN 2 */
+  usb_init(&usb, &hpcd_USB_FS);
 
   /* USER CODE END 2 */
 
   /* Infinite loop */
   /* USER CODE BEGIN WHILE */
+  int cnt = 0;
   while (1)
   {
     LL_GPIO_SetOutputPin(LED_GPIO_Port, LED_Pin);
-    LL_mDelay(1000);
+    LL_mDelay(500);
     LL_GPIO_ResetOutputPin(LED_GPIO_Port, LED_Pin);
-    LL_mDelay(1000);
+    LL_mDelay(500);
+    debug_printf("Counter = %d\n", cnt++);
 
   /* USER CODE END WHILE */
 
@@ -263,7 +429,6 @@ static void MX_I2C2_Init(void)
 /* USB init function */
 static void MX_USB_PCD_Init(void)
 {
-
   hpcd_USB_FS.Instance = USB;
   hpcd_USB_FS.Init.dev_endpoints = 8;
   hpcd_USB_FS.Init.speed = PCD_SPEED_FULL;
@@ -275,7 +440,6 @@ static void MX_USB_PCD_Init(void)
   {
     _Error_Handler(__FILE__, __LINE__);
   }
-
 }
 
 /** Configure pins as 
diff --git a/Src/usb.c b/Src/usb.c
new file mode 100644 (file)
index 0000000..434408d
--- /dev/null
+++ b/Src/usb.c
@@ -0,0 +1,507 @@
+#include "stm32f1xx.h"
+#include "stm32f1xx_hal.h"
+
+#include "usb.h"
+
+void _Error_Handler(char * file, int line);    // FIXME
+
+void usb_init(struct usb *usb, PCD_HandleTypeDef *hpcd)
+{
+  usb->hpcd = hpcd;
+  usb->state = USB_STATE_DEFAULT;
+  usb->ep0_state = USB_EP0_IDLE;
+
+  HAL_PCDEx_PMAConfig(hpcd, 0x00, PCD_SNG_BUF, 0x18);
+  HAL_PCDEx_PMAConfig(hpcd, 0x80, PCD_SNG_BUF, 0x58);
+
+  HAL_PCD_Start(hpcd);
+}
+
+static inline uint get_u16(byte *p)
+{
+  return (p[1] << 8) | p[0];
+}
+
+static inline void put_u16(byte *p, u16 x)
+{
+  p[0] = x;
+  p[1] = x >> 8;
+}
+
+#if 0  // FIXME
+static struct usb_endpoint *ep_by_addr(struct usb *usb, byte ep_addr)
+{
+  return ((ep_addr & 0x80) ? usb->ep_in : usb->ep_out) + (ep_addr & 0x7f);
+}
+#endif
+
+static void usb_ctl_send_status(struct usb *usb)
+{
+  usb->ep0_state = USB_EP0_STATUS_IN;
+  usb_ep_transmit(usb, 0x00, NULL, 0);
+}
+
+#if 0  // FIXME
+static void usb_ctl_recv_status(struct usb *usb)
+{
+  usb->ep0_state = USB_EP0_STATUS_OUT;
+  usb_ep_receive(usb, 0x00, NULL, 0);
+}
+#endif
+
+static void usb_ctl_send_data(struct usb *usb, byte *data, uint len)
+{
+  usb->ep0_state = USB_EP0_DATA_IN;
+  usb->ep_in[0].total_length = len;
+  usb->ep_in[0].remaining_length = len;
+  usb_ep_transmit(usb, 0x00, data, len);
+}
+
+static void usb_ctl_send_byte(struct usb *usb, byte data)
+{
+  usb->status_buf[0] = data;
+  usb_ctl_send_data(usb, usb->status_buf, 1);
+}
+
+static void usb_ctl_send_u16(struct usb *usb, u16 data)
+{
+  put_u16(usb->status_buf, data);
+  usb_ctl_send_data(usb, usb->status_buf, 2);
+}
+
+static void usb_ctl_error(struct usb *usb)
+{
+  usb_ep_stall(usb, 0x00);
+  usb_ep_stall(usb, 0x80);
+}
+
+struct setup_request {
+  byte bmRequest;
+  byte bRequest;
+  u16 wValue;
+  u16 wIndex;
+  u16 wLength;
+};
+
+static void usb_ctl_setup_error(struct usb *usb, struct setup_request *setup)
+{
+  usb_ep_stall(usb, setup->bmRequest & USB_REQ_DIRECTION);
+}
+
+static void dev_get_status(struct usb *usb, struct setup_request *setup)
+{
+  if ((usb->state != USB_STATE_ADDRESSED && usb->state != USB_STATE_CONFIGURED) ||
+      setup->wValue || setup->wIndex || setup->wLength != 2)
+    return usb_ctl_error(usb);
+
+  uint stat = 0;
+#ifdef USB_SELF_POWERED
+  stat |= USB_DEV_STATUS_SELF_POWERED;
+#endif
+  if (usb->remote_wakeup)
+    stat |= USB_DEV_STATUS_REMOTE_WAKEUP;
+  usb_ctl_send_u16(usb, stat);
+}
+
+static void dev_clear_feature(struct usb *usb, struct setup_request *setup)
+{
+  if (setup->wIndex || setup->wLength)
+    return usb_ctl_error(usb);
+
+  if (setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
+    {
+      usb->remote_wakeup = 0;
+      usb_ctl_send_status(usb);
+    }
+  else
+    usb_ctl_error(usb);
+}
+
+static void dev_set_feature(struct usb *usb, struct setup_request *setup)
+{
+  if (setup->wIndex || setup->wLength)
+    return usb_ctl_error(usb);
+
+  if (setup->wValue == USB_FEATURE_REMOTE_WAKEUP)
+    {
+      usb->remote_wakeup = 1;
+      usb_ctl_send_status(usb);
+    }
+  else
+    usb_ctl_error(usb);
+}
+
+static void dev_set_address(struct usb *usb, struct setup_request *setup)
+{
+  if (setup->wIndex || setup->wLength)
+    return usb_ctl_error(usb);
+
+  uint addr = setup->wValue & 0x7f;
+  if (usb->state == USB_STATE_CONFIGURED)
+    usb_ctl_error(usb);
+  else
+    {
+      usb->address = addr;
+      HAL_PCD_SetAddress(usb->hpcd, addr);
+      usb_ctl_send_status(usb);
+      usb->state = addr ? USB_STATE_ADDRESSED : USB_STATE_DEFAULT;
+    }
+}
+
+static void dev_get_descriptor(struct usb *usb, struct setup_request *setup)
+{
+}
+
+static void dev_get_configuration(struct usb *usb, struct setup_request *setup)
+{
+  if (setup->wValue || setup->wIndex || setup->wLength != 1)
+    return usb_ctl_error(usb);
+
+  switch (usb->state)
+    {
+    case USB_STATE_ADDRESSED:
+      usb_ctl_send_byte(usb, 0);
+      break;
+    case USB_STATE_CONFIGURED:
+      usb_ctl_send_byte(usb, usb->config);
+      break;
+    default:
+      usb_ctl_error(usb);
+    }
+}
+
+static void dev_set_configuration(struct usb *usb, struct setup_request *setup)
+{
+  byte cfg = setup->wValue & 0xff;
+
+  // FIXME: Support more configurations
+  if (cfg > 1 || setup->wIndex || setup->wLength)
+    return usb_ctl_error(usb);
+
+  switch (usb->state)
+    {
+    case USB_STATE_ADDRESSED:
+      if (cfg)
+       {
+         usb->config = cfg;
+         usb->state = USB_STATE_CONFIGURED;
+         // FIXME: Notify that the device was configured
+       }
+      usb_ctl_send_status(usb);
+      break;
+    case USB_STATE_CONFIGURED:
+      if (!cfg)
+       {
+         // Unconfiguring
+         usb->config = 0;
+         usb->state = USB_STATE_ADDRESSED;
+         // FIXME: Notify that the device was unconfigured
+       }
+      else if (cfg != usb->config)
+       {
+         usb->config = cfg;
+         // FIXME: Notify about configuration change
+       }
+      usb_ctl_send_status(usb);
+      break;
+    default:
+      usb_ctl_error(usb);
+    }
+}
+
+static void dev_setup(struct usb *usb, struct setup_request *setup)
+{
+  switch (setup->bRequest)
+    {
+    case USB_REQ_GET_STATUS:
+      return dev_get_status(usb, setup);
+    case USB_REQ_CLEAR_FEATURE:
+      return dev_clear_feature(usb, setup);
+    case USB_REQ_SET_FEATURE:
+      return dev_set_feature(usb, setup);
+    case USB_REQ_SET_ADDRESS:
+      return dev_set_address(usb, setup);
+    case USB_REQ_GET_DESCRIPTOR:
+      return dev_get_descriptor(usb, setup);
+    case USB_REQ_GET_CONFIGURATION:
+      return dev_get_configuration(usb, setup);
+    case USB_REQ_SET_CONFIGURATION:
+      return dev_set_configuration(usb, setup);
+    }
+
+  usb_ctl_setup_error(usb, setup);
+}
+
+static void intf_setup(struct usb *usb, struct setup_request *setup)
+{
+  byte intf = setup->wIndex & 0xff;
+
+  if (!intf)
+    {
+      // FIXME: Support more interfaces
+      usb_ctl_error(usb);
+      return;
+    }
+
+  switch (setup->bRequest)
+    {
+    case USB_REQ_GET_STATUS:
+      if (setup->wValue || setup->wLength != 2 || usb->state != USB_STATE_CONFIGURED)
+       usb_ctl_error(usb);
+      else
+       usb_ctl_send_u16(usb, 0);
+      return;
+
+    case USB_REQ_CLEAR_FEATURE:
+    case USB_REQ_SET_FEATURE:
+      // Interfaces have no standard features
+      return usb_ctl_error(usb);
+    }
+
+  usb_ctl_setup_error(usb, setup);
+}
+
+static void ep_setup(struct usb *usb, struct setup_request *setup)
+{
+  byte ep_addr = setup->wIndex & 0x8f;
+
+  switch (setup->bRequest)
+    {
+    case USB_REQ_GET_STATUS:
+      if (setup->wValue || setup->wLength != 2)
+       return usb_ctl_error(usb);
+      switch (usb->state)
+       {
+       case USB_STATE_ADDRESSED:
+         if (ep_addr & 0x7f)
+           usb_ctl_error(usb);
+         return;
+       case USB_STATE_CONFIGURED:
+         {
+           if (usb_ep_is_stalled(usb, ep_addr))
+             usb_ctl_send_u16(usb, 1);
+           else
+             usb_ctl_send_u16(usb, 0);
+           return;
+         }
+       default:
+         return usb_ctl_error(usb);
+       }
+      break;
+
+    case USB_REQ_SET_FEATURE:
+      if (setup->wLength)
+       return usb_ctl_error(usb);
+      switch (usb->state)
+       {
+       case USB_STATE_ADDRESSED:
+         if (ep_addr & 0x7f)
+           usb_ctl_error(usb);
+         return;
+       case USB_STATE_CONFIGURED:
+         if (setup->wValue == USB_FEATURE_EP_HALT)
+           {
+             if (ep_addr & 0x7f)
+               usb_ep_stall(usb, ep_addr);
+           }
+         usb_ctl_send_status(usb);
+         return;
+       default:
+         usb_ctl_error(usb);
+       }
+      break;
+
+    case USB_REQ_CLEAR_FEATURE:
+      if (setup->wLength)
+       return usb_ctl_error(usb);
+      switch (usb->state)
+       {
+       case USB_STATE_ADDRESSED:
+         if (ep_addr & 0x7f)
+           usb_ctl_error(usb);
+         return;
+       case USB_STATE_CONFIGURED:
+         if (setup->wValue == USB_FEATURE_EP_HALT)
+           {
+             if (ep_addr & 0x7f)
+               usb_ep_unstall(usb, ep_addr);
+           }
+         usb_ctl_send_status(usb);
+         return;
+       default:
+         usb_ctl_error(usb);
+       }
+      break;
+    }
+
+  usb_ctl_setup_error(usb, setup);
+}
+
+static void usb_handle_setup(struct usb *usb, struct setup_request *setup)
+{
+  usb->ep0_state = USB_EP0_SETUP;
+  usb->ep0_data_len = setup->wLength;
+
+  if ((setup->bmRequest & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
+    {
+      // FIXME: Class-specific and vendor-specific setup packets not supported
+      // FIXME: Check USB_STATE_CONFIGURED here
+      usb_ctl_setup_error(usb, setup);
+    }
+
+  switch (setup->bmRequest & USB_REQ_RECIPIENT_MASK)
+    {
+    case USB_REQ_RECIPIENT_DEVICE:
+      return dev_setup(usb, setup);
+    case USB_REQ_RECIPIENT_INTERFACE:
+      return intf_setup(usb, setup);
+    case USB_REQ_RECIPIENT_ENDPOINT:
+      return ep_setup(usb, setup);
+    }
+
+  usb_ctl_setup_error(usb, setup);
+}
+
+void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
+{
+  struct usb *usb = hpcd->pData;
+  byte *req = (byte *) hpcd->Setup;
+
+  struct setup_request setup = {
+    .bmRequest = req[0],
+    .bRequest = req[1],
+    .wValue = get_u16(req+2),
+    .wIndex = get_u16(req+4),
+    .wLength = get_u16(req+2),
+  };
+  usb_handle_setup(usb, &setup);
+}
+
+void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+  struct usb *usb = hpcd->pData;
+  struct usb_endpoint *ep = &usb->ep_out[epnum];
+  byte *data = hpcd->OUT_ep[epnum].xfer_buff;
+
+  if (!epnum)
+    {
+      if (usb->ep0_state != USB_EP0_DATA_OUT)
+       return;
+      if (ep->remaining_length > ep->max_packet_size)
+       {
+         ep->remaining_length -= ep->max_packet_size;
+         usb_ep_receive(usb, 0x00, data, MIN(ep->remaining_length, ep->max_packet_size));
+       }
+      else
+       {
+         if (usb->state == USB_STATE_CONFIGURED)
+           {
+             // FIXME: Handle incoming control packet
+           }
+         usb_ctl_send_status(usb);
+       }
+    }
+  else
+    {
+      if (usb->state == USB_STATE_CONFIGURED)
+       {
+         // FIXME: Custom data callback
+       }
+    }
+}
+
+void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+  struct usb *usb = hpcd->pData;
+  struct usb_endpoint *ep = &usb->ep_in[epnum];
+  byte *data = hpcd->IN_ep[epnum].xfer_buff;
+
+  if (!epnum)
+    {
+      if (usb->ep0_state != USB_EP0_DATA_IN)
+       return;
+      if (ep->remaining_length > ep->max_packet_size)
+       {
+         ep->remaining_length -= ep->max_packet_size;
+         usb_ep_transmit(usb, 0x00, data, ep->remaining_length);
+         usb_ep_receive(usb, 0x00, NULL, 0);
+       }
+      else if (ep->total_length && ep->total_length % ep->max_packet_size == 0 && ep->total_length < usb->ep0_data_len)
+       {
+         // Send an empty packet if total length is divisible by MTU
+         usb_ep_transmit(usb, 0x00, NULL, 0);
+         usb->ep0_data_len = 0;
+         usb_ep_receive(usb, 0x00, NULL, 0);
+       }
+      else
+       {
+         if (usb->state == USB_STATE_CONFIGURED)
+           {
+             // FIXME: Custom data callback
+           }
+       }
+    }
+  else
+    {
+      if (usb->state == USB_STATE_CONFIGURED)
+       {
+         // FIXME: Custom data callback
+       }
+    }
+}
+
+void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
+{
+  // FIXME
+}
+
+void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
+{ 
+  struct usb *usb = hpcd->pData;
+
+  usb->state = USB_STATE_DEFAULT;
+  usb->speed = hpcd->Init.speed;
+
+  usb_ep_open(usb, 0x00, USB_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
+  usb->ep_out[0].max_packet_size = USB_MAX_EP0_SIZE;
+
+  usb_ep_open(usb, 0x80, USB_EP_TYPE_CTRL, USB_MAX_EP0_SIZE);
+  usb->ep_in[0].max_packet_size = USB_MAX_EP0_SIZE;
+}
+
+void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
+{
+  struct usb *usb = hpcd->pData;
+
+  usb->pre_suspend_state = usb->state;
+  usb->state = USB_STATE_SUSPENDED;
+
+  if (hpcd->Init.low_power_enable)
+    SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
+}
+
+void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
+{
+  struct usb *usb = hpcd->pData;
+  usb->state = usb->pre_suspend_state;
+}
+
+void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+  // We do not support isochronous mode
+}
+
+void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
+{
+  // We do not support isochronous mode
+}
+
+void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
+{
+}
+
+void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
+{
+  struct usb *usb = hpcd->pData;
+  usb->state = USB_STATE_DEFAULT;
+}