USB: serial: generalise write buffer preparation
authorJohan Hovold <jhovold@gmail.com>
Wed, 17 Mar 2010 22:06:08 +0000 (23:06 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 20 May 2010 20:21:35 +0000 (13:21 -0700)
Generalise write buffer preparation.

This allows for drivers to manipulate (e.g. add headers) to bulk out
data before it is sent.

This adds a new function pointer to usb_serial_driver:

int (*prepare_write_buffer)(struct usb_serial_port *port,
void **dest, size_t size, const void *src, size_t count);

The function is generic and can be used with either kfifo-based or
multi-urb writes:

If *dest is NULL the implementation should allocate dest.
If src is NULL the implementation should use the port write fifo.

If not set, a generic implementation is used which simply uses memcpy or
kfifo_out.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/generic.c
drivers/usb/serial/usb-serial.c
include/linux/usb/serial.h

index ad4823bbfa19c8d73058360319ec3050ff362f2e..1a134f9c64f302528a475406ecf3e7a9907d3c66 100644 (file)
@@ -167,12 +167,35 @@ void usb_serial_generic_close(struct usb_serial_port *port)
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_close);
 
+int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
+               void **dest, size_t size, const void *src, size_t count)
+{
+       if (!*dest) {
+               size = count;
+               *dest = kmalloc(count, GFP_ATOMIC);
+               if (!*dest) {
+                       dev_err(&port->dev, "%s - could not allocate buffer\n",
+                                       __func__);
+                       return -ENOMEM;
+               }
+       }
+       if (src) {
+               count = size;
+               memcpy(*dest, src, size);
+       } else {
+               count = kfifo_out_locked(&port->write_fifo, *dest, size,
+                                                               &port->lock);
+       }
+       return count;
+}
+EXPORT_SYMBOL_GPL(usb_serial_generic_prepare_write_buffer);
+
 static int usb_serial_multi_urb_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count)
 {
        unsigned long flags;
        struct urb *urb;
-       unsigned char *buffer;
+       void *buffer;
        int status;
 
        spin_lock_irqsave(&port->lock, flags);
@@ -191,16 +214,14 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty,
                goto err_urb;
        }
 
+       buffer = NULL;
        count = min_t(int, count, PAGE_SIZE);
-       buffer = kmalloc(count, GFP_ATOMIC);
-       if (!buffer) {
-               dev_err(&port->dev, "%s - could not allocate buffer\n",
-                               __func__);
-               status = -ENOMEM;
+       count = port->serial->type->prepare_write_buffer(port, &buffer, 0,
+                                                               buf, count);
+       if (count < 0) {
+               status = count;
                goto err_buf;
        }
-
-       memcpy(buffer, buf, count);
        usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
        usb_fill_bulk_urb(urb, port->serial->dev,
                        usb_sndbulkpipe(port->serial->dev,
@@ -242,7 +263,6 @@ err_urb:
  */
 static int usb_serial_generic_write_start(struct usb_serial_port *port)
 {
-       unsigned char *data;
        int result;
        int count;
        unsigned long flags;
@@ -255,10 +275,11 @@ static int usb_serial_generic_write_start(struct usb_serial_port *port)
        port->write_urb_busy = 1;
        spin_unlock_irqrestore(&port->lock, flags);
 
-       data = port->write_urb->transfer_buffer;
-       count = kfifo_out_locked(&port->write_fifo, data, port->bulk_out_size, &port->lock);
-       usb_serial_debug_data(debug, &port->dev, __func__, count, data);
-
+       count = port->serial->type->prepare_write_buffer(port,
+                                       &port->write_urb->transfer_buffer,
+                                       port->bulk_out_size, NULL, 0);
+       usb_serial_debug_data(debug, &port->dev, __func__,
+                               count, port->write_urb->transfer_buffer);
        port->write_urb->transfer_buffer_length = count;
 
        /* send the data out the bulk port */
index 1b924425089f3d4baf91a61b3c305d1c1bfd587a..8249fd8381fb8b5f8d4a179faad963a628dd9d20 100644 (file)
@@ -1299,6 +1299,7 @@ static void fixup_generic(struct usb_serial_driver *device)
        set_to_generic_if_null(device, disconnect);
        set_to_generic_if_null(device, release);
        set_to_generic_if_null(device, process_read_urb);
+       set_to_generic_if_null(device, prepare_write_buffer);
 }
 
 int usb_serial_register(struct usb_serial_driver *driver)
index 2a328376160033e09bf5fd2c6b5cb3f4a23b4c86..a4c99ea390e70a6b59e943f502e029f90f1bed52 100644 (file)
@@ -277,6 +277,9 @@ 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 by the generic write implementation */
+       int (*prepare_write_buffer)(struct usb_serial_port *port,
+               void **dest, size_t size, const void *src, size_t count);
 };
 #define to_usb_serial_driver(d) \
        container_of(d, struct usb_serial_driver, driver)
@@ -329,6 +332,8 @@ extern void usb_serial_generic_deregister(void);
 extern int usb_serial_generic_submit_read_urb(struct usb_serial_port *port,
                                                 gfp_t mem_flags);
 extern void usb_serial_generic_process_read_urb(struct urb *urb);
+extern int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
+               void **dest, size_t size, const void *src, size_t count);
 extern int usb_serial_handle_sysrq_char(struct tty_struct *tty,
                                        struct usb_serial_port *port,
                                        unsigned int ch);