287 lines
6.5 KiB
C
287 lines
6.5 KiB
C
#include "usb.h"
|
|
#include "usb_conf.h"
|
|
#include "command_handler.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/ring_buffer.h>
|
|
|
|
// PICO-SDK
|
|
#include "pico/bootrom.h"
|
|
|
|
LOG_MODULE_REGISTER(usb, LOG_LEVEL_INF);
|
|
|
|
// DEVICE
|
|
const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart);
|
|
static struct usbd_context *usb_context;
|
|
|
|
// RX THREAD
|
|
static struct k_thread usb_rx_thread_data;
|
|
static k_tid_t usb_rx_thread_id = NULL;
|
|
#define USB_RX_THREAD_STACK_SIZE 2048
|
|
K_THREAD_STACK_DEFINE(usb_rx_thread_stack, USB_RX_THREAD_STACK_SIZE);
|
|
|
|
// RX BUFFER
|
|
#define RING_BUF_SIZE 255
|
|
static uint8_t ring_buffer[RING_BUF_SIZE];
|
|
static struct ring_buf ringbuf;
|
|
struct k_sem rx_semaphore;
|
|
|
|
// TX THREAD
|
|
static struct k_thread usb_tx_thread_data;
|
|
static k_tid_t usb_tx_thread_id = NULL;
|
|
#define USB_TX_THREAD_STACK_SIZE 512
|
|
K_THREAD_STACK_DEFINE(usb_tx_thread_stack, USB_TX_THREAD_STACK_SIZE);
|
|
|
|
// TX BUFFER (add ack and nack at the and as static)
|
|
#define TX_BUFFER_SIZE 10
|
|
struct command_message_t usb_tx_buffer[TX_BUFFER_SIZE + 2];
|
|
struct command_message_t *usb_tx_buf_ptr;
|
|
char usb_tx_ptr_msgq_buffer[(TX_BUFFER_SIZE + 2) * sizeof(struct command_message_t *)];
|
|
struct k_msgq usb_tx_ptr_msgq;
|
|
|
|
// ACK / NACK messages
|
|
#define RETURN_ACK true
|
|
|
|
|
|
static void interrupt_handler(const struct device *dev, void *user_data) {
|
|
ARG_UNUSED(user_data);
|
|
|
|
while (true) {
|
|
uart_irq_update(dev);
|
|
|
|
if (uart_irq_is_pending(dev) <= 0) {
|
|
break;
|
|
}
|
|
|
|
if (uart_irq_rx_ready(dev)) {
|
|
int recv_len, rb_len;
|
|
uint8_t buffer[64];
|
|
size_t len = MIN(ring_buf_space_get(&ringbuf), sizeof(buffer));
|
|
|
|
if (len == 0) {
|
|
// ring buffer full, drops package(s)
|
|
uart_irq_rx_disable(dev);
|
|
k_sem_give(&rx_semaphore);
|
|
break;
|
|
}
|
|
|
|
recv_len = uart_fifo_read(dev, buffer, len);
|
|
if (recv_len < 0) {
|
|
LOG_ERR("Failed to read UART FIFO");
|
|
recv_len = 0;
|
|
};
|
|
|
|
rb_len = ring_buf_put(&ringbuf, buffer, recv_len);
|
|
if (rb_len < recv_len) {
|
|
LOG_ERR("Drop %u bytes", recv_len - rb_len);
|
|
}
|
|
|
|
k_sem_give(&rx_semaphore);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usb_rx_thread(void *p1, void *p2, void *p3) {
|
|
ARG_UNUSED(p1);
|
|
ARG_UNUSED(p2);
|
|
ARG_UNUSED(p3);
|
|
struct command_message_t msg;
|
|
command_message_init(&msg);
|
|
|
|
LOG_INF("USB command processing thread started");
|
|
|
|
while (1) {
|
|
k_sem_take(&rx_semaphore, K_FOREVER);
|
|
|
|
int len;
|
|
|
|
// While ring buffer has data
|
|
do {
|
|
uint8_t buf_prefix;
|
|
len = ring_buf_get(&ringbuf, &buf_prefix, 1);
|
|
|
|
if (len && (buf_prefix == COMMAND_PREFIX)) {
|
|
uint8_t buf_header[4];
|
|
len = ring_buf_get(&ringbuf, buf_header, 4);
|
|
|
|
if ((len == 4) && (buf_header[1] == COMMAND_ID) && (buf_header[0] <= COMMAND_DATA_SIZE)) {
|
|
msg.length = buf_header[0];
|
|
msg.command = buf_header[2];
|
|
msg.crc = buf_header[3];
|
|
|
|
if (msg.length) {
|
|
len = ring_buf_get(&ringbuf, msg.data, msg.length);
|
|
}
|
|
|
|
uint8_t calculated_crc = command_calculate_crc(&msg);
|
|
if (calculated_crc != msg.crc) {
|
|
if (RETURN_ACK) {
|
|
// Send NACK
|
|
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
int ret = command_handler(&msg);
|
|
if (ret == 0) {
|
|
if (RETURN_ACK) {
|
|
// Send ACK
|
|
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE]);
|
|
}
|
|
}
|
|
else {
|
|
if (RETURN_ACK) {
|
|
// Send NACK
|
|
usb_send_command(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
// Command_id did not match, ignore
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
// Prefix did not match, ignore
|
|
continue;
|
|
}
|
|
} while (len > 0);
|
|
|
|
uart_irq_rx_enable(uart_dev);
|
|
}
|
|
|
|
LOG_INF("USB command processing thread exiting");
|
|
}
|
|
|
|
static void usb_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) {
|
|
if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) {
|
|
// Jump to BOOTSEL when baudrate changes to 1200
|
|
uint32_t baudrate;
|
|
|
|
if (uart_line_ctrl_get(msg->dev, UART_LINE_CTRL_BAUD_RATE, &baudrate) == 0) {
|
|
LOG_INF("Baudrate %u", baudrate);
|
|
|
|
if (baudrate == 1200) {
|
|
LOG_INF("Entering BOOTSEL...");
|
|
reset_usb_boot(0, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usb_tx_thread(void *p1, void *p2, void *p3) {
|
|
struct command_message_t *data;
|
|
|
|
while (1) {
|
|
k_msgq_get(&usb_tx_ptr_msgq, &data, K_FOREVER);
|
|
|
|
if (!device_is_ready(uart_dev)) {
|
|
// FIXME: Don't drop packages
|
|
continue;
|
|
}
|
|
|
|
// Message size: prefix + length + id + command + crc + data
|
|
size_t msg_size = COMMAND_HEADER_SIZE + data->length;
|
|
uint8_t *msg_bytes = (uint8_t *)data;
|
|
|
|
/* uart_poll_out blocks until sent, ensuring data integrity */
|
|
for (size_t i = 0; i < msg_size; i++) {
|
|
uart_poll_out(uart_dev, msg_bytes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
int usb_init() {
|
|
ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer);
|
|
k_sem_init(&rx_semaphore, 0, 1);
|
|
usb_tx_buf_ptr = usb_tx_buffer;
|
|
k_msgq_init(&usb_tx_ptr_msgq, usb_tx_ptr_msgq_buffer, sizeof(struct command_message_t *), TX_BUFFER_SIZE);
|
|
|
|
command_create_ack(&usb_tx_buffer[TX_BUFFER_SIZE]);
|
|
command_create_nack(&usb_tx_buffer[TX_BUFFER_SIZE + 1]);
|
|
|
|
int ret;
|
|
|
|
if (!device_is_ready(uart_dev)) {
|
|
LOG_ERR("CDC ACM device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
usb_context = usb_device_init(usb_msg_cb);
|
|
if (usb_context == NULL) {
|
|
LOG_ERR("Failed to initialize USB device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (!usbd_can_detect_vbus(usb_context)) {
|
|
ret = usbd_enable(usb_context);
|
|
if (ret) {
|
|
LOG_ERR("Failed to enable device support");
|
|
return ret;
|
|
}
|
|
}
|
|
k_msleep(100);
|
|
|
|
uart_irq_callback_set(uart_dev, interrupt_handler);
|
|
uart_irq_rx_enable(uart_dev);
|
|
|
|
// RX THREAD
|
|
usb_rx_thread_id = k_thread_create(
|
|
&usb_rx_thread_data,
|
|
usb_rx_thread_stack,
|
|
K_THREAD_STACK_SIZEOF(usb_rx_thread_stack),
|
|
usb_rx_thread,
|
|
NULL, NULL, NULL,
|
|
5,
|
|
0,
|
|
K_NO_WAIT
|
|
);
|
|
|
|
if (usb_rx_thread_id == NULL) {
|
|
LOG_ERR("Failed to create USB RX thread");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// TX THREAD
|
|
usb_tx_thread_id = k_thread_create(
|
|
&usb_tx_thread_data,
|
|
usb_tx_thread_stack,
|
|
K_THREAD_STACK_SIZEOF(usb_tx_thread_stack),
|
|
usb_tx_thread,
|
|
NULL, NULL, NULL,
|
|
5,
|
|
0,
|
|
K_NO_WAIT
|
|
);
|
|
|
|
if (usb_tx_thread_id == NULL) {
|
|
LOG_ERR("Failed to create USB TX thread");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct command_message_t* usb_get_next_tx_buf() {
|
|
struct command_message_t *buf = usb_tx_buf_ptr;
|
|
|
|
// Increment the buffer pointer
|
|
usb_tx_buf_ptr++;
|
|
|
|
if (usb_tx_buf_ptr > &usb_tx_buffer[TX_BUFFER_SIZE-1]) {
|
|
usb_tx_buf_ptr = usb_tx_buffer;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
int usb_send_command(struct command_message_t *msg) {
|
|
|
|
k_msgq_put(&usb_tx_ptr_msgq, &msg, K_NO_WAIT);
|
|
|
|
return 0;
|
|
} |