Commit 60b3013c authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman

USB: kl5usb105: reimplement using generic framework

Kill custom read and write implementations (static per-port,
singleton(!) urb pool).

Also remove changelog header (can be retrieved through git).

Read processing and write-buffer handling tested using a cp210x device
in a loopback setup.
Signed-off-by: default avatarJohan Hovold <jhovold@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ff8c195f
/* /*
* KLSI KL5KUSB105 chip RS232 converter driver * KLSI KL5KUSB105 chip RS232 converter driver
* *
* Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
* Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de> * Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -34,17 +35,6 @@ ...@@ -34,17 +35,6 @@
* implement handshaking or decide that we do not support it * implement handshaking or decide that we do not support it
*/ */
/* History:
* 0.3a - implemented pools of write URBs
* 0.3 - alpha version for public testing
* 0.2 - TIOCMGET works, so autopilot(1) can be used!
* 0.1 - can be used to do pilot-xfer -p /dev/ttyUSB0 -l
*
* The driver skeleton is mainly based on mct_u232.c and various other
* pieces of code shamelessly copied from the drivers/usb/serial/ directory.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -64,8 +54,8 @@ static int debug; ...@@ -64,8 +54,8 @@ static int debug;
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.3a" #define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>" #define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver" #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
...@@ -73,23 +63,17 @@ static int debug; ...@@ -73,23 +63,17 @@ static int debug;
* Function prototypes * Function prototypes
*/ */
static int klsi_105_startup(struct usb_serial *serial); static int klsi_105_startup(struct usb_serial *serial);
static void klsi_105_disconnect(struct usb_serial *serial);
static void klsi_105_release(struct usb_serial *serial); static void klsi_105_release(struct usb_serial *serial);
static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port);
static int klsi_105_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count);
static void klsi_105_write_bulk_callback(struct urb *urb);
static int klsi_105_chars_in_buffer(struct tty_struct *tty);
static int klsi_105_write_room(struct tty_struct *tty);
static void klsi_105_read_bulk_callback(struct urb *urb);
static void klsi_105_set_termios(struct tty_struct *tty, static void klsi_105_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old); struct usb_serial_port *port, struct ktermios *old);
static void klsi_105_throttle(struct tty_struct *tty);
static void klsi_105_unthrottle(struct tty_struct *tty);
static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file); static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file);
static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear); unsigned int set, unsigned int clear);
static void klsi_105_process_read_urb(struct urb *urb);
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
void *dest, size_t size);
/* /*
* All of the device info needed for the KLSI converters. * All of the device info needed for the KLSI converters.
...@@ -119,22 +103,19 @@ static struct usb_serial_driver kl5kusb105d_device = { ...@@ -119,22 +103,19 @@ static struct usb_serial_driver kl5kusb105d_device = {
.usb_driver = &kl5kusb105d_driver, .usb_driver = &kl5kusb105d_driver,
.id_table = id_table, .id_table = id_table,
.num_ports = 1, .num_ports = 1,
.bulk_out_size = 64,
.open = klsi_105_open, .open = klsi_105_open,
.close = klsi_105_close, .close = klsi_105_close,
.write = klsi_105_write,
.write_bulk_callback = klsi_105_write_bulk_callback,
.chars_in_buffer = klsi_105_chars_in_buffer,
.write_room = klsi_105_write_room,
.read_bulk_callback = klsi_105_read_bulk_callback,
.set_termios = klsi_105_set_termios, .set_termios = klsi_105_set_termios,
/*.break_ctl = klsi_105_break_ctl,*/ /*.break_ctl = klsi_105_break_ctl,*/
.tiocmget = klsi_105_tiocmget, .tiocmget = klsi_105_tiocmget,
.tiocmset = klsi_105_tiocmset, .tiocmset = klsi_105_tiocmset,
.attach = klsi_105_startup, .attach = klsi_105_startup,
.disconnect = klsi_105_disconnect,
.release = klsi_105_release, .release = klsi_105_release,
.throttle = klsi_105_throttle, .throttle = usb_serial_generic_throttle,
.unthrottle = klsi_105_unthrottle, .unthrottle = usb_serial_generic_unthrottle,
.process_read_urb = klsi_105_process_read_urb,
.prepare_write_buffer = klsi_105_prepare_write_buffer,
}; };
struct klsi_105_port_settings { struct klsi_105_port_settings {
...@@ -145,18 +126,11 @@ struct klsi_105_port_settings { ...@@ -145,18 +126,11 @@ struct klsi_105_port_settings {
__u8 unknown2; __u8 unknown2;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* we implement a pool of NUM_URBS urbs per usb_serial */
#define NUM_URBS 1
#define URB_TRANSFER_BUFFER_SIZE 64
struct klsi_105_private { struct klsi_105_private {
struct klsi_105_port_settings cfg; struct klsi_105_port_settings cfg;
struct ktermios termios; struct ktermios termios;
unsigned long line_state; /* modem line settings */ unsigned long line_state; /* modem line settings */
/* write pool */
struct urb *write_urb_pool[NUM_URBS];
spinlock_t lock; spinlock_t lock;
unsigned long bytes_in;
unsigned long bytes_out;
}; };
...@@ -259,7 +233,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, ...@@ -259,7 +233,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
static int klsi_105_startup(struct usb_serial *serial) static int klsi_105_startup(struct usb_serial *serial)
{ {
struct klsi_105_private *priv; struct klsi_105_private *priv;
int i, j; int i;
/* check if we support the product id (see keyspan.c) /* check if we support the product id (see keyspan.c)
* FIXME * FIXME
...@@ -283,29 +257,9 @@ static int klsi_105_startup(struct usb_serial *serial) ...@@ -283,29 +257,9 @@ static int klsi_105_startup(struct usb_serial *serial)
priv->line_state = 0; priv->line_state = 0;
priv->bytes_in = 0;
priv->bytes_out = 0;
usb_set_serial_port_data(serial->port[i], priv); usb_set_serial_port_data(serial->port[i], priv);
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
for (j = 0; j < NUM_URBS; j++) {
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
priv->write_urb_pool[j] = urb;
if (urb == NULL) {
dev_err(&serial->dev->dev, "No more urbs???\n");
goto err_cleanup;
}
urb->transfer_buffer =
kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
if (!urb->transfer_buffer) {
dev_err(&serial->dev->dev,
"%s - out of memory for urb buffers.\n",
__func__);
goto err_cleanup;
}
}
/* priv->termios is left uninitalized until port opening */ /* priv->termios is left uninitalized until port opening */
init_waitqueue_head(&serial->port[i]->write_wait); init_waitqueue_head(&serial->port[i]->write_wait);
...@@ -316,56 +270,20 @@ static int klsi_105_startup(struct usb_serial *serial) ...@@ -316,56 +270,20 @@ static int klsi_105_startup(struct usb_serial *serial)
err_cleanup: err_cleanup:
for (; i >= 0; i--) { for (; i >= 0; i--) {
priv = usb_get_serial_port_data(serial->port[i]); priv = usb_get_serial_port_data(serial->port[i]);
for (j = 0; j < NUM_URBS; j++) {
if (priv->write_urb_pool[j]) {
kfree(priv->write_urb_pool[j]->transfer_buffer);
usb_free_urb(priv->write_urb_pool[j]);
}
}
kfree(priv); kfree(priv);
usb_set_serial_port_data(serial->port[i], NULL); usb_set_serial_port_data(serial->port[i], NULL);
} }
return -ENOMEM; return -ENOMEM;
} }
static void klsi_105_disconnect(struct usb_serial *serial)
{
int i;
dbg("%s", __func__);
/* stop reads and writes on all ports */
for (i = 0; i < serial->num_ports; ++i) {
struct klsi_105_private *priv =
usb_get_serial_port_data(serial->port[i]);
if (priv) {
/* kill our write urb pool */
int j;
struct urb **write_urbs = priv->write_urb_pool;
for (j = 0; j < NUM_URBS; j++) {
if (write_urbs[j]) {
usb_kill_urb(write_urbs[j]);
usb_free_urb(write_urbs[j]);
}
}
}
}
}
static void klsi_105_release(struct usb_serial *serial) static void klsi_105_release(struct usb_serial *serial)
{ {
int i; int i;
dbg("%s", __func__); dbg("%s", __func__);
for (i = 0; i < serial->num_ports; ++i) { for (i = 0; i < serial->num_ports; ++i)
struct klsi_105_private *priv = kfree(usb_get_serial_port_data(serial->port[i]));
usb_get_serial_port_data(serial->port[i]);
kfree(priv);
}
} }
static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
...@@ -416,18 +334,8 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) ...@@ -416,18 +334,8 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
/* READ_ON and urb submission */ /* READ_ON and urb submission */
usb_fill_bulk_urb(port->read_urb, port->serial->dev, rc = usb_serial_generic_open(tty, port);
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer,
port->read_urb->transfer_buffer_length,
klsi_105_read_bulk_callback,
port);
rc = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (rc) { if (rc) {
dev_err(&port->dev, "%s - failed submitting read urb, "
"error %d\n", __func__, rc);
retval = rc; retval = rc;
goto exit; goto exit;
} }
...@@ -464,7 +372,6 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) ...@@ -464,7 +372,6 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
static void klsi_105_close(struct usb_serial_port *port) static void klsi_105_close(struct usb_serial_port *port)
{ {
struct klsi_105_private *priv = usb_get_serial_port_data(port);
int rc; int rc;
dbg("%s port %d", __func__, port->number); dbg("%s port %d", __func__, port->number);
...@@ -487,232 +394,61 @@ static void klsi_105_close(struct usb_serial_port *port) ...@@ -487,232 +394,61 @@ static void klsi_105_close(struct usb_serial_port *port)
mutex_unlock(&port->serial->disc_mutex); mutex_unlock(&port->serial->disc_mutex);
/* shutdown our bulk reads and writes */ /* shutdown our bulk reads and writes */
usb_kill_urb(port->write_urb); usb_serial_generic_close(port);
usb_kill_urb(port->read_urb);
/* unlink our write pool */
/* FIXME */
/* wgg - do I need this? I think so. */ /* wgg - do I need this? I think so. */
usb_kill_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_in_urb);
dev_info(&port->serial->dev->dev,
"port stats: %ld bytes in, %ld bytes out\n",
priv->bytes_in, priv->bytes_out);
} }
/* We need to write a complete 64-byte data block and encode the /* We need to write a complete 64-byte data block and encode the
* number actually sent in the first double-byte, LSB-order. That * number actually sent in the first double-byte, LSB-order. That
* leaves at most 62 bytes of payload. * leaves at most 62 bytes of payload.
*/ */
#define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */ #define KLSI_HDR_LEN 2
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
static int klsi_105_write(struct tty_struct *tty, void *dest, size_t size)
struct usb_serial_port *port, const unsigned char *buf, int count)
{
struct klsi_105_private *priv = usb_get_serial_port_data(port);
int result, size;
int bytes_sent = 0;
dbg("%s - port %d", __func__, port->number);
while (count > 0) {
/* try to find a free urb (write 0 bytes if none) */
struct urb *urb = NULL;
unsigned long flags;
int i;
/* since the pool is per-port we might not need
the spin lock !? */
spin_lock_irqsave(&priv->lock, flags);
for (i = 0; i < NUM_URBS; i++) {
if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
urb = priv->write_urb_pool[i];
dbg("%s - using pool URB %d", __func__, i);
break;
}
}
spin_unlock_irqrestore(&priv->lock, flags);
if (urb == NULL) {
dbg("%s - no more free urbs", __func__);
goto exit;
}
if (urb->transfer_buffer == NULL) {
urb->transfer_buffer =
kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC);
if (urb->transfer_buffer == NULL) {
dev_err(&port->dev,
"%s - no more kernel memory...\n",
__func__);
goto exit;
}
}
size = min(count, port->bulk_out_size - KLSI_105_DATA_OFFSET);
size = min(size, URB_TRANSFER_BUFFER_SIZE -
KLSI_105_DATA_OFFSET);
memcpy(urb->transfer_buffer + KLSI_105_DATA_OFFSET, buf, size);
/* write payload size into transfer buffer */
((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF);
((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8);
/* set up our urb */
usb_fill_bulk_urb(urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
urb->transfer_buffer,
URB_TRANSFER_BUFFER_SIZE,
klsi_105_write_bulk_callback,
port);
/* send the data out the bulk port */
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev,
"%s - failed submitting write urb, error %d\n",
__func__, result);
goto exit;
}
buf += size;
bytes_sent += size;
count -= size;
}
exit:
/* lockless, but it's for debug info only... */
priv->bytes_out += bytes_sent;
return bytes_sent; /* that's how much we wrote */
}
static void klsi_105_write_bulk_callback(struct urb *urb)
{ {
struct usb_serial_port *port = urb->context; unsigned char *buf = dest;
int status = urb->status; int count;
dbg("%s - port %d", __func__, port->number);
if (status) { count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
dbg("%s - nonzero write bulk status received: %d", __func__, &port->lock);
status); put_unaligned_le16(count, buf);
return;
}
usb_serial_port_softint(port); return count + KLSI_HDR_LEN;
} }
/* return number of characters currently in the writing process */ /* The data received is preceded by a length double-byte in LSB-first order.
static int klsi_105_chars_in_buffer(struct tty_struct *tty) */
{ static void klsi_105_process_read_urb(struct urb *urb)
struct usb_serial_port *port = tty->driver_data;
int chars = 0;
int i;
unsigned long flags;
struct klsi_105_private *priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
if (priv->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
spin_unlock_irqrestore(&priv->lock, flags);
dbg("%s - returns %d", __func__, chars);
return chars;
}
static int klsi_105_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
unsigned long flags;
int i;
int room = 0;
struct klsi_105_private *priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
if (priv->write_urb_pool[i]->status != -EINPROGRESS)
room += URB_TRANSFER_BUFFER_SIZE;
}
spin_unlock_irqrestore(&priv->lock, flags);
dbg("%s - returns %d", __func__, room);
return room;
}
static void klsi_105_read_bulk_callback(struct urb *urb)
{ {
struct usb_serial_port *port = urb->context; struct usb_serial_port *port = urb->context;
struct klsi_105_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer; unsigned char *data = urb->transfer_buffer;
int rc; struct tty_struct *tty;
int status = urb->status; unsigned len;
dbg("%s - port %d", __func__, port->number); /* empty urbs seem to happen, we ignore them */
if (!urb->actual_length)
return;
/* The urb might have been killed. */ if (urb->actual_length <= KLSI_HDR_LEN) {
if (status) { dbg("%s - malformed packet", __func__);
dbg("%s - nonzero read bulk status received: %d", __func__,
status);
return; return;
} }
/* The data received is again preceded by a length double-byte in LSB- tty = tty_port_tty_get(&port->port);
* first order (see klsi_105_write() ) if (!tty)
*/ return;
if (urb->actual_length == 0) {
/* empty urbs seem to happen, we ignore them */
/* dbg("%s - emtpy URB", __func__); */
;
} else if (urb->actual_length <= 2) {
dbg("%s - size %d URB not understood", __func__,
urb->actual_length);
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
} else {
int bytes_sent = ((__u8 *) data)[0] +
((unsigned int) ((__u8 *) data)[1] << 8);
tty = tty_port_tty_get(&port->port);
/* we should immediately resubmit the URB, before attempting
* to pass the data on to the tty layer. But that needs locking
* against re-entry an then mixed-up data because of
* intermixed tty_flip_buffer_push()s
* FIXME
*/
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
if (bytes_sent + 2 > urb->actual_length) {
dbg("%s - trying to read more data than available"
" (%d vs. %d)", __func__,
bytes_sent+2, urb->actual_length);
/* cap at implied limit */
bytes_sent = urb->actual_length - 2;
}
tty_insert_flip_string(tty, data + 2, bytes_sent);
tty_flip_buffer_push(tty);
tty_kref_put(tty);
/* again lockless, but debug info only */ len = get_unaligned_le16(data);
priv->bytes_in += bytes_sent; if (len > urb->actual_length - KLSI_HDR_LEN) {
dbg("%s - packet length mismatch", __func__);
len = urb->actual_length - KLSI_HDR_LEN;
} }
/* Continue trying to always read */
usb_fill_bulk_urb(port->read_urb, port->serial->dev, tty_insert_flip_string(tty, data + KLSI_HDR_LEN, len);
usb_rcvbulkpipe(port->serial->dev, tty_flip_buffer_push(tty);
port->bulk_in_endpointAddress), tty_kref_put(tty);
port->read_urb->transfer_buffer,
port->read_urb->transfer_buffer_length,
klsi_105_read_bulk_callback,
port);
rc = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (rc)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__func__, rc);
} }
static void klsi_105_set_termios(struct tty_struct *tty, static void klsi_105_set_termios(struct tty_struct *tty,
...@@ -954,28 +690,6 @@ static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file, ...@@ -954,28 +690,6 @@ static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file,
return retval; return retval;
} }
static void klsi_105_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
dbg("%s - port %d", __func__, port->number);
usb_kill_urb(port->read_urb);
}
static void klsi_105_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
int result;
dbg("%s - port %d", __func__, port->number);
port->read_urb->dev = port->serial->dev;
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (result)
dev_err(&port->dev,
"%s - failed submitting read urb, error %d\n",
__func__, result);
}
static int __init klsi_105_init(void) static int __init klsi_105_init(void)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment