[PATCH 32/40] i2400m/USB: write transactions to the USB device

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Inaky Perez-Gonzalez
Date: Friday, December 5, 2008 - 11:55 am

Implements the backend so that the generic driver can send data to the
USB device. Implemented with a kthread sitting in a never-ending loop
that when kicked by the generic driver will pull data from the TX FIFO
and send it to the device until it drains it. Then it goes back sleep,
waiting for another kick.

This is a thread for similar reasons as the case of USB reading. We
need to be able to use the USB autopm management functions, which are
blocking. As well, it is dedicated to a single task, so it has less
overhead than a workqueue.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
---
 drivers/net/wimax/i2400m/usb-tx.c |  229 +++++++++++++++++++++++++++++++++++++
 1 files changed, 229 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/wimax/i2400m/usb-tx.c

diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c
new file mode 100644
index 0000000..dfd8933
--- /dev/null
+++ b/drivers/net/wimax/i2400m/usb-tx.c
@@ -0,0 +1,229 @@
+/*
+ * Intel Wireless WiMAX Connection 2400m
+ * USB specific TX handling
+ *
+ *
+ * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Intel Corporation <linux-wimax@intel.com>
+ * Yanir Lubetkin <yanirx.lubetkin@intel.com>
+ *  - Initial implementation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *  - Split transport/device specific
+ *
+ *
+ * Takes the TX messages in the i2400m's driver TX FIFO and sends them
+ * to the device until there are no more.
+ *
+ * If we fail sending the message, we just drop it. There isn't much
+ * we can do at this point. We could also retry, but the USB stack has
+ * already retried and still failed, so there is not much of a
+ * point. As well, most of the traffic is network, which has recovery
+ * methods for dropped packets.
+ *
+ * For sending we just obtain a FIFO buffer to send, send it to the
+ * USB bulk out, tell the TX FIFO code we have sent it; query for
+ * another one, etc... until done.
+ *
+ * We use a thread so we can call usb_autopm_enable() and
+ * usb_autopm_disable() for each transaction; this way when the device
+ * goes idle, it will suspend. It also has less overhead than a
+ * dedicated workqueue, as it is being used for a single task.
+ *
+ * ROADMAP
+ *
+ * i2400mu_tx_setup()
+ * i2400mu_tx_release()
+ *
+ * i2400mu_bus_tx_kick()	- Called by the tx.c code when there
+ *                                is new data in the FIFO.
+ * i2400mu_txd()
+ *   i2400m_tx_msg_get()
+ *   i2400m_tx_msg_sent()
+ */
+#include "i2400m-usb.h"
+
+
+#define D_SUBMODULE tx
+#include "usb-debug-levels.h"
+
+
+/*
+ * Get the next TX message in the TX FIFO and send it to the device
+ *
+ * Note that any iteration consumes a message to be sent, no matter if
+ * it succeeds or fails (we have no real way to retry or complain).
+ *
+ * Return: 0 if ok, < 0 errno code on hard error.
+ */
+static
+int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
+	       size_t tx_msg_size)
+{
+	int result = 0;
+	struct i2400m *i2400m = &i2400mu->i2400m;
+	struct device *dev = &i2400mu->usb_iface->dev;
+	int usb_pipe, sent_size, do_autopm;
+	struct usb_endpoint_descriptor *epd;
+
+	d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
+	do_autopm = atomic_read(&i2400mu->do_autopm);
+	result = do_autopm ?
+		usb_autopm_get_interface(i2400mu->usb_iface) : 0;
+	if (result < 0) {
+		dev_err(dev, "TX: can't get autopm: %d\n", result);
+		do_autopm = 0;
+	}
+	epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
+	usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
+retry:
+	result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
+			      tx_msg, tx_msg_size, &sent_size, HZ);
+	usb_mark_last_busy(i2400mu->usb_dev);
+	switch (result) {
+	case 0:
+		if (sent_size != tx_msg_size) {	/* Too short? drop it */
+			dev_err(dev, "TX: short write (%d B vs %zu "
+				"expected)\n", sent_size, tx_msg_size);
+			result = -EIO;
+		}
+		break;
+	case -EINVAL:			/* while removing driver */
+	case -ENODEV:			/* dev disconnect ... */
+	case -ENOENT:			/* just ignore it */
+	case -ESHUTDOWN:		/* and exit */
+	case -ECONNRESET:
+		result = -ESHUTDOWN;
+		break;
+	default:			/* Some error? */
+		if (edc_inc(&i2400mu->urb_edc,
+			    EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+			dev_err(dev, "TX: maximum errors in URB "
+				"exceeded; resetting device\n");
+			usb_queue_reset_device(i2400mu->usb_iface);
+		} else {
+			dev_err(dev, "TX: cannot send URB; retrying. "
+				"tx_msg @%zu %zu B [%d sent]: %d\n",
+				(void *) tx_msg - i2400m->tx_buf,
+				tx_msg_size, sent_size, result);
+			goto retry;
+		}
+	}
+	if (do_autopm)
+		usb_autopm_put_interface(i2400mu->usb_iface);
+	d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu);
+	return result;
+}
+
+
+/*
+ * Get the next TX message in the TX FIFO and send it to the device
+ *
+ * Note we exit the loop if i2400mu_tx() fails; that funtion only
+ * fails on hard error (failing to tx a buffer not being one of them,
+ * see its doc).
+ *
+ * Return: 0
+ */
+static
+int i2400mu_txd(void *_i2400mu)
+{
+	int result = 0;
+	struct i2400mu *i2400mu = _i2400mu;
+	struct i2400m *i2400m = &i2400mu->i2400m;
+	struct device *dev = &i2400mu->usb_iface->dev;
+	struct i2400m_msg_hdr *tx_msg;
+	size_t tx_msg_size;
+
+	d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
+
+	while (1) {
+		d_printf(2, dev, "TX: waiting for messages\n");
+		tx_msg = NULL;
+		wait_event_interruptible(
+			i2400mu->tx_wq,
+			(kthread_should_stop()	/* check this first! */
+			 || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size)))
+			);
+		if (kthread_should_stop())
+			break;
+		WARN_ON(tx_msg == NULL);	/* should not happen...*/
+		d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
+		d_dump(5, dev, tx_msg, tx_msg_size);
+		/* Yeah, we ignore errors ... not much we can do */
+		i2400mu_tx(i2400mu, tx_msg, tx_msg_size);
+		i2400m_tx_msg_sent(i2400m);	/* ack it, advance the FIFO */
+		if (result < 0)
+			break;
+	}
+	d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
+	return result;
+}
+
+
+/*
+ * i2400m TX engine notifies us that there is data in the FIFO ready
+ * for TX
+ *
+ * If there is a URB in flight, don't do anything; when it finishes,
+ * it will see there is data in the FIFO and send it. Else, just
+ * submit a write.
+ */
+void i2400mu_bus_tx_kick(struct i2400m *i2400m)
+{
+	struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
+	struct device *dev = &i2400mu->usb_iface->dev;
+
+	d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
+	wake_up_all(&i2400mu->tx_wq);
+	d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+}
+
+
+int i2400mu_tx_setup(struct i2400mu *i2400mu)
+{
+	int result = 0;
+	struct i2400m *i2400m = &i2400mu->i2400m;
+	struct device *dev = &i2400mu->usb_iface->dev;
+	struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
+
+	i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
+					  wimax_dev->name);
+	if (IS_ERR(i2400mu->tx_kthread)) {
+		result = PTR_ERR(i2400mu->tx_kthread);
+		dev_err(dev, "TX: cannot start thread: %d\n", result);
+	}
+	return result;
+}
+
+void i2400mu_tx_release(struct i2400mu *i2400mu)
+{
+	kthread_stop(i2400mu->tx_kthread);
+}
-- 
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[PATCH 00/40] merge request for WiMAX kernel stack and i24 ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 01/40] wimax: documentation for the stack, Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 02/40] wimax: declarations for the in-kernel WiMAX API, Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 03/40] wimax: constants and definitions to interact ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 04/40] wimax: internal API for the kernel space WiM ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 05/40] wimax: debug macros and debug settings for t ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 06/40] genetlink: export genl_unregister_mc_group(), Inaky Perez-Gonzalez, (Fri Dec 5, 11:54 am)
[PATCH 07/40] debugfs: add helpers for exporting a size_t ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 08/40] wimax: generic WiMAX device management (regi ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 09/40] wimax: Mappping of generic netlink family ID ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 10/40] wimax: Generic messaging interface between u ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 11/40] wimax: RF-kill framework integration, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 12/40] wimax: API call to reset a WiMAX device, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 13/40] wimax: debugfs controls, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 14/40] wimax: Makefile, Kconfig and docbook linkage ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 15/40] i2400m: documentation and instructions for usage, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 16/40] i2400m: host-to-device protocol definitions, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 17/40] i2400m: core driver definitions and API, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 18/40] i2400m: Generic probe/disconnect, reset and ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 19/40] i2400m: linkage to the networking stack, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 20/40] i2400m: debugfs controls, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 21/40] i2400m: rfkill integration with the WiMAX stack, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 22/40] i2400m: firmware loading and bootrom initial ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 23/40] i2400m: handling of the data/control recepti ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 24/40] i2400m: handling of the data/control transmi ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 25/40] i2400m: various functions for device management, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 26/40] i2400m/USB: header for the USB bus driver, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 27/40] i2400m/USB: error density tracking, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 28/40] i2400m/USB: main probe/disconnect and backen ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 29/40] i2400m/USB: firmware upload backend, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 30/40] i2400m/USB: handling of notifications from t ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 31/40] i2400m/USB: read transactions from the USB d ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 32/40] i2400m/USB: write transactions to the USB device, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 33/40] i2400m/SDIO: header for the SDIO subdriver, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 34/40] i2400m/SDIO: main probe/disconnect and backe ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 35/40] i2400m/SDIO: firmware upload backend, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 36/40] i2400m/SDIO: read transactions from the SDIO ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 37/40] i2400m/SDIO: write transactions to the SDIO ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 38/40] i2400m: Makefile and Kconfig, Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 39/40] wimax: export linux/wimax.h and linux/wimax/ ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
[PATCH 40/40] wimax/i2400m: add CREDITS and MAINTAINERS en ..., Inaky Perez-Gonzalez, (Fri Dec 5, 11:55 am)
Re: [PATCH 02/40] wimax: declarations for the in-kernel Wi ..., Inaky Perez-Gonzalez, (Sat Dec 6, 6:04 pm)