162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2002-2017 Volkswagen Group Electronic Research 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 962306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1062306a36Sopenharmony_ci * are met: 1162306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1262306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1362306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1462306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1562306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1662306a36Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors 1762306a36Sopenharmony_ci * may be used to endorse or promote products derived from this software 1862306a36Sopenharmony_ci * without specific prior written permission. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 2162306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General 2262306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 2362306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * The provided data structures and external interfaces from this code 2662306a36Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2962306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3062306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3162306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3262306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3362306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3462306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3562306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3662306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3762306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3862306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 3962306a36Sopenharmony_ci * DAMAGE. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include <linux/module.h> 4462306a36Sopenharmony_ci#include <linux/init.h> 4562306a36Sopenharmony_ci#include <linux/interrupt.h> 4662306a36Sopenharmony_ci#include <linux/hrtimer.h> 4762306a36Sopenharmony_ci#include <linux/list.h> 4862306a36Sopenharmony_ci#include <linux/proc_fs.h> 4962306a36Sopenharmony_ci#include <linux/seq_file.h> 5062306a36Sopenharmony_ci#include <linux/uio.h> 5162306a36Sopenharmony_ci#include <linux/net.h> 5262306a36Sopenharmony_ci#include <linux/netdevice.h> 5362306a36Sopenharmony_ci#include <linux/socket.h> 5462306a36Sopenharmony_ci#include <linux/if_arp.h> 5562306a36Sopenharmony_ci#include <linux/skbuff.h> 5662306a36Sopenharmony_ci#include <linux/can.h> 5762306a36Sopenharmony_ci#include <linux/can/core.h> 5862306a36Sopenharmony_ci#include <linux/can/skb.h> 5962306a36Sopenharmony_ci#include <linux/can/bcm.h> 6062306a36Sopenharmony_ci#include <linux/slab.h> 6162306a36Sopenharmony_ci#include <net/sock.h> 6262306a36Sopenharmony_ci#include <net/net_namespace.h> 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * To send multiple CAN frame content within TX_SETUP or to filter 6662306a36Sopenharmony_ci * CAN messages with multiplex index within RX_SETUP, the number of 6762306a36Sopenharmony_ci * different filters is limited to 256 due to the one byte index value. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci#define MAX_NFRAMES 256 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* limit timers to 400 days for sending/timeouts */ 7262306a36Sopenharmony_ci#define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* use of last_frames[index].flags */ 7562306a36Sopenharmony_ci#define RX_RECV 0x40 /* received data for this element */ 7662306a36Sopenharmony_ci#define RX_THR 0x80 /* element not been sent due to throttle feature */ 7762306a36Sopenharmony_ci#define BCM_CAN_FLAGS_MASK 0x3F /* to clean private flags after usage */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* get best masking value for can_rx_register() for a given single can_id */ 8062306a36Sopenharmony_ci#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \ 8162306a36Sopenharmony_ci (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \ 8262306a36Sopenharmony_ci (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG)) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciMODULE_DESCRIPTION("PF_CAN broadcast manager protocol"); 8562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 8662306a36Sopenharmony_ciMODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>"); 8762306a36Sopenharmony_ciMODULE_ALIAS("can-proto-2"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * easy access to the first 64 bit of can(fd)_frame payload. cp->data is 9362306a36Sopenharmony_ci * 64 bit aligned so the offset has to be multiples of 8 which is ensured 9462306a36Sopenharmony_ci * by the only callers in bcm_rx_cmp_to_index() bcm_rx_handler(). 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic inline u64 get_u64(const struct canfd_frame *cp, int offset) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return *(u64 *)(cp->data + offset); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct bcm_op { 10262306a36Sopenharmony_ci struct list_head list; 10362306a36Sopenharmony_ci struct rcu_head rcu; 10462306a36Sopenharmony_ci int ifindex; 10562306a36Sopenharmony_ci canid_t can_id; 10662306a36Sopenharmony_ci u32 flags; 10762306a36Sopenharmony_ci unsigned long frames_abs, frames_filtered; 10862306a36Sopenharmony_ci struct bcm_timeval ival1, ival2; 10962306a36Sopenharmony_ci struct hrtimer timer, thrtimer; 11062306a36Sopenharmony_ci ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; 11162306a36Sopenharmony_ci int rx_ifindex; 11262306a36Sopenharmony_ci int cfsiz; 11362306a36Sopenharmony_ci u32 count; 11462306a36Sopenharmony_ci u32 nframes; 11562306a36Sopenharmony_ci u32 currframe; 11662306a36Sopenharmony_ci /* void pointers to arrays of struct can[fd]_frame */ 11762306a36Sopenharmony_ci void *frames; 11862306a36Sopenharmony_ci void *last_frames; 11962306a36Sopenharmony_ci struct canfd_frame sframe; 12062306a36Sopenharmony_ci struct canfd_frame last_sframe; 12162306a36Sopenharmony_ci struct sock *sk; 12262306a36Sopenharmony_ci struct net_device *rx_reg_dev; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct bcm_sock { 12662306a36Sopenharmony_ci struct sock sk; 12762306a36Sopenharmony_ci int bound; 12862306a36Sopenharmony_ci int ifindex; 12962306a36Sopenharmony_ci struct list_head notifier; 13062306a36Sopenharmony_ci struct list_head rx_ops; 13162306a36Sopenharmony_ci struct list_head tx_ops; 13262306a36Sopenharmony_ci unsigned long dropped_usr_msgs; 13362306a36Sopenharmony_ci struct proc_dir_entry *bcm_proc_read; 13462306a36Sopenharmony_ci char procname [32]; /* inode number in decimal with \0 */ 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic LIST_HEAD(bcm_notifier_list); 13862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(bcm_notifier_lock); 13962306a36Sopenharmony_cistatic struct bcm_sock *bcm_busy_notifier; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic inline struct bcm_sock *bcm_sk(const struct sock *sk) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci return (struct bcm_sock *)sk; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* check limitations for timeval provided by user */ 15262306a36Sopenharmony_cistatic bool bcm_is_invalid_tv(struct bcm_msg_head *msg_head) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci if ((msg_head->ival1.tv_sec < 0) || 15562306a36Sopenharmony_ci (msg_head->ival1.tv_sec > BCM_TIMER_SEC_MAX) || 15662306a36Sopenharmony_ci (msg_head->ival1.tv_usec < 0) || 15762306a36Sopenharmony_ci (msg_head->ival1.tv_usec >= USEC_PER_SEC) || 15862306a36Sopenharmony_ci (msg_head->ival2.tv_sec < 0) || 15962306a36Sopenharmony_ci (msg_head->ival2.tv_sec > BCM_TIMER_SEC_MAX) || 16062306a36Sopenharmony_ci (msg_head->ival2.tv_usec < 0) || 16162306a36Sopenharmony_ci (msg_head->ival2.tv_usec >= USEC_PER_SEC)) 16262306a36Sopenharmony_ci return true; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return false; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define CFSIZ(flags) ((flags & CAN_FD_FRAME) ? CANFD_MTU : CAN_MTU) 16862306a36Sopenharmony_ci#define OPSIZ sizeof(struct bcm_op) 16962306a36Sopenharmony_ci#define MHSIZ sizeof(struct bcm_msg_head) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * procfs functions 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS) 17562306a36Sopenharmony_cistatic char *bcm_proc_getifname(struct net *net, char *result, int ifindex) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct net_device *dev; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!ifindex) 18062306a36Sopenharmony_ci return "any"; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci rcu_read_lock(); 18362306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, ifindex); 18462306a36Sopenharmony_ci if (dev) 18562306a36Sopenharmony_ci strcpy(result, dev->name); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci strcpy(result, "???"); 18862306a36Sopenharmony_ci rcu_read_unlock(); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return result; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int bcm_proc_show(struct seq_file *m, void *v) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci char ifname[IFNAMSIZ]; 19662306a36Sopenharmony_ci struct net *net = m->private; 19762306a36Sopenharmony_ci struct sock *sk = (struct sock *)pde_data(m->file->f_inode); 19862306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 19962306a36Sopenharmony_ci struct bcm_op *op; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci seq_printf(m, ">>> socket %pK", sk->sk_socket); 20262306a36Sopenharmony_ci seq_printf(m, " / sk %pK", sk); 20362306a36Sopenharmony_ci seq_printf(m, " / bo %pK", bo); 20462306a36Sopenharmony_ci seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); 20562306a36Sopenharmony_ci seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex)); 20662306a36Sopenharmony_ci seq_printf(m, " <<<\n"); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci list_for_each_entry(op, &bo->rx_ops, list) { 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci unsigned long reduction; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* print only active entries & prevent division by zero */ 21362306a36Sopenharmony_ci if (!op->frames_abs) 21462306a36Sopenharmony_ci continue; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci seq_printf(m, "rx_op: %03X %-5s ", op->can_id, 21762306a36Sopenharmony_ci bcm_proc_getifname(net, ifname, op->ifindex)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (op->flags & CAN_FD_FRAME) 22062306a36Sopenharmony_ci seq_printf(m, "(%u)", op->nframes); 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci seq_printf(m, "[%u]", op->nframes); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci seq_printf(m, "%c ", (op->flags & RX_CHECK_DLC) ? 'd' : ' '); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (op->kt_ival1) 22762306a36Sopenharmony_ci seq_printf(m, "timeo=%lld ", 22862306a36Sopenharmony_ci (long long)ktime_to_us(op->kt_ival1)); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (op->kt_ival2) 23162306a36Sopenharmony_ci seq_printf(m, "thr=%lld ", 23262306a36Sopenharmony_ci (long long)ktime_to_us(op->kt_ival2)); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci seq_printf(m, "# recv %ld (%ld) => reduction: ", 23562306a36Sopenharmony_ci op->frames_filtered, op->frames_abs); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci seq_printf(m, "%s%ld%%\n", 24062306a36Sopenharmony_ci (reduction == 100) ? "near " : "", reduction); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci list_for_each_entry(op, &bo->tx_ops, list) { 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci seq_printf(m, "tx_op: %03X %s ", op->can_id, 24662306a36Sopenharmony_ci bcm_proc_getifname(net, ifname, op->ifindex)); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (op->flags & CAN_FD_FRAME) 24962306a36Sopenharmony_ci seq_printf(m, "(%u) ", op->nframes); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci seq_printf(m, "[%u] ", op->nframes); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (op->kt_ival1) 25462306a36Sopenharmony_ci seq_printf(m, "t1=%lld ", 25562306a36Sopenharmony_ci (long long)ktime_to_us(op->kt_ival1)); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (op->kt_ival2) 25862306a36Sopenharmony_ci seq_printf(m, "t2=%lld ", 25962306a36Sopenharmony_ci (long long)ktime_to_us(op->kt_ival2)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci seq_printf(m, "# sent %ld\n", op->frames_abs); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci seq_putc(m, '\n'); 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface 27062306a36Sopenharmony_ci * of the given bcm tx op 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic void bcm_can_tx(struct bcm_op *op) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct sk_buff *skb; 27562306a36Sopenharmony_ci struct net_device *dev; 27662306a36Sopenharmony_ci struct canfd_frame *cf = op->frames + op->cfsiz * op->currframe; 27762306a36Sopenharmony_ci int err; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* no target device? => exit */ 28062306a36Sopenharmony_ci if (!op->ifindex) 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dev = dev_get_by_index(sock_net(op->sk), op->ifindex); 28462306a36Sopenharmony_ci if (!dev) { 28562306a36Sopenharmony_ci /* RFC: should this bcm_op remove itself here? */ 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci skb = alloc_skb(op->cfsiz + sizeof(struct can_skb_priv), gfp_any()); 29062306a36Sopenharmony_ci if (!skb) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci can_skb_reserve(skb); 29462306a36Sopenharmony_ci can_skb_prv(skb)->ifindex = dev->ifindex; 29562306a36Sopenharmony_ci can_skb_prv(skb)->skbcnt = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci skb_put_data(skb, cf, op->cfsiz); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* send with loopback */ 30062306a36Sopenharmony_ci skb->dev = dev; 30162306a36Sopenharmony_ci can_skb_set_owner(skb, op->sk); 30262306a36Sopenharmony_ci err = can_send(skb, 1); 30362306a36Sopenharmony_ci if (!err) 30462306a36Sopenharmony_ci op->frames_abs++; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci op->currframe++; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* reached last frame? */ 30962306a36Sopenharmony_ci if (op->currframe >= op->nframes) 31062306a36Sopenharmony_ci op->currframe = 0; 31162306a36Sopenharmony_ciout: 31262306a36Sopenharmony_ci dev_put(dev); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * bcm_send_to_user - send a BCM message to the userspace 31762306a36Sopenharmony_ci * (consisting of bcm_msg_head + x CAN frames) 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_cistatic void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, 32062306a36Sopenharmony_ci struct canfd_frame *frames, int has_timestamp) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct sk_buff *skb; 32362306a36Sopenharmony_ci struct canfd_frame *firstframe; 32462306a36Sopenharmony_ci struct sockaddr_can *addr; 32562306a36Sopenharmony_ci struct sock *sk = op->sk; 32662306a36Sopenharmony_ci unsigned int datalen = head->nframes * op->cfsiz; 32762306a36Sopenharmony_ci int err; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci skb = alloc_skb(sizeof(*head) + datalen, gfp_any()); 33062306a36Sopenharmony_ci if (!skb) 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci skb_put_data(skb, head, sizeof(*head)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (head->nframes) { 33662306a36Sopenharmony_ci /* CAN frames starting here */ 33762306a36Sopenharmony_ci firstframe = (struct canfd_frame *)skb_tail_pointer(skb); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci skb_put_data(skb, frames, datalen); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * the BCM uses the flags-element of the canfd_frame 34362306a36Sopenharmony_ci * structure for internal purposes. This is only 34462306a36Sopenharmony_ci * relevant for updates that are generated by the 34562306a36Sopenharmony_ci * BCM, where nframes is 1 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci if (head->nframes == 1) 34862306a36Sopenharmony_ci firstframe->flags &= BCM_CAN_FLAGS_MASK; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (has_timestamp) { 35262306a36Sopenharmony_ci /* restore rx timestamp */ 35362306a36Sopenharmony_ci skb->tstamp = op->rx_stamp; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Put the datagram to the queue so that bcm_recvmsg() can 35862306a36Sopenharmony_ci * get it from there. We need to pass the interface index to 35962306a36Sopenharmony_ci * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb 36062306a36Sopenharmony_ci * containing the interface index. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci sock_skb_cb_check_size(sizeof(struct sockaddr_can)); 36462306a36Sopenharmony_ci addr = (struct sockaddr_can *)skb->cb; 36562306a36Sopenharmony_ci memset(addr, 0, sizeof(*addr)); 36662306a36Sopenharmony_ci addr->can_family = AF_CAN; 36762306a36Sopenharmony_ci addr->can_ifindex = op->rx_ifindex; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci err = sock_queue_rcv_skb(sk, skb); 37062306a36Sopenharmony_ci if (err < 0) { 37162306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci kfree_skb(skb); 37462306a36Sopenharmony_ci /* don't care about overflows in this statistic */ 37562306a36Sopenharmony_ci bo->dropped_usr_msgs++; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci ktime_t ival; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (op->kt_ival1 && op->count) 38462306a36Sopenharmony_ci ival = op->kt_ival1; 38562306a36Sopenharmony_ci else if (op->kt_ival2) 38662306a36Sopenharmony_ci ival = op->kt_ival2; 38762306a36Sopenharmony_ci else 38862306a36Sopenharmony_ci return false; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival)); 39162306a36Sopenharmony_ci return true; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void bcm_tx_start_timer(struct bcm_op *op) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci if (bcm_tx_set_expiry(op, &op->timer)) 39762306a36Sopenharmony_ci hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */ 40162306a36Sopenharmony_cistatic enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); 40462306a36Sopenharmony_ci struct bcm_msg_head msg_head; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (op->kt_ival1 && (op->count > 0)) { 40762306a36Sopenharmony_ci op->count--; 40862306a36Sopenharmony_ci if (!op->count && (op->flags & TX_COUNTEVT)) { 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* create notification to user */ 41162306a36Sopenharmony_ci memset(&msg_head, 0, sizeof(msg_head)); 41262306a36Sopenharmony_ci msg_head.opcode = TX_EXPIRED; 41362306a36Sopenharmony_ci msg_head.flags = op->flags; 41462306a36Sopenharmony_ci msg_head.count = op->count; 41562306a36Sopenharmony_ci msg_head.ival1 = op->ival1; 41662306a36Sopenharmony_ci msg_head.ival2 = op->ival2; 41762306a36Sopenharmony_ci msg_head.can_id = op->can_id; 41862306a36Sopenharmony_ci msg_head.nframes = 0; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci bcm_send_to_user(op, &msg_head, NULL, 0); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci bcm_can_tx(op); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci } else if (op->kt_ival2) { 42562306a36Sopenharmony_ci bcm_can_tx(op); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return bcm_tx_set_expiry(op, &op->timer) ? 42962306a36Sopenharmony_ci HRTIMER_RESTART : HRTIMER_NORESTART; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* 43362306a36Sopenharmony_ci * bcm_rx_changed - create a RX_CHANGED notification due to changed content 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_cistatic void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct bcm_msg_head head; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* update statistics */ 44062306a36Sopenharmony_ci op->frames_filtered++; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* prevent statistics overflow */ 44362306a36Sopenharmony_ci if (op->frames_filtered > ULONG_MAX/100) 44462306a36Sopenharmony_ci op->frames_filtered = op->frames_abs = 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* this element is not throttled anymore */ 44762306a36Sopenharmony_ci data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci memset(&head, 0, sizeof(head)); 45062306a36Sopenharmony_ci head.opcode = RX_CHANGED; 45162306a36Sopenharmony_ci head.flags = op->flags; 45262306a36Sopenharmony_ci head.count = op->count; 45362306a36Sopenharmony_ci head.ival1 = op->ival1; 45462306a36Sopenharmony_ci head.ival2 = op->ival2; 45562306a36Sopenharmony_ci head.can_id = op->can_id; 45662306a36Sopenharmony_ci head.nframes = 1; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci bcm_send_to_user(op, &head, data, 1); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/* 46262306a36Sopenharmony_ci * bcm_rx_update_and_send - process a detected relevant receive content change 46362306a36Sopenharmony_ci * 1. update the last received data 46462306a36Sopenharmony_ci * 2. send a notification to the user (if possible) 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_cistatic void bcm_rx_update_and_send(struct bcm_op *op, 46762306a36Sopenharmony_ci struct canfd_frame *lastdata, 46862306a36Sopenharmony_ci const struct canfd_frame *rxdata) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci memcpy(lastdata, rxdata, op->cfsiz); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* mark as used and throttled by default */ 47362306a36Sopenharmony_ci lastdata->flags |= (RX_RECV|RX_THR); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* throttling mode inactive ? */ 47662306a36Sopenharmony_ci if (!op->kt_ival2) { 47762306a36Sopenharmony_ci /* send RX_CHANGED to the user immediately */ 47862306a36Sopenharmony_ci bcm_rx_changed(op, lastdata); 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* with active throttling timer we are just done here */ 48362306a36Sopenharmony_ci if (hrtimer_active(&op->thrtimer)) 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* first reception with enabled throttling mode */ 48762306a36Sopenharmony_ci if (!op->kt_lastmsg) 48862306a36Sopenharmony_ci goto rx_changed_settime; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* got a second frame inside a potential throttle period? */ 49162306a36Sopenharmony_ci if (ktime_us_delta(ktime_get(), op->kt_lastmsg) < 49262306a36Sopenharmony_ci ktime_to_us(op->kt_ival2)) { 49362306a36Sopenharmony_ci /* do not send the saved data - only start throttle timer */ 49462306a36Sopenharmony_ci hrtimer_start(&op->thrtimer, 49562306a36Sopenharmony_ci ktime_add(op->kt_lastmsg, op->kt_ival2), 49662306a36Sopenharmony_ci HRTIMER_MODE_ABS_SOFT); 49762306a36Sopenharmony_ci return; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* the gap was that big, that throttling was not needed here */ 50162306a36Sopenharmony_cirx_changed_settime: 50262306a36Sopenharmony_ci bcm_rx_changed(op, lastdata); 50362306a36Sopenharmony_ci op->kt_lastmsg = ktime_get(); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly 50862306a36Sopenharmony_ci * received data stored in op->last_frames[] 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_cistatic void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index, 51162306a36Sopenharmony_ci const struct canfd_frame *rxdata) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct canfd_frame *cf = op->frames + op->cfsiz * index; 51462306a36Sopenharmony_ci struct canfd_frame *lcf = op->last_frames + op->cfsiz * index; 51562306a36Sopenharmony_ci int i; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * no one uses the MSBs of flags for comparison, 51962306a36Sopenharmony_ci * so we use it here to detect the first time of reception 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (!(lcf->flags & RX_RECV)) { 52362306a36Sopenharmony_ci /* received data for the first time => send update to user */ 52462306a36Sopenharmony_ci bcm_rx_update_and_send(op, lcf, rxdata); 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* do a real check in CAN frame data section */ 52962306a36Sopenharmony_ci for (i = 0; i < rxdata->len; i += 8) { 53062306a36Sopenharmony_ci if ((get_u64(cf, i) & get_u64(rxdata, i)) != 53162306a36Sopenharmony_ci (get_u64(cf, i) & get_u64(lcf, i))) { 53262306a36Sopenharmony_ci bcm_rx_update_and_send(op, lcf, rxdata); 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (op->flags & RX_CHECK_DLC) { 53862306a36Sopenharmony_ci /* do a real check in CAN frame length */ 53962306a36Sopenharmony_ci if (rxdata->len != lcf->len) { 54062306a36Sopenharmony_ci bcm_rx_update_and_send(op, lcf, rxdata); 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_cistatic void bcm_rx_starttimer(struct bcm_op *op) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci if (op->flags & RX_NO_AUTOTIMER) 55262306a36Sopenharmony_ci return; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (op->kt_ival1) 55562306a36Sopenharmony_ci hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */ 55962306a36Sopenharmony_cistatic enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); 56262306a36Sopenharmony_ci struct bcm_msg_head msg_head; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* if user wants to be informed, when cyclic CAN-Messages come back */ 56562306a36Sopenharmony_ci if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { 56662306a36Sopenharmony_ci /* clear received CAN frames to indicate 'nothing received' */ 56762306a36Sopenharmony_ci memset(op->last_frames, 0, op->nframes * op->cfsiz); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* create notification to user */ 57162306a36Sopenharmony_ci memset(&msg_head, 0, sizeof(msg_head)); 57262306a36Sopenharmony_ci msg_head.opcode = RX_TIMEOUT; 57362306a36Sopenharmony_ci msg_head.flags = op->flags; 57462306a36Sopenharmony_ci msg_head.count = op->count; 57562306a36Sopenharmony_ci msg_head.ival1 = op->ival1; 57662306a36Sopenharmony_ci msg_head.ival2 = op->ival2; 57762306a36Sopenharmony_ci msg_head.can_id = op->can_id; 57862306a36Sopenharmony_ci msg_head.nframes = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci bcm_send_to_user(op, &msg_head, NULL, 0); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return HRTIMER_NORESTART; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* 58662306a36Sopenharmony_ci * bcm_rx_do_flush - helper for bcm_rx_thr_flush 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_cistatic inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct canfd_frame *lcf = op->last_frames + op->cfsiz * index; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if ((op->last_frames) && (lcf->flags & RX_THR)) { 59362306a36Sopenharmony_ci bcm_rx_changed(op, lcf); 59462306a36Sopenharmony_ci return 1; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * bcm_rx_thr_flush - Check for throttled data and send it to the userspace 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_cistatic int bcm_rx_thr_flush(struct bcm_op *op) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci int updated = 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (op->nframes > 1) { 60762306a36Sopenharmony_ci unsigned int i; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* for MUX filter we start at index 1 */ 61062306a36Sopenharmony_ci for (i = 1; i < op->nframes; i++) 61162306a36Sopenharmony_ci updated += bcm_rx_do_flush(op, i); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci } else { 61462306a36Sopenharmony_ci /* for RX_FILTER_ID and simple filter */ 61562306a36Sopenharmony_ci updated += bcm_rx_do_flush(op, 0); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return updated; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* 62262306a36Sopenharmony_ci * bcm_rx_thr_handler - the time for blocked content updates is over now: 62362306a36Sopenharmony_ci * Check for throttled data and send it to the userspace 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_cistatic enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (bcm_rx_thr_flush(op)) { 63062306a36Sopenharmony_ci hrtimer_forward_now(hrtimer, op->kt_ival2); 63162306a36Sopenharmony_ci return HRTIMER_RESTART; 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci /* rearm throttle handling */ 63462306a36Sopenharmony_ci op->kt_lastmsg = 0; 63562306a36Sopenharmony_ci return HRTIMER_NORESTART; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* 64062306a36Sopenharmony_ci * bcm_rx_handler - handle a CAN frame reception 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_cistatic void bcm_rx_handler(struct sk_buff *skb, void *data) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct bcm_op *op = (struct bcm_op *)data; 64562306a36Sopenharmony_ci const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; 64662306a36Sopenharmony_ci unsigned int i; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (op->can_id != rxframe->can_id) 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* make sure to handle the correct frame type (CAN / CAN FD) */ 65262306a36Sopenharmony_ci if (op->flags & CAN_FD_FRAME) { 65362306a36Sopenharmony_ci if (!can_is_canfd_skb(skb)) 65462306a36Sopenharmony_ci return; 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci if (!can_is_can_skb(skb)) 65762306a36Sopenharmony_ci return; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* disable timeout */ 66162306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* save rx timestamp */ 66462306a36Sopenharmony_ci op->rx_stamp = skb->tstamp; 66562306a36Sopenharmony_ci /* save originator for recvfrom() */ 66662306a36Sopenharmony_ci op->rx_ifindex = skb->dev->ifindex; 66762306a36Sopenharmony_ci /* update statistics */ 66862306a36Sopenharmony_ci op->frames_abs++; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (op->flags & RX_RTR_FRAME) { 67162306a36Sopenharmony_ci /* send reply for RTR-request (placed in op->frames[0]) */ 67262306a36Sopenharmony_ci bcm_can_tx(op); 67362306a36Sopenharmony_ci return; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (op->flags & RX_FILTER_ID) { 67762306a36Sopenharmony_ci /* the easiest case */ 67862306a36Sopenharmony_ci bcm_rx_update_and_send(op, op->last_frames, rxframe); 67962306a36Sopenharmony_ci goto rx_starttimer; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (op->nframes == 1) { 68362306a36Sopenharmony_ci /* simple compare with index 0 */ 68462306a36Sopenharmony_ci bcm_rx_cmp_to_index(op, 0, rxframe); 68562306a36Sopenharmony_ci goto rx_starttimer; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (op->nframes > 1) { 68962306a36Sopenharmony_ci /* 69062306a36Sopenharmony_ci * multiplex compare 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * find the first multiplex mask that fits. 69362306a36Sopenharmony_ci * Remark: The MUX-mask is stored in index 0 - but only the 69462306a36Sopenharmony_ci * first 64 bits of the frame data[] are relevant (CAN FD) 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci for (i = 1; i < op->nframes; i++) { 69862306a36Sopenharmony_ci if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) == 69962306a36Sopenharmony_ci (get_u64(op->frames, 0) & 70062306a36Sopenharmony_ci get_u64(op->frames + op->cfsiz * i, 0))) { 70162306a36Sopenharmony_ci bcm_rx_cmp_to_index(op, i, rxframe); 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cirx_starttimer: 70862306a36Sopenharmony_ci bcm_rx_starttimer(op); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/* 71262306a36Sopenharmony_ci * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_cistatic struct bcm_op *bcm_find_op(struct list_head *ops, 71562306a36Sopenharmony_ci struct bcm_msg_head *mh, int ifindex) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct bcm_op *op; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci list_for_each_entry(op, ops, list) { 72062306a36Sopenharmony_ci if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) && 72162306a36Sopenharmony_ci (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) 72262306a36Sopenharmony_ci return op; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return NULL; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void bcm_free_op_rcu(struct rcu_head *rcu_head) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if ((op->frames) && (op->frames != &op->sframe)) 73362306a36Sopenharmony_ci kfree(op->frames); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if ((op->last_frames) && (op->last_frames != &op->last_sframe)) 73662306a36Sopenharmony_ci kfree(op->last_frames); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci kfree(op); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic void bcm_remove_op(struct bcm_op *op) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 74462306a36Sopenharmony_ci hrtimer_cancel(&op->thrtimer); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci call_rcu(&op->rcu, bcm_free_op_rcu); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci if (op->rx_reg_dev == dev) { 75262306a36Sopenharmony_ci can_rx_unregister(dev_net(dev), dev, op->can_id, 75362306a36Sopenharmony_ci REGMASK(op->can_id), bcm_rx_handler, op); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* mark as removed subscription */ 75662306a36Sopenharmony_ci op->rx_reg_dev = NULL; 75762306a36Sopenharmony_ci } else 75862306a36Sopenharmony_ci printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device " 75962306a36Sopenharmony_ci "mismatch %p %p\n", op->rx_reg_dev, dev); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/* 76362306a36Sopenharmony_ci * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops) 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, 76662306a36Sopenharmony_ci int ifindex) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct bcm_op *op, *n; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci list_for_each_entry_safe(op, n, ops, list) { 77162306a36Sopenharmony_ci if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) && 77262306a36Sopenharmony_ci (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) { 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* disable automatic timer on frame reception */ 77562306a36Sopenharmony_ci op->flags |= RX_NO_AUTOTIMER; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * Don't care if we're bound or not (due to netdev 77962306a36Sopenharmony_ci * problems) can_rx_unregister() is always a save 78062306a36Sopenharmony_ci * thing to do here. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci if (op->ifindex) { 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * Only remove subscriptions that had not 78562306a36Sopenharmony_ci * been removed due to NETDEV_UNREGISTER 78662306a36Sopenharmony_ci * in bcm_notifier() 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci if (op->rx_reg_dev) { 78962306a36Sopenharmony_ci struct net_device *dev; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci dev = dev_get_by_index(sock_net(op->sk), 79262306a36Sopenharmony_ci op->ifindex); 79362306a36Sopenharmony_ci if (dev) { 79462306a36Sopenharmony_ci bcm_rx_unreg(dev, op); 79562306a36Sopenharmony_ci dev_put(dev); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci } else 79962306a36Sopenharmony_ci can_rx_unregister(sock_net(op->sk), NULL, 80062306a36Sopenharmony_ci op->can_id, 80162306a36Sopenharmony_ci REGMASK(op->can_id), 80262306a36Sopenharmony_ci bcm_rx_handler, op); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci list_del(&op->list); 80562306a36Sopenharmony_ci bcm_remove_op(op); 80662306a36Sopenharmony_ci return 1; /* done */ 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return 0; /* not found */ 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/* 81462306a36Sopenharmony_ci * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops) 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_cistatic int bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh, 81762306a36Sopenharmony_ci int ifindex) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct bcm_op *op, *n; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci list_for_each_entry_safe(op, n, ops, list) { 82262306a36Sopenharmony_ci if ((op->can_id == mh->can_id) && (op->ifindex == ifindex) && 82362306a36Sopenharmony_ci (op->flags & CAN_FD_FRAME) == (mh->flags & CAN_FD_FRAME)) { 82462306a36Sopenharmony_ci list_del(&op->list); 82562306a36Sopenharmony_ci bcm_remove_op(op); 82662306a36Sopenharmony_ci return 1; /* done */ 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; /* not found */ 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg) 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_cistatic int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head, 83762306a36Sopenharmony_ci int ifindex) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct bcm_op *op = bcm_find_op(ops, msg_head, ifindex); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (!op) 84262306a36Sopenharmony_ci return -EINVAL; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* put current values into msg_head */ 84562306a36Sopenharmony_ci msg_head->flags = op->flags; 84662306a36Sopenharmony_ci msg_head->count = op->count; 84762306a36Sopenharmony_ci msg_head->ival1 = op->ival1; 84862306a36Sopenharmony_ci msg_head->ival2 = op->ival2; 84962306a36Sopenharmony_ci msg_head->nframes = op->nframes; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci bcm_send_to_user(op, msg_head, op->frames, 0); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return MHSIZ; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci/* 85762306a36Sopenharmony_ci * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg) 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_cistatic int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 86062306a36Sopenharmony_ci int ifindex, struct sock *sk) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 86362306a36Sopenharmony_ci struct bcm_op *op; 86462306a36Sopenharmony_ci struct canfd_frame *cf; 86562306a36Sopenharmony_ci unsigned int i; 86662306a36Sopenharmony_ci int err; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* we need a real device to send frames */ 86962306a36Sopenharmony_ci if (!ifindex) 87062306a36Sopenharmony_ci return -ENODEV; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* check nframes boundaries - we need at least one CAN frame */ 87362306a36Sopenharmony_ci if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES) 87462306a36Sopenharmony_ci return -EINVAL; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* check timeval limitations */ 87762306a36Sopenharmony_ci if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head)) 87862306a36Sopenharmony_ci return -EINVAL; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* check the given can_id */ 88162306a36Sopenharmony_ci op = bcm_find_op(&bo->tx_ops, msg_head, ifindex); 88262306a36Sopenharmony_ci if (op) { 88362306a36Sopenharmony_ci /* update existing BCM operation */ 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Do we need more space for the CAN frames than currently 88762306a36Sopenharmony_ci * allocated? -> This is a _really_ unusual use-case and 88862306a36Sopenharmony_ci * therefore (complexity / locking) it is not supported. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci if (msg_head->nframes > op->nframes) 89162306a36Sopenharmony_ci return -E2BIG; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* update CAN frames content */ 89462306a36Sopenharmony_ci for (i = 0; i < msg_head->nframes; i++) { 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci cf = op->frames + op->cfsiz * i; 89762306a36Sopenharmony_ci err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (op->flags & CAN_FD_FRAME) { 90062306a36Sopenharmony_ci if (cf->len > 64) 90162306a36Sopenharmony_ci err = -EINVAL; 90262306a36Sopenharmony_ci } else { 90362306a36Sopenharmony_ci if (cf->len > 8) 90462306a36Sopenharmony_ci err = -EINVAL; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (err < 0) 90862306a36Sopenharmony_ci return err; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (msg_head->flags & TX_CP_CAN_ID) { 91162306a36Sopenharmony_ci /* copy can_id into frame */ 91262306a36Sopenharmony_ci cf->can_id = msg_head->can_id; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci op->flags = msg_head->flags; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci } else { 91862306a36Sopenharmony_ci /* insert new BCM operation for the given can_id */ 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci op = kzalloc(OPSIZ, GFP_KERNEL); 92162306a36Sopenharmony_ci if (!op) 92262306a36Sopenharmony_ci return -ENOMEM; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci op->can_id = msg_head->can_id; 92562306a36Sopenharmony_ci op->cfsiz = CFSIZ(msg_head->flags); 92662306a36Sopenharmony_ci op->flags = msg_head->flags; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* create array for CAN frames and copy the data */ 92962306a36Sopenharmony_ci if (msg_head->nframes > 1) { 93062306a36Sopenharmony_ci op->frames = kmalloc_array(msg_head->nframes, 93162306a36Sopenharmony_ci op->cfsiz, 93262306a36Sopenharmony_ci GFP_KERNEL); 93362306a36Sopenharmony_ci if (!op->frames) { 93462306a36Sopenharmony_ci kfree(op); 93562306a36Sopenharmony_ci return -ENOMEM; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci } else 93862306a36Sopenharmony_ci op->frames = &op->sframe; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci for (i = 0; i < msg_head->nframes; i++) { 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci cf = op->frames + op->cfsiz * i; 94362306a36Sopenharmony_ci err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); 94462306a36Sopenharmony_ci if (err < 0) 94562306a36Sopenharmony_ci goto free_op; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (op->flags & CAN_FD_FRAME) { 94862306a36Sopenharmony_ci if (cf->len > 64) 94962306a36Sopenharmony_ci err = -EINVAL; 95062306a36Sopenharmony_ci } else { 95162306a36Sopenharmony_ci if (cf->len > 8) 95262306a36Sopenharmony_ci err = -EINVAL; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (err < 0) 95662306a36Sopenharmony_ci goto free_op; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (msg_head->flags & TX_CP_CAN_ID) { 95962306a36Sopenharmony_ci /* copy can_id into frame */ 96062306a36Sopenharmony_ci cf->can_id = msg_head->can_id; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* tx_ops never compare with previous received messages */ 96562306a36Sopenharmony_ci op->last_frames = NULL; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* bcm_can_tx / bcm_tx_timeout_handler needs this */ 96862306a36Sopenharmony_ci op->sk = sk; 96962306a36Sopenharmony_ci op->ifindex = ifindex; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* initialize uninitialized (kzalloc) structure */ 97262306a36Sopenharmony_ci hrtimer_init(&op->timer, CLOCK_MONOTONIC, 97362306a36Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 97462306a36Sopenharmony_ci op->timer.function = bcm_tx_timeout_handler; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* currently unused in tx_ops */ 97762306a36Sopenharmony_ci hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, 97862306a36Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* add this bcm_op to the list of the tx_ops */ 98162306a36Sopenharmony_ci list_add(&op->list, &bo->tx_ops); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */ 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (op->nframes != msg_head->nframes) { 98662306a36Sopenharmony_ci op->nframes = msg_head->nframes; 98762306a36Sopenharmony_ci /* start multiple frame transmission with index 0 */ 98862306a36Sopenharmony_ci op->currframe = 0; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* check flags */ 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (op->flags & TX_RESET_MULTI_IDX) { 99462306a36Sopenharmony_ci /* start multiple frame transmission with index 0 */ 99562306a36Sopenharmony_ci op->currframe = 0; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (op->flags & SETTIMER) { 99962306a36Sopenharmony_ci /* set timer values */ 100062306a36Sopenharmony_ci op->count = msg_head->count; 100162306a36Sopenharmony_ci op->ival1 = msg_head->ival1; 100262306a36Sopenharmony_ci op->ival2 = msg_head->ival2; 100362306a36Sopenharmony_ci op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); 100462306a36Sopenharmony_ci op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* disable an active timer due to zero values? */ 100762306a36Sopenharmony_ci if (!op->kt_ival1 && !op->kt_ival2) 100862306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (op->flags & STARTTIMER) { 101262306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 101362306a36Sopenharmony_ci /* spec: send CAN frame when starting timer */ 101462306a36Sopenharmony_ci op->flags |= TX_ANNOUNCE; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (op->flags & TX_ANNOUNCE) { 101862306a36Sopenharmony_ci bcm_can_tx(op); 101962306a36Sopenharmony_ci if (op->count) 102062306a36Sopenharmony_ci op->count--; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (op->flags & STARTTIMER) 102462306a36Sopenharmony_ci bcm_tx_start_timer(op); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return msg_head->nframes * op->cfsiz + MHSIZ; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cifree_op: 102962306a36Sopenharmony_ci if (op->frames != &op->sframe) 103062306a36Sopenharmony_ci kfree(op->frames); 103162306a36Sopenharmony_ci kfree(op); 103262306a36Sopenharmony_ci return err; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci/* 103662306a36Sopenharmony_ci * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg) 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_cistatic int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 103962306a36Sopenharmony_ci int ifindex, struct sock *sk) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 104262306a36Sopenharmony_ci struct bcm_op *op; 104362306a36Sopenharmony_ci int do_rx_register; 104462306a36Sopenharmony_ci int err = 0; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { 104762306a36Sopenharmony_ci /* be robust against wrong usage ... */ 104862306a36Sopenharmony_ci msg_head->flags |= RX_FILTER_ID; 104962306a36Sopenharmony_ci /* ignore trailing garbage */ 105062306a36Sopenharmony_ci msg_head->nframes = 0; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* the first element contains the mux-mask => MAX_NFRAMES + 1 */ 105462306a36Sopenharmony_ci if (msg_head->nframes > MAX_NFRAMES + 1) 105562306a36Sopenharmony_ci return -EINVAL; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if ((msg_head->flags & RX_RTR_FRAME) && 105862306a36Sopenharmony_ci ((msg_head->nframes != 1) || 105962306a36Sopenharmony_ci (!(msg_head->can_id & CAN_RTR_FLAG)))) 106062306a36Sopenharmony_ci return -EINVAL; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* check timeval limitations */ 106362306a36Sopenharmony_ci if ((msg_head->flags & SETTIMER) && bcm_is_invalid_tv(msg_head)) 106462306a36Sopenharmony_ci return -EINVAL; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* check the given can_id */ 106762306a36Sopenharmony_ci op = bcm_find_op(&bo->rx_ops, msg_head, ifindex); 106862306a36Sopenharmony_ci if (op) { 106962306a36Sopenharmony_ci /* update existing BCM operation */ 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* 107262306a36Sopenharmony_ci * Do we need more space for the CAN frames than currently 107362306a36Sopenharmony_ci * allocated? -> This is a _really_ unusual use-case and 107462306a36Sopenharmony_ci * therefore (complexity / locking) it is not supported. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci if (msg_head->nframes > op->nframes) 107762306a36Sopenharmony_ci return -E2BIG; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (msg_head->nframes) { 108062306a36Sopenharmony_ci /* update CAN frames content */ 108162306a36Sopenharmony_ci err = memcpy_from_msg(op->frames, msg, 108262306a36Sopenharmony_ci msg_head->nframes * op->cfsiz); 108362306a36Sopenharmony_ci if (err < 0) 108462306a36Sopenharmony_ci return err; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* clear last_frames to indicate 'nothing received' */ 108762306a36Sopenharmony_ci memset(op->last_frames, 0, msg_head->nframes * op->cfsiz); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci op->nframes = msg_head->nframes; 109162306a36Sopenharmony_ci op->flags = msg_head->flags; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Only an update -> do not call can_rx_register() */ 109462306a36Sopenharmony_ci do_rx_register = 0; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci } else { 109762306a36Sopenharmony_ci /* insert new BCM operation for the given can_id */ 109862306a36Sopenharmony_ci op = kzalloc(OPSIZ, GFP_KERNEL); 109962306a36Sopenharmony_ci if (!op) 110062306a36Sopenharmony_ci return -ENOMEM; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci op->can_id = msg_head->can_id; 110362306a36Sopenharmony_ci op->nframes = msg_head->nframes; 110462306a36Sopenharmony_ci op->cfsiz = CFSIZ(msg_head->flags); 110562306a36Sopenharmony_ci op->flags = msg_head->flags; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (msg_head->nframes > 1) { 110862306a36Sopenharmony_ci /* create array for CAN frames and copy the data */ 110962306a36Sopenharmony_ci op->frames = kmalloc_array(msg_head->nframes, 111062306a36Sopenharmony_ci op->cfsiz, 111162306a36Sopenharmony_ci GFP_KERNEL); 111262306a36Sopenharmony_ci if (!op->frames) { 111362306a36Sopenharmony_ci kfree(op); 111462306a36Sopenharmony_ci return -ENOMEM; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* create and init array for received CAN frames */ 111862306a36Sopenharmony_ci op->last_frames = kcalloc(msg_head->nframes, 111962306a36Sopenharmony_ci op->cfsiz, 112062306a36Sopenharmony_ci GFP_KERNEL); 112162306a36Sopenharmony_ci if (!op->last_frames) { 112262306a36Sopenharmony_ci kfree(op->frames); 112362306a36Sopenharmony_ci kfree(op); 112462306a36Sopenharmony_ci return -ENOMEM; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci } else { 112862306a36Sopenharmony_ci op->frames = &op->sframe; 112962306a36Sopenharmony_ci op->last_frames = &op->last_sframe; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (msg_head->nframes) { 113362306a36Sopenharmony_ci err = memcpy_from_msg(op->frames, msg, 113462306a36Sopenharmony_ci msg_head->nframes * op->cfsiz); 113562306a36Sopenharmony_ci if (err < 0) { 113662306a36Sopenharmony_ci if (op->frames != &op->sframe) 113762306a36Sopenharmony_ci kfree(op->frames); 113862306a36Sopenharmony_ci if (op->last_frames != &op->last_sframe) 113962306a36Sopenharmony_ci kfree(op->last_frames); 114062306a36Sopenharmony_ci kfree(op); 114162306a36Sopenharmony_ci return err; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* bcm_can_tx / bcm_tx_timeout_handler needs this */ 114662306a36Sopenharmony_ci op->sk = sk; 114762306a36Sopenharmony_ci op->ifindex = ifindex; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* ifindex for timeout events w/o previous frame reception */ 115062306a36Sopenharmony_ci op->rx_ifindex = ifindex; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* initialize uninitialized (kzalloc) structure */ 115362306a36Sopenharmony_ci hrtimer_init(&op->timer, CLOCK_MONOTONIC, 115462306a36Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 115562306a36Sopenharmony_ci op->timer.function = bcm_rx_timeout_handler; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, 115862306a36Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 115962306a36Sopenharmony_ci op->thrtimer.function = bcm_rx_thr_handler; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* add this bcm_op to the list of the rx_ops */ 116262306a36Sopenharmony_ci list_add(&op->list, &bo->rx_ops); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* call can_rx_register() */ 116562306a36Sopenharmony_ci do_rx_register = 1; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */ 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* check flags */ 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (op->flags & RX_RTR_FRAME) { 117262306a36Sopenharmony_ci struct canfd_frame *frame0 = op->frames; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* no timers in RTR-mode */ 117562306a36Sopenharmony_ci hrtimer_cancel(&op->thrtimer); 117662306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* 117962306a36Sopenharmony_ci * funny feature in RX(!)_SETUP only for RTR-mode: 118062306a36Sopenharmony_ci * copy can_id into frame BUT without RTR-flag to 118162306a36Sopenharmony_ci * prevent a full-load-loopback-test ... ;-] 118262306a36Sopenharmony_ci */ 118362306a36Sopenharmony_ci if ((op->flags & TX_CP_CAN_ID) || 118462306a36Sopenharmony_ci (frame0->can_id == op->can_id)) 118562306a36Sopenharmony_ci frame0->can_id = op->can_id & ~CAN_RTR_FLAG; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci if (op->flags & SETTIMER) { 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* set timer value */ 119162306a36Sopenharmony_ci op->ival1 = msg_head->ival1; 119262306a36Sopenharmony_ci op->ival2 = msg_head->ival2; 119362306a36Sopenharmony_ci op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1); 119462306a36Sopenharmony_ci op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* disable an active timer due to zero value? */ 119762306a36Sopenharmony_ci if (!op->kt_ival1) 119862306a36Sopenharmony_ci hrtimer_cancel(&op->timer); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci * In any case cancel the throttle timer, flush 120262306a36Sopenharmony_ci * potentially blocked msgs and reset throttle handling 120362306a36Sopenharmony_ci */ 120462306a36Sopenharmony_ci op->kt_lastmsg = 0; 120562306a36Sopenharmony_ci hrtimer_cancel(&op->thrtimer); 120662306a36Sopenharmony_ci bcm_rx_thr_flush(op); 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if ((op->flags & STARTTIMER) && op->kt_ival1) 121062306a36Sopenharmony_ci hrtimer_start(&op->timer, op->kt_ival1, 121162306a36Sopenharmony_ci HRTIMER_MODE_REL_SOFT); 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* now we can register for can_ids, if we added a new bcm_op */ 121562306a36Sopenharmony_ci if (do_rx_register) { 121662306a36Sopenharmony_ci if (ifindex) { 121762306a36Sopenharmony_ci struct net_device *dev; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci dev = dev_get_by_index(sock_net(sk), ifindex); 122062306a36Sopenharmony_ci if (dev) { 122162306a36Sopenharmony_ci err = can_rx_register(sock_net(sk), dev, 122262306a36Sopenharmony_ci op->can_id, 122362306a36Sopenharmony_ci REGMASK(op->can_id), 122462306a36Sopenharmony_ci bcm_rx_handler, op, 122562306a36Sopenharmony_ci "bcm", sk); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci op->rx_reg_dev = dev; 122862306a36Sopenharmony_ci dev_put(dev); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci } else 123262306a36Sopenharmony_ci err = can_rx_register(sock_net(sk), NULL, op->can_id, 123362306a36Sopenharmony_ci REGMASK(op->can_id), 123462306a36Sopenharmony_ci bcm_rx_handler, op, "bcm", sk); 123562306a36Sopenharmony_ci if (err) { 123662306a36Sopenharmony_ci /* this bcm rx op is broken -> remove it */ 123762306a36Sopenharmony_ci list_del(&op->list); 123862306a36Sopenharmony_ci bcm_remove_op(op); 123962306a36Sopenharmony_ci return err; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci return msg_head->nframes * op->cfsiz + MHSIZ; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci/* 124762306a36Sopenharmony_ci * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg) 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_cistatic int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, 125062306a36Sopenharmony_ci int cfsiz) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci struct sk_buff *skb; 125362306a36Sopenharmony_ci struct net_device *dev; 125462306a36Sopenharmony_ci int err; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* we need a real device to send frames */ 125762306a36Sopenharmony_ci if (!ifindex) 125862306a36Sopenharmony_ci return -ENODEV; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci skb = alloc_skb(cfsiz + sizeof(struct can_skb_priv), GFP_KERNEL); 126162306a36Sopenharmony_ci if (!skb) 126262306a36Sopenharmony_ci return -ENOMEM; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci can_skb_reserve(skb); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci err = memcpy_from_msg(skb_put(skb, cfsiz), msg, cfsiz); 126762306a36Sopenharmony_ci if (err < 0) { 126862306a36Sopenharmony_ci kfree_skb(skb); 126962306a36Sopenharmony_ci return err; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci dev = dev_get_by_index(sock_net(sk), ifindex); 127362306a36Sopenharmony_ci if (!dev) { 127462306a36Sopenharmony_ci kfree_skb(skb); 127562306a36Sopenharmony_ci return -ENODEV; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci can_skb_prv(skb)->ifindex = dev->ifindex; 127962306a36Sopenharmony_ci can_skb_prv(skb)->skbcnt = 0; 128062306a36Sopenharmony_ci skb->dev = dev; 128162306a36Sopenharmony_ci can_skb_set_owner(skb, sk); 128262306a36Sopenharmony_ci err = can_send(skb, 1); /* send with loopback */ 128362306a36Sopenharmony_ci dev_put(dev); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (err) 128662306a36Sopenharmony_ci return err; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci return cfsiz + MHSIZ; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci/* 129262306a36Sopenharmony_ci * bcm_sendmsg - process BCM commands (opcodes) from the userspace 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_cistatic int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci struct sock *sk = sock->sk; 129762306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 129862306a36Sopenharmony_ci int ifindex = bo->ifindex; /* default ifindex for this bcm_op */ 129962306a36Sopenharmony_ci struct bcm_msg_head msg_head; 130062306a36Sopenharmony_ci int cfsiz; 130162306a36Sopenharmony_ci int ret; /* read bytes or error codes as return value */ 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (!bo->bound) 130462306a36Sopenharmony_ci return -ENOTCONN; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* check for valid message length from userspace */ 130762306a36Sopenharmony_ci if (size < MHSIZ) 130862306a36Sopenharmony_ci return -EINVAL; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* read message head information */ 131162306a36Sopenharmony_ci ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ); 131262306a36Sopenharmony_ci if (ret < 0) 131362306a36Sopenharmony_ci return ret; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci cfsiz = CFSIZ(msg_head.flags); 131662306a36Sopenharmony_ci if ((size - MHSIZ) % cfsiz) 131762306a36Sopenharmony_ci return -EINVAL; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* check for alternative ifindex for this bcm_op */ 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (!ifindex && msg->msg_name) { 132262306a36Sopenharmony_ci /* no bound device as default => check msg_name */ 132362306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (msg->msg_namelen < BCM_MIN_NAMELEN) 132662306a36Sopenharmony_ci return -EINVAL; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (addr->can_family != AF_CAN) 132962306a36Sopenharmony_ci return -EINVAL; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci /* ifindex from sendto() */ 133262306a36Sopenharmony_ci ifindex = addr->can_ifindex; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if (ifindex) { 133562306a36Sopenharmony_ci struct net_device *dev; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci dev = dev_get_by_index(sock_net(sk), ifindex); 133862306a36Sopenharmony_ci if (!dev) 133962306a36Sopenharmony_ci return -ENODEV; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci if (dev->type != ARPHRD_CAN) { 134262306a36Sopenharmony_ci dev_put(dev); 134362306a36Sopenharmony_ci return -ENODEV; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci dev_put(dev); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci lock_sock(sk); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci switch (msg_head.opcode) { 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci case TX_SETUP: 135562306a36Sopenharmony_ci ret = bcm_tx_setup(&msg_head, msg, ifindex, sk); 135662306a36Sopenharmony_ci break; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci case RX_SETUP: 135962306a36Sopenharmony_ci ret = bcm_rx_setup(&msg_head, msg, ifindex, sk); 136062306a36Sopenharmony_ci break; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci case TX_DELETE: 136362306a36Sopenharmony_ci if (bcm_delete_tx_op(&bo->tx_ops, &msg_head, ifindex)) 136462306a36Sopenharmony_ci ret = MHSIZ; 136562306a36Sopenharmony_ci else 136662306a36Sopenharmony_ci ret = -EINVAL; 136762306a36Sopenharmony_ci break; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci case RX_DELETE: 137062306a36Sopenharmony_ci if (bcm_delete_rx_op(&bo->rx_ops, &msg_head, ifindex)) 137162306a36Sopenharmony_ci ret = MHSIZ; 137262306a36Sopenharmony_ci else 137362306a36Sopenharmony_ci ret = -EINVAL; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci case TX_READ: 137762306a36Sopenharmony_ci /* reuse msg_head for the reply to TX_READ */ 137862306a36Sopenharmony_ci msg_head.opcode = TX_STATUS; 137962306a36Sopenharmony_ci ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex); 138062306a36Sopenharmony_ci break; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci case RX_READ: 138362306a36Sopenharmony_ci /* reuse msg_head for the reply to RX_READ */ 138462306a36Sopenharmony_ci msg_head.opcode = RX_STATUS; 138562306a36Sopenharmony_ci ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex); 138662306a36Sopenharmony_ci break; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci case TX_SEND: 138962306a36Sopenharmony_ci /* we need exactly one CAN frame behind the msg head */ 139062306a36Sopenharmony_ci if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ)) 139162306a36Sopenharmony_ci ret = -EINVAL; 139262306a36Sopenharmony_ci else 139362306a36Sopenharmony_ci ret = bcm_tx_send(msg, ifindex, sk, cfsiz); 139462306a36Sopenharmony_ci break; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci default: 139762306a36Sopenharmony_ci ret = -EINVAL; 139862306a36Sopenharmony_ci break; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci release_sock(sk); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci return ret; 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci/* 140762306a36Sopenharmony_ci * notification handler for netdevice status changes 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_cistatic void bcm_notify(struct bcm_sock *bo, unsigned long msg, 141062306a36Sopenharmony_ci struct net_device *dev) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct sock *sk = &bo->sk; 141362306a36Sopenharmony_ci struct bcm_op *op; 141462306a36Sopenharmony_ci int notify_enodev = 0; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (!net_eq(dev_net(dev), sock_net(sk))) 141762306a36Sopenharmony_ci return; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci switch (msg) { 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci case NETDEV_UNREGISTER: 142262306a36Sopenharmony_ci lock_sock(sk); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* remove device specific receive entries */ 142562306a36Sopenharmony_ci list_for_each_entry(op, &bo->rx_ops, list) 142662306a36Sopenharmony_ci if (op->rx_reg_dev == dev) 142762306a36Sopenharmony_ci bcm_rx_unreg(dev, op); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* remove device reference, if this is our bound device */ 143062306a36Sopenharmony_ci if (bo->bound && bo->ifindex == dev->ifindex) { 143162306a36Sopenharmony_ci bo->bound = 0; 143262306a36Sopenharmony_ci bo->ifindex = 0; 143362306a36Sopenharmony_ci notify_enodev = 1; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci release_sock(sk); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (notify_enodev) { 143962306a36Sopenharmony_ci sk->sk_err = ENODEV; 144062306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 144162306a36Sopenharmony_ci sk_error_report(sk); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci break; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci case NETDEV_DOWN: 144662306a36Sopenharmony_ci if (bo->bound && bo->ifindex == dev->ifindex) { 144762306a36Sopenharmony_ci sk->sk_err = ENETDOWN; 144862306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 144962306a36Sopenharmony_ci sk_error_report(sk); 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int bcm_notifier(struct notifier_block *nb, unsigned long msg, 145562306a36Sopenharmony_ci void *ptr) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (dev->type != ARPHRD_CAN) 146062306a36Sopenharmony_ci return NOTIFY_DONE; 146162306a36Sopenharmony_ci if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN) 146262306a36Sopenharmony_ci return NOTIFY_DONE; 146362306a36Sopenharmony_ci if (unlikely(bcm_busy_notifier)) /* Check for reentrant bug. */ 146462306a36Sopenharmony_ci return NOTIFY_DONE; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci spin_lock(&bcm_notifier_lock); 146762306a36Sopenharmony_ci list_for_each_entry(bcm_busy_notifier, &bcm_notifier_list, notifier) { 146862306a36Sopenharmony_ci spin_unlock(&bcm_notifier_lock); 146962306a36Sopenharmony_ci bcm_notify(bcm_busy_notifier, msg, dev); 147062306a36Sopenharmony_ci spin_lock(&bcm_notifier_lock); 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci bcm_busy_notifier = NULL; 147362306a36Sopenharmony_ci spin_unlock(&bcm_notifier_lock); 147462306a36Sopenharmony_ci return NOTIFY_DONE; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci/* 147862306a36Sopenharmony_ci * initial settings for all BCM sockets to be set at socket creation time 147962306a36Sopenharmony_ci */ 148062306a36Sopenharmony_cistatic int bcm_init(struct sock *sk) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci bo->bound = 0; 148562306a36Sopenharmony_ci bo->ifindex = 0; 148662306a36Sopenharmony_ci bo->dropped_usr_msgs = 0; 148762306a36Sopenharmony_ci bo->bcm_proc_read = NULL; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci INIT_LIST_HEAD(&bo->tx_ops); 149062306a36Sopenharmony_ci INIT_LIST_HEAD(&bo->rx_ops); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* set notifier */ 149362306a36Sopenharmony_ci spin_lock(&bcm_notifier_lock); 149462306a36Sopenharmony_ci list_add_tail(&bo->notifier, &bcm_notifier_list); 149562306a36Sopenharmony_ci spin_unlock(&bcm_notifier_lock); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci/* 150162306a36Sopenharmony_ci * standard socket functions 150262306a36Sopenharmony_ci */ 150362306a36Sopenharmony_cistatic int bcm_release(struct socket *sock) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct sock *sk = sock->sk; 150662306a36Sopenharmony_ci struct net *net; 150762306a36Sopenharmony_ci struct bcm_sock *bo; 150862306a36Sopenharmony_ci struct bcm_op *op, *next; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (!sk) 151162306a36Sopenharmony_ci return 0; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci net = sock_net(sk); 151462306a36Sopenharmony_ci bo = bcm_sk(sk); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* remove bcm_ops, timer, rx_unregister(), etc. */ 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci spin_lock(&bcm_notifier_lock); 151962306a36Sopenharmony_ci while (bcm_busy_notifier == bo) { 152062306a36Sopenharmony_ci spin_unlock(&bcm_notifier_lock); 152162306a36Sopenharmony_ci schedule_timeout_uninterruptible(1); 152262306a36Sopenharmony_ci spin_lock(&bcm_notifier_lock); 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci list_del(&bo->notifier); 152562306a36Sopenharmony_ci spin_unlock(&bcm_notifier_lock); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci lock_sock(sk); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS) 153062306a36Sopenharmony_ci /* remove procfs entry */ 153162306a36Sopenharmony_ci if (net->can.bcmproc_dir && bo->bcm_proc_read) 153262306a36Sopenharmony_ci remove_proc_entry(bo->procname, net->can.bcmproc_dir); 153362306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci list_for_each_entry_safe(op, next, &bo->tx_ops, list) 153662306a36Sopenharmony_ci bcm_remove_op(op); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci list_for_each_entry_safe(op, next, &bo->rx_ops, list) { 153962306a36Sopenharmony_ci /* 154062306a36Sopenharmony_ci * Don't care if we're bound or not (due to netdev problems) 154162306a36Sopenharmony_ci * can_rx_unregister() is always a save thing to do here. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ci if (op->ifindex) { 154462306a36Sopenharmony_ci /* 154562306a36Sopenharmony_ci * Only remove subscriptions that had not 154662306a36Sopenharmony_ci * been removed due to NETDEV_UNREGISTER 154762306a36Sopenharmony_ci * in bcm_notifier() 154862306a36Sopenharmony_ci */ 154962306a36Sopenharmony_ci if (op->rx_reg_dev) { 155062306a36Sopenharmony_ci struct net_device *dev; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci dev = dev_get_by_index(net, op->ifindex); 155362306a36Sopenharmony_ci if (dev) { 155462306a36Sopenharmony_ci bcm_rx_unreg(dev, op); 155562306a36Sopenharmony_ci dev_put(dev); 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci } else 155962306a36Sopenharmony_ci can_rx_unregister(net, NULL, op->can_id, 156062306a36Sopenharmony_ci REGMASK(op->can_id), 156162306a36Sopenharmony_ci bcm_rx_handler, op); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci synchronize_rcu(); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci list_for_each_entry_safe(op, next, &bo->rx_ops, list) 156862306a36Sopenharmony_ci bcm_remove_op(op); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci /* remove device reference */ 157162306a36Sopenharmony_ci if (bo->bound) { 157262306a36Sopenharmony_ci bo->bound = 0; 157362306a36Sopenharmony_ci bo->ifindex = 0; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci sock_orphan(sk); 157762306a36Sopenharmony_ci sock->sk = NULL; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci release_sock(sk); 158062306a36Sopenharmony_ci sock_put(sk); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci return 0; 158362306a36Sopenharmony_ci} 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cistatic int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, 158662306a36Sopenharmony_ci int flags) 158762306a36Sopenharmony_ci{ 158862306a36Sopenharmony_ci struct sockaddr_can *addr = (struct sockaddr_can *)uaddr; 158962306a36Sopenharmony_ci struct sock *sk = sock->sk; 159062306a36Sopenharmony_ci struct bcm_sock *bo = bcm_sk(sk); 159162306a36Sopenharmony_ci struct net *net = sock_net(sk); 159262306a36Sopenharmony_ci int ret = 0; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (len < BCM_MIN_NAMELEN) 159562306a36Sopenharmony_ci return -EINVAL; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci lock_sock(sk); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (bo->bound) { 160062306a36Sopenharmony_ci ret = -EISCONN; 160162306a36Sopenharmony_ci goto fail; 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* bind a device to this socket */ 160562306a36Sopenharmony_ci if (addr->can_ifindex) { 160662306a36Sopenharmony_ci struct net_device *dev; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci dev = dev_get_by_index(net, addr->can_ifindex); 160962306a36Sopenharmony_ci if (!dev) { 161062306a36Sopenharmony_ci ret = -ENODEV; 161162306a36Sopenharmony_ci goto fail; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci if (dev->type != ARPHRD_CAN) { 161462306a36Sopenharmony_ci dev_put(dev); 161562306a36Sopenharmony_ci ret = -ENODEV; 161662306a36Sopenharmony_ci goto fail; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci bo->ifindex = dev->ifindex; 162062306a36Sopenharmony_ci dev_put(dev); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci } else { 162362306a36Sopenharmony_ci /* no interface reference for ifindex = 0 ('any' CAN device) */ 162462306a36Sopenharmony_ci bo->ifindex = 0; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS) 162862306a36Sopenharmony_ci if (net->can.bcmproc_dir) { 162962306a36Sopenharmony_ci /* unique socket address as filename */ 163062306a36Sopenharmony_ci sprintf(bo->procname, "%lu", sock_i_ino(sk)); 163162306a36Sopenharmony_ci bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644, 163262306a36Sopenharmony_ci net->can.bcmproc_dir, 163362306a36Sopenharmony_ci bcm_proc_show, sk); 163462306a36Sopenharmony_ci if (!bo->bcm_proc_read) { 163562306a36Sopenharmony_ci ret = -ENOMEM; 163662306a36Sopenharmony_ci goto fail; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci bo->bound = 1; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cifail: 164462306a36Sopenharmony_ci release_sock(sk); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci return ret; 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_cistatic int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, 165062306a36Sopenharmony_ci int flags) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci struct sock *sk = sock->sk; 165362306a36Sopenharmony_ci struct sk_buff *skb; 165462306a36Sopenharmony_ci int error = 0; 165562306a36Sopenharmony_ci int err; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &error); 165862306a36Sopenharmony_ci if (!skb) 165962306a36Sopenharmony_ci return error; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (skb->len < size) 166262306a36Sopenharmony_ci size = skb->len; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci err = memcpy_to_msg(msg, skb->data, size); 166562306a36Sopenharmony_ci if (err < 0) { 166662306a36Sopenharmony_ci skb_free_datagram(sk, skb); 166762306a36Sopenharmony_ci return err; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci sock_recv_cmsgs(msg, sk, skb); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (msg->msg_name) { 167362306a36Sopenharmony_ci __sockaddr_check_size(BCM_MIN_NAMELEN); 167462306a36Sopenharmony_ci msg->msg_namelen = BCM_MIN_NAMELEN; 167562306a36Sopenharmony_ci memcpy(msg->msg_name, skb->cb, msg->msg_namelen); 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci skb_free_datagram(sk, skb); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci return size; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic int bcm_sock_no_ioctlcmd(struct socket *sock, unsigned int cmd, 168462306a36Sopenharmony_ci unsigned long arg) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci /* no ioctls for socket layer -> hand it down to NIC layer */ 168762306a36Sopenharmony_ci return -ENOIOCTLCMD; 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic const struct proto_ops bcm_ops = { 169162306a36Sopenharmony_ci .family = PF_CAN, 169262306a36Sopenharmony_ci .release = bcm_release, 169362306a36Sopenharmony_ci .bind = sock_no_bind, 169462306a36Sopenharmony_ci .connect = bcm_connect, 169562306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 169662306a36Sopenharmony_ci .accept = sock_no_accept, 169762306a36Sopenharmony_ci .getname = sock_no_getname, 169862306a36Sopenharmony_ci .poll = datagram_poll, 169962306a36Sopenharmony_ci .ioctl = bcm_sock_no_ioctlcmd, 170062306a36Sopenharmony_ci .gettstamp = sock_gettstamp, 170162306a36Sopenharmony_ci .listen = sock_no_listen, 170262306a36Sopenharmony_ci .shutdown = sock_no_shutdown, 170362306a36Sopenharmony_ci .sendmsg = bcm_sendmsg, 170462306a36Sopenharmony_ci .recvmsg = bcm_recvmsg, 170562306a36Sopenharmony_ci .mmap = sock_no_mmap, 170662306a36Sopenharmony_ci}; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic struct proto bcm_proto __read_mostly = { 170962306a36Sopenharmony_ci .name = "CAN_BCM", 171062306a36Sopenharmony_ci .owner = THIS_MODULE, 171162306a36Sopenharmony_ci .obj_size = sizeof(struct bcm_sock), 171262306a36Sopenharmony_ci .init = bcm_init, 171362306a36Sopenharmony_ci}; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic const struct can_proto bcm_can_proto = { 171662306a36Sopenharmony_ci .type = SOCK_DGRAM, 171762306a36Sopenharmony_ci .protocol = CAN_BCM, 171862306a36Sopenharmony_ci .ops = &bcm_ops, 171962306a36Sopenharmony_ci .prot = &bcm_proto, 172062306a36Sopenharmony_ci}; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_cistatic int canbcm_pernet_init(struct net *net) 172362306a36Sopenharmony_ci{ 172462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS) 172562306a36Sopenharmony_ci /* create /proc/net/can-bcm directory */ 172662306a36Sopenharmony_ci net->can.bcmproc_dir = proc_net_mkdir(net, "can-bcm", net->proc_net); 172762306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return 0; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void canbcm_pernet_exit(struct net *net) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PROC_FS) 173562306a36Sopenharmony_ci /* remove /proc/net/can-bcm directory */ 173662306a36Sopenharmony_ci if (net->can.bcmproc_dir) 173762306a36Sopenharmony_ci remove_proc_entry("can-bcm", net->proc_net); 173862306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_cistatic struct pernet_operations canbcm_pernet_ops __read_mostly = { 174262306a36Sopenharmony_ci .init = canbcm_pernet_init, 174362306a36Sopenharmony_ci .exit = canbcm_pernet_exit, 174462306a36Sopenharmony_ci}; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic struct notifier_block canbcm_notifier = { 174762306a36Sopenharmony_ci .notifier_call = bcm_notifier 174862306a36Sopenharmony_ci}; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic int __init bcm_module_init(void) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci int err; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci pr_info("can: broadcast manager protocol\n"); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci err = register_pernet_subsys(&canbcm_pernet_ops); 175762306a36Sopenharmony_ci if (err) 175862306a36Sopenharmony_ci return err; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci err = register_netdevice_notifier(&canbcm_notifier); 176162306a36Sopenharmony_ci if (err) 176262306a36Sopenharmony_ci goto register_notifier_failed; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci err = can_proto_register(&bcm_can_proto); 176562306a36Sopenharmony_ci if (err < 0) { 176662306a36Sopenharmony_ci printk(KERN_ERR "can: registration of bcm protocol failed\n"); 176762306a36Sopenharmony_ci goto register_proto_failed; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci return 0; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ciregister_proto_failed: 177362306a36Sopenharmony_ci unregister_netdevice_notifier(&canbcm_notifier); 177462306a36Sopenharmony_ciregister_notifier_failed: 177562306a36Sopenharmony_ci unregister_pernet_subsys(&canbcm_pernet_ops); 177662306a36Sopenharmony_ci return err; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic void __exit bcm_module_exit(void) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci can_proto_unregister(&bcm_can_proto); 178262306a36Sopenharmony_ci unregister_netdevice_notifier(&canbcm_notifier); 178362306a36Sopenharmony_ci unregister_pernet_subsys(&canbcm_pernet_ops); 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cimodule_init(bcm_module_init); 178762306a36Sopenharmony_cimodule_exit(bcm_module_exit); 1788