Skip to content

Commit

Permalink
USB: serial: generalise write buffer preparation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johan Hovold authored and Greg Kroah-Hartman committed May 20, 2010
1 parent 25d514c commit eaa3bcb
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 13 deletions.
47 changes: 34 additions & 13 deletions drivers/usb/serial/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -242,7 +263,6 @@ static int usb_serial_multi_urb_write(struct tty_struct *tty,
*/
static int usb_serial_generic_write_start(struct usb_serial_port *port)
{
unsigned char *data;
int result;
int count;
unsigned long flags;
Expand All @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/serial/usb-serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions include/linux/usb/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit eaa3bcb

Please sign in to comment.