From nobody Fri Jun 12 19:58:01 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AAE1B38E119 for ; Wed, 13 May 2026 02:38:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778639911; cv=none; b=b5NzWvpAJJzvn172D8hbzKbxWvLS00omP3yEsXStiLOf/4AkL7wjZXlZQMj2Zx3yBccOQ3Yg/27jO3R3Ebzv9wpDMNnpTi3YkXOMSBsvo0hUwEkYOvBxaiSBpmhKHiObhaLwsH1Ws/iwvmxm4Op9sAso4nsUDXUc2V9KcMNfNWw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778639911; c=relaxed/simple; bh=PACgyRDHY8+p2IqJDyyFDxhAY/5G5muP957NySbjyv0=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=UcMYr4xRhDdWxddC1ejGCCrZgeBiHFeKjTrHA+q8e3soYQb+xGYoNON14N42pWpfcAeFUaZxN354OS10nvDrcwwPiuXd22vSQ/HGhI4p2F7tB5sKwFKlFSCxeV4CLpNtGUb1XAmOOKfx5m3EWvjhZJqlDdudIEX8TuvXzM8F09Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=oes.ch; spf=pass smtp.mailfrom=oes.ch; dkim=pass (2048-bit key) header.d=oes.ch header.i=@oes.ch header.b=oKNVEwJu; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=oes.ch Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oes.ch Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=oes.ch header.i=@oes.ch header.b="oKNVEwJu" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2baca4df358so38311995ad.2 for ; Tue, 12 May 2026 19:38:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oes.ch; s=google; t=1778639908; x=1779244708; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=s+qUGTPmf2JaFyRhBX/Sm1elkBeMqWvLUvWhaNkiQMI=; b=oKNVEwJuWxGwagbCrak1VETVFdM/Bgga6DS+UDdwiJ7+3yR6YQ0ft0108g38riG8oF J6hkkt69AyAm5SdRcJIF6xaRdgqMiub9+8HD2Po2OiWA6XMSNgXKxRbnlZqIYX1pkexu B6vjnQ8sdkTIfzI11bNg1EAE8ujjyb/A3/GeCCcuwt6dj8dS2KSv7bM7CCV9cDwijX9M KVsvONLgkVQHBXHpH6/+2SM4a3EneSuXIyWrOOXMyrCCOJTtfuMcitXcgrTSZmTZkXTt i7e5zAhmgY/VznhGSo02Pl7leI+9QbwsAx2zWtnwcppOGlgR1qD4eGvenRATaVLCQL+U VmwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778639908; x=1779244708; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=s+qUGTPmf2JaFyRhBX/Sm1elkBeMqWvLUvWhaNkiQMI=; b=pVAUYpy1T+/y1k1m2lFQQdsh0qx1kJyXjJRltaurSgVs1Df+MVmkBheSgIieABouR6 b19zbQ+LZh0q93Pu5i2SK91xIFFh6gKOVb9nwcnZad8LT0Ykbd7iRKyCizTrwlIlqoFc gk0eihZnFa/f7m49h7yHgPbvjYKqOacPD21+bZDLHEZUNZIrvdc18gr6uHRjdFOdtUeh gfMJCFnk6TdAlCY2/KlT6IaCB7fuE7FOs9q/N54zjntwEZSC7ltnW2FXhED6M2tV/vcd n93T8FVKB3gJUGxOShdXtnXvQNlriajCSEuCIFjGfOHhM+4ACpBYdirzxoK1bk0b8HYg L/AQ== X-Forwarded-Encrypted: i=1; AFNElJ+dM2f5/KdQ3+0BDYbHfNRgHFzkkTJH+1wDM+m3y4ov5IDu2RnpypQ8Ik4uCGxv6KA78dxqrz8C4N5M2d8=@vger.kernel.org X-Gm-Message-State: AOJu0YxOwzeyC/T/YVEDyqrdM/97S6M+Rm1t4H8gHBneuv7Hwhuu0Z8a RHza6AuCR0JS8SnRMvII7VwtUKbjJcATps+aCChgpoQ/JLN7FcfWW8mDTeb5twK6gQ== X-Gm-Gg: Acq92OEEmNRIJr68gy758H+9y6+8YanMQDIPqpI3DPYuuJL7M+B7aKlYPFDl1YyhdML hWSJBiQmngUuEX1u8P00P1BZf0BPEOFG85a74zu5cRRcyXuZUCqjmsMuH511oAr13dBWbBXSMSM qM+CctoQA6oTzZ7LEg+zC1/EE/FkGbuvebxS8Y6NoioD4ugzGpsS0piHjyYNBvOw9sJIh/vPJud elA1Z/fCBFX167N4Mc5WCF8KC9zIbJ3ApTbWvShIRF4LKuiwBi1mmWyoWXj1327ZWD+Y8Xp4Ofg ZQZ0rLWcpnR6L9QPUrjmmo8MVRFPaq+A1OFps1axN9iGle6eO3rf9KvD3Lzr++B0VZ0Um4sk1UP UGs4wiN5NZFIMqoxr51XQ6a+OfgL0orE+SCouR9YBwID7JCj4P2P0tEpbq9ggpiRwmNLGNuN72q jgT3asns6uGTecAJ74EI3ui+L4pjCaXw9g6ezHfNAlN7zshq3WMBfUV7RuMAmxC44vbSHNAK+6g q4CWG9LAhB3uV5HcO876e2wDT3fZJl9NW7T6LFDQfe85s5e32IEtMA= X-Received: by 2002:a17:903:4b03:b0:2ba:99a2:c44e with SMTP id d9443c01a7336-2bd275c6b33mr14897575ad.21.1778639908456; Tue, 12 May 2026 19:38:28 -0700 (PDT) Received: from cosinus.tail5d817.ts.net ([118.148.153.235]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bc83101ee1sm116597975ad.79.2026.05.12.19.38.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 12 May 2026 19:38:27 -0700 (PDT) From: Julian Oes To: johan@kernel.org Cc: gregkh@linuxfoundation.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Julian Oes Subject: [PATCH] USB: serial: generic: recover from bulk-in endpoint stall Date: Wed, 13 May 2026 14:37:28 +1200 Message-ID: <20260513023728.55557-1-julian@oes.ch> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A USB-serial device can go permanently silent when its bulk-in endpoint gets stalled (-EPIPE). In my case, disconnecting a CDC ACM device causes my USB hub to stall the bulk-in endpoint of an FTDI adapter on a sibling port: the FTDI's bulk-in URB completes with -EPIPE and no further data arrives. Recovery requires userspace to close and reopen the tty. usb_serial_generic_read_bulk_callback() treats -EPIPE as fatal and abandons the URB. My assumption is that for a halted bulk endpoint, recovery is CLEAR_FEATURE(ENDPOINT_HALT) followed by resubmission. The fix follows the pattern used in cdc-acm: On -EPIPE, defer recovery to the existing per-port work, which clears the endpoint halt and resubmits the read URBs. With this patch, data from the FTDI adapter keeps arriving after the unrelated CDC ACM device is unplugged. Fixes: fc11efe2800f ("USB: serial: continue to read on errors") Signed-off-by: Julian Oes Tested-by: Julian Oes --- Reproducer hardware: - CDC ACM device: PX4 Pixhawk 6X (NuttX, 3185:0035) - FTDI device: SiK telemetry radio (FTDI FT231X, 0403:6015) - Hub: j5create JUH377 7-port hub. https://info.j5create.com/products/juh377 Tested on Linux 7.1-rc3 on Linux Mint 22.3 x86_64. Debugging and patch authoring were assisted by AI. usbmon traces of broken and fixed behaviour available on request. --- drivers/usb/serial/generic.c | 12 ++++++++---- drivers/usb/serial/usb-serial.c | 13 +++++++++++++ include/linux/usb/serial.h | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 757f0a586ddb..7f58ea55507b 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -371,6 +371,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *= urb) struct usb_serial_port *port =3D urb->context; unsigned char *data =3D urb->transfer_buffer; bool stopped =3D false; + bool stalled =3D false; int status =3D urb->status; int i; =20 @@ -395,9 +396,10 @@ void usb_serial_generic_read_bulk_callback(struct urb = *urb) stopped =3D true; break; case -EPIPE: - dev_err(&port->dev, "%s - urb stopped: %d\n", - __func__, status); - stopped =3D true; + dev_err(&port->dev, "%s - urb stalled: %d\n", + __func__, status); + set_bit(USB_SERIAL_RX_STALLED, &port->flags); + stalled =3D true; break; default: dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", @@ -420,7 +422,9 @@ void usb_serial_generic_read_bulk_callback(struct urb *= urb) */ smp_mb__after_atomic(); =20 - if (stopped) + if (stalled) + schedule_work(&port->work); + if (stopped || stalled) return; =20 if (test_bit(USB_SERIAL_THROTTLED, &port->flags)) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-seria= l.c index 0e072fd87c3d..383ff1d2fc35 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -629,6 +629,19 @@ static void usb_serial_port_work(struct work_struct *w= ork) struct usb_serial_port *port =3D container_of(work, struct usb_serial_port, work); =20 + if (test_and_clear_bit(USB_SERIAL_RX_STALLED, &port->flags)) { + int i; + + for (i =3D 0; i < ARRAY_SIZE(port->read_urbs); ++i) + usb_kill_urb(port->read_urbs[i]); + + usb_clear_halt(port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress)); + + usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); + } + tty_port_tty_wakeup(&port->port); } =20 diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 75b2b763f1ba..64dadb2cb7ea 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -20,6 +20,7 @@ /* USB serial flags */ #define USB_SERIAL_WRITE_BUSY 0 #define USB_SERIAL_THROTTLED 1 +#define USB_SERIAL_RX_STALLED 2 =20 /** * usb_serial_port: structure for the specific ports of a device. --=20 2.43.0