348 lines
9.7 KiB
C
348 lines
9.7 KiB
C
/*
|
|
* 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 |= CAN_EFF_FLAG; /* Extended ID flag */
|
|
}
|
|
if (frame->flags & CAN_FRAME_RTR) {
|
|
gs_frame.can_id |= CAN_RTR_FLAG; /* RTR flag */
|
|
}
|
|
|
|
/* Handle CAN-FD specific flags */
|
|
if (frame->flags & CAN_FRAME_FDF) {
|
|
gs_frame.flags |= GS_CAN_FLAG_FD; /* FD frame */
|
|
}
|
|
if (frame->flags & CAN_FRAME_BRS) {
|
|
gs_frame.flags |= GS_CAN_FLAG_BRS; /* BRS frame */
|
|
}
|
|
if (frame->flags & CAN_FRAME_ESI) {
|
|
gs_frame.flags |= GS_CAN_FLAG_ESI; /* ESI frame */
|
|
}
|
|
|
|
gs_frame.can_dlc = frame->dlc;
|
|
gs_frame.channel = 0;
|
|
gs_frame.timestamp_us = k_uptime_get_32() * 1000; /* Convert to microseconds */
|
|
|
|
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 FD=%d", frame->id, frame->dlc,
|
|
(frame->flags & CAN_FRAME_FDF) ? 1 : 0);
|
|
}
|
|
|
|
int gs_usb_can_init(void)
|
|
{
|
|
int ret;
|
|
|
|
if (!device_is_ready(can_dev)) {
|
|
LOG_ERR("CAN device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Set default timing for 500kbps */
|
|
struct can_timing timing = {
|
|
.sjw = 1,
|
|
.prop_seg = 6,
|
|
.phase_seg1 = 7,
|
|
.phase_seg2 = 2,
|
|
.prescaler = 6
|
|
};
|
|
|
|
ret = can_set_timing(can_dev, &timing);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to set CAN timing: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* 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 add CAN RX filter: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
LOG_INF("gs_usb CAN interface initialized");
|
|
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);
|
|
}
|
|
|
|
/* 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 & CAN_EFF_MASK;
|
|
|
|
if (gs_frame->can_id & CAN_EFF_FLAG) {
|
|
frame.flags |= CAN_FRAME_IDE; /* Extended ID */
|
|
}
|
|
if (gs_frame->can_id & CAN_RTR_FLAG) {
|
|
frame.flags |= CAN_FRAME_RTR; /* RTR frame */
|
|
}
|
|
|
|
/* Handle CAN-FD flags */
|
|
if (gs_frame->flags & GS_CAN_FLAG_FD) {
|
|
frame.flags |= CAN_FRAME_FDF; /* FD frame */
|
|
}
|
|
if (gs_frame->flags & GS_CAN_FLAG_BRS) {
|
|
frame.flags |= CAN_FRAME_BRS; /* Bit rate switch */
|
|
}
|
|
if (gs_frame->flags & GS_CAN_FLAG_ESI) {
|
|
frame.flags |= CAN_FRAME_ESI; /* Error state indicator */
|
|
}
|
|
|
|
frame.dlc = gs_frame->can_dlc;
|
|
memcpy(frame.data, gs_frame->data, MIN(frame.dlc, sizeof(frame.data)));
|
|
|
|
/* 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.timestamp_us = k_uptime_get_32() * 1000; /* Convert to microseconds */
|
|
|
|
/* 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 FD=%d",
|
|
frame.id, frame.dlc, (frame.flags & CAN_FRAME_FDF) ? 1 : 0);
|
|
} else {
|
|
LOG_ERR("Failed to send CAN frame: %d", ret);
|
|
|
|
/* Send error frame back to host */
|
|
struct gs_host_frame error_frame = *gs_frame;
|
|
error_frame.flags |= GS_CAN_FLAG_ERROR;
|
|
gs_usb_send_frame_to_host((const uint8_t*)&error_frame, sizeof(error_frame));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Start CAN interface
|
|
*/
|
|
int gs_usb_can_start(void)
|
|
{
|
|
int ret = can_start(can_dev);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to start CAN interface: %d", ret);
|
|
} else {
|
|
LOG_INF("CAN interface started");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Stop CAN interface
|
|
*/
|
|
int gs_usb_can_stop(void)
|
|
{
|
|
int ret = can_stop(can_dev);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to stop CAN interface: %d", ret);
|
|
} else {
|
|
LOG_INF("CAN interface stopped");
|
|
}
|
|
return ret;
|
|
} |