]> mj.ucw.cz Git - home-hw.git/commitdiff
test-display2: No longer needed
authorMartin Mares <mj@ucw.cz>
Sun, 14 May 2023 12:40:09 +0000 (14:40 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 14 May 2023 12:40:09 +0000 (14:40 +0200)
test-display2/Makefile [deleted file]
test-display2/README [deleted file]
test-display2/config.h [deleted file]
test-display2/host/Makefile [deleted file]
test-display2/host/test.c [deleted file]
test-display2/main.c [deleted file]

diff --git a/test-display2/Makefile b/test-display2/Makefile
deleted file mode 100644 (file)
index 8063dfb..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-ROOT=..
-BINARY=test
-OBJS=main.o
-LIB_OBJS=util-debug.o
-
-WITH_BOOT_LOADER=1
-WITH_DFU_FLASH=1
-DFU_ARGS=-d 4242:0007
-# DFU_ARGS=-d 4242:0008
-
-include $(ROOT)/mk/bluepill.mk
diff --git a/test-display2/README b/test-display2/README
deleted file mode 100644 (file)
index 19ac2c9..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-Assignment of peripherals and pins
-==================================
-
-I2C1   display
-USART1 debugging
-
-
-                          Blue Pill pinout
-                       +--------------------+
-                       | VBATT         3.3V |
-BluePill LED           | PC13           GND |  display power
-                       | PC14            5V |  display power (white side of connector)
-                       | PC15           PB9 |
-                       | PA0            PB8 |
-                       | PA1            PB7 |  SDA1 display (white side of connector)
-                       | PA2            PB6 |  SCL1 display
-                       | PA3            PB5 |
-                       | PA4            PB4 |
-                       | PA5            PB3 |
-                       | PA6           PA15 |
-                       | PA7           PA12 |
-                       | PB0           PA11 |
-                       | PB1           PA10 |  RXD1 - debugging console
-                       | PB10           PA9 |  TXD1 - debugging console
-                       | PB11           PA8 |  SFH5110 output (white side of connector)
-                       | RESET         PB15 |
-                       | 3.3 V         PB14 |
-                       | GND           PB13 |
-                       | GND           PB12 |
-                       +--------------------+
diff --git a/test-display2/config.h b/test-display2/config.h
deleted file mode 100644 (file)
index 50926f9..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- *     Workshop Clock -- Configuration
- *
- *     (c) 2020-2023 Martin Mareš <mj@ucw.cz>
- */
-
-// Processor clock
-
-#define CPU_CLOCK_MHZ 72
-
-// Debugging port
-
-#define DEBUG_USART USART1
-#define DEBUG_LED_BLUEPILL
-
-// Testing of IR receiver
-
-#undef IR_TEST
diff --git a/test-display2/host/Makefile b/test-display2/host/Makefile
deleted file mode 100644 (file)
index 5e6e00c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw)
-UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw)
-
-CFLAGS=-std=gnu99 -O2 -Wall -Wextra -Wno-parentheses $(UCWCF)
-LDLIBS=-lusb-1.0 $(UCWLF)
-
-all: test
-
-test: test.c
-
-clean:
-       rm -f test
diff --git a/test-display2/host/test.c b/test-display2/host/test.c
deleted file mode 100644 (file)
index cad8474..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#include <ucw/lib.h>
-#include <ucw/unaligned.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <time.h>
-#include <libusb-1.0/libusb.h>
-
-struct libusb_context *usb_ctxt;
-struct libusb_device_handle *devh;
-
-static libusb_device *find_device(void)
-{
-  libusb_device **devlist;
-  ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
-  if (devn < 0)
-    {
-      fprintf(stderr, "Cannot enumerate USB devices: error %d\n", (int) devn);
-      exit(1);
-    }
-
-  for (ssize_t i=0; i<devn; i++)
-    {
-      struct libusb_device_descriptor desc;
-      libusb_device *dev = devlist[i];
-      if (!libusb_get_device_descriptor(dev, &desc))
-       {
-         if (desc.idVendor == 0x4242 && desc.idProduct == 0x0007)
-           {
-             printf("Found device at usb%d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev));
-             // FIXME: Free device list
-             return dev;
-           }
-       }
-    }
-
-  libusb_free_device_list(devlist, 1);
-  fprintf(stderr, "Device not found\n");
-  exit(1);
-}
-
-int main(void)
-{
-  int err;
-  if (err = libusb_init(&usb_ctxt))
-    {
-      fprintf(stderr, "Cannot initialize libusb: error %d\n", err);
-      exit(1);
-    }
-  // libusb_set_debug(usb_ctxt, 3);
-
-  libusb_device *dev = find_device();
-
-  if (err = libusb_open(dev, &devh))
-    {
-      fprintf(stderr, "Cannot open device: error %d\n", err);
-      exit(1);
-    }
-  libusb_reset_device(devh);
-  if (err = libusb_claim_interface(devh, 0))
-    {
-      fprintf(stderr, "Cannot claim interface: error %d\n", err);
-      exit(1);
-    }
-
-  for (;;)
-    {
-      time_t t = time(NULL);
-      struct tm *tm = localtime(&t);
-
-      unsigned char req[8] = {
-       tm->tm_hour / 10,
-       tm->tm_hour % 10,
-       tm->tm_min / 10,
-       tm->tm_min % 10,
-       (tm->tm_sec % 2 ? 0xff : 0),
-      };
-      int transferred;
-      if (err = libusb_bulk_transfer(devh, 0x01, req, 5, &transferred, 1000))
-       die("Transfer failed: error %d", err);
-      // printf("Transferred %d bytes\n", transferred);
-
-#if 0
-      unsigned char resp[64];
-      int received;
-      if (err = libusb_bulk_transfer(devh, 0x82, resp, 64, &received, 2000))
-       die("Receive failed: error %d", err);
-      // printf("Received %d bytes\n", received);
-      if (received >= 12)
-       {
-         int t = get_u32_be(resp);
-         int p = get_u32_be(resp + 4);
-         uint cnt = get_u32_be(resp + 8);
-         msg(L_INFO, "Temperature %d ddegC, pressure %d Pa, cnt %u", t, p, cnt);
-       }
-#endif
-
-      sleep(1);
-    }
-
-  return 0;
-}
diff --git a/test-display2/main.c b/test-display2/main.c
deleted file mode 100644 (file)
index 50ce567..0000000
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- *     Workshop Clock
- *
- *     (c) 2020-2023 Martin Mareš <mj@ucw.cz>
- */
-
-#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/usart.h>
-#include <libopencm3/stm32/i2c.h>
-#include <libopencm3/stm32/timer.h>
-#include <libopencm3/usb/dfu.h>
-#include <libopencm3/usb/usbd.h>
-
-#include <string.h>
-
-static void ep82_send(u32 key_code);
-
-/*** Hardware init ***/
-
-static void clock_init(void)
-{
-       rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
-
-       rcc_periph_clock_enable(RCC_GPIOA);
-       rcc_periph_clock_enable(RCC_GPIOB);
-       rcc_periph_clock_enable(RCC_GPIOC);
-       rcc_periph_clock_enable(RCC_I2C1);
-       rcc_periph_clock_enable(RCC_USART1);
-       rcc_periph_clock_enable(RCC_USB);
-       rcc_periph_clock_enable(RCC_TIM1);
-
-       rcc_periph_reset_pulse(RST_GPIOA);
-       rcc_periph_reset_pulse(RST_GPIOB);
-       rcc_periph_reset_pulse(RST_GPIOC);
-       rcc_periph_reset_pulse(RST_I2C1);
-       rcc_periph_reset_pulse(RST_USART1);
-       rcc_periph_reset_pulse(RST_USB);
-       rcc_periph_reset_pulse(RST_TIM1);
-}
-
-static void gpio_init(void)
-{
-       // PA9 = TXD1 for debugging console
-       // PA10 = RXD1 for debugging console
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
-       gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
-
-       // PC13 = BluePill LED
-       gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
-       gpio_clear(GPIOC, GPIO13);
-
-       // PB7 = SDA for display controller
-       // PB6 = SCL for display controller
-       gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO6 | GPIO7);
-
-       // PA8 = SFH5110 output (5V tolerant) connected to TIM1_CH1
-       gpio_set_mode(GPIOC, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO8);
-}
-
-static void usart_init(void)
-{
-       usart_set_baudrate(USART1, 115200);
-       usart_set_databits(USART1, 8);
-       usart_set_stopbits(USART1, USART_STOPBITS_1);
-       usart_set_mode(USART1, USART_MODE_TX);
-       usart_set_parity(USART1, USART_PARITY_NONE);
-       usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
-
-       usart_enable(USART1);
-}
-
-/*** System ticks ***/
-
-static volatile u32 ms_ticks;
-
-void sys_tick_handler(void)
-{
-       ms_ticks++;
-}
-
-static void tick_init(void)
-{
-       systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
-       systick_counter_enable();
-       systick_interrupt_enable();
-}
-
-static void delay_ms(uint ms)
-{
-       u32 start_ticks = ms_ticks;
-       while (ms_ticks - start_ticks < ms)
-               ;
-}
-
-/*** Display ***/
-
-/*
- *     Display digits:
- *
- *                ---- 40 ----
- *              |              |
- *              |              |
- *             20              80
- *              |              |
- *              |              |
- *                ---- 10 ----
- *              |              |
- *              |              |
- *             08              02
- *              |              |
- *              |              |
- *                ---- 04 ----
- *                                     (01)
- */
-
-static byte disp[4];
-static byte ctrl = 0x56;
-
-static const byte disp_font[] = {
-       [0] = 0xee,
-       [1] = 0x82,
-       [2] = 0xdc,
-       [3] = 0xd6,
-       [4] = 0xb2,
-       [5] = 0x76,
-       [6] = 0x7e,
-       [7] = 0xc2,
-       [8] = 0xfe,
-       [9] = 0xf6,
-       [10] = 0xea,
-       [11] = 0x3e,
-       [12] = 0x6c,
-       [13] = 0x9e,
-       [14] = 0x7c,
-       [15] = 0x78,
-};
-
-static void display_update(void)
-{
-       // debug_puts("Display update\n");
-
-       byte cmds[4];
-       cmds[0] = 0;
-       cmds[1] = ctrl;
-       cmds[2] = ((disp[1] & 0x88) >> 3) | ((disp[1] & 0x44) >> 1) | ((disp[1] & 0x22) << 1) | ((disp[1] & 0x11) << 3);
-       cmds[3] = disp[0];
-       i2c_transfer7(I2C1, 0x76/2, (byte *) cmds, sizeof(cmds), NULL, 0);
-
-       cmds[2] = ((disp[3] & 0x88) >> 3) | ((disp[3] & 0x44) >> 1) | ((disp[3] & 0x22) << 1) | ((disp[3] & 0x11) << 3);
-       cmds[3] = disp[2];
-       i2c_transfer7(I2C1, 0x70/2, (byte *) cmds, sizeof(cmds), NULL, 0);
-
-       // debug_puts("Update done\n");
-}
-
-static void display_init(void)
-{
-       debug_puts("I2C init\n");
-       i2c_peripheral_disable(I2C1);
-        i2c_set_speed(I2C1, i2c_speed_sm_100k, rcc_apb1_frequency / 1000000);
-        i2c_peripheral_enable(I2C1);
-
-       disp[0] = 0x10;
-       disp[1] = 0x10;
-       disp[2] = 0x10;
-       disp[3] = 0x10;
-       display_update();
-}
-
-/*** Infrared Remote Control ***/
-
-static void ir_init(void)
-{
-       debug_puts("IR init\n");
-
-       // TIM1 will measure pulses and spaces between them with 1μs resolution
-       timer_set_prescaler(TIM1, 71);          // 72 MHz / 72 = 1 MHz
-       timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
-       timer_set_period(TIM1, 65535);
-       timer_update_on_overflow(TIM1);
-
-       // IC1 will trigger on TI1 (TIM1_CH1) falling edge
-       timer_ic_set_input(TIM1, TIM_IC1, TIM_IC_IN_TI1);
-       // timer_ic_set_filter(TIM1, TIM_IC1, TIM_IC_OFF);
-       timer_set_oc_polarity_low(TIM1, TIM_OC1);       // OC functions affect IC, too
-
-       // IC2 will trigger on TI1 (TIM1_CH1) rising edge
-       timer_ic_set_input(TIM1, TIM_IC2, TIM_IC_IN_TI1);
-       timer_set_oc_polarity_high(TIM1, TIM_OC2);
-
-       // OC3 will trigger on a break longer than 50 ms
-       timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_ACTIVE);
-       timer_set_oc_value(TIM1, TIM_OC3, 30000);
-
-       // Program slave controller to reset the timer on IC1
-       timer_slave_set_trigger(TIM1, TIM_SMCR_TS_TI1FP1);
-       timer_slave_set_mode(TIM1, TIM_SMCR_SMS_RM);
-
-       // Request interrupts
-       timer_enable_irq(TIM1, TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE);
-       nvic_enable_irq(NVIC_TIM1_CC_IRQ);
-
-       // Enable ICs and OCs
-       timer_enable_oc_output(TIM1, TIM_OC1);
-       timer_enable_oc_output(TIM1, TIM_OC2);
-       timer_enable_oc_output(TIM1, TIM_OC3);
-
-       timer_enable_counter(TIM1);
-}
-
-// Circular queue of pulse durations
-#define IR_MAX_PULSES 32
-static u32 ir_pulses[IR_MAX_PULSES];   // Top 16 bits = mark, bottom 16 bits = space
-static u16 ir_last_pulse;
-static uint ir_pulses_rx, ir_pulses_tx;
-
-#define IR_INF 0xffff
-
-#define IR_MARK(x) (uint)((x) >> 16)
-#define IR_SPACE(x) (uint)((x) & 0xffff)
-
-static inline bool between(uint x, uint min, uint max)
-{
-       return x >= min && x <= max;
-}
-
-static void ir_record_pulse(uint mark, uint space)
-{
-       uint i = ir_pulses_tx;
-       ir_pulses_tx = (i + 1) % IR_MAX_PULSES;
-       if (ir_pulses_tx != ir_pulses_rx) {
-               ir_pulses[i] = (mark << 16) | space;
-       } else {
-               // Overflow detected
-               ir_pulses[i] = (IR_INF << 16) | IR_INF;
-       }
-}
-
-void tim1_cc_isr(void)
-{
-       if (TIM_SR(TIM1) & TIM_SR_CC1IF) {
-               TIM_SR(TIM1) &= ~TIM_SR_CC1IF;
-               u16 now = TIM_CCR1(TIM1);
-               if (ir_last_pulse) {
-                       ir_record_pulse(ir_last_pulse, now - ir_last_pulse);
-                       ir_last_pulse = 0;
-               }
-       }
-       if (TIM_SR(TIM1) & TIM_SR_CC2IF) {
-               TIM_SR(TIM1) &= ~TIM_SR_CC2IF;
-               ir_last_pulse = TIM_CCR2(TIM1);
-       }
-       if (TIM_SR(TIM1) & TIM_SR_CC3IF) {
-               TIM_SR(TIM1) &= ~TIM_SR_CC3IF;
-               if (ir_last_pulse) {
-                       ir_record_pulse(ir_last_pulse, IR_INF);
-                       ir_last_pulse = 0;
-               }
-       }
-}
-
-static u32 ir_get_pulse(void)
-{
-       u32 out = 0;
-
-       cm_disable_interrupts();
-       if (ir_pulses_rx != ir_pulses_tx) {
-               out = ir_pulses[ir_pulses_rx];
-               ir_pulses_rx = (ir_pulses_rx + 1) % IR_MAX_PULSES;
-       }
-       cm_enable_interrupts();
-       return out;
-}
-
-// Decoder for Onkyo RC-748S
-
-static u32 ir_blink_start;
-
-static void ir_decode(void)
-{
-       u32 pulse = ir_get_pulse();
-       if (!pulse)
-               return;
-
-       uint mark = IR_MARK(pulse);
-       uint space = IR_SPACE(pulse);
-
-#ifdef IR_TEST
-       debug_printf("IR: %d %d\n", mark, space);
-       return;
-#endif
-
-       static u16 ir_bits;
-       static u32 ir_code;
-#define IR_ERR 0xff
-
-       // debug_printf("IR(%d): %d %d\n", ir_bits, mark, space);
-
-       if (space == IR_INF) {
-               ir_bits = 0;
-       } else if (ir_bits == IR_ERR) {
-               // Error state
-       } else if (ir_bits == 0) {
-               // Start?
-               if (between(mark, 8900, 9200)) {
-                       if (between(space, 4200, 4600)) {
-                               ir_bits = 1;
-                               ir_code = 0;
-                       } else if (between(space, 2000, 2300)) {
-                               debug_printf("IR: => REP\n");
-                               ir_bits = IR_ERR;
-                       }
-               }
-       } else {
-               if (between(mark, 500, 700)) {
-                       ir_bits++;
-                       if (between(space, 400, 700)) {
-                               // 0
-                       } else if (between(space, 1500, 1800)) {
-                               // 1
-                               ir_code |= 1U << (33 - ir_bits);
-                       } else {
-                               ir_bits = IR_ERR;
-                       }
-                       if (ir_bits == 33) {
-                               debug_printf("IR: => %08x\n", (uint)ir_code);
-                               disp[3] |= 0x01;
-                               ir_blink_start = ms_ticks;
-                               display_update();
-                               ir_bits = IR_ERR;
-                               ep82_send(ir_code);
-                       }
-               } else {
-                       ir_bits = IR_ERR;
-               }
-       }
-}
-
-/*** USB ***/
-
-static usbd_device *usbd_dev;
-
-enum usb_string {
-       STR_MANUFACTURER = 1,
-       STR_PRODUCT,
-       STR_SERIAL,
-};
-
-static char usb_serial_number[13];
-
-static const char *usb_strings[] = {
-       "United Computer Wizards",
-       "Workshop Clock",
-       usb_serial_number,
-};
-
-static const struct usb_device_descriptor device = {
-       .bLength = USB_DT_DEVICE_SIZE,
-       .bDescriptorType = USB_DT_DEVICE,
-       .bcdUSB = 0x0200,
-       .bDeviceClass = 0xFF,
-       .bDeviceSubClass = 0,
-       .bDeviceProtocol = 0,
-       .bMaxPacketSize0 = 64,
-       .idVendor = 0x4242,
-       .idProduct = 0x0007,
-       .bcdDevice = 0x0000,
-       .iManufacturer = STR_MANUFACTURER,
-       .iProduct = STR_PRODUCT,
-       .iSerialNumber = STR_SERIAL,
-       .bNumConfigurations = 1,
-};
-
-static const struct usb_endpoint_descriptor endpoints[] = {{
-       // Bulk end-point for sending values to the display
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-       .bEndpointAddress = 0x01,
-       .bmAttributes = USB_ENDPOINT_ATTR_BULK,
-       .wMaxPacketSize = 64,
-       .bInterval = 1,
-}, {
-       // Bulk end-point for receiving remote control keys
-       .bLength = USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType = USB_DT_ENDPOINT,
-       .bEndpointAddress = 0x82,
-       .bmAttributes = USB_ENDPOINT_ATTR_BULK,
-       .wMaxPacketSize = 4,
-       .bInterval = 1,
-}};
-
-static const struct usb_interface_descriptor iface = {
-       .bLength = USB_DT_INTERFACE_SIZE,
-       .bDescriptorType = USB_DT_INTERFACE,
-       .bInterfaceNumber = 0,
-       .bAlternateSetting = 0,
-       .bNumEndpoints = 2,
-       .bInterfaceClass = 0xFF,
-       .bInterfaceSubClass = 0,
-       .bInterfaceProtocol = 0,
-       .iInterface = 0,
-       .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 = 2,
-       .bConfigurationValue = 1,
-       .iConfiguration = 0,
-       .bmAttributes = 0x80,
-       .bMaxPower = 50,        // multiplied by 2 mA
-       .interface = ifaces,
-};
-
-static bool usb_configured;
-static uint8_t usbd_control_buffer[64];
-
-static bool usb_tx_in_flight;
-static byte ep82_tx_buffer[4];
-
-static byte disp_alive;
-
-static void dfu_detach_complete(usbd_device *dev UNUSED, struct usb_setup_data *req UNUSED)
-{
-       // Reset to bootloader, which implements the rest of DFU
-       debug_printf("Switching to DFU\n");
-       debug_flush();
-       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 ep01_cb(usbd_device *dev, uint8_t ep UNUSED)
-{
-       // We received a frame from the USB host
-       byte buf[8];
-       uint len = usbd_ep_read_packet(dev, 0x01, buf, 8);
-       debug_printf("USB: Host sent %u bytes\n", len);
-       if (len >= 5) {
-               for (uint i=0; i<4; i++) {
-                       disp[i] &= 0x01;
-                       if (buf[i] < 16)
-                               disp[i] |= disp_font[buf[i]];
-               }
-               disp[1] &= 0xfe;
-               if (buf[4])
-                       disp[1] |= 0x01;
-               display_update();
-               disp_alive = 10;
-       }
-}
-
-static void ep82_send(u32 key_code)
-{
-       if (usb_tx_in_flight) {
-               debug_printf("USB: Send overrun!\n");
-               return;
-       }
-
-       debug_printf("USB: Sending key to host\n");
-       put_u32_be(ep82_tx_buffer, key_code);
-       usbd_ep_write_packet(usbd_dev, 0x82, ep82_tx_buffer, 4);
-       usb_tx_in_flight = true;
-}
-
-static void ep82_cb(usbd_device *dev UNUSED, uint8_t ep UNUSED)
-{
-       // We completed sending a frame to the USB host
-       usb_tx_in_flight = false;
-       debug_printf("USB: Key sending complete\n");
-}
-
-static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED)
-{
-       usbd_register_control_callback(
-               dev,
-               USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
-               USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
-               dfu_control_cb);
-       usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, ep01_cb);
-       usbd_ep_setup(dev, 0x82, USB_ENDPOINT_ATTR_BULK, 4, ep82_cb);
-       usb_configured = true;
-}
-
-static void reset_cb(void)
-{
-       debug_printf("USB: Reset\n");
-       usb_configured = false;
-}
-
-static volatile bool usb_event_pending;
-
-void usb_lp_can_rx0_isr(void)
-{
-       /*
-        *  We handle USB in the main loop to avoid race conditions between
-        *  USB interrupts and other code. However, we need an interrupt to
-        *  up the main loop from sleep.
-        *
-        *  We set up only the low-priority ISR, because high-priority ISR handles
-        *  only double-buffered bulk transfers and isochronous transfers.
-        */
-       nvic_disable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
-       usb_event_pending = 1;
-}
-
-static void usb_init(void)
-{
-       // Simulate USB disconnect
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
-       gpio_clear(GPIOA, GPIO11 | GPIO12);
-       delay_ms(100);
-
-       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);
-       usb_event_pending = 1;
-}
-
-/*** Main ***/
-
-int main(void)
-{
-       clock_init();
-       gpio_init();
-       usart_init();
-
-       tick_init();
-       desig_get_unique_id_as_dfu(usb_serial_number);
-
-       debug_printf("Hello, world!\n");
-
-       usb_init();
-       display_init();
-       ir_init();
-
-       u32 last_blink = 0;
-
-       for (;;) {
-               if (ms_ticks - last_blink >= 500) {
-                       debug_led_toggle();
-                       last_blink = ms_ticks;
-                       if (disp_alive) {
-                               if (!--disp_alive) {
-                                       disp[0] = (disp[0] & 0x01) | 0x10;
-                                       disp[1] = (disp[1] & 0x01) | 0x10;
-                                       disp[2] = (disp[2] & 0x01) | 0x10;
-                                       disp[3] = (disp[3] & 0x01) | 0x10;
-                               }
-                       }
-                       display_update();
-               }
-
-               if ((disp[3] & 0x01) && ms_ticks - ir_blink_start >= 100) {
-                       disp[3] &= 0xfe;
-                       display_update();
-               }
-
-               ir_decode();
-
-               if (usb_event_pending) {
-                       usbd_poll(usbd_dev);
-                       usb_event_pending = 0;
-                       nvic_clear_pending_irq(NVIC_USB_LP_CAN_RX0_IRQ);
-                       nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
-               }
-
-               wait_for_interrupt();
-       }
-
-       return 0;
-}