usb_canbus: Add a local queue for USB messages received from host

Read USB messages arriving from the host into a queue.  This makes it
less likely that USB "bulk out" packets will be NAK'ed on the USB bus,
which improves USB bus utilization.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2023-09-25 11:34:31 -04:00
parent 5b204866c5
commit 78ae83c314
1 changed files with 38 additions and 15 deletions

View File

@ -104,17 +104,15 @@ struct gs_host_frame {
static struct usbcan_data { static struct usbcan_data {
struct task_wake wake; struct task_wake wake;
// Canbus data from host
union {
struct gs_host_frame host_frame;
uint8_t rx_frame_pad[USB_CDC_EP_BULK_OUT_SIZE];
};
uint8_t host_status;
// Canbus data routed locally // Canbus data routed locally
uint8_t notify_local, usb_send_busy; uint8_t notify_local, usb_send_busy;
uint32_t assigned_id; uint32_t assigned_id;
// Canbus data from host
uint8_t host_status;
uint32_t host_pull_pos, host_push_pos;
struct gs_host_frame host_frames[16];
// Data from physical canbus interface // Data from physical canbus interface
uint32_t canhw_pull_pos, canhw_push_pos; uint32_t canhw_pull_pos, canhw_push_pos;
struct canbus_msg canhw_queue[32]; struct canbus_msg canhw_queue[32];
@ -188,6 +186,25 @@ drain_canhw_queue(void)
} }
} }
// Fill local queue with any USB messages sent from host
static void
fill_usb_host_queue(void)
{
uint32_t pull_pos = UsbCan.host_pull_pos, push_pos = UsbCan.host_push_pos;
for (;;) {
if (push_pos - pull_pos >= ARRAY_SIZE(UsbCan.host_frames))
// No more space in queue
break;
uint32_t pushp = push_pos % ARRAY_SIZE(UsbCan.host_frames);
struct gs_host_frame *gs = &UsbCan.host_frames[pushp];
int ret = usb_read_bulk_out(gs, sizeof(*gs));
if (ret <= 0)
// No more messages ready
break;
UsbCan.host_push_pos = push_pos = push_pos + 1;
}
}
void void
usbcan_task(void) usbcan_task(void)
{ {
@ -197,11 +214,17 @@ usbcan_task(void)
// Send any pending hw frames to host // Send any pending hw frames to host
drain_canhw_queue(); drain_canhw_queue();
// Fill local queue with any USB messages arriving from host
fill_usb_host_queue();
// Route messages received from host
uint32_t pull_pos = UsbCan.host_pull_pos, push_pos = UsbCan.host_push_pos;
uint32_t pullp = pull_pos % ARRAY_SIZE(UsbCan.host_frames);
struct gs_host_frame *gs = &UsbCan.host_frames[pullp];
for (;;) { for (;;) {
// See if previous host frame needs to be transmitted // See if previous host frame needs to be transmitted
uint_fast8_t host_status = UsbCan.host_status; uint_fast8_t host_status = UsbCan.host_status;
if (host_status & (HS_TX_HW | HS_TX_LOCAL)) { if (host_status & (HS_TX_HW | HS_TX_LOCAL)) {
struct gs_host_frame *gs = &UsbCan.host_frame;
struct canbus_msg msg; struct canbus_msg msg;
msg.id = gs->can_id; msg.id = gs->can_id;
msg.dlc = gs->can_dlc; msg.dlc = gs->can_dlc;
@ -224,20 +247,19 @@ usbcan_task(void)
if (UsbCan.usb_send_busy) if (UsbCan.usb_send_busy)
// Don't send echo frame until other traffic is sent // Don't send echo frame until other traffic is sent
return; return;
int ret = usb_send_bulk_in(&UsbCan.host_frame int ret = usb_send_bulk_in(gs, sizeof(*gs));
, sizeof(UsbCan.host_frame));
if (ret < 0) if (ret < 0)
return; return;
UsbCan.host_status = 0; UsbCan.host_status = 0;
UsbCan.host_pull_pos = pull_pos = pull_pos + 1;
} }
// Read next frame from host // Process next frame from host
int ret = usb_read_bulk_out(&UsbCan.host_frame if (pull_pos == push_pos)
, USB_CDC_EP_BULK_OUT_SIZE);
if (ret <= 0)
// No frame available - no more work to be done // No frame available - no more work to be done
break; break;
uint32_t id = UsbCan.host_frame.can_id; gs = &UsbCan.host_frames[pull_pos % ARRAY_SIZE(UsbCan.host_frames)];
uint32_t id = gs->can_id;
UsbCan.host_status = HS_TX_ECHO | HS_TX_HW; UsbCan.host_status = HS_TX_ECHO | HS_TX_HW;
if (id == CANBUS_ID_ADMIN) if (id == CANBUS_ID_ADMIN)
UsbCan.host_status = HS_TX_ECHO | HS_TX_HW | HS_TX_LOCAL; UsbCan.host_status = HS_TX_ECHO | HS_TX_HW | HS_TX_LOCAL;
@ -245,6 +267,7 @@ usbcan_task(void)
UsbCan.host_status = HS_TX_ECHO | HS_TX_LOCAL; UsbCan.host_status = HS_TX_ECHO | HS_TX_LOCAL;
} }
// Wake up local message response handling (if usb is not busy)
if (UsbCan.notify_local && !UsbCan.usb_send_busy) if (UsbCan.notify_local && !UsbCan.usb_send_busy)
canserial_notify_tx(); canserial_notify_tx();
} }