Skip to content

Commit

Permalink
usb: device: gs_usb: introduce a compatibility mode kconfig option
Browse files Browse the repository at this point in the history
Introduce a Kconfig option for turning off 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.

Signed-off-by: Henrik Brix Andersen <[email protected]>
  • Loading branch information
henrikbrixandersen committed Dec 29, 2024
1 parent 0cc807e commit 72396ba
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 10 deletions.
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
34 changes: 29 additions & 5 deletions subsys/usb/device/class/gs_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@ LOG_MODULE_REGISTER(gs_usb, CONFIG_USB_DEVICE_GS_USB_LOG_LEVEL);

/* USB endpoint indexes */
#define GS_USB_IN_EP_IDX 0U
#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;
#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;

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

#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 Down Expand Up @@ -1047,7 +1055,6 @@ static void gs_usb_can_tx_callback(const struct device *can_dev, int error, void
static void gs_usb_transfer_tx_callback(uint8_t ep, int tsize, void *priv)
{
const struct device *dev = priv;
struct usb_cfg_data *cfg = (void *)dev->config;
struct gs_usb_data *data = dev->data;
struct net_buf *buf;

Expand All @@ -1058,11 +1065,17 @@ static void gs_usb_transfer_tx_callback(uint8_t ep, int tsize, void *priv)
return;
}

#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);
}
Expand All @@ -1075,15 +1088,19 @@ 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;

#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 @@ -1367,9 +1384,11 @@ 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);
#ifdef CONFIG_USB_DEVICE_GS_USB_COMPATIBILITY_MODE
LOG_DBG("EP OUT1 addr = 0x%02x", cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr);
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_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:
Expand All @@ -1379,7 +1398,9 @@ 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);
#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:
Expand Down Expand Up @@ -1471,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 @@ -1501,7 +1523,8 @@ 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_out1_ep = INITIALIZER_IF_EP(GS_USB_OUT1_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), \
}; \
\
Expand All @@ -1510,10 +1533,11 @@ static int gs_usb_init(const struct device *dev)
.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_OUT1_EP_ADDR, \
}, \
},)) \
{ \
.ep_cb = usb_transfer_ep_callback, \
.ep_addr = GS_USB_OUT2_EP_ADDR, \
Expand Down
18 changes: 18 additions & 0 deletions subsys/usb/device_next/class/Kconfig.gs_usb
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,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
29 changes: 24 additions & 5 deletions subsys/usb/device_next/class/gs_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ struct gs_usb_desc {
struct usb_association_descriptor iad;
struct usb_if_descriptor if0;
struct usb_ep_descriptor if0_in_ep;
#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
struct usb_ep_descriptor if0_out1_ep;
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */
struct usb_ep_descriptor if0_out2_ep;
struct usb_ep_descriptor if0_hs_in_ep;
#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
struct usb_ep_descriptor if0_hs_out1_ep;
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */
struct usb_ep_descriptor if0_hs_out2_ep;
struct usb_desc_header nil_desc;
};
Expand Down Expand Up @@ -809,6 +813,7 @@ static uint8_t gs_usb_get_bulk_in_ep_addr(struct usbd_class_data *const c_data)
return desc->if0_in_ep.bEndpointAddress;
}

#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
static uint8_t gs_usb_get_bulk_out1_ep_addr(struct usbd_class_data *const c_data)
{
const struct device *dev = usbd_class_get_private(c_data);
Expand All @@ -822,6 +827,7 @@ static uint8_t gs_usb_get_bulk_out1_ep_addr(struct usbd_class_data *const c_data

return desc->if0_out1_ep.bEndpointAddress;
}
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */

static uint8_t gs_usb_get_bulk_out2_ep_addr(struct usbd_class_data *const c_data)
{
Expand Down Expand Up @@ -1285,8 +1291,12 @@ static int gs_usb_request(struct usbd_class_data *const c_data, struct net_buf *

LOG_DBG("request complete for ep 0x%02x (err %d)", bi->ep, err);

#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
if (bi->ep == gs_usb_get_bulk_out1_ep_addr(c_data) ||
bi->ep == gs_usb_get_bulk_out2_ep_addr(c_data)) {
#else /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */
if (bi->ep == gs_usb_get_bulk_out2_ep_addr(c_data)) {
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */
if (!atomic_test_bit(&data->state, GS_USB_STATE_CLASS_ENABLED)) {
LOG_WRN("class not enabled");
net_buf_unref(buf);
Expand Down Expand Up @@ -1322,11 +1332,13 @@ static void gs_usb_enable(struct usbd_class_data *const c_data)
atomic_set_bit(&data->state, GS_USB_STATE_CLASS_ENABLED);
LOG_DBG("enabled");

#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
ep = gs_usb_get_bulk_out1_ep_addr(c_data);
err = gs_usb_out_start(c_data, ep);
if (err != 0) {
LOG_ERR("failed to start OUT transfer for ep 0x%02x (err %d)", ep, err);
}
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */

ep = gs_usb_get_bulk_out2_ep_addr(c_data);
err = gs_usb_out_start(c_data, ep);
Expand Down Expand Up @@ -1356,11 +1368,13 @@ static void gs_usb_disable(struct usbd_class_data *const c_data)
LOG_ERR("failed to dequeue IN ep 0x%02x (err %d)", ep, err);
}

#ifdef CONFIG_USBD_GS_USB_COMPATIBILITY_MODE
ep = gs_usb_get_bulk_out1_ep_addr(c_data);
err = usbd_ep_dequeue(uds_ctx, ep);
if (err != 0) {
LOG_ERR("failed to dequeue OUT ep 0x%02x (err %d)", ep, err);
}
#endif /* CONFIG_USBD_GS_USB_COMPATIBILITY_MODE */

ep = gs_usb_get_bulk_out2_ep_addr(c_data);
err = usbd_ep_dequeue(uds_ctx, ep);
Expand Down Expand Up @@ -1630,7 +1644,8 @@ struct usbd_class_api gs_usb_api = {
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 0, \
.bAlternateSetting = 0, \
.bNumEndpoints = 3, \
.bNumEndpoints = \
COND_CODE_1(CONFIG_USBD_GS_USB_COMPATIBILITY_MODE, (3), (2)), \
.bInterfaceClass = USB_BCC_VENDOR, \
.bInterfaceSubClass = 0, \
.bInterfaceProtocol = 0, \
Expand All @@ -1644,14 +1659,15 @@ struct usbd_class_api gs_usb_api = {
.wMaxPacketSize = sys_cpu_to_le16(64), \
.bInterval = 0x00, \
}, \
IF_ENABLED(CONFIG_USBD_GS_USB_COMPATIBILITY_MODE, ( \
.if0_out1_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = GS_USB_OUT1_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(64U), \
.bInterval = 0x00, \
}, \
},)) \
.if0_out2_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
Expand All @@ -1668,14 +1684,15 @@ struct usbd_class_api gs_usb_api = {
.wMaxPacketSize = sys_cpu_to_le16(512), \
.bInterval = 0x00, \
}, \
IF_ENABLED(CONFIG_USBD_GS_USB_COMPATIBILITY_MODE, ( \
.if0_hs_out1_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = GS_USB_OUT1_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(512U), \
.bInterval = 0x00, \
}, \
},)) \
.if0_hs_out2_ep = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
Expand All @@ -1694,7 +1711,8 @@ struct usbd_class_api gs_usb_api = {
(struct usb_desc_header *)&gs_usb_desc_##n.iad, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_in_ep, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_out1_ep, \
IF_ENABLED(CONFIG_USBD_GS_USB_COMPATIBILITY_MODE, ( \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_out1_ep,)) \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_out2_ep, \
(struct usb_desc_header *)&gs_usb_desc_##n.nil_desc, \
}; \
Expand All @@ -1703,7 +1721,8 @@ struct usbd_class_api gs_usb_api = {
(struct usb_desc_header *)&gs_usb_desc_##n.iad, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_hs_in_ep, \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_hs_out1_ep, \
IF_ENABLED(CONFIG_USBD_GS_USB_COMPATIBILITY_MODE, ( \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_hs_out1_ep,)) \
(struct usb_desc_header *)&gs_usb_desc_##n.if0_hs_out2_ep, \
(struct usb_desc_header *)&gs_usb_desc_##n.nil_desc, \
}
Expand Down

0 comments on commit 72396ba

Please sign in to comment.