18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Intel Wireless WiMAX Connection 2400m 38c2ecf20Sopenharmony_ci * Handle incoming traffic and deliver it to the control or data planes 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 * - Use skb_clone(), break up processing in chunks 408c2ecf20Sopenharmony_ci * - Split transport/device specific 418c2ecf20Sopenharmony_ci * - Make buffer size dynamic to exert less memory pressure 428c2ecf20Sopenharmony_ci * - RX reorder support 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * This handles the RX path. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * We receive an RX message from the bus-specific driver, which 478c2ecf20Sopenharmony_ci * contains one or more payloads that have potentially different 488c2ecf20Sopenharmony_ci * destinataries (data or control paths). 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * So we just take that payload from the transport specific code in 518c2ecf20Sopenharmony_ci * the form of an skb, break it up in chunks (a cloned skb each in the 528c2ecf20Sopenharmony_ci * case of network packets) and pass it to netdev or to the 538c2ecf20Sopenharmony_ci * command/ack handler (and from there to the WiMAX stack). 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * PROTOCOL FORMAT 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * The format of the buffer is: 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * HEADER (struct i2400m_msg_hdr) 608c2ecf20Sopenharmony_ci * PAYLOAD DESCRIPTOR 0 (struct i2400m_pld) 618c2ecf20Sopenharmony_ci * PAYLOAD DESCRIPTOR 1 628c2ecf20Sopenharmony_ci * ... 638c2ecf20Sopenharmony_ci * PAYLOAD DESCRIPTOR N 648c2ecf20Sopenharmony_ci * PAYLOAD 0 (raw bytes) 658c2ecf20Sopenharmony_ci * PAYLOAD 1 668c2ecf20Sopenharmony_ci * ... 678c2ecf20Sopenharmony_ci * PAYLOAD N 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * See tx.c for a deeper description on alignment requirements and 708c2ecf20Sopenharmony_ci * other fun facts of it. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * DATA PACKETS 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * In firmwares <= v1.3, data packets have no header for RX, but they 758c2ecf20Sopenharmony_ci * do for TX (currently unused). 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * In firmware >= 1.4, RX packets have an extended header (16 788c2ecf20Sopenharmony_ci * bytes). This header conveys information for management of host 798c2ecf20Sopenharmony_ci * reordering of packets (the device offloads storage of the packets 808c2ecf20Sopenharmony_ci * for reordering to the host). Read below for more information. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The header is used as dummy space to emulate an ethernet header and 838c2ecf20Sopenharmony_ci * thus be able to act as an ethernet device without having to reallocate. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * DATA RX REORDERING 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Starting in firmware v1.4, the device can deliver packets for 888c2ecf20Sopenharmony_ci * delivery with special reordering information; this allows it to 898c2ecf20Sopenharmony_ci * more effectively do packet management when some frames were lost in 908c2ecf20Sopenharmony_ci * the radio traffic. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Thus, for RX packets that come out of order, the device gives the 938c2ecf20Sopenharmony_ci * driver enough information to queue them properly and then at some 948c2ecf20Sopenharmony_ci * point, the signal to deliver the whole (or part) of the queued 958c2ecf20Sopenharmony_ci * packets to the networking stack. There are 16 such queues. 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * This only happens when a packet comes in with the "need reorder" 988c2ecf20Sopenharmony_ci * flag set in the RX header. When such bit is set, the following 998c2ecf20Sopenharmony_ci * operations might be indicated: 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * - reset queue: send all queued packets to the OS 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * - queue: queue a packet 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * - update ws: update the queue's window start and deliver queued 1068c2ecf20Sopenharmony_ci * packets that meet the criteria 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * - queue & update ws: queue a packet, update the window start and 1098c2ecf20Sopenharmony_ci * deliver queued packets that meet the criteria 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * (delivery criteria: the packet's [normalized] sequence number is 1128c2ecf20Sopenharmony_ci * lower than the new [normalized] window start). 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * See the i2400m_roq_*() functions for details. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * ROADMAP 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * i2400m_rx 1198c2ecf20Sopenharmony_ci * i2400m_rx_msg_hdr_check 1208c2ecf20Sopenharmony_ci * i2400m_rx_pl_descr_check 1218c2ecf20Sopenharmony_ci * i2400m_rx_payload 1228c2ecf20Sopenharmony_ci * i2400m_net_rx 1238c2ecf20Sopenharmony_ci * i2400m_rx_edata 1248c2ecf20Sopenharmony_ci * i2400m_net_erx 1258c2ecf20Sopenharmony_ci * i2400m_roq_reset 1268c2ecf20Sopenharmony_ci * i2400m_net_erx 1278c2ecf20Sopenharmony_ci * i2400m_roq_queue 1288c2ecf20Sopenharmony_ci * __i2400m_roq_queue 1298c2ecf20Sopenharmony_ci * i2400m_roq_update_ws 1308c2ecf20Sopenharmony_ci * __i2400m_roq_update_ws 1318c2ecf20Sopenharmony_ci * i2400m_net_erx 1328c2ecf20Sopenharmony_ci * i2400m_roq_queue_update_ws 1338c2ecf20Sopenharmony_ci * __i2400m_roq_queue 1348c2ecf20Sopenharmony_ci * __i2400m_roq_update_ws 1358c2ecf20Sopenharmony_ci * i2400m_net_erx 1368c2ecf20Sopenharmony_ci * i2400m_rx_ctl 1378c2ecf20Sopenharmony_ci * i2400m_msg_size_check 1388c2ecf20Sopenharmony_ci * i2400m_report_hook_work [in a workqueue] 1398c2ecf20Sopenharmony_ci * i2400m_report_hook 1408c2ecf20Sopenharmony_ci * wimax_msg_to_user 1418c2ecf20Sopenharmony_ci * i2400m_rx_ctl_ack 1428c2ecf20Sopenharmony_ci * wimax_msg_to_user_alloc 1438c2ecf20Sopenharmony_ci * i2400m_rx_trace 1448c2ecf20Sopenharmony_ci * i2400m_msg_size_check 1458c2ecf20Sopenharmony_ci * wimax_msg 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci#include <linux/slab.h> 1488c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1498c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 1508c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 1518c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 1528c2ecf20Sopenharmony_ci#include <linux/export.h> 1538c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 1548c2ecf20Sopenharmony_ci#include "i2400m.h" 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define D_SUBMODULE rx 1588c2ecf20Sopenharmony_ci#include "debug-levels.h" 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int i2400m_rx_reorder_disabled; /* 0 (rx reorder enabled) by default */ 1618c2ecf20Sopenharmony_cimodule_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644); 1628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_reorder_disabled, 1638c2ecf20Sopenharmony_ci "If true, RX reordering will be disabled."); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct i2400m_report_hook_args { 1668c2ecf20Sopenharmony_ci struct sk_buff *skb_rx; 1678c2ecf20Sopenharmony_ci const struct i2400m_l3l4_hdr *l3l4_hdr; 1688c2ecf20Sopenharmony_ci size_t size; 1698c2ecf20Sopenharmony_ci struct list_head list_node; 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * Execute i2400m_report_hook in a workqueue 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * Goes over the list of queued reports in i2400m->rx_reports and 1778c2ecf20Sopenharmony_ci * processes them. 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * NOTE: refcounts on i2400m are not needed because we flush the 1808c2ecf20Sopenharmony_ci * workqueue this runs on (i2400m->work_queue) before destroying 1818c2ecf20Sopenharmony_ci * i2400m. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_civoid i2400m_report_hook_work(struct work_struct *ws) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct i2400m *i2400m = container_of(ws, struct i2400m, rx_report_ws); 1868c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 1878c2ecf20Sopenharmony_ci struct i2400m_report_hook_args *args, *args_next; 1888c2ecf20Sopenharmony_ci LIST_HEAD(list); 1898c2ecf20Sopenharmony_ci unsigned long flags; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci while (1) { 1928c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 1938c2ecf20Sopenharmony_ci list_splice_init(&i2400m->rx_reports, &list); 1948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 1958c2ecf20Sopenharmony_ci if (list_empty(&list)) 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci else 1988c2ecf20Sopenharmony_ci d_printf(1, dev, "processing queued reports\n"); 1998c2ecf20Sopenharmony_ci list_for_each_entry_safe(args, args_next, &list, list_node) { 2008c2ecf20Sopenharmony_ci d_printf(2, dev, "processing queued report %p\n", args); 2018c2ecf20Sopenharmony_ci i2400m_report_hook(i2400m, args->l3l4_hdr, args->size); 2028c2ecf20Sopenharmony_ci kfree_skb(args->skb_rx); 2038c2ecf20Sopenharmony_ci list_del(&args->list_node); 2048c2ecf20Sopenharmony_ci kfree(args); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* 2118c2ecf20Sopenharmony_ci * Flush the list of queued reports 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_cistatic 2148c2ecf20Sopenharmony_civoid i2400m_report_hook_flush(struct i2400m *i2400m) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 2178c2ecf20Sopenharmony_ci struct i2400m_report_hook_args *args, *args_next; 2188c2ecf20Sopenharmony_ci LIST_HEAD(list); 2198c2ecf20Sopenharmony_ci unsigned long flags; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci d_printf(1, dev, "flushing queued reports\n"); 2228c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 2238c2ecf20Sopenharmony_ci list_splice_init(&i2400m->rx_reports, &list); 2248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 2258c2ecf20Sopenharmony_ci list_for_each_entry_safe(args, args_next, &list, list_node) { 2268c2ecf20Sopenharmony_ci d_printf(2, dev, "flushing queued report %p\n", args); 2278c2ecf20Sopenharmony_ci kfree_skb(args->skb_rx); 2288c2ecf20Sopenharmony_ci list_del(&args->list_node); 2298c2ecf20Sopenharmony_ci kfree(args); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* 2358c2ecf20Sopenharmony_ci * Queue a report for later processing 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * @i2400m: device descriptor 2388c2ecf20Sopenharmony_ci * @skb_rx: skb that contains the payload (for reference counting) 2398c2ecf20Sopenharmony_ci * @l3l4_hdr: pointer to the control 2408c2ecf20Sopenharmony_ci * @size: size of the message 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic 2438c2ecf20Sopenharmony_civoid i2400m_report_hook_queue(struct i2400m *i2400m, struct sk_buff *skb_rx, 2448c2ecf20Sopenharmony_ci const void *l3l4_hdr, size_t size) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 2478c2ecf20Sopenharmony_ci unsigned long flags; 2488c2ecf20Sopenharmony_ci struct i2400m_report_hook_args *args; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci args = kzalloc(sizeof(*args), GFP_NOIO); 2518c2ecf20Sopenharmony_ci if (args) { 2528c2ecf20Sopenharmony_ci args->skb_rx = skb_get(skb_rx); 2538c2ecf20Sopenharmony_ci args->l3l4_hdr = l3l4_hdr; 2548c2ecf20Sopenharmony_ci args->size = size; 2558c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 2568c2ecf20Sopenharmony_ci list_add_tail(&args->list_node, &i2400m->rx_reports); 2578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 2588c2ecf20Sopenharmony_ci d_printf(2, dev, "queued report %p\n", args); 2598c2ecf20Sopenharmony_ci rmb(); /* see i2400m->ready's documentation */ 2608c2ecf20Sopenharmony_ci if (likely(i2400m->ready)) /* only send if up */ 2618c2ecf20Sopenharmony_ci queue_work(i2400m->work_queue, &i2400m->rx_report_ws); 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci if (printk_ratelimit()) 2648c2ecf20Sopenharmony_ci dev_err(dev, "%s:%u: Can't allocate %zu B\n", 2658c2ecf20Sopenharmony_ci __func__, __LINE__, sizeof(*args)); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * Process an ack to a command 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * @i2400m: device descriptor 2748c2ecf20Sopenharmony_ci * @payload: pointer to message 2758c2ecf20Sopenharmony_ci * @size: size of the message 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * Pass the acknodledgment (in an skb) to the thread that is waiting 2788c2ecf20Sopenharmony_ci * for it in i2400m->msg_completion. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * We need to coordinate properly with the thread waiting for the 2818c2ecf20Sopenharmony_ci * ack. Check if it is waiting or if it is gone. We loose the spinlock 2828c2ecf20Sopenharmony_ci * to avoid allocating on atomic contexts (yeah, could use GFP_ATOMIC, 2838c2ecf20Sopenharmony_ci * but this is not so speed critical). 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic 2868c2ecf20Sopenharmony_civoid i2400m_rx_ctl_ack(struct i2400m *i2400m, 2878c2ecf20Sopenharmony_ci const void *payload, size_t size) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 2908c2ecf20Sopenharmony_ci struct wimax_dev *wimax_dev = &i2400m->wimax_dev; 2918c2ecf20Sopenharmony_ci unsigned long flags; 2928c2ecf20Sopenharmony_ci struct sk_buff *ack_skb; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Anyone waiting for an answer? */ 2958c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 2968c2ecf20Sopenharmony_ci if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { 2978c2ecf20Sopenharmony_ci dev_err(dev, "Huh? reply to command with no waiters\n"); 2988c2ecf20Sopenharmony_ci goto error_no_waiter; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ack_skb = wimax_msg_alloc(wimax_dev, NULL, payload, size, GFP_KERNEL); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Check waiter didn't time out waiting for the answer... */ 3058c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 3068c2ecf20Sopenharmony_ci if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { 3078c2ecf20Sopenharmony_ci d_printf(1, dev, "Huh? waiter for command reply cancelled\n"); 3088c2ecf20Sopenharmony_ci goto error_waiter_cancelled; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci if (IS_ERR(ack_skb)) 3118c2ecf20Sopenharmony_ci dev_err(dev, "CMD/GET/SET ack: cannot allocate SKB\n"); 3128c2ecf20Sopenharmony_ci i2400m->ack_skb = ack_skb; 3138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 3148c2ecf20Sopenharmony_ci complete(&i2400m->msg_completion); 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cierror_waiter_cancelled: 3188c2ecf20Sopenharmony_ci if (!IS_ERR(ack_skb)) 3198c2ecf20Sopenharmony_ci kfree_skb(ack_skb); 3208c2ecf20Sopenharmony_cierror_no_waiter: 3218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Receive and process a control payload 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * @i2400m: device descriptor 3298c2ecf20Sopenharmony_ci * @skb_rx: skb that contains the payload (for reference counting) 3308c2ecf20Sopenharmony_ci * @payload: pointer to message 3318c2ecf20Sopenharmony_ci * @size: size of the message 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * There are two types of control RX messages: reports (asynchronous, 3348c2ecf20Sopenharmony_ci * like your every day interrupts) and 'acks' (reponses to a command, 3358c2ecf20Sopenharmony_ci * get or set request). 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * If it is a report, we run hooks on it (to extract information for 3388c2ecf20Sopenharmony_ci * things we need to do in the driver) and then pass it over to the 3398c2ecf20Sopenharmony_ci * WiMAX stack to send it to user space. 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * NOTE: report processing is done in a workqueue specific to the 3428c2ecf20Sopenharmony_ci * generic driver, to avoid deadlocks in the system. 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * If it is not a report, it is an ack to a previously executed 3458c2ecf20Sopenharmony_ci * command, set or get, so wake up whoever is waiting for it from 3468c2ecf20Sopenharmony_ci * i2400m_msg_to_dev(). i2400m_rx_ctl_ack() takes care of that. 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Note that the sizes we pass to other functions from here are the 3498c2ecf20Sopenharmony_ci * sizes of the _l3l4_hdr + payload, not full buffer sizes, as we have 3508c2ecf20Sopenharmony_ci * verified in _msg_size_check() that they are congruent. 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * For reports: We can't clone the original skb where the data is 3538c2ecf20Sopenharmony_ci * because we need to send this up via netlink; netlink has to add 3548c2ecf20Sopenharmony_ci * headers and we can't overwrite what's preceding the payload...as 3558c2ecf20Sopenharmony_ci * it is another message. So we just dup them. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic 3588c2ecf20Sopenharmony_civoid i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, 3598c2ecf20Sopenharmony_ci const void *payload, size_t size) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int result; 3628c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 3638c2ecf20Sopenharmony_ci const struct i2400m_l3l4_hdr *l3l4_hdr = payload; 3648c2ecf20Sopenharmony_ci unsigned msg_type; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); 3678c2ecf20Sopenharmony_ci if (result < 0) { 3688c2ecf20Sopenharmony_ci dev_err(dev, "HW BUG? device sent a bad message: %d\n", 3698c2ecf20Sopenharmony_ci result); 3708c2ecf20Sopenharmony_ci goto error_check; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci msg_type = le16_to_cpu(l3l4_hdr->type); 3738c2ecf20Sopenharmony_ci d_printf(1, dev, "%s 0x%04x: %zu bytes\n", 3748c2ecf20Sopenharmony_ci msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", 3758c2ecf20Sopenharmony_ci msg_type, size); 3768c2ecf20Sopenharmony_ci d_dump(2, dev, l3l4_hdr, size); 3778c2ecf20Sopenharmony_ci if (msg_type & I2400M_MT_REPORT_MASK) { 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Process each report 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * - has to be ran serialized as well 3828c2ecf20Sopenharmony_ci * 3838c2ecf20Sopenharmony_ci * - the handling might force the execution of 3848c2ecf20Sopenharmony_ci * commands. That might cause reentrancy issues with 3858c2ecf20Sopenharmony_ci * bus-specific subdrivers and workqueues, so the we 3868c2ecf20Sopenharmony_ci * run it in a separate workqueue. 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * - when the driver is not yet ready to handle them, 3898c2ecf20Sopenharmony_ci * they are queued and at some point the queue is 3908c2ecf20Sopenharmony_ci * restarted [NOTE: we can't queue SKBs directly, as 3918c2ecf20Sopenharmony_ci * this might be a piece of a SKB, not the whole 3928c2ecf20Sopenharmony_ci * thing, and this is cheaper than cloning the 3938c2ecf20Sopenharmony_ci * SKB]. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Note we don't do refcounting for the device 3968c2ecf20Sopenharmony_ci * structure; this is because before destroying 3978c2ecf20Sopenharmony_ci * 'i2400m', we make sure to flush the 3988c2ecf20Sopenharmony_ci * i2400m->work_queue, so there are no issues. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci i2400m_report_hook_queue(i2400m, skb_rx, l3l4_hdr, size); 4018c2ecf20Sopenharmony_ci if (unlikely(i2400m->trace_msg_from_user)) 4028c2ecf20Sopenharmony_ci wimax_msg(&i2400m->wimax_dev, "echo", 4038c2ecf20Sopenharmony_ci l3l4_hdr, size, GFP_KERNEL); 4048c2ecf20Sopenharmony_ci result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size, 4058c2ecf20Sopenharmony_ci GFP_KERNEL); 4068c2ecf20Sopenharmony_ci if (result < 0) 4078c2ecf20Sopenharmony_ci dev_err(dev, "error sending report to userspace: %d\n", 4088c2ecf20Sopenharmony_ci result); 4098c2ecf20Sopenharmony_ci } else /* an ack to a CMD, GET or SET */ 4108c2ecf20Sopenharmony_ci i2400m_rx_ctl_ack(i2400m, payload, size); 4118c2ecf20Sopenharmony_cierror_check: 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * Receive and send up a trace 4188c2ecf20Sopenharmony_ci * 4198c2ecf20Sopenharmony_ci * @i2400m: device descriptor 4208c2ecf20Sopenharmony_ci * @skb_rx: skb that contains the trace (for reference counting) 4218c2ecf20Sopenharmony_ci * @payload: pointer to trace message inside the skb 4228c2ecf20Sopenharmony_ci * @size: size of the message 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * THe i2400m might produce trace information (diagnostics) and we 4258c2ecf20Sopenharmony_ci * send them through a different kernel-to-user pipe (to avoid 4268c2ecf20Sopenharmony_ci * clogging it). 4278c2ecf20Sopenharmony_ci * 4288c2ecf20Sopenharmony_ci * As in i2400m_rx_ctl(), we can't clone the original skb where the 4298c2ecf20Sopenharmony_ci * data is because we need to send this up via netlink; netlink has to 4308c2ecf20Sopenharmony_ci * add headers and we can't overwrite what's preceding the 4318c2ecf20Sopenharmony_ci * payload...as it is another message. So we just dup them. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_cistatic 4348c2ecf20Sopenharmony_civoid i2400m_rx_trace(struct i2400m *i2400m, 4358c2ecf20Sopenharmony_ci const void *payload, size_t size) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int result; 4388c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 4398c2ecf20Sopenharmony_ci struct wimax_dev *wimax_dev = &i2400m->wimax_dev; 4408c2ecf20Sopenharmony_ci const struct i2400m_l3l4_hdr *l3l4_hdr = payload; 4418c2ecf20Sopenharmony_ci unsigned msg_type; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); 4448c2ecf20Sopenharmony_ci if (result < 0) { 4458c2ecf20Sopenharmony_ci dev_err(dev, "HW BUG? device sent a bad trace message: %d\n", 4468c2ecf20Sopenharmony_ci result); 4478c2ecf20Sopenharmony_ci goto error_check; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci msg_type = le16_to_cpu(l3l4_hdr->type); 4508c2ecf20Sopenharmony_ci d_printf(1, dev, "Trace %s 0x%04x: %zu bytes\n", 4518c2ecf20Sopenharmony_ci msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", 4528c2ecf20Sopenharmony_ci msg_type, size); 4538c2ecf20Sopenharmony_ci d_dump(2, dev, l3l4_hdr, size); 4548c2ecf20Sopenharmony_ci result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); 4558c2ecf20Sopenharmony_ci if (result < 0) 4568c2ecf20Sopenharmony_ci dev_err(dev, "error sending trace to userspace: %d\n", 4578c2ecf20Sopenharmony_ci result); 4588c2ecf20Sopenharmony_cierror_check: 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* 4648c2ecf20Sopenharmony_ci * Reorder queue data stored on skb->cb while the skb is queued in the 4658c2ecf20Sopenharmony_ci * reorder queues. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_cistruct i2400m_roq_data { 4688c2ecf20Sopenharmony_ci unsigned sn; /* Serial number for the skb */ 4698c2ecf20Sopenharmony_ci enum i2400m_cs cs; /* packet type for the skb */ 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* 4748c2ecf20Sopenharmony_ci * ReOrder Queue 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * @ws: Window Start; sequence number where the current window start 4778c2ecf20Sopenharmony_ci * is for this queue 4788c2ecf20Sopenharmony_ci * @queue: the skb queue itself 4798c2ecf20Sopenharmony_ci * @log: circular ring buffer used to log information about the 4808c2ecf20Sopenharmony_ci * reorder process in this queue that can be displayed in case of 4818c2ecf20Sopenharmony_ci * error to help diagnose it. 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * This is the head for a list of skbs. In the skb->cb member of the 4848c2ecf20Sopenharmony_ci * skb when queued here contains a 'struct i2400m_roq_data' were we 4858c2ecf20Sopenharmony_ci * store the sequence number (sn) and the cs (packet type) coming from 4868c2ecf20Sopenharmony_ci * the RX payload header from the device. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_cistruct i2400m_roq 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci unsigned ws; 4918c2ecf20Sopenharmony_ci struct sk_buff_head queue; 4928c2ecf20Sopenharmony_ci struct i2400m_roq_log *log; 4938c2ecf20Sopenharmony_ci}; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic 4978c2ecf20Sopenharmony_civoid __i2400m_roq_init(struct i2400m_roq *roq) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci roq->ws = 0; 5008c2ecf20Sopenharmony_ci skb_queue_head_init(&roq->queue); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic 5058c2ecf20Sopenharmony_ciunsigned __i2400m_roq_index(struct i2400m *i2400m, struct i2400m_roq *roq) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci return ((unsigned long) roq - (unsigned long) i2400m->rx_roq) 5088c2ecf20Sopenharmony_ci / sizeof(*roq); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* 5138c2ecf20Sopenharmony_ci * Normalize a sequence number based on the queue's window start 5148c2ecf20Sopenharmony_ci * 5158c2ecf20Sopenharmony_ci * nsn = (sn - ws) % 2048 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * Note that if @sn < @roq->ws, we still need a positive number; %'s 5188c2ecf20Sopenharmony_ci * sign is implementation specific, so we normalize it by adding 2048 5198c2ecf20Sopenharmony_ci * to bring it to be positive. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_cistatic 5228c2ecf20Sopenharmony_ciunsigned __i2400m_roq_nsn(struct i2400m_roq *roq, unsigned sn) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int r; 5258c2ecf20Sopenharmony_ci r = ((int) sn - (int) roq->ws) % 2048; 5268c2ecf20Sopenharmony_ci if (r < 0) 5278c2ecf20Sopenharmony_ci r += 2048; 5288c2ecf20Sopenharmony_ci return r; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/* 5338c2ecf20Sopenharmony_ci * Circular buffer to keep the last N reorder operations 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * In case something fails, dumb then to try to come up with what 5368c2ecf20Sopenharmony_ci * happened. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cienum { 5398c2ecf20Sopenharmony_ci I2400M_ROQ_LOG_LENGTH = 32, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistruct i2400m_roq_log { 5438c2ecf20Sopenharmony_ci struct i2400m_roq_log_entry { 5448c2ecf20Sopenharmony_ci enum i2400m_ro_type type; 5458c2ecf20Sopenharmony_ci unsigned ws, count, sn, nsn, new_ws; 5468c2ecf20Sopenharmony_ci } entry[I2400M_ROQ_LOG_LENGTH]; 5478c2ecf20Sopenharmony_ci unsigned in, out; 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/* Print a log entry */ 5528c2ecf20Sopenharmony_cistatic 5538c2ecf20Sopenharmony_civoid i2400m_roq_log_entry_print(struct i2400m *i2400m, unsigned index, 5548c2ecf20Sopenharmony_ci unsigned e_index, 5558c2ecf20Sopenharmony_ci struct i2400m_roq_log_entry *e) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci switch(e->type) { 5608c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_RESET: 5618c2ecf20Sopenharmony_ci dev_err(dev, "q#%d reset ws %u cnt %u sn %u/%u" 5628c2ecf20Sopenharmony_ci " - new nws %u\n", 5638c2ecf20Sopenharmony_ci index, e->ws, e->count, e->sn, e->nsn, e->new_ws); 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_PACKET: 5668c2ecf20Sopenharmony_ci dev_err(dev, "q#%d queue ws %u cnt %u sn %u/%u\n", 5678c2ecf20Sopenharmony_ci index, e->ws, e->count, e->sn, e->nsn); 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_WS: 5708c2ecf20Sopenharmony_ci dev_err(dev, "q#%d update_ws ws %u cnt %u sn %u/%u" 5718c2ecf20Sopenharmony_ci " - new nws %u\n", 5728c2ecf20Sopenharmony_ci index, e->ws, e->count, e->sn, e->nsn, e->new_ws); 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_PACKET_WS: 5758c2ecf20Sopenharmony_ci dev_err(dev, "q#%d queue_update_ws ws %u cnt %u sn %u/%u" 5768c2ecf20Sopenharmony_ci " - new nws %u\n", 5778c2ecf20Sopenharmony_ci index, e->ws, e->count, e->sn, e->nsn, e->new_ws); 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci dev_err(dev, "q#%d BUG? entry %u - unknown type %u\n", 5818c2ecf20Sopenharmony_ci index, e_index, e->type); 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic 5888c2ecf20Sopenharmony_civoid i2400m_roq_log_add(struct i2400m *i2400m, 5898c2ecf20Sopenharmony_ci struct i2400m_roq *roq, enum i2400m_ro_type type, 5908c2ecf20Sopenharmony_ci unsigned ws, unsigned count, unsigned sn, 5918c2ecf20Sopenharmony_ci unsigned nsn, unsigned new_ws) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct i2400m_roq_log_entry *e; 5948c2ecf20Sopenharmony_ci unsigned cnt_idx; 5958c2ecf20Sopenharmony_ci int index = __i2400m_roq_index(i2400m, roq); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* if we run out of space, we eat from the end */ 5988c2ecf20Sopenharmony_ci if (roq->log->in - roq->log->out == I2400M_ROQ_LOG_LENGTH) 5998c2ecf20Sopenharmony_ci roq->log->out++; 6008c2ecf20Sopenharmony_ci cnt_idx = roq->log->in++ % I2400M_ROQ_LOG_LENGTH; 6018c2ecf20Sopenharmony_ci e = &roq->log->entry[cnt_idx]; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci e->type = type; 6048c2ecf20Sopenharmony_ci e->ws = ws; 6058c2ecf20Sopenharmony_ci e->count = count; 6068c2ecf20Sopenharmony_ci e->sn = sn; 6078c2ecf20Sopenharmony_ci e->nsn = nsn; 6088c2ecf20Sopenharmony_ci e->new_ws = new_ws; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (d_test(1)) 6118c2ecf20Sopenharmony_ci i2400m_roq_log_entry_print(i2400m, index, cnt_idx, e); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/* Dump all the entries in the FIFO and reinitialize it */ 6168c2ecf20Sopenharmony_cistatic 6178c2ecf20Sopenharmony_civoid i2400m_roq_log_dump(struct i2400m *i2400m, struct i2400m_roq *roq) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci unsigned cnt, cnt_idx; 6208c2ecf20Sopenharmony_ci struct i2400m_roq_log_entry *e; 6218c2ecf20Sopenharmony_ci int index = __i2400m_roq_index(i2400m, roq); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci BUG_ON(roq->log->out > roq->log->in); 6248c2ecf20Sopenharmony_ci for (cnt = roq->log->out; cnt < roq->log->in; cnt++) { 6258c2ecf20Sopenharmony_ci cnt_idx = cnt % I2400M_ROQ_LOG_LENGTH; 6268c2ecf20Sopenharmony_ci e = &roq->log->entry[cnt_idx]; 6278c2ecf20Sopenharmony_ci i2400m_roq_log_entry_print(i2400m, index, cnt_idx, e); 6288c2ecf20Sopenharmony_ci memset(e, 0, sizeof(*e)); 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci roq->log->in = roq->log->out = 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/* 6358c2ecf20Sopenharmony_ci * Backbone for the queuing of an skb (by normalized sequence number) 6368c2ecf20Sopenharmony_ci * 6378c2ecf20Sopenharmony_ci * @i2400m: device descriptor 6388c2ecf20Sopenharmony_ci * @roq: reorder queue where to add 6398c2ecf20Sopenharmony_ci * @skb: the skb to add 6408c2ecf20Sopenharmony_ci * @sn: the sequence number of the skb 6418c2ecf20Sopenharmony_ci * @nsn: the normalized sequence number of the skb (pre-computed by the 6428c2ecf20Sopenharmony_ci * caller from the @sn and @roq->ws). 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * We try first a couple of quick cases: 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * - the queue is empty 6478c2ecf20Sopenharmony_ci * - the skb would be appended to the queue 6488c2ecf20Sopenharmony_ci * 6498c2ecf20Sopenharmony_ci * These will be the most common operations. 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci * If these fail, then we have to do a sorted insertion in the queue, 6528c2ecf20Sopenharmony_ci * which is the slowest path. 6538c2ecf20Sopenharmony_ci * 6548c2ecf20Sopenharmony_ci * We don't have to acquire a reference count as we are going to own it. 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_cistatic 6578c2ecf20Sopenharmony_civoid __i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, 6588c2ecf20Sopenharmony_ci struct sk_buff *skb, unsigned sn, unsigned nsn) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 6618c2ecf20Sopenharmony_ci struct sk_buff *skb_itr; 6628c2ecf20Sopenharmony_ci struct i2400m_roq_data *roq_data_itr, *roq_data; 6638c2ecf20Sopenharmony_ci unsigned nsn_itr; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci d_fnstart(4, dev, "(i2400m %p roq %p skb %p sn %u nsn %u)\n", 6668c2ecf20Sopenharmony_ci i2400m, roq, skb, sn, nsn); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci roq_data = (struct i2400m_roq_data *) &skb->cb; 6698c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*roq_data) > sizeof(skb->cb)); 6708c2ecf20Sopenharmony_ci roq_data->sn = sn; 6718c2ecf20Sopenharmony_ci d_printf(3, dev, "ERX: roq %p [ws %u] nsn %d sn %u\n", 6728c2ecf20Sopenharmony_ci roq, roq->ws, nsn, roq_data->sn); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Queues will be empty on not-so-bad environments, so try 6758c2ecf20Sopenharmony_ci * that first */ 6768c2ecf20Sopenharmony_ci if (skb_queue_empty(&roq->queue)) { 6778c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: roq %p - first one\n", roq); 6788c2ecf20Sopenharmony_ci __skb_queue_head(&roq->queue, skb); 6798c2ecf20Sopenharmony_ci goto out; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci /* Now try append, as most of the operations will be that */ 6828c2ecf20Sopenharmony_ci skb_itr = skb_peek_tail(&roq->queue); 6838c2ecf20Sopenharmony_ci roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; 6848c2ecf20Sopenharmony_ci nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); 6858c2ecf20Sopenharmony_ci /* NSN bounds assumed correct (checked when it was queued) */ 6868c2ecf20Sopenharmony_ci if (nsn >= nsn_itr) { 6878c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: roq %p - appended after %p (nsn %d sn %u)\n", 6888c2ecf20Sopenharmony_ci roq, skb_itr, nsn_itr, roq_data_itr->sn); 6898c2ecf20Sopenharmony_ci __skb_queue_tail(&roq->queue, skb); 6908c2ecf20Sopenharmony_ci goto out; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci /* None of the fast paths option worked. Iterate to find the 6938c2ecf20Sopenharmony_ci * right spot where to insert the packet; we know the queue is 6948c2ecf20Sopenharmony_ci * not empty, so we are not the first ones; we also know we 6958c2ecf20Sopenharmony_ci * are not going to be the last ones. The list is sorted, so 6968c2ecf20Sopenharmony_ci * we have to insert before the the first guy with an nsn_itr 6978c2ecf20Sopenharmony_ci * greater that our nsn. */ 6988c2ecf20Sopenharmony_ci skb_queue_walk(&roq->queue, skb_itr) { 6998c2ecf20Sopenharmony_ci roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; 7008c2ecf20Sopenharmony_ci nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); 7018c2ecf20Sopenharmony_ci /* NSN bounds assumed correct (checked when it was queued) */ 7028c2ecf20Sopenharmony_ci if (nsn_itr > nsn) { 7038c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: roq %p - queued before %p " 7048c2ecf20Sopenharmony_ci "(nsn %d sn %u)\n", roq, skb_itr, nsn_itr, 7058c2ecf20Sopenharmony_ci roq_data_itr->sn); 7068c2ecf20Sopenharmony_ci __skb_queue_before(&roq->queue, skb_itr, skb); 7078c2ecf20Sopenharmony_ci goto out; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci /* If we get here, that is VERY bad -- print info to help 7118c2ecf20Sopenharmony_ci * diagnose and crash it */ 7128c2ecf20Sopenharmony_ci dev_err(dev, "SW BUG? failed to insert packet\n"); 7138c2ecf20Sopenharmony_ci dev_err(dev, "ERX: roq %p [ws %u] skb %p nsn %d sn %u\n", 7148c2ecf20Sopenharmony_ci roq, roq->ws, skb, nsn, roq_data->sn); 7158c2ecf20Sopenharmony_ci skb_queue_walk(&roq->queue, skb_itr) { 7168c2ecf20Sopenharmony_ci roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; 7178c2ecf20Sopenharmony_ci nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); 7188c2ecf20Sopenharmony_ci /* NSN bounds assumed correct (checked when it was queued) */ 7198c2ecf20Sopenharmony_ci dev_err(dev, "ERX: roq %p skb_itr %p nsn %d sn %u\n", 7208c2ecf20Sopenharmony_ci roq, skb_itr, nsn_itr, roq_data_itr->sn); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci BUG(); 7238c2ecf20Sopenharmony_ciout: 7248c2ecf20Sopenharmony_ci d_fnend(4, dev, "(i2400m %p roq %p skb %p sn %u nsn %d) = void\n", 7258c2ecf20Sopenharmony_ci i2400m, roq, skb, sn, nsn); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/* 7308c2ecf20Sopenharmony_ci * Backbone for the update window start operation 7318c2ecf20Sopenharmony_ci * 7328c2ecf20Sopenharmony_ci * @i2400m: device descriptor 7338c2ecf20Sopenharmony_ci * @roq: Reorder queue 7348c2ecf20Sopenharmony_ci * @sn: New sequence number 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * Updates the window start of a queue; when doing so, it must deliver 7378c2ecf20Sopenharmony_ci * to the networking stack all the queued skb's whose normalized 7388c2ecf20Sopenharmony_ci * sequence number is lower than the new normalized window start. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_cistatic 7418c2ecf20Sopenharmony_ciunsigned __i2400m_roq_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, 7428c2ecf20Sopenharmony_ci unsigned sn) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 7458c2ecf20Sopenharmony_ci struct sk_buff *skb_itr, *tmp_itr; 7468c2ecf20Sopenharmony_ci struct i2400m_roq_data *roq_data_itr; 7478c2ecf20Sopenharmony_ci unsigned new_nws, nsn_itr; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci new_nws = __i2400m_roq_nsn(roq, sn); 7508c2ecf20Sopenharmony_ci /* 7518c2ecf20Sopenharmony_ci * For type 2(update_window_start) rx messages, there is no 7528c2ecf20Sopenharmony_ci * need to check if the normalized sequence number is greater 1023. 7538c2ecf20Sopenharmony_ci * Simply insert and deliver all packets to the host up to the 7548c2ecf20Sopenharmony_ci * window start. 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_ci skb_queue_walk_safe(&roq->queue, skb_itr, tmp_itr) { 7578c2ecf20Sopenharmony_ci roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; 7588c2ecf20Sopenharmony_ci nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); 7598c2ecf20Sopenharmony_ci /* NSN bounds assumed correct (checked when it was queued) */ 7608c2ecf20Sopenharmony_ci if (nsn_itr < new_nws) { 7618c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: roq %p - release skb %p " 7628c2ecf20Sopenharmony_ci "(nsn %u/%u new nws %u)\n", 7638c2ecf20Sopenharmony_ci roq, skb_itr, nsn_itr, roq_data_itr->sn, 7648c2ecf20Sopenharmony_ci new_nws); 7658c2ecf20Sopenharmony_ci __skb_unlink(skb_itr, &roq->queue); 7668c2ecf20Sopenharmony_ci i2400m_net_erx(i2400m, skb_itr, roq_data_itr->cs); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci break; /* rest of packets all nsn_itr > nws */ 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci roq->ws = sn; 7728c2ecf20Sopenharmony_ci return new_nws; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/* 7778c2ecf20Sopenharmony_ci * Reset a queue 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * @i2400m: device descriptor 7808c2ecf20Sopenharmony_ci * @cin: Queue Index 7818c2ecf20Sopenharmony_ci * 7828c2ecf20Sopenharmony_ci * Deliver all the packets and reset the window-start to zero. Name is 7838c2ecf20Sopenharmony_ci * kind of misleading. 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_cistatic 7868c2ecf20Sopenharmony_civoid i2400m_roq_reset(struct i2400m *i2400m, struct i2400m_roq *roq) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 7898c2ecf20Sopenharmony_ci struct sk_buff *skb_itr, *tmp_itr; 7908c2ecf20Sopenharmony_ci struct i2400m_roq_data *roq_data_itr; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p roq %p)\n", i2400m, roq); 7938c2ecf20Sopenharmony_ci i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_RESET, 7948c2ecf20Sopenharmony_ci roq->ws, skb_queue_len(&roq->queue), 7958c2ecf20Sopenharmony_ci ~0, ~0, 0); 7968c2ecf20Sopenharmony_ci skb_queue_walk_safe(&roq->queue, skb_itr, tmp_itr) { 7978c2ecf20Sopenharmony_ci roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; 7988c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: roq %p - release skb %p (sn %u)\n", 7998c2ecf20Sopenharmony_ci roq, skb_itr, roq_data_itr->sn); 8008c2ecf20Sopenharmony_ci __skb_unlink(skb_itr, &roq->queue); 8018c2ecf20Sopenharmony_ci i2400m_net_erx(i2400m, skb_itr, roq_data_itr->cs); 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci roq->ws = 0; 8048c2ecf20Sopenharmony_ci d_fnend(2, dev, "(i2400m %p roq %p) = void\n", i2400m, roq); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* 8098c2ecf20Sopenharmony_ci * Queue a packet 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * @i2400m: device descriptor 8128c2ecf20Sopenharmony_ci * @cin: Queue Index 8138c2ecf20Sopenharmony_ci * @skb: containing the packet data 8148c2ecf20Sopenharmony_ci * @fbn: First block number of the packet in @skb 8158c2ecf20Sopenharmony_ci * @lbn: Last block number of the packet in @skb 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * The hardware is asking the driver to queue a packet for later 8188c2ecf20Sopenharmony_ci * delivery to the networking stack. 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_cistatic 8218c2ecf20Sopenharmony_civoid i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, 8228c2ecf20Sopenharmony_ci struct sk_buff * skb, unsigned lbn) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 8258c2ecf20Sopenharmony_ci unsigned nsn, len; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p roq %p skb %p lbn %u) = void\n", 8288c2ecf20Sopenharmony_ci i2400m, roq, skb, lbn); 8298c2ecf20Sopenharmony_ci len = skb_queue_len(&roq->queue); 8308c2ecf20Sopenharmony_ci nsn = __i2400m_roq_nsn(roq, lbn); 8318c2ecf20Sopenharmony_ci if (unlikely(nsn >= 1024)) { 8328c2ecf20Sopenharmony_ci dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n", 8338c2ecf20Sopenharmony_ci nsn, lbn, roq->ws); 8348c2ecf20Sopenharmony_ci i2400m_roq_log_dump(i2400m, roq); 8358c2ecf20Sopenharmony_ci i2400m_reset(i2400m, I2400M_RT_WARM); 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci __i2400m_roq_queue(i2400m, roq, skb, lbn, nsn); 8388c2ecf20Sopenharmony_ci i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET, 8398c2ecf20Sopenharmony_ci roq->ws, len, lbn, nsn, ~0); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci d_fnend(2, dev, "(i2400m %p roq %p skb %p lbn %u) = void\n", 8428c2ecf20Sopenharmony_ci i2400m, roq, skb, lbn); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* 8478c2ecf20Sopenharmony_ci * Update the window start in a reorder queue and deliver all skbs 8488c2ecf20Sopenharmony_ci * with a lower window start 8498c2ecf20Sopenharmony_ci * 8508c2ecf20Sopenharmony_ci * @i2400m: device descriptor 8518c2ecf20Sopenharmony_ci * @roq: Reorder queue 8528c2ecf20Sopenharmony_ci * @sn: New sequence number 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic 8558c2ecf20Sopenharmony_civoid i2400m_roq_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, 8568c2ecf20Sopenharmony_ci unsigned sn) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 8598c2ecf20Sopenharmony_ci unsigned old_ws, nsn, len; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p roq %p sn %u)\n", i2400m, roq, sn); 8628c2ecf20Sopenharmony_ci old_ws = roq->ws; 8638c2ecf20Sopenharmony_ci len = skb_queue_len(&roq->queue); 8648c2ecf20Sopenharmony_ci nsn = __i2400m_roq_update_ws(i2400m, roq, sn); 8658c2ecf20Sopenharmony_ci i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_WS, 8668c2ecf20Sopenharmony_ci old_ws, len, sn, nsn, roq->ws); 8678c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p roq %p sn %u) = void\n", i2400m, roq, sn); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci/* 8728c2ecf20Sopenharmony_ci * Queue a packet and update the window start 8738c2ecf20Sopenharmony_ci * 8748c2ecf20Sopenharmony_ci * @i2400m: device descriptor 8758c2ecf20Sopenharmony_ci * @cin: Queue Index 8768c2ecf20Sopenharmony_ci * @skb: containing the packet data 8778c2ecf20Sopenharmony_ci * @fbn: First block number of the packet in @skb 8788c2ecf20Sopenharmony_ci * @sn: Last block number of the packet in @skb 8798c2ecf20Sopenharmony_ci * 8808c2ecf20Sopenharmony_ci * Note that unlike i2400m_roq_update_ws(), which sets the new window 8818c2ecf20Sopenharmony_ci * start to @sn, in here we'll set it to @sn + 1. 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_cistatic 8848c2ecf20Sopenharmony_civoid i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, 8858c2ecf20Sopenharmony_ci struct sk_buff * skb, unsigned sn) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 8888c2ecf20Sopenharmony_ci unsigned nsn, old_ws, len; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p roq %p skb %p sn %u)\n", 8918c2ecf20Sopenharmony_ci i2400m, roq, skb, sn); 8928c2ecf20Sopenharmony_ci len = skb_queue_len(&roq->queue); 8938c2ecf20Sopenharmony_ci nsn = __i2400m_roq_nsn(roq, sn); 8948c2ecf20Sopenharmony_ci /* 8958c2ecf20Sopenharmony_ci * For type 3(queue_update_window_start) rx messages, there is no 8968c2ecf20Sopenharmony_ci * need to check if the normalized sequence number is greater 1023. 8978c2ecf20Sopenharmony_ci * Simply insert and deliver all packets to the host up to the 8988c2ecf20Sopenharmony_ci * window start. 8998c2ecf20Sopenharmony_ci */ 9008c2ecf20Sopenharmony_ci old_ws = roq->ws; 9018c2ecf20Sopenharmony_ci /* If the queue is empty, don't bother as we'd queue 9028c2ecf20Sopenharmony_ci * it and immediately unqueue it -- just deliver it. 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_ci if (len == 0) { 9058c2ecf20Sopenharmony_ci struct i2400m_roq_data *roq_data; 9068c2ecf20Sopenharmony_ci roq_data = (struct i2400m_roq_data *) &skb->cb; 9078c2ecf20Sopenharmony_ci i2400m_net_erx(i2400m, skb, roq_data->cs); 9088c2ecf20Sopenharmony_ci } else 9098c2ecf20Sopenharmony_ci __i2400m_roq_queue(i2400m, roq, skb, sn, nsn); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci __i2400m_roq_update_ws(i2400m, roq, sn + 1); 9128c2ecf20Sopenharmony_ci i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET_WS, 9138c2ecf20Sopenharmony_ci old_ws, len, sn, nsn, roq->ws); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci d_fnend(2, dev, "(i2400m %p roq %p skb %p sn %u) = void\n", 9168c2ecf20Sopenharmony_ci i2400m, roq, skb, sn); 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/* 9218c2ecf20Sopenharmony_ci * This routine destroys the memory allocated for rx_roq, when no 9228c2ecf20Sopenharmony_ci * other thread is accessing it. Access to rx_roq is refcounted by 9238c2ecf20Sopenharmony_ci * rx_roq_refcount, hence memory allocated must be destroyed when 9248c2ecf20Sopenharmony_ci * rx_roq_refcount becomes zero. This routine gets executed when 9258c2ecf20Sopenharmony_ci * rx_roq_refcount becomes zero. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_cistatic void i2400m_rx_roq_destroy(struct kref *ref) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci unsigned itr; 9308c2ecf20Sopenharmony_ci struct i2400m *i2400m 9318c2ecf20Sopenharmony_ci = container_of(ref, struct i2400m, rx_roq_refcount); 9328c2ecf20Sopenharmony_ci for (itr = 0; itr < I2400M_RO_CIN + 1; itr++) 9338c2ecf20Sopenharmony_ci __skb_queue_purge(&i2400m->rx_roq[itr].queue); 9348c2ecf20Sopenharmony_ci kfree(i2400m->rx_roq[0].log); 9358c2ecf20Sopenharmony_ci kfree(i2400m->rx_roq); 9368c2ecf20Sopenharmony_ci i2400m->rx_roq = NULL; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * Receive and send up an extended data packet 9418c2ecf20Sopenharmony_ci * 9428c2ecf20Sopenharmony_ci * @i2400m: device descriptor 9438c2ecf20Sopenharmony_ci * @skb_rx: skb that contains the extended data packet 9448c2ecf20Sopenharmony_ci * @single_last: 1 if the payload is the only one or the last one of 9458c2ecf20Sopenharmony_ci * the skb. 9468c2ecf20Sopenharmony_ci * @payload: pointer to the packet's data inside the skb 9478c2ecf20Sopenharmony_ci * @size: size of the payload 9488c2ecf20Sopenharmony_ci * 9498c2ecf20Sopenharmony_ci * Starting in v1.4 of the i2400m's firmware, the device can send data 9508c2ecf20Sopenharmony_ci * packets to the host in an extended format that; this incudes a 16 9518c2ecf20Sopenharmony_ci * byte header (struct i2400m_pl_edata_hdr). Using this header's space 9528c2ecf20Sopenharmony_ci * we can fake ethernet headers for ethernet device emulation without 9538c2ecf20Sopenharmony_ci * having to copy packets around. 9548c2ecf20Sopenharmony_ci * 9558c2ecf20Sopenharmony_ci * This function handles said path. 9568c2ecf20Sopenharmony_ci * 9578c2ecf20Sopenharmony_ci * 9588c2ecf20Sopenharmony_ci * Receive and send up an extended data packet that requires no reordering 9598c2ecf20Sopenharmony_ci * 9608c2ecf20Sopenharmony_ci * @i2400m: device descriptor 9618c2ecf20Sopenharmony_ci * @skb_rx: skb that contains the extended data packet 9628c2ecf20Sopenharmony_ci * @single_last: 1 if the payload is the only one or the last one of 9638c2ecf20Sopenharmony_ci * the skb. 9648c2ecf20Sopenharmony_ci * @payload: pointer to the packet's data (past the actual extended 9658c2ecf20Sopenharmony_ci * data payload header). 9668c2ecf20Sopenharmony_ci * @size: size of the payload 9678c2ecf20Sopenharmony_ci * 9688c2ecf20Sopenharmony_ci * Pass over to the networking stack a data packet that might have 9698c2ecf20Sopenharmony_ci * reordering requirements. 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * This needs to the decide if the skb in which the packet is 9728c2ecf20Sopenharmony_ci * contained can be reused or if it needs to be cloned. Then it has to 9738c2ecf20Sopenharmony_ci * be trimmed in the edges so that the beginning is the space for eth 9748c2ecf20Sopenharmony_ci * header and then pass it to i2400m_net_erx() for the stack 9758c2ecf20Sopenharmony_ci * 9768c2ecf20Sopenharmony_ci * Assumes the caller has verified the sanity of the payload (size, 9778c2ecf20Sopenharmony_ci * etc) already. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_cistatic 9808c2ecf20Sopenharmony_civoid i2400m_rx_edata(struct i2400m *i2400m, struct sk_buff *skb_rx, 9818c2ecf20Sopenharmony_ci unsigned single_last, const void *payload, size_t size) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 9848c2ecf20Sopenharmony_ci const struct i2400m_pl_edata_hdr *hdr = payload; 9858c2ecf20Sopenharmony_ci struct net_device *net_dev = i2400m->wimax_dev.net_dev; 9868c2ecf20Sopenharmony_ci struct sk_buff *skb; 9878c2ecf20Sopenharmony_ci enum i2400m_cs cs; 9888c2ecf20Sopenharmony_ci u32 reorder; 9898c2ecf20Sopenharmony_ci unsigned ro_needed, ro_type, ro_cin, ro_sn; 9908c2ecf20Sopenharmony_ci struct i2400m_roq *roq; 9918c2ecf20Sopenharmony_ci struct i2400m_roq_data *roq_data; 9928c2ecf20Sopenharmony_ci unsigned long flags; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci BUILD_BUG_ON(ETH_HLEN > sizeof(*hdr)); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci d_fnstart(2, dev, "(i2400m %p skb_rx %p single %u payload %p " 9978c2ecf20Sopenharmony_ci "size %zu)\n", i2400m, skb_rx, single_last, payload, size); 9988c2ecf20Sopenharmony_ci if (size < sizeof(*hdr)) { 9998c2ecf20Sopenharmony_ci dev_err(dev, "ERX: HW BUG? message with short header (%zu " 10008c2ecf20Sopenharmony_ci "vs %zu bytes expected)\n", size, sizeof(*hdr)); 10018c2ecf20Sopenharmony_ci goto error; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (single_last) { 10058c2ecf20Sopenharmony_ci skb = skb_get(skb_rx); 10068c2ecf20Sopenharmony_ci d_printf(3, dev, "ERX: skb %p reusing\n", skb); 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci skb = skb_clone(skb_rx, GFP_KERNEL); 10098c2ecf20Sopenharmony_ci if (skb == NULL) { 10108c2ecf20Sopenharmony_ci dev_err(dev, "ERX: no memory to clone skb\n"); 10118c2ecf20Sopenharmony_ci net_dev->stats.rx_dropped++; 10128c2ecf20Sopenharmony_ci goto error_skb_clone; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci d_printf(3, dev, "ERX: skb %p cloned from %p\n", skb, skb_rx); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci /* now we have to pull and trim so that the skb points to the 10178c2ecf20Sopenharmony_ci * beginning of the IP packet; the netdev part will add the 10188c2ecf20Sopenharmony_ci * ethernet header as needed - we know there is enough space 10198c2ecf20Sopenharmony_ci * because we checked in i2400m_rx_edata(). */ 10208c2ecf20Sopenharmony_ci skb_pull(skb, payload + sizeof(*hdr) - (void *) skb->data); 10218c2ecf20Sopenharmony_ci skb_trim(skb, (void *) skb_end_pointer(skb) - payload - sizeof(*hdr)); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci reorder = le32_to_cpu(hdr->reorder); 10248c2ecf20Sopenharmony_ci ro_needed = reorder & I2400M_RO_NEEDED; 10258c2ecf20Sopenharmony_ci cs = hdr->cs; 10268c2ecf20Sopenharmony_ci if (ro_needed) { 10278c2ecf20Sopenharmony_ci ro_type = (reorder >> I2400M_RO_TYPE_SHIFT) & I2400M_RO_TYPE; 10288c2ecf20Sopenharmony_ci ro_cin = (reorder >> I2400M_RO_CIN_SHIFT) & I2400M_RO_CIN; 10298c2ecf20Sopenharmony_ci ro_sn = (reorder >> I2400M_RO_SN_SHIFT) & I2400M_RO_SN; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 10328c2ecf20Sopenharmony_ci if (i2400m->rx_roq == NULL) { 10338c2ecf20Sopenharmony_ci kfree_skb(skb); /* rx_roq is already destroyed */ 10348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 10358c2ecf20Sopenharmony_ci goto error; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci roq = &i2400m->rx_roq[ro_cin]; 10388c2ecf20Sopenharmony_ci kref_get(&i2400m->rx_roq_refcount); 10398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci roq_data = (struct i2400m_roq_data *) &skb->cb; 10428c2ecf20Sopenharmony_ci roq_data->sn = ro_sn; 10438c2ecf20Sopenharmony_ci roq_data->cs = cs; 10448c2ecf20Sopenharmony_ci d_printf(2, dev, "ERX: reorder needed: " 10458c2ecf20Sopenharmony_ci "type %u cin %u [ws %u] sn %u/%u len %zuB\n", 10468c2ecf20Sopenharmony_ci ro_type, ro_cin, roq->ws, ro_sn, 10478c2ecf20Sopenharmony_ci __i2400m_roq_nsn(roq, ro_sn), size); 10488c2ecf20Sopenharmony_ci d_dump(2, dev, payload, size); 10498c2ecf20Sopenharmony_ci switch(ro_type) { 10508c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_RESET: 10518c2ecf20Sopenharmony_ci i2400m_roq_reset(i2400m, roq); 10528c2ecf20Sopenharmony_ci kfree_skb(skb); /* no data here */ 10538c2ecf20Sopenharmony_ci break; 10548c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_PACKET: 10558c2ecf20Sopenharmony_ci i2400m_roq_queue(i2400m, roq, skb, ro_sn); 10568c2ecf20Sopenharmony_ci break; 10578c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_WS: 10588c2ecf20Sopenharmony_ci i2400m_roq_update_ws(i2400m, roq, ro_sn); 10598c2ecf20Sopenharmony_ci kfree_skb(skb); /* no data here */ 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci case I2400M_RO_TYPE_PACKET_WS: 10628c2ecf20Sopenharmony_ci i2400m_roq_queue_update_ws(i2400m, roq, skb, ro_sn); 10638c2ecf20Sopenharmony_ci break; 10648c2ecf20Sopenharmony_ci default: 10658c2ecf20Sopenharmony_ci dev_err(dev, "HW BUG? unknown reorder type %u\n", ro_type); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 10698c2ecf20Sopenharmony_ci kref_put(&i2400m->rx_roq_refcount, i2400m_rx_roq_destroy); 10708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci else 10738c2ecf20Sopenharmony_ci i2400m_net_erx(i2400m, skb, cs); 10748c2ecf20Sopenharmony_cierror_skb_clone: 10758c2ecf20Sopenharmony_cierror: 10768c2ecf20Sopenharmony_ci d_fnend(2, dev, "(i2400m %p skb_rx %p single %u payload %p " 10778c2ecf20Sopenharmony_ci "size %zu) = void\n", i2400m, skb_rx, single_last, payload, size); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci/* 10828c2ecf20Sopenharmony_ci * Act on a received payload 10838c2ecf20Sopenharmony_ci * 10848c2ecf20Sopenharmony_ci * @i2400m: device instance 10858c2ecf20Sopenharmony_ci * @skb_rx: skb where the transaction was received 10868c2ecf20Sopenharmony_ci * @single_last: 1 this is the only payload or the last one (so the 10878c2ecf20Sopenharmony_ci * skb can be reused instead of cloned). 10888c2ecf20Sopenharmony_ci * @pld: payload descriptor 10898c2ecf20Sopenharmony_ci * @payload: payload data 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci * Upon reception of a payload, look at its guts in the payload 10928c2ecf20Sopenharmony_ci * descriptor and decide what to do with it. If it is a single payload 10938c2ecf20Sopenharmony_ci * skb or if the last skb is a data packet, the skb will be referenced 10948c2ecf20Sopenharmony_ci * and modified (so it doesn't have to be cloned). 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_cistatic 10978c2ecf20Sopenharmony_civoid i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, 10988c2ecf20Sopenharmony_ci unsigned single_last, const struct i2400m_pld *pld, 10998c2ecf20Sopenharmony_ci const void *payload) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11028c2ecf20Sopenharmony_ci size_t pl_size = i2400m_pld_size(pld); 11038c2ecf20Sopenharmony_ci enum i2400m_pt pl_type = i2400m_pld_type(pld); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci d_printf(7, dev, "RX: received payload type %u, %zu bytes\n", 11068c2ecf20Sopenharmony_ci pl_type, pl_size); 11078c2ecf20Sopenharmony_ci d_dump(8, dev, payload, pl_size); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci switch (pl_type) { 11108c2ecf20Sopenharmony_ci case I2400M_PT_DATA: 11118c2ecf20Sopenharmony_ci d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size); 11128c2ecf20Sopenharmony_ci i2400m_net_rx(i2400m, skb_rx, single_last, payload, pl_size); 11138c2ecf20Sopenharmony_ci break; 11148c2ecf20Sopenharmony_ci case I2400M_PT_CTRL: 11158c2ecf20Sopenharmony_ci i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size); 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci case I2400M_PT_TRACE: 11188c2ecf20Sopenharmony_ci i2400m_rx_trace(i2400m, payload, pl_size); 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case I2400M_PT_EDATA: 11218c2ecf20Sopenharmony_ci d_printf(3, dev, "ERX: data payload %zu bytes\n", pl_size); 11228c2ecf20Sopenharmony_ci i2400m_rx_edata(i2400m, skb_rx, single_last, payload, pl_size); 11238c2ecf20Sopenharmony_ci break; 11248c2ecf20Sopenharmony_ci default: /* Anything else shouldn't come to the host */ 11258c2ecf20Sopenharmony_ci if (printk_ratelimit()) 11268c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? unexpected payload type %u\n", 11278c2ecf20Sopenharmony_ci pl_type); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/* 11338c2ecf20Sopenharmony_ci * Check a received transaction's message header 11348c2ecf20Sopenharmony_ci * 11358c2ecf20Sopenharmony_ci * @i2400m: device descriptor 11368c2ecf20Sopenharmony_ci * @msg_hdr: message header 11378c2ecf20Sopenharmony_ci * @buf_size: size of the received buffer 11388c2ecf20Sopenharmony_ci * 11398c2ecf20Sopenharmony_ci * Check that the declarations done by a RX buffer message header are 11408c2ecf20Sopenharmony_ci * sane and consistent with the amount of data that was received. 11418c2ecf20Sopenharmony_ci */ 11428c2ecf20Sopenharmony_cistatic 11438c2ecf20Sopenharmony_ciint i2400m_rx_msg_hdr_check(struct i2400m *i2400m, 11448c2ecf20Sopenharmony_ci const struct i2400m_msg_hdr *msg_hdr, 11458c2ecf20Sopenharmony_ci size_t buf_size) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci int result = -EIO; 11488c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11498c2ecf20Sopenharmony_ci if (buf_size < sizeof(*msg_hdr)) { 11508c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? message with short header (%zu " 11518c2ecf20Sopenharmony_ci "vs %zu bytes expected)\n", buf_size, sizeof(*msg_hdr)); 11528c2ecf20Sopenharmony_ci goto error; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci if (msg_hdr->barker != cpu_to_le32(I2400M_D2H_MSG_BARKER)) { 11558c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? message received with unknown " 11568c2ecf20Sopenharmony_ci "barker 0x%08x (buf_size %zu bytes)\n", 11578c2ecf20Sopenharmony_ci le32_to_cpu(msg_hdr->barker), buf_size); 11588c2ecf20Sopenharmony_ci goto error; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci if (msg_hdr->num_pls == 0) { 11618c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? zero payload packets in message\n"); 11628c2ecf20Sopenharmony_ci goto error; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci if (le16_to_cpu(msg_hdr->num_pls) > I2400M_MAX_PLS_IN_MSG) { 11658c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? message contains more payload " 11668c2ecf20Sopenharmony_ci "than maximum; ignoring.\n"); 11678c2ecf20Sopenharmony_ci goto error; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci result = 0; 11708c2ecf20Sopenharmony_cierror: 11718c2ecf20Sopenharmony_ci return result; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/* 11768c2ecf20Sopenharmony_ci * Check a payload descriptor against the received data 11778c2ecf20Sopenharmony_ci * 11788c2ecf20Sopenharmony_ci * @i2400m: device descriptor 11798c2ecf20Sopenharmony_ci * @pld: payload descriptor 11808c2ecf20Sopenharmony_ci * @pl_itr: offset (in bytes) in the received buffer the payload is 11818c2ecf20Sopenharmony_ci * located 11828c2ecf20Sopenharmony_ci * @buf_size: size of the received buffer 11838c2ecf20Sopenharmony_ci * 11848c2ecf20Sopenharmony_ci * Given a payload descriptor (part of a RX buffer), check it is sane 11858c2ecf20Sopenharmony_ci * and that the data it declares fits in the buffer. 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_cistatic 11888c2ecf20Sopenharmony_ciint i2400m_rx_pl_descr_check(struct i2400m *i2400m, 11898c2ecf20Sopenharmony_ci const struct i2400m_pld *pld, 11908c2ecf20Sopenharmony_ci size_t pl_itr, size_t buf_size) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci int result = -EIO; 11938c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 11948c2ecf20Sopenharmony_ci size_t pl_size = i2400m_pld_size(pld); 11958c2ecf20Sopenharmony_ci enum i2400m_pt pl_type = i2400m_pld_type(pld); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (pl_size > i2400m->bus_pl_size_max) { 11988c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? payload @%zu: size %zu is " 11998c2ecf20Sopenharmony_ci "bigger than maximum %zu; ignoring message\n", 12008c2ecf20Sopenharmony_ci pl_itr, pl_size, i2400m->bus_pl_size_max); 12018c2ecf20Sopenharmony_ci goto error; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci if (pl_itr + pl_size > buf_size) { /* enough? */ 12048c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? payload @%zu: size %zu " 12058c2ecf20Sopenharmony_ci "goes beyond the received buffer " 12068c2ecf20Sopenharmony_ci "size (%zu bytes); ignoring message\n", 12078c2ecf20Sopenharmony_ci pl_itr, pl_size, buf_size); 12088c2ecf20Sopenharmony_ci goto error; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci if (pl_type >= I2400M_PT_ILLEGAL) { 12118c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? illegal payload type %u; " 12128c2ecf20Sopenharmony_ci "ignoring message\n", pl_type); 12138c2ecf20Sopenharmony_ci goto error; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci result = 0; 12168c2ecf20Sopenharmony_cierror: 12178c2ecf20Sopenharmony_ci return result; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci/** 12228c2ecf20Sopenharmony_ci * i2400m_rx - Receive a buffer of data from the device 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * @i2400m: device descriptor 12258c2ecf20Sopenharmony_ci * @skb: skbuff where the data has been received 12268c2ecf20Sopenharmony_ci * 12278c2ecf20Sopenharmony_ci * Parse in a buffer of data that contains an RX message sent from the 12288c2ecf20Sopenharmony_ci * device. See the file header for the format. Run all checks on the 12298c2ecf20Sopenharmony_ci * buffer header, then run over each payload's descriptors, verify 12308c2ecf20Sopenharmony_ci * their consistency and act on each payload's contents. If 12318c2ecf20Sopenharmony_ci * everything is successful, update the device's statistics. 12328c2ecf20Sopenharmony_ci * 12338c2ecf20Sopenharmony_ci * Note: You need to set the skb to contain only the length of the 12348c2ecf20Sopenharmony_ci * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE). 12358c2ecf20Sopenharmony_ci * 12368c2ecf20Sopenharmony_ci * Returns: 12378c2ecf20Sopenharmony_ci * 12388c2ecf20Sopenharmony_ci * 0 if ok, < 0 errno on error 12398c2ecf20Sopenharmony_ci * 12408c2ecf20Sopenharmony_ci * If ok, this function owns now the skb and the caller DOESN'T have 12418c2ecf20Sopenharmony_ci * to run kfree_skb() on it. However, on error, the caller still owns 12428c2ecf20Sopenharmony_ci * the skb and it is responsible for releasing it. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ciint i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci int i, result; 12478c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 12488c2ecf20Sopenharmony_ci const struct i2400m_msg_hdr *msg_hdr; 12498c2ecf20Sopenharmony_ci size_t pl_itr, pl_size; 12508c2ecf20Sopenharmony_ci unsigned long flags; 12518c2ecf20Sopenharmony_ci unsigned num_pls, single_last, skb_len; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci skb_len = skb->len; 12548c2ecf20Sopenharmony_ci d_fnstart(4, dev, "(i2400m %p skb %p [size %u])\n", 12558c2ecf20Sopenharmony_ci i2400m, skb, skb_len); 12568c2ecf20Sopenharmony_ci msg_hdr = (void *) skb->data; 12578c2ecf20Sopenharmony_ci result = i2400m_rx_msg_hdr_check(i2400m, msg_hdr, skb_len); 12588c2ecf20Sopenharmony_ci if (result < 0) 12598c2ecf20Sopenharmony_ci goto error_msg_hdr_check; 12608c2ecf20Sopenharmony_ci result = -EIO; 12618c2ecf20Sopenharmony_ci num_pls = le16_to_cpu(msg_hdr->num_pls); 12628c2ecf20Sopenharmony_ci /* Check payload descriptor(s) */ 12638c2ecf20Sopenharmony_ci pl_itr = struct_size(msg_hdr, pld, num_pls); 12648c2ecf20Sopenharmony_ci pl_itr = ALIGN(pl_itr, I2400M_PL_ALIGN); 12658c2ecf20Sopenharmony_ci if (pl_itr > skb_len) { /* got all the payload descriptors? */ 12668c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? message too short (%u bytes) for " 12678c2ecf20Sopenharmony_ci "%u payload descriptors (%zu each, total %zu)\n", 12688c2ecf20Sopenharmony_ci skb_len, num_pls, sizeof(msg_hdr->pld[0]), pl_itr); 12698c2ecf20Sopenharmony_ci goto error_pl_descr_short; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci /* Walk each payload payload--check we really got it */ 12728c2ecf20Sopenharmony_ci for (i = 0; i < num_pls; i++) { 12738c2ecf20Sopenharmony_ci /* work around old gcc warnings */ 12748c2ecf20Sopenharmony_ci pl_size = i2400m_pld_size(&msg_hdr->pld[i]); 12758c2ecf20Sopenharmony_ci result = i2400m_rx_pl_descr_check(i2400m, &msg_hdr->pld[i], 12768c2ecf20Sopenharmony_ci pl_itr, skb_len); 12778c2ecf20Sopenharmony_ci if (result < 0) 12788c2ecf20Sopenharmony_ci goto error_pl_descr_check; 12798c2ecf20Sopenharmony_ci single_last = num_pls == 1 || i == num_pls - 1; 12808c2ecf20Sopenharmony_ci i2400m_rx_payload(i2400m, skb, single_last, &msg_hdr->pld[i], 12818c2ecf20Sopenharmony_ci skb->data + pl_itr); 12828c2ecf20Sopenharmony_ci pl_itr += ALIGN(pl_size, I2400M_PL_ALIGN); 12838c2ecf20Sopenharmony_ci cond_resched(); /* Don't monopolize */ 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci kfree_skb(skb); 12868c2ecf20Sopenharmony_ci /* Update device statistics */ 12878c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 12888c2ecf20Sopenharmony_ci i2400m->rx_pl_num += i; 12898c2ecf20Sopenharmony_ci if (i > i2400m->rx_pl_max) 12908c2ecf20Sopenharmony_ci i2400m->rx_pl_max = i; 12918c2ecf20Sopenharmony_ci if (i < i2400m->rx_pl_min) 12928c2ecf20Sopenharmony_ci i2400m->rx_pl_min = i; 12938c2ecf20Sopenharmony_ci i2400m->rx_num++; 12948c2ecf20Sopenharmony_ci i2400m->rx_size_acc += skb_len; 12958c2ecf20Sopenharmony_ci if (skb_len < i2400m->rx_size_min) 12968c2ecf20Sopenharmony_ci i2400m->rx_size_min = skb_len; 12978c2ecf20Sopenharmony_ci if (skb_len > i2400m->rx_size_max) 12988c2ecf20Sopenharmony_ci i2400m->rx_size_max = skb_len; 12998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 13008c2ecf20Sopenharmony_cierror_pl_descr_check: 13018c2ecf20Sopenharmony_cierror_pl_descr_short: 13028c2ecf20Sopenharmony_cierror_msg_hdr_check: 13038c2ecf20Sopenharmony_ci d_fnend(4, dev, "(i2400m %p skb %p [size %u]) = %d\n", 13048c2ecf20Sopenharmony_ci i2400m, skb, skb_len, result); 13058c2ecf20Sopenharmony_ci return result; 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i2400m_rx); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_civoid i2400m_unknown_barker(struct i2400m *i2400m, 13118c2ecf20Sopenharmony_ci const void *buf, size_t size) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct device *dev = i2400m_dev(i2400m); 13148c2ecf20Sopenharmony_ci char prefix[64]; 13158c2ecf20Sopenharmony_ci const __le32 *barker = buf; 13168c2ecf20Sopenharmony_ci dev_err(dev, "RX: HW BUG? unknown barker %08x, " 13178c2ecf20Sopenharmony_ci "dropping %zu bytes\n", le32_to_cpu(*barker), size); 13188c2ecf20Sopenharmony_ci snprintf(prefix, sizeof(prefix), "%s %s: ", 13198c2ecf20Sopenharmony_ci dev_driver_string(dev), dev_name(dev)); 13208c2ecf20Sopenharmony_ci if (size > 64) { 13218c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 13228c2ecf20Sopenharmony_ci 8, 4, buf, 64, 0); 13238c2ecf20Sopenharmony_ci printk(KERN_ERR "%s... (only first 64 bytes " 13248c2ecf20Sopenharmony_ci "dumped)\n", prefix); 13258c2ecf20Sopenharmony_ci } else 13268c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 13278c2ecf20Sopenharmony_ci 8, 4, buf, size, 0); 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(i2400m_unknown_barker); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* 13338c2ecf20Sopenharmony_ci * Initialize the RX queue and infrastructure 13348c2ecf20Sopenharmony_ci * 13358c2ecf20Sopenharmony_ci * This sets up all the RX reordering infrastructures, which will not 13368c2ecf20Sopenharmony_ci * be used if reordering is not enabled or if the firmware does not 13378c2ecf20Sopenharmony_ci * support it. The device is told to do reordering in 13388c2ecf20Sopenharmony_ci * i2400m_dev_initialize(), where it also looks at the value of the 13398c2ecf20Sopenharmony_ci * i2400m->rx_reorder switch before taking a decission. 13408c2ecf20Sopenharmony_ci * 13418c2ecf20Sopenharmony_ci * Note we allocate the roq queues in one chunk and the actual logging 13428c2ecf20Sopenharmony_ci * support for it (logging) in another one and then we setup the 13438c2ecf20Sopenharmony_ci * pointers from the first to the last. 13448c2ecf20Sopenharmony_ci */ 13458c2ecf20Sopenharmony_ciint i2400m_rx_setup(struct i2400m *i2400m) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci int result = 0; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci i2400m->rx_reorder = i2400m_rx_reorder_disabled? 0 : 1; 13508c2ecf20Sopenharmony_ci if (i2400m->rx_reorder) { 13518c2ecf20Sopenharmony_ci unsigned itr; 13528c2ecf20Sopenharmony_ci struct i2400m_roq_log *rd; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci result = -ENOMEM; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci i2400m->rx_roq = kcalloc(I2400M_RO_CIN + 1, 13578c2ecf20Sopenharmony_ci sizeof(i2400m->rx_roq[0]), GFP_KERNEL); 13588c2ecf20Sopenharmony_ci if (i2400m->rx_roq == NULL) 13598c2ecf20Sopenharmony_ci goto error_roq_alloc; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci rd = kcalloc(I2400M_RO_CIN + 1, sizeof(*i2400m->rx_roq[0].log), 13628c2ecf20Sopenharmony_ci GFP_KERNEL); 13638c2ecf20Sopenharmony_ci if (rd == NULL) { 13648c2ecf20Sopenharmony_ci result = -ENOMEM; 13658c2ecf20Sopenharmony_ci goto error_roq_log_alloc; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci for(itr = 0; itr < I2400M_RO_CIN + 1; itr++) { 13698c2ecf20Sopenharmony_ci __i2400m_roq_init(&i2400m->rx_roq[itr]); 13708c2ecf20Sopenharmony_ci i2400m->rx_roq[itr].log = &rd[itr]; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci kref_init(&i2400m->rx_roq_refcount); 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci return 0; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cierror_roq_log_alloc: 13778c2ecf20Sopenharmony_ci kfree(i2400m->rx_roq); 13788c2ecf20Sopenharmony_cierror_roq_alloc: 13798c2ecf20Sopenharmony_ci return result; 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci/* Tear down the RX queue and infrastructure */ 13848c2ecf20Sopenharmony_civoid i2400m_rx_release(struct i2400m *i2400m) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci unsigned long flags; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (i2400m->rx_reorder) { 13898c2ecf20Sopenharmony_ci spin_lock_irqsave(&i2400m->rx_lock, flags); 13908c2ecf20Sopenharmony_ci kref_put(&i2400m->rx_roq_refcount, i2400m_rx_roq_destroy); 13918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&i2400m->rx_lock, flags); 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci /* at this point, nothing can be received... */ 13948c2ecf20Sopenharmony_ci i2400m_report_hook_flush(i2400m); 13958c2ecf20Sopenharmony_ci} 1396