candleLight_fw/src/usbd_gs_can.c

838 lines
25 KiB
C

/*
The MIT License (MIT)
Copyright (c) 2016 Hubert Denkmair
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "can.h"
#include "compiler.h"
#include "config.h"
#include "gpio.h"
#include "gs_usb.h"
#include "hal_include.h"
#include "led.h"
#include "timer.h"
#include "usbd_core.h"
#include "usbd_ctlreq.h"
#include "usbd_def.h"
#include "usbd_desc.h"
#include "usbd_gs_can.h"
#include "usbd_ioreq.h"
#include "util.h"
static volatile bool is_usb_suspend_cb = false;
/* Configuration Descriptor */
static const uint8_t USBD_GS_CAN_CfgDesc[USB_CAN_CONFIG_DESC_SIZ] =
{
/*---------------------------------------------------------------------------*/
/* Configuration Descriptor */
0x09, /* bLength */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType */
USB_CAN_CONFIG_DESC_SIZ, /* wTotalLength */
0x00,
0x02, /* bNumInterfaces */
0x01, /* bConfigurationValue */
USBD_IDX_CONFIG_STR, /* iConfiguration */
0x80, /* bmAttributes */
0x4B, /* MaxPower 150 mA */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* GS_USB Interface Descriptor */
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0xFF, /* bInterfaceClass: Vendor Specific*/
0xFF, /* bInterfaceSubClass: Vendor Specific */
0xFF, /* bInterfaceProtocol: Vendor Specific */
0x00, /* iInterface */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* EP1 descriptor */
0x07, /* bLength */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
GSUSB_ENDPOINT_IN, /* bEndpointAddress */
0x02, /* bmAttributes: bulk */
LOBYTE(CAN_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize */
HIBYTE(CAN_DATA_MAX_PACKET_SIZE),
0x00, /* bInterval: */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* EP2 descriptor */
0x07, /* bLength */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
GSUSB_ENDPOINT_OUT, /* bEndpointAddress */
0x02, /* bmAttributes: bulk */
LOBYTE(CAN_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize */
HIBYTE(CAN_DATA_MAX_PACKET_SIZE),
0x00, /* bInterval: */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* DFU Interface Descriptor */
/*---------------------------------------------------------------------------*/
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
DFU_INTERFACE_NUM, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x00, /* bNumEndpoints */
0xFE, /* bInterfaceClass: Vendor Specific*/
0x01, /* bInterfaceSubClass */
0x01, /* bInterfaceProtocol : Runtime mode */
DFU_INTERFACE_STR_INDEX, /* iInterface */
/*---------------------------------------------------------------------------*/
/* Run-Time DFU Functional Descriptor */
/*---------------------------------------------------------------------------*/
0x09, /* bLength */
0x21, /* bDescriptorType: DFU FUNCTIONAL */
0x0B, /* bmAttributes: detach, upload, download */
0xFF, 0x00, /* wDetachTimeOut */
0x00, 0x08, /* wTransferSize */
0x1a, 0x01, /* bcdDFUVersion: 1.1a */
};
/* Microsoft OS String Descriptor */
static const uint8_t USBD_GS_CAN_WINUSB_STR[] =
{
0x12, /* length */
0x03, /* descriptor type == string */
0x4D, 0x00, 0x53, 0x00, /* signature: "MSFT100" */
0x46, 0x00, 0x54, 0x00,
0x31, 0x00, 0x30, 0x00,
0x30, 0x00,
USBD_GS_CAN_VENDOR_CODE, /* vendor code */
0x00 /* padding */
};
/* Microsoft Compatible ID Feature Descriptor */
static const uint8_t USBD_MS_COMP_ID_FEATURE_DESC[] = {
0x40, 0x00, 0x00, 0x00, /* length */
0x00, 0x01, /* version 1.0 */
0x04, 0x00, /* descr index (0x0004) */
0x02, /* number of sections */
0x00, 0x00, 0x00, 0x00, /* reserved */
0x00, 0x00, 0x00,
0x00, /* interface number */
0x01, /* reserved */
0x57, 0x49, 0x4E, 0x55, /* compatible ID ("WINUSB\0\0") */
0x53, 0x42, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, /* sub-compatible ID */
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, /* reserved */
0x00, 0x00,
0x01, /* interface number */
0x01, /* reserved */
0x57, 0x49, 0x4E, 0x55, /* compatible ID ("WINUSB\0\0") */
0x53, 0x42, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, /* sub-compatible ID */
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, /* reserved */
0x00, 0x00
};
/* Microsoft Extended Properties Feature Descriptor */
static const uint8_t USBD_MS_EXT_PROP_FEATURE_DESC[] = {
0x92, 0x00, 0x00, 0x00, /* length */
0x00, 0x01, /* version 1.0 */
0x05, 0x00, /* descr index (0x0005) */
0x01, 0x00, /* number of sections */
0x88, 0x00, 0x00, 0x00, /* property section size */
0x07, 0x00, 0x00, 0x00, /* property data type 7: Unicode REG_MULTI_SZ */
0x2a, 0x00, /* property name length */
0x44, 0x00, 0x65, 0x00, /* property name "DeviceInterfaceGUIDs" */
0x76, 0x00, 0x69, 0x00,
0x63, 0x00, 0x65, 0x00,
0x49, 0x00, 0x6e, 0x00,
0x74, 0x00, 0x65, 0x00,
0x72, 0x00, 0x66, 0x00,
0x61, 0x00, 0x63, 0x00,
0x65, 0x00, 0x47, 0x00,
0x55, 0x00, 0x49, 0x00,
0x44, 0x00, 0x73, 0x00,
0x00, 0x00,
0x50, 0x00, 0x00, 0x00, /* property data length */
0x7b, 0x00, 0x63, 0x00, /* property name: "{c15b4308-04d3-11e6-b3ea-6057189e6443}\0\0" */
0x31, 0x00, 0x35, 0x00,
0x62, 0x00, 0x34, 0x00,
0x33, 0x00, 0x30, 0x00,
0x38, 0x00, 0x2d, 0x00,
0x30, 0x00, 0x34, 0x00,
0x64, 0x00, 0x33, 0x00,
0x2d, 0x00, 0x31, 0x00,
0x31, 0x00, 0x65, 0x00,
0x36, 0x00, 0x2d, 0x00,
0x62, 0x00, 0x33, 0x00,
0x65, 0x00, 0x61, 0x00,
0x2d, 0x00, 0x36, 0x00,
0x30, 0x00, 0x35, 0x00,
0x37, 0x00, 0x31, 0x00,
0x38, 0x00, 0x39, 0x00,
0x65, 0x00, 0x36, 0x00,
0x34, 0x00, 0x34, 0x00,
0x33, 0x00, 0x7d, 0x00,
0x00, 0x00, 0x00, 0x00
};
// device info
static const struct gs_device_config USBD_GS_CAN_dconf = {
.icount = NUM_CAN_CHANNEL - 1,
.sw_version = 2,
.hw_version = 1,
};
/* It's unclear from the documentation, but it appears that the USB library is
* not safely reentrant. It attempts to signal errors via return values if it is
* reentered, but that code is not interrupt-safe and the error values are
* silently ignored within the library in several cases. We'll just disable
* interrupts at all entry points to be safe. Note that the callbacks are all
* called from within the libary itself, either within the interrupt handler or
* within other calls, which means the USB interrupt is already disabled and we
* don't have any other interrupts to worry about. */
static inline uint8_t USBD_GS_CAN_PrepareReceive(USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
struct gs_host_frame *frame = &hcan->from_host_buf->frame;
uint16_t size;
if (IS_ENABLED(CONFIG_CANFD)) {
size = struct_size(frame, canfd_ts, 1);
} else {
size = struct_size(frame, classic_can_ts, 1);
}
return USBD_LL_PrepareReceive(pdev, GSUSB_ENDPOINT_OUT, (uint8_t *)frame, size);
}
static uint8_t USBD_GS_CAN_Start(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
assert_basic(pdev->pClassData);
USBD_LL_OpenEP(pdev, GSUSB_ENDPOINT_IN, USBD_EP_TYPE_BULK, CAN_DATA_MAX_PACKET_SIZE);
USBD_LL_OpenEP(pdev, GSUSB_ENDPOINT_OUT, USBD_EP_TYPE_BULK, CAN_DATA_MAX_PACKET_SIZE);
USBD_GS_CAN_PrepareReceive(pdev);
return USBD_OK;
}
static uint8_t USBD_GS_CAN_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
USBD_LL_CloseEP(pdev, GSUSB_ENDPOINT_IN);
USBD_LL_CloseEP(pdev, GSUSB_ENDPOINT_OUT);
return USBD_OK;
}
static uint8_t USBD_GS_CAN_DFU_Request(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*) pdev->pClassData;
switch (req->bRequest) {
case 0: // DETACH request
hcan->dfu_detach_requested = true;
break;
case 3: // GET_STATUS request
hcan->ep0_buf[0] = 0x00; // bStatus: 0x00 == OK
hcan->ep0_buf[1] = 0x00; // bwPollTimeout
hcan->ep0_buf[2] = 0x00;
hcan->ep0_buf[3] = 0x00;
hcan->ep0_buf[4] = 0x00; // bState: appIDLE
hcan->ep0_buf[5] = 0xFF; // status string descriptor index
USBD_CtlSendData(pdev, hcan->ep0_buf, 6);
break;
default:
USBD_CtlError(pdev, req);
}
return USBD_OK;
}
static can_data_t *USBD_GS_CAN_GetChannel(USBD_GS_CAN_HandleTypeDef *hcan,
const uint16_t ch)
{
if (ch < ARRAY_SIZE(hcan->channels)) {
return &hcan->channels[ch];
}
return NULL;
}
static uint8_t USBD_GS_CAN_Config_Request(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*) pdev->pClassData;
struct gs_device_termination_state term_state;
can_data_t *channel;
const void *src = NULL;
size_t len;
/*
* For all "per device" USB control messages
* (GS_USB_BREQ_HOST_FORMAT and GS_USB_BREQ_DEVICE_CONFIG) the
* Linux gs_usb driver uses a req->wValue = 1.
*
* All other control messages are "per channel" and specify the
* channel number in req->wValue. So check req->wValue for valid
* CAN channel.
*
*/
if (!(req->bRequest == GS_USB_BREQ_HOST_FORMAT ||
req->bRequest == GS_USB_BREQ_DEVICE_CONFIG)) {
channel = USBD_GS_CAN_GetChannel(hcan, req->wValue);
if (!channel) {
goto out_fail;
}
}
switch (req->bRequest) {
// Host -> Device
case GS_USB_BREQ_HOST_FORMAT:
len = sizeof(struct gs_host_config);
break;
case GS_USB_BREQ_BITTIMING:
len = sizeof(struct gs_device_bittiming);
break;
case GS_USB_BREQ_MODE:
len = sizeof(struct gs_device_mode);
break;
case GS_USB_BREQ_BT_CONST:
src = &CAN_btconst;
len = sizeof(CAN_btconst);
break;
case GS_USB_BREQ_DEVICE_CONFIG:
src = &USBD_GS_CAN_dconf;
len = sizeof(USBD_GS_CAN_dconf);
break;
case GS_USB_BREQ_TIMESTAMP:
src = &hcan->sof_timestamp_us;
len = sizeof(hcan->sof_timestamp_us);
break;
case GS_USB_BREQ_IDENTIFY:
len = sizeof(struct gs_identify_mode);
break;
case GS_USB_BREQ_SET_TERMINATION:
if (get_term(req->wValue) == GS_CAN_TERMINATION_UNSUPPORTED) {
goto out_fail;
}
len = sizeof(struct gs_device_termination_state);
break;
case GS_USB_BREQ_GET_TERMINATION: {
enum gs_can_termination_state state;
state = get_term(req->wValue);
if (state == GS_CAN_TERMINATION_UNSUPPORTED) {
goto out_fail;
}
term_state.state = state;
src = &term_state;
len = sizeof(term_state);
break;
}
default:
goto out_fail;
}
if (req->wLength < len) {
goto out_fail;
}
switch (req->bRequest) {
case GS_USB_BREQ_HOST_FORMAT:
case GS_USB_BREQ_BITTIMING:
case GS_USB_BREQ_MODE:
case GS_USB_BREQ_IDENTIFY:
case GS_USB_BREQ_SET_TERMINATION:
if (req->wLength > sizeof(hcan->ep0_buf)) {
goto out_fail;
}
hcan->last_setup_request = *req;
USBD_CtlPrepareRx(pdev, hcan->ep0_buf, req->wLength);
break;
// Device -> Host
case GS_USB_BREQ_BT_CONST:
case GS_USB_BREQ_DEVICE_CONFIG:
case GS_USB_BREQ_TIMESTAMP:
case GS_USB_BREQ_GET_TERMINATION:
USBD_CtlSendData(pdev, (uint8_t *)src, len);
break;
default:
goto out_fail;
}
return USBD_OK;
out_fail:
USBD_CtlError(pdev, req);
return USBD_FAIL;
}
static uint8_t USBD_GS_CAN_Vendor_Request(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
uint8_t req_rcpt = req->bmRequest & 0x1F;
uint8_t req_type = (req->bmRequest >> 5) & 0x03;
if (
(req_type == 0x01) // class request
&& (req_rcpt == 0x01) // recipient: interface
&& (req->wIndex == DFU_INTERFACE_NUM)
) {
return USBD_GS_CAN_DFU_Request(pdev, req);
} else {
return USBD_GS_CAN_Config_Request(pdev, req);
}
}
static uint8_t USBD_GS_CAN_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
static uint8_t ifalt = 0;
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
case USB_REQ_TYPE_CLASS:
case USB_REQ_TYPE_VENDOR:
return USBD_GS_CAN_Vendor_Request(pdev, req);
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest) {
case USB_REQ_GET_INTERFACE:
USBD_CtlSendData(pdev, &ifalt, 1);
break;
case USB_REQ_SET_INTERFACE:
default:
break;
}
break;
default:
break;
}
return USBD_OK;
}
static const led_seq_step_t led_identify_seq[] = {
{ .state = 0x01, .time_in_10ms = 10 },
{ .state = 0x02, .time_in_10ms = 10 },
{ .state = 0x00, .time_in_10ms = 0 }
};
static uint8_t USBD_GS_CAN_EP0_RxReady(USBD_HandleTypeDef *pdev) {
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*) pdev->pClassData;
can_data_t *channel = NULL;
USBD_SetupReqTypedef *req = &hcan->last_setup_request;
/*
* The control messages GS_USB_BREQ_HOST_FORMAT and
* GS_USB_BREQ_DEVICE_CONFIG are per device, all others are per
* channel. The validity of req->wValue has been checked in
* USBD_GS_CAN_Config_Request() already.
*/
if (!(req->bRequest == GS_USB_BREQ_HOST_FORMAT ||
req->bRequest == GS_USB_BREQ_DEVICE_CONFIG))
channel = USBD_GS_CAN_GetChannel(hcan, req->wValue);
switch (req->bRequest) {
case GS_USB_BREQ_HOST_FORMAT:
/* The firmware on the original USB2CAN by Geschwister Schneider
* Technologie Entwicklungs- und Vertriebs UG exchanges all data
* between the host and the device in host byte order. This is done
* with the struct gs_host_config::byte_order member, which is sent
* first to indicate the desired byte order.
*
* The widely used open source firmware candleLight doesn't support
* this feature and exchanges the data in little endian byte order.
*/
break;
case GS_USB_BREQ_BITTIMING: {
const struct gs_device_bittiming *timing = (struct gs_device_bittiming *)hcan->ep0_buf;
can_set_bittiming(channel, timing);
break;
}
case GS_USB_BREQ_MODE: {
struct gs_device_mode *mode;
mode = (struct gs_device_mode*)hcan->ep0_buf;
if (mode->mode == GS_CAN_MODE_RESET) {
can_disable(channel);
led_set_mode(&channel->leds, LED_MODE_OFF);
} else if (mode->mode == GS_CAN_MODE_START) {
hcan->timestamps_enabled = (mode->flags & GS_CAN_MODE_HW_TIMESTAMP) != 0;
hcan->pad_pkts_to_max_pkt_size = (mode->flags & GS_CAN_MODE_PAD_PKTS_TO_MAX_PKT_SIZE) != 0;
can_enable(channel, mode->flags);
led_set_mode(&channel->leds, LED_MODE_NORMAL);
}
break;
}
case GS_USB_BREQ_IDENTIFY: {
struct gs_identify_mode *imode;
imode = (struct gs_identify_mode *)hcan->ep0_buf;
if (imode->mode) {
led_run_sequence(&channel->leds, led_identify_seq, -1);
} else {
led_set_mode(&channel->leds, can_is_enabled(channel) ?
LED_MODE_NORMAL : LED_MODE_OFF);
}
break;
}
case GS_USB_BREQ_SET_TERMINATION: {
if (get_term(req->wValue) != GS_CAN_TERMINATION_UNSUPPORTED) {
struct gs_device_termination_state *term_state;
term_state = (struct gs_device_termination_state *)hcan->ep0_buf;
if (set_term(req->wValue, term_state->state) == GS_CAN_TERMINATION_UNSUPPORTED) {
USBD_CtlError(pdev, req);
}
}
break;
}
default:
break;
}
req->bRequest = 0xFF;
return USBD_OK;
}
static uint8_t USBD_GS_CAN_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) {
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
(void) epnum;
bool was_irq_enabled = disable_irq();
list_add_tail(&hcan->to_host_buf->list, &hcan->list_frame_pool);
hcan->to_host_buf = NULL;
restore_irq(was_irq_enabled);
return USBD_OK;
}
// Note that the return value is completely ignored by the stack.
static uint8_t USBD_GS_CAN_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) {
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
can_data_t *channel;
uint32_t rxlen = USBD_LL_GetRxDataSize(pdev, epnum);
if (rxlen < (struct_size(&hcan->from_host_buf->frame, classic_can, 1))) {
// Invalid frame length, just ignore it and receive into the same buffer
// again next time.
goto out_prepare_receive;
}
channel = USBD_GS_CAN_GetChannel(hcan, hcan->from_host_buf->frame.channel);
if (!channel) {
goto out_prepare_receive;
}
if (IS_ENABLED(CONFIG_CANFD) &&
hcan->from_host_buf->frame.flags & GS_CAN_FLAG_FD &&
rxlen < struct_size(&hcan->from_host_buf->frame, canfd, 1)) {
goto out_prepare_receive;
}
bool was_irq_enabled = disable_irq();
// Enqueue the frame we just received.
list_add_tail(&hcan->from_host_buf->list, &channel->list_from_host);
// Grab a buffer for the next frame from the pool.
hcan->from_host_buf = list_first_entry_or_null(&hcan->list_frame_pool,
struct gs_host_frame_object,
list);
if (hcan->from_host_buf) {
list_del(&hcan->from_host_buf->list);
restore_irq(was_irq_enabled);
// We got a buffer! Get ready to receive from the USB host into it.
USBD_GS_CAN_PrepareReceive(pdev);
} else {
restore_irq(was_irq_enabled);
// gs_can has no way to drop packets. If we just drop this one, gs_can
// will fill up its queue of packets awaiting ACKs and then hang. Instead,
// wait to call PrepareReceive until we have a frame to receive into.
}
return USBD_OK;
out_prepare_receive:
USBD_GS_CAN_PrepareReceive(pdev);
return USBD_OK;
}
static uint8_t USBD_GS_CAN_SOF(struct _USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*) pdev->pClassData;
hcan->sof_timestamp_us = timer_get();
return USBD_OK;
}
static uint8_t *USBD_GS_CAN_GetCfgDesc(uint16_t *len)
{
/*
* The USB stack writes to the returned buffer, so it cannot live
* in ROM.
*/
*len = sizeof(USBD_GS_CAN_CfgDesc);
memcpy(USBD_DescBuf, USBD_GS_CAN_CfgDesc, sizeof(USBD_GS_CAN_CfgDesc));
return USBD_DescBuf;
}
uint8_t *USBD_GS_CAN_GetStrDesc(USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length)
{
UNUSED(pdev);
switch (index) {
case DFU_INTERFACE_STR_INDEX:
USBD_GetString(DFU_INTERFACE_STRING_FS, USBD_DescBuf, length);
return USBD_DescBuf;
case 0xEE:
*length = sizeof(USBD_GS_CAN_WINUSB_STR);
memcpy(USBD_DescBuf, USBD_GS_CAN_WINUSB_STR, sizeof(USBD_GS_CAN_WINUSB_STR));
return USBD_DescBuf;
default:
*length = 0;
USBD_CtlError(pdev, 0);
return 0;
}
}
/* CAN interface class callbacks structure */
USBD_ClassTypeDef USBD_GS_CAN = {
.Init = USBD_GS_CAN_Start,
.DeInit = USBD_GS_CAN_DeInit,
.Setup = USBD_GS_CAN_Setup,
.EP0_RxReady = USBD_GS_CAN_EP0_RxReady,
.DataIn = USBD_GS_CAN_DataIn,
.DataOut = USBD_GS_CAN_DataOut,
.SOF = USBD_GS_CAN_SOF,
.GetHSConfigDescriptor = USBD_GS_CAN_GetCfgDesc,
.GetFSConfigDescriptor = USBD_GS_CAN_GetCfgDesc,
.GetOtherSpeedConfigDescriptor = USBD_GS_CAN_GetCfgDesc,
.GetUsrStrDescriptor = USBD_GS_CAN_GetStrDesc,
};
uint8_t USBD_GS_CAN_Init(USBD_GS_CAN_HandleTypeDef *hcan, USBD_HandleTypeDef *pdev)
{
pdev->pClassData = hcan;
return USBD_OK;
}
bool USBD_GS_CAN_CustomDeviceRequest(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
if (req->bRequest == USBD_GS_CAN_VENDOR_CODE) {
switch (req->wIndex) {
case 0x0004:
memcpy(USBD_DescBuf, USBD_MS_COMP_ID_FEATURE_DESC, sizeof(USBD_MS_COMP_ID_FEATURE_DESC));
USBD_CtlSendData(pdev, USBD_DescBuf, MIN(sizeof(USBD_MS_COMP_ID_FEATURE_DESC), req->wLength));
return true;
case 0x0005:
if (req->wValue==0) { // only return our GUID for interface #0
memcpy(USBD_DescBuf, USBD_MS_EXT_PROP_FEATURE_DESC, sizeof(USBD_MS_EXT_PROP_FEATURE_DESC));
USBD_CtlSendData(pdev, USBD_DescBuf, MIN(sizeof(USBD_MS_EXT_PROP_FEATURE_DESC), req->wLength));
return true;
}
break;
}
}
return false;
}
bool USBD_GS_CAN_CustomInterfaceRequest(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
return USBD_GS_CAN_CustomDeviceRequest(pdev, req);
}
void USBD_GS_CAN_ReceiveFromHost(USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
bool was_irq_enabled = disable_irq();
if (hcan->from_host_buf) {
restore_irq(was_irq_enabled);
return;
}
hcan->from_host_buf = list_first_entry_or_null(&hcan->list_frame_pool,
struct gs_host_frame_object,
list);
if (!hcan->from_host_buf) {
restore_irq(was_irq_enabled);
return;
}
list_del(&hcan->from_host_buf->list);
restore_irq(was_irq_enabled);
USBD_GS_CAN_PrepareReceive(pdev);
}
static uint8_t USBD_GS_CAN_Transmit(USBD_HandleTypeDef *pdev, uint8_t *buf, uint16_t len)
{
if (false == is_usb_suspend_cb) {
USBD_LL_Transmit(pdev, GSUSB_ENDPOINT_IN, buf, len);
return USBD_OK;
} else {
return USBD_BUSY;
}
}
static uint8_t USBD_GS_CAN_SendFrame(USBD_HandleTypeDef *pdev, struct gs_host_frame *frame)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
uint8_t buf[CAN_DATA_MAX_PACKET_SIZE];
uint8_t *send_addr;
size_t len;
if (IS_ENABLED(CONFIG_CANFD) &&
frame->flags & GS_CAN_FLAG_FD) {
if (hcan->timestamps_enabled) {
len = struct_size(frame, canfd_ts, 1);
} else {
len = struct_size(frame, canfd, 1);
}
} else {
if (hcan->timestamps_enabled) {
len = struct_size(frame, classic_can_ts, 1);
} else {
len = struct_size(frame, classic_can, 1);
}
}
send_addr = (uint8_t *)frame;
/*
* When talking to WinUSB it seems to help a lot if the size of
* packet you send equals the max packet size. In this mode, fill
* packets out to max packet size and then send.
*
* Don't know if the above observation is still true for CAN-FD
* (CAN-FD frames don't fit into a single the full speed USB
* packet of 64 byte), so don't do any padding for CAN-FD frames
* for now.
*/
if (hcan->pad_pkts_to_max_pkt_size &&
!((IS_ENABLED(CONFIG_CANFD) && frame->flags & GS_CAN_FLAG_FD))) {
memcpy(buf, frame, len);
// zero rest of buffer
memset(buf + len, 0, sizeof(buf) - len);
send_addr = buf;
len = sizeof(buf);
}
return USBD_GS_CAN_Transmit(pdev, send_addr, len);
}
void USBD_GS_CAN_SendToHost(USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
bool was_irq_enabled = disable_irq();
if (hcan->to_host_buf) {
restore_irq(was_irq_enabled);
return;
}
hcan->to_host_buf = list_first_entry_or_null(&hcan->list_to_host,
struct gs_host_frame_object,
list);
if (!hcan->to_host_buf) {
restore_irq(was_irq_enabled);
return;
}
list_del(&hcan->to_host_buf->list);
restore_irq(was_irq_enabled);
uint8_t result = USBD_GS_CAN_SendFrame(pdev, &hcan->to_host_buf->frame);
if (result == USBD_OK)
return;
was_irq_enabled = disable_irq();
list_add(&hcan->to_host_buf->list, &hcan->list_frame_pool);
hcan->to_host_buf = NULL;
restore_irq(was_irq_enabled);
}
bool USBD_GS_CAN_DfuDetachRequested(USBD_HandleTypeDef *pdev)
{
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;
return hcan->dfu_detach_requested;
}
// Handle USB suspend event
void USBD_GS_CAN_SuspendCallback(USBD_HandleTypeDef *pdev)
{
// Disable CAN and go off bus on USB suspend
USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*) pdev->pClassData;
for (unsigned int i = 0; i < ARRAY_SIZE(hcan->channels); i++) {
can_data_t *channel = &hcan->channels[i];
can_disable(channel);
led_set_mode(&channel->leds, LED_MODE_OFF);
}
is_usb_suspend_cb = true;
}
void USBD_GS_CAN_ResumeCallback(USBD_HandleTypeDef *pdev)
{
(void)pdev;
is_usb_suspend_cb = false;
}