+/*** 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 = {