Commit 5c0c7582 authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman
Browse files

USB: whiteheat: reimplement using generic framework



Kill custom list-based read and write implementations and reimplement
using the generic framework.

Compile-only tested.

Signed-off-by: default avatarJohan Hovold <jhovold@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a6765cba
Loading
Loading
Loading
Loading
+8 −531
Original line number Diff line number Diff line
@@ -95,10 +95,6 @@ static void whiteheat_release(struct usb_serial *serial);
static int  whiteheat_open(struct tty_struct *tty,
			struct usb_serial_port *port);
static void whiteheat_close(struct usb_serial_port *port);
static int  whiteheat_write(struct tty_struct *tty,
			struct usb_serial_port *port,
			const unsigned char *buf, int count);
static int  whiteheat_write_room(struct tty_struct *tty);
static int  whiteheat_ioctl(struct tty_struct *tty,
			unsigned int cmd, unsigned long arg);
static void whiteheat_set_termios(struct tty_struct *tty,
@@ -107,11 +103,6 @@ static int whiteheat_tiocmget(struct tty_struct *tty);
static int  whiteheat_tiocmset(struct tty_struct *tty,
			unsigned int set, unsigned int clear);
static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
static int  whiteheat_chars_in_buffer(struct tty_struct *tty);
static void whiteheat_throttle(struct tty_struct *tty);
static void whiteheat_unthrottle(struct tty_struct *tty);
static void whiteheat_read_callback(struct urb *urb);
static void whiteheat_write_callback(struct urb *urb);

static struct usb_serial_driver whiteheat_fake_device = {
	.driver = {
@@ -137,18 +128,13 @@ static struct usb_serial_driver whiteheat_device = {
	.release =		whiteheat_release,
	.open =			whiteheat_open,
	.close =		whiteheat_close,
	.write =		whiteheat_write,
	.write_room =		whiteheat_write_room,
	.ioctl =		whiteheat_ioctl,
	.set_termios =		whiteheat_set_termios,
	.break_ctl =		whiteheat_break_ctl,
	.tiocmget =		whiteheat_tiocmget,
	.tiocmset =		whiteheat_tiocmset,
	.chars_in_buffer =	whiteheat_chars_in_buffer,
	.throttle =		whiteheat_throttle,
	.unthrottle =		whiteheat_unthrottle,
	.read_bulk_callback =	whiteheat_read_callback,
	.write_bulk_callback =	whiteheat_write_callback,
	.throttle =		usb_serial_generic_throttle,
	.unthrottle =		usb_serial_generic_unthrottle,
};

static struct usb_serial_driver * const serial_drivers[] = {
@@ -165,29 +151,8 @@ struct whiteheat_command_private {
	__u8			result_buffer[64];
};


#define THROTTLED		0x01
#define ACTUALLY_THROTTLED	0x02

static int urb_pool_size = 8;

struct whiteheat_urb_wrap {
	struct list_head	list;
	struct urb		*urb;
};

struct whiteheat_private {
	spinlock_t		lock;
	__u8			flags;
	__u8			mcr;		/* FIXME: no locking on mcr */
	struct list_head	rx_urbs_free;
	struct list_head	rx_urbs_submitted;
	struct list_head	rx_urb_q;
	struct work_struct	rx_work;
	struct usb_serial_port	*port;
	struct list_head	tx_urbs_free;
	struct list_head	tx_urbs_submitted;
	struct mutex		deathwarrant;
};


@@ -197,12 +162,6 @@ static void stop_command_port(struct usb_serial *serial);
static void command_port_write_callback(struct urb *urb);
static void command_port_read_callback(struct urb *urb);

static int start_port_read(struct usb_serial_port *port);
static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
						struct list_head *head);
static struct list_head *list_first(struct list_head *head);
static void rx_data_softint(struct work_struct *work);

static int firm_send_command(struct usb_serial_port *port, __u8 command,
						__u8 *data, __u8 datasize);
static int firm_open(struct usb_serial_port *port);
@@ -348,11 +307,6 @@ static int whiteheat_attach(struct usb_serial *serial)
	__u8 *command;
	__u8 *result;
	int i;
	int j;
	struct urb *urb;
	int buf_size;
	struct whiteheat_urb_wrap *wrap;
	struct list_head *tmp;

	command_port = serial->port[COMMAND_PORT];

@@ -422,72 +376,7 @@ static int whiteheat_attach(struct usb_serial *serial)
			goto no_private;
		}

		spin_lock_init(&info->lock);
		mutex_init(&info->deathwarrant);
		info->flags = 0;
		info->mcr = 0;
		INIT_WORK(&info->rx_work, rx_data_softint);
		info->port = port;

		INIT_LIST_HEAD(&info->rx_urbs_free);
		INIT_LIST_HEAD(&info->rx_urbs_submitted);
		INIT_LIST_HEAD(&info->rx_urb_q);
		INIT_LIST_HEAD(&info->tx_urbs_free);
		INIT_LIST_HEAD(&info->tx_urbs_submitted);

		for (j = 0; j < urb_pool_size; j++) {
			urb = usb_alloc_urb(0, GFP_KERNEL);
			if (!urb) {
				dev_err(&port->dev, "No free urbs available\n");
				goto no_rx_urb;
			}
			buf_size = port->read_urb->transfer_buffer_length;
			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
			if (!urb->transfer_buffer) {
				dev_err(&port->dev,
					"Couldn't allocate urb buffer\n");
				goto no_rx_buf;
			}
			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
			if (!wrap) {
				dev_err(&port->dev,
					"Couldn't allocate urb wrapper\n");
				goto no_rx_wrap;
			}
			usb_fill_bulk_urb(urb, serial->dev,
					usb_rcvbulkpipe(serial->dev,
						port->bulk_in_endpointAddress),
					urb->transfer_buffer, buf_size,
					whiteheat_read_callback, port);
			wrap->urb = urb;
			list_add(&wrap->list, &info->rx_urbs_free);

			urb = usb_alloc_urb(0, GFP_KERNEL);
			if (!urb) {
				dev_err(&port->dev, "No free urbs available\n");
				goto no_tx_urb;
			}
			buf_size = port->write_urb->transfer_buffer_length;
			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
			if (!urb->transfer_buffer) {
				dev_err(&port->dev,
					"Couldn't allocate urb buffer\n");
				goto no_tx_buf;
			}
			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
			if (!wrap) {
				dev_err(&port->dev,
					"Couldn't allocate urb wrapper\n");
				goto no_tx_wrap;
			}
			usb_fill_bulk_urb(urb, serial->dev,
					usb_sndbulkpipe(serial->dev,
						port->bulk_out_endpointAddress),
					urb->transfer_buffer, buf_size,
					whiteheat_write_callback, port);
			wrap->urb = urb;
			list_add(&wrap->list, &info->tx_urbs_free);
		}

		usb_set_serial_port_data(port, info);
	}
@@ -530,29 +419,6 @@ no_command_private:
	for (i = serial->num_ports - 1; i >= 0; i--) {
		port = serial->port[i];
		info = usb_get_serial_port_data(port);
		for (j = urb_pool_size - 1; j >= 0; j--) {
			tmp = list_first(&info->tx_urbs_free);
			list_del(tmp);
			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
			urb = wrap->urb;
			kfree(wrap);
no_tx_wrap:
			kfree(urb->transfer_buffer);
no_tx_buf:
			usb_free_urb(urb);
no_tx_urb:
			tmp = list_first(&info->rx_urbs_free);
			list_del(tmp);
			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
			urb = wrap->urb;
			kfree(wrap);
no_rx_wrap:
			kfree(urb->transfer_buffer);
no_rx_buf:
			usb_free_urb(urb);
no_rx_urb:
			;
		}
		kfree(info);
no_private:
		;
@@ -568,12 +434,7 @@ no_command_buffer:
static void whiteheat_release(struct usb_serial *serial)
{
	struct usb_serial_port *command_port;
	struct usb_serial_port *port;
	struct whiteheat_private *info;
	struct whiteheat_urb_wrap *wrap;
	struct urb *urb;
	struct list_head *tmp;
	struct list_head *tmp2;
	int i;

	dbg("%s", __func__);
@@ -583,31 +444,14 @@ static void whiteheat_release(struct usb_serial *serial)
	kfree(usb_get_serial_port_data(command_port));

	for (i = 0; i < serial->num_ports; i++) {
		port = serial->port[i];
		info = usb_get_serial_port_data(port);
		list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
			list_del(tmp);
			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
			urb = wrap->urb;
			kfree(wrap);
			kfree(urb->transfer_buffer);
			usb_free_urb(urb);
		}
		list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) {
			list_del(tmp);
			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
			urb = wrap->urb;
			kfree(wrap);
			kfree(urb->transfer_buffer);
			usb_free_urb(urb);
		}
		info = usb_get_serial_port_data(serial->port[i]);
		kfree(info);
	}
}

static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
{
	int		retval = 0;
	int retval;

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

@@ -615,9 +459,6 @@ static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
	if (retval)
		goto exit;

	if (tty)
		tty->low_latency = 1;

	/* send an open port command */
	retval = firm_open(port);
	if (retval) {
@@ -639,17 +480,12 @@ static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
	usb_clear_halt(port->serial->dev, port->read_urb->pipe);
	usb_clear_halt(port->serial->dev, port->write_urb->pipe);

	/* Start reading from the device */
	retval = start_port_read(port);
	retval = usb_serial_generic_open(tty, port);
	if (retval) {
		dev_err(&port->dev,
			"%s - failed submitting read urb, error %d\n",
			__func__, retval);
		firm_close(port);
		stop_command_port(port->serial);
		goto exit;
	}

exit:
	dbg("%s - exit, retval = %d", __func__, retval);
	return retval;
@@ -658,125 +494,14 @@ exit:

static void whiteheat_close(struct usb_serial_port *port)
{
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct whiteheat_urb_wrap *wrap;
	struct urb *urb;
	struct list_head *tmp;
	struct list_head *tmp2;

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

	firm_report_tx_done(port);
	firm_close(port);

	/* shutdown our bulk reads and writes */
	mutex_lock(&info->deathwarrant);
	spin_lock_irq(&info->lock);
	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		urb = wrap->urb;
		list_del(tmp);
		spin_unlock_irq(&info->lock);
		usb_kill_urb(urb);
		spin_lock_irq(&info->lock);
		list_add(tmp, &info->rx_urbs_free);
	}
	list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
		list_move(tmp, &info->rx_urbs_free);
	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		urb = wrap->urb;
		list_del(tmp);
		spin_unlock_irq(&info->lock);
		usb_kill_urb(urb);
		spin_lock_irq(&info->lock);
		list_add(tmp, &info->tx_urbs_free);
	}
	spin_unlock_irq(&info->lock);
	mutex_unlock(&info->deathwarrant);
	stop_command_port(port->serial);
}


static int whiteheat_write(struct tty_struct *tty,
	struct usb_serial_port *port, const unsigned char *buf, int count)
{
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct whiteheat_urb_wrap *wrap;
	struct urb *urb;
	int result;
	int bytes;
	int sent = 0;
	unsigned long flags;
	struct list_head *tmp;

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

	if (count == 0) {
		dbg("%s - write request of 0 bytes", __func__);
		return (0);
	}

	while (count) {
		spin_lock_irqsave(&info->lock, flags);
		if (list_empty(&info->tx_urbs_free)) {
			spin_unlock_irqrestore(&info->lock, flags);
			break;
		}
		tmp = list_first(&info->tx_urbs_free);
		list_del(tmp);
		spin_unlock_irqrestore(&info->lock, flags);

		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		urb = wrap->urb;
		bytes = (count > port->bulk_out_size) ?
					port->bulk_out_size : count;
		memcpy(urb->transfer_buffer, buf + sent, bytes);

		usb_serial_debug_data(debug, &port->dev,
				__func__, bytes, urb->transfer_buffer);

		urb->transfer_buffer_length = bytes;
		result = usb_submit_urb(urb, GFP_ATOMIC);
		if (result) {
			dev_err_console(port,
				"%s - failed submitting write urb, error %d\n",
				__func__, result);
			sent = result;
			spin_lock_irqsave(&info->lock, flags);
			list_add(tmp, &info->tx_urbs_free);
			spin_unlock_irqrestore(&info->lock, flags);
			break;
		} else {
			sent += bytes;
			count -= bytes;
			spin_lock_irqsave(&info->lock, flags);
			list_add(tmp, &info->tx_urbs_submitted);
			spin_unlock_irqrestore(&info->lock, flags);
		}
	}

	return sent;
}

static int whiteheat_write_room(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct list_head *tmp;
	int room = 0;
	unsigned long flags;

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

	spin_lock_irqsave(&info->lock, flags);
	list_for_each(tmp, &info->tx_urbs_free)
		room++;
	spin_unlock_irqrestore(&info->lock, flags);
	room *= port->bulk_out_size;

	dbg("%s - returns %d", __func__, room);
	return (room);
	stop_command_port(port->serial);
}

static int whiteheat_tiocmget(struct tty_struct *tty)
@@ -836,7 +561,7 @@ static int whiteheat_ioctl(struct tty_struct *tty,
		serstruct.line = port->serial->minor;
		serstruct.port = port->number;
		serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
		serstruct.xmit_fifo_size = port->bulk_out_size;
		serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
		serstruct.custom_divisor = 0;
		serstruct.baud_base = 460800;
		serstruct.close_delay = CLOSING_DELAY;
@@ -866,60 +591,6 @@ static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
}


static int whiteheat_chars_in_buffer(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct list_head *tmp;
	struct whiteheat_urb_wrap *wrap;
	int chars = 0;
	unsigned long flags;

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

	spin_lock_irqsave(&info->lock, flags);
	list_for_each(tmp, &info->tx_urbs_submitted) {
		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		chars += wrap->urb->transfer_buffer_length;
	}
	spin_unlock_irqrestore(&info->lock, flags);

	dbg("%s - returns %d", __func__, chars);
	return chars;
}


static void whiteheat_throttle(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct whiteheat_private *info = usb_get_serial_port_data(port);

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

	spin_lock_irq(&info->lock);
	info->flags |= THROTTLED;
	spin_unlock_irq(&info->lock);
}


static void whiteheat_unthrottle(struct tty_struct *tty)
{
	struct usb_serial_port *port = tty->driver_data;
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	int actually_throttled;

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

	spin_lock_irq(&info->lock);
	actually_throttled = info->flags & ACTUALLY_THROTTLED;
	info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
	spin_unlock_irq(&info->lock);

	if (actually_throttled)
		rx_data_softint(&info->rx_work);
}


/*****************************************************************************
 * Connect Tech's White Heat callback routines
 *****************************************************************************/
@@ -988,80 +659,6 @@ static void command_port_read_callback(struct urb *urb)
}


static void whiteheat_read_callback(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	struct whiteheat_urb_wrap *wrap;
	unsigned char *data = urb->transfer_buffer;
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	int status = urb->status;

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

	spin_lock(&info->lock);
	wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);
	if (!wrap) {
		spin_unlock(&info->lock);
		dev_err(&port->dev, "%s - Not my urb!\n", __func__);
		return;
	}
	list_del(&wrap->list);
	spin_unlock(&info->lock);

	if (status) {
		dbg("%s - nonzero read bulk status received: %d",
		    __func__, status);
		spin_lock(&info->lock);
		list_add(&wrap->list, &info->rx_urbs_free);
		spin_unlock(&info->lock);
		return;
	}

	usb_serial_debug_data(debug, &port->dev,
				__func__, urb->actual_length, data);

	spin_lock(&info->lock);
	list_add_tail(&wrap->list, &info->rx_urb_q);
	if (info->flags & THROTTLED) {
		info->flags |= ACTUALLY_THROTTLED;
		spin_unlock(&info->lock);
		return;
	}
	spin_unlock(&info->lock);

	schedule_work(&info->rx_work);
}


static void whiteheat_write_callback(struct urb *urb)
{
	struct usb_serial_port *port = urb->context;
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct whiteheat_urb_wrap *wrap;
	int status = urb->status;

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

	spin_lock(&info->lock);
	wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
	if (!wrap) {
		spin_unlock(&info->lock);
		dev_err(&port->dev, "%s - Not my urb!\n", __func__);
		return;
	}
	list_move(&wrap->list, &info->tx_urbs_free);
	spin_unlock(&info->lock);

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

	usb_serial_port_softint(port);
}


/*****************************************************************************
 * Connect Tech's White Heat firmware interface
 *****************************************************************************/
@@ -1336,123 +933,6 @@ static void stop_command_port(struct usb_serial *serial)
	mutex_unlock(&command_info->mutex);
}


static int start_port_read(struct usb_serial_port *port)
{
	struct whiteheat_private *info = usb_get_serial_port_data(port);
	struct whiteheat_urb_wrap *wrap;
	struct urb *urb;
	int retval = 0;
	unsigned long flags;
	struct list_head *tmp;
	struct list_head *tmp2;

	spin_lock_irqsave(&info->lock, flags);

	list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
		list_del(tmp);
		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		urb = wrap->urb;
		spin_unlock_irqrestore(&info->lock, flags);
		retval = usb_submit_urb(urb, GFP_KERNEL);
		if (retval) {
			spin_lock_irqsave(&info->lock, flags);
			list_add(tmp, &info->rx_urbs_free);
			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
				wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
				urb = wrap->urb;
				list_del(tmp);
				spin_unlock_irqrestore(&info->lock, flags);
				usb_kill_urb(urb);
				spin_lock_irqsave(&info->lock, flags);
				list_add(tmp, &info->rx_urbs_free);
			}
			break;
		}
		spin_lock_irqsave(&info->lock, flags);
		list_add(tmp, &info->rx_urbs_submitted);
	}

	spin_unlock_irqrestore(&info->lock, flags);

	return retval;
}


static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
						struct list_head *head)
{
	struct whiteheat_urb_wrap *wrap;
	struct list_head *tmp;

	list_for_each(tmp, head) {
		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		if (wrap->urb == urb)
			return wrap;
	}

	return NULL;
}


static struct list_head *list_first(struct list_head *head)
{
	return head->next;
}


static void rx_data_softint(struct work_struct *work)
{
	struct whiteheat_private *info =
		container_of(work, struct whiteheat_private, rx_work);
	struct usb_serial_port *port = info->port;
	struct tty_struct *tty = tty_port_tty_get(&port->port);
	struct whiteheat_urb_wrap *wrap;
	struct urb *urb;
	unsigned long flags;
	struct list_head *tmp;
	struct list_head *tmp2;
	int result;
	int sent = 0;

	spin_lock_irqsave(&info->lock, flags);
	if (info->flags & THROTTLED) {
		spin_unlock_irqrestore(&info->lock, flags);
		goto out;
	}

	list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
		list_del(tmp);
		spin_unlock_irqrestore(&info->lock, flags);

		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
		urb = wrap->urb;

		if (tty && urb->actual_length)
			sent += tty_insert_flip_string(tty,
				urb->transfer_buffer, urb->actual_length);

		result = usb_submit_urb(urb, GFP_ATOMIC);
		if (result) {
			dev_err(&port->dev,
				"%s - failed resubmitting read urb, error %d\n",
				__func__, result);
			spin_lock_irqsave(&info->lock, flags);
			list_add(tmp, &info->rx_urbs_free);
			continue;
		}

		spin_lock_irqsave(&info->lock, flags);
		list_add(tmp, &info->rx_urbs_submitted);
	}
	spin_unlock_irqrestore(&info->lock, flags);

	if (sent)
		tty_flip_buffer_push(tty);
out:
	tty_kref_put(tty);
}

module_usb_serial_driver(whiteheat_driver, serial_drivers);

MODULE_AUTHOR(DRIVER_AUTHOR);
@@ -1462,8 +942,5 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE("whiteheat.fw");
MODULE_FIRMWARE("whiteheat_loader.fw");

module_param(urb_pool_size, int, 0);
MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");