18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2002-2017 Volkswagen Group Electronic Research
68c2ecf20Sopenharmony_ci * 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 * 1. Redistributions of source code must retain the above copyright
128c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
138c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
148c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
158c2ecf20Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
168c2ecf20Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors
178c2ecf20Sopenharmony_ci *    may be used to endorse or promote products derived from this software
188c2ecf20Sopenharmony_ci *    without specific prior written permission.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this
218c2ecf20Sopenharmony_ci * software may be distributed under the terms of the GNU General
228c2ecf20Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the
238c2ecf20Sopenharmony_ci * GPL apply INSTEAD OF those given above.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * The provided data structures and external interfaces from this code
268c2ecf20Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
298c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
308c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
318c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
328c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
338c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
348c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
358c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
368c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
378c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
388c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
398c2ecf20Sopenharmony_ci * DAMAGE.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include <linux/module.h>
448c2ecf20Sopenharmony_ci#include <linux/init.h>
458c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
468c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
478c2ecf20Sopenharmony_ci#include <linux/list.h>
488c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
498c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
508c2ecf20Sopenharmony_ci#include <linux/uio.h>
518c2ecf20Sopenharmony_ci#include <linux/net.h>
528c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
538c2ecf20Sopenharmony_ci#include <linux/socket.h>
548c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
558c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
568c2ecf20Sopenharmony_ci#include <linux/can.h>
578c2ecf20Sopenharmony_ci#include <linux/can/core.h>
588c2ecf20Sopenharmony_ci#include <linux/can/skb.h>
598c2ecf20Sopenharmony_ci#include <linux/can/bcm.h>
608c2ecf20Sopenharmony_ci#include <linux/slab.h>
618c2ecf20Sopenharmony_ci#include <net/sock.h>
628c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * To send multiple CAN frame content within TX_SETUP or to filter
668c2ecf20Sopenharmony_ci * CAN messages with multiplex index within RX_SETUP, the number of
678c2ecf20Sopenharmony_ci * different filters is limited to 256 due to the one byte index value.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_ci#define MAX_NFRAMES 256
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* limit timers to 400 days for sending/timeouts */
728c2ecf20Sopenharmony_ci#define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60)
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* use of last_frames[index].flags */
758c2ecf20Sopenharmony_ci#define RX_RECV    0x40 /* received data for this element */
768c2ecf20Sopenharmony_ci#define RX_THR     0x80 /* element not been sent due to throttle feature */
778c2ecf20Sopenharmony_ci#define BCM_CAN_FLAGS_MASK 0x3F /* to clean private flags after usage */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* get best masking value for can_rx_register() for a given single can_id */
808c2ecf20Sopenharmony_ci#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
818c2ecf20Sopenharmony_ci		     (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
828c2ecf20Sopenharmony_ci		     (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
858c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
878c2ecf20Sopenharmony_ciMODULE_ALIAS("can-proto-2");
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/*
928c2ecf20Sopenharmony_ci * easy access to the first 64 bit of can(fd)_frame payload. cp->data is
938c2ecf20Sopenharmony_ci * 64 bit aligned so the offset has to be multiples of 8 which is ensured
948c2ecf20Sopenharmony_ci * by the only callers in bcm_rx_cmp_to_index() bcm_rx_handler().
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic inline u64 get_u64(const struct canfd_frame *cp, int offset)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	return *(u64 *)(cp->data + offset);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistruct bcm_op {
1028c2ecf20Sopenharmony_ci	struct list_head list;
1038c2ecf20Sopenharmony_ci	struct rcu_head rcu;
1048c2ecf20Sopenharmony_ci	int ifindex;
1058c2ecf20Sopenharmony_ci	canid_t can_id;
1068c2ecf20Sopenharmony_ci	u32 flags;
1078c2ecf20Sopenharmony_ci	unsigned long frames_abs, frames_filtered;
1088c2ecf20Sopenharmony_ci	struct bcm_timeval ival1, ival2;
1098c2ecf20Sopenharmony_ci	struct hrtimer timer, thrtimer;
1108c2ecf20Sopenharmony_ci	ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
1118c2ecf20Sopenharmony_ci	int rx_ifindex;
1128c2ecf20Sopenharmony_ci	int cfsiz;
1138c2ecf20Sopenharmony_ci	u32 count;
1148c2ecf20Sopenharmony_ci	u32 nframes;
1158c2ecf20Sopenharmony_ci	u32 currframe;
1168c2ecf20Sopenharmony_ci	/* void pointers to arrays of struct can[fd]_frame */
1178c2ecf20Sopenharmony_ci	void *frames;
1188c2ecf20Sopenharmony_ci	void *last_frames;
1198c2ecf20Sopenharmony_ci	struct canfd_frame sframe;
1208c2ecf20Sopenharmony_ci	struct canfd_frame last_sframe;
1218c2ecf20Sopenharmony_ci	struct sock *sk;
1228c2ecf20Sopenharmony_ci	struct net_device *rx_reg_dev;
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct bcm_sock {
1268c2ecf20Sopenharmony_ci	struct sock sk;
1278c2ecf20Sopenharmony_ci	int bound;
1288c2ecf20Sopenharmony_ci	int ifindex;
1298c2ecf20Sopenharmony_ci	struct list_head notifier;
1308c2ecf20Sopenharmony_ci	struct list_head rx_ops;
1318c2ecf20Sopenharmony_ci	struct list_head tx_ops;
1328c2ecf20Sopenharmony_ci	unsigned long dropped_usr_msgs;
1338c2ecf20Sopenharmony_ci	struct proc_dir_entry *bcm_proc_read;
1348c2ecf20Sopenharmony_ci	char procname [32]; /* inode number in decimal with \0 */
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic LIST_HEAD(bcm_notifier_list);
1388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(bcm_notifier_lock);
1398c2ecf20Sopenharmony_cistatic struct bcm_sock *bcm_busy_notifier;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic inline struct bcm_sock *bcm_sk(const struct sock *sk)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	return (struct bcm_sock *)sk;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* check limitations for timeval provided by user */
1528c2ecf20Sopenharmony_cistatic bool bcm_is_invalid_tv(struct bcm_msg_head *msg_head)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	if ((msg_head->ival1.tv_sec < 0) ||
1558c2ecf20Sopenharmony_ci	    (msg_head->ival1.tv_sec > BCM_TIMER_SEC_MAX) ||
1568c2ecf20Sopenharmony_ci	    (msg_head->ival1.tv_usec < 0) ||
1578c2ecf20Sopenharmony_ci	    (msg_head->ival1.tv_usec >= USEC_PER_SEC) ||
1588c2ecf20Sopenharmony_ci	    (msg_head->ival2.tv_sec < 0) ||
1598c2ecf20Sopenharmony_ci	    (msg_head->ival2.tv_sec > BCM_TIMER_SEC_MAX) ||
1608c2ecf20Sopenharmony_ci	    (msg_head->ival2.tv_usec < 0) ||
1618c2ecf20Sopenharmony_ci	    (msg_head->ival2.tv_usec >= USEC_PER_SEC))
1628c2ecf20Sopenharmony_ci		return true;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return false;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci#define CFSIZ(flags) ((flags & CAN_FD_FRAME) ? CANFD_MTU : CAN_MTU)
1688c2ecf20Sopenharmony_ci#define OPSIZ sizeof(struct bcm_op)
1698c2ecf20Sopenharmony_ci#define MHSIZ sizeof(struct bcm_msg_head)
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * procfs functions
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
1758c2ecf20Sopenharmony_cistatic char *bcm_proc_getifname(struct net *net, char *result, int ifindex)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct net_device *dev;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (!ifindex)
1808c2ecf20Sopenharmony_ci		return "any";
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	rcu_read_lock();
1838c2ecf20Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
1848c2ecf20Sopenharmony_ci	if (dev)
1858c2ecf20Sopenharmony_ci		strcpy(result, dev->name);
1868c2ecf20Sopenharmony_ci	else
1878c2ecf20Sopenharmony_ci		strcpy(result, "???");
1888c2ecf20Sopenharmony_ci	rcu_read_unlock();
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return result;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int bcm_proc_show(struct seq_file *m, void *v)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	char ifname[IFNAMSIZ];
1968c2ecf20Sopenharmony_ci	struct net *net = m->private;
1978c2ecf20Sopenharmony_ci	struct sock *sk = (struct sock *)PDE_DATA(m->file->f_inode);
1988c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
1998c2ecf20Sopenharmony_ci	struct bcm_op *op;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	seq_printf(m, ">>> socket %pK", sk->sk_socket);
2028c2ecf20Sopenharmony_ci	seq_printf(m, " / sk %pK", sk);
2038c2ecf20Sopenharmony_ci	seq_printf(m, " / bo %pK", bo);
2048c2ecf20Sopenharmony_ci	seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
2058c2ecf20Sopenharmony_ci	seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
2068c2ecf20Sopenharmony_ci	seq_printf(m, " <<<\n");
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	list_for_each_entry(op, &bo->rx_ops, list) {
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		unsigned long reduction;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		/* print only active entries & prevent division by zero */
2138c2ecf20Sopenharmony_ci		if (!op->frames_abs)
2148c2ecf20Sopenharmony_ci			continue;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
2178c2ecf20Sopenharmony_ci			   bcm_proc_getifname(net, ifname, op->ifindex));
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		if (op->flags & CAN_FD_FRAME)
2208c2ecf20Sopenharmony_ci			seq_printf(m, "(%u)", op->nframes);
2218c2ecf20Sopenharmony_ci		else
2228c2ecf20Sopenharmony_ci			seq_printf(m, "[%u]", op->nframes);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		seq_printf(m, "%c ", (op->flags & RX_CHECK_DLC) ? 'd' : ' ');
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (op->kt_ival1)
2278c2ecf20Sopenharmony_ci			seq_printf(m, "timeo=%lld ",
2288c2ecf20Sopenharmony_ci				   (long long)ktime_to_us(op->kt_ival1));
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		if (op->kt_ival2)
2318c2ecf20Sopenharmony_ci			seq_printf(m, "thr=%lld ",
2328c2ecf20Sopenharmony_ci				   (long long)ktime_to_us(op->kt_ival2));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		seq_printf(m, "# recv %ld (%ld) => reduction: ",
2358c2ecf20Sopenharmony_ci			   op->frames_filtered, op->frames_abs);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		seq_printf(m, "%s%ld%%\n",
2408c2ecf20Sopenharmony_ci			   (reduction == 100) ? "near " : "", reduction);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	list_for_each_entry(op, &bo->tx_ops, list) {
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		seq_printf(m, "tx_op: %03X %s ", op->can_id,
2468c2ecf20Sopenharmony_ci			   bcm_proc_getifname(net, ifname, op->ifindex));
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		if (op->flags & CAN_FD_FRAME)
2498c2ecf20Sopenharmony_ci			seq_printf(m, "(%u) ", op->nframes);
2508c2ecf20Sopenharmony_ci		else
2518c2ecf20Sopenharmony_ci			seq_printf(m, "[%u] ", op->nframes);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		if (op->kt_ival1)
2548c2ecf20Sopenharmony_ci			seq_printf(m, "t1=%lld ",
2558c2ecf20Sopenharmony_ci				   (long long)ktime_to_us(op->kt_ival1));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (op->kt_ival2)
2588c2ecf20Sopenharmony_ci			seq_printf(m, "t2=%lld ",
2598c2ecf20Sopenharmony_ci				   (long long)ktime_to_us(op->kt_ival2));
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		seq_printf(m, "# sent %ld\n", op->frames_abs);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	seq_putc(m, '\n');
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
2708c2ecf20Sopenharmony_ci *              of the given bcm tx op
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_cistatic void bcm_can_tx(struct bcm_op *op)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2758c2ecf20Sopenharmony_ci	struct net_device *dev;
2768c2ecf20Sopenharmony_ci	struct canfd_frame *cf = op->frames + op->cfsiz * op->currframe;
2778c2ecf20Sopenharmony_ci	int err;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* no target device? => exit */
2808c2ecf20Sopenharmony_ci	if (!op->ifindex)
2818c2ecf20Sopenharmony_ci		return;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	dev = dev_get_by_index(sock_net(op->sk), op->ifindex);
2848c2ecf20Sopenharmony_ci	if (!dev) {
2858c2ecf20Sopenharmony_ci		/* RFC: should this bcm_op remove itself here? */
2868c2ecf20Sopenharmony_ci		return;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any());
2908c2ecf20Sopenharmony_ci	if (!skb)
2918c2ecf20Sopenharmony_ci		goto out;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	can_skb_reserve(skb);
2948c2ecf20Sopenharmony_ci	can_skb_prv(skb)->ifindex = dev->ifindex;
2958c2ecf20Sopenharmony_ci	can_skb_prv(skb)->skbcnt = 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	skb_put_data(skb, cf, op->cfsiz);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* send with loopback */
3008c2ecf20Sopenharmony_ci	skb->dev = dev;
3018c2ecf20Sopenharmony_ci	can_skb_set_owner(skb, op->sk);
3028c2ecf20Sopenharmony_ci	err = can_send(skb, 1);
3038c2ecf20Sopenharmony_ci	if (!err)
3048c2ecf20Sopenharmony_ci		op->frames_abs++;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	op->currframe++;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* reached last frame? */
3098c2ecf20Sopenharmony_ci	if (op->currframe >= op->nframes)
3108c2ecf20Sopenharmony_ci		op->currframe = 0;
3118c2ecf20Sopenharmony_ciout:
3128c2ecf20Sopenharmony_ci	dev_put(dev);
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/*
3168c2ecf20Sopenharmony_ci * bcm_send_to_user - send a BCM message to the userspace
3178c2ecf20Sopenharmony_ci *                    (consisting of bcm_msg_head + x CAN frames)
3188c2ecf20Sopenharmony_ci */
3198c2ecf20Sopenharmony_cistatic void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
3208c2ecf20Sopenharmony_ci			     struct canfd_frame *frames, int has_timestamp)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3238c2ecf20Sopenharmony_ci	struct canfd_frame *firstframe;
3248c2ecf20Sopenharmony_ci	struct sockaddr_can *addr;
3258c2ecf20Sopenharmony_ci	struct sock *sk = op->sk;
3268c2ecf20Sopenharmony_ci	unsigned int datalen = head->nframes * op->cfsiz;
3278c2ecf20Sopenharmony_ci	int err;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
3308c2ecf20Sopenharmony_ci	if (!skb)
3318c2ecf20Sopenharmony_ci		return;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	skb_put_data(skb, head, sizeof(*head));
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (head->nframes) {
3368c2ecf20Sopenharmony_ci		/* CAN frames starting here */
3378c2ecf20Sopenharmony_ci		firstframe = (struct canfd_frame *)skb_tail_pointer(skb);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		skb_put_data(skb, frames, datalen);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		/*
3428c2ecf20Sopenharmony_ci		 * the BCM uses the flags-element of the canfd_frame
3438c2ecf20Sopenharmony_ci		 * structure for internal purposes. This is only
3448c2ecf20Sopenharmony_ci		 * relevant for updates that are generated by the
3458c2ecf20Sopenharmony_ci		 * BCM, where nframes is 1
3468c2ecf20Sopenharmony_ci		 */
3478c2ecf20Sopenharmony_ci		if (head->nframes == 1)
3488c2ecf20Sopenharmony_ci			firstframe->flags &= BCM_CAN_FLAGS_MASK;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (has_timestamp) {
3528c2ecf20Sopenharmony_ci		/* restore rx timestamp */
3538c2ecf20Sopenharmony_ci		skb->tstamp = op->rx_stamp;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/*
3578c2ecf20Sopenharmony_ci	 *  Put the datagram to the queue so that bcm_recvmsg() can
3588c2ecf20Sopenharmony_ci	 *  get it from there.  We need to pass the interface index to
3598c2ecf20Sopenharmony_ci	 *  bcm_recvmsg().  We pass a whole struct sockaddr_can in skb->cb
3608c2ecf20Sopenharmony_ci	 *  containing the interface index.
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	sock_skb_cb_check_size(sizeof(struct sockaddr_can));
3648c2ecf20Sopenharmony_ci	addr = (struct sockaddr_can *)skb->cb;
3658c2ecf20Sopenharmony_ci	memset(addr, 0, sizeof(*addr));
3668c2ecf20Sopenharmony_ci	addr->can_family  = AF_CAN;
3678c2ecf20Sopenharmony_ci	addr->can_ifindex = op->rx_ifindex;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	err = sock_queue_rcv_skb(sk, skb);
3708c2ecf20Sopenharmony_ci	if (err < 0) {
3718c2ecf20Sopenharmony_ci		struct bcm_sock *bo = bcm_sk(sk);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		kfree_skb(skb);
3748c2ecf20Sopenharmony_ci		/* don't care about overflows in this statistic */
3758c2ecf20Sopenharmony_ci		bo->dropped_usr_msgs++;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	ktime_t ival;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (op->kt_ival1 && op->count)
3848c2ecf20Sopenharmony_ci		ival = op->kt_ival1;
3858c2ecf20Sopenharmony_ci	else if (op->kt_ival2)
3868c2ecf20Sopenharmony_ci		ival = op->kt_ival2;
3878c2ecf20Sopenharmony_ci	else
3888c2ecf20Sopenharmony_ci		return false;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
3918c2ecf20Sopenharmony_ci	return true;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void bcm_tx_start_timer(struct bcm_op *op)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	if (bcm_tx_set_expiry(op, &op->timer))
3978c2ecf20Sopenharmony_ci		hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */
4018c2ecf20Sopenharmony_cistatic enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
4048c2ecf20Sopenharmony_ci	struct bcm_msg_head msg_head;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (op->kt_ival1 && (op->count > 0)) {
4078c2ecf20Sopenharmony_ci		op->count--;
4088c2ecf20Sopenharmony_ci		if (!op->count && (op->flags & TX_COUNTEVT)) {
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			/* create notification to user */
4118c2ecf20Sopenharmony_ci			memset(&msg_head, 0, sizeof(msg_head));
4128c2ecf20Sopenharmony_ci			msg_head.opcode  = TX_EXPIRED;
4138c2ecf20Sopenharmony_ci			msg_head.flags   = op->flags;
4148c2ecf20Sopenharmony_ci			msg_head.count   = op->count;
4158c2ecf20Sopenharmony_ci			msg_head.ival1   = op->ival1;
4168c2ecf20Sopenharmony_ci			msg_head.ival2   = op->ival2;
4178c2ecf20Sopenharmony_ci			msg_head.can_id  = op->can_id;
4188c2ecf20Sopenharmony_ci			msg_head.nframes = 0;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci			bcm_send_to_user(op, &msg_head, NULL, 0);
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci		bcm_can_tx(op);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	} else if (op->kt_ival2) {
4258c2ecf20Sopenharmony_ci		bcm_can_tx(op);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return bcm_tx_set_expiry(op, &op->timer) ?
4298c2ecf20Sopenharmony_ci		HRTIMER_RESTART : HRTIMER_NORESTART;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci/*
4338c2ecf20Sopenharmony_ci * bcm_rx_changed - create a RX_CHANGED notification due to changed content
4348c2ecf20Sopenharmony_ci */
4358c2ecf20Sopenharmony_cistatic void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct bcm_msg_head head;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* update statistics */
4408c2ecf20Sopenharmony_ci	op->frames_filtered++;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* prevent statistics overflow */
4438c2ecf20Sopenharmony_ci	if (op->frames_filtered > ULONG_MAX/100)
4448c2ecf20Sopenharmony_ci		op->frames_filtered = op->frames_abs = 0;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* this element is not throttled anymore */
4478c2ecf20Sopenharmony_ci	data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	memset(&head, 0, sizeof(head));
4508c2ecf20Sopenharmony_ci	head.opcode  = RX_CHANGED;
4518c2ecf20Sopenharmony_ci	head.flags   = op->flags;
4528c2ecf20Sopenharmony_ci	head.count   = op->count;
4538c2ecf20Sopenharmony_ci	head.ival1   = op->ival1;
4548c2ecf20Sopenharmony_ci	head.ival2   = op->ival2;
4558c2ecf20Sopenharmony_ci	head.can_id  = op->can_id;
4568c2ecf20Sopenharmony_ci	head.nframes = 1;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	bcm_send_to_user(op, &head, data, 1);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/*
4628c2ecf20Sopenharmony_ci * bcm_rx_update_and_send - process a detected relevant receive content change
4638c2ecf20Sopenharmony_ci *                          1. update the last received data
4648c2ecf20Sopenharmony_ci *                          2. send a notification to the user (if possible)
4658c2ecf20Sopenharmony_ci */
4668c2ecf20Sopenharmony_cistatic void bcm_rx_update_and_send(struct bcm_op *op,
4678c2ecf20Sopenharmony_ci				   struct canfd_frame *lastdata,
4688c2ecf20Sopenharmony_ci				   const struct canfd_frame *rxdata)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	memcpy(lastdata, rxdata, op->cfsiz);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* mark as used and throttled by default */
4738c2ecf20Sopenharmony_ci	lastdata->flags |= (RX_RECV|RX_THR);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* throttling mode inactive ? */
4768c2ecf20Sopenharmony_ci	if (!op->kt_ival2) {
4778c2ecf20Sopenharmony_ci		/* send RX_CHANGED to the user immediately */
4788c2ecf20Sopenharmony_ci		bcm_rx_changed(op, lastdata);
4798c2ecf20Sopenharmony_ci		return;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* with active throttling timer we are just done here */
4838c2ecf20Sopenharmony_ci	if (hrtimer_active(&op->thrtimer))
4848c2ecf20Sopenharmony_ci		return;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* first reception with enabled throttling mode */
4878c2ecf20Sopenharmony_ci	if (!op->kt_lastmsg)
4888c2ecf20Sopenharmony_ci		goto rx_changed_settime;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* got a second frame inside a potential throttle period? */
4918c2ecf20Sopenharmony_ci	if (ktime_us_delta(ktime_get(), op->kt_lastmsg) <
4928c2ecf20Sopenharmony_ci	    ktime_to_us(op->kt_ival2)) {
4938c2ecf20Sopenharmony_ci		/* do not send the saved data - only start throttle timer */
4948c2ecf20Sopenharmony_ci		hrtimer_start(&op->thrtimer,
4958c2ecf20Sopenharmony_ci			      ktime_add(op->kt_lastmsg, op->kt_ival2),
4968c2ecf20Sopenharmony_ci			      HRTIMER_MODE_ABS_SOFT);
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/* the gap was that big, that throttling was not needed here */
5018c2ecf20Sopenharmony_cirx_changed_settime:
5028c2ecf20Sopenharmony_ci	bcm_rx_changed(op, lastdata);
5038c2ecf20Sopenharmony_ci	op->kt_lastmsg = ktime_get();
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci/*
5078c2ecf20Sopenharmony_ci * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
5088c2ecf20Sopenharmony_ci *                       received data stored in op->last_frames[]
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_cistatic void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
5118c2ecf20Sopenharmony_ci				const struct canfd_frame *rxdata)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct canfd_frame *cf = op->frames + op->cfsiz * index;
5148c2ecf20Sopenharmony_ci	struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
5158c2ecf20Sopenharmony_ci	int i;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/*
5188c2ecf20Sopenharmony_ci	 * no one uses the MSBs of flags for comparison,
5198c2ecf20Sopenharmony_ci	 * so we use it here to detect the first time of reception
5208c2ecf20Sopenharmony_ci	 */
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!(lcf->flags & RX_RECV)) {
5238c2ecf20Sopenharmony_ci		/* received data for the first time => send update to user */
5248c2ecf20Sopenharmony_ci		bcm_rx_update_and_send(op, lcf, rxdata);
5258c2ecf20Sopenharmony_ci		return;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* do a real check in CAN frame data section */
5298c2ecf20Sopenharmony_ci	for (i = 0; i < rxdata->len; i += 8) {
5308c2ecf20Sopenharmony_ci		if ((get_u64(cf, i) & get_u64(rxdata, i)) !=
5318c2ecf20Sopenharmony_ci		    (get_u64(cf, i) & get_u64(lcf, i))) {
5328c2ecf20Sopenharmony_ci			bcm_rx_update_and_send(op, lcf, rxdata);
5338c2ecf20Sopenharmony_ci			return;
5348c2ecf20Sopenharmony_ci		}
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (op->flags & RX_CHECK_DLC) {
5388c2ecf20Sopenharmony_ci		/* do a real check in CAN frame length */
5398c2ecf20Sopenharmony_ci		if (rxdata->len != lcf->len) {
5408c2ecf20Sopenharmony_ci			bcm_rx_update_and_send(op, lcf, rxdata);
5418c2ecf20Sopenharmony_ci			return;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci/*
5478c2ecf20Sopenharmony_ci * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
5488c2ecf20Sopenharmony_ci */
5498c2ecf20Sopenharmony_cistatic void bcm_rx_starttimer(struct bcm_op *op)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	if (op->flags & RX_NO_AUTOTIMER)
5528c2ecf20Sopenharmony_ci		return;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (op->kt_ival1)
5558c2ecf20Sopenharmony_ci		hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */
5598c2ecf20Sopenharmony_cistatic enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
5628c2ecf20Sopenharmony_ci	struct bcm_msg_head msg_head;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* if user wants to be informed, when cyclic CAN-Messages come back */
5658c2ecf20Sopenharmony_ci	if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
5668c2ecf20Sopenharmony_ci		/* clear received CAN frames to indicate 'nothing received' */
5678c2ecf20Sopenharmony_ci		memset(op->last_frames, 0, op->nframes * op->cfsiz);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* create notification to user */
5718c2ecf20Sopenharmony_ci	memset(&msg_head, 0, sizeof(msg_head));
5728c2ecf20Sopenharmony_ci	msg_head.opcode  = RX_TIMEOUT;
5738c2ecf20Sopenharmony_ci	msg_head.flags   = op->flags;
5748c2ecf20Sopenharmony_ci	msg_head.count   = op->count;
5758c2ecf20Sopenharmony_ci	msg_head.ival1   = op->ival1;
5768c2ecf20Sopenharmony_ci	msg_head.ival2   = op->ival2;
5778c2ecf20Sopenharmony_ci	msg_head.can_id  = op->can_id;
5788c2ecf20Sopenharmony_ci	msg_head.nframes = 0;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	bcm_send_to_user(op, &msg_head, NULL, 0);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/*
5868c2ecf20Sopenharmony_ci * bcm_rx_do_flush - helper for bcm_rx_thr_flush
5878c2ecf20Sopenharmony_ci */
5888c2ecf20Sopenharmony_cistatic inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if ((op->last_frames) && (lcf->flags & RX_THR)) {
5938c2ecf20Sopenharmony_ci		bcm_rx_changed(op, lcf);
5948c2ecf20Sopenharmony_ci		return 1;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci	return 0;
5978c2ecf20Sopenharmony_ci}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci/*
6008c2ecf20Sopenharmony_ci * bcm_rx_thr_flush - Check for throttled data and send it to the userspace
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_cistatic int bcm_rx_thr_flush(struct bcm_op *op)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	int updated = 0;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (op->nframes > 1) {
6078c2ecf20Sopenharmony_ci		unsigned int i;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		/* for MUX filter we start at index 1 */
6108c2ecf20Sopenharmony_ci		for (i = 1; i < op->nframes; i++)
6118c2ecf20Sopenharmony_ci			updated += bcm_rx_do_flush(op, i);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	} else {
6148c2ecf20Sopenharmony_ci		/* for RX_FILTER_ID and simple filter */
6158c2ecf20Sopenharmony_ci		updated += bcm_rx_do_flush(op, 0);
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return updated;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/*
6228c2ecf20Sopenharmony_ci * bcm_rx_thr_handler - the time for blocked content updates is over now:
6238c2ecf20Sopenharmony_ci *                      Check for throttled data and send it to the userspace
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (bcm_rx_thr_flush(op)) {
6308c2ecf20Sopenharmony_ci		hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
6318c2ecf20Sopenharmony_ci		return HRTIMER_RESTART;
6328c2ecf20Sopenharmony_ci	} else {
6338c2ecf20Sopenharmony_ci		/* rearm throttle handling */
6348c2ecf20Sopenharmony_ci		op->kt_lastmsg = 0;
6358c2ecf20Sopenharmony_ci		return HRTIMER_NORESTART;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/*
6408c2ecf20Sopenharmony_ci * bcm_rx_handler - handle a CAN frame reception
6418c2ecf20Sopenharmony_ci */
6428c2ecf20Sopenharmony_cistatic void bcm_rx_handler(struct sk_buff *skb, void *data)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct bcm_op *op = (struct bcm_op *)data;
6458c2ecf20Sopenharmony_ci	const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data;
6468c2ecf20Sopenharmony_ci	unsigned int i;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (op->can_id != rxframe->can_id)
6498c2ecf20Sopenharmony_ci		return;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* make sure to handle the correct frame type (CAN / CAN FD) */
6528c2ecf20Sopenharmony_ci	if (skb->len != op->cfsiz)
6538c2ecf20Sopenharmony_ci		return;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	/* disable timeout */
6568c2ecf20Sopenharmony_ci	hrtimer_cancel(&op->timer);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* save rx timestamp */
6598c2ecf20Sopenharmony_ci	op->rx_stamp = skb->tstamp;
6608c2ecf20Sopenharmony_ci	/* save originator for recvfrom() */
6618c2ecf20Sopenharmony_ci	op->rx_ifindex = skb->dev->ifindex;
6628c2ecf20Sopenharmony_ci	/* update statistics */
6638c2ecf20Sopenharmony_ci	op->frames_abs++;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (op->flags & RX_RTR_FRAME) {
6668c2ecf20Sopenharmony_ci		/* send reply for RTR-request (placed in op->frames[0]) */
6678c2ecf20Sopenharmony_ci		bcm_can_tx(op);
6688c2ecf20Sopenharmony_ci		return;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (op->flags & RX_FILTER_ID) {
6728c2ecf20Sopenharmony_ci		/* the easiest case */
6738c2ecf20Sopenharmony_ci		bcm_rx_update_and_send(op, op->last_frames, rxframe);
6748c2ecf20Sopenharmony_ci		goto rx_starttimer;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (op->nframes == 1) {
6788c2ecf20Sopenharmony_ci		/* simple compare with index 0 */
6798c2ecf20Sopenharmony_ci		bcm_rx_cmp_to_index(op, 0, rxframe);
6808c2ecf20Sopenharmony_ci		goto rx_starttimer;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (op->nframes > 1) {
6848c2ecf20Sopenharmony_ci		/*
6858c2ecf20Sopenharmony_ci		 * multiplex compare
6868c2ecf20Sopenharmony_ci		 *
6878c2ecf20Sopenharmony_ci		 * find the first multiplex mask that fits.
6888c2ecf20Sopenharmony_ci		 * Remark: The MUX-mask is stored in index 0 - but only the
6898c2ecf20Sopenharmony_ci		 * first 64 bits of the frame data[] are relevant (CAN FD)
6908c2ecf20Sopenharmony_ci		 */
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		for (i = 1; i < op->nframes; i++) {
6938c2ecf20Sopenharmony_ci			if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) ==
6948c2ecf20Sopenharmony_ci			    (get_u64(op->frames, 0) &
6958c2ecf20Sopenharmony_ci			     get_u64(op->frames + op->cfsiz * i, 0))) {
6968c2ecf20Sopenharmony_ci				bcm_rx_cmp_to_index(op, i, rxframe);
6978c2ecf20Sopenharmony_ci				break;
6988c2ecf20Sopenharmony_ci			}
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cirx_starttimer:
7038c2ecf20Sopenharmony_ci	bcm_rx_starttimer(op);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci/*
7078c2ecf20Sopenharmony_ci * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
7088c2ecf20Sopenharmony_ci */
7098c2ecf20Sopenharmony_cistatic struct bcm_op *bcm_find_op(struct list_head *ops,
7108c2ecf20Sopenharmony_ci				  struct bcm_msg_head *mh, int ifindex)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	struct bcm_op *op;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	list_for_each_entry(op, ops, list) {
7158c2ecf20Sopenharmony_ci		if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
7168c2ecf20Sopenharmony_ci		    (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME))
7178c2ecf20Sopenharmony_ci			return op;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return NULL;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic void bcm_free_op_rcu(struct rcu_head *rcu_head)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if ((op->frames) && (op->frames != &op->sframe))
7288c2ecf20Sopenharmony_ci		kfree(op->frames);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if ((op->last_frames) && (op->last_frames != &op->last_sframe))
7318c2ecf20Sopenharmony_ci		kfree(op->last_frames);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	kfree(op);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic void bcm_remove_op(struct bcm_op *op)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	hrtimer_cancel(&op->timer);
7398c2ecf20Sopenharmony_ci	hrtimer_cancel(&op->thrtimer);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	call_rcu(&op->rcu, bcm_free_op_rcu);
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	if (op->rx_reg_dev == dev) {
7478c2ecf20Sopenharmony_ci		can_rx_unregister(dev_net(dev), dev, op->can_id,
7488c2ecf20Sopenharmony_ci				  REGMASK(op->can_id), bcm_rx_handler, op);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		/* mark as removed subscription */
7518c2ecf20Sopenharmony_ci		op->rx_reg_dev = NULL;
7528c2ecf20Sopenharmony_ci	} else
7538c2ecf20Sopenharmony_ci		printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
7548c2ecf20Sopenharmony_ci		       "mismatch %p %p\n", op->rx_reg_dev, dev);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci/*
7588c2ecf20Sopenharmony_ci * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
7598c2ecf20Sopenharmony_ci */
7608c2ecf20Sopenharmony_cistatic int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,
7618c2ecf20Sopenharmony_ci			    int ifindex)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct bcm_op *op, *n;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	list_for_each_entry_safe(op, n, ops, list) {
7668c2ecf20Sopenharmony_ci		if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
7678c2ecf20Sopenharmony_ci		    (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci			/* disable automatic timer on frame reception */
7708c2ecf20Sopenharmony_ci			op->flags |= RX_NO_AUTOTIMER;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci			/*
7738c2ecf20Sopenharmony_ci			 * Don't care if we're bound or not (due to netdev
7748c2ecf20Sopenharmony_ci			 * problems) can_rx_unregister() is always a save
7758c2ecf20Sopenharmony_ci			 * thing to do here.
7768c2ecf20Sopenharmony_ci			 */
7778c2ecf20Sopenharmony_ci			if (op->ifindex) {
7788c2ecf20Sopenharmony_ci				/*
7798c2ecf20Sopenharmony_ci				 * Only remove subscriptions that had not
7808c2ecf20Sopenharmony_ci				 * been removed due to NETDEV_UNREGISTER
7818c2ecf20Sopenharmony_ci				 * in bcm_notifier()
7828c2ecf20Sopenharmony_ci				 */
7838c2ecf20Sopenharmony_ci				if (op->rx_reg_dev) {
7848c2ecf20Sopenharmony_ci					struct net_device *dev;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci					dev = dev_get_by_index(sock_net(op->sk),
7878c2ecf20Sopenharmony_ci							       op->ifindex);
7888c2ecf20Sopenharmony_ci					if (dev) {
7898c2ecf20Sopenharmony_ci						bcm_rx_unreg(dev, op);
7908c2ecf20Sopenharmony_ci						dev_put(dev);
7918c2ecf20Sopenharmony_ci					}
7928c2ecf20Sopenharmony_ci				}
7938c2ecf20Sopenharmony_ci			} else
7948c2ecf20Sopenharmony_ci				can_rx_unregister(sock_net(op->sk), NULL,
7958c2ecf20Sopenharmony_ci						  op->can_id,
7968c2ecf20Sopenharmony_ci						  REGMASK(op->can_id),
7978c2ecf20Sopenharmony_ci						  bcm_rx_handler, op);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci			list_del(&op->list);
8008c2ecf20Sopenharmony_ci			bcm_remove_op(op);
8018c2ecf20Sopenharmony_ci			return 1; /* done */
8028c2ecf20Sopenharmony_ci		}
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return 0; /* not found */
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci/*
8098c2ecf20Sopenharmony_ci * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
8108c2ecf20Sopenharmony_ci */
8118c2ecf20Sopenharmony_cistatic int bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh,
8128c2ecf20Sopenharmony_ci			    int ifindex)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct bcm_op *op, *n;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	list_for_each_entry_safe(op, n, ops, list) {
8178c2ecf20Sopenharmony_ci		if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) &&
8188c2ecf20Sopenharmony_ci		    (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) {
8198c2ecf20Sopenharmony_ci			list_del(&op->list);
8208c2ecf20Sopenharmony_ci			bcm_remove_op(op);
8218c2ecf20Sopenharmony_ci			return 1; /* done */
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return 0; /* not found */
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci/*
8298c2ecf20Sopenharmony_ci * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
8308c2ecf20Sopenharmony_ci */
8318c2ecf20Sopenharmony_cistatic int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
8328c2ecf20Sopenharmony_ci		       int ifindex)
8338c2ecf20Sopenharmony_ci{
8348c2ecf20Sopenharmony_ci	struct bcm_op *op = bcm_find_op(ops, msg_head, ifindex);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (!op)
8378c2ecf20Sopenharmony_ci		return -EINVAL;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* put current values into msg_head */
8408c2ecf20Sopenharmony_ci	msg_head->flags   = op->flags;
8418c2ecf20Sopenharmony_ci	msg_head->count   = op->count;
8428c2ecf20Sopenharmony_ci	msg_head->ival1   = op->ival1;
8438c2ecf20Sopenharmony_ci	msg_head->ival2   = op->ival2;
8448c2ecf20Sopenharmony_ci	msg_head->nframes = op->nframes;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	bcm_send_to_user(op, msg_head, op->frames, 0);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return MHSIZ;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/*
8528c2ecf20Sopenharmony_ci * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
8538c2ecf20Sopenharmony_ci */
8548c2ecf20Sopenharmony_cistatic int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
8558c2ecf20Sopenharmony_ci			int ifindex, struct sock *sk)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
8588c2ecf20Sopenharmony_ci	struct bcm_op *op;
8598c2ecf20Sopenharmony_ci	struct canfd_frame *cf;
8608c2ecf20Sopenharmony_ci	unsigned int i;
8618c2ecf20Sopenharmony_ci	int err;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* we need a real device to send frames */
8648c2ecf20Sopenharmony_ci	if (!ifindex)
8658c2ecf20Sopenharmony_ci		return -ENODEV;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* check nframes boundaries - we need at least one CAN frame */
8688c2ecf20Sopenharmony_ci	if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES)
8698c2ecf20Sopenharmony_ci		return -EINVAL;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* check timeval limitations */
8728c2ecf20Sopenharmony_ci	if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
8738c2ecf20Sopenharmony_ci		return -EINVAL;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	/* check the given can_id */
8768c2ecf20Sopenharmony_ci	op = bcm_find_op(&bo->tx_ops, msg_head, ifindex);
8778c2ecf20Sopenharmony_ci	if (op) {
8788c2ecf20Sopenharmony_ci		/* update existing BCM operation */
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci		/*
8818c2ecf20Sopenharmony_ci		 * Do we need more space for the CAN frames than currently
8828c2ecf20Sopenharmony_ci		 * allocated? -> This is a _really_ unusual use-case and
8838c2ecf20Sopenharmony_ci		 * therefore (complexity / locking) it is not supported.
8848c2ecf20Sopenharmony_ci		 */
8858c2ecf20Sopenharmony_ci		if (msg_head->nframes > op->nframes)
8868c2ecf20Sopenharmony_ci			return -E2BIG;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci		/* update CAN frames content */
8898c2ecf20Sopenharmony_ci		for (i = 0; i < msg_head->nframes; i++) {
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci			cf = op->frames + op->cfsiz * i;
8928c2ecf20Sopenharmony_ci			err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci			if (op->flags & CAN_FD_FRAME) {
8958c2ecf20Sopenharmony_ci				if (cf->len > 64)
8968c2ecf20Sopenharmony_ci					err = -EINVAL;
8978c2ecf20Sopenharmony_ci			} else {
8988c2ecf20Sopenharmony_ci				if (cf->len > 8)
8998c2ecf20Sopenharmony_ci					err = -EINVAL;
9008c2ecf20Sopenharmony_ci			}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci			if (err < 0)
9038c2ecf20Sopenharmony_ci				return err;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci			if (msg_head->flags & TX_CP_CAN_ID) {
9068c2ecf20Sopenharmony_ci				/* copy can_id into frame */
9078c2ecf20Sopenharmony_ci				cf->can_id = msg_head->can_id;
9088c2ecf20Sopenharmony_ci			}
9098c2ecf20Sopenharmony_ci		}
9108c2ecf20Sopenharmony_ci		op->flags = msg_head->flags;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	} else {
9138c2ecf20Sopenharmony_ci		/* insert new BCM operation for the given can_id */
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		op = kzalloc(OPSIZ, GFP_KERNEL);
9168c2ecf20Sopenharmony_ci		if (!op)
9178c2ecf20Sopenharmony_ci			return -ENOMEM;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci		op->can_id = msg_head->can_id;
9208c2ecf20Sopenharmony_ci		op->cfsiz = CFSIZ(msg_head->flags);
9218c2ecf20Sopenharmony_ci		op->flags = msg_head->flags;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci		/* create array for CAN frames and copy the data */
9248c2ecf20Sopenharmony_ci		if (msg_head->nframes > 1) {
9258c2ecf20Sopenharmony_ci			op->frames = kmalloc_array(msg_head->nframes,
9268c2ecf20Sopenharmony_ci						   op->cfsiz,
9278c2ecf20Sopenharmony_ci						   GFP_KERNEL);
9288c2ecf20Sopenharmony_ci			if (!op->frames) {
9298c2ecf20Sopenharmony_ci				kfree(op);
9308c2ecf20Sopenharmony_ci				return -ENOMEM;
9318c2ecf20Sopenharmony_ci			}
9328c2ecf20Sopenharmony_ci		} else
9338c2ecf20Sopenharmony_ci			op->frames = &op->sframe;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		for (i = 0; i < msg_head->nframes; i++) {
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci			cf = op->frames + op->cfsiz * i;
9388c2ecf20Sopenharmony_ci			err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz);
9398c2ecf20Sopenharmony_ci			if (err < 0)
9408c2ecf20Sopenharmony_ci				goto free_op;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci			if (op->flags & CAN_FD_FRAME) {
9438c2ecf20Sopenharmony_ci				if (cf->len > 64)
9448c2ecf20Sopenharmony_ci					err = -EINVAL;
9458c2ecf20Sopenharmony_ci			} else {
9468c2ecf20Sopenharmony_ci				if (cf->len > 8)
9478c2ecf20Sopenharmony_ci					err = -EINVAL;
9488c2ecf20Sopenharmony_ci			}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci			if (err < 0)
9518c2ecf20Sopenharmony_ci				goto free_op;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci			if (msg_head->flags & TX_CP_CAN_ID) {
9548c2ecf20Sopenharmony_ci				/* copy can_id into frame */
9558c2ecf20Sopenharmony_ci				cf->can_id = msg_head->can_id;
9568c2ecf20Sopenharmony_ci			}
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		/* tx_ops never compare with previous received messages */
9608c2ecf20Sopenharmony_ci		op->last_frames = NULL;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		/* bcm_can_tx / bcm_tx_timeout_handler needs this */
9638c2ecf20Sopenharmony_ci		op->sk = sk;
9648c2ecf20Sopenharmony_ci		op->ifindex = ifindex;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci		/* initialize uninitialized (kzalloc) structure */
9678c2ecf20Sopenharmony_ci		hrtimer_init(&op->timer, CLOCK_MONOTONIC,
9688c2ecf20Sopenharmony_ci			     HRTIMER_MODE_REL_SOFT);
9698c2ecf20Sopenharmony_ci		op->timer.function = bcm_tx_timeout_handler;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		/* currently unused in tx_ops */
9728c2ecf20Sopenharmony_ci		hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
9738c2ecf20Sopenharmony_ci			     HRTIMER_MODE_REL_SOFT);
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci		/* add this bcm_op to the list of the tx_ops */
9768c2ecf20Sopenharmony_ci		list_add(&op->list, &bo->tx_ops);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (op->nframes != msg_head->nframes) {
9818c2ecf20Sopenharmony_ci		op->nframes   = msg_head->nframes;
9828c2ecf20Sopenharmony_ci		/* start multiple frame transmission with index 0 */
9838c2ecf20Sopenharmony_ci		op->currframe = 0;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* check flags */
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	if (op->flags & TX_RESET_MULTI_IDX) {
9898c2ecf20Sopenharmony_ci		/* start multiple frame transmission with index 0 */
9908c2ecf20Sopenharmony_ci		op->currframe = 0;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (op->flags & SETTIMER) {
9948c2ecf20Sopenharmony_ci		/* set timer values */
9958c2ecf20Sopenharmony_ci		op->count = msg_head->count;
9968c2ecf20Sopenharmony_ci		op->ival1 = msg_head->ival1;
9978c2ecf20Sopenharmony_ci		op->ival2 = msg_head->ival2;
9988c2ecf20Sopenharmony_ci		op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
9998c2ecf20Sopenharmony_ci		op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		/* disable an active timer due to zero values? */
10028c2ecf20Sopenharmony_ci		if (!op->kt_ival1 && !op->kt_ival2)
10038c2ecf20Sopenharmony_ci			hrtimer_cancel(&op->timer);
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	if (op->flags & STARTTIMER) {
10078c2ecf20Sopenharmony_ci		hrtimer_cancel(&op->timer);
10088c2ecf20Sopenharmony_ci		/* spec: send CAN frame when starting timer */
10098c2ecf20Sopenharmony_ci		op->flags |= TX_ANNOUNCE;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (op->flags & TX_ANNOUNCE) {
10138c2ecf20Sopenharmony_ci		bcm_can_tx(op);
10148c2ecf20Sopenharmony_ci		if (op->count)
10158c2ecf20Sopenharmony_ci			op->count--;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (op->flags & STARTTIMER)
10198c2ecf20Sopenharmony_ci		bcm_tx_start_timer(op);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	return msg_head->nframes * op->cfsiz + MHSIZ;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cifree_op:
10248c2ecf20Sopenharmony_ci	if (op->frames != &op->sframe)
10258c2ecf20Sopenharmony_ci		kfree(op->frames);
10268c2ecf20Sopenharmony_ci	kfree(op);
10278c2ecf20Sopenharmony_ci	return err;
10288c2ecf20Sopenharmony_ci}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/*
10318c2ecf20Sopenharmony_ci * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
10328c2ecf20Sopenharmony_ci */
10338c2ecf20Sopenharmony_cistatic int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
10348c2ecf20Sopenharmony_ci			int ifindex, struct sock *sk)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
10378c2ecf20Sopenharmony_ci	struct bcm_op *op;
10388c2ecf20Sopenharmony_ci	int do_rx_register;
10398c2ecf20Sopenharmony_ci	int err = 0;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
10428c2ecf20Sopenharmony_ci		/* be robust against wrong usage ... */
10438c2ecf20Sopenharmony_ci		msg_head->flags |= RX_FILTER_ID;
10448c2ecf20Sopenharmony_ci		/* ignore trailing garbage */
10458c2ecf20Sopenharmony_ci		msg_head->nframes = 0;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/* the first element contains the mux-mask => MAX_NFRAMES + 1  */
10498c2ecf20Sopenharmony_ci	if (msg_head->nframes > MAX_NFRAMES + 1)
10508c2ecf20Sopenharmony_ci		return -EINVAL;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	if ((msg_head->flags & RX_RTR_FRAME) &&
10538c2ecf20Sopenharmony_ci	    ((msg_head->nframes != 1) ||
10548c2ecf20Sopenharmony_ci	     (!(msg_head->can_id & CAN_RTR_FLAG))))
10558c2ecf20Sopenharmony_ci		return -EINVAL;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* check timeval limitations */
10588c2ecf20Sopenharmony_ci	if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head))
10598c2ecf20Sopenharmony_ci		return -EINVAL;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	/* check the given can_id */
10628c2ecf20Sopenharmony_ci	op = bcm_find_op(&bo->rx_ops, msg_head, ifindex);
10638c2ecf20Sopenharmony_ci	if (op) {
10648c2ecf20Sopenharmony_ci		/* update existing BCM operation */
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci		/*
10678c2ecf20Sopenharmony_ci		 * Do we need more space for the CAN frames than currently
10688c2ecf20Sopenharmony_ci		 * allocated? -> This is a _really_ unusual use-case and
10698c2ecf20Sopenharmony_ci		 * therefore (complexity / locking) it is not supported.
10708c2ecf20Sopenharmony_ci		 */
10718c2ecf20Sopenharmony_ci		if (msg_head->nframes > op->nframes)
10728c2ecf20Sopenharmony_ci			return -E2BIG;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		if (msg_head->nframes) {
10758c2ecf20Sopenharmony_ci			/* update CAN frames content */
10768c2ecf20Sopenharmony_ci			err = memcpy_from_msg(op->frames, msg,
10778c2ecf20Sopenharmony_ci					      msg_head->nframes * op->cfsiz);
10788c2ecf20Sopenharmony_ci			if (err < 0)
10798c2ecf20Sopenharmony_ci				return err;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci			/* clear last_frames to indicate 'nothing received' */
10828c2ecf20Sopenharmony_ci			memset(op->last_frames, 0, msg_head->nframes * op->cfsiz);
10838c2ecf20Sopenharmony_ci		}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		op->nframes = msg_head->nframes;
10868c2ecf20Sopenharmony_ci		op->flags = msg_head->flags;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci		/* Only an update -> do not call can_rx_register() */
10898c2ecf20Sopenharmony_ci		do_rx_register = 0;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	} else {
10928c2ecf20Sopenharmony_ci		/* insert new BCM operation for the given can_id */
10938c2ecf20Sopenharmony_ci		op = kzalloc(OPSIZ, GFP_KERNEL);
10948c2ecf20Sopenharmony_ci		if (!op)
10958c2ecf20Sopenharmony_ci			return -ENOMEM;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		op->can_id = msg_head->can_id;
10988c2ecf20Sopenharmony_ci		op->nframes = msg_head->nframes;
10998c2ecf20Sopenharmony_ci		op->cfsiz = CFSIZ(msg_head->flags);
11008c2ecf20Sopenharmony_ci		op->flags = msg_head->flags;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci		if (msg_head->nframes > 1) {
11038c2ecf20Sopenharmony_ci			/* create array for CAN frames and copy the data */
11048c2ecf20Sopenharmony_ci			op->frames = kmalloc_array(msg_head->nframes,
11058c2ecf20Sopenharmony_ci						   op->cfsiz,
11068c2ecf20Sopenharmony_ci						   GFP_KERNEL);
11078c2ecf20Sopenharmony_ci			if (!op->frames) {
11088c2ecf20Sopenharmony_ci				kfree(op);
11098c2ecf20Sopenharmony_ci				return -ENOMEM;
11108c2ecf20Sopenharmony_ci			}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci			/* create and init array for received CAN frames */
11138c2ecf20Sopenharmony_ci			op->last_frames = kcalloc(msg_head->nframes,
11148c2ecf20Sopenharmony_ci						  op->cfsiz,
11158c2ecf20Sopenharmony_ci						  GFP_KERNEL);
11168c2ecf20Sopenharmony_ci			if (!op->last_frames) {
11178c2ecf20Sopenharmony_ci				kfree(op->frames);
11188c2ecf20Sopenharmony_ci				kfree(op);
11198c2ecf20Sopenharmony_ci				return -ENOMEM;
11208c2ecf20Sopenharmony_ci			}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		} else {
11238c2ecf20Sopenharmony_ci			op->frames = &op->sframe;
11248c2ecf20Sopenharmony_ci			op->last_frames = &op->last_sframe;
11258c2ecf20Sopenharmony_ci		}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		if (msg_head->nframes) {
11288c2ecf20Sopenharmony_ci			err = memcpy_from_msg(op->frames, msg,
11298c2ecf20Sopenharmony_ci					      msg_head->nframes * op->cfsiz);
11308c2ecf20Sopenharmony_ci			if (err < 0) {
11318c2ecf20Sopenharmony_ci				if (op->frames != &op->sframe)
11328c2ecf20Sopenharmony_ci					kfree(op->frames);
11338c2ecf20Sopenharmony_ci				if (op->last_frames != &op->last_sframe)
11348c2ecf20Sopenharmony_ci					kfree(op->last_frames);
11358c2ecf20Sopenharmony_ci				kfree(op);
11368c2ecf20Sopenharmony_ci				return err;
11378c2ecf20Sopenharmony_ci			}
11388c2ecf20Sopenharmony_ci		}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci		/* bcm_can_tx / bcm_tx_timeout_handler needs this */
11418c2ecf20Sopenharmony_ci		op->sk = sk;
11428c2ecf20Sopenharmony_ci		op->ifindex = ifindex;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		/* ifindex for timeout events w/o previous frame reception */
11458c2ecf20Sopenharmony_ci		op->rx_ifindex = ifindex;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		/* initialize uninitialized (kzalloc) structure */
11488c2ecf20Sopenharmony_ci		hrtimer_init(&op->timer, CLOCK_MONOTONIC,
11498c2ecf20Sopenharmony_ci			     HRTIMER_MODE_REL_SOFT);
11508c2ecf20Sopenharmony_ci		op->timer.function = bcm_rx_timeout_handler;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci		hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
11538c2ecf20Sopenharmony_ci			     HRTIMER_MODE_REL_SOFT);
11548c2ecf20Sopenharmony_ci		op->thrtimer.function = bcm_rx_thr_handler;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci		/* add this bcm_op to the list of the rx_ops */
11578c2ecf20Sopenharmony_ci		list_add(&op->list, &bo->rx_ops);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		/* call can_rx_register() */
11608c2ecf20Sopenharmony_ci		do_rx_register = 1;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	} /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	/* check flags */
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	if (op->flags & RX_RTR_FRAME) {
11678c2ecf20Sopenharmony_ci		struct canfd_frame *frame0 = op->frames;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci		/* no timers in RTR-mode */
11708c2ecf20Sopenharmony_ci		hrtimer_cancel(&op->thrtimer);
11718c2ecf20Sopenharmony_ci		hrtimer_cancel(&op->timer);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci		/*
11748c2ecf20Sopenharmony_ci		 * funny feature in RX(!)_SETUP only for RTR-mode:
11758c2ecf20Sopenharmony_ci		 * copy can_id into frame BUT without RTR-flag to
11768c2ecf20Sopenharmony_ci		 * prevent a full-load-loopback-test ... ;-]
11778c2ecf20Sopenharmony_ci		 */
11788c2ecf20Sopenharmony_ci		if ((op->flags & TX_CP_CAN_ID) ||
11798c2ecf20Sopenharmony_ci		    (frame0->can_id == op->can_id))
11808c2ecf20Sopenharmony_ci			frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	} else {
11838c2ecf20Sopenharmony_ci		if (op->flags & SETTIMER) {
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci			/* set timer value */
11868c2ecf20Sopenharmony_ci			op->ival1 = msg_head->ival1;
11878c2ecf20Sopenharmony_ci			op->ival2 = msg_head->ival2;
11888c2ecf20Sopenharmony_ci			op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
11898c2ecf20Sopenharmony_ci			op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci			/* disable an active timer due to zero value? */
11928c2ecf20Sopenharmony_ci			if (!op->kt_ival1)
11938c2ecf20Sopenharmony_ci				hrtimer_cancel(&op->timer);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci			/*
11968c2ecf20Sopenharmony_ci			 * In any case cancel the throttle timer, flush
11978c2ecf20Sopenharmony_ci			 * potentially blocked msgs and reset throttle handling
11988c2ecf20Sopenharmony_ci			 */
11998c2ecf20Sopenharmony_ci			op->kt_lastmsg = 0;
12008c2ecf20Sopenharmony_ci			hrtimer_cancel(&op->thrtimer);
12018c2ecf20Sopenharmony_ci			bcm_rx_thr_flush(op);
12028c2ecf20Sopenharmony_ci		}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci		if ((op->flags & STARTTIMER) && op->kt_ival1)
12058c2ecf20Sopenharmony_ci			hrtimer_start(&op->timer, op->kt_ival1,
12068c2ecf20Sopenharmony_ci				      HRTIMER_MODE_REL_SOFT);
12078c2ecf20Sopenharmony_ci	}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	/* now we can register for can_ids, if we added a new bcm_op */
12108c2ecf20Sopenharmony_ci	if (do_rx_register) {
12118c2ecf20Sopenharmony_ci		if (ifindex) {
12128c2ecf20Sopenharmony_ci			struct net_device *dev;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci			dev = dev_get_by_index(sock_net(sk), ifindex);
12158c2ecf20Sopenharmony_ci			if (dev) {
12168c2ecf20Sopenharmony_ci				err = can_rx_register(sock_net(sk), dev,
12178c2ecf20Sopenharmony_ci						      op->can_id,
12188c2ecf20Sopenharmony_ci						      REGMASK(op->can_id),
12198c2ecf20Sopenharmony_ci						      bcm_rx_handler, op,
12208c2ecf20Sopenharmony_ci						      "bcm", sk);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci				op->rx_reg_dev = dev;
12238c2ecf20Sopenharmony_ci				dev_put(dev);
12248c2ecf20Sopenharmony_ci			}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci		} else
12278c2ecf20Sopenharmony_ci			err = can_rx_register(sock_net(sk), NULL, op->can_id,
12288c2ecf20Sopenharmony_ci					      REGMASK(op->can_id),
12298c2ecf20Sopenharmony_ci					      bcm_rx_handler, op, "bcm", sk);
12308c2ecf20Sopenharmony_ci		if (err) {
12318c2ecf20Sopenharmony_ci			/* this bcm rx op is broken -> remove it */
12328c2ecf20Sopenharmony_ci			list_del(&op->list);
12338c2ecf20Sopenharmony_ci			bcm_remove_op(op);
12348c2ecf20Sopenharmony_ci			return err;
12358c2ecf20Sopenharmony_ci		}
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	return msg_head->nframes * op->cfsiz + MHSIZ;
12398c2ecf20Sopenharmony_ci}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci/*
12428c2ecf20Sopenharmony_ci * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
12438c2ecf20Sopenharmony_ci */
12448c2ecf20Sopenharmony_cistatic int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk,
12458c2ecf20Sopenharmony_ci		       int cfsiz)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct sk_buff *skb;
12488c2ecf20Sopenharmony_ci	struct net_device *dev;
12498c2ecf20Sopenharmony_ci	int err;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* we need a real device to send frames */
12528c2ecf20Sopenharmony_ci	if (!ifindex)
12538c2ecf20Sopenharmony_ci		return -ENODEV;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), GFP_KERNEL);
12568c2ecf20Sopenharmony_ci	if (!skb)
12578c2ecf20Sopenharmony_ci		return -ENOMEM;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	can_skb_reserve(skb);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz);
12628c2ecf20Sopenharmony_ci	if (err < 0) {
12638c2ecf20Sopenharmony_ci		kfree_skb(skb);
12648c2ecf20Sopenharmony_ci		return err;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	dev = dev_get_by_index(sock_net(sk), ifindex);
12688c2ecf20Sopenharmony_ci	if (!dev) {
12698c2ecf20Sopenharmony_ci		kfree_skb(skb);
12708c2ecf20Sopenharmony_ci		return -ENODEV;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	can_skb_prv(skb)->ifindex = dev->ifindex;
12748c2ecf20Sopenharmony_ci	can_skb_prv(skb)->skbcnt = 0;
12758c2ecf20Sopenharmony_ci	skb->dev = dev;
12768c2ecf20Sopenharmony_ci	can_skb_set_owner(skb, sk);
12778c2ecf20Sopenharmony_ci	err = can_send(skb, 1); /* send with loopback */
12788c2ecf20Sopenharmony_ci	dev_put(dev);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (err)
12818c2ecf20Sopenharmony_ci		return err;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	return cfsiz + MHSIZ;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci/*
12878c2ecf20Sopenharmony_ci * bcm_sendmsg - process BCM commands (opcodes) from the userspace
12888c2ecf20Sopenharmony_ci */
12898c2ecf20Sopenharmony_cistatic int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
12928c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
12938c2ecf20Sopenharmony_ci	int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
12948c2ecf20Sopenharmony_ci	struct bcm_msg_head msg_head;
12958c2ecf20Sopenharmony_ci	int cfsiz;
12968c2ecf20Sopenharmony_ci	int ret; /* read bytes or error codes as return value */
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	if (!bo->bound)
12998c2ecf20Sopenharmony_ci		return -ENOTCONN;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	/* check for valid message length from userspace */
13028c2ecf20Sopenharmony_ci	if (size < MHSIZ)
13038c2ecf20Sopenharmony_ci		return -EINVAL;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	/* read message head information */
13068c2ecf20Sopenharmony_ci	ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ);
13078c2ecf20Sopenharmony_ci	if (ret < 0)
13088c2ecf20Sopenharmony_ci		return ret;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	cfsiz = CFSIZ(msg_head.flags);
13118c2ecf20Sopenharmony_ci	if ((size - MHSIZ) % cfsiz)
13128c2ecf20Sopenharmony_ci		return -EINVAL;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	/* check for alternative ifindex for this bcm_op */
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	if (!ifindex && msg->msg_name) {
13178c2ecf20Sopenharmony_ci		/* no bound device as default => check msg_name */
13188c2ecf20Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		if (msg->msg_namelen < BCM_MIN_NAMELEN)
13218c2ecf20Sopenharmony_ci			return -EINVAL;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		if (addr->can_family != AF_CAN)
13248c2ecf20Sopenharmony_ci			return -EINVAL;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci		/* ifindex from sendto() */
13278c2ecf20Sopenharmony_ci		ifindex = addr->can_ifindex;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci		if (ifindex) {
13308c2ecf20Sopenharmony_ci			struct net_device *dev;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci			dev = dev_get_by_index(sock_net(sk), ifindex);
13338c2ecf20Sopenharmony_ci			if (!dev)
13348c2ecf20Sopenharmony_ci				return -ENODEV;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci			if (dev->type != ARPHRD_CAN) {
13378c2ecf20Sopenharmony_ci				dev_put(dev);
13388c2ecf20Sopenharmony_ci				return -ENODEV;
13398c2ecf20Sopenharmony_ci			}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci			dev_put(dev);
13428c2ecf20Sopenharmony_ci		}
13438c2ecf20Sopenharmony_ci	}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	lock_sock(sk);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	switch (msg_head.opcode) {
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	case TX_SETUP:
13508c2ecf20Sopenharmony_ci		ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
13518c2ecf20Sopenharmony_ci		break;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	case RX_SETUP:
13548c2ecf20Sopenharmony_ci		ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
13558c2ecf20Sopenharmony_ci		break;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	case TX_DELETE:
13588c2ecf20Sopenharmony_ci		if (bcm_delete_tx_op(&bo->tx_ops, &msg_head, ifindex))
13598c2ecf20Sopenharmony_ci			ret = MHSIZ;
13608c2ecf20Sopenharmony_ci		else
13618c2ecf20Sopenharmony_ci			ret = -EINVAL;
13628c2ecf20Sopenharmony_ci		break;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	case RX_DELETE:
13658c2ecf20Sopenharmony_ci		if (bcm_delete_rx_op(&bo->rx_ops, &msg_head, ifindex))
13668c2ecf20Sopenharmony_ci			ret = MHSIZ;
13678c2ecf20Sopenharmony_ci		else
13688c2ecf20Sopenharmony_ci			ret = -EINVAL;
13698c2ecf20Sopenharmony_ci		break;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	case TX_READ:
13728c2ecf20Sopenharmony_ci		/* reuse msg_head for the reply to TX_READ */
13738c2ecf20Sopenharmony_ci		msg_head.opcode  = TX_STATUS;
13748c2ecf20Sopenharmony_ci		ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
13758c2ecf20Sopenharmony_ci		break;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	case RX_READ:
13788c2ecf20Sopenharmony_ci		/* reuse msg_head for the reply to RX_READ */
13798c2ecf20Sopenharmony_ci		msg_head.opcode  = RX_STATUS;
13808c2ecf20Sopenharmony_ci		ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
13818c2ecf20Sopenharmony_ci		break;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	case TX_SEND:
13848c2ecf20Sopenharmony_ci		/* we need exactly one CAN frame behind the msg head */
13858c2ecf20Sopenharmony_ci		if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ))
13868c2ecf20Sopenharmony_ci			ret = -EINVAL;
13878c2ecf20Sopenharmony_ci		else
13888c2ecf20Sopenharmony_ci			ret = bcm_tx_send(msg, ifindex, sk, cfsiz);
13898c2ecf20Sopenharmony_ci		break;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	default:
13928c2ecf20Sopenharmony_ci		ret = -EINVAL;
13938c2ecf20Sopenharmony_ci		break;
13948c2ecf20Sopenharmony_ci	}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	release_sock(sk);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	return ret;
13998c2ecf20Sopenharmony_ci}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci/*
14028c2ecf20Sopenharmony_ci * notification handler for netdevice status changes
14038c2ecf20Sopenharmony_ci */
14048c2ecf20Sopenharmony_cistatic void bcm_notify(struct bcm_sock *bo, unsigned long msg,
14058c2ecf20Sopenharmony_ci		       struct net_device *dev)
14068c2ecf20Sopenharmony_ci{
14078c2ecf20Sopenharmony_ci	struct sock *sk = &bo->sk;
14088c2ecf20Sopenharmony_ci	struct bcm_op *op;
14098c2ecf20Sopenharmony_ci	int notify_enodev = 0;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), sock_net(sk)))
14128c2ecf20Sopenharmony_ci		return;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	switch (msg) {
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
14178c2ecf20Sopenharmony_ci		lock_sock(sk);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci		/* remove device specific receive entries */
14208c2ecf20Sopenharmony_ci		list_for_each_entry(op, &bo->rx_ops, list)
14218c2ecf20Sopenharmony_ci			if (op->rx_reg_dev == dev)
14228c2ecf20Sopenharmony_ci				bcm_rx_unreg(dev, op);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci		/* remove device reference, if this is our bound device */
14258c2ecf20Sopenharmony_ci		if (bo->bound && bo->ifindex == dev->ifindex) {
14268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
14278c2ecf20Sopenharmony_ci			if (sock_net(sk)->can.bcmproc_dir && bo->bcm_proc_read)
14288c2ecf20Sopenharmony_ci				remove_proc_entry(bo->procname, sock_net(sk)->can.bcmproc_dir);
14298c2ecf20Sopenharmony_ci#endif
14308c2ecf20Sopenharmony_ci			bo->bound   = 0;
14318c2ecf20Sopenharmony_ci			bo->ifindex = 0;
14328c2ecf20Sopenharmony_ci			notify_enodev = 1;
14338c2ecf20Sopenharmony_ci		}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci		release_sock(sk);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		if (notify_enodev) {
14388c2ecf20Sopenharmony_ci			sk->sk_err = ENODEV;
14398c2ecf20Sopenharmony_ci			if (!sock_flag(sk, SOCK_DEAD))
14408c2ecf20Sopenharmony_ci				sk->sk_error_report(sk);
14418c2ecf20Sopenharmony_ci		}
14428c2ecf20Sopenharmony_ci		break;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
14458c2ecf20Sopenharmony_ci		if (bo->bound && bo->ifindex == dev->ifindex) {
14468c2ecf20Sopenharmony_ci			sk->sk_err = ENETDOWN;
14478c2ecf20Sopenharmony_ci			if (!sock_flag(sk, SOCK_DEAD))
14488c2ecf20Sopenharmony_ci				sk->sk_error_report(sk);
14498c2ecf20Sopenharmony_ci		}
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic int bcm_notifier(struct notifier_block *nb, unsigned long msg,
14548c2ecf20Sopenharmony_ci			void *ptr)
14558c2ecf20Sopenharmony_ci{
14568c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_CAN)
14598c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
14608c2ecf20Sopenharmony_ci	if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
14618c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
14628c2ecf20Sopenharmony_ci	if (unlikely(bcm_busy_notifier)) /* Check for reentrant bug. */
14638c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	spin_lock(&bcm_notifier_lock);
14668c2ecf20Sopenharmony_ci	list_for_each_entry(bcm_busy_notifier, &bcm_notifier_list, notifier) {
14678c2ecf20Sopenharmony_ci		spin_unlock(&bcm_notifier_lock);
14688c2ecf20Sopenharmony_ci		bcm_notify(bcm_busy_notifier, msg, dev);
14698c2ecf20Sopenharmony_ci		spin_lock(&bcm_notifier_lock);
14708c2ecf20Sopenharmony_ci	}
14718c2ecf20Sopenharmony_ci	bcm_busy_notifier = NULL;
14728c2ecf20Sopenharmony_ci	spin_unlock(&bcm_notifier_lock);
14738c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci/*
14778c2ecf20Sopenharmony_ci * initial settings for all BCM sockets to be set at socket creation time
14788c2ecf20Sopenharmony_ci */
14798c2ecf20Sopenharmony_cistatic int bcm_init(struct sock *sk)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	bo->bound            = 0;
14848c2ecf20Sopenharmony_ci	bo->ifindex          = 0;
14858c2ecf20Sopenharmony_ci	bo->dropped_usr_msgs = 0;
14868c2ecf20Sopenharmony_ci	bo->bcm_proc_read    = NULL;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bo->tx_ops);
14898c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bo->rx_ops);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	/* set notifier */
14928c2ecf20Sopenharmony_ci	spin_lock(&bcm_notifier_lock);
14938c2ecf20Sopenharmony_ci	list_add_tail(&bo->notifier, &bcm_notifier_list);
14948c2ecf20Sopenharmony_ci	spin_unlock(&bcm_notifier_lock);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	return 0;
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci/*
15008c2ecf20Sopenharmony_ci * standard socket functions
15018c2ecf20Sopenharmony_ci */
15028c2ecf20Sopenharmony_cistatic int bcm_release(struct socket *sock)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
15058c2ecf20Sopenharmony_ci	struct net *net;
15068c2ecf20Sopenharmony_ci	struct bcm_sock *bo;
15078c2ecf20Sopenharmony_ci	struct bcm_op *op, *next;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (!sk)
15108c2ecf20Sopenharmony_ci		return 0;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	net = sock_net(sk);
15138c2ecf20Sopenharmony_ci	bo = bcm_sk(sk);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	/* remove bcm_ops, timer, rx_unregister(), etc. */
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	spin_lock(&bcm_notifier_lock);
15188c2ecf20Sopenharmony_ci	while (bcm_busy_notifier == bo) {
15198c2ecf20Sopenharmony_ci		spin_unlock(&bcm_notifier_lock);
15208c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
15218c2ecf20Sopenharmony_ci		spin_lock(&bcm_notifier_lock);
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci	list_del(&bo->notifier);
15248c2ecf20Sopenharmony_ci	spin_unlock(&bcm_notifier_lock);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	lock_sock(sk);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
15298c2ecf20Sopenharmony_ci	/* remove procfs entry */
15308c2ecf20Sopenharmony_ci	if (net->can.bcmproc_dir && bo->bcm_proc_read)
15318c2ecf20Sopenharmony_ci		remove_proc_entry(bo->procname, net->can.bcmproc_dir);
15328c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	list_for_each_entry_safe(op, next, &bo->tx_ops, list)
15358c2ecf20Sopenharmony_ci		bcm_remove_op(op);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
15388c2ecf20Sopenharmony_ci		/*
15398c2ecf20Sopenharmony_ci		 * Don't care if we're bound or not (due to netdev problems)
15408c2ecf20Sopenharmony_ci		 * can_rx_unregister() is always a save thing to do here.
15418c2ecf20Sopenharmony_ci		 */
15428c2ecf20Sopenharmony_ci		if (op->ifindex) {
15438c2ecf20Sopenharmony_ci			/*
15448c2ecf20Sopenharmony_ci			 * Only remove subscriptions that had not
15458c2ecf20Sopenharmony_ci			 * been removed due to NETDEV_UNREGISTER
15468c2ecf20Sopenharmony_ci			 * in bcm_notifier()
15478c2ecf20Sopenharmony_ci			 */
15488c2ecf20Sopenharmony_ci			if (op->rx_reg_dev) {
15498c2ecf20Sopenharmony_ci				struct net_device *dev;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci				dev = dev_get_by_index(net, op->ifindex);
15528c2ecf20Sopenharmony_ci				if (dev) {
15538c2ecf20Sopenharmony_ci					bcm_rx_unreg(dev, op);
15548c2ecf20Sopenharmony_ci					dev_put(dev);
15558c2ecf20Sopenharmony_ci				}
15568c2ecf20Sopenharmony_ci			}
15578c2ecf20Sopenharmony_ci		} else
15588c2ecf20Sopenharmony_ci			can_rx_unregister(net, NULL, op->can_id,
15598c2ecf20Sopenharmony_ci					  REGMASK(op->can_id),
15608c2ecf20Sopenharmony_ci					  bcm_rx_handler, op);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	synchronize_rcu();
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	list_for_each_entry_safe(op, next, &bo->rx_ops, list)
15678c2ecf20Sopenharmony_ci		bcm_remove_op(op);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	/* remove device reference */
15708c2ecf20Sopenharmony_ci	if (bo->bound) {
15718c2ecf20Sopenharmony_ci		bo->bound   = 0;
15728c2ecf20Sopenharmony_ci		bo->ifindex = 0;
15738c2ecf20Sopenharmony_ci	}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	sock_orphan(sk);
15768c2ecf20Sopenharmony_ci	sock->sk = NULL;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	release_sock(sk);
15798c2ecf20Sopenharmony_ci	sock_put(sk);
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return 0;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
15858c2ecf20Sopenharmony_ci		       int flags)
15868c2ecf20Sopenharmony_ci{
15878c2ecf20Sopenharmony_ci	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
15888c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
15898c2ecf20Sopenharmony_ci	struct bcm_sock *bo = bcm_sk(sk);
15908c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
15918c2ecf20Sopenharmony_ci	int ret = 0;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if (len < BCM_MIN_NAMELEN)
15948c2ecf20Sopenharmony_ci		return -EINVAL;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	lock_sock(sk);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	if (bo->bound) {
15998c2ecf20Sopenharmony_ci		ret = -EISCONN;
16008c2ecf20Sopenharmony_ci		goto fail;
16018c2ecf20Sopenharmony_ci	}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	/* bind a device to this socket */
16048c2ecf20Sopenharmony_ci	if (addr->can_ifindex) {
16058c2ecf20Sopenharmony_ci		struct net_device *dev;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci		dev = dev_get_by_index(net, addr->can_ifindex);
16088c2ecf20Sopenharmony_ci		if (!dev) {
16098c2ecf20Sopenharmony_ci			ret = -ENODEV;
16108c2ecf20Sopenharmony_ci			goto fail;
16118c2ecf20Sopenharmony_ci		}
16128c2ecf20Sopenharmony_ci		if (dev->type != ARPHRD_CAN) {
16138c2ecf20Sopenharmony_ci			dev_put(dev);
16148c2ecf20Sopenharmony_ci			ret = -ENODEV;
16158c2ecf20Sopenharmony_ci			goto fail;
16168c2ecf20Sopenharmony_ci		}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		bo->ifindex = dev->ifindex;
16198c2ecf20Sopenharmony_ci		dev_put(dev);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	} else {
16228c2ecf20Sopenharmony_ci		/* no interface reference for ifindex = 0 ('any' CAN device) */
16238c2ecf20Sopenharmony_ci		bo->ifindex = 0;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
16278c2ecf20Sopenharmony_ci	if (net->can.bcmproc_dir) {
16288c2ecf20Sopenharmony_ci		/* unique socket address as filename */
16298c2ecf20Sopenharmony_ci		sprintf(bo->procname, "%lu", sock_i_ino(sk));
16308c2ecf20Sopenharmony_ci		bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
16318c2ecf20Sopenharmony_ci						     net->can.bcmproc_dir,
16328c2ecf20Sopenharmony_ci						     bcm_proc_show, sk);
16338c2ecf20Sopenharmony_ci		if (!bo->bcm_proc_read) {
16348c2ecf20Sopenharmony_ci			ret = -ENOMEM;
16358c2ecf20Sopenharmony_ci			goto fail;
16368c2ecf20Sopenharmony_ci		}
16378c2ecf20Sopenharmony_ci	}
16388c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	bo->bound = 1;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_cifail:
16438c2ecf20Sopenharmony_ci	release_sock(sk);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	return ret;
16468c2ecf20Sopenharmony_ci}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cistatic int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
16498c2ecf20Sopenharmony_ci		       int flags)
16508c2ecf20Sopenharmony_ci{
16518c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
16528c2ecf20Sopenharmony_ci	struct sk_buff *skb;
16538c2ecf20Sopenharmony_ci	int error = 0;
16548c2ecf20Sopenharmony_ci	int noblock;
16558c2ecf20Sopenharmony_ci	int err;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	noblock =  flags & MSG_DONTWAIT;
16588c2ecf20Sopenharmony_ci	flags   &= ~MSG_DONTWAIT;
16598c2ecf20Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, noblock, &error);
16608c2ecf20Sopenharmony_ci	if (!skb)
16618c2ecf20Sopenharmony_ci		return error;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	if (skb->len < size)
16648c2ecf20Sopenharmony_ci		size = skb->len;
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	err = memcpy_to_msg(msg, skb->data, size);
16678c2ecf20Sopenharmony_ci	if (err < 0) {
16688c2ecf20Sopenharmony_ci		skb_free_datagram(sk, skb);
16698c2ecf20Sopenharmony_ci		return err;
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	sock_recv_ts_and_drops(msg, sk, skb);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	if (msg->msg_name) {
16758c2ecf20Sopenharmony_ci		__sockaddr_check_size(BCM_MIN_NAMELEN);
16768c2ecf20Sopenharmony_ci		msg->msg_namelen = BCM_MIN_NAMELEN;
16778c2ecf20Sopenharmony_ci		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
16788c2ecf20Sopenharmony_ci	}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	skb_free_datagram(sk, skb);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return size;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic int bcm_sock_no_ioctlcmd(struct socket *sock, unsigned int cmd,
16868c2ecf20Sopenharmony_ci				unsigned long arg)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	/* no ioctls for socket layer -> hand it down to NIC layer */
16898c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cistatic const struct proto_ops bcm_ops = {
16938c2ecf20Sopenharmony_ci	.family        = PF_CAN,
16948c2ecf20Sopenharmony_ci	.release       = bcm_release,
16958c2ecf20Sopenharmony_ci	.bind          = sock_no_bind,
16968c2ecf20Sopenharmony_ci	.connect       = bcm_connect,
16978c2ecf20Sopenharmony_ci	.socketpair    = sock_no_socketpair,
16988c2ecf20Sopenharmony_ci	.accept        = sock_no_accept,
16998c2ecf20Sopenharmony_ci	.getname       = sock_no_getname,
17008c2ecf20Sopenharmony_ci	.poll          = datagram_poll,
17018c2ecf20Sopenharmony_ci	.ioctl         = bcm_sock_no_ioctlcmd,
17028c2ecf20Sopenharmony_ci	.gettstamp     = sock_gettstamp,
17038c2ecf20Sopenharmony_ci	.listen        = sock_no_listen,
17048c2ecf20Sopenharmony_ci	.shutdown      = sock_no_shutdown,
17058c2ecf20Sopenharmony_ci	.sendmsg       = bcm_sendmsg,
17068c2ecf20Sopenharmony_ci	.recvmsg       = bcm_recvmsg,
17078c2ecf20Sopenharmony_ci	.mmap          = sock_no_mmap,
17088c2ecf20Sopenharmony_ci	.sendpage      = sock_no_sendpage,
17098c2ecf20Sopenharmony_ci};
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic struct proto bcm_proto __read_mostly = {
17128c2ecf20Sopenharmony_ci	.name       = "CAN_BCM",
17138c2ecf20Sopenharmony_ci	.owner      = THIS_MODULE,
17148c2ecf20Sopenharmony_ci	.obj_size   = sizeof(struct bcm_sock),
17158c2ecf20Sopenharmony_ci	.init       = bcm_init,
17168c2ecf20Sopenharmony_ci};
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_cistatic const struct can_proto bcm_can_proto = {
17198c2ecf20Sopenharmony_ci	.type       = SOCK_DGRAM,
17208c2ecf20Sopenharmony_ci	.protocol   = CAN_BCM,
17218c2ecf20Sopenharmony_ci	.ops        = &bcm_ops,
17228c2ecf20Sopenharmony_ci	.prot       = &bcm_proto,
17238c2ecf20Sopenharmony_ci};
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic int canbcm_pernet_init(struct net *net)
17268c2ecf20Sopenharmony_ci{
17278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
17288c2ecf20Sopenharmony_ci	/* create /proc/net/can-bcm directory */
17298c2ecf20Sopenharmony_ci	net->can.bcmproc_dir = proc_net_mkdir(net, "can-bcm", net->proc_net);
17308c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	return 0;
17338c2ecf20Sopenharmony_ci}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_cistatic void canbcm_pernet_exit(struct net *net)
17368c2ecf20Sopenharmony_ci{
17378c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS)
17388c2ecf20Sopenharmony_ci	/* remove /proc/net/can-bcm directory */
17398c2ecf20Sopenharmony_ci	if (net->can.bcmproc_dir)
17408c2ecf20Sopenharmony_ci		remove_proc_entry("can-bcm", net->proc_net);
17418c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
17428c2ecf20Sopenharmony_ci}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cistatic struct pernet_operations canbcm_pernet_ops __read_mostly = {
17458c2ecf20Sopenharmony_ci	.init = canbcm_pernet_init,
17468c2ecf20Sopenharmony_ci	.exit = canbcm_pernet_exit,
17478c2ecf20Sopenharmony_ci};
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_cistatic struct notifier_block canbcm_notifier = {
17508c2ecf20Sopenharmony_ci	.notifier_call = bcm_notifier
17518c2ecf20Sopenharmony_ci};
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_cistatic int __init bcm_module_init(void)
17548c2ecf20Sopenharmony_ci{
17558c2ecf20Sopenharmony_ci	int err;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	pr_info("can: broadcast manager protocol\n");
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	err = can_proto_register(&bcm_can_proto);
17608c2ecf20Sopenharmony_ci	if (err < 0) {
17618c2ecf20Sopenharmony_ci		printk(KERN_ERR "can: registration of bcm protocol failed\n");
17628c2ecf20Sopenharmony_ci		return err;
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	register_pernet_subsys(&canbcm_pernet_ops);
17668c2ecf20Sopenharmony_ci	register_netdevice_notifier(&canbcm_notifier);
17678c2ecf20Sopenharmony_ci	return 0;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_cistatic void __exit bcm_module_exit(void)
17718c2ecf20Sopenharmony_ci{
17728c2ecf20Sopenharmony_ci	can_proto_unregister(&bcm_can_proto);
17738c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&canbcm_notifier);
17748c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&canbcm_pernet_ops);
17758c2ecf20Sopenharmony_ci}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_cimodule_init(bcm_module_init);
17788c2ecf20Sopenharmony_cimodule_exit(bcm_module_exit);
1779