X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=bsb%2Ffirmware%2Fmain.c;h=bb0c48e53bf140ae98ade0e46e9da73180760dbf;hb=37082aba8175e02e86271010ee372d2f34fc6413;hp=c2b5397bd901e106aecfc18d6d024e20bb03f400;hpb=abca287bfe8ba1d1ef3240f4e4e4cff724a8bfd2;p=home-hw.git diff --git a/bsb/firmware/main.c b/bsb/firmware/main.c index c2b5397..bb0c48e 100644 --- a/bsb/firmware/main.c +++ b/bsb/firmware/main.c @@ -118,13 +118,14 @@ 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 +#undef DEBUG_BSB #ifdef DEBUG_BSB #define BSB_DEBUG(x) debug_putc(x) #else @@ -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 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); @@ -422,6 +471,20 @@ static void bsb_init(void) 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, @@ -430,12 +493,12 @@ static const struct usb_device_descriptor device = { .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, }; @@ -514,14 +577,6 @@ static const struct usb_config_descriptor config = { .interface = ifaces, }; -static char usb_serial_number[13]; - -static const char *usb_strings[] = { - "United Computer Wizards", - "BSB Gateway", - usb_serial_number, -}; - static byte usb_configured; static byte usb_ep82_pending; static uint8_t usbd_control_buffer[64]; @@ -679,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