[PATCH 2/4] usb: usbtmc: handle undersized high-speed bulk OUT packets

From: Nicolai Buchwitz

Date: Tue Oct 20 2026 - 11:30:25 EST


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@xxxxxxxxxxxxxxx
Signed-off-by: Nicolai Buchwitz <nb@xxxxxxxxxxx>
---
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