From 8f4af20c52b4cb3b6fe03678da148451a067e8cb Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Mon, 23 Dec 2024 16:12:40 +0000 Subject: [PATCH 1/2] usb: device: gs_usb: convert dummy endpoint to fully functional OUT ep Convert the dummy endpoint to a fully functional bulk OUT endpoint in order to be compatible with gs_usb drivers selecting the first available IN/OUT endpoints for use. Signed-off-by: Henrik Brix Andersen --- include/cannectivity/usb/class/gs_usb.h | 11 ++-- subsys/usb/device/class/gs_usb.c | 55 ++++++++++------ subsys/usb/device_next/class/gs_usb.c | 88 ++++++++++++++++--------- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/include/cannectivity/usb/class/gs_usb.h b/include/cannectivity/usb/class/gs_usb.h index 436737f..42d8401 100644 --- a/include/cannectivity/usb/class/gs_usb.h +++ b/include/cannectivity/usb/class/gs_usb.h @@ -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 /** @} */ diff --git a/subsys/usb/device/class/gs_usb.c b/subsys/usb/device/class/gs_usb.c index 6ab85c5..69994da 100644 --- a/subsys/usb/device/class/gs_usb.c +++ b/subsys/usb/device/class/gs_usb.c @@ -24,15 +24,15 @@ 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 +#define GS_USB_OUT1_EP_IDX 1U +#define GS_USB_OUT2_EP_IDX 2U 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; + struct usb_ep_descriptor if0_out1_ep; + struct usb_ep_descriptor if0_out2_ep; } __packed; struct gs_usb_channel_data { @@ -61,7 +61,8 @@ struct gs_usb_data { struct k_fifo rx_fifo; struct k_thread rx_thread; - uint8_t tx_buffer[GS_USB_HOST_FRAME_MAX_SIZE]; + uint8_t tx_buffer1[GS_USB_HOST_FRAME_MAX_SIZE]; + uint8_t tx_buffer2[GS_USB_HOST_FRAME_MAX_SIZE]; struct k_fifo tx_fifo; struct k_thread tx_thread; @@ -72,7 +73,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) @@ -1046,6 +1047,7 @@ 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; @@ -1056,21 +1058,32 @@ static void gs_usb_transfer_tx_callback(uint8_t ep, int tsize, void *priv) return; } - net_buf_add_mem(buf, data->tx_buffer, tsize); + if (ep == cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr) { + net_buf_add_mem(buf, data->tx_buffer1, tsize); + } else { + net_buf_add_mem(buf, data->tx_buffer2, tsize); + } + 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); + 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 { + 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); + } } static void gs_usb_tx_thread(void *p1, void *p2, void *p3) @@ -1354,9 +1367,10 @@ 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); + 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); + 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"); @@ -1365,7 +1379,8 @@ 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); + usb_cancel_transfer(cfg->endpoint[GS_USB_OUT1_EP_IDX].ep_addr); + usb_cancel_transfer(cfg->endpoint[GS_USB_OUT2_EP_IDX].ep_addr); break; case USB_DC_SUSPEND: LOG_DBG("USB device suspend"); @@ -1486,8 +1501,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_dummy_ep = INITIALIZER_IF_EP(GS_USB_DUMMY_EP_ADDR), \ - .if0_out_ep = INITIALIZER_IF_EP(GS_USB_OUT_EP_ADDR), \ + .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[] = { \ @@ -1497,11 +1512,11 @@ static int gs_usb_init(const struct device *dev) }, \ { \ .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, \ }, \ }; \ \ diff --git a/subsys/usb/device_next/class/gs_usb.c b/subsys/usb/device_next/class/gs_usb.c index ab80654..c88b763 100644 --- a/subsys/usb/device_next/class/gs_usb.c +++ b/subsys/usb/device_next/class/gs_usb.c @@ -28,11 +28,11 @@ struct gs_usb_desc { 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; + struct usb_ep_descriptor if0_out1_ep; + struct usb_ep_descriptor if0_out2_ep; struct usb_ep_descriptor if0_hs_in_ep; - struct usb_ep_descriptor if0_hs_dummy_ep; - struct usb_ep_descriptor if0_hs_out_ep; + struct usb_ep_descriptor if0_hs_out1_ep; + struct usb_ep_descriptor if0_hs_out2_ep; struct usb_desc_header nil_desc; }; @@ -809,7 +809,7 @@ static uint8_t gs_usb_get_bulk_in_ep_addr(struct usbd_class_data *const c_data) return desc->if0_in_ep.bEndpointAddress; } -static uint8_t gs_usb_get_bulk_out_ep_addr(struct usbd_class_data *const c_data) +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); const struct gs_usb_config *config = dev->config; @@ -817,10 +817,24 @@ static uint8_t gs_usb_get_bulk_out_ep_addr(struct usbd_class_data *const c_data) struct gs_usb_desc *desc = config->desc; if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { - return desc->if0_hs_out_ep.bEndpointAddress; + return desc->if0_hs_out1_ep.bEndpointAddress; } - return desc->if0_out_ep.bEndpointAddress; + return desc->if0_out1_ep.bEndpointAddress; +} + +static uint8_t gs_usb_get_bulk_out2_ep_addr(struct usbd_class_data *const c_data) +{ + const struct device *dev = usbd_class_get_private(c_data); + const struct gs_usb_config *config = dev->config; + struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); + struct gs_usb_desc *desc = config->desc; + + if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { + return desc->if0_hs_out2_ep.bEndpointAddress; + } + + return desc->if0_out2_ep.bEndpointAddress; } static struct net_buf *gs_usb_buf_alloc(const struct gs_usb_config *config, uint8_t ep) @@ -859,13 +873,12 @@ static void gs_usb_fill_echo_frame_hdr(struct net_buf *buf, uint32_t echo_id, ui bi->ep = ep; } -static int gs_usb_out_start(struct usbd_class_data *const c_data) +static int gs_usb_out_start(struct usbd_class_data *const c_data, uint8_t ep) { const struct device *dev = usbd_class_get_private(c_data); const struct gs_usb_config *config = dev->config; struct gs_usb_data *data = dev->data; struct net_buf *buf; - uint8_t ep; int ret; if (!atomic_test_bit(&data->state, GS_USB_STATE_CLASS_ENABLED)) { @@ -873,16 +886,15 @@ static int gs_usb_out_start(struct usbd_class_data *const c_data) return -EPERM; } - ep = gs_usb_get_bulk_out_ep_addr(c_data); buf = gs_usb_buf_alloc(config, ep); if (buf == NULL) { - LOG_ERR("failed to allocate buffer for ep 0x%02x", ep); + LOG_ERR("failed to allocate buffer for OUT ep 0x%02x", ep); return -ENOMEM; } ret = usbd_ep_enqueue(c_data, buf); if (ret != 0) { - LOG_ERR("failed to enqueue buffer for ep 0x%02x (err %d)", ep, ret); + LOG_ERR("failed to enqueue buffer for OUT ep 0x%02x (err %d)", ep, ret); net_buf_unref(buf); } @@ -1273,7 +1285,8 @@ 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); - if (bi->ep == gs_usb_get_bulk_out_ep_addr(c_data)) { + if (bi->ep == gs_usb_get_bulk_out1_ep_addr(c_data) || + bi->ep == gs_usb_get_bulk_out2_ep_addr(c_data)) { if (!atomic_test_bit(&data->state, GS_USB_STATE_CLASS_ENABLED)) { LOG_WRN("class not enabled"); net_buf_unref(buf); @@ -1282,9 +1295,10 @@ static int gs_usb_request(struct usbd_class_data *const c_data, struct net_buf * k_fifo_put(&data->tx_fifo, buf); - ret = gs_usb_out_start(c_data); + ret = gs_usb_out_start(c_data, bi->ep); if (ret != 0) { - LOG_ERR("failed to restart OUT transfer (err %d)", ret); + LOG_ERR("failed to restart OUT transfer for ep 0x%02x (err %d)", bi->ep, + ret); } return ret; @@ -1302,14 +1316,22 @@ static void gs_usb_enable(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct gs_usb_data *data = dev->data; + uint8_t ep; int err; atomic_set_bit(&data->state, GS_USB_STATE_CLASS_ENABLED); LOG_DBG("enabled"); - err = gs_usb_out_start(c_data); + 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); + } + + ep = gs_usb_get_bulk_out2_ep_addr(c_data); + err = gs_usb_out_start(c_data, ep); if (err != 0) { - LOG_ERR("failed to start OUT transfer (err %d)", err); + LOG_ERR("failed to start OUT transfer for ep 0x%02x (err %d)", ep, err); } } @@ -1334,7 +1356,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); } - ep = gs_usb_get_bulk_out_ep_addr(c_data); + 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); + } + + ep = gs_usb_get_bulk_out2_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); @@ -1616,18 +1644,18 @@ struct usbd_class_api gs_usb_api = { .wMaxPacketSize = sys_cpu_to_le16(64), \ .bInterval = 0x00, \ }, \ - .if0_dummy_ep = { \ + .if0_out1_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ - .bEndpointAddress = GS_USB_DUMMY_EP_ADDR, \ + .bEndpointAddress = GS_USB_OUT1_EP_ADDR, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(64U), \ .bInterval = 0x00, \ }, \ - .if0_out_ep = { \ + .if0_out2_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ - .bEndpointAddress = GS_USB_OUT_EP_ADDR, \ + .bEndpointAddress = GS_USB_OUT2_EP_ADDR, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(64U), \ .bInterval = 0x00, \ @@ -1640,18 +1668,18 @@ struct usbd_class_api gs_usb_api = { .wMaxPacketSize = sys_cpu_to_le16(512), \ .bInterval = 0x00, \ }, \ - .if0_hs_dummy_ep = { \ + .if0_hs_out1_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ - .bEndpointAddress = GS_USB_DUMMY_EP_ADDR, \ + .bEndpointAddress = GS_USB_OUT1_EP_ADDR, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(512U), \ .bInterval = 0x00, \ }, \ - .if0_hs_out_ep = { \ + .if0_hs_out2_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ - .bEndpointAddress = GS_USB_OUT_EP_ADDR, \ + .bEndpointAddress = GS_USB_OUT2_EP_ADDR, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(512U), \ .bInterval = 0x00, \ @@ -1666,8 +1694,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_dummy_ep, \ - (struct usb_desc_header *)&gs_usb_desc_##n.if0_out_ep, \ + (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, \ }; \ \ @@ -1675,8 +1703,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_dummy_ep, \ - (struct usb_desc_header *)&gs_usb_desc_##n.if0_hs_out_ep, \ + (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, \ } From a058069630519a68e995ded4538f278370f6f3d6 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Sun, 29 Dec 2024 22:06:49 +0000 Subject: [PATCH 2/2] usb: device: gs_usb: introduce a compatibility mode kconfig option 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 --- subsys/usb/device/class/Kconfig.gs_usb | 18 +++++++++++ subsys/usb/device/class/gs_usb.c | 34 ++++++++++++++++++--- subsys/usb/device_next/class/Kconfig.gs_usb | 21 ++++++++++++- subsys/usb/device_next/class/gs_usb.c | 29 +++++++++++++++--- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/subsys/usb/device/class/Kconfig.gs_usb b/subsys/usb/device/class/Kconfig.gs_usb index 5b23c30..f3e47d1 100644 --- a/subsys/usb/device/class/Kconfig.gs_usb +++ b/subsys/usb/device/class/Kconfig.gs_usb @@ -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 diff --git a/subsys/usb/device/class/gs_usb.c b/subsys/usb/device/class/gs_usb.c index 69994da..0aaa230 100644 --- a/subsys/usb/device/class/gs_usb.c +++ b/subsys/usb/device/class/gs_usb.c @@ -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; @@ -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; @@ -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; @@ -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); } @@ -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) @@ -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: @@ -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: @@ -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, \ @@ -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), \ }; \ \ @@ -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, \ diff --git a/subsys/usb/device_next/class/Kconfig.gs_usb b/subsys/usb/device_next/class/Kconfig.gs_usb index f611068..96948f8 100644 --- a/subsys/usb/device_next/class/Kconfig.gs_usb +++ b/subsys/usb/device_next/class/Kconfig.gs_usb @@ -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 @@ -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 diff --git a/subsys/usb/device_next/class/gs_usb.c b/subsys/usb/device_next/class/gs_usb.c index c88b763..81720a2 100644 --- a/subsys/usb/device_next/class/gs_usb.c +++ b/subsys/usb/device_next/class/gs_usb.c @@ -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; }; @@ -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); @@ -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) { @@ -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); @@ -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); @@ -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); @@ -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, \ @@ -1644,6 +1659,7 @@ 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, \ @@ -1651,7 +1667,7 @@ struct usbd_class_api gs_usb_api = { .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, \ @@ -1668,6 +1684,7 @@ 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, \ @@ -1675,7 +1692,7 @@ struct usbd_class_api gs_usb_api = { .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, \ @@ -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, \ }; \ @@ -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, \ }