// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content * * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Volkswagen nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * Alternatively, provided that this notice is retained in full, this * software may be distributed under the terms of the GNU General * Public License ("GPL") version 2, in which case the provisions of the * GPL apply INSTEAD OF those given above. * * The provided data structures and external interfaces from this code * are not restricted to be used by modules with a GPL compatible license. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. *
*/
/* * To send multiple CAN frame content within TX_SETUP or to filter * CAN messages with multiplex index within RX_SETUP, the number of * different filters is limited to 256 due to the one byte index value.
*/ #define MAX_NFRAMES 256
/* limit timers to 400 days for sending/timeouts */ #define BCM_TIMER_SEC_MAX (400 * 24 * 60 * 60)
/* use of last_frames[index].flags */ #define RX_LOCAL 0x10 /* frame was created on the local host */ #define RX_OWN 0x20 /* frame was sent via the socket it was received on */ #define RX_RECV 0x40 /* received data for this element */ #define RX_THR 0x80 /* element not been sent due to throttle feature */ #define BCM_CAN_FLAGS_MASK 0x0F /* to clean private flags after usage */
/* get best masking value for can_rx_register() for a given single can_id */ #define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
(CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
(CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
/* * easy access to the first 64 bit of can(fd)_frame payload. cp->data is * 64 bit aligned so the offset has to be multiples of 8 which is ensured * by the only callers in bcm_rx_cmp_to_index() bcm_rx_handler().
*/ staticinline u64 get_u64(conststruct canfd_frame *cp, int offset)
{ return *(u64 *)(cp->data + offset);
}
/* Return pointer to store the extra msg flags for bcm_recvmsg(). * We use the space of one unsigned int beyond the 'struct sockaddr_can' * in skb->cb.
*/ staticinlineunsignedint *bcm_flags(struct sk_buff *skb)
{ /* return pointer after struct sockaddr_can */ return (unsignedint *)(&((struct sockaddr_can *)skb->cb)[1]);
}
if (op->kt_ival1)
seq_printf(m, "t1=%lld ",
(longlong)ktime_to_us(op->kt_ival1));
if (op->kt_ival2)
seq_printf(m, "t2=%lld ",
(longlong)ktime_to_us(op->kt_ival2));
seq_printf(m, "# sent %ld\n", op->frames_abs);
}
seq_putc(m, '\n');
rcu_read_unlock();
return 0;
} #endif/* CONFIG_PROC_FS */
/* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op
*/ staticvoid bcm_can_tx(struct bcm_op *op)
{ struct sk_buff *skb; struct net_device *dev; struct canfd_frame *cf; int err;
/* no target device? => exit */ if (!op->ifindex) return;
if (head->nframes) { /* CAN frames starting here */
firstframe = (struct canfd_frame *)skb_tail_pointer(skb);
skb_put_data(skb, frames, datalen);
/* * the BCM uses the flags-element of the canfd_frame * structure for internal purposes. This is only * relevant for updates that are generated by the * BCM, where nframes is 1
*/ if (head->nframes == 1) { if (firstframe->flags & RX_LOCAL)
*pflags |= MSG_DONTROUTE; if (firstframe->flags & RX_OWN)
*pflags |= MSG_CONFIRM;
/* * Put the datagram to the queue so that bcm_recvmsg() can * get it from there. We need to pass the interface index to * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb * containing the interface index.
*/
/* * bcm_rx_update_and_send - process a detected relevant receive content change * 1. update the last received data * 2. send a notification to the user (if possible)
*/ staticvoid bcm_rx_update_and_send(struct bcm_op *op, struct canfd_frame *lastdata, conststruct canfd_frame *rxdata, unsignedchar traffic_flags)
{
memcpy(lastdata, rxdata, op->cfsiz);
/* mark as used and throttled by default */
lastdata->flags |= (RX_RECV|RX_THR);
/* throttling mode inactive ? */ if (!op->kt_ival2) { /* send RX_CHANGED to the user immediately */
bcm_rx_changed(op, lastdata); return;
}
/* with active throttling timer we are just done here */ if (hrtimer_active(&op->thrtimer)) return;
/* first reception with enabled throttling mode */ if (!op->kt_lastmsg) goto rx_changed_settime;
/* got a second frame inside a potential throttle period? */ if (ktime_us_delta(ktime_get(), op->kt_lastmsg) <
ktime_to_us(op->kt_ival2)) { /* do not send the saved data - only start throttle timer */
hrtimer_start(&op->thrtimer,
ktime_add(op->kt_lastmsg, op->kt_ival2),
HRTIMER_MODE_ABS_SOFT); return;
}
/* the gap was that big, that throttling was not needed here */
rx_changed_settime:
bcm_rx_changed(op, lastdata);
op->kt_lastmsg = ktime_get();
}
/* * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly * received data stored in op->last_frames[]
*/ staticvoid bcm_rx_cmp_to_index(struct bcm_op *op, unsignedint index, conststruct canfd_frame *rxdata, unsignedchar traffic_flags)
{ struct canfd_frame *cf = op->frames + op->cfsiz * index; struct canfd_frame *lcf = op->last_frames + op->cfsiz * index; int i;
/* * no one uses the MSBs of flags for comparison, * so we use it here to detect the first time of reception
*/
if (!(lcf->flags & RX_RECV)) { /* received data for the first time => send update to user */
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags); return;
}
/* do a real check in CAN frame data section */ for (i = 0; i < rxdata->len; i += 8) { if ((get_u64(cf, i) & get_u64(rxdata, i)) !=
(get_u64(cf, i) & get_u64(lcf, i))) {
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags); return;
}
}
if (op->flags & RX_CHECK_DLC) { /* do a real check in CAN frame length */ if (rxdata->len != lcf->len) {
bcm_rx_update_and_send(op, lcf, rxdata, traffic_flags); return;
}
}
}
/* * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
*/ staticvoid bcm_rx_starttimer(struct bcm_op *op)
{ if (op->flags & RX_NO_AUTOTIMER) return;
if (op->kt_ival1)
hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
}
/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */ staticenum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
{ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); struct bcm_msg_head msg_head;
/* if user wants to be informed, when cyclic CAN-Messages come back */ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) { /* clear received CAN frames to indicate 'nothing received' */
memset(op->last_frames, 0, op->nframes * op->cfsiz);
}
/* * bcm_rx_thr_flush - Check for throttled data and send it to the userspace
*/ staticint bcm_rx_thr_flush(struct bcm_op *op)
{ int updated = 0;
if (op->nframes > 1) { unsignedint i;
/* for MUX filter we start at index 1 */ for (i = 1; i < op->nframes; i++)
updated += bcm_rx_do_flush(op, i);
} else { /* for RX_FILTER_ID and simple filter */
updated += bcm_rx_do_flush(op, 0);
}
return updated;
}
/* * bcm_rx_thr_handler - the time for blocked content updates is over now: * Check for throttled data and send it to the userspace
*/ staticenum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
{ struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
/* make sure to handle the correct frame type (CAN / CAN FD) */ if (op->flags & CAN_FD_FRAME) { if (!can_is_canfd_skb(skb)) return;
} else { if (!can_is_can_skb(skb)) return;
}
/* disable timeout */
hrtimer_cancel(&op->timer);
/* save rx timestamp */
op->rx_stamp = skb->tstamp; /* save originator for recvfrom() */
op->rx_ifindex = skb->dev->ifindex; /* update statistics */
op->frames_abs++;
if (op->flags & RX_RTR_FRAME) { /* send reply for RTR-request (placed in op->frames[0]) */
bcm_can_tx(op); return;
}
/* compute flags to distinguish between own/local/remote CAN traffic */
traffic_flags = 0; if (skb->sk) {
traffic_flags |= RX_LOCAL; if (skb->sk == op->sk)
traffic_flags |= RX_OWN;
}
if (op->flags & RX_FILTER_ID) { /* the easiest case */
bcm_rx_update_and_send(op, op->last_frames, rxframe,
traffic_flags); goto rx_starttimer;
}
if (op->nframes == 1) { /* simple compare with index 0 */
bcm_rx_cmp_to_index(op, 0, rxframe, traffic_flags); goto rx_starttimer;
}
if (op->nframes > 1) { /* * multiplex compare * * find the first multiplex mask that fits. * Remark: The MUX-mask is stored in index 0 - but only the * first 64 bits of the frame data[] are relevant (CAN FD)
*/
for (i = 1; i < op->nframes; i++) { if ((get_u64(op->frames, 0) & get_u64(rxframe, 0)) ==
(get_u64(op->frames, 0) &
get_u64(op->frames + op->cfsiz * i, 0))) {
bcm_rx_cmp_to_index(op, i, rxframe,
traffic_flags); break;
}
}
}
rx_starttimer:
bcm_rx_starttimer(op);
}
/* * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
*/ staticstruct bcm_op *bcm_find_op(struct list_head *ops, struct bcm_msg_head *mh, int ifindex)
{ struct bcm_op *op;
/* * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
*/ staticint bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, int ifindex)
{ struct bcm_op *op, *n;
/* * Don't care if we're bound or not (due to netdev * problems) can_rx_unregister() is always a save * thing to do here.
*/ if (op->ifindex) { /* * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier()
*/ if (op->rx_reg_dev) { struct net_device *dev;
dev = dev_get_by_index(sock_net(op->sk),
op->ifindex); if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
}
}
} else
can_rx_unregister(sock_net(op->sk), NULL,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
/* * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
*/ staticint bcm_delete_tx_op(struct list_head *ops, struct bcm_msg_head *mh, int ifindex)
{ struct bcm_op *op, *n;
/* * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
*/ staticint bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head, int ifindex)
{ struct bcm_op *op = bcm_find_op(ops, msg_head, ifindex);
if (!op) return -EINVAL;
/* put current values into msg_head */
msg_head->flags = op->flags;
msg_head->count = op->count;
msg_head->ival1 = op->ival1;
msg_head->ival2 = op->ival2;
msg_head->nframes = op->nframes;
bcm_send_to_user(op, msg_head, op->frames, 0);
return MHSIZ;
}
/* * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
*/ staticint bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, int ifindex, struct sock *sk)
{ struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; struct canfd_frame *cf; unsignedint i; int err;
/* we need a real device to send frames */ if (!ifindex) return -ENODEV;
/* check nframes boundaries - we need at least one CAN frame */ if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES) return -EINVAL;
/* check the given can_id */
op = bcm_find_op(&bo->tx_ops, msg_head, ifindex); if (op) { /* update existing BCM operation */
/* * Do we need more space for the CAN frames than currently * allocated? -> This is a _really_ unusual use-case and * therefore (complexity / locking) it is not supported.
*/ if (msg_head->nframes > op->nframes) return -E2BIG;
/* update CAN frames content */ for (i = 0; i < msg_head->nframes; i++) {
if (op->flags & SETTIMER)
op->count = msg_head->count;
/* create array for CAN frames and copy the data */ if (msg_head->nframes > 1) {
op->frames = kmalloc_array(msg_head->nframes,
op->cfsiz,
GFP_KERNEL); if (!op->frames) {
kfree(op); return -ENOMEM;
}
} else
op->frames = &op->sframe;
/* check the given can_id */
op = bcm_find_op(&bo->rx_ops, msg_head, ifindex); if (op) { /* update existing BCM operation */
/* * Do we need more space for the CAN frames than currently * allocated? -> This is a _really_ unusual use-case and * therefore (complexity / locking) it is not supported.
*/ if (msg_head->nframes > op->nframes) return -E2BIG;
if (msg_head->nframes) { /* update CAN frames content */
err = memcpy_from_msg(op->frames, msg,
msg_head->nframes * op->cfsiz); if (err < 0) return err;
if (msg_head->nframes > 1) { /* create array for CAN frames and copy the data */
op->frames = kmalloc_array(msg_head->nframes,
op->cfsiz,
GFP_KERNEL); if (!op->frames) {
kfree(op); return -ENOMEM;
}
/* create and init array for received CAN frames */
op->last_frames = kcalloc(msg_head->nframes,
op->cfsiz,
GFP_KERNEL); if (!op->last_frames) {
kfree(op->frames);
kfree(op); return -ENOMEM;
}
/* add this bcm_op to the list of the rx_ops */
list_add(&op->list, &bo->rx_ops);
/* call can_rx_register() */
do_rx_register = 1;
} /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
/* check flags */
if (op->flags & RX_RTR_FRAME) { struct canfd_frame *frame0 = op->frames;
/* no timers in RTR-mode */
hrtimer_cancel(&op->thrtimer);
hrtimer_cancel(&op->timer);
/* * funny feature in RX(!)_SETUP only for RTR-mode: * copy can_id into frame BUT without RTR-flag to * prevent a full-load-loopback-test ... ;-]
*/ if ((op->flags & TX_CP_CAN_ID) ||
(frame0->can_id == op->can_id))
frame0->can_id = op->can_id & ~CAN_RTR_FLAG;
} else { if (op->flags & SETTIMER) {
/* set timer value */
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
op->kt_ival1 = bcm_timeval_to_ktime(msg_head->ival1);
op->kt_ival2 = bcm_timeval_to_ktime(msg_head->ival2);
/* disable an active timer due to zero value? */ if (!op->kt_ival1)
hrtimer_cancel(&op->timer);
/* * In any case cancel the throttle timer, flush * potentially blocked msgs and reset throttle handling
*/
op->kt_lastmsg = 0;
hrtimer_cancel(&op->thrtimer);
bcm_rx_thr_flush(op);
}
if ((op->flags & STARTTIMER) && op->kt_ival1)
hrtimer_start(&op->timer, op->kt_ival1,
HRTIMER_MODE_REL_SOFT);
}
/* now we can register for can_ids, if we added a new bcm_op */ if (do_rx_register) { if (ifindex) { struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex); if (dev) {
err = can_rx_register(sock_net(sk), dev,
op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk);
op->rx_reg_dev = dev;
dev_put(dev);
}
} else
err = can_rx_register(sock_net(sk), NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op, "bcm", sk); if (err) { /* this bcm rx op is broken -> remove it */
list_del_rcu(&op->list);
bcm_remove_op(op); return err;
}
}
return msg_head->nframes * op->cfsiz + MHSIZ;
}
/* * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
*/ staticint bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, int cfsiz)
{ struct sk_buff *skb; struct net_device *dev; int err;
/* we need a real device to send frames */ if (!ifindex) return -ENODEV;
/* check for alternative ifindex for this bcm_op */
if (!ifindex && msg->msg_name) { /* no bound device as default => check msg_name */
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
if (msg->msg_namelen < BCM_MIN_NAMELEN) return -EINVAL;
if (addr->can_family != AF_CAN) return -EINVAL;
/* ifindex from sendto() */
ifindex = addr->can_ifindex;
if (ifindex) { struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex); if (!dev) return -ENODEV;
if (dev->type != ARPHRD_CAN) {
dev_put(dev); return -ENODEV;
}
dev_put(dev);
}
}
lock_sock(sk);
switch (msg_head.opcode) {
case TX_SETUP:
ret = bcm_tx_setup(&msg_head, msg, ifindex, sk); break;
case RX_SETUP:
ret = bcm_rx_setup(&msg_head, msg, ifindex, sk); break;
case TX_DELETE: if (bcm_delete_tx_op(&bo->tx_ops, &msg_head, ifindex))
ret = MHSIZ; else
ret = -EINVAL; break;
case RX_DELETE: if (bcm_delete_rx_op(&bo->rx_ops, &msg_head, ifindex))
ret = MHSIZ; else
ret = -EINVAL; break;
case TX_READ: /* reuse msg_head for the reply to TX_READ */
msg_head.opcode = TX_STATUS;
ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex); break;
case RX_READ: /* reuse msg_head for the reply to RX_READ */
msg_head.opcode = RX_STATUS;
ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex); break;
case TX_SEND: /* we need exactly one CAN frame behind the msg head */ if ((msg_head.nframes != 1) || (size != cfsiz + MHSIZ))
ret = -EINVAL; else
ret = bcm_tx_send(msg, ifindex, sk, cfsiz); break;
list_for_each_entry_safe(op, next, &bo->rx_ops, list) { /* * Don't care if we're bound or not (due to netdev problems) * can_rx_unregister() is always a save thing to do here.
*/ if (op->ifindex) { /* * Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier()
*/ if (op->rx_reg_dev) { struct net_device *dev;
dev = dev_get_by_index(net, op->ifindex); if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
}
}
} else
can_rx_unregister(net, NULL, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op);
staticint bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, int flags)
{ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr; struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); struct net *net = sock_net(sk); int ret = 0;
if (len < BCM_MIN_NAMELEN) return -EINVAL;
lock_sock(sk);
if (bo->bound) {
ret = -EISCONN; goto fail;
}
/* bind a device to this socket */ if (addr->can_ifindex) { struct net_device *dev;
dev = dev_get_by_index(net, addr->can_ifindex); if (!dev) {
ret = -ENODEV; goto fail;
} if (dev->type != ARPHRD_CAN) {
dev_put(dev);
ret = -ENODEV; goto fail;
}
bo->ifindex = dev->ifindex;
dev_put(dev);
} else { /* no interface reference for ifindex = 0 ('any' CAN device) */
bo->ifindex = 0;
}
#if IS_ENABLED(CONFIG_PROC_FS) if (net->can.bcmproc_dir) { /* unique socket address as filename */
sprintf(bo->procname, "%lu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_net_single(bo->procname, 0644,
net->can.bcmproc_dir,
bcm_proc_show, sk); if (!bo->bcm_proc_read) {
ret = -ENOMEM; goto fail;
}
} #endif/* CONFIG_PROC_FS */
bo->bound = 1;
fail:
release_sock(sk);
return ret;
}
staticint bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags)
{ struct sock *sk = sock->sk; struct sk_buff *skb; int error = 0; int err;
skb = skb_recv_datagram(sk, flags, &error); if (!skb) return error;
/* assign the flags that have been recorded in bcm_send_to_user() */
msg->msg_flags |= *(bcm_flags(skb));
skb_free_datagram(sk, skb);
return size;
}
staticint bcm_sock_no_ioctlcmd(struct socket *sock, unsignedint cmd, unsignedlong arg)
{ /* no ioctls for socket layer -> hand it down to NIC layer */ return -ENOIOCTLCMD;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.