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); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index ab0a32a90629..5866f19d3c7b 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -305,9 +305,17 @@ struct usb_serial_driver { void (*write_bulk_callback)(struct urb *urb); /* Called by the generic read bulk callback */ void (*process_read_urb)(struct urb *urb); + /* + * Called with port->lock held before preparing another write URB. + * Drivers returning false must restart the generic write path when + * writes may proceed again. + */ + bool (*write_ready)(struct usb_serial_port *port); /* Called by the generic write implementation */ int (*prepare_write_buffer)(struct usb_serial_port *port, void *dest, size_t size); + /* Called with port->lock held to roll back a failed write submission */ + void (*write_rollback)(struct usb_serial_port *port, int count); }; #define to_usb_serial_driver(d) \ container_of(d, struct usb_serial_driver, driver)