18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2014      Protonic Holland,
38c2ecf20Sopenharmony_ci *                         David Jander
48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2017 Pengutronix,
58c2ecf20Sopenharmony_ci *                         Marc Kleine-Budde <kernel@pengutronix.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/can/dev.h>
98c2ecf20Sopenharmony_ci#include <linux/can/rx-offload.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistruct can_rx_offload_cb {
128c2ecf20Sopenharmony_ci	u32 timestamp;
138c2ecf20Sopenharmony_ci};
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic inline struct can_rx_offload_cb *
168c2ecf20Sopenharmony_cican_rx_offload_get_cb(struct sk_buff *skb)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct can_rx_offload_cb) > sizeof(skb->cb));
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	return (struct can_rx_offload_cb *)skb->cb;
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic inline bool
248c2ecf20Sopenharmony_cican_rx_offload_le(struct can_rx_offload *offload,
258c2ecf20Sopenharmony_ci		  unsigned int a, unsigned int b)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	if (offload->inc)
288c2ecf20Sopenharmony_ci		return a <= b;
298c2ecf20Sopenharmony_ci	else
308c2ecf20Sopenharmony_ci		return a >= b;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline unsigned int
348c2ecf20Sopenharmony_cican_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	if (offload->inc)
378c2ecf20Sopenharmony_ci		return (*val)++;
388c2ecf20Sopenharmony_ci	else
398c2ecf20Sopenharmony_ci		return (*val)--;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int can_rx_offload_napi_poll(struct napi_struct *napi, int quota)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct can_rx_offload *offload = container_of(napi,
458c2ecf20Sopenharmony_ci						      struct can_rx_offload,
468c2ecf20Sopenharmony_ci						      napi);
478c2ecf20Sopenharmony_ci	struct net_device *dev = offload->dev;
488c2ecf20Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
498c2ecf20Sopenharmony_ci	struct sk_buff *skb;
508c2ecf20Sopenharmony_ci	int work_done = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	while ((work_done < quota) &&
538c2ecf20Sopenharmony_ci	       (skb = skb_dequeue(&offload->skb_queue))) {
548c2ecf20Sopenharmony_ci		struct can_frame *cf = (struct can_frame *)skb->data;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		work_done++;
578c2ecf20Sopenharmony_ci		stats->rx_packets++;
588c2ecf20Sopenharmony_ci		stats->rx_bytes += cf->can_dlc;
598c2ecf20Sopenharmony_ci		netif_receive_skb(skb);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (work_done < quota) {
638c2ecf20Sopenharmony_ci		napi_complete_done(napi, work_done);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		/* Check if there was another interrupt */
668c2ecf20Sopenharmony_ci		if (!skb_queue_empty(&offload->skb_queue))
678c2ecf20Sopenharmony_ci			napi_reschedule(&offload->napi);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	can_led_event(offload->dev, CAN_LED_EVENT_RX);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return work_done;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline void
768c2ecf20Sopenharmony_ci__skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new,
778c2ecf20Sopenharmony_ci		     int (*compare)(struct sk_buff *a, struct sk_buff *b))
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct sk_buff *pos, *insert = NULL;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	skb_queue_reverse_walk(head, pos) {
828c2ecf20Sopenharmony_ci		const struct can_rx_offload_cb *cb_pos, *cb_new;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		cb_pos = can_rx_offload_get_cb(pos);
858c2ecf20Sopenharmony_ci		cb_new = can_rx_offload_get_cb(new);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		netdev_dbg(new->dev,
888c2ecf20Sopenharmony_ci			   "%s: pos=0x%08x, new=0x%08x, diff=%10d, queue_len=%d\n",
898c2ecf20Sopenharmony_ci			   __func__,
908c2ecf20Sopenharmony_ci			   cb_pos->timestamp, cb_new->timestamp,
918c2ecf20Sopenharmony_ci			   cb_new->timestamp - cb_pos->timestamp,
928c2ecf20Sopenharmony_ci			   skb_queue_len(head));
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		if (compare(pos, new) < 0)
958c2ecf20Sopenharmony_ci			continue;
968c2ecf20Sopenharmony_ci		insert = pos;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	if (!insert)
1008c2ecf20Sopenharmony_ci		__skb_queue_head(head, new);
1018c2ecf20Sopenharmony_ci	else
1028c2ecf20Sopenharmony_ci		__skb_queue_after(head, insert, new);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	const struct can_rx_offload_cb *cb_a, *cb_b;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	cb_a = can_rx_offload_get_cb(a);
1108c2ecf20Sopenharmony_ci	cb_b = can_rx_offload_get_cb(b);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Subtract two u32 and return result as int, to keep
1138c2ecf20Sopenharmony_ci	 * difference steady around the u32 overflow.
1148c2ecf20Sopenharmony_ci	 */
1158c2ecf20Sopenharmony_ci	return cb_b->timestamp - cb_a->timestamp;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/**
1198c2ecf20Sopenharmony_ci * can_rx_offload_offload_one() - Read one CAN frame from HW
1208c2ecf20Sopenharmony_ci * @offload: pointer to rx_offload context
1218c2ecf20Sopenharmony_ci * @n: number of mailbox to read
1228c2ecf20Sopenharmony_ci *
1238c2ecf20Sopenharmony_ci * The task of this function is to read a CAN frame from mailbox @n
1248c2ecf20Sopenharmony_ci * from the device and return the mailbox's content as a struct
1258c2ecf20Sopenharmony_ci * sk_buff.
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * If the struct can_rx_offload::skb_queue exceeds the maximal queue
1288c2ecf20Sopenharmony_ci * length (struct can_rx_offload::skb_queue_len_max) or no skb can be
1298c2ecf20Sopenharmony_ci * allocated, the mailbox contents is discarded by reading it into an
1308c2ecf20Sopenharmony_ci * overflow buffer. This way the mailbox is marked as free by the
1318c2ecf20Sopenharmony_ci * driver.
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci * Return: A pointer to skb containing the CAN frame on success.
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci *         NULL if the mailbox @n is empty.
1368c2ecf20Sopenharmony_ci *
1378c2ecf20Sopenharmony_ci *         ERR_PTR() in case of an error
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic struct sk_buff *
1408c2ecf20Sopenharmony_cican_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1438c2ecf20Sopenharmony_ci	struct can_rx_offload_cb *cb;
1448c2ecf20Sopenharmony_ci	bool drop = false;
1458c2ecf20Sopenharmony_ci	u32 timestamp;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* If queue is full drop frame */
1488c2ecf20Sopenharmony_ci	if (unlikely(skb_queue_len(&offload->skb_queue) >
1498c2ecf20Sopenharmony_ci		     offload->skb_queue_len_max))
1508c2ecf20Sopenharmony_ci		drop = true;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	skb = offload->mailbox_read(offload, n, &timestamp, drop);
1538c2ecf20Sopenharmony_ci	/* Mailbox was empty. */
1548c2ecf20Sopenharmony_ci	if (unlikely(!skb))
1558c2ecf20Sopenharmony_ci		return NULL;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* There was a problem reading the mailbox, propagate
1588c2ecf20Sopenharmony_ci	 * error value.
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	if (unlikely(IS_ERR(skb))) {
1618c2ecf20Sopenharmony_ci		offload->dev->stats.rx_dropped++;
1628c2ecf20Sopenharmony_ci		offload->dev->stats.rx_fifo_errors++;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		return skb;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* Mailbox was read. */
1688c2ecf20Sopenharmony_ci	cb = can_rx_offload_get_cb(skb);
1698c2ecf20Sopenharmony_ci	cb->timestamp = timestamp;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return skb;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ciint can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
1758c2ecf20Sopenharmony_ci					 u64 pending)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct sk_buff_head skb_queue;
1788c2ecf20Sopenharmony_ci	unsigned int i;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	__skb_queue_head_init(&skb_queue);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	for (i = offload->mb_first;
1838c2ecf20Sopenharmony_ci	     can_rx_offload_le(offload, i, offload->mb_last);
1848c2ecf20Sopenharmony_ci	     can_rx_offload_inc(offload, &i)) {
1858c2ecf20Sopenharmony_ci		struct sk_buff *skb;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		if (!(pending & BIT_ULL(i)))
1888c2ecf20Sopenharmony_ci			continue;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		skb = can_rx_offload_offload_one(offload, i);
1918c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(skb))
1928c2ecf20Sopenharmony_ci			continue;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		__skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&skb_queue)) {
1988c2ecf20Sopenharmony_ci		unsigned long flags;
1998c2ecf20Sopenharmony_ci		u32 queue_len;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		spin_lock_irqsave(&offload->skb_queue.lock, flags);
2028c2ecf20Sopenharmony_ci		skb_queue_splice_tail(&skb_queue, &offload->skb_queue);
2038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		queue_len = skb_queue_len(&offload->skb_queue);
2068c2ecf20Sopenharmony_ci		if (queue_len > offload->skb_queue_len_max / 8)
2078c2ecf20Sopenharmony_ci			netdev_dbg(offload->dev, "%s: queue_len=%d\n",
2088c2ecf20Sopenharmony_ci				   __func__, queue_len);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		can_rx_offload_schedule(offload);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return skb_queue_len(&skb_queue);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciint can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2208c2ecf20Sopenharmony_ci	int received = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	while (1) {
2238c2ecf20Sopenharmony_ci		skb = can_rx_offload_offload_one(offload, 0);
2248c2ecf20Sopenharmony_ci		if (IS_ERR(skb))
2258c2ecf20Sopenharmony_ci			continue;
2268c2ecf20Sopenharmony_ci		if (!skb)
2278c2ecf20Sopenharmony_ci			break;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		skb_queue_tail(&offload->skb_queue, skb);
2308c2ecf20Sopenharmony_ci		received++;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (received)
2348c2ecf20Sopenharmony_ci		can_rx_offload_schedule(offload);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return received;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciint can_rx_offload_queue_sorted(struct can_rx_offload *offload,
2418c2ecf20Sopenharmony_ci				struct sk_buff *skb, u32 timestamp)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct can_rx_offload_cb *cb;
2448c2ecf20Sopenharmony_ci	unsigned long flags;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (skb_queue_len(&offload->skb_queue) >
2478c2ecf20Sopenharmony_ci	    offload->skb_queue_len_max) {
2488c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
2498c2ecf20Sopenharmony_ci		return -ENOBUFS;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	cb = can_rx_offload_get_cb(skb);
2538c2ecf20Sopenharmony_ci	cb->timestamp = timestamp;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&offload->skb_queue.lock, flags);
2568c2ecf20Sopenharmony_ci	__skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare);
2578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	can_rx_offload_schedule(offload);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciunsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
2668c2ecf20Sopenharmony_ci					 unsigned int idx, u32 timestamp)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct net_device *dev = offload->dev;
2698c2ecf20Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
2708c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2718c2ecf20Sopenharmony_ci	u8 len;
2728c2ecf20Sopenharmony_ci	int err;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	skb = __can_get_echo_skb(dev, idx, &len);
2758c2ecf20Sopenharmony_ci	if (!skb)
2768c2ecf20Sopenharmony_ci		return 0;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	err = can_rx_offload_queue_sorted(offload, skb, timestamp);
2798c2ecf20Sopenharmony_ci	if (err) {
2808c2ecf20Sopenharmony_ci		stats->rx_errors++;
2818c2ecf20Sopenharmony_ci		stats->tx_fifo_errors++;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return len;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciint can_rx_offload_queue_tail(struct can_rx_offload *offload,
2898c2ecf20Sopenharmony_ci			      struct sk_buff *skb)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	if (skb_queue_len(&offload->skb_queue) >
2928c2ecf20Sopenharmony_ci	    offload->skb_queue_len_max) {
2938c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
2948c2ecf20Sopenharmony_ci		return -ENOBUFS;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	skb_queue_tail(&offload->skb_queue, skb);
2988c2ecf20Sopenharmony_ci	can_rx_offload_schedule(offload);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int can_rx_offload_init_queue(struct net_device *dev,
3058c2ecf20Sopenharmony_ci				     struct can_rx_offload *offload,
3068c2ecf20Sopenharmony_ci				     unsigned int weight)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	offload->dev = dev;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* Limit queue len to 4x the weight (rounted to next power of two) */
3118c2ecf20Sopenharmony_ci	offload->skb_queue_len_max = 2 << fls(weight);
3128c2ecf20Sopenharmony_ci	offload->skb_queue_len_max *= 4;
3138c2ecf20Sopenharmony_ci	skb_queue_head_init(&offload->skb_queue);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n",
3188c2ecf20Sopenharmony_ci		__func__, offload->skb_queue_len_max);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ciint can_rx_offload_add_timestamp(struct net_device *dev,
3248c2ecf20Sopenharmony_ci				 struct can_rx_offload *offload)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	unsigned int weight;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (offload->mb_first > BITS_PER_LONG_LONG ||
3298c2ecf20Sopenharmony_ci	    offload->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read)
3308c2ecf20Sopenharmony_ci		return -EINVAL;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (offload->mb_first < offload->mb_last) {
3338c2ecf20Sopenharmony_ci		offload->inc = true;
3348c2ecf20Sopenharmony_ci		weight = offload->mb_last - offload->mb_first;
3358c2ecf20Sopenharmony_ci	} else {
3368c2ecf20Sopenharmony_ci		offload->inc = false;
3378c2ecf20Sopenharmony_ci		weight = offload->mb_first - offload->mb_last;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return can_rx_offload_init_queue(dev, offload, weight);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciint can_rx_offload_add_fifo(struct net_device *dev,
3458c2ecf20Sopenharmony_ci			    struct can_rx_offload *offload, unsigned int weight)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	if (!offload->mailbox_read)
3488c2ecf20Sopenharmony_ci		return -EINVAL;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return can_rx_offload_init_queue(dev, offload, weight);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ciint can_rx_offload_add_manual(struct net_device *dev,
3558c2ecf20Sopenharmony_ci			      struct can_rx_offload *offload,
3568c2ecf20Sopenharmony_ci			      unsigned int weight)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	if (offload->mailbox_read)
3598c2ecf20Sopenharmony_ci		return -EINVAL;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return can_rx_offload_init_queue(dev, offload, weight);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_add_manual);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_civoid can_rx_offload_enable(struct can_rx_offload *offload)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	napi_enable(&offload->napi);
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_enable);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_civoid can_rx_offload_del(struct can_rx_offload *offload)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	netif_napi_del(&offload->napi);
3748c2ecf20Sopenharmony_ci	skb_queue_purge(&offload->skb_queue);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(can_rx_offload_del);
377