Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a USB message queue to USB to CANbus bridge code #6354

Merged
merged 3 commits into from
Oct 4, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 56 additions & 31 deletions src/generic/usb_canbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,18 @@ struct gs_host_frame {
static struct usbcan_data {
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
uint8_t notify_local, usb_send_busy;
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
uint32_t pull_pos, push_pos;
struct canbus_msg queue[32];
uint32_t canhw_pull_pos, canhw_push_pos;
struct canbus_msg canhw_queue[32];
} UsbCan;

enum {
Expand All @@ -139,16 +137,16 @@ void
canbus_process_data(struct canbus_msg *msg)
{
// Add to admin command queue
uint32_t pushp = UsbCan.push_pos;
if (pushp - UsbCan.pull_pos >= ARRAY_SIZE(UsbCan.queue))
uint32_t pushp = UsbCan.canhw_push_pos;
if (pushp - UsbCan.canhw_pull_pos >= ARRAY_SIZE(UsbCan.canhw_queue))
// No space - drop message
return;
if (UsbCan.assigned_id && (msg->id & ~1) == UsbCan.assigned_id)
// Id reserved for local
return;
uint32_t pos = pushp % ARRAY_SIZE(UsbCan.queue);
memcpy(&UsbCan.queue[pos], msg, sizeof(*msg));
UsbCan.push_pos = pushp + 1;
uint32_t pos = pushp % ARRAY_SIZE(UsbCan.canhw_queue);
memcpy(&UsbCan.canhw_queue[pos], msg, sizeof(*msg));
UsbCan.canhw_push_pos = pushp + 1;
usb_notify_bulk_out();
}

Expand All @@ -167,24 +165,43 @@ send_frame(struct canbus_msg *msg)

// Send any pending hw frames to host
static void
drain_hw_queue(void)
drain_canhw_queue(void)
{
uint32_t pull_pos = UsbCan.pull_pos;
uint32_t pull_pos = UsbCan.canhw_pull_pos;
for (;;) {
uint32_t push_pos = readl(&UsbCan.push_pos);
uint32_t push_pos = readl(&UsbCan.canhw_push_pos);
if (push_pos == pull_pos) {
// No more data to send
UsbCan.usb_send_busy = 0;
return;
}
uint32_t pos = pull_pos % ARRAY_SIZE(UsbCan.queue);
int ret = send_frame(&UsbCan.queue[pos]);
uint32_t pos = pull_pos % ARRAY_SIZE(UsbCan.canhw_queue);
int ret = send_frame(&UsbCan.canhw_queue[pos]);
if (ret < 0) {
// USB is busy - retry later
UsbCan.usb_send_busy = 1;
return;
}
UsbCan.pull_pos = pull_pos = pull_pos + 1;
UsbCan.canhw_pull_pos = pull_pos = pull_pos + 1;
}
}

// 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;
}
}

Expand All @@ -195,13 +212,19 @@ usbcan_task(void)
return;

// Send any pending hw frames to host
drain_hw_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 (;;) {
// See if previous host frame needs to be transmitted
uint_fast8_t host_status = UsbCan.host_status;
if (host_status & (HS_TX_HW | HS_TX_LOCAL)) {
struct gs_host_frame *gs = &UsbCan.host_frame;
struct canbus_msg msg;
msg.id = gs->can_id;
msg.dlc = gs->can_dlc;
Expand All @@ -221,30 +244,30 @@ usbcan_task(void)

// Send any previous echo frames
if (host_status) {
if (UsbCan.usb_send_busy)
if (UsbCan.notify_local || UsbCan.usb_send_busy)
// Don't send echo frame until other traffic is sent
return;
int ret = usb_send_bulk_in(&UsbCan.host_frame
, sizeof(UsbCan.host_frame));
break;
int ret = usb_send_bulk_in(gs, sizeof(*gs));
if (ret < 0)
return;
UsbCan.host_status = 0;
UsbCan.host_pull_pos = pull_pos = pull_pos + 1;
}

// Read next frame from host
int ret = usb_read_bulk_out(&UsbCan.host_frame
, USB_CDC_EP_BULK_OUT_SIZE);
if (ret <= 0)
// Process next frame from host
if (pull_pos == push_pos)
// No frame available - no more work to be done
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;
if (id == CANBUS_ID_ADMIN)
UsbCan.host_status = HS_TX_ECHO | HS_TX_HW | HS_TX_LOCAL;
else if (UsbCan.assigned_id && UsbCan.assigned_id == id)
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)
canserial_notify_tx();
}
Expand All @@ -258,6 +281,8 @@ canbus_send(struct canbus_msg *msg)
int ret = send_frame(msg);
if (ret < 0)
goto retry_later;
if (UsbCan.notify_local && UsbCan.host_status)
canbus_notify_tx();
UsbCan.notify_local = 0;
return msg->dlc;
retry_later:
Expand Down