]> mj.ucw.cz Git - home-hw.git/blobdiff - bsb/firmware/main.c
BSB: Switching to DFU mode
[home-hw.git] / bsb / firmware / main.c
index 778dc4516b15bcebe7841e65a5a31ce89c1e1814..c54d914fe770e183084cccb071b609f10538a5b5 100644 (file)
@@ -6,13 +6,16 @@
 
 #include "util.h"
 
+#include <libopencm3/cm3/cortex.h>
 #include <libopencm3/cm3/nvic.h>
 #include <libopencm3/cm3/systick.h>
+#include <libopencm3/cm3/scb.h>
 #include <libopencm3/stm32/rcc.h>
 #include <libopencm3/stm32/desig.h>
 #include <libopencm3/stm32/gpio.h>
 #include <libopencm3/stm32/timer.h>
 #include <libopencm3/stm32/usart.h>
+#include <libopencm3/usb/dfu.h>
 #include <libopencm3/usb/usbd.h>
 
 #include <string.h>
@@ -80,7 +83,7 @@ static void usart_init(void)
        usart_enable(USART1);
 }
 
-static const struct usb_device_descriptor dev = {
+static const struct usb_device_descriptor device = {
        .bLength = USB_DT_DEVICE_SIZE,
        .bDescriptorType = USB_DT_DEVICE,
        .bcdUSB = 0x0200,
@@ -126,16 +129,43 @@ static const struct usb_interface_descriptor iface = {
        .endpoint = endpoints,
 };
 
+static const struct usb_dfu_descriptor dfu_function = {
+       .bLength = sizeof(struct usb_dfu_descriptor),
+       .bDescriptorType = DFU_FUNCTIONAL,
+       .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
+       .wDetachTimeout = 255,
+       .wTransferSize = 1024,
+       .bcdDFUVersion = 0x0100,
+};
+
+static const struct usb_interface_descriptor dfu_iface = {
+       .bLength = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType = USB_DT_INTERFACE,
+       .bInterfaceNumber = 1,
+       .bAlternateSetting = 0,
+       .bNumEndpoints = 0,
+       .bInterfaceClass = 0xFE,
+       .bInterfaceSubClass = 1,
+       .bInterfaceProtocol = 1,
+       .iInterface = 0,
+
+       .extra = &dfu_function,
+       .extralen = sizeof(dfu_function),
+};
+
 static const struct usb_interface ifaces[] = {{
        .num_altsetting = 1,
        .altsetting = &iface,
+}, {
+       .num_altsetting = 1,
+       .altsetting = &dfu_iface,
 }};
 
 static const struct usb_config_descriptor config = {
        .bLength = USB_DT_CONFIGURATION_SIZE,
        .bDescriptorType = USB_DT_CONFIGURATION,
        .wTotalLength = 0,
-       .bNumInterfaces = 1,
+       .bNumInterfaces = 2,
        .bConfigurationValue = 1,
        .iConfiguration = 0,
        .bmAttributes = 0x80,
@@ -180,6 +210,25 @@ static enum usbd_request_return_codes control_cb(usbd_device *usbd_dev,
        return USBD_REQ_HANDLED;
 }
 
+static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED)
+{
+       // Reset to bootloader, which implements the rest of DFU
+       scb_reset_core();
+}
+
+static enum usbd_request_return_codes dfu_control_cb(usbd_device *dev UNUSED,
+       struct usb_setup_data *req,
+       uint8_t **buf UNUSED,
+       uint16_t *len UNUSED,
+       void (**complete)(usbd_device *dev, struct usb_setup_data *req))
+{
+       if (req->bmRequestType != 0x21 || req->bRequest != DFU_DETACH)
+               return USBD_REQ_NOTSUPP;
+
+       *complete = dfu_detach_complete;
+       return USBD_REQ_HANDLED;
+}
+
 static void ep81_cb(usbd_device *usbd_dev, uint8_t ep UNUSED)
 {
        byte buf[4];
@@ -191,6 +240,10 @@ static void ep81_cb(usbd_device *usbd_dev, uint8_t ep UNUSED)
 static void set_config_cb(usbd_device *usbd_dev, uint16_t wValue UNUSED)
 {
        usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, USB_REQ_TYPE_TYPE, control_cb);
+       usbd_register_control_callback(usbd_dev,
+               USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+               USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+               dfu_control_cb);
        usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, ep81_cb);
        usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
        ep81_cb(usbd_dev, 0);
@@ -218,7 +271,7 @@ int main(void)
        gpio_clear(GPIOA, GPIO11 | GPIO12);
        delay_ms(1000);
 
-       usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
+       usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &device, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
        usbd_register_reset_callback(usbd_dev, reset_cb);
        usbd_register_set_config_callback(usbd_dev, set_config_cb);
        u32 last_ds_step = 0;