+#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;
+}