Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+47 −333
Original line number Diff line number Diff line
/*
 * KLSI KL5KUSB105 chip RS232 converter driver
 *
 *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
 *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
 *
 *   This program is free software; you can redistribute it and/or modify
@@ -34,17 +35,6 @@
 *	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/errno.h>
#include <linux/init.h>
@@ -64,8 +54,8 @@ static int debug;
/*
 * Version Information
 */
#define DRIVER_VERSION "v0.3a"
#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"
#define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"


@@ -73,23 +63,17 @@ static int debug;
 * Function prototypes
 */
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 int  klsi_105_open(struct tty_struct *tty, 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,
			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_tiocmset(struct tty_struct *tty, struct file *file,
			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.
@@ -119,22 +103,19 @@ static struct usb_serial_driver kl5kusb105d_device = {
	.usb_driver =		&kl5kusb105d_driver,
	.id_table =		id_table,
	.num_ports =		1,
	.bulk_out_size =	64,
	.open =			klsi_105_open,
	.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,
	/*.break_ctl =		klsi_105_break_ctl,*/
	.tiocmget =		klsi_105_tiocmget,
	.tiocmset =		klsi_105_tiocmset,
	.attach =		klsi_105_startup,
	.disconnect =		klsi_105_disconnect,
	.release =		klsi_105_release,
	.throttle =		klsi_105_throttle,
	.unthrottle =		klsi_105_unthrottle,
	.throttle =		usb_serial_generic_throttle,
	.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 {
@@ -145,18 +126,11 @@ struct klsi_105_port_settings {
	__u8	unknown2;
} __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_port_settings	cfg;
	struct ktermios			termios;
	unsigned long			line_state; /* modem line settings */
	/* write pool */
	struct urb			*write_urb_pool[NUM_URBS];
	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,
static int klsi_105_startup(struct usb_serial *serial)
{
	struct klsi_105_private *priv;
	int i, j;
	int i;

	/* check if we support the product id (see keyspan.c)
	 * FIXME
@@ -283,29 +257,9 @@ static int klsi_105_startup(struct usb_serial *serial)

		priv->line_state    = 0;

		priv->bytes_in	    = 0;
		priv->bytes_out	    = 0;
		usb_set_serial_port_data(serial->port[i], priv);

		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 */
		init_waitqueue_head(&serial->port[i]->write_wait);
@@ -316,56 +270,20 @@ static int klsi_105_startup(struct usb_serial *serial)
err_cleanup:
	for (; i >= 0; 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);
		usb_set_serial_port_data(serial->port[i], NULL);
	}
	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)
{
	int i;

	dbg("%s", __func__);

	for (i = 0; i < serial->num_ports; ++i) {
		struct klsi_105_private *priv =
				usb_get_serial_port_data(serial->port[i]);

		kfree(priv);
	}
	for (i = 0; i < serial->num_ports; ++i)
		kfree(usb_get_serial_port_data(serial->port[i]));
}

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);

	/* READ_ON and urb submission */
	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
		      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);
	rc = usb_serial_generic_open(tty, port);
	if (rc) {
		dev_err(&port->dev, "%s - failed submitting read urb, "
			"error %d\n", __func__, rc);
		retval = rc;
		goto exit;
	}
@@ -464,7 +372,6 @@ exit:

static void klsi_105_close(struct usb_serial_port *port)
{
	struct klsi_105_private *priv = usb_get_serial_port_data(port);
	int rc;

	dbg("%s port %d", __func__, port->number);
@@ -487,232 +394,61 @@ static void klsi_105_close(struct usb_serial_port *port)
	mutex_unlock(&port->serial->disc_mutex);

	/* shutdown our bulk reads and writes */
	usb_kill_urb(port->write_urb);
	usb_kill_urb(port->read_urb);
	/* unlink our write pool */
	/* FIXME */
	usb_serial_generic_close(port);

	/* wgg - do I need this? I think so. */
	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
 * number actually sent in the first double-byte, LSB-order. That
 * leaves at most 62 bytes of payload.
 */
#define KLSI_105_DATA_OFFSET	2   /* in the bulk urb data block */

static int klsi_105_write(struct tty_struct *tty,
	struct usb_serial_port *port, const unsigned char *buf, int count)
#define KLSI_HDR_LEN		2
static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
						void *dest, size_t size)
{
	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;
		}
	unsigned char *buf = dest;
	int count;

		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;
	count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
								&port->lock);
	put_unaligned_le16(count, buf);

	return bytes_sent;	/* that's how much we wrote */
	return count + KLSI_HDR_LEN;
}

static void klsi_105_write_bulk_callback(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	int status = urb->status;

	dbg("%s - port %d", __func__, port->number);

	if (status) {
		dbg("%s - nonzero write bulk status received: %d", __func__,
		    status);
		return;
	}

	usb_serial_port_softint(port);
}

/* return number of characters currently in the writing process */
static int klsi_105_chars_in_buffer(struct tty_struct *tty)
{
	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)
/* The data received is preceded by a length double-byte in LSB-first order.
 */
static void klsi_105_process_read_urb(struct urb *urb)
{
	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;
	int rc;
	int status = urb->status;
	struct tty_struct *tty;
	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 (status) {
		dbg("%s - nonzero read bulk status received: %d", __func__,
		    status);
	if (urb->actual_length <= KLSI_HDR_LEN) {
		dbg("%s - malformed packet", __func__);
		return;
	}

	/* The data received is again preceded by a length double-byte in LSB-
	 * first order (see klsi_105_write() )
	 */
	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 (!tty)
		return;

		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;
	len = get_unaligned_le16(data);
	if (len > urb->actual_length - KLSI_HDR_LEN) {
		dbg("%s - packet length mismatch", __func__);
		len = urb->actual_length - KLSI_HDR_LEN;
	}

		tty_insert_flip_string(tty, data + 2, bytes_sent);
	tty_insert_flip_string(tty, data + KLSI_HDR_LEN, len);
	tty_flip_buffer_push(tty);
	tty_kref_put(tty);

		/* again lockless, but debug info only */
		priv->bytes_in += bytes_sent;
	}
	/* Continue trying to always read  */
	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
		      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_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,
@@ -954,28 +690,6 @@ static int klsi_105_tiocmset(struct tty_struct *tty, struct file *file,
	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)
{