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