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