]> mj.ucw.cz Git - home-hw.git/commitdiff
BSB: Firmware handles replies
authorMartin Mares <mj@ucw.cz>
Wed, 26 Feb 2020 21:35:12 +0000 (22:35 +0100)
committerMartin Mares <mj@ucw.cz>
Wed, 26 Feb 2020 21:35:12 +0000 (22:35 +0100)
bsb/daemon/burrow-bsb.c
bsb/firmware/interface.h
bsb/firmware/main.c

index 6563458c7c0bd3bb29cae24c4e2178149fdf80f4..d20392aaa48cd1307db7ae9c19fa01a124aaf9b6 100644 (file)
@@ -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<len; i++)
                printf(" %02x", pkt[i]);
        putchar('\n');
 #endif
 
-       printf("%02x -> %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);
index a4f42406b9e86c056644bd8170eaf12d31e290eb..c2abf053b22a87c5fb61dd698ce96abbb96d5149 100644 (file)
  *             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,
index ca30ffe1e6c02244e211ed285ed85c5aa87a1f44..04dc0e50681b523342d9203ad99e1c30b836d5e0 100644 (file)
@@ -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<len; i++) {
-               bsb.tx_buf[i] = buf[i];
+       for (uint i = 0; i < len - 2; i++)
                crc = bsb_crc_update(crc, buf[i]);
-       }
-       bsb.tx_buf[len++] = crc >> 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