/*** 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
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_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;
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,
};
} 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');
// 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++;
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;
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:
;
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;
}
}
// 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;
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);
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,
.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,
};
.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];
// 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