X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=bsb%2Ffirmware%2Fmain.c;h=bb0c48e53bf140ae98ade0e46e9da73180760dbf;hb=37082aba8175e02e86271010ee372d2f34fc6413;hp=fda60d40f416e6c4f035a0dad88f585b8043282e;hpb=a692aefc44560a674b2057c6166c9d6b673cf779;p=home-hw.git diff --git a/bsb/firmware/main.c b/bsb/firmware/main.c index fda60d4..bb0c48e 100644 --- a/bsb/firmware/main.c +++ b/bsb/firmware/main.c @@ -5,17 +5,24 @@ */ #include "util.h" +#include "interface.h" +#include #include #include -#include // FIXME: Clean up +#include +#include +#include #include #include #include +#include #include #include +/*** Hardware init ***/ + static void clock_init(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); @@ -24,28 +31,52 @@ static void clock_init(void) rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOC); rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USART3); rcc_periph_clock_enable(RCC_USB); rcc_periph_reset_pulse(RST_GPIOA); rcc_periph_reset_pulse(RST_GPIOB); rcc_periph_reset_pulse(RST_GPIOC); rcc_periph_reset_pulse(RST_USART1); + rcc_periph_reset_pulse(RST_USART3); rcc_periph_reset_pulse(RST_USB); } static void gpio_init(void) { // PA9 = TXD1 for debugging console - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); - // 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); + + // PB0 = yellow LED*, PB1 = green LED* + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0 | GPIO1); + gpio_set(GPIOB, GPIO0 | GPIO1); + + // PB10 = TXD3 for BSB + // PB11 = RXD3 for BSB + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO10); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11); } +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) @@ -55,7 +86,7 @@ void sys_tick_handler(void) static void tick_init(void) { - systick_set_frequency(1000, 72000000); + systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000); systick_counter_enable(); systick_interrupt_enable(); } @@ -67,19 +98,394 @@ static void delay_ms(uint ms) ; } -static void usart_init(void) +/*** Random generator ***/ + +static u32 rng_state; + +static u32 random_u32(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_RX); - usart_set_parity(USART1, USART_PARITY_NONE); - usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + rng_state *= 2654289733; + return rng_state; +} - usart_enable(USART1); +static void random_init(void) +{ + u32 desig[3]; + desig_get_unique_id(desig); + rng_state = desig[0] ^ desig[1] ^ desig[2]; } -static const struct usb_device_descriptor dev = { +/*** BSB ***/ + +#define BSB_MAX_SIZE 32 +#define BSB_RX_TIMEOUT 10 // If no byte was received for this time, end the current frame [ms] +#define BSB_TX_IDLE_LINE 100 // Consider the line idle if no byte was received for this time [ms] +#define BSB_TX_MAX_ATTEMPTS 3 +#define BSB_TX_WAIT_LOG 8 // base 2 log of pre-frame waiting time in ms +#define BSB_TX_TIMEOUT 10000 // Total time to transmit a frame including all retries and waiting for reply [ms] +#define BSB_REPLY_TIMEOUT 1000 // How long to wait for a reply [ms] + +#undef DEBUG_BSB +#ifdef DEBUG_BSB +#define BSB_DEBUG(x) debug_putc(x) +#else +#define BSB_DEBUG(x) do { } while (0) +#endif + +struct bsb_state { + byte rx_len; // Bytes received so far + byte rx_led; // Countdown to turning RX LED off + u16 rx_crc; + volatile u32 rx_timestamp; // Last byte received + + byte tx_state; // TX_STATE_xxx + byte tx_len; + byte tx_send_index; + byte tx_check_index; + byte tx_attempts; + byte tx_result; // TX_RESULT_xxx + u32 tx_start_timestamp; + u32 tx_ticks_to_send; + u32 tx_end_timestamp; + u32 reply_type_mask; // Expected reply types (0=no reply) + + byte rx_buf[BSB_MAX_SIZE]; + byte tx_buf[BSB_MAX_SIZE]; +}; + +static struct bsb_state bsb; + +// Passing received frames and status codes to the main loop +static byte bsb_rx_status_and_frame[1 + BSB_MAX_SIZE]; +static volatile byte bsb_rx_frame_len; +static byte bsb_tx_result; + +enum bsb_tx_state { + TX_STATE_OFF, + TX_STATE_WAITING_FOR_IDLE_LINE, + TX_STATE_PRE_PACKET_DELAY, + TX_STATE_SENDING, + TX_STATE_SENT, + TX_STATE_WAITING_FOR_REPLY, + TX_STATE_COLLISION, +}; + +static struct bsb_stats bsb_stats; + +static void bsb_attempt_tx(void); + +static u16 bsb_crc_update(u16 crc, byte data) +{ + crc = crc ^ (data << 8); + for (uint i=0; i<8; i++) { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + } + return crc; +} + +void usart3_isr(void) +{ + u32 status = USART_SR(USART3); + + if (status & USART_SR_RXNE) { + uint ch = ~usart_recv(USART3) & 0xff; + bsb.rx_timestamp = ms_ticks; +#ifdef DEBUG_BSB + debug_printf(" %02x", ch); +#endif + + if (bsb.tx_state == TX_STATE_SENDING) { + // We are hearing the echo of a transmitted packet, check it for collisions + if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) { + BSB_DEBUG('e'); + bsb.tx_state = TX_STATE_COLLISION; + } else if (ch == bsb.tx_buf[bsb.tx_check_index++]) { + if (bsb.tx_check_index >= bsb.tx_len) { + BSB_DEBUG('d'); + bsb.tx_state = TX_STATE_SENT; + } + } else { + BSB_DEBUG('c'); + bsb.tx_state = TX_STATE_COLLISION; + } + } else { + // Regular receive + if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) { + bsb_stats.rx_errors++; + bsb.rx_len = 0; + } else if (!bsb.rx_len) { + // Start of frame + if (ch == 0xdc) { + bsb.rx_buf[bsb.rx_len++] = ch; + bsb.rx_crc = bsb_crc_update(0, ch); + BSB_DEBUG('<'); + } else { + bsb_stats.rx_noise++; + BSB_DEBUG('?'); + } + } else { + bsb.rx_buf[bsb.rx_len++] = ch; + bsb.rx_crc = bsb_crc_update(bsb.rx_crc, ch); + if (bsb.rx_len < 4) { + // First three bytes: SOF, source addr, destination addr + } else if (bsb.rx_len == 4) { + // Received length byte + if (bsb.rx_buf[BF_LEN] < 7 || bsb.rx_buf[BF_LEN] > BSB_MAX_SIZE) { + bsb_stats.rx_invalid++; + bsb.rx_len = 0; + BSB_DEBUG('L'); + } + } else if (bsb.rx_len == bsb.rx_buf[BF_LEN]) { + // Received a complete frame: check CRC and pass it to the main loop + if (bsb.rx_crc) { + bsb_stats.rx_bad_crc++; + BSB_DEBUG('C'); + } else { + if (bsb_rx_frame_len) { + // The previous one was not sent yet + bsb_stats.rx_overruns++; + BSB_DEBUG('O'); + } else { + bsb_rx_frame_len = bsb.rx_buf[BF_LEN]; + memcpy(bsb_rx_status_and_frame + 1, bsb.rx_buf, bsb_rx_frame_len); + bsb_stats.rx_ok++; + BSB_DEBUG('.'); + // Will match with expected replies in main loop + } + gpio_clear(GPIOB, GPIO1); + bsb.rx_led = 100; + } + bsb.rx_len = 0; + } + } + } + } + + if (status & USART_SR_TXE) { + if (bsb.tx_state == TX_STATE_SENDING && bsb.tx_send_index < bsb.tx_len) { + BSB_DEBUG('>'); + usart_send(USART3, bsb.tx_buf[bsb.tx_send_index++] ^ 0xff); + } else { + usart_disable_tx_interrupt(USART3); + // Main loop will be notified by receiving the echo of our frame, + // so we do not need to wait for the "transmit complete" flag. + } + } +} + +static void bsb_rx_step(void) +{ + cm_disable_interrupts(); + barrier(); + if (bsb.rx_len && ms_ticks - bsb.rx_timestamp >= BSB_RX_TIMEOUT) { + bsb.rx_len = 0; + bsb_stats.rx_timeouts++; + BSB_DEBUG('T'); + } + if (bsb.rx_led) { + if (!--bsb.rx_led) + gpio_set(GPIOB, GPIO1); + } + cm_enable_interrupts(); +} + +static void bsb_tx_step(void) +{ + switch (bsb.tx_state) { + case TX_STATE_WAITING_FOR_IDLE_LINE: + if (ms_ticks - bsb.rx_timestamp < BSB_TX_IDLE_LINE) + return; + bsb.tx_state = TX_STATE_PRE_PACKET_DELAY; + BSB_DEBUG('i'); + // Fall thru + case TX_STATE_PRE_PACKET_DELAY: + if (ms_ticks - bsb.tx_start_timestamp > BSB_TX_TIMEOUT) { + bsb.tx_state = TX_STATE_OFF; + bsb_tx_result = TX_RESULT_TIMEOUT; + bsb_stats.tx_timeouts++; + return; + } + if (ms_ticks - bsb.rx_timestamp < BSB_TX_IDLE_LINE) { + bsb.tx_state = TX_STATE_WAITING_FOR_IDLE_LINE; + BSB_DEBUG('n'); + return; + } + if (--bsb.tx_ticks_to_send) + return; + BSB_DEBUG('s'); + bsb.rx_timestamp = ms_ticks; + barrier(); + bsb.tx_state = TX_STATE_SENDING; + barrier(); + usart_enable_tx_interrupt(USART3); + return; + case TX_STATE_SENDING: + // In this state, we can race with the USART interrupt handler + cm_disable_interrupts(); + if (bsb.tx_state == TX_STATE_SENDING && ms_ticks - bsb.rx_timestamp >= BSB_RX_TIMEOUT) { + // The echo of our packet was truncated due to collision + bsb.tx_state = TX_STATE_COLLISION; + BSB_DEBUG('t'); + } + cm_enable_interrupts(); + return; + case TX_STATE_COLLISION: + bsb_stats.tx_collisions++; + bsb_attempt_tx(); + return; + case TX_STATE_SENT: + if (!bsb.reply_type_mask) { + // The frame needs no reply => confirm transmission + bsb_stats.tx_ok_no_reply++; + bsb.tx_state = TX_STATE_OFF; + bsb_tx_result = TX_RESULT_OK; + } else { + bsb.tx_state = TX_STATE_WAITING_FOR_REPLY; + bsb.tx_end_timestamp = ms_ticks; + BSB_DEBUG('w'); + } + return; + case TX_STATE_WAITING_FOR_REPLY: + if (ms_ticks - bsb.tx_end_timestamp > BSB_REPLY_TIMEOUT) { + // We did not receive a reply. Maybe the request was lost? Re-transmit it. + bsb_stats.tx_reply_timeouts++; + BSB_DEBUG('t'); + bsb_attempt_tx(); + } + return; + default: + ; + } +} + +static void bsb_attempt_tx(void) +{ + if (bsb.tx_attempts++ >= BSB_TX_MAX_ATTEMPTS) { + bsb.tx_state = TX_STATE_OFF; + bsb_tx_result = TX_RESULT_TOO_MANY_RETRIES; + return; + } + + bsb.tx_ticks_to_send = (random_u32() | 0x80000000) >> (32 - BSB_TX_WAIT_LOG - (bsb.tx_attempts-1)); + + bsb.tx_state = TX_STATE_WAITING_FOR_IDLE_LINE; + bsb.tx_send_index = 0; + bsb.tx_check_index = 0; + debug_printf("TX: Attempt #%u, wait for %u ms\n", bsb.tx_attempts, (uint) bsb.tx_ticks_to_send); +} + +static void bsb_send(byte *buf, uint len) +{ + // If a transmit is in progress, declare overrrun + if (bsb_tx_result != TX_RESULT_NONE) { + bsb_stats.tx_overruns++; + return; + } + if (bsb.tx_state != TX_STATE_OFF) { + bsb_stats.tx_overruns++; + bsb_tx_result = TX_RESULT_OVERRUN; + return; + } + + // Check frame validity + if (len < 7 || len > BSB_MAX_SIZE || buf[BF_SOF] != 0xdc || buf[BF_LEN] != len) { + bsb_stats.tx_rejects++; + bsb_tx_result = TX_RESULT_MALFORMED; + return; + } + + // Detect frame type and prepare expected reply types + if (buf[BF_OP] == BSB_OP_QUERY) { + // So far, we allow only QUERY frames + bsb.reply_type_mask = (1 << BSB_OP_ANSWER) | (1 << BSB_OP_ERROR); + } else { + bsb_stats.tx_rejects++; + bsb_tx_result = TX_RESULT_FORBIDDEN; + return; + } + + // Fill in source address + buf[BF_SRC] = 0x80 + BSB_ADDR_GATEWAY; + + // Calculate CRC + u16 crc = 0; + for (uint i = 0; i < len - 2; i++) + crc = bsb_crc_update(crc, buf[i]); + buf[len-2] = crc >> 8; + buf[len-1] = crc; + debug_printf("TX: Prepared frame: type=%02x dest=%02x len=%u crc=%04x reply=%08x\n", buf[BF_OP], buf[BF_DEST], len, crc, (uint) bsb.reply_type_mask); + + // Queue frame for transmission + memcpy(bsb.tx_buf, buf, len); + bsb.tx_len = len; + bsb.tx_start_timestamp = ms_ticks; + bsb.tx_attempts = 0; + bsb_attempt_tx(); + + // Light the TX LED + gpio_clear(GPIOB, GPIO0); +} + +static void bsb_check_reply(void) +{ + // Match received frame in bsb_rx_status_and_frame to expected replies + // and set the status in byte 0. + + byte *frame = bsb_rx_status_and_frame + 1; + // XXX: This might be executed too fast, so we can still be in TX_STATE_SENT + if (bsb.tx_state == TX_STATE_WAITING_FOR_REPLY || bsb.tx_state == TX_STATE_SENT) { + if (frame[BF_DEST] == BSB_ADDR_GATEWAY && + frame[BF_SRC] == bsb.tx_buf[BF_DEST] ^ 0x80 && + frame[BF_OP] < 32 && + bsb.reply_type_mask & (1 << frame[BF_OP])) { + debug_printf("BSB: Matched reply\n"); + bsb.tx_state = TX_STATE_OFF; + bsb_stats.tx_ok_replied++; + bsb_rx_status_and_frame[0] = TX_RESULT_OK; + gpio_set(GPIOB, GPIO0); // Turn the TX LED off + return; + } + } + + bsb_rx_status_and_frame[0] = TX_RESULT_NONE; +} + +static void bsb_init(void) +{ + usart_set_baudrate(USART3, 4800); + usart_set_databits(USART3, 9); + usart_set_stopbits(USART3, USART_STOPBITS_1); + usart_set_mode(USART3, USART_MODE_TX_RX); + usart_set_parity(USART3, USART_PARITY_ODD); + usart_set_flow_control(USART3, USART_FLOWCONTROL_NONE); + + usart_enable(USART3); + nvic_enable_irq(NVIC_USART3_IRQ); + usart_enable_rx_interrupt(USART3); +} + +/*** 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", + "BSB Gateway", + usb_serial_number, +}; + +static const struct usb_device_descriptor device = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, @@ -87,23 +493,25 @@ static const struct usb_device_descriptor dev = { .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = 64, - .idVendor = 0x4242, - .idProduct = 0x0003, - .bcdDevice = 0x0200, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, + .idVendor = BSB_USB_VENDOR, + .idProduct = BSB_USB_PRODUCT, + .bcdDevice = BSB_USB_VERSION, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIAL, .bNumConfigurations = 1, }; static const struct usb_endpoint_descriptor endpoints[] = {{ + // Bulk end-point for sending frames to BSB .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x81, + .bEndpointAddress = 0x01, .bmAttributes = USB_ENDPOINT_ATTR_BULK, .wMaxPacketSize = 64, .bInterval = 1, },{ + // Interrupt end-point for receiving frames from BSB .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0x82, @@ -125,16 +533,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, @@ -142,55 +577,80 @@ static const struct usb_config_descriptor config = { .interface = ifaces, }; -static const char *usb_strings[] = { - "United Computer Wizards", - "BSB Gateway", - "1", -}; - static byte usb_configured; +static byte usb_ep82_pending; static uint8_t usbd_control_buffer[64]; -static enum usbd_request_return_codes control_cb(usbd_device *usbd_dev, +static enum usbd_request_return_codes control_cb( + usbd_device *dev UNUSED, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, - void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req) UNUSED) + void (**complete)(usbd_device *dev, struct usb_setup_data *req) UNUSED) { if (req->bmRequestType != 0xc0 || req->bRequest != 0x00) return USBD_REQ_NOTSUPP; - (void) usbd_dev; - (void) buf; - (void) len; - (void) complete; - debug_printf("USB: Control request\n"); + // We support reading of statistics via control requests + debug_printf("USB: Reading statistics\n"); + uint n = MIN(*len, sizeof(bsb_stats)); + memcpy(*buf, (char *) &bsb_stats, n); + *len = n; + + 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 + debug_printf("Switching to DFU\n"); + debug_flush(); + scb_reset_core(); +} - byte *b = *buf; - b[0] = 0x12; - b[1] = 0x34; - b[2] = 0x56; - b[3] = 0x78; - if (*len > 4) - *len = 4; +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) +static void ep01_cb(usbd_device *dev, uint8_t ep UNUSED) { - byte buf[4]; - put_u32_be(buf, ms_ticks); - usbd_ep_write_packet(usbd_dev, 0x81, buf, sizeof(buf)); - debug_printf("USB: Bulk write\n"); + // We received a frame from the USB host + byte buf[64]; + uint len = usbd_ep_read_packet(dev, 0x01, buf, sizeof(buf)); + debug_printf("USB: Host sent %u bytes\n", len); + bsb_send(buf, len); } -static void set_config_cb(usbd_device *usbd_dev, uint16_t wValue UNUSED) +static void ep82_cb(usbd_device *dev UNUSED, uint8_t ep UNUSED) { - usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, USB_REQ_TYPE_TYPE, 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); + // The frame was accepted by the USB host + debug_printf("USB: Interrupt done\n"); + usb_ep82_pending = 0; +} + +static void set_config_cb(usbd_device *dev, uint16_t wValue UNUSED) +{ + usbd_register_control_callback( + dev, + USB_REQ_TYPE_VENDOR, + USB_REQ_TYPE_TYPE, + control_cb); + 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_INTERRUPT, 64, ep82_cb); usb_configured = 1; } @@ -198,41 +658,108 @@ static void reset_cb(void) { debug_printf("USB: Reset\n"); usb_configured = 0; + usb_ep82_pending = 0; } +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(); tick_init(); usart_init(); + desig_get_unique_id_as_dfu(usb_serial_number); + random_init(); - debug_printf("Hello, kitty!\n"); + debug_printf("Hail, Lord Damian! Thy BSB interface is ready.\n"); - // 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(1000); + bsb_init(); + usb_init(); - usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, 3, 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; + u32 last_blink = 0; + u32 last_step = 0; for (;;) { - if (ms_ticks - last_ds_step >= 100) { + if (ms_ticks - last_blink >= 100) { debug_led_toggle(); - last_ds_step = ms_ticks; - if (usb_configured) { - byte x[4]; - put_u32_be(x, ms_ticks); - usbd_ep_write_packet(usbd_dev, 0x82, x, 4); + last_blink = ms_ticks; + } + + 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); + } + + if (usb_configured) { + // Passing of received frames to USB + if (!usb_ep82_pending) { + if (bsb_rx_frame_len) { + // Received frame, possibly marked as reply + barrier(); + bsb_check_reply(); + usbd_ep_write_packet(usbd_dev, 0x82, bsb_rx_status_and_frame, 1 + bsb_rx_frame_len); + usb_ep82_pending = 1; + barrier(); + bsb_rx_frame_len = 0; + debug_printf("USB: Sending frame to interrupt EP\n"); + } else if (bsb_tx_result != TX_RESULT_NONE) { + // Only a transmission result + usbd_ep_write_packet(usbd_dev, 0x82, &bsb_tx_result, 1); + usb_ep82_pending = 1; + gpio_set(GPIOB, GPIO0); // Turn the TX LED off + debug_printf("USB: Sending status %u to interrupt EP\n", bsb_tx_result); + bsb_tx_result = TX_RESULT_NONE; + } } } - // XXX: libopencm3 usbd does not use interrupts at the moment, need to poll... - // wait_for_interrupt(); - usbd_poll(usbd_dev); + if (ms_ticks != last_step) { + last_step = ms_ticks; + bsb_rx_step(); + bsb_tx_step(); + } + + wait_for_interrupt(); } return 0;