summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorCrescent Hsieh <crescentcy.hsieh@moxa.com>2026-08-11 14:04:59 +0800
committerJohan Hovold <johan@kernel.org>2026-10-15 11:28:49 +0200
commit296ebbfc856e25bf0f10af648b3fe899a381bfce (patch)
treed01ae0d6aa790afc240aa32253d7ed840d6821fd /drivers/usb
parenta3ad29ec47d3bb4fb3fa20bf8fcc0a466052dd2c (diff)
USB: serial: add generic write control callbacks
Some USB serial devices use device-specific flow control to determine whether write submission may proceed. The generic write path currently provides no way to pause submission based on driver-specific state. It also provides no way for a driver to restore its state if URB submission fails. Add an optional write_ready() callback to struct usb_serial_driver. Call it with port->lock held after confirming that data is queued and a write URB is available. If the callback returns false, stop write processing and leave the data in the FIFO. The driver must call usb_serial_generic_write_start() when writes may proceed again. Add an optional write_rollback() callback for restoring driver-specific state after usb_submit_urb() fails. Call it with port->lock held after restoring the generic transmit accounting, and pass it the length of the prepared transfer. Drivers which do not provide these callbacks retain the existing generic write behaviour. Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com> Signed-off-by: Johan Hovold <johan@kernel.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/generic.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 17272701fab0..ba78635b0392 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -152,6 +152,7 @@ int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
int usb_serial_generic_write_start(struct usb_serial_port *port,
gfp_t mem_flags)
{
+ struct usb_serial_driver *type = port->serial->type;
struct urb *urb;
int count, result;
unsigned long flags;
@@ -161,7 +162,8 @@ int usb_serial_generic_write_start(struct usb_serial_port *port,
return 0;
retry:
spin_lock_irqsave(&port->lock, flags);
- if (!port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
+ if (!port->write_urbs_free || !kfifo_len(&port->write_fifo) ||
+ (type->write_ready && !type->write_ready(port))) {
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
spin_unlock_irqrestore(&port->lock, flags);
return 0;
@@ -171,8 +173,7 @@ retry:
spin_unlock_irqrestore(&port->lock, flags);
urb = port->write_urbs[i];
- count = port->serial->type->prepare_write_buffer(port,
- urb->transfer_buffer,
+ count = type->prepare_write_buffer(port, urb->transfer_buffer,
port->bulk_out_size);
urb->transfer_buffer_length = count;
usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer);
@@ -188,6 +189,10 @@ retry:
set_bit(i, &port->write_urbs_free);
spin_lock_irqsave(&port->lock, flags);
port->tx_bytes -= count;
+
+ if (type->write_rollback)
+ type->write_rollback(port, count);
+
spin_unlock_irqrestore(&port->lock, flags);
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);