From: Marc Kleine-Budde <mkl@pengutronix.de>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, linux-can@vger.kernel.org,
kernel@pengutronix.de, Marc Kleine-Budde <mkl@pengutronix.de>,
stable@vger.kernel.org
Subject: [PATCH net 20/22] can: gs_usb: add workarounds for HScanT USB to CAN adapter
Date: Mon, 28 Sep 2026 20:45:27 +0200 [thread overview]
Message-ID: <20260928193312.553632-21-mkl@pengutronix.de> (raw)
In-Reply-To: <20260928193312.553632-1-mkl@pengutronix.de>
The HScanT [1] is a RISC-V based USB to 4 channels CAN-FD adapter, which is
compatible with the gs_usb protocol.
The device is shipped with firmware version 0x00010007 and needs several
quirks to work properly.
The HScanT FW announces 5 channels, but the hardware has only 4. Workaround
the problem by changing the struct gs_device_config::icount to 3, which
corresponds to 4 channels.
The HScanT FW requires a USB High Speed Hub, bail out if device is
connected to slower USB Hub.
The HScanT FW always sends USB In URB with length 512 bytes and seems to
have broken Zero Packet Length handling. Add quirk
GS_CAN_FEATURE_QUIRK_HSCANT_URB_SIZE to allocate URBs of 513 bytes to work
around these issues.
This driver supports up to 256 channels per USB Interface. The HScanT
device has 4 channels, but the FW requires each channels to be bound to a
USB interface using the GS_USB_BREQ_HSCANT_SET_INTERFACENUMBER_ENDPOINT USB
request. Add quirk to GS_CAN_FEATURE_QUIRK_HSCANT_BIND_CHANNEL to bind the
CAN channel to the USB Interface 0 during gs_can_open()
Link: https://github.com/cherry-embedded/HSCanT-hardware
Link: https://patch.msgid.link/20260928-gs_usb-hscant-v2-1-a7c1c02460e9@pengutronix.de
Cc: stable@vger.kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
drivers/net/can/usb/gs_usb.c | 125 +++++++++++++++++++++++++++++++++--
1 file changed, 121 insertions(+), 4 deletions(-)
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index f604358c8259..f7a349902c42 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -72,6 +72,7 @@ enum gs_usb_breq {
GS_USB_BREQ_SET_TERMINATION,
GS_USB_BREQ_GET_TERMINATION,
GS_USB_BREQ_GET_STATE,
+ GS_USB_BREQ_HSCANT_SET_INTERFACENUMBER_ENDPOINT = 17,
};
enum gs_can_mode {
@@ -188,6 +189,21 @@ struct gs_device_termination_state {
/* internal quirks - keep in GS_CAN_FEATURE space for now */
+/* HScanT firmware version 0x00010007:
+ * - FW requires the binding of CAN channels to USB Interfaces.
+ * - Route all CAN channels to USB Interface 0.
+ */
+#define GS_CAN_FEATURE_QUIRK_HSCANT_BIND_CHANNEL BIT(29)
+
+/* HScanT firmware version 0x00010007:
+ * - FW sends bulk In URBs with length of 512 bytes.
+ * - When using In URBs with 512 bytes it will send a second in URB with length 0
+ * It seems the ZLP handling is broken.
+ * - Use In URBs of length GS_USB_QUIRK_HSCANT_IN_URB_SIZE as a workaround.
+ */
+#define GS_CAN_FEATURE_QUIRK_HSCANT_URB_SIZE BIT(30)
+#define GS_USB_QUIRK_HSCANT_IN_URB_SIZE (513)
+
/* CANtact Pro original firmware:
* BREQ DATA_BITTIMING overlaps with GET_USER_ID
*/
@@ -812,6 +828,18 @@ static int gs_usb_set_data_bittiming(struct gs_can *dev)
GFP_KERNEL);
}
+static int gs_usb_hscant_bind_channel_to_interface(const struct gs_can *dev)
+{
+ const u16 interface_number = 0;
+
+ /* Bind dev->channel to interface_number */
+ return usb_control_msg_send(dev->udev, 0, GS_USB_BREQ_HSCANT_SET_INTERFACENUMBER_ENDPOINT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ dev->channel, interface_number,
+ NULL, 0, 1000,
+ GFP_KERNEL);
+}
+
static void gs_usb_xmit_callback(struct urb *urb)
{
struct gs_tx_context *txc = urb->context;
@@ -1069,6 +1097,16 @@ static int gs_can_open(struct net_device *netdev)
}
}
+ if (dev->feature & GS_CAN_FEATURE_QUIRK_HSCANT_BIND_CHANNEL) {
+ rc = gs_usb_hscant_bind_channel_to_interface(dev);
+ if (rc) {
+ netdev_err(netdev,
+ "failed to bind Channel to Interface: %pe\n",
+ ERR_PTR(rc));
+ goto out_usb_kill_anchored_urbs;
+ }
+ }
+
/* finally start device */
dev->can.state = CAN_STATE_ERROR_ACTIVE;
dm.flags = cpu_to_le32(flags);
@@ -1314,6 +1352,49 @@ static const u16 gs_usb_termination_const[] = {
GS_USB_TERMINATION_ENABLED
};
+static bool gs_usb_is_hscant(const struct usb_device *udev,
+ const struct gs_device_config *dconf,
+ const u32 sw_version)
+{
+ if (udev->descriptor.idVendor != cpu_to_le16(USB_GS_USB_1_VENDOR_ID) ||
+ udev->descriptor.idProduct != cpu_to_le16(USB_GS_USB_1_PRODUCT_ID))
+ return false;
+
+ if (strcmp(udev->manufacturer, "HScanT") ||
+ strcmp(udev->product, "HScanT USB to CAN adapter"))
+ return false;
+
+ if (dconf->sw_version != cpu_to_le32(sw_version))
+ return false;
+
+ return true;
+}
+
+static void
+gs_usb_make_candev_get_feature(struct gs_can *dev, const struct gs_device_config *dconf,
+ const struct gs_device_bt_const *bt_const)
+{
+ const struct usb_device *udev = dev->udev;
+ const u32 feature = le32_to_cpu(bt_const->feature);
+
+ dev->feature = FIELD_GET(GS_CAN_FEATURE_MASK, feature);
+
+ if (!udev->manufacturer || !udev->product)
+ return;
+
+ /* HScanT firmware version 0x00010007:
+ * - FW doesn't advertise GS_CAN_FEATURE_BT_CONST_EXT,
+ * but implements GS_USB_BREQ_BT_CONST_EXT, fixup.
+ * - FW requires binding of CAN channel to USB Interface, add quirk.
+ * - FW requires bulk In URBs with >= 512 bytes, add quirk.
+ */
+ if (gs_usb_is_hscant(udev, dconf, 0x00010007)) {
+ dev->feature |= GS_CAN_FEATURE_BT_CONST_EXT |
+ GS_CAN_FEATURE_QUIRK_HSCANT_BIND_CHANNEL |
+ GS_CAN_FEATURE_QUIRK_HSCANT_URB_SIZE;
+ }
+}
+
static struct gs_can *gs_make_candev(unsigned int channel,
struct usb_interface *intf,
struct gs_device_config *dconf)
@@ -1385,8 +1466,9 @@ static struct gs_can *gs_make_candev(unsigned int channel,
dev->can.ctrlmode_supported = CAN_CTRLMODE_CC_LEN8_DLC;
- feature = le32_to_cpu(bt_const.feature);
- dev->feature = FIELD_GET(GS_CAN_FEATURE_MASK, feature);
+ gs_usb_make_candev_get_feature(dev, dconf, &bt_const);
+ feature = dev->feature;
+
if (feature & GS_CAN_FEATURE_LISTEN_ONLY)
dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
@@ -1514,6 +1596,34 @@ static void gs_destroy_candev(struct gs_can *dev)
free_candev(dev->netdev);
}
+static int gs_usb_probe_quirks(const struct usb_interface *intf, struct gs_device_config *dconf)
+{
+ const struct usb_device *udev = interface_to_usbdev(intf);
+
+ if (!udev->manufacturer || !udev->product)
+ return 0;
+
+ /* HScanT firmware version 0x00010007:
+ * - FW has an icount of 4, which corresponds to 5 CAN interfaces.
+ * The hardware has only 4 interfaces, fixup.
+ * - FW provides broken Endpoint Descriptors on USB Full Speed Hubs:
+ * config 1 interface 0 altsetting 0 endpoint 0x4 has invalid maxpacket 512, setting to 64
+ * Probably related to GS_CAN_FEATURE_QUIRK_HSCANT_URB_SIZE,
+ * FW only works on USB High Speed Hubs, detect and bail out.
+ */
+ if (gs_usb_is_hscant(udev, dconf, 0x00010007)) {
+ if (dconf->icount == 4)
+ dconf->icount = 3;
+
+ if (udev->speed < USB_SPEED_HIGH) {
+ dev_err(&intf->dev, "Device only works with USB High Speed Hubs\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
static int gs_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1560,6 +1670,10 @@ static int gs_usb_probe(struct usb_interface *intf,
return rc;
}
+ rc = gs_usb_probe_quirks(intf, &dconf);
+ if (rc)
+ return rc;
+
icount = dconf.icount + 1;
dev_info(&intf->dev, "Configuring for %u interfaces\n", icount);
@@ -1604,10 +1718,13 @@ static int gs_usb_probe(struct usb_interface *intf,
}
parent->canch[i]->parent = parent;
- /* set RX packet size based on FD and if hardware
+ /* set RX packet size based on quirks, FD and if hardware
* timestamps are supported.
*/
- if (parent->canch[i]->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
+ if (parent->canch[i]->feature & GS_CAN_FEATURE_QUIRK_HSCANT_URB_SIZE) {
+ hf_size_rx = GS_USB_QUIRK_HSCANT_IN_URB_SIZE;
+ BUILD_BUG_ON(struct_size(hf, canfd, 1) > GS_USB_QUIRK_HSCANT_IN_URB_SIZE);
+ } else if (parent->canch[i]->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
if (parent->canch[i]->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
hf_size_rx = struct_size(hf, canfd_ts, 1);
else
--
2.53.0
next prev parent reply other threads:[~2026-09-28 19:33 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 18:45 [PATCH net 0/22] pull-request: can 2026-09-28 Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 01/22] can: dev: can_dropped_invalid_skb: drop CAN XL frames on non-CAN XL devices Marc Kleine-Budde
2026-09-28 19:36 ` netdev-bot+sinfo
2026-09-28 18:45 ` [PATCH net 02/22] can: skb: make echo skb freeing safe in any IRQ context Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 03/22] can: skb: make CAN skb allocation failure paths IRQ-safe Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 04/22] can: dev: can_put_echo_skb(): free skb on invalid echo index Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 05/22] can: dev: init_can_skb(): restore skb header initialization Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 06/22] can: j1939: j1939_sk_bind(): fix j1939_ecu leak when re-bind failed Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 07/22] can: isotp: check the frame type, not just the length Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 08/22] can: cc770: platform_get_irq(): propagate the error Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 09/22] can: cc770: fix the clock divider check on the platform bus Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 10/22] can: kvaser_pciefd: fix use-after-free in bec poll timer Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 11/22] can: m_can: pci: add missing pm_runtime_dont_use_autosuspend() call Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 12/22] can: m_can: m_can_class_suspend(): fix suspend deinit() error path Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 13/22] can: sun4i_can: sun4ican_probe(): fix clk leak Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 14/22] can: xilinx_can: set CAN FD flags on received frames Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 15/22] can: mcp251xfd: mcp251xfd_probe(): reject devices without match data Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 16/22] can: hi311x: drop hi3110_lock before free_irq() on open failure Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 17/22] can: ems_usb: use usb_kill_urb() to stop the intr URB Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 18/22] usb: f81604: fix struct f81604_int_data size mismatch Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 19/22] can: gs_usb: kill RX URBs before destroying the netdevs Marc Kleine-Budde
2026-09-28 18:45 ` Marc Kleine-Budde [this message]
2026-09-28 18:45 ` [PATCH net 21/22] can: kvaser_usb: validate command format before parsing in hydra receive path Marc Kleine-Budde
2026-09-28 18:45 ` [PATCH net 22/22] can: peak_usb: fix missing CAN_ERR_FLAG when reporting error counters Marc Kleine-Budde
2026-09-29 1:09 ` [PATCH net 0/22] pull-request: can 2026-09-28 Jakub Kicinski
2026-09-29 21:12 ` Marc Kleine-Budde
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260928193312.553632-21-mkl@pengutronix.de \
--to=mkl@pengutronix.de \
--cc=davem@davemloft.net \
--cc=kernel@pengutronix.de \
--cc=kuba@kernel.org \
--cc=linux-can@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox