From f31b2b06632bb339afe9b453cc4c5a55775514e0 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 26 Feb 2020 22:35:12 +0100 Subject: [PATCH] BSB: Firmware handles replies --- bsb/daemon/burrow-bsb.c | 20 +++++-- bsb/firmware/interface.h | 36 +++++++------ bsb/firmware/main.c | 109 ++++++++++++++++++++++++++++----------- 3 files changed, 114 insertions(+), 51 deletions(-) diff --git a/bsb/daemon/burrow-bsb.c b/bsb/daemon/burrow-bsb.c index 6563458..d20392a 100644 --- a/bsb/daemon/burrow-bsb.c +++ b/bsb/daemon/burrow-bsb.c @@ -105,19 +105,29 @@ static void show_stats(byte *resp, uint len) static void show_packet(byte *pkt, uint len) { - if (len == 1) { - printf("Received status %u\n", pkt[0]); + if (!len) { + printf("ERROR: Received empty frame!\n"); + return; + } + + byte status = *pkt++; + len--; + + if (!len) { + printf("Transmit status: %u\n", status); return; } #if 1 - printf(":"); + printf(": [%d]", status); for (uint i=0; i %02x: ", pkt[1] ^ 0x80, pkt[2]); + printf("%02x -> %02x: ", pkt[BF_SRC] ^ 0x80, pkt[BF_DEST]); + if (status) + printf("[REPLY] "); switch (pkt[4]) { case 2: printf("INFO %04x:%04x =", (pkt[5]<<8) | pkt[6], (pkt[7]<<8) | pkt[8]); @@ -176,7 +186,7 @@ int main(void) } if (last_query + 10 < now) { - byte pkt[] = { 0xdc, 0xc2, 0x00, 0x0b, 0x06, 0x3d, 0x2e, 0x11, 0x25 }; + byte pkt[] = { 0xdc, 0xc2, 0x00, 0x0b, 0x06, 0x3d, 0x2e, 0x11, 0x25, 0x00, 0x00 }; if (err = libusb_bulk_transfer(devh, 0x01, pkt, sizeof(pkt), &received, 2000)) { printf("Send failed: error %d\n", err); // usb_error("Receive failed: error %d", received); diff --git a/bsb/firmware/interface.h b/bsb/firmware/interface.h index a4f4240..c2abf05 100644 --- a/bsb/firmware/interface.h +++ b/bsb/firmware/interface.h @@ -15,11 +15,15 @@ * Vendor-defined request 0x00 sends struct bsb_stats in little endian. * * 0x01 = bulk endpoint - * Used for sending frames to BSB. + * Used for sending frames to BSB. Accepts BSB frames. CRC will be calculated + * automatically. * * 0x82 = interrupt endpoint - * Used for receiving frames from BSB. - * Also transmits 1-byte status reports on frames sent on endpoint 0x01. + * Used for receiving frames from BSB and status reports on sent frames. + * The first byte is a status byte (TX_RESULT_xxx), the rest is a frame. + * If status == TX_RESULT_NONE, the frame is a received frame. + * If status == TX_RESULT_OK, the frame is a received reply to the sent frame. + * Otherwise, the status indicates transmit error and the frame is empty. */ // Status sent on the interrupt endpoint @@ -51,20 +55,9 @@ enum bsb_tx_result { P(tx_rejects) \ P(tx_timeouts) \ P(tx_collisions) \ - P(tx_ok) - - u32 rx_noise; - u32 rx_errors; - u32 rx_invalid; - u32 rx_overruns; - u32 rx_timeouts; - u32 rx_bad_crc; - u32 rx_ok; - u32 tx_overruns; - u32 tx_rejects; - u32 tx_timeouts; - u32 tx_collisions; - u32 tx_ok; + P(tx_reply_timeouts) \ + P(tx_ok_no_reply) \ + P(tx_ok_replied) struct bsb_stats { #define P(x) u32 x; @@ -93,6 +86,15 @@ enum bsb_address { BSB_ADDR_BROADCAST = 0x7f, }; +// Positions of fields in a frame +enum bsb_frame { + BF_SOF, + BF_SRC, + BF_DEST, + BF_LEN, + BF_OP, +}; + enum bsb_op { BSB_OP_REQUEST_INFO = 1, BSB_OP_INFO = 2, diff --git a/bsb/firmware/main.c b/bsb/firmware/main.c index ca30ffe..04dc0e5 100644 --- a/bsb/firmware/main.c +++ b/bsb/firmware/main.c @@ -118,11 +118,12 @@ static void random_init(void) /*** BSB ***/ #define BSB_MAX_SIZE 32 -#define BSB_RX_TIMEOUT 10 // ms -#define BSB_TX_IDLE_LINE 100 // ms +#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 10 // base 2 log of timeout in ms -#define BSB_TX_TIMEOUT 10000 // ms +#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] #define DEBUG_BSB #ifdef DEBUG_BSB @@ -145,6 +146,8 @@ struct bsb_state { 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]; @@ -153,7 +156,7 @@ struct bsb_state { static struct bsb_state bsb; // Passing received frames and status codes to the main loop -static byte bsb_rx_frame[BSB_MAX_SIZE]; +static byte bsb_rx_status_and_frame[1 + BSB_MAX_SIZE]; static volatile byte bsb_rx_frame_len; static byte bsb_tx_result; @@ -162,7 +165,8 @@ enum bsb_tx_state { TX_STATE_WAITING_FOR_IDLE_LINE, TX_STATE_PRE_PACKET_DELAY, TX_STATE_SENDING, - TX_STATE_DONE, + TX_STATE_SENT, + TX_STATE_WAITING_FOR_REPLY, TX_STATE_COLLISION, }; @@ -201,7 +205,7 @@ void usart3_isr(void) } 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_DONE; + bsb.tx_state = TX_STATE_SENT; } } else { BSB_DEBUG('c'); @@ -229,12 +233,12 @@ void usart3_isr(void) // First three bytes: SOF, source addr, destination addr } else if (bsb.rx_len == 4) { // Received length byte - if (bsb.rx_buf[3] < 7 || bsb.rx_buf[3] > BSB_MAX_SIZE) { + 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[3]) { + } 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++; @@ -245,10 +249,11 @@ void usart3_isr(void) bsb_stats.rx_overruns++; BSB_DEBUG('O'); } else { - memcpy(bsb_rx_frame, bsb.rx_buf, bsb.rx_buf[3]); - bsb_rx_frame_len = bsb.rx_buf[3] - 2; + 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; @@ -331,10 +336,25 @@ static void bsb_tx_step(void) bsb_stats.tx_collisions++; bsb_attempt_tx(); return; - case TX_STATE_DONE: - bsb_stats.tx_ok++; - bsb.tx_state = TX_STATE_OFF; - bsb_tx_result = TX_RESULT_OK; + 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: ; @@ -344,8 +364,8 @@ static void bsb_tx_step(void) static void bsb_attempt_tx(void) { if (bsb.tx_attempts++ >= BSB_TX_MAX_ATTEMPTS) { - bsb_tx_result = TX_RESULT_TOO_MANY_RETRIES; bsb.tx_state = TX_STATE_OFF; + bsb_tx_result = TX_RESULT_TOO_MANY_RETRIES; return; } @@ -371,30 +391,35 @@ static void bsb_send(byte *buf, uint len) } // Check frame validity - if (len < 5 || len > BSB_MAX_SIZE - 2 || buf[0] != 0xdc || buf[3] != len + 2) { + 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; } - // So far, we allow only QUERY frames from address 42 - if (buf[1] != 0x80 + 42 && buf[4] != 0x06) { + // 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; } - // Copy frame and calculate CRC + // Fill in source address + buf[BF_SRC] = 0x80 + BSB_ADDR_GATEWAY; + + // Calculate CRC u16 crc = 0; - for (uint i=0; i> 8; - bsb.tx_buf[len++] = crc; - debug_printf("TX: Prepared frame of %u bytes with CRC %04x\n", len, crc); + 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; @@ -404,6 +429,30 @@ static void bsb_send(byte *buf, uint len) 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 byte 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); @@ -685,14 +734,16 @@ int main(void) // Passing of received frames to USB if (!usb_ep82_pending) { if (bsb_rx_frame_len) { + // Received frame, possibly marked as reply barrier(); - usbd_ep_write_packet(usbd_dev, 0x82, bsb_rx_frame, bsb_rx_frame_len); + 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"); - } - if (bsb_tx_result != TX_RESULT_NONE) { + } 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 -- 2.39.2