aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/usb-tx.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2008-12-20 19:57:53 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 13:00:21 -0500
commita8ebf98f541463107bb9544a1b611981dc2477e7 (patch)
treeaae6235a1c14ecc2a212f020c9168507d630a23a /drivers/net/wimax/i2400m/usb-tx.c
parent795038107b0078ee5ad3ad33327fe1c3520f6bf2 (diff)
i2400m/USB: TX and RX path backends
Implements the backend so that the generic driver can TX/RX to/from the USB device. TX is implemented with a kthread sitting in a never-ending loop that when kicked by the generic driver's TX code 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. RX is implemented in a similar fashion, but reads are kicked in by the device notifying in the interrupt endpoint that data is ready. Device reset notifications are also sent via the notification endpoint. We need a thread contexts to run USB autopm functions (blocking) and to process the received data (can get to be heavy in processing time). Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/net/wimax/i2400m/usb-tx.c')
-rw-r--r--drivers/net/wimax/i2400m/usb-tx.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c
new file mode 100644
index 000000000000..dfd893356f49
--- /dev/null
+++ b/drivers/net/wimax/i2400m/usb-tx.c
@@ -0,0 +1,229 @@
1/*
2 * Intel Wireless WiMAX Connection 2400m
3 * USB specific TX handling
4 *
5 *
6 * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Intel Corporation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 *
35 * Intel Corporation <linux-wimax@intel.com>
36 * Yanir Lubetkin <yanirx.lubetkin@intel.com>
37 * - Initial implementation
38 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
39 * - Split transport/device specific
40 *
41 *
42 * Takes the TX messages in the i2400m's driver TX FIFO and sends them
43 * to the device until there are no more.
44 *
45 * If we fail sending the message, we just drop it. There isn't much
46 * we can do at this point. We could also retry, but the USB stack has
47 * already retried and still failed, so there is not much of a
48 * point. As well, most of the traffic is network, which has recovery
49 * methods for dropped packets.
50 *
51 * For sending we just obtain a FIFO buffer to send, send it to the
52 * USB bulk out, tell the TX FIFO code we have sent it; query for
53 * another one, etc... until done.
54 *
55 * We use a thread so we can call usb_autopm_enable() and
56 * usb_autopm_disable() for each transaction; this way when the device
57 * goes idle, it will suspend. It also has less overhead than a
58 * dedicated workqueue, as it is being used for a single task.
59 *
60 * ROADMAP
61 *
62 * i2400mu_tx_setup()
63 * i2400mu_tx_release()
64 *
65 * i2400mu_bus_tx_kick() - Called by the tx.c code when there
66 * is new data in the FIFO.
67 * i2400mu_txd()
68 * i2400m_tx_msg_get()
69 * i2400m_tx_msg_sent()
70 */
71#include "i2400m-usb.h"
72
73
74#define D_SUBMODULE tx
75#include "usb-debug-levels.h"
76
77
78/*
79 * Get the next TX message in the TX FIFO and send it to the device
80 *
81 * Note that any iteration consumes a message to be sent, no matter if
82 * it succeeds or fails (we have no real way to retry or complain).
83 *
84 * Return: 0 if ok, < 0 errno code on hard error.
85 */
86static
87int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
88 size_t tx_msg_size)
89{
90 int result = 0;
91 struct i2400m *i2400m = &i2400mu->i2400m;
92 struct device *dev = &i2400mu->usb_iface->dev;
93 int usb_pipe, sent_size, do_autopm;
94 struct usb_endpoint_descriptor *epd;
95
96 d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
97 do_autopm = atomic_read(&i2400mu->do_autopm);
98 result = do_autopm ?
99 usb_autopm_get_interface(i2400mu->usb_iface) : 0;
100 if (result < 0) {
101 dev_err(dev, "TX: can't get autopm: %d\n", result);
102 do_autopm = 0;
103 }
104 epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
105 usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
106retry:
107 result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
108 tx_msg, tx_msg_size, &sent_size, HZ);
109 usb_mark_last_busy(i2400mu->usb_dev);
110 switch (result) {
111 case 0:
112 if (sent_size != tx_msg_size) { /* Too short? drop it */
113 dev_err(dev, "TX: short write (%d B vs %zu "
114 "expected)\n", sent_size, tx_msg_size);
115 result = -EIO;
116 }
117 break;
118 case -EINVAL: /* while removing driver */
119 case -ENODEV: /* dev disconnect ... */
120 case -ENOENT: /* just ignore it */
121 case -ESHUTDOWN: /* and exit */
122 case -ECONNRESET:
123 result = -ESHUTDOWN;
124 break;
125 default: /* Some error? */
126 if (edc_inc(&i2400mu->urb_edc,
127 EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
128 dev_err(dev, "TX: maximum errors in URB "
129 "exceeded; resetting device\n");
130 usb_queue_reset_device(i2400mu->usb_iface);
131 } else {
132 dev_err(dev, "TX: cannot send URB; retrying. "
133 "tx_msg @%zu %zu B [%d sent]: %d\n",
134 (void *) tx_msg - i2400m->tx_buf,
135 tx_msg_size, sent_size, result);
136 goto retry;
137 }
138 }
139 if (do_autopm)
140 usb_autopm_put_interface(i2400mu->usb_iface);
141 d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu);
142 return result;
143}
144
145
146/*
147 * Get the next TX message in the TX FIFO and send it to the device
148 *
149 * Note we exit the loop if i2400mu_tx() fails; that funtion only
150 * fails on hard error (failing to tx a buffer not being one of them,
151 * see its doc).
152 *
153 * Return: 0
154 */
155static
156int i2400mu_txd(void *_i2400mu)
157{
158 int result = 0;
159 struct i2400mu *i2400mu = _i2400mu;
160 struct i2400m *i2400m = &i2400mu->i2400m;
161 struct device *dev = &i2400mu->usb_iface->dev;
162 struct i2400m_msg_hdr *tx_msg;
163 size_t tx_msg_size;
164
165 d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
166
167 while (1) {
168 d_printf(2, dev, "TX: waiting for messages\n");
169 tx_msg = NULL;
170 wait_event_interruptible(
171 i2400mu->tx_wq,
172 (kthread_should_stop() /* check this first! */
173 || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size)))
174 );
175 if (kthread_should_stop())
176 break;
177 WARN_ON(tx_msg == NULL); /* should not happen...*/
178 d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
179 d_dump(5, dev, tx_msg, tx_msg_size);
180 /* Yeah, we ignore errors ... not much we can do */
181 i2400mu_tx(i2400mu, tx_msg, tx_msg_size);
182 i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */
183 if (result < 0)
184 break;
185 }
186 d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
187 return result;
188}
189
190
191/*
192 * i2400m TX engine notifies us that there is data in the FIFO ready
193 * for TX
194 *
195 * If there is a URB in flight, don't do anything; when it finishes,
196 * it will see there is data in the FIFO and send it. Else, just
197 * submit a write.
198 */
199void i2400mu_bus_tx_kick(struct i2400m *i2400m)
200{
201 struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
202 struct device *dev = &i2400mu->usb_iface->dev;
203
204 d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
205 wake_up_all(&i2400mu->tx_wq);
206 d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
207}
208
209
210int i2400mu_tx_setup(struct i2400mu *i2400mu)
211{
212 int result = 0;
213 struct i2400m *i2400m = &i2400mu->i2400m;
214 struct device *dev = &i2400mu->usb_iface->dev;
215 struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
216
217 i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
218 wimax_dev->name);
219 if (IS_ERR(i2400mu->tx_kthread)) {
220 result = PTR_ERR(i2400mu->tx_kthread);
221 dev_err(dev, "TX: cannot start thread: %d\n", result);
222 }
223 return result;
224}
225
226void i2400mu_tx_release(struct i2400mu *i2400mu)
227{
228 kthread_stop(i2400mu->tx_kthread);
229}