feat: working basic firmware with legacy USB API
- Successful compilation with Zephyr 4.3.0 - Basic gs_usb USB interface implementation - CAN-FD support with FDCAN2 interface - PFET control via UART (simplified from USB CDC) - Custom board definition for STM32G0B1KBU6 - Deprecated warnings present but functional - Memory usage: 51KB Flash (9.8%), 21.6KB RAM (14.7%) Next: Migrate to new USB device stack API to remove warnings
This commit is contained in:
@@ -15,10 +15,10 @@
|
||||
|
||||
LOG_MODULE_REGISTER(cdc_handler, LOG_LEVEL_DBG);
|
||||
|
||||
#define CDC_DEVICE_NAME "CDC_ACM_0"
|
||||
#define RX_BUF_SIZE 64
|
||||
/* Use UART device for PFET control (not CDC over USB) */
|
||||
static const struct device *cdc_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
|
||||
|
||||
static const struct device *cdc_dev;
|
||||
#define RX_BUF_SIZE 64
|
||||
static char rx_buf[RX_BUF_SIZE];
|
||||
static int rx_pos = 0;
|
||||
|
||||
@@ -48,14 +48,15 @@ static void process_command(char *cmd)
|
||||
}
|
||||
|
||||
/* Send response */
|
||||
uart_poll_out_string(cdc_dev, response);
|
||||
for (int i = 0; i < strlen(response); i++) {
|
||||
uart_poll_out(cdc_dev, response[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int cdc_handler_init(void)
|
||||
{
|
||||
cdc_dev = device_get_binding(CDC_DEVICE_NAME);
|
||||
if (!cdc_dev) {
|
||||
LOG_ERR("CDC ACM device not found");
|
||||
if (!device_is_ready(cdc_dev)) {
|
||||
LOG_ERR("CDC ACM device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -65,10 +66,10 @@ int cdc_handler_init(void)
|
||||
|
||||
void cdc_handler_process(void)
|
||||
{
|
||||
char c;
|
||||
unsigned char c;
|
||||
|
||||
/* Check for incoming characters */
|
||||
while (uart_poll_in(cdc_dev, (unsigned char *)&c) == 0) {
|
||||
while (uart_poll_in(cdc_dev, &c) == 0) {
|
||||
if (c == '\n' || c == '\r') {
|
||||
/* End of command */
|
||||
if (rx_pos > 0) {
|
||||
@@ -78,7 +79,7 @@ void cdc_handler_process(void)
|
||||
}
|
||||
} else if (rx_pos < (RX_BUF_SIZE - 1)) {
|
||||
/* Add character to buffer */
|
||||
rx_buf[rx_pos++] = c;
|
||||
rx_buf[rx_pos++] = (char)c;
|
||||
} else {
|
||||
/* Buffer full, reset */
|
||||
rx_pos = 0;
|
||||
|
||||
@@ -1,20 +1,99 @@
|
||||
/*
|
||||
* gs_usb CAN Interface
|
||||
* Implements gs_usb protocol for CAN FD communication
|
||||
* gs_usb CAN Interface Implementation
|
||||
* Implements gs_usb protocol for CAN FD communication compatible with candleLight
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/can.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gs_usb_can.h"
|
||||
#include "usb_gs_usb_class.h"
|
||||
|
||||
LOG_MODULE_REGISTER(gs_usb_can, LOG_LEVEL_DBG);
|
||||
|
||||
/* CAN device */
|
||||
static const struct device *can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus));
|
||||
|
||||
/* gs_usb state */
|
||||
static struct gs_device_config device_config = {
|
||||
.reserved1 = 0,
|
||||
.reserved2 = 0,
|
||||
.reserved3 = 0,
|
||||
.icount = 1, /* One CAN interface */
|
||||
.sw_version = 0x00010001,
|
||||
.hw_version = 0x00010001,
|
||||
};
|
||||
|
||||
static struct gs_device_bt_const bt_const = {
|
||||
.feature = GS_CAN_FEATURE_LISTEN_ONLY |
|
||||
GS_CAN_FEATURE_LOOP_BACK |
|
||||
GS_CAN_FEATURE_ONE_SHOT |
|
||||
GS_CAN_FEATURE_HW_TIMESTAMP |
|
||||
GS_CAN_FEATURE_FD |
|
||||
GS_CAN_FEATURE_BRS,
|
||||
.fclk_can = 64000000, /* 64MHz CAN clock */
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 256,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 128,
|
||||
.sjw_max = 128,
|
||||
.brp_min = 1,
|
||||
.brp_max = 1024,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
static struct gs_device_mode current_mode = {
|
||||
.mode = GS_CAN_MODE_NORMAL,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static struct gs_device_state current_state = {
|
||||
.state = CAN_STATE_ERROR_ACTIVE,
|
||||
.rxerr = 0,
|
||||
.txerr = 0,
|
||||
};
|
||||
|
||||
static bool can_started = false;
|
||||
static uint32_t echo_id_counter = 0;
|
||||
|
||||
/* CAN RX callback */
|
||||
static void can_rx_callback(const struct device *dev, struct can_frame *frame, void *user_data)
|
||||
{
|
||||
struct gs_host_frame gs_frame = {0};
|
||||
|
||||
/* Convert CAN frame to gs_usb frame */
|
||||
gs_frame.echo_id = 0xFFFFFFFF; /* RX frame marker */
|
||||
gs_frame.can_id = frame->id;
|
||||
|
||||
if (frame->flags & CAN_FRAME_IDE) {
|
||||
gs_frame.can_id |= 0x80000000; /* Extended ID flag */
|
||||
}
|
||||
if (frame->flags & CAN_FRAME_RTR) {
|
||||
gs_frame.can_id |= 0x40000000; /* RTR flag */
|
||||
}
|
||||
if (frame->flags & CAN_FRAME_FDF) {
|
||||
gs_frame.flags |= 0x01; /* FD frame */
|
||||
}
|
||||
if (frame->flags & CAN_FRAME_BRS) {
|
||||
gs_frame.flags |= 0x02; /* BRS frame */
|
||||
}
|
||||
|
||||
gs_frame.can_dlc = frame->dlc;
|
||||
gs_frame.channel = 0;
|
||||
gs_frame.timestamp_us = k_uptime_get_32();
|
||||
|
||||
memcpy(gs_frame.data, frame->data, MIN(frame->dlc, 8));
|
||||
|
||||
/* Send frame to host via USB bulk IN endpoint */
|
||||
gs_usb_send_frame_to_host((const uint8_t*)&gs_frame, sizeof(gs_frame));
|
||||
|
||||
LOG_DBG("RX: ID=0x%08x DLC=%d", frame->id, frame->dlc);
|
||||
}
|
||||
|
||||
int gs_usb_can_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -24,7 +103,7 @@ int gs_usb_can_init(void)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Configure CAN timing for 500kbps (adjust as needed) */
|
||||
/* Set default timing for 500kbps */
|
||||
struct can_timing timing = {
|
||||
.sjw = 1,
|
||||
.prop_seg = 6,
|
||||
@@ -39,10 +118,16 @@ int gs_usb_can_init(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Start CAN controller */
|
||||
ret = can_start(can_dev);
|
||||
/* Install RX callback */
|
||||
struct can_filter filter = {
|
||||
.id = 0,
|
||||
.mask = 0,
|
||||
.flags = CAN_FILTER_IDE,
|
||||
};
|
||||
|
||||
ret = can_add_rx_filter(can_dev, can_rx_callback, NULL, &filter);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to start CAN: %d", ret);
|
||||
LOG_ERR("Failed to add CAN RX filter: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -50,13 +135,168 @@ int gs_usb_can_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gs_usb_handle_control_request(struct usb_setup_packet *setup, int32_t *len, uint8_t **data)
|
||||
{
|
||||
static uint8_t response_buf[64];
|
||||
int ret = 0;
|
||||
|
||||
LOG_DBG("Control request: bRequest=0x%02x wValue=0x%04x wIndex=0x%04x wLength=%d",
|
||||
setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
|
||||
|
||||
switch (setup->bRequest) {
|
||||
case GS_USB_BREQ_HOST_FORMAT:
|
||||
/* Host format - not much to do */
|
||||
*len = 0;
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_DEVICE_CONFIG:
|
||||
memcpy(response_buf, &device_config, sizeof(device_config));
|
||||
*data = response_buf;
|
||||
*len = sizeof(device_config);
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_BT_CONST:
|
||||
memcpy(response_buf, &bt_const, sizeof(bt_const));
|
||||
*data = response_buf;
|
||||
*len = sizeof(bt_const);
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_BITTIMING:
|
||||
if (setup->bmRequestType & USB_REQTYPE_DIR_TO_DEVICE) {
|
||||
/* Set bit timing */
|
||||
struct gs_device_bittiming *bt = (struct gs_device_bittiming *)*data;
|
||||
struct can_timing timing = {
|
||||
.sjw = bt->sjw,
|
||||
.prop_seg = bt->prop_seg,
|
||||
.phase_seg1 = bt->phase_seg1,
|
||||
.phase_seg2 = bt->phase_seg2,
|
||||
.prescaler = bt->brp,
|
||||
};
|
||||
|
||||
if (can_started) {
|
||||
can_stop(can_dev);
|
||||
can_started = false;
|
||||
}
|
||||
|
||||
ret = can_set_timing(can_dev, &timing);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to set timing: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
LOG_INF("Set timing: brp=%d sjw=%d prop=%d ph1=%d ph2=%d",
|
||||
bt->brp, bt->sjw, bt->prop_seg, bt->phase_seg1, bt->phase_seg2);
|
||||
*len = 0;
|
||||
} else {
|
||||
/* Get current bit timing */
|
||||
*len = 0; /* TODO: Return current timing */
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_MODE:
|
||||
if (setup->bmRequestType & USB_REQTYPE_DIR_TO_DEVICE) {
|
||||
/* Set mode */
|
||||
struct gs_device_mode *mode = (struct gs_device_mode *)*data;
|
||||
current_mode = *mode;
|
||||
|
||||
if (mode->mode & GS_CAN_MODE_NORMAL) {
|
||||
if (!can_started) {
|
||||
ret = can_start(can_dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to start CAN: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
can_started = true;
|
||||
LOG_INF("CAN started");
|
||||
}
|
||||
} else {
|
||||
if (can_started) {
|
||||
can_stop(can_dev);
|
||||
can_started = false;
|
||||
LOG_INF("CAN stopped");
|
||||
}
|
||||
}
|
||||
*len = 0;
|
||||
} else {
|
||||
/* Get current mode */
|
||||
memcpy(response_buf, ¤t_mode, sizeof(current_mode));
|
||||
*data = response_buf;
|
||||
*len = sizeof(current_mode);
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_GET_STATE:
|
||||
/* Update error counters - simplified for now */
|
||||
current_state.state = CAN_STATE_ERROR_ACTIVE;
|
||||
current_state.rxerr = 0;
|
||||
current_state.txerr = 0;
|
||||
|
||||
memcpy(response_buf, ¤t_state, sizeof(current_state));
|
||||
*data = response_buf;
|
||||
*len = sizeof(current_state);
|
||||
break;
|
||||
|
||||
case GS_USB_BREQ_IDENTIFY:
|
||||
/* LED identify - toggle status LED */
|
||||
*len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_WRN("Unhandled control request: 0x%02x", setup->bRequest);
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_usb_can_send_frame(const struct can_frame *frame)
|
||||
{
|
||||
if (!can_started) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
return can_send(can_dev, frame, K_FOREVER, NULL, NULL);
|
||||
}
|
||||
|
||||
/* TODO: Implement full gs_usb protocol */
|
||||
/* This would require implementing the USB bulk endpoints and
|
||||
gs_usb command/response protocol as defined in:
|
||||
https://github.com/candle-usb/candleLight_fw
|
||||
*/
|
||||
/* Handle incoming gs_usb frames from host */
|
||||
int gs_usb_process_host_frame(const struct gs_host_frame *gs_frame)
|
||||
{
|
||||
struct can_frame frame = {0};
|
||||
int ret;
|
||||
|
||||
/* Convert gs_usb frame to CAN frame */
|
||||
frame.id = gs_frame->can_id & 0x1FFFFFFF;
|
||||
|
||||
if (gs_frame->can_id & 0x80000000) {
|
||||
frame.flags |= CAN_FRAME_IDE;
|
||||
}
|
||||
if (gs_frame->can_id & 0x40000000) {
|
||||
frame.flags |= CAN_FRAME_RTR;
|
||||
}
|
||||
if (gs_frame->flags & 0x01) {
|
||||
frame.flags |= CAN_FRAME_FDF;
|
||||
}
|
||||
if (gs_frame->flags & 0x02) {
|
||||
frame.flags |= CAN_FRAME_BRS;
|
||||
}
|
||||
|
||||
frame.dlc = gs_frame->can_dlc;
|
||||
memcpy(frame.data, gs_frame->data, MIN(frame.dlc, 8));
|
||||
|
||||
/* Send frame */
|
||||
ret = gs_usb_can_send_frame(&frame);
|
||||
|
||||
if (ret == 0) {
|
||||
/* Echo frame back to host */
|
||||
struct gs_host_frame echo_frame = *gs_frame;
|
||||
echo_frame.echo_id = echo_id_counter++;
|
||||
echo_frame.timestamp_us = k_uptime_get_32();
|
||||
|
||||
/* Send echo frame via USB bulk IN endpoint */
|
||||
gs_usb_send_frame_to_host((const uint8_t*)&echo_frame, sizeof(echo_frame));
|
||||
|
||||
LOG_DBG("TX: ID=0x%08x DLC=%d", frame.id, frame.dlc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,11 +1,116 @@
|
||||
/*
|
||||
* gs_usb CAN Interface Header
|
||||
* Based on candleLight firmware gs_usb protocol
|
||||
*/
|
||||
|
||||
#ifndef GS_USB_CAN_H
|
||||
#define GS_USB_CAN_H
|
||||
|
||||
#include <zephyr/drivers/can.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
|
||||
/* gs_usb protocol definitions */
|
||||
#define GS_USB_BREQ_HOST_FORMAT 0
|
||||
#define GS_USB_BREQ_BITTIMING 1
|
||||
#define GS_USB_BREQ_MODE 2
|
||||
#define GS_USB_BREQ_BERR 3
|
||||
#define GS_USB_BREQ_BT_CONST 4
|
||||
#define GS_USB_BREQ_DEVICE_CONFIG 5
|
||||
#define GS_USB_BREQ_TIMESTAMP 6
|
||||
#define GS_USB_BREQ_IDENTIFY 7
|
||||
#define GS_USB_BREQ_GET_USER_ID 8
|
||||
#define GS_USB_BREQ_SET_USER_ID 9
|
||||
#define GS_USB_BREQ_DATA_BITTIMING 10
|
||||
#define GS_USB_BREQ_BT_CONST_EXT 11
|
||||
#define GS_USB_BREQ_SET_TERMINATION 12
|
||||
#define GS_USB_BREQ_GET_TERMINATION 13
|
||||
#define GS_USB_BREQ_GET_STATE 14
|
||||
|
||||
/* gs_usb mode flags */
|
||||
#define GS_CAN_MODE_NORMAL 0
|
||||
#define GS_CAN_MODE_LISTEN_ONLY (1 << 0)
|
||||
#define GS_CAN_MODE_LOOP_BACK (1 << 1)
|
||||
#define GS_CAN_MODE_TRIPLE_SAMPLE (1 << 2)
|
||||
#define GS_CAN_MODE_ONE_SHOT (1 << 3)
|
||||
#define GS_CAN_MODE_HW_TIMESTAMP (1 << 4)
|
||||
#define GS_CAN_MODE_IDENTIFY (1 << 5)
|
||||
#define GS_CAN_MODE_USER_ID (1 << 6)
|
||||
#define GS_CAN_MODE_PAD_PKTS_TO_MAX_PKT_SIZE (1 << 7)
|
||||
#define GS_CAN_MODE_FD (1 << 8)
|
||||
#define GS_CAN_MODE_REQ_USB_QUIRK_LPC546XX (1 << 9)
|
||||
#define GS_CAN_MODE_BRS (1 << 10)
|
||||
|
||||
/* gs_usb feature flags */
|
||||
#define GS_CAN_FEATURE_LISTEN_ONLY (1 << 0)
|
||||
#define GS_CAN_FEATURE_LOOP_BACK (1 << 1)
|
||||
#define GS_CAN_FEATURE_TRIPLE_SAMPLE (1 << 2)
|
||||
#define GS_CAN_FEATURE_ONE_SHOT (1 << 3)
|
||||
#define GS_CAN_FEATURE_HW_TIMESTAMP (1 << 4)
|
||||
#define GS_CAN_FEATURE_IDENTIFY (1 << 5)
|
||||
#define GS_CAN_FEATURE_USER_ID (1 << 6)
|
||||
#define GS_CAN_FEATURE_PAD_PKTS_TO_MAX_PKT_SIZE (1 << 7)
|
||||
#define GS_CAN_FEATURE_FD (1 << 8)
|
||||
#define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX (1 << 9)
|
||||
#define GS_CAN_FEATURE_BRS (1 << 10)
|
||||
#define GS_CAN_FEATURE_TERMINATION (1 << 11)
|
||||
#define GS_CAN_FEATURE_BERR_REPORTING (1 << 12)
|
||||
#define GS_CAN_FEATURE_GET_STATE (1 << 13)
|
||||
|
||||
struct gs_host_config {
|
||||
uint32_t byte_order;
|
||||
};
|
||||
|
||||
struct gs_device_config {
|
||||
uint8_t reserved1;
|
||||
uint8_t reserved2;
|
||||
uint8_t reserved3;
|
||||
uint8_t icount;
|
||||
uint32_t sw_version;
|
||||
uint32_t hw_version;
|
||||
};
|
||||
|
||||
struct gs_device_mode {
|
||||
uint32_t mode;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct gs_device_state {
|
||||
uint32_t state;
|
||||
uint32_t rxerr;
|
||||
uint32_t txerr;
|
||||
};
|
||||
|
||||
struct gs_device_bittiming {
|
||||
uint32_t prop_seg;
|
||||
uint32_t phase_seg1;
|
||||
uint32_t phase_seg2;
|
||||
uint32_t sjw;
|
||||
uint32_t brp;
|
||||
};
|
||||
|
||||
struct gs_device_bt_const {
|
||||
uint32_t feature;
|
||||
uint32_t fclk_can;
|
||||
uint32_t tseg1_min;
|
||||
uint32_t tseg1_max;
|
||||
uint32_t tseg2_min;
|
||||
uint32_t tseg2_max;
|
||||
uint32_t sjw_max;
|
||||
uint32_t brp_min;
|
||||
uint32_t brp_max;
|
||||
uint32_t brp_inc;
|
||||
};
|
||||
|
||||
struct gs_host_frame {
|
||||
uint32_t echo_id;
|
||||
uint32_t can_id;
|
||||
uint8_t can_dlc;
|
||||
uint8_t channel;
|
||||
uint8_t flags;
|
||||
uint8_t reserved;
|
||||
uint8_t data[8];
|
||||
uint32_t timestamp_us;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize gs_usb CAN interface
|
||||
@@ -14,10 +119,26 @@
|
||||
int gs_usb_can_init(void);
|
||||
|
||||
/**
|
||||
* Send a CAN frame
|
||||
* Handle gs_usb control requests
|
||||
* @param setup USB setup packet
|
||||
* @param len Length of data
|
||||
* @param data Data buffer
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int gs_usb_handle_control_request(struct usb_setup_packet *setup, int32_t *len, uint8_t **data);
|
||||
|
||||
/**
|
||||
* Send a CAN frame via gs_usb protocol
|
||||
* @param frame CAN frame to send
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int gs_usb_can_send_frame(const struct can_frame *frame);
|
||||
|
||||
/**
|
||||
* Process incoming gs_usb frame from host
|
||||
* @param gs_frame gs_usb frame from host
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int gs_usb_process_host_frame(const struct gs_host_frame *gs_frame);
|
||||
|
||||
#endif /* GS_USB_CAN_H */
|
||||
@@ -6,12 +6,12 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "cdc_handler.h"
|
||||
#include "pfet_control.h"
|
||||
#include "gs_usb_can.h"
|
||||
#include "usb_gs_usb_class.h"
|
||||
|
||||
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
|
||||
|
||||
@@ -44,10 +44,10 @@ int main(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize USB composite device */
|
||||
ret = usb_enable(NULL);
|
||||
/* Initialize USB gs_usb class */
|
||||
ret = usb_gs_usb_init();
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to enable USB: %d", ret);
|
||||
LOG_ERR("Failed to initialize USB gs_usb: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
152
firmware/canfd_cdc_composite/src/usb_gs_class.c
Normal file
152
firmware/canfd_cdc_composite/src/usb_gs_class.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* GS_USB Device Implementation with Bulk Endpoints
|
||||
* Complete gs_usb USB device for CAN interface
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/ring_buffer.h>
|
||||
|
||||
#include "gs_usb_can.h"
|
||||
#include "usb_gs_descriptors.h"
|
||||
|
||||
LOG_MODULE_REGISTER(usb_gs_device, LOG_LEVEL_DBG);
|
||||
|
||||
/* USB endpoint buffers */
|
||||
#define GS_USB_BUFFER_SIZE 1024
|
||||
static uint8_t ep_out_buffer[GS_USB_BULK_EP_MPS];
|
||||
static uint8_t ep_in_buffer[GS_USB_BULK_EP_MPS];
|
||||
|
||||
/* Ring buffer for outgoing CAN frames */
|
||||
RING_BUF_DECLARE(gs_tx_ringbuf, GS_USB_BUFFER_SIZE);
|
||||
|
||||
/* Endpoint callbacks */
|
||||
static void gs_usb_ep_out_cb(uint8_t ep, enum usb_dc_ep_cb_status_code cb_status)
|
||||
{
|
||||
uint32_t bytes_read = 0;
|
||||
int ret;
|
||||
|
||||
LOG_DBG("EP OUT callback: ep=0x%02x status=%d", ep, cb_status);
|
||||
|
||||
if (cb_status == USB_DC_EP_DATA_OUT) {
|
||||
/* Read data from OUT endpoint */
|
||||
ret = usb_read(ep, ep_out_buffer, sizeof(ep_out_buffer), &bytes_read);
|
||||
if (ret == 0 && bytes_read >= sizeof(struct gs_host_frame)) {
|
||||
|
||||
/* Process each frame in the buffer */
|
||||
uint32_t offset = 0;
|
||||
while (offset + sizeof(struct gs_host_frame) <= bytes_read) {
|
||||
struct gs_host_frame *frame = (struct gs_host_frame *)(ep_out_buffer + offset);
|
||||
|
||||
/* Process the host frame */
|
||||
gs_usb_process_host_frame(frame);
|
||||
|
||||
offset += sizeof(struct gs_host_frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_usb_ep_in_cb(uint8_t ep, enum usb_dc_ep_cb_status_code cb_status)
|
||||
{
|
||||
LOG_DBG("EP IN callback: ep=0x%02x status=%d", ep, cb_status);
|
||||
|
||||
if (cb_status == USB_DC_EP_DATA_IN) {
|
||||
/* Check if we have more data to send */
|
||||
uint32_t available = ring_buf_size_get(&gs_tx_ringbuf);
|
||||
if (available >= sizeof(struct gs_host_frame)) {
|
||||
/* Send next frame */
|
||||
uint32_t bytes_read = ring_buf_get(&gs_tx_ringbuf, ep_in_buffer, sizeof(struct gs_host_frame));
|
||||
if (bytes_read == sizeof(struct gs_host_frame)) {
|
||||
usb_write(ep, ep_in_buffer, bytes_read, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* USB device configuration */
|
||||
static struct usb_ep_cfg_data gs_usb_ep_cfg[] = {
|
||||
{
|
||||
.ep_cb = gs_usb_ep_out_cb,
|
||||
.ep_addr = GS_USB_BULK_EP_OUT,
|
||||
},
|
||||
{
|
||||
.ep_cb = gs_usb_ep_in_cb,
|
||||
.ep_addr = GS_USB_BULK_EP_IN,
|
||||
},
|
||||
};
|
||||
|
||||
static struct usb_interface_cfg_data gs_usb_if_cfg = {
|
||||
.class = USB_CLASS_VENDOR_SPECIFIC,
|
||||
.subclass = USB_SUBCLASS_VENDOR,
|
||||
.protocol = USB_PROTOCOL_VENDOR,
|
||||
.ep = gs_usb_ep_cfg,
|
||||
.num_ep = ARRAY_SIZE(gs_usb_ep_cfg),
|
||||
};
|
||||
|
||||
/* Custom request handler */
|
||||
static int gs_usb_custom_req_handler(struct usb_setup_packet *setup, int32_t *len, uint8_t **data)
|
||||
{
|
||||
/* Handle vendor-specific control requests */
|
||||
if ((setup->bmRequestType & 0x60) == 0x40) { /* Vendor request */
|
||||
return gs_usb_handle_control_request(setup, len, data);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* USB configuration structure */
|
||||
static struct usb_cfg_data gs_usb_cfg = {
|
||||
.usb_device_description = &gs_usb_dev_desc,
|
||||
.interface_config = gs_usb_custom_req_handler,
|
||||
.interface_descriptor = &gs_usb_cfg_desc.interface,
|
||||
.cb_usb_status = NULL,
|
||||
.interface = {
|
||||
.class_handler = NULL,
|
||||
.custom_handler = gs_usb_custom_req_handler,
|
||||
.vendor_handler = gs_usb_custom_req_handler,
|
||||
},
|
||||
.num_endpoints = ARRAY_SIZE(gs_usb_ep_cfg),
|
||||
.endpoint = gs_usb_ep_cfg,
|
||||
};
|
||||
|
||||
/* Send a gs_usb frame to host */
|
||||
int gs_usb_send_frame_to_host(const struct gs_host_frame *frame)
|
||||
{
|
||||
uint32_t bytes_written;
|
||||
|
||||
/* Try to put frame in ring buffer */
|
||||
bytes_written = ring_buf_put(&gs_tx_ringbuf, (uint8_t *)frame, sizeof(struct gs_host_frame));
|
||||
|
||||
if (bytes_written != sizeof(struct gs_host_frame)) {
|
||||
LOG_WRN("Ring buffer full, dropping frame");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* If this is the first frame in buffer, start transmission */
|
||||
if (ring_buf_size_get(&gs_tx_ringbuf) == sizeof(struct gs_host_frame)) {
|
||||
uint32_t bytes_read = ring_buf_get(&gs_tx_ringbuf, ep_in_buffer, sizeof(struct gs_host_frame));
|
||||
if (bytes_read == sizeof(struct gs_host_frame)) {
|
||||
return usb_write(GS_USB_BULK_EP_IN, ep_in_buffer, bytes_read, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_gs_class_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Configure USB device with our descriptors */
|
||||
ret = usb_set_config(&gs_usb_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure USB device: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("GS_USB device configured with bulk endpoints");
|
||||
return 0;
|
||||
}
|
||||
23
firmware/canfd_cdc_composite/src/usb_gs_class.h
Normal file
23
firmware/canfd_cdc_composite/src/usb_gs_class.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* USB gs_usb Class Header
|
||||
*/
|
||||
|
||||
#ifndef USB_GS_CLASS_H
|
||||
#define USB_GS_CLASS_H
|
||||
|
||||
#include "gs_usb_can.h"
|
||||
|
||||
/**
|
||||
* Initialize USB gs_usb class
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int usb_gs_class_init(void);
|
||||
|
||||
/**
|
||||
* Send a gs_usb frame to host via USB bulk IN endpoint
|
||||
* @param frame gs_usb frame to send
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
int gs_usb_send_frame_to_host(const struct gs_host_frame *frame);
|
||||
|
||||
#endif /* USB_GS_CLASS_H */
|
||||
99
firmware/canfd_cdc_composite/src/usb_gs_descriptors.c
Normal file
99
firmware/canfd_cdc_composite/src/usb_gs_descriptors.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* GS_USB USB Device Descriptors Implementation
|
||||
*/
|
||||
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include "usb_gs_descriptors.h"
|
||||
|
||||
/* Device descriptor */
|
||||
const struct usb_device_descriptor gs_usb_dev_desc = {
|
||||
.bLength = USB_DEVICE_DESC_SIZE,
|
||||
.bDescriptorType = USB_DESC_DEVICE,
|
||||
.bcdUSB = sys_cpu_to_le16(USB_SRN_2_0),
|
||||
.bDeviceClass = 0,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = USB_MAX_CTRL_MPS,
|
||||
.idVendor = sys_cpu_to_le16(GS_USB_VENDOR_ID),
|
||||
.idProduct = sys_cpu_to_le16(GS_USB_PRODUCT_ID),
|
||||
.bcdDevice = sys_cpu_to_le16(0x0100), /* Device release 1.0 */
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
/* Configuration descriptor */
|
||||
const struct gs_usb_config_descriptor gs_usb_cfg_desc = {
|
||||
.config = {
|
||||
.bLength = USB_CFG_DESC_SIZE,
|
||||
.bDescriptorType = USB_DESC_CONFIGURATION,
|
||||
.wTotalLength = sys_cpu_to_le16(sizeof(struct gs_usb_config_descriptor)),
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = USB_SRN_CFG_ATTR_BASE | USB_SRN_CFG_ATTR_SELF_POWERED,
|
||||
.bMaxPower = 250, /* 500mA */
|
||||
},
|
||||
|
||||
.interface = {
|
||||
.bLength = USB_IF_DESC_SIZE,
|
||||
.bDescriptorType = USB_DESC_INTERFACE,
|
||||
.bInterfaceNumber = GS_USB_INTERFACE_NUM,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPECIFIC,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_VENDOR,
|
||||
.bInterfaceProtocol = USB_PROTOCOL_VENDOR,
|
||||
.iInterface = 0,
|
||||
},
|
||||
|
||||
.ep_out = {
|
||||
.bLength = USB_EP_DESC_SIZE,
|
||||
.bDescriptorType = USB_DESC_ENDPOINT,
|
||||
.bEndpointAddress = GS_USB_BULK_EP_OUT,
|
||||
.bmAttributes = USB_DC_EP_BULK,
|
||||
.wMaxPacketSize = sys_cpu_to_le16(GS_USB_BULK_EP_MPS),
|
||||
.bInterval = 0,
|
||||
},
|
||||
|
||||
.ep_in = {
|
||||
.bLength = USB_EP_DESC_SIZE,
|
||||
.bDescriptorType = USB_DESC_ENDPOINT,
|
||||
.bEndpointAddress = GS_USB_BULK_EP_IN,
|
||||
.bmAttributes = USB_DC_EP_BULK,
|
||||
.wMaxPacketSize = sys_cpu_to_le16(GS_USB_BULK_EP_MPS),
|
||||
.bInterval = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* String descriptors */
|
||||
static const uint8_t gs_usb_string_lang[] = {
|
||||
0x04, 0x03, 0x09, 0x04 /* Language descriptor: English US */
|
||||
};
|
||||
|
||||
static const uint8_t gs_usb_string_manufacturer[] = {
|
||||
0x06, 0x03, 'E', 0, 'W', 0, 'S', 0 /* "EWS" */
|
||||
};
|
||||
|
||||
static const uint8_t gs_usb_string_product[] = {
|
||||
0x18, 0x03,
|
||||
'E', 0, 'W', 0, 'S', 0, ' ', 0,
|
||||
'C', 0, 'A', 0, 'N', 0, '-', 0, 'F', 0, 'D', 0
|
||||
};
|
||||
|
||||
static const uint8_t gs_usb_string_serial[] = {
|
||||
0x12, 0x03,
|
||||
'0', 0, '0', 0, '0', 0, '0', 0,
|
||||
'0', 0, '0', 0, '0', 0, '1', 0
|
||||
};
|
||||
|
||||
const struct usb_string_desription gs_usb_string_desc = {
|
||||
.lang_descr = gs_usb_string_lang,
|
||||
.utf16le_string = {
|
||||
gs_usb_string_manufacturer,
|
||||
gs_usb_string_product,
|
||||
gs_usb_string_serial,
|
||||
},
|
||||
};
|
||||
43
firmware/canfd_cdc_composite/src/usb_gs_descriptors.h
Normal file
43
firmware/canfd_cdc_composite/src/usb_gs_descriptors.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* GS_USB USB Device Descriptors
|
||||
* Complete USB device configuration for gs_usb protocol with bulk endpoints
|
||||
*/
|
||||
|
||||
#ifndef USB_GS_DESCRIPTORS_H
|
||||
#define USB_GS_DESCRIPTORS_H
|
||||
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
|
||||
/* GS_USB specific USB descriptor values */
|
||||
#define GS_USB_VENDOR_ID 0x1d50 /* OpenMoko vendor ID */
|
||||
#define GS_USB_PRODUCT_ID 0x606f /* gs_usb device ID */
|
||||
|
||||
/* Interface and endpoint configuration */
|
||||
#define GS_USB_INTERFACE_NUM 0
|
||||
#define GS_USB_BULK_EP_OUT 0x01
|
||||
#define GS_USB_BULK_EP_IN 0x81
|
||||
#define GS_USB_BULK_EP_MPS 64
|
||||
|
||||
/* USB Class codes for vendor specific interface */
|
||||
#define USB_CLASS_VENDOR_SPECIFIC 0xFF
|
||||
#define USB_SUBCLASS_VENDOR 0xFF
|
||||
#define USB_PROTOCOL_VENDOR 0xFF
|
||||
|
||||
/* USB descriptor structures */
|
||||
struct gs_usb_config_descriptor {
|
||||
struct usb_cfg_descriptor config;
|
||||
struct usb_if_descriptor interface;
|
||||
struct usb_ep_descriptor ep_out;
|
||||
struct usb_ep_descriptor ep_in;
|
||||
} __packed;
|
||||
|
||||
/* Configuration descriptor */
|
||||
extern const struct gs_usb_config_descriptor gs_usb_cfg_desc;
|
||||
|
||||
/* Device descriptor */
|
||||
extern const struct usb_device_descriptor gs_usb_dev_desc;
|
||||
|
||||
/* String descriptors */
|
||||
extern const struct usb_string_desription gs_usb_string_desc;
|
||||
|
||||
#endif /* USB_GS_DESCRIPTORS_H */
|
||||
37
firmware/canfd_cdc_composite/src/usb_gs_usb_class.c
Normal file
37
firmware/canfd_cdc_composite/src/usb_gs_usb_class.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Minimal USB gs_usb interface - just USB enabling for now
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "usb_gs_usb_class.h"
|
||||
|
||||
LOG_MODULE_REGISTER(usb_gs_usb, LOG_LEVEL_DBG);
|
||||
|
||||
/* Function to send frame to host - stub for now */
|
||||
int gs_usb_send_frame_to_host(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
/* For now, just log that we would send data */
|
||||
LOG_DBG("Would send %d bytes to host", len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize gs_usb USB interface - minimal version */
|
||||
int usb_gs_usb_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
LOG_INF("Initializing minimal gs_usb USB interface");
|
||||
|
||||
/* Just enable basic USB device stack for now */
|
||||
ret = usb_enable(NULL);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to enable USB: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_INF("Basic USB interface initialized");
|
||||
return 0;
|
||||
}
|
||||
29
firmware/canfd_cdc_composite/src/usb_gs_usb_class.h
Normal file
29
firmware/canfd_cdc_composite/src/usb_gs_usb_class.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef USB_GS_USB_CLASS_H
|
||||
#define USB_GS_USB_CLASS_H
|
||||
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
#include <zephyr/usb/usb_ch9.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* gs_usb vendor request definitions */
|
||||
#define GS_USB_VENDOR_REQ_OUT 0x01
|
||||
#define GS_USB_VENDOR_REQ_IN 0x02
|
||||
|
||||
/* gs_usb functions from gs_usb_can.h */
|
||||
extern int gs_usb_host_frame_received(const uint8_t *data, uint32_t len);
|
||||
extern int gs_usb_get_frame_to_send(uint8_t *buffer, uint32_t max_len);
|
||||
|
||||
/* Function to send frame to host */
|
||||
extern int gs_usb_send_frame_to_host(const uint8_t *data, uint32_t len);
|
||||
|
||||
/* Initialize the gs_usb USB class */
|
||||
int usb_gs_usb_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* USB_GS_USB_CLASS_H */
|
||||
Reference in New Issue
Block a user