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

usb: device: gs_usb: convert dummy endpoint to fully functional OUT ep #61

Merged
Merged
Show file tree
Hide file tree
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
11 changes: 4 additions & 7 deletions include/cannectivity/usb/class/gs_usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,18 +458,15 @@ struct gs_usb_host_frame_hdr {
/**
* @name USB endpoint addresses
*
* Existing drivers expect endpoints 0x81 and 0x02. Include a dummy endpoint 0x01 to work-around the
* endpoint address fixup of the Zephyr USB device stack.
*
* @{
*/

/** USB bulk IN endpoint address */
#define GS_USB_IN_EP_ADDR 0x81
/** USB (dummy) bulk OUT endpoint address */
#define GS_USB_DUMMY_EP_ADDR 0x01
/** USB bulk OUT endpoint address */
#define GS_USB_OUT_EP_ADDR 0x02
/** USB bulk OUT1 endpoint address */
#define GS_USB_OUT1_EP_ADDR 0x01
/** USB bulk OUT2 endpoint address */
#define GS_USB_OUT2_EP_ADDR 0x02

/** @} */

Expand Down
18 changes: 18 additions & 0 deletions subsys/usb/device/class/Kconfig.gs_usb
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,22 @@ config USB_DEVICE_GS_USB_MAX_PACKET_SIZE
Maximum bulk endpoint packet size in bytes. Classic CAN host frames fit into 64 byte
packets, whereas CAN FD host frames can benefit from a maximum packet size of 512 bytes.

config USB_DEVICE_GS_USB_COMPATIBILITY_MODE
bool "Enable compatibility mode"
default y
help
Enable Geschwister Schneider USB/CAN (gs_usb) driver compatibility mode.

The Linux kernel gs_usb driver prior to kernel v6.12.5 uses hardcoded USB endpoint
addresses 0x81 (bulk IN) and 0x02 (bulk OUT). The same assumption on USB endpoint
addresses may be present in other drivers as well (e.g. python-can).

Depending on the capabilities of the USB device controller, the requested USB endpoint
addresses may be rewritten by the Zephyr USB device stack at runtime. Enabling
compatibility mode will include a second bulk OUT endpoint to ensure correct operation
with gs_usb drivers using the hardcoded USB endpoint addresses described above.

It is safe to enable compatibility mode regardless of the driver being used, but the
resulting firmware will require slightly more RAM and flash resources.

endif # USB_DEVICE_GS_USB
83 changes: 61 additions & 22 deletions subsys/usb/device/class/gs_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@ LOG_MODULE_REGISTER(gs_usb, CONFIG_USB_DEVICE_GS_USB_LOG_LEVEL);

/* USB endpoint indexes */
#define GS_USB_IN_EP_IDX 0U
#define GS_USB_DUMMY_EP_IDX 1U
#define GS_USB_OUT_EP_IDX 2U
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
#define GS_USB_OUT1_EP_IDX 1U
#define GS_USB_OUT2_EP_IDX 2U
#else /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
#define GS_USB_OUT2_EP_IDX 1U
#endif /* !CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */

struct gs_usb_config {
struct usb_association_descriptor iad;
struct usb_if_descriptor if0;
struct usb_ep_descriptor if0_in_ep;
struct usb_ep_descriptor if0_dummy_ep;
struct usb_ep_descriptor if0_out_ep;
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
struct usb_ep_descriptor if0_out1_ep;
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
struct usb_ep_descriptor if0_out2_ep;
} __packed;

struct gs_usb_channel_data {
Expand Down Expand Up @@ -61,7 +67,10 @@ struct gs_usb_data {
struct k_fifo rx_fifo;
struct k_thread rx_thread;

uint8_t tx_buffer[GS_USB_HOST_FRAME_MAX_SIZE];
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
uint8_t tx_buffer1[GS_USB_HOST_FRAME_MAX_SIZE];
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
uint8_t tx_buffer2[GS_USB_HOST_FRAME_MAX_SIZE];
struct k_fifo tx_fifo;
struct k_thread tx_thread;

Expand All @@ -72,7 +81,7 @@ struct gs_usb_data {
static sys_slist_t gs_usb_data_devlist;
static gs_usb_vendorcode_callback_t vendorcode_callback;

static void gs_usb_transfer_tx_prepare(const struct device *dev);
static void gs_usb_transfer_tx_prepare(const struct device *dev, uint8_t ep);
static int gs_usb_reset_channel(const struct device *dev, uint16_t ch);

static int gs_usb_request_host_format(int32_t tlen, const uint8_t *tdata)
Expand Down Expand Up @@ -1056,21 +1065,42 @@ static void gs_usb_transfer_tx_callback(uint8_t ep, int tsize, void *priv)
return;
}

net_buf_add_mem(buf, data->tx_buffer, tsize);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
struct usb_cfg_data *cfg = (void *)dev->config;

if (ep == cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr) {
net_buf_add_mem(buf, data->tx_buffer1, tsize);
} else {
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
net_buf_add_mem(buf, data->tx_buffer2, tsize);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
}
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */

k_fifo_put(&data->tx_fifo, buf);
}

gs_usb_transfer_tx_prepare(dev);
gs_usb_transfer_tx_prepare(dev, ep);
}

static void gs_usb_transfer_tx_prepare(const struct device *dev)
static void gs_usb_transfer_tx_prepare(const struct device *dev, uint8_t ep)
{
struct usb_cfg_data *cfg = (void *)dev->config;
struct gs_usb_data *data = dev->data;

usb_transfer(cfg->endpoint[GS_USB_OUT_EP_IDX].ep_addr, data->tx_buffer,
sizeof(data->tx_buffer), USB_TRANS_READ, gs_usb_transfer_tx_callback,
(void *)dev);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
if (ep == cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr) {
usb_transfer(cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr, data->tx_buffer1,
sizeof(data->tx_buffer1), USB_TRANS_READ, gs_usb_transfer_tx_callback,
(void *)dev);
} else {
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
usb_transfer(cfg->endpoint[GS_USB_OUT2_EP_IDX].ep_addr, data->tx_buffer2,
sizeof(data->tx_buffer2), USB_TRANS_READ, gs_usb_transfer_tx_callback,
(void *)dev);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
}
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
}

static void gs_usb_tx_thread(void *p1, void *p2, void *p3)
Expand Down Expand Up @@ -1354,9 +1384,12 @@ static void gs_usb_status_callback(struct usb_cfg_data *cfg, enum usb_dc_status_
case USB_DC_CONFIGURED:
LOG_DBG("USB device configured");
LOG_DBG("EP IN addr = 0x%02x", cfg->endpoint[GS_USB_IN_EP_IDX].ep_addr);
LOG_DBG("EP DUMMY addr = 0x%02x", cfg->endpoint[GS_USB_DUMMY_EP_IDX].ep_addr);
LOG_DBG("EP OUT addr = 0x%02x", cfg->endpoint[GS_USB_OUT_EP_IDX].ep_addr);
gs_usb_transfer_tx_prepare(common->dev);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
LOG_DBG("EP OUT1 addr = 0x%02x", cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr);
gs_usb_transfer_tx_prepare(common->dev, cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr);
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
LOG_DBG("EP OUT2 addr = 0x%02x", cfg->endpoint[GS_USB_OUT2_EP_IDX].ep_addr);
gs_usb_transfer_tx_prepare(common->dev, cfg->endpoint[GS_USB_OUT2_EP_IDX].ep_addr);
break;
case USB_DC_DISCONNECTED:
LOG_DBG("USB device disconnected");
Expand All @@ -1365,7 +1398,10 @@ static void gs_usb_status_callback(struct usb_cfg_data *cfg, enum usb_dc_status_
}

usb_cancel_transfer(cfg->endpoint[GS_USB_IN_EP_IDX].ep_addr);
usb_cancel_transfer(cfg->endpoint[GS_USB_OUT_EP_IDX].ep_addr);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
usb_cancel_transfer(cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr);
#endif /* CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE */
usb_cancel_transfer(cfg->endpoint[GS_USB_OUT2_EP_IDX].ep_addr);
break;
case USB_DC_SUSPEND:
LOG_DBG("USB device suspend");
Expand Down Expand Up @@ -1456,7 +1492,8 @@ static int gs_usb_init(const struct device *dev)
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 0, \
.bAlternateSetting = 0, \
.bNumEndpoints = 3, \
.bNumEndpoints = \
COND_CODE_1(CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE, (3), (2)), \
.bInterfaceClass = USB_BCC_VENDOR, \
.bInterfaceSubClass = 0, \
.bInterfaceProtocol = 0, \
Expand Down Expand Up @@ -1486,22 +1523,24 @@ static int gs_usb_init(const struct device *dev)
.iad = INITIALIZER_IAD, \
.if0 = INITIALIZER_IF, \
.if0_in_ep = INITIALIZER_IF_EP(GS_USB_IN_EP_ADDR), \
.if0_dummy_ep = INITIALIZER_IF_EP(GS_USB_DUMMY_EP_ADDR), \
.if0_out_ep = INITIALIZER_IF_EP(GS_USB_OUT_EP_ADDR), \
IF_ENABLED(CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE, \
(.if0_out1_ep = INITIALIZER_IF_EP(GS_USB_OUT1_EP_ADDR),)) \
.if0_out2_ep = INITIALIZER_IF_EP(GS_USB_OUT2_EP_ADDR), \
}; \
\
static struct usb_ep_cfg_data gs_usb_ep_cfg_data_##inst[] = { \
{ \
.ep_cb = usb_transfer_ep_callback, \
.ep_addr = GS_USB_IN_EP_ADDR, \
}, \
IF_ENABLED(CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE, ( \
{ \
.ep_cb = usb_transfer_ep_callback, \
.ep_addr = GS_USB_DUMMY_EP_ADDR, \
}, \
.ep_addr = GS_USB_OUT1_EP_ADDR, \
},)) \
{ \
.ep_cb = usb_transfer_ep_callback, \
.ep_addr = GS_USB_OUT_EP_ADDR, \
.ep_addr = GS_USB_OUT2_EP_ADDR, \
}, \
}; \
\
Expand Down
21 changes: 20 additions & 1 deletion subsys/usb/device_next/class/Kconfig.gs_usb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ config USBD_GS_USB_MAX_CHANNELS

config USBD_GS_USB_POOL_SIZE
int "Host frame buffer pool size"
default 20
default 20 if !USBD_GS_USB_COMPATIBILITY_MODE
default 21 if USBD_GS_USB_COMPATIBILITY_MODE
help
Size of the pool used for allocating Geschwister Schneider USB/CAN (gs_usb) host
frames. The pool is used for both RX and TX, and shared between all channels of a given
Expand Down Expand Up @@ -83,4 +84,22 @@ config USBD_GS_USB_TX_THREAD_PRIO
help
Priority level for the internal TX thread.

config USBD_GS_USB_COMPATIBILITY_MODE
bool "Enable compatibility mode"
default y
help
Enable Geschwister Schneider USB/CAN (gs_usb) driver compatibility mode.

The Linux kernel gs_usb driver prior to kernel v6.12.5 uses hardcoded USB endpoint
addresses 0x81 (bulk IN) and 0x02 (bulk OUT). The same assumption on USB endpoint
addresses may be present in other drivers as well (e.g. python-can).

Depending on the capabilities of the USB device controller, the requested USB endpoint
addresses may be rewritten by the Zephyr USB device stack at runtime. Enabling
compatibility mode will include a second bulk OUT endpoint to ensure correct operation
with gs_usb drivers using the hardcoded USB endpoint addresses described above.

It is safe to enable compatibility mode regardless of the driver being used, but the
resulting firmware will require slightly more RAM and flash resources.

endif # USBD_GS_USB
Loading