18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel Wireless WiMAX Connection 2400m 38c2ecf20Sopenharmony_ci * USB specific TX handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 98c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 108c2ecf20Sopenharmony_ci * are met: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 138c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 148c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 158c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 168c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 178c2ecf20Sopenharmony_ci * distribution. 188c2ecf20Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 198c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 208c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 238c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 248c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 258c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 268c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 278c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 288c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 298c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 308c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 318c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 328c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Intel Corporation <linux-wimax@intel.com> 368c2ecf20Sopenharmony_ci * Yanir Lubetkin <yanirx.lubetkin@intel.com> 378c2ecf20Sopenharmony_ci * - Initial implementation 388c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 398c2ecf20Sopenharmony_ci * - Split transport/device specific 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * Takes the TX messages in the i2400m's driver TX FIFO and sends them 438c2ecf20Sopenharmony_ci * to the device until there are no more. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * If we fail sending the message, we just drop it. There isn't much 468c2ecf20Sopenharmony_ci * we can do at this point. We could also retry, but the USB stack has 478c2ecf20Sopenharmony_ci * already retried and still failed, so there is not much of a 488c2ecf20Sopenharmony_ci * point. As well, most of the traffic is network, which has recovery 498c2ecf20Sopenharmony_ci * methods for dropped packets. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * For sending we just obtain a FIFO buffer to send, send it to the 528c2ecf20Sopenharmony_ci * USB bulk out, tell the TX FIFO code we have sent it; query for 538c2ecf20Sopenharmony_ci * another one, etc... until done. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * We use a thread so we can call usb_autopm_enable() and 568c2ecf20Sopenharmony_ci * usb_autopm_disable() for each transaction; this way when the device 578c2ecf20Sopenharmony_ci * goes idle, it will suspend. It also has less overhead than a 588c2ecf20Sopenharmony_ci * dedicated workqueue, as it is being used for a single task. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * ROADMAP 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * i2400mu_tx_setup() 638c2ecf20Sopenharmony_ci * i2400mu_tx_release() 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * i2400mu_bus_tx_kick() - Called by the tx.c code when there 668c2ecf20Sopenharmony_ci * is new data in the FIFO. 678c2ecf20Sopenharmony_ci * i2400mu_txd() 688c2ecf20Sopenharmony_ci * i2400m_tx_msg_get() 698c2ecf20Sopenharmony_ci * i2400m_tx_msg_sent() 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci#include "i2400m-usb.h" 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define D_SUBMODULE tx 758c2ecf20Sopenharmony_ci#include "usb-debug-levels.h" 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * Get the next TX message in the TX FIFO and send it to the device 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Note that any iteration consumes a message to be sent, no matter if 828c2ecf20Sopenharmony_ci * it succeeds or fails (we have no real way to retry or complain). 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Return: 0 if ok, < 0 errno code on hard error. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic 878c2ecf20Sopenharmony_ciint i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg, 888c2ecf20Sopenharmony_ci size_t tx_msg_size) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int result = 0; 918c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 928c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 938c2ecf20Sopenharmony_ci int usb_pipe, sent_size, do_autopm; 948c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *epd; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); 978c2ecf20Sopenharmony_ci do_autopm = atomic_read(&i2400mu->do_autopm); 988c2ecf20Sopenharmony_ci result = do_autopm ? 998c2ecf20Sopenharmony_ci usb_autopm_get_interface(i2400mu->usb_iface) : 0; 1008c2ecf20Sopenharmony_ci if (result < 0) { 1018c2ecf20Sopenharmony_ci dev_err(dev, "TX: can't get autopm: %d\n", result); 1028c2ecf20Sopenharmony_ci do_autopm = 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out); 1058c2ecf20Sopenharmony_ci usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); 1068c2ecf20Sopenharmony_ciretry: 1078c2ecf20Sopenharmony_ci result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe, 1088c2ecf20Sopenharmony_ci tx_msg, tx_msg_size, &sent_size, 200); 1098c2ecf20Sopenharmony_ci usb_mark_last_busy(i2400mu->usb_dev); 1108c2ecf20Sopenharmony_ci switch (result) { 1118c2ecf20Sopenharmony_ci case 0: 1128c2ecf20Sopenharmony_ci if (sent_size != tx_msg_size) { /* Too short? drop it */ 1138c2ecf20Sopenharmony_ci dev_err(dev, "TX: short write (%d B vs %zu " 1148c2ecf20Sopenharmony_ci "expected)\n", sent_size, tx_msg_size); 1158c2ecf20Sopenharmony_ci result = -EIO; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case -EPIPE: 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * Stall -- maybe the device is choking with our 1218c2ecf20Sopenharmony_ci * requests. Clear it and give it some time. If they 1228c2ecf20Sopenharmony_ci * happen to often, it might be another symptom, so we 1238c2ecf20Sopenharmony_ci * reset. 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * No error handling for usb_clear_halt(0; if it 1268c2ecf20Sopenharmony_ci * works, the retry works; if it fails, this switch 1278c2ecf20Sopenharmony_ci * does the error handling for us. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (edc_inc(&i2400mu->urb_edc, 1308c2ecf20Sopenharmony_ci 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { 1318c2ecf20Sopenharmony_ci dev_err(dev, "BM-CMD: too many stalls in " 1328c2ecf20Sopenharmony_ci "URB; resetting device\n"); 1338c2ecf20Sopenharmony_ci usb_queue_reset_device(i2400mu->usb_iface); 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci usb_clear_halt(i2400mu->usb_dev, usb_pipe); 1368c2ecf20Sopenharmony_ci msleep(10); /* give the device some time */ 1378c2ecf20Sopenharmony_ci goto retry; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci fallthrough; 1408c2ecf20Sopenharmony_ci case -EINVAL: /* while removing driver */ 1418c2ecf20Sopenharmony_ci case -ENODEV: /* dev disconnect ... */ 1428c2ecf20Sopenharmony_ci case -ENOENT: /* just ignore it */ 1438c2ecf20Sopenharmony_ci case -ESHUTDOWN: /* and exit */ 1448c2ecf20Sopenharmony_ci case -ECONNRESET: 1458c2ecf20Sopenharmony_ci result = -ESHUTDOWN; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci default: /* Some error? */ 1488c2ecf20Sopenharmony_ci if (edc_inc(&i2400mu->urb_edc, 1498c2ecf20Sopenharmony_ci EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { 1508c2ecf20Sopenharmony_ci dev_err(dev, "TX: maximum errors in URB " 1518c2ecf20Sopenharmony_ci "exceeded; resetting device\n"); 1528c2ecf20Sopenharmony_ci usb_queue_reset_device(i2400mu->usb_iface); 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci dev_err(dev, "TX: cannot send URB; retrying. " 1558c2ecf20Sopenharmony_ci "tx_msg @%zu %zu B [%d sent]: %d\n", 1568c2ecf20Sopenharmony_ci (void *) tx_msg - i2400m->tx_buf, 1578c2ecf20Sopenharmony_ci tx_msg_size, sent_size, result); 1588c2ecf20Sopenharmony_ci goto retry; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci if (do_autopm) 1628c2ecf20Sopenharmony_ci usb_autopm_put_interface(i2400mu->usb_iface); 1638c2ecf20Sopenharmony_ci d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu); 1648c2ecf20Sopenharmony_ci return result; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Get the next TX message in the TX FIFO and send it to the device 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * Note we exit the loop if i2400mu_tx() fails; that function only 1728c2ecf20Sopenharmony_ci * fails on hard error (failing to tx a buffer not being one of them, 1738c2ecf20Sopenharmony_ci * see its doc). 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * Return: 0 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistatic 1788c2ecf20Sopenharmony_ciint i2400mu_txd(void *_i2400mu) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = _i2400mu; 1818c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 1828c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 1838c2ecf20Sopenharmony_ci struct i2400m_msg_hdr *tx_msg; 1848c2ecf20Sopenharmony_ci size_t tx_msg_size; 1858c2ecf20Sopenharmony_ci unsigned long flags; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->tx_lock, flags); 1908c2ecf20Sopenharmony_ci BUG_ON(i2400mu->tx_kthread != NULL); 1918c2ecf20Sopenharmony_ci i2400mu->tx_kthread = current; 1928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->tx_lock, flags); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci while (1) { 1958c2ecf20Sopenharmony_ci d_printf(2, dev, "TX: waiting for messages\n"); 1968c2ecf20Sopenharmony_ci tx_msg = NULL; 1978c2ecf20Sopenharmony_ci wait_event_interruptible( 1988c2ecf20Sopenharmony_ci i2400mu->tx_wq, 1998c2ecf20Sopenharmony_ci (kthread_should_stop() /* check this first! */ 2008c2ecf20Sopenharmony_ci || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) 2018c2ecf20Sopenharmony_ci ); 2028c2ecf20Sopenharmony_ci if (kthread_should_stop()) 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci WARN_ON(tx_msg == NULL); /* should not happen...*/ 2058c2ecf20Sopenharmony_ci d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size); 2068c2ecf20Sopenharmony_ci d_dump(5, dev, tx_msg, tx_msg_size); 2078c2ecf20Sopenharmony_ci /* Yeah, we ignore errors ... not much we can do */ 2088c2ecf20Sopenharmony_ci i2400mu_tx(i2400mu, tx_msg, tx_msg_size); 2098c2ecf20Sopenharmony_ci i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */ 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->tx_lock, flags); 2138c2ecf20Sopenharmony_ci i2400mu->tx_kthread = NULL; 2148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->tx_lock, flags); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci d_fnend(4, dev, "(i2400mu %p)\n", i2400mu); 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * i2400m TX engine notifies us that there is data in the FIFO ready 2238c2ecf20Sopenharmony_ci * for TX 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * If there is a URB in flight, don't do anything; when it finishes, 2268c2ecf20Sopenharmony_ci * it will see there is data in the FIFO and send it. Else, just 2278c2ecf20Sopenharmony_ci * submit a write. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_civoid i2400mu_bus_tx_kick(struct i2400m *i2400m) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); 2328c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); 2358c2ecf20Sopenharmony_ci wake_up_all(&i2400mu->tx_wq); 2368c2ecf20Sopenharmony_ci d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciint i2400mu_tx_setup(struct i2400mu *i2400mu) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci int result = 0; 2438c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 2448c2ecf20Sopenharmony_ci struct device *dev = &i2400mu->usb_iface->dev; 2458c2ecf20Sopenharmony_ci struct wimax_dev *wimax_dev = &i2400m->wimax_dev; 2468c2ecf20Sopenharmony_ci struct task_struct *kthread; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", 2498c2ecf20Sopenharmony_ci wimax_dev->name); 2508c2ecf20Sopenharmony_ci /* the kthread function sets i2400mu->tx_thread */ 2518c2ecf20Sopenharmony_ci if (IS_ERR(kthread)) { 2528c2ecf20Sopenharmony_ci result = PTR_ERR(kthread); 2538c2ecf20Sopenharmony_ci dev_err(dev, "TX: cannot start thread: %d\n", result); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci return result; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid i2400mu_tx_release(struct i2400mu *i2400mu) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned long flags; 2618c2ecf20Sopenharmony_ci struct i2400m *i2400m = &i2400mu->i2400m; 2628c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 2638c2ecf20Sopenharmony_ci struct task_struct *kthread; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->tx_lock, flags); 2668c2ecf20Sopenharmony_ci kthread = i2400mu->tx_kthread; 2678c2ecf20Sopenharmony_ci i2400mu->tx_kthread = NULL; 2688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->tx_lock, flags); 2698c2ecf20Sopenharmony_ci if (kthread) 2708c2ecf20Sopenharmony_ci kthread_stop(kthread); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci d_printf(1, dev, "TX: kthread had already exited\n"); 2738c2ecf20Sopenharmony_ci} 274