* [PATCH 1/4] usb: usbtmc: handle undersized high-speed bulk IN packets
2026-10-20 14:33 [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Nicolai Buchwitz
@ 2026-10-20 14:33 ` Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 2/4] usb: usbtmc: handle undersized high-speed bulk OUT packets Nicolai Buchwitz
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Nicolai Buchwitz @ 2026-10-20 14:33 UTC (permalink / raw)
To: Greg Kroah-Hartman, Steve Bayless, Guido Kiener
Cc: linux-usb, linux-kernel, Nicolai Buchwitz, stable
The Siglent SDM4065A-SC advertises 64-byte bulk endpoints at high
speed. USB 2.0 requires 512 bytes there, and xHCI uses 512. The device
still sends 64-byte packets, so each packet ends the transfer and
usbtmc_read() returns only the first one. The rest is read as the next
reply:
usbtmc 1-1.3:1.0: Device sent too small first packet: 8 < 12
usbtmc 1-1.3:1.0: Device sent reply with wrong bTag: 4 != 6
Read one packet per URB in usbtmc_read() on this device. Also check for
a full first transfer before stripping the alignment bytes, otherwise a
reply whose padding fills that transfer leaves its trailing ZLP behind.
The existing 4096-byte path had the same problem.
Fixes: d7604ff0dc01 ("usb: usbtmc: Optimize usbtmc_read")
Cc: stable@vger.kernel.org
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
drivers/usb/class/usbtmc.c | 60 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 53 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 51cd9320a7366..7a2ea14aa524b 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -54,6 +54,16 @@ static const struct usb_device_id usbtmc_devices[] = {
};
MODULE_DEVICE_TABLE(usb, usbtmc_devices);
+/*
+ * Devices advertising high-speed bulk packets smaller than the 512 bytes
+ * xHCI uses. Transfers are limited to one advertised packet so the
+ * device's packet boundaries are preserved.
+ */
+static const struct usb_device_id usbtmc_packet_io_devices[] = {
+ { USB_DEVICE(0xf4ec, 0x1220) }, /* Siglent SDM4065A-SC */
+ { } /* terminating entry */
+};
+
/*
* This structure is the capabilities for the device
* See section 4.2.1.8 of the USBTMC specification,
@@ -84,6 +94,10 @@ struct usbtmc_device_data {
/* packet size of IN bulk */
u16 wMaxPacketSize;
+ /* packet size of OUT bulk */
+ u16 out_wMaxPacketSize;
+ /* one URB per advertised bulk packet */
+ bool packet_io;
/* data for interrupt in endpoint handling */
u8 bNotify1;
@@ -826,13 +840,13 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
void __user *user_buffer,
u32 transfer_size,
u32 *transferred,
- u32 flags)
+ u32 flags, bool packet_mode)
{
struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
+ const u32 bufsize = packet_mode ? data->wMaxPacketSize : USBTMC_BUFSIZE;
u32 done = 0;
u32 remaining;
- const u32 bufsize = USBTMC_BUFSIZE;
int retval = 0;
u32 max_transfer_size;
unsigned long expire;
@@ -852,7 +866,13 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
* packet
*/
remaining = transfer_size;
- if ((max_transfer_size % data->wMaxPacketSize) == 0)
+ /*
+ * Packet-sized reads must also consume a trailing short packet
+ * when alignment bytes fill the last data packet.
+ */
+ if (packet_mode)
+ max_transfer_size += data->wMaxPacketSize;
+ else if ((max_transfer_size % data->wMaxPacketSize) == 0)
max_transfer_size += (data->wMaxPacketSize - 1);
} else {
/* round down to bufsize to avoid truncated data left */
@@ -899,6 +919,12 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
bufcount = MAX_URBS_IN_FLIGHT -
file_data->in_urbs_used;
}
+ /*
+ * Queue one URB at a time. A trailer URB queued ahead would be
+ * killed after most replies and log -ENOENT.
+ */
+ if (packet_mode)
+ bufcount = min(bufcount, 1);
}
spin_unlock_irq(&file_data->err_lock);
@@ -1072,7 +1098,7 @@ static ssize_t usbtmc_ioctl_generic_read(struct usbtmc_file_data *file_data,
retval = usbtmc_generic_read(file_data, msg.message,
msg.transfer_size, &msg.transferred,
- msg.flags);
+ msg.flags, false);
if (put_user(msg.transferred,
&((struct usbtmc_message __user *)arg)->transferred))
@@ -1377,7 +1403,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
struct usbtmc_file_data *file_data;
struct usbtmc_device_data *data;
struct device *dev;
- const u32 bufsize = USBTMC_BUFSIZE;
+ u32 bufsize = USBTMC_BUFSIZE;
+ bool full_buffer;
u32 n_characters;
u8 *buffer;
int actual;
@@ -1390,6 +1417,9 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
data = file_data->data;
dev = &data->intf->dev;
+ if (data->packet_io)
+ bufsize = data->wMaxPacketSize;
+
buffer = kmalloc(bufsize, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
@@ -1488,6 +1518,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
remaining = n_characters;
+ full_buffer = actual == bufsize;
+
/* Remove the USBTMC header */
actual -= USBTMC_HEADER_SIZE;
@@ -1504,11 +1536,12 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
goto exit;
}
- if ((actual + USBTMC_HEADER_SIZE) == bufsize) {
+ if (full_buffer) {
retval = usbtmc_generic_read(file_data, buf + actual,
remaining,
&done,
- USBTMC_FLAG_IGNORE_TRAILER);
+ USBTMC_FLAG_IGNORE_TRAILER,
+ data->packet_io);
if (retval < 0)
goto exit;
}
@@ -2378,6 +2411,12 @@ static void usbtmc_free_int(struct usbtmc_device_data *data)
kref_put(&data->kref, usbtmc_delete);
}
+static bool usbtmc_packet_io_ok(u16 maxpacket)
+{
+ return maxpacket >= USBTMC_HEADER_SIZE && maxpacket < 512 &&
+ !(maxpacket & 3);
+}
+
static int usbtmc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -2429,8 +2468,15 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in);
data->bulk_out = bulk_out->bEndpointAddress;
+ data->out_wMaxPacketSize = usb_endpoint_maxp(bulk_out);
dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", data->bulk_out);
+ /* Packets must carry the header and keep writes 32-bit aligned */
+ data->packet_io = usb_match_id(intf, usbtmc_packet_io_devices) &&
+ data->usb_dev->speed == USB_SPEED_HIGH &&
+ usbtmc_packet_io_ok(data->wMaxPacketSize) &&
+ usbtmc_packet_io_ok(data->out_wMaxPacketSize);
+
/* Find int endpoint */
retcode = usb_find_int_in_endpoint(iface_desc, &int_in);
if (!retcode) {
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/4] usb: usbtmc: handle undersized high-speed bulk OUT packets
2026-10-20 14:33 [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 1/4] usb: usbtmc: handle undersized high-speed bulk IN packets Nicolai Buchwitz
@ 2026-10-20 14:33 ` Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 3/4] usb: usbtmc: add Rigol DP800 and DS1000Z to the packet I/O quirk table Nicolai Buchwitz
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Nicolai Buchwitz @ 2026-10-20 14:33 UTC (permalink / raw)
To: Greg Kroah-Hartman, Steve Bayless, Guido Kiener
Cc: linux-usb, linux-kernel, Nicolai Buchwitz, stable
The Siglent SDM4065A-SC hangs its OUT endpoint when xHCI sends a packet
larger than the 64 bytes it advertises. Every transfer after that times
out until a USB reset:
usbtmc 1-1.3:1.0: Unable to send data, error -110
usbtmc 1-1.3:1.0: send_request_dev_dep_msg_in returned -110
Limit usbtmc_write() to URBs of the advertised packet size on this
device and send them one at a time. Some devices with this bug also
stall on back-to-back OUT packets.
Cc: stable@vger.kernel.org
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
The write path never handled this device, so there is no Fixes: target.
---
drivers/usb/class/usbtmc.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 7a2ea14aa524b..679ab5908e203 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1145,14 +1145,15 @@ static ssize_t usbtmc_generic_write(struct usbtmc_file_data *file_data,
const void __user *user_buffer,
u32 transfer_size,
u32 *transferred,
- u32 flags)
+ u32 flags, bool packet_mode)
{
struct usbtmc_device_data *data = file_data->data;
+ const u32 bufsize = packet_mode ? data->out_wMaxPacketSize :
+ USBTMC_BUFSIZE;
struct device *dev;
u32 done = 0;
u32 remaining;
unsigned long expire;
- const u32 bufsize = USBTMC_BUFSIZE;
struct urb *urb = NULL;
int retval = 0;
u32 timeout;
@@ -1190,6 +1191,14 @@ static ssize_t usbtmc_generic_write(struct usbtmc_file_data *file_data,
u32 this_part, aligned;
u8 *buffer = NULL;
+ /* Some packet mode devices stall on back-to-back OUT packets */
+ if (packet_mode &&
+ !usb_wait_anchor_empty_timeout(&file_data->submitted,
+ timeout)) {
+ retval = -ETIMEDOUT;
+ goto error;
+ }
+
if (flags & USBTMC_FLAG_ASYNC) {
if (down_trylock(&file_data->limit_write_sem)) {
retval = (done)?(0):(-EAGAIN);
@@ -1309,7 +1318,7 @@ static ssize_t usbtmc_ioctl_generic_write(struct usbtmc_file_data *file_data,
retval = usbtmc_generic_write(file_data, msg.message,
msg.transfer_size, &msg.transferred,
- msg.flags);
+ msg.flags, false);
if (put_user(msg.transferred,
&((struct usbtmc_message __user *)arg)->transferred))
@@ -1605,6 +1614,9 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
buffer = urb->transfer_buffer;
buflen = urb->transfer_buffer_length;
+ if (data->packet_io)
+ buflen = data->out_wMaxPacketSize;
+
if (count > INT_MAX) {
transfersize = INT_MAX;
buffer[8] = 0;
@@ -1671,7 +1683,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
/* call generic_write even when remaining = 0 */
retval = usbtmc_generic_write(file_data, buf + transfersize, remaining,
- &done, USBTMC_FLAG_APPEND);
+ &done, USBTMC_FLAG_APPEND, data->packet_io);
/* truncate alignment bytes */
if (done > remaining)
done = remaining;
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/4] usb: usbtmc: add Rigol DP800 and DS1000Z to the packet I/O quirk table
2026-10-20 14:33 [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 1/4] usb: usbtmc: handle undersized high-speed bulk IN packets Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 2/4] usb: usbtmc: handle undersized high-speed bulk OUT packets Nicolai Buchwitz
@ 2026-10-20 14:33 ` Nicolai Buchwitz
2026-10-20 14:33 ` [PATCH 4/4] usb: usbtmc: send a ZLP after a full last packet in packet I/O mode Nicolai Buchwitz
2026-10-20 16:42 ` [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Michal Pecio
4 siblings, 0 replies; 6+ messages in thread
From: Nicolai Buchwitz @ 2026-10-20 14:33 UTC (permalink / raw)
To: Greg Kroah-Hartman, Steve Bayless, Guido Kiener
Cc: linux-usb, linux-kernel, Nicolai Buchwitz, stable
The Rigol DP800 power supply and DS1000Z oscilloscope families also
advertise 64-byte bulk endpoints at high speed. Replies are cut off
after the first packet, and writes larger than 64 bytes fail on the
DP800:
usbtmc 1-1.3:1.0: Unable to send data, error -71
Tested on a DP832A and a DS1054Z.
Cc: stable@vger.kernel.org
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
drivers/usb/class/usbtmc.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 679ab5908e203..861913b9de918 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -60,6 +60,8 @@ MODULE_DEVICE_TABLE(usb, usbtmc_devices);
* device's packet boundaries are preserved.
*/
static const struct usb_device_id usbtmc_packet_io_devices[] = {
+ { USB_DEVICE(0x1ab1, 0x04ce) }, /* Rigol DS1000Z family */
+ { USB_DEVICE(0x1ab1, 0x0e11) }, /* Rigol DP800 family */
{ USB_DEVICE(0xf4ec, 0x1220) }, /* Siglent SDM4065A-SC */
{ } /* terminating entry */
};
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/4] usb: usbtmc: send a ZLP after a full last packet in packet I/O mode
2026-10-20 14:33 [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Nicolai Buchwitz
` (2 preceding siblings ...)
2026-10-20 14:33 ` [PATCH 3/4] usb: usbtmc: add Rigol DP800 and DS1000Z to the packet I/O quirk table Nicolai Buchwitz
@ 2026-10-20 14:33 ` Nicolai Buchwitz
2026-10-20 16:42 ` [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Michal Pecio
4 siblings, 0 replies; 6+ messages in thread
From: Nicolai Buchwitz @ 2026-10-20 14:33 UTC (permalink / raw)
To: Greg Kroah-Hartman, Steve Bayless, Guido Kiener
Cc: linux-usb, linux-kernel, Nicolai Buchwitz, stable
The Rigol DP800 waits for a short packet before it handles a command.
A message that ends exactly on a 64-byte boundary is never processed.
The read times out, and once the next message gets appended the device
shows "command incorrect".
Send a zero-length packet after a full last packet in usbtmc_write()
on quirked devices.
Tested on a DP832A, a DS1054Z and an SDM4065A-SC.
Cc: stable@vger.kernel.org
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
drivers/usb/class/usbtmc.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 861913b9de918..101d4f9fff6b5 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1259,6 +1259,8 @@ static ssize_t usbtmc_generic_write(struct usbtmc_file_data *file_data,
usb_sndbulkpipe(data->usb_dev, data->bulk_out),
urb->transfer_buffer, aligned,
usbtmc_write_bulk_cb, file_data);
+ if (packet_mode && this_part == remaining)
+ urb->transfer_flags |= URB_ZERO_PACKET;
usb_anchor_urb(urb, &file_data->submitted);
retval = usb_submit_urb(urb, GFP_KERNEL);
@@ -1666,6 +1668,9 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
usb_sndbulkpipe(data->usb_dev, data->bulk_out),
urb->transfer_buffer, aligned,
usbtmc_write_bulk_cb, file_data);
+ /* Some devices only end a transfer on a short packet */
+ if (data->packet_io && transfersize == remaining)
+ urb->transfer_flags |= URB_ZERO_PACKET;
usb_anchor_urb(urb, &file_data->submitted);
retval = usb_submit_urb(urb, GFP_KERNEL);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI
2026-10-20 14:33 [PATCH 0/4] usb: usbtmc: fix undersized high-speed bulk packets on xHCI Nicolai Buchwitz
` (3 preceding siblings ...)
2026-10-20 14:33 ` [PATCH 4/4] usb: usbtmc: send a ZLP after a full last packet in packet I/O mode Nicolai Buchwitz
@ 2026-10-20 16:42 ` Michal Pecio
4 siblings, 0 replies; 6+ messages in thread
From: Michal Pecio @ 2026-10-20 16:42 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: Greg Kroah-Hartman, Steve Bayless, Guido Kiener, linux-usb,
linux-kernel, stable
On Tue, 20 Oct 2026 16:33:37 +0200, Nicolai Buchwitz wrote:
> The Siglent SDM4065A-SC, Rigol DP800 and Rigol DS1000Z (and probably
> others) advertise 64-byte bulk endpoints at high speed. USB 2.0 only
> allows 512, which xHCI correctly implements. Unfortunately this breaks
> usbtmc on these devices. Reads stop after the first packet, and longer
> writes also fail or hang the OUT endpoint until a USB reset:
>
> usb 1-1.3: config 1 interface 0 altsetting 0 bulk endpoint 0x81 has invalid maxpacket 64
> usbtmc 1-1.3:1.0: Device sent too small first packet: 8 < 12
> usbtmc 1-1.3:1.0: Unable to send data, error -110
Fascinating.
But SDM4065A is apparently pretty modern (2025) gear marketed for
"automatic production lines", so it's odd that it has such issues.
Has anyone looked if the same out-of-spec descriptor shows up on
Windows or (don't laught at my optimism) consulted vendor support?
Maybe Windows xHCI driver tries to enable out-of-spec maxpacket?
We could do that too, though IIRC not all controllers are happy,
so a SW workaround may be unavoidable.
And xHCI doesn't support URB_SHORT_NOT_OK, for the record.
> We ran into this in our factory once we switched some test PCs from
> dwc2 based hardware to xHCI based ones. dwc2 uses the advertised 64
> bytes, so the devices worked fine before.
>
> This series adds a quirk table to usbtmc and moves one packet per URB
> for the devices in it. The DP800 also wants a ZLP after a write that
> ends on a packet boundary. Only read() and write() change, the ioctls
> behave as before. More devices can go into the table as they turn up.
>
> The Rigols still have one issue I can't work around. They split
> replies over 500 bytes into several transfers, and a follow-on
> transfer that ends exactly on a packet boundary never arrives. Raw
> libusb shows the same, so I guess it's another firmware bug. Should
> be fine for most use cases though.
What exactly is the sequence of URBs you see vs would want to see?
>
> Tested on a Raspberry Pi CM4S (BCM2711 xHCI) and a CM5 (RP1 xHCI).
>
> Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
> ---
> Nicolai Buchwitz (4):
> usb: usbtmc: handle undersized high-speed bulk IN packets
> usb: usbtmc: handle undersized high-speed bulk OUT packets
> usb: usbtmc: add Rigol DP800 and DS1000Z to the packet I/O quirk table
> usb: usbtmc: send a ZLP after a full last packet in packet I/O mode
>
> drivers/usb/class/usbtmc.c | 87 ++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 76 insertions(+), 11 deletions(-)
> ---
> base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
> change-id: 20261020-fix-usbtmc-short-packets-1c4e233ae407
>
> Best regards,
> --
> Nicolai Buchwitz <nb@tipi-net.de>
>
^ permalink raw reply [flat|nested] 6+ messages in thread