mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
56
net/can/Kconfig
Normal file
56
net/can/Kconfig
Normal file
|
@ -0,0 +1,56 @@
|
|||
#
|
||||
# Controller Area Network (CAN) network layer core configuration
|
||||
#
|
||||
|
||||
menuconfig CAN
|
||||
depends on NET
|
||||
tristate "CAN bus subsystem support"
|
||||
---help---
|
||||
Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
|
||||
communications protocol which was developed by Bosch in
|
||||
1991, mainly for automotive, but now widely used in marine
|
||||
(NMEA2000), industrial, and medical applications.
|
||||
More information on the CAN network protocol family PF_CAN
|
||||
is contained in <Documentation/networking/can.txt>.
|
||||
|
||||
If you want CAN support you should say Y here and also to the
|
||||
specific driver for your controller(s) below.
|
||||
|
||||
if CAN
|
||||
|
||||
config CAN_RAW
|
||||
tristate "Raw CAN Protocol (raw access with CAN-ID filtering)"
|
||||
default y
|
||||
---help---
|
||||
The raw CAN protocol option offers access to the CAN bus via
|
||||
the BSD socket API. You probably want to use the raw socket in
|
||||
most cases where no higher level protocol is being used. The raw
|
||||
socket has several filter options e.g. ID masking / error frames.
|
||||
To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
|
||||
|
||||
config CAN_BCM
|
||||
tristate "Broadcast Manager CAN Protocol (with content filtering)"
|
||||
default y
|
||||
---help---
|
||||
The Broadcast Manager offers content filtering, timeout monitoring,
|
||||
sending of RTR frames, and cyclic CAN messages without permanent user
|
||||
interaction. The BCM can be 'programmed' via the BSD socket API and
|
||||
informs you on demand e.g. only on content updates / timeouts.
|
||||
You probably want to use the bcm socket in most cases where cyclic
|
||||
CAN messages are used on the bus (e.g. in automotive environments).
|
||||
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
|
||||
|
||||
config CAN_GW
|
||||
tristate "CAN Gateway/Router (with netlink configuration)"
|
||||
default y
|
||||
---help---
|
||||
The CAN Gateway/Router is used to route (and modify) CAN frames.
|
||||
It is based on the PF_CAN core infrastructure for msg filtering and
|
||||
msg sending and can optionally modify routed CAN frames on the fly.
|
||||
CAN frames can be routed between CAN network interfaces (one hop).
|
||||
They can be modified with AND/OR/XOR/SET operations as configured
|
||||
by the netlink configuration interface known e.g. from iptables.
|
||||
|
||||
source "drivers/net/can/Kconfig"
|
||||
|
||||
endif
|
15
net/can/Makefile
Normal file
15
net/can/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# Makefile for the Linux Controller Area Network core.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN) += can.o
|
||||
can-y := af_can.o proc.o
|
||||
|
||||
obj-$(CONFIG_CAN_RAW) += can-raw.o
|
||||
can-raw-y := raw.o
|
||||
|
||||
obj-$(CONFIG_CAN_BCM) += can-bcm.o
|
||||
can-bcm-y := bcm.o
|
||||
|
||||
obj-$(CONFIG_CAN_GW) += can-gw.o
|
||||
can-gw-y := gw.o
|
964
net/can/af_can.c
Normal file
964
net/can/af_can.c
Normal file
|
@ -0,0 +1,964 @@
|
|||
/*
|
||||
* af_can.c - Protocol family CAN core module
|
||||
* (used by different CAN protocol modules)
|
||||
*
|
||||
* Copyright (c) 2002-2007 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "af_can.h"
|
||||
|
||||
static __initconst const char banner[] = KERN_INFO
|
||||
"can: controller area network core (" CAN_VERSION_STRING ")\n";
|
||||
|
||||
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
|
||||
"Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
|
||||
|
||||
MODULE_ALIAS_NETPROTO(PF_CAN);
|
||||
|
||||
static int stats_timer __read_mostly = 1;
|
||||
module_param(stats_timer, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
|
||||
|
||||
/* receive filters subscribed for 'all' CAN devices */
|
||||
struct dev_rcv_lists can_rx_alldev_list;
|
||||
static DEFINE_SPINLOCK(can_rcvlists_lock);
|
||||
|
||||
static struct kmem_cache *rcv_cache __read_mostly;
|
||||
|
||||
/* table of registered CAN protocols */
|
||||
static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
|
||||
static DEFINE_MUTEX(proto_tab_lock);
|
||||
|
||||
struct timer_list can_stattimer; /* timer for statistics update */
|
||||
struct s_stats can_stats; /* packet statistics */
|
||||
struct s_pstats can_pstats; /* receive list statistics */
|
||||
|
||||
/*
|
||||
* af_can socket functions
|
||||
*/
|
||||
|
||||
int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case SIOCGSTAMP:
|
||||
return sock_get_timestamp(sk, (struct timeval __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(can_ioctl);
|
||||
|
||||
static void can_sock_destruct(struct sock *sk)
|
||||
{
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
}
|
||||
|
||||
static const struct can_proto *can_get_proto(int protocol)
|
||||
{
|
||||
const struct can_proto *cp;
|
||||
|
||||
rcu_read_lock();
|
||||
cp = rcu_dereference(proto_tab[protocol]);
|
||||
if (cp && !try_module_get(cp->prot->owner))
|
||||
cp = NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
static inline void can_put_proto(const struct can_proto *cp)
|
||||
{
|
||||
module_put(cp->prot->owner);
|
||||
}
|
||||
|
||||
static int can_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
const struct can_proto *cp;
|
||||
int err = 0;
|
||||
|
||||
sock->state = SS_UNCONNECTED;
|
||||
|
||||
if (protocol < 0 || protocol >= CAN_NPROTO)
|
||||
return -EINVAL;
|
||||
|
||||
if (!net_eq(net, &init_net))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
cp = can_get_proto(protocol);
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!cp) {
|
||||
/* try to load protocol module if kernel is modular */
|
||||
|
||||
err = request_module("can-proto-%d", protocol);
|
||||
|
||||
/*
|
||||
* In case of error we only print a message but don't
|
||||
* return the error code immediately. Below we will
|
||||
* return -EPROTONOSUPPORT
|
||||
*/
|
||||
if (err)
|
||||
printk_ratelimited(KERN_ERR "can: request_module "
|
||||
"(can-proto-%d) failed.\n", protocol);
|
||||
|
||||
cp = can_get_proto(protocol);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* check for available protocol and correct usage */
|
||||
|
||||
if (!cp)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (cp->type != sock->type) {
|
||||
err = -EPROTOTYPE;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
sock->ops = cp->ops;
|
||||
|
||||
sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot);
|
||||
if (!sk) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
sock_init_data(sock, sk);
|
||||
sk->sk_destruct = can_sock_destruct;
|
||||
|
||||
if (sk->sk_prot->init)
|
||||
err = sk->sk_prot->init(sk);
|
||||
|
||||
if (err) {
|
||||
/* release sk on errors */
|
||||
sock_orphan(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
errout:
|
||||
can_put_proto(cp);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* af_can tx path
|
||||
*/
|
||||
|
||||
/**
|
||||
* can_send - transmit a CAN frame (optional with local loopback)
|
||||
* @skb: pointer to socket buffer with CAN frame in data section
|
||||
* @loop: loopback for listeners on local CAN sockets (recommended default!)
|
||||
*
|
||||
* Due to the loopback this routine must not be called from hardirq context.
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENETDOWN when the selected interface is down
|
||||
* -ENOBUFS on full driver queue (see net_xmit_errno())
|
||||
* -ENOMEM when local loopback failed at calling skb_clone()
|
||||
* -EPERM when trying to send on a non-CAN interface
|
||||
* -EMSGSIZE CAN frame size is bigger than CAN interface MTU
|
||||
* -EINVAL when the skb->data does not contain a valid CAN frame
|
||||
*/
|
||||
int can_send(struct sk_buff *skb, int loop)
|
||||
{
|
||||
struct sk_buff *newskb = NULL;
|
||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (skb->len == CAN_MTU) {
|
||||
skb->protocol = htons(ETH_P_CAN);
|
||||
if (unlikely(cfd->len > CAN_MAX_DLEN))
|
||||
goto inval_skb;
|
||||
} else if (skb->len == CANFD_MTU) {
|
||||
skb->protocol = htons(ETH_P_CANFD);
|
||||
if (unlikely(cfd->len > CANFD_MAX_DLEN))
|
||||
goto inval_skb;
|
||||
} else
|
||||
goto inval_skb;
|
||||
|
||||
/*
|
||||
* Make sure the CAN frame can pass the selected CAN netdevice.
|
||||
* As structs can_frame and canfd_frame are similar, we can provide
|
||||
* CAN FD frames to legacy CAN drivers as long as the length is <= 8
|
||||
*/
|
||||
if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) {
|
||||
err = -EMSGSIZE;
|
||||
goto inval_skb;
|
||||
}
|
||||
|
||||
if (unlikely(skb->dev->type != ARPHRD_CAN)) {
|
||||
err = -EPERM;
|
||||
goto inval_skb;
|
||||
}
|
||||
|
||||
if (unlikely(!(skb->dev->flags & IFF_UP))) {
|
||||
err = -ENETDOWN;
|
||||
goto inval_skb;
|
||||
}
|
||||
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
if (loop) {
|
||||
/* local loopback of sent CAN frames */
|
||||
|
||||
/* indication for the CAN driver: do loopback */
|
||||
skb->pkt_type = PACKET_LOOPBACK;
|
||||
|
||||
/*
|
||||
* The reference to the originating sock may be required
|
||||
* by the receiving socket to check whether the frame is
|
||||
* its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
|
||||
* Therefore we have to ensure that skb->sk remains the
|
||||
* reference to the originating sock by restoring skb->sk
|
||||
* after each skb_clone() or skb_orphan() usage.
|
||||
*/
|
||||
|
||||
if (!(skb->dev->flags & IFF_ECHO)) {
|
||||
/*
|
||||
* If the interface is not capable to do loopback
|
||||
* itself, we do it here.
|
||||
*/
|
||||
newskb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!newskb) {
|
||||
kfree_skb(skb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
can_skb_set_owner(newskb, skb->sk);
|
||||
newskb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
newskb->pkt_type = PACKET_BROADCAST;
|
||||
}
|
||||
} else {
|
||||
/* indication for the CAN driver: no loopback required */
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
}
|
||||
|
||||
/* send to netdevice */
|
||||
err = dev_queue_xmit(skb);
|
||||
if (err > 0)
|
||||
err = net_xmit_errno(err);
|
||||
|
||||
if (err) {
|
||||
kfree_skb(newskb);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (newskb)
|
||||
netif_rx_ni(newskb);
|
||||
|
||||
/* update statistics */
|
||||
can_stats.tx_frames++;
|
||||
can_stats.tx_frames_delta++;
|
||||
|
||||
return 0;
|
||||
|
||||
inval_skb:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(can_send);
|
||||
|
||||
/*
|
||||
* af_can rx path
|
||||
*/
|
||||
|
||||
static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return &can_rx_alldev_list;
|
||||
else
|
||||
return (struct dev_rcv_lists *)dev->ml_priv;
|
||||
}
|
||||
|
||||
/**
|
||||
* effhash - hash function for 29 bit CAN identifier reduction
|
||||
* @can_id: 29 bit CAN identifier
|
||||
*
|
||||
* Description:
|
||||
* To reduce the linear traversal in one linked list of _single_ EFF CAN
|
||||
* frame subscriptions the 29 bit identifier is mapped to 10 bits.
|
||||
* (see CAN_EFF_RCV_HASH_BITS definition)
|
||||
*
|
||||
* Return:
|
||||
* Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask )
|
||||
*/
|
||||
static unsigned int effhash(canid_t can_id)
|
||||
{
|
||||
unsigned int hash;
|
||||
|
||||
hash = can_id;
|
||||
hash ^= can_id >> CAN_EFF_RCV_HASH_BITS;
|
||||
hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS);
|
||||
|
||||
return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* find_rcv_list - determine optimal filterlist inside device filter struct
|
||||
* @can_id: pointer to CAN identifier of a given can_filter
|
||||
* @mask: pointer to CAN mask of a given can_filter
|
||||
* @d: pointer to the device filter struct
|
||||
*
|
||||
* Description:
|
||||
* Returns the optimal filterlist to reduce the filter handling in the
|
||||
* receive path. This function is called by service functions that need
|
||||
* to register or unregister a can_filter in the filter lists.
|
||||
*
|
||||
* A filter matches in general, when
|
||||
*
|
||||
* <received_can_id> & mask == can_id & mask
|
||||
*
|
||||
* so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
|
||||
* relevant bits for the filter.
|
||||
*
|
||||
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
|
||||
* filter for error messages (CAN_ERR_FLAG bit set in mask). For error msg
|
||||
* frames there is a special filterlist and a special rx path filter handling.
|
||||
*
|
||||
* Return:
|
||||
* Pointer to optimal filterlist for the given can_id/mask pair.
|
||||
* Constistency checked mask.
|
||||
* Reduced can_id to have a preprocessed filter compare value.
|
||||
*/
|
||||
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
|
||||
struct dev_rcv_lists *d)
|
||||
{
|
||||
canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
|
||||
|
||||
/* filter for error message frames in extra filterlist */
|
||||
if (*mask & CAN_ERR_FLAG) {
|
||||
/* clear CAN_ERR_FLAG in filter entry */
|
||||
*mask &= CAN_ERR_MASK;
|
||||
return &d->rx[RX_ERR];
|
||||
}
|
||||
|
||||
/* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
|
||||
|
||||
#define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
|
||||
|
||||
/* ensure valid values in can_mask for 'SFF only' frame filtering */
|
||||
if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
|
||||
*mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
|
||||
|
||||
/* reduce condition testing at receive time */
|
||||
*can_id &= *mask;
|
||||
|
||||
/* inverse can_id/can_mask filter */
|
||||
if (inv)
|
||||
return &d->rx[RX_INV];
|
||||
|
||||
/* mask == 0 => no condition testing at receive time */
|
||||
if (!(*mask))
|
||||
return &d->rx[RX_ALL];
|
||||
|
||||
/* extra filterlists for the subscription of a single non-RTR can_id */
|
||||
if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS) &&
|
||||
!(*can_id & CAN_RTR_FLAG)) {
|
||||
|
||||
if (*can_id & CAN_EFF_FLAG) {
|
||||
if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS))
|
||||
return &d->rx_eff[effhash(*can_id)];
|
||||
} else {
|
||||
if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
|
||||
return &d->rx_sff[*can_id];
|
||||
}
|
||||
}
|
||||
|
||||
/* default: filter via can_id/can_mask */
|
||||
return &d->rx[RX_FIL];
|
||||
}
|
||||
|
||||
/**
|
||||
* can_rx_register - subscribe CAN frames from a specific interface
|
||||
* @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
|
||||
* @can_id: CAN identifier (see description)
|
||||
* @mask: CAN mask (see description)
|
||||
* @func: callback function on filter match
|
||||
* @data: returned parameter for callback function
|
||||
* @ident: string for calling module identification
|
||||
*
|
||||
* Description:
|
||||
* Invokes the callback function with the received sk_buff and the given
|
||||
* parameter 'data' on a matching receive filter. A filter matches, when
|
||||
*
|
||||
* <received_can_id> & mask == can_id & mask
|
||||
*
|
||||
* The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
|
||||
* filter for error message frames (CAN_ERR_FLAG bit set in mask).
|
||||
*
|
||||
* The provided pointer to the sk_buff is guaranteed to be valid as long as
|
||||
* the callback function is running. The callback function must *not* free
|
||||
* the given sk_buff while processing it's task. When the given sk_buff is
|
||||
* needed after the end of the callback function it must be cloned inside
|
||||
* the callback function with skb_clone().
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -ENOMEM on missing cache mem to create subscription entry
|
||||
* -ENODEV unknown device
|
||||
*/
|
||||
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
|
||||
void (*func)(struct sk_buff *, void *), void *data,
|
||||
char *ident)
|
||||
{
|
||||
struct receiver *r;
|
||||
struct hlist_head *rl;
|
||||
struct dev_rcv_lists *d;
|
||||
int err = 0;
|
||||
|
||||
/* insert new receiver (dev,canid,mask) -> (func,data) */
|
||||
|
||||
if (dev && dev->type != ARPHRD_CAN)
|
||||
return -ENODEV;
|
||||
|
||||
r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&can_rcvlists_lock);
|
||||
|
||||
d = find_dev_rcv_lists(dev);
|
||||
if (d) {
|
||||
rl = find_rcv_list(&can_id, &mask, d);
|
||||
|
||||
r->can_id = can_id;
|
||||
r->mask = mask;
|
||||
r->matches = 0;
|
||||
r->func = func;
|
||||
r->data = data;
|
||||
r->ident = ident;
|
||||
|
||||
hlist_add_head_rcu(&r->list, rl);
|
||||
d->entries++;
|
||||
|
||||
can_pstats.rcv_entries++;
|
||||
if (can_pstats.rcv_entries_max < can_pstats.rcv_entries)
|
||||
can_pstats.rcv_entries_max = can_pstats.rcv_entries;
|
||||
} else {
|
||||
kmem_cache_free(rcv_cache, r);
|
||||
err = -ENODEV;
|
||||
}
|
||||
|
||||
spin_unlock(&can_rcvlists_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(can_rx_register);
|
||||
|
||||
/*
|
||||
* can_rx_delete_receiver - rcu callback for single receiver entry removal
|
||||
*/
|
||||
static void can_rx_delete_receiver(struct rcu_head *rp)
|
||||
{
|
||||
struct receiver *r = container_of(rp, struct receiver, rcu);
|
||||
|
||||
kmem_cache_free(rcv_cache, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* can_rx_unregister - unsubscribe CAN frames from a specific interface
|
||||
* @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
|
||||
* @can_id: CAN identifier
|
||||
* @mask: CAN mask
|
||||
* @func: callback function on filter match
|
||||
* @data: returned parameter for callback function
|
||||
*
|
||||
* Description:
|
||||
* Removes subscription entry depending on given (subscription) values.
|
||||
*/
|
||||
void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
|
||||
void (*func)(struct sk_buff *, void *), void *data)
|
||||
{
|
||||
struct receiver *r = NULL;
|
||||
struct hlist_head *rl;
|
||||
struct dev_rcv_lists *d;
|
||||
|
||||
if (dev && dev->type != ARPHRD_CAN)
|
||||
return;
|
||||
|
||||
spin_lock(&can_rcvlists_lock);
|
||||
|
||||
d = find_dev_rcv_lists(dev);
|
||||
if (!d) {
|
||||
pr_err("BUG: receive list not found for "
|
||||
"dev %s, id %03X, mask %03X\n",
|
||||
DNAME(dev), can_id, mask);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rl = find_rcv_list(&can_id, &mask, d);
|
||||
|
||||
/*
|
||||
* Search the receiver list for the item to delete. This should
|
||||
* exist, since no receiver may be unregistered that hasn't
|
||||
* been registered before.
|
||||
*/
|
||||
|
||||
hlist_for_each_entry_rcu(r, rl, list) {
|
||||
if (r->can_id == can_id && r->mask == mask &&
|
||||
r->func == func && r->data == data)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for bugs in CAN protocol implementations using af_can.c:
|
||||
* 'r' will be NULL if no matching list item was found for removal.
|
||||
*/
|
||||
|
||||
if (!r) {
|
||||
WARN(1, "BUG: receive list entry not found for dev %s, "
|
||||
"id %03X, mask %03X\n", DNAME(dev), can_id, mask);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hlist_del_rcu(&r->list);
|
||||
d->entries--;
|
||||
|
||||
if (can_pstats.rcv_entries > 0)
|
||||
can_pstats.rcv_entries--;
|
||||
|
||||
/* remove device structure requested by NETDEV_UNREGISTER */
|
||||
if (d->remove_on_zero_entries && !d->entries) {
|
||||
kfree(d);
|
||||
dev->ml_priv = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&can_rcvlists_lock);
|
||||
|
||||
/* schedule the receiver item for deletion */
|
||||
if (r)
|
||||
call_rcu(&r->rcu, can_rx_delete_receiver);
|
||||
}
|
||||
EXPORT_SYMBOL(can_rx_unregister);
|
||||
|
||||
static inline void deliver(struct sk_buff *skb, struct receiver *r)
|
||||
{
|
||||
r->func(skb, r->data);
|
||||
r->matches++;
|
||||
}
|
||||
|
||||
static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
|
||||
{
|
||||
struct receiver *r;
|
||||
int matches = 0;
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
canid_t can_id = cf->can_id;
|
||||
|
||||
if (d->entries == 0)
|
||||
return 0;
|
||||
|
||||
if (can_id & CAN_ERR_FLAG) {
|
||||
/* check for error message frame entries only */
|
||||
hlist_for_each_entry_rcu(r, &d->rx[RX_ERR], list) {
|
||||
if (can_id & r->mask) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/* check for unfiltered entries */
|
||||
hlist_for_each_entry_rcu(r, &d->rx[RX_ALL], list) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
|
||||
/* check for can_id/mask entries */
|
||||
hlist_for_each_entry_rcu(r, &d->rx[RX_FIL], list) {
|
||||
if ((can_id & r->mask) == r->can_id) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for inverted can_id/mask entries */
|
||||
hlist_for_each_entry_rcu(r, &d->rx[RX_INV], list) {
|
||||
if ((can_id & r->mask) != r->can_id) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
/* check filterlists for single non-RTR can_ids */
|
||||
if (can_id & CAN_RTR_FLAG)
|
||||
return matches;
|
||||
|
||||
if (can_id & CAN_EFF_FLAG) {
|
||||
hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {
|
||||
if (r->can_id == can_id) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
can_id &= CAN_SFF_MASK;
|
||||
hlist_for_each_entry_rcu(r, &d->rx_sff[can_id], list) {
|
||||
deliver(skb, r);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
static void can_receive(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct dev_rcv_lists *d;
|
||||
int matches;
|
||||
|
||||
/* update statistics */
|
||||
can_stats.rx_frames++;
|
||||
can_stats.rx_frames_delta++;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* deliver the packet to sockets listening on all devices */
|
||||
matches = can_rcv_filter(&can_rx_alldev_list, skb);
|
||||
|
||||
/* find receive list for this device */
|
||||
d = find_dev_rcv_lists(dev);
|
||||
if (d)
|
||||
matches += can_rcv_filter(d, skb);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* consume the skbuff allocated by the netdevice driver */
|
||||
consume_skb(skb);
|
||||
|
||||
if (matches > 0) {
|
||||
can_stats.matches++;
|
||||
can_stats.matches_delta++;
|
||||
}
|
||||
}
|
||||
|
||||
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
||||
|
||||
if (unlikely(!net_eq(dev_net(dev), &init_net)))
|
||||
goto drop;
|
||||
|
||||
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
|
||||
skb->len != CAN_MTU ||
|
||||
cfd->len > CAN_MAX_DLEN,
|
||||
"PF_CAN: dropped non conform CAN skbuf: "
|
||||
"dev type %d, len %d, datalen %d\n",
|
||||
dev->type, skb->len, cfd->len))
|
||||
goto drop;
|
||||
|
||||
can_receive(skb, dev);
|
||||
return NET_RX_SUCCESS;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
|
||||
|
||||
if (unlikely(!net_eq(dev_net(dev), &init_net)))
|
||||
goto drop;
|
||||
|
||||
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
|
||||
skb->len != CANFD_MTU ||
|
||||
cfd->len > CANFD_MAX_DLEN,
|
||||
"PF_CAN: dropped non conform CAN FD skbuf: "
|
||||
"dev type %d, len %d, datalen %d\n",
|
||||
dev->type, skb->len, cfd->len))
|
||||
goto drop;
|
||||
|
||||
can_receive(skb, dev);
|
||||
return NET_RX_SUCCESS;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return NET_RX_DROP;
|
||||
}
|
||||
|
||||
/*
|
||||
* af_can protocol functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* can_proto_register - register CAN transport protocol
|
||||
* @cp: pointer to CAN protocol structure
|
||||
*
|
||||
* Return:
|
||||
* 0 on success
|
||||
* -EINVAL invalid (out of range) protocol number
|
||||
* -EBUSY protocol already in use
|
||||
* -ENOBUF if proto_register() fails
|
||||
*/
|
||||
int can_proto_register(const struct can_proto *cp)
|
||||
{
|
||||
int proto = cp->protocol;
|
||||
int err = 0;
|
||||
|
||||
if (proto < 0 || proto >= CAN_NPROTO) {
|
||||
pr_err("can: protocol number %d out of range\n", proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = proto_register(cp->prot, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&proto_tab_lock);
|
||||
|
||||
if (proto_tab[proto]) {
|
||||
pr_err("can: protocol %d already registered\n", proto);
|
||||
err = -EBUSY;
|
||||
} else
|
||||
RCU_INIT_POINTER(proto_tab[proto], cp);
|
||||
|
||||
mutex_unlock(&proto_tab_lock);
|
||||
|
||||
if (err < 0)
|
||||
proto_unregister(cp->prot);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(can_proto_register);
|
||||
|
||||
/**
|
||||
* can_proto_unregister - unregister CAN transport protocol
|
||||
* @cp: pointer to CAN protocol structure
|
||||
*/
|
||||
void can_proto_unregister(const struct can_proto *cp)
|
||||
{
|
||||
int proto = cp->protocol;
|
||||
|
||||
mutex_lock(&proto_tab_lock);
|
||||
BUG_ON(proto_tab[proto] != cp);
|
||||
RCU_INIT_POINTER(proto_tab[proto], NULL);
|
||||
mutex_unlock(&proto_tab_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
proto_unregister(cp->prot);
|
||||
}
|
||||
EXPORT_SYMBOL(can_proto_unregister);
|
||||
|
||||
/*
|
||||
* af_can notifier to create/remove CAN netdevice specific structs
|
||||
*/
|
||||
static int can_notifier(struct notifier_block *nb, unsigned long msg,
|
||||
void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct dev_rcv_lists *d;
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (msg) {
|
||||
|
||||
case NETDEV_REGISTER:
|
||||
|
||||
/* create new dev_rcv_lists for this device */
|
||||
d = kzalloc(sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return NOTIFY_DONE;
|
||||
BUG_ON(dev->ml_priv);
|
||||
dev->ml_priv = d;
|
||||
|
||||
break;
|
||||
|
||||
case NETDEV_UNREGISTER:
|
||||
spin_lock(&can_rcvlists_lock);
|
||||
|
||||
d = dev->ml_priv;
|
||||
if (d) {
|
||||
if (d->entries)
|
||||
d->remove_on_zero_entries = 1;
|
||||
else {
|
||||
kfree(d);
|
||||
dev->ml_priv = NULL;
|
||||
}
|
||||
} else
|
||||
pr_err("can: notifier: receive list not found for dev "
|
||||
"%s\n", dev->name);
|
||||
|
||||
spin_unlock(&can_rcvlists_lock);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* af_can module init/exit functions
|
||||
*/
|
||||
|
||||
static struct packet_type can_packet __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_CAN),
|
||||
.func = can_rcv,
|
||||
};
|
||||
|
||||
static struct packet_type canfd_packet __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_CANFD),
|
||||
.func = canfd_rcv,
|
||||
};
|
||||
|
||||
static const struct net_proto_family can_family_ops = {
|
||||
.family = PF_CAN,
|
||||
.create = can_create,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* notifier block for netdevice event */
|
||||
static struct notifier_block can_netdev_notifier __read_mostly = {
|
||||
.notifier_call = can_notifier,
|
||||
};
|
||||
|
||||
static __init int can_init(void)
|
||||
{
|
||||
/* check for correct padding to be able to use the structs similarly */
|
||||
BUILD_BUG_ON(offsetof(struct can_frame, can_dlc) !=
|
||||
offsetof(struct canfd_frame, len) ||
|
||||
offsetof(struct can_frame, data) !=
|
||||
offsetof(struct canfd_frame, data));
|
||||
|
||||
printk(banner);
|
||||
|
||||
memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
|
||||
|
||||
rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
|
||||
0, 0, NULL);
|
||||
if (!rcv_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
if (stats_timer) {
|
||||
/* the statistics are updated every second (timer triggered) */
|
||||
setup_timer(&can_stattimer, can_stat_update, 0);
|
||||
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
|
||||
} else
|
||||
can_stattimer.function = NULL;
|
||||
|
||||
can_init_proc();
|
||||
|
||||
/* protocol register */
|
||||
sock_register(&can_family_ops);
|
||||
register_netdevice_notifier(&can_netdev_notifier);
|
||||
dev_add_pack(&can_packet);
|
||||
dev_add_pack(&canfd_packet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __exit void can_exit(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
|
||||
if (stats_timer)
|
||||
del_timer_sync(&can_stattimer);
|
||||
|
||||
can_remove_proc();
|
||||
|
||||
/* protocol unregister */
|
||||
dev_remove_pack(&canfd_packet);
|
||||
dev_remove_pack(&can_packet);
|
||||
unregister_netdevice_notifier(&can_netdev_notifier);
|
||||
sock_unregister(PF_CAN);
|
||||
|
||||
/* remove created dev_rcv_lists from still registered CAN devices */
|
||||
rcu_read_lock();
|
||||
for_each_netdev_rcu(&init_net, dev) {
|
||||
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
|
||||
|
||||
struct dev_rcv_lists *d = dev->ml_priv;
|
||||
|
||||
BUG_ON(d->entries);
|
||||
kfree(d);
|
||||
dev->ml_priv = NULL;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
||||
|
||||
kmem_cache_destroy(rcv_cache);
|
||||
}
|
||||
|
||||
module_init(can_init);
|
||||
module_exit(can_exit);
|
126
net/can/af_can.h
Normal file
126
net/can/af_can.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2002-2007 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AF_CAN_H
|
||||
#define AF_CAN_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/can.h>
|
||||
|
||||
/* af_can rx dispatcher structures */
|
||||
|
||||
struct receiver {
|
||||
struct hlist_node list;
|
||||
struct rcu_head rcu;
|
||||
canid_t can_id;
|
||||
canid_t mask;
|
||||
unsigned long matches;
|
||||
void (*func)(struct sk_buff *, void *);
|
||||
void *data;
|
||||
char *ident;
|
||||
};
|
||||
|
||||
#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
|
||||
#define CAN_EFF_RCV_HASH_BITS 10
|
||||
#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS)
|
||||
|
||||
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
|
||||
|
||||
/* per device receive filters linked at dev->ml_priv */
|
||||
struct dev_rcv_lists {
|
||||
struct hlist_head rx[RX_MAX];
|
||||
struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
|
||||
struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
|
||||
int remove_on_zero_entries;
|
||||
int entries;
|
||||
};
|
||||
|
||||
/* statistic structures */
|
||||
|
||||
/* can be reset e.g. by can_init_stats() */
|
||||
struct s_stats {
|
||||
unsigned long jiffies_init;
|
||||
|
||||
unsigned long rx_frames;
|
||||
unsigned long tx_frames;
|
||||
unsigned long matches;
|
||||
|
||||
unsigned long total_rx_rate;
|
||||
unsigned long total_tx_rate;
|
||||
unsigned long total_rx_match_ratio;
|
||||
|
||||
unsigned long current_rx_rate;
|
||||
unsigned long current_tx_rate;
|
||||
unsigned long current_rx_match_ratio;
|
||||
|
||||
unsigned long max_rx_rate;
|
||||
unsigned long max_tx_rate;
|
||||
unsigned long max_rx_match_ratio;
|
||||
|
||||
unsigned long rx_frames_delta;
|
||||
unsigned long tx_frames_delta;
|
||||
unsigned long matches_delta;
|
||||
};
|
||||
|
||||
/* persistent statistics */
|
||||
struct s_pstats {
|
||||
unsigned long stats_reset;
|
||||
unsigned long user_reset;
|
||||
unsigned long rcv_entries;
|
||||
unsigned long rcv_entries_max;
|
||||
};
|
||||
|
||||
/* receive filters subscribed for 'all' CAN devices */
|
||||
extern struct dev_rcv_lists can_rx_alldev_list;
|
||||
|
||||
/* function prototypes for the CAN networklayer procfs (proc.c) */
|
||||
void can_init_proc(void);
|
||||
void can_remove_proc(void);
|
||||
void can_stat_update(unsigned long data);
|
||||
|
||||
/* structures and variables from af_can.c needed in proc.c for reading */
|
||||
extern struct timer_list can_stattimer; /* timer for statistics update */
|
||||
extern struct s_stats can_stats; /* packet statistics */
|
||||
extern struct s_pstats can_pstats; /* receive list statistics */
|
||||
extern struct hlist_head can_rx_dev_list; /* rx dispatcher structures */
|
||||
|
||||
#endif /* AF_CAN_H */
|
1640
net/can/bcm.c
Normal file
1640
net/can/bcm.c
Normal file
File diff suppressed because it is too large
Load diff
997
net/can/gw.c
Normal file
997
net/can/gw.c
Normal file
|
@ -0,0 +1,997 @@
|
|||
/*
|
||||
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
|
||||
*
|
||||
* Copyright (c) 2011 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/gw.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#define CAN_GW_VERSION "20130117"
|
||||
#define CAN_GW_NAME "can-gw"
|
||||
|
||||
MODULE_DESCRIPTION("PF_CAN netlink gateway");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
|
||||
MODULE_ALIAS(CAN_GW_NAME);
|
||||
|
||||
#define CGW_MIN_HOPS 1
|
||||
#define CGW_MAX_HOPS 6
|
||||
#define CGW_DEFAULT_HOPS 1
|
||||
|
||||
static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS;
|
||||
module_param(max_hops, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(max_hops,
|
||||
"maximum " CAN_GW_NAME " routing hops for CAN frames "
|
||||
"(valid values: " __stringify(CGW_MIN_HOPS) "-"
|
||||
__stringify(CGW_MAX_HOPS) " hops, "
|
||||
"default: " __stringify(CGW_DEFAULT_HOPS) ")");
|
||||
|
||||
static HLIST_HEAD(cgw_list);
|
||||
static struct notifier_block notifier;
|
||||
|
||||
static struct kmem_cache *cgw_cache __read_mostly;
|
||||
|
||||
/* structure that contains the (on-the-fly) CAN frame modifications */
|
||||
struct cf_mod {
|
||||
struct {
|
||||
struct can_frame and;
|
||||
struct can_frame or;
|
||||
struct can_frame xor;
|
||||
struct can_frame set;
|
||||
} modframe;
|
||||
struct {
|
||||
u8 and;
|
||||
u8 or;
|
||||
u8 xor;
|
||||
u8 set;
|
||||
} modtype;
|
||||
void (*modfunc[MAX_MODFUNCTIONS])(struct can_frame *cf,
|
||||
struct cf_mod *mod);
|
||||
|
||||
/* CAN frame checksum calculation after CAN frame modifications */
|
||||
struct {
|
||||
struct cgw_csum_xor xor;
|
||||
struct cgw_csum_crc8 crc8;
|
||||
} csum;
|
||||
struct {
|
||||
void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
|
||||
void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
|
||||
} csumfunc;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* So far we just support CAN -> CAN routing and frame modifications.
|
||||
*
|
||||
* The internal can_can_gw structure contains data and attributes for
|
||||
* a CAN -> CAN gateway job.
|
||||
*/
|
||||
struct can_can_gw {
|
||||
struct can_filter filter;
|
||||
int src_idx;
|
||||
int dst_idx;
|
||||
};
|
||||
|
||||
/* list entry for CAN gateways jobs */
|
||||
struct cgw_job {
|
||||
struct hlist_node list;
|
||||
struct rcu_head rcu;
|
||||
u32 handled_frames;
|
||||
u32 dropped_frames;
|
||||
u32 deleted_frames;
|
||||
struct cf_mod mod;
|
||||
union {
|
||||
/* CAN frame data source */
|
||||
struct net_device *dev;
|
||||
} src;
|
||||
union {
|
||||
/* CAN frame data destination */
|
||||
struct net_device *dev;
|
||||
} dst;
|
||||
union {
|
||||
struct can_can_gw ccgw;
|
||||
/* tbc */
|
||||
};
|
||||
u8 gwtype;
|
||||
u8 limit_hops;
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
/* modification functions that are invoked in the hot path in can_can_gw_rcv */
|
||||
|
||||
#define MODFUNC(func, op) static void func(struct can_frame *cf, \
|
||||
struct cf_mod *mod) { op ; }
|
||||
|
||||
MODFUNC(mod_and_id, cf->can_id &= mod->modframe.and.can_id)
|
||||
MODFUNC(mod_and_dlc, cf->can_dlc &= mod->modframe.and.can_dlc)
|
||||
MODFUNC(mod_and_data, *(u64 *)cf->data &= *(u64 *)mod->modframe.and.data)
|
||||
MODFUNC(mod_or_id, cf->can_id |= mod->modframe.or.can_id)
|
||||
MODFUNC(mod_or_dlc, cf->can_dlc |= mod->modframe.or.can_dlc)
|
||||
MODFUNC(mod_or_data, *(u64 *)cf->data |= *(u64 *)mod->modframe.or.data)
|
||||
MODFUNC(mod_xor_id, cf->can_id ^= mod->modframe.xor.can_id)
|
||||
MODFUNC(mod_xor_dlc, cf->can_dlc ^= mod->modframe.xor.can_dlc)
|
||||
MODFUNC(mod_xor_data, *(u64 *)cf->data ^= *(u64 *)mod->modframe.xor.data)
|
||||
MODFUNC(mod_set_id, cf->can_id = mod->modframe.set.can_id)
|
||||
MODFUNC(mod_set_dlc, cf->can_dlc = mod->modframe.set.can_dlc)
|
||||
MODFUNC(mod_set_data, *(u64 *)cf->data = *(u64 *)mod->modframe.set.data)
|
||||
|
||||
static inline void canframecpy(struct can_frame *dst, struct can_frame *src)
|
||||
{
|
||||
/*
|
||||
* Copy the struct members separately to ensure that no uninitialized
|
||||
* data are copied in the 3 bytes hole of the struct. This is needed
|
||||
* to make easy compares of the data in the struct cf_mod.
|
||||
*/
|
||||
|
||||
dst->can_id = src->can_id;
|
||||
dst->can_dlc = src->can_dlc;
|
||||
*(u64 *)dst->data = *(u64 *)src->data;
|
||||
}
|
||||
|
||||
static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
|
||||
{
|
||||
/*
|
||||
* absolute dlc values 0 .. 7 => 0 .. 7, e.g. data [0]
|
||||
* relative to received dlc -1 .. -8 :
|
||||
* e.g. for received dlc = 8
|
||||
* -1 => index = 7 (data[7])
|
||||
* -3 => index = 5 (data[5])
|
||||
* -8 => index = 0 (data[0])
|
||||
*/
|
||||
|
||||
if (fr > -9 && fr < 8 &&
|
||||
to > -9 && to < 8 &&
|
||||
re > -9 && re < 8)
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int calc_idx(int idx, int rx_dlc)
|
||||
{
|
||||
if (idx < 0)
|
||||
return rx_dlc + idx;
|
||||
else
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
|
||||
{
|
||||
int from = calc_idx(xor->from_idx, cf->can_dlc);
|
||||
int to = calc_idx(xor->to_idx, cf->can_dlc);
|
||||
int res = calc_idx(xor->result_idx, cf->can_dlc);
|
||||
u8 val = xor->init_xor_val;
|
||||
int i;
|
||||
|
||||
if (from < 0 || to < 0 || res < 0)
|
||||
return;
|
||||
|
||||
if (from <= to) {
|
||||
for (i = from; i <= to; i++)
|
||||
val ^= cf->data[i];
|
||||
} else {
|
||||
for (i = from; i >= to; i--)
|
||||
val ^= cf->data[i];
|
||||
}
|
||||
|
||||
cf->data[res] = val;
|
||||
}
|
||||
|
||||
static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
|
||||
{
|
||||
u8 val = xor->init_xor_val;
|
||||
int i;
|
||||
|
||||
for (i = xor->from_idx; i <= xor->to_idx; i++)
|
||||
val ^= cf->data[i];
|
||||
|
||||
cf->data[xor->result_idx] = val;
|
||||
}
|
||||
|
||||
static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
|
||||
{
|
||||
u8 val = xor->init_xor_val;
|
||||
int i;
|
||||
|
||||
for (i = xor->from_idx; i >= xor->to_idx; i--)
|
||||
val ^= cf->data[i];
|
||||
|
||||
cf->data[xor->result_idx] = val;
|
||||
}
|
||||
|
||||
static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
|
||||
{
|
||||
int from = calc_idx(crc8->from_idx, cf->can_dlc);
|
||||
int to = calc_idx(crc8->to_idx, cf->can_dlc);
|
||||
int res = calc_idx(crc8->result_idx, cf->can_dlc);
|
||||
u8 crc = crc8->init_crc_val;
|
||||
int i;
|
||||
|
||||
if (from < 0 || to < 0 || res < 0)
|
||||
return;
|
||||
|
||||
if (from <= to) {
|
||||
for (i = crc8->from_idx; i <= crc8->to_idx; i++)
|
||||
crc = crc8->crctab[crc^cf->data[i]];
|
||||
} else {
|
||||
for (i = crc8->from_idx; i >= crc8->to_idx; i--)
|
||||
crc = crc8->crctab[crc^cf->data[i]];
|
||||
}
|
||||
|
||||
switch (crc8->profile) {
|
||||
|
||||
case CGW_CRC8PRF_1U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[0]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_16U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_SFFID_XOR:
|
||||
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
|
||||
(cf->can_id >> 8 & 0xFF)];
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
|
||||
}
|
||||
|
||||
static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
|
||||
{
|
||||
u8 crc = crc8->init_crc_val;
|
||||
int i;
|
||||
|
||||
for (i = crc8->from_idx; i <= crc8->to_idx; i++)
|
||||
crc = crc8->crctab[crc^cf->data[i]];
|
||||
|
||||
switch (crc8->profile) {
|
||||
|
||||
case CGW_CRC8PRF_1U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[0]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_16U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_SFFID_XOR:
|
||||
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
|
||||
(cf->can_id >> 8 & 0xFF)];
|
||||
break;
|
||||
}
|
||||
|
||||
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
|
||||
}
|
||||
|
||||
static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
|
||||
{
|
||||
u8 crc = crc8->init_crc_val;
|
||||
int i;
|
||||
|
||||
for (i = crc8->from_idx; i >= crc8->to_idx; i--)
|
||||
crc = crc8->crctab[crc^cf->data[i]];
|
||||
|
||||
switch (crc8->profile) {
|
||||
|
||||
case CGW_CRC8PRF_1U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[0]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_16U8:
|
||||
crc = crc8->crctab[crc^crc8->profile_data[cf->data[1] & 0xF]];
|
||||
break;
|
||||
|
||||
case CGW_CRC8PRF_SFFID_XOR:
|
||||
crc = crc8->crctab[crc^(cf->can_id & 0xFF)^
|
||||
(cf->can_id >> 8 & 0xFF)];
|
||||
break;
|
||||
}
|
||||
|
||||
cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
|
||||
}
|
||||
|
||||
/* the receive & process & send function */
|
||||
static void can_can_gw_rcv(struct sk_buff *skb, void *data)
|
||||
{
|
||||
struct cgw_job *gwj = (struct cgw_job *)data;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *nskb;
|
||||
int modidx = 0;
|
||||
|
||||
/*
|
||||
* Do not handle CAN frames routed more than 'max_hops' times.
|
||||
* In general we should never catch this delimiter which is intended
|
||||
* to cover a misconfiguration protection (e.g. circular CAN routes).
|
||||
*
|
||||
* The Controller Area Network controllers only accept CAN frames with
|
||||
* correct CRCs - which are not visible in the controller registers.
|
||||
* According to skbuff.h documentation the csum_start element for IP
|
||||
* checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY.
|
||||
* Only CAN skbs can be processed here which already have this property.
|
||||
*/
|
||||
|
||||
#define cgw_hops(skb) ((skb)->csum_start)
|
||||
|
||||
BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY);
|
||||
|
||||
if (cgw_hops(skb) >= max_hops) {
|
||||
/* indicate deleted frames due to misconfiguration */
|
||||
gwj->deleted_frames++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(gwj->dst.dev->flags & IFF_UP)) {
|
||||
gwj->dropped_frames++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* is sending the skb back to the incoming interface not allowed? */
|
||||
if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) &&
|
||||
can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex)
|
||||
return;
|
||||
|
||||
/*
|
||||
* clone the given skb, which has not been done in can_rcv()
|
||||
*
|
||||
* When there is at least one modification function activated,
|
||||
* we need to copy the skb as we want to modify skb->data.
|
||||
*/
|
||||
if (gwj->mod.modfunc[0])
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
else
|
||||
nskb = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
if (!nskb) {
|
||||
gwj->dropped_frames++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* put the incremented hop counter in the cloned skb */
|
||||
cgw_hops(nskb) = cgw_hops(skb) + 1;
|
||||
|
||||
/* first processing of this CAN frame -> adjust to private hop limit */
|
||||
if (gwj->limit_hops && cgw_hops(nskb) == 1)
|
||||
cgw_hops(nskb) = max_hops - gwj->limit_hops + 1;
|
||||
|
||||
nskb->dev = gwj->dst.dev;
|
||||
|
||||
/* pointer to modifiable CAN frame */
|
||||
cf = (struct can_frame *)nskb->data;
|
||||
|
||||
/* perform preprocessed modification functions if there are any */
|
||||
while (modidx < MAX_MODFUNCTIONS && gwj->mod.modfunc[modidx])
|
||||
(*gwj->mod.modfunc[modidx++])(cf, &gwj->mod);
|
||||
|
||||
/* check for checksum updates when the CAN frame has been modified */
|
||||
if (modidx) {
|
||||
if (gwj->mod.csumfunc.crc8)
|
||||
(*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
|
||||
|
||||
if (gwj->mod.csumfunc.xor)
|
||||
(*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
|
||||
}
|
||||
|
||||
/* clear the skb timestamp if not configured the other way */
|
||||
if (!(gwj->flags & CGW_FLAGS_CAN_SRC_TSTAMP))
|
||||
nskb->tstamp.tv64 = 0;
|
||||
|
||||
/* send to netdevice */
|
||||
if (can_send(nskb, gwj->flags & CGW_FLAGS_CAN_ECHO))
|
||||
gwj->dropped_frames++;
|
||||
else
|
||||
gwj->handled_frames++;
|
||||
}
|
||||
|
||||
static inline int cgw_register_filter(struct cgw_job *gwj)
|
||||
{
|
||||
return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
|
||||
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
|
||||
gwj, "gw");
|
||||
}
|
||||
|
||||
static inline void cgw_unregister_filter(struct cgw_job *gwj)
|
||||
{
|
||||
can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id,
|
||||
gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj);
|
||||
}
|
||||
|
||||
static int cgw_notifier(struct notifier_block *nb,
|
||||
unsigned long msg, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
return NOTIFY_DONE;
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (msg == NETDEV_UNREGISTER) {
|
||||
|
||||
struct cgw_job *gwj = NULL;
|
||||
struct hlist_node *nx;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
||||
|
||||
if (gwj->src.dev == dev || gwj->dst.dev == dev) {
|
||||
hlist_del(&gwj->list);
|
||||
cgw_unregister_filter(gwj);
|
||||
kmem_cache_free(cgw_cache, gwj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
|
||||
u32 pid, u32 seq, int flags)
|
||||
{
|
||||
struct cgw_frame_mod mb;
|
||||
struct rtcanmsg *rtcan;
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtcan), flags);
|
||||
if (!nlh)
|
||||
return -EMSGSIZE;
|
||||
|
||||
rtcan = nlmsg_data(nlh);
|
||||
rtcan->can_family = AF_CAN;
|
||||
rtcan->gwtype = gwj->gwtype;
|
||||
rtcan->flags = gwj->flags;
|
||||
|
||||
/* add statistics if available */
|
||||
|
||||
if (gwj->handled_frames) {
|
||||
if (nla_put_u32(skb, CGW_HANDLED, gwj->handled_frames) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->dropped_frames) {
|
||||
if (nla_put_u32(skb, CGW_DROPPED, gwj->dropped_frames) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->deleted_frames) {
|
||||
if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
/* check non default settings of attributes */
|
||||
|
||||
if (gwj->limit_hops) {
|
||||
if (nla_put_u8(skb, CGW_LIM_HOPS, gwj->limit_hops) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.modtype.and) {
|
||||
memcpy(&mb.cf, &gwj->mod.modframe.and, sizeof(mb.cf));
|
||||
mb.modtype = gwj->mod.modtype.and;
|
||||
if (nla_put(skb, CGW_MOD_AND, sizeof(mb), &mb) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.modtype.or) {
|
||||
memcpy(&mb.cf, &gwj->mod.modframe.or, sizeof(mb.cf));
|
||||
mb.modtype = gwj->mod.modtype.or;
|
||||
if (nla_put(skb, CGW_MOD_OR, sizeof(mb), &mb) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.modtype.xor) {
|
||||
memcpy(&mb.cf, &gwj->mod.modframe.xor, sizeof(mb.cf));
|
||||
mb.modtype = gwj->mod.modtype.xor;
|
||||
if (nla_put(skb, CGW_MOD_XOR, sizeof(mb), &mb) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.modtype.set) {
|
||||
memcpy(&mb.cf, &gwj->mod.modframe.set, sizeof(mb.cf));
|
||||
mb.modtype = gwj->mod.modtype.set;
|
||||
if (nla_put(skb, CGW_MOD_SET, sizeof(mb), &mb) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.csumfunc.crc8) {
|
||||
if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
|
||||
&gwj->mod.csum.crc8) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.csumfunc.xor) {
|
||||
if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
|
||||
&gwj->mod.csum.xor) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
|
||||
|
||||
if (gwj->ccgw.filter.can_id || gwj->ccgw.filter.can_mask) {
|
||||
if (nla_put(skb, CGW_FILTER, sizeof(struct can_filter),
|
||||
&gwj->ccgw.filter) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (nla_put_u32(skb, CGW_SRC_IF, gwj->ccgw.src_idx) < 0)
|
||||
goto cancel;
|
||||
|
||||
if (nla_put_u32(skb, CGW_DST_IF, gwj->ccgw.dst_idx) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
return nlmsg_end(skb, nlh);
|
||||
|
||||
cancel:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */
|
||||
static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct cgw_job *gwj = NULL;
|
||||
int idx = 0;
|
||||
int s_idx = cb->args[0];
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(gwj, &cgw_list, list) {
|
||||
if (idx < s_idx)
|
||||
goto cont;
|
||||
|
||||
if (cgw_put_job(skb, gwj, RTM_NEWROUTE, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI) < 0)
|
||||
break;
|
||||
cont:
|
||||
idx++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
cb->args[0] = idx;
|
||||
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static const struct nla_policy cgw_policy[CGW_MAX+1] = {
|
||||
[CGW_MOD_AND] = { .len = sizeof(struct cgw_frame_mod) },
|
||||
[CGW_MOD_OR] = { .len = sizeof(struct cgw_frame_mod) },
|
||||
[CGW_MOD_XOR] = { .len = sizeof(struct cgw_frame_mod) },
|
||||
[CGW_MOD_SET] = { .len = sizeof(struct cgw_frame_mod) },
|
||||
[CGW_CS_XOR] = { .len = sizeof(struct cgw_csum_xor) },
|
||||
[CGW_CS_CRC8] = { .len = sizeof(struct cgw_csum_crc8) },
|
||||
[CGW_SRC_IF] = { .type = NLA_U32 },
|
||||
[CGW_DST_IF] = { .type = NLA_U32 },
|
||||
[CGW_FILTER] = { .len = sizeof(struct can_filter) },
|
||||
[CGW_LIM_HOPS] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
/* check for common and gwtype specific attributes */
|
||||
static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
|
||||
u8 gwtype, void *gwtypeattr, u8 *limhops)
|
||||
{
|
||||
struct nlattr *tb[CGW_MAX+1];
|
||||
struct cgw_frame_mod mb;
|
||||
int modidx = 0;
|
||||
int err = 0;
|
||||
|
||||
/* initialize modification & checksum data space */
|
||||
memset(mod, 0, sizeof(*mod));
|
||||
|
||||
err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX,
|
||||
cgw_policy);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tb[CGW_LIM_HOPS]) {
|
||||
*limhops = nla_get_u8(tb[CGW_LIM_HOPS]);
|
||||
|
||||
if (*limhops < 1 || *limhops > max_hops)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check for AND/OR/XOR/SET modifications */
|
||||
|
||||
if (tb[CGW_MOD_AND]) {
|
||||
nla_memcpy(&mb, tb[CGW_MOD_AND], CGW_MODATTR_LEN);
|
||||
|
||||
canframecpy(&mod->modframe.and, &mb.cf);
|
||||
mod->modtype.and = mb.modtype;
|
||||
|
||||
if (mb.modtype & CGW_MOD_ID)
|
||||
mod->modfunc[modidx++] = mod_and_id;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DLC)
|
||||
mod->modfunc[modidx++] = mod_and_dlc;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DATA)
|
||||
mod->modfunc[modidx++] = mod_and_data;
|
||||
}
|
||||
|
||||
if (tb[CGW_MOD_OR]) {
|
||||
nla_memcpy(&mb, tb[CGW_MOD_OR], CGW_MODATTR_LEN);
|
||||
|
||||
canframecpy(&mod->modframe.or, &mb.cf);
|
||||
mod->modtype.or = mb.modtype;
|
||||
|
||||
if (mb.modtype & CGW_MOD_ID)
|
||||
mod->modfunc[modidx++] = mod_or_id;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DLC)
|
||||
mod->modfunc[modidx++] = mod_or_dlc;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DATA)
|
||||
mod->modfunc[modidx++] = mod_or_data;
|
||||
}
|
||||
|
||||
if (tb[CGW_MOD_XOR]) {
|
||||
nla_memcpy(&mb, tb[CGW_MOD_XOR], CGW_MODATTR_LEN);
|
||||
|
||||
canframecpy(&mod->modframe.xor, &mb.cf);
|
||||
mod->modtype.xor = mb.modtype;
|
||||
|
||||
if (mb.modtype & CGW_MOD_ID)
|
||||
mod->modfunc[modidx++] = mod_xor_id;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DLC)
|
||||
mod->modfunc[modidx++] = mod_xor_dlc;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DATA)
|
||||
mod->modfunc[modidx++] = mod_xor_data;
|
||||
}
|
||||
|
||||
if (tb[CGW_MOD_SET]) {
|
||||
nla_memcpy(&mb, tb[CGW_MOD_SET], CGW_MODATTR_LEN);
|
||||
|
||||
canframecpy(&mod->modframe.set, &mb.cf);
|
||||
mod->modtype.set = mb.modtype;
|
||||
|
||||
if (mb.modtype & CGW_MOD_ID)
|
||||
mod->modfunc[modidx++] = mod_set_id;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DLC)
|
||||
mod->modfunc[modidx++] = mod_set_dlc;
|
||||
|
||||
if (mb.modtype & CGW_MOD_DATA)
|
||||
mod->modfunc[modidx++] = mod_set_data;
|
||||
}
|
||||
|
||||
/* check for checksum operations after CAN frame modifications */
|
||||
if (modidx) {
|
||||
|
||||
if (tb[CGW_CS_CRC8]) {
|
||||
struct cgw_csum_crc8 *c = nla_data(tb[CGW_CS_CRC8]);
|
||||
|
||||
err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
|
||||
c->result_idx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
|
||||
CGW_CS_CRC8_LEN);
|
||||
|
||||
/*
|
||||
* select dedicated processing function to reduce
|
||||
* runtime operations in receive hot path.
|
||||
*/
|
||||
if (c->from_idx < 0 || c->to_idx < 0 ||
|
||||
c->result_idx < 0)
|
||||
mod->csumfunc.crc8 = cgw_csum_crc8_rel;
|
||||
else if (c->from_idx <= c->to_idx)
|
||||
mod->csumfunc.crc8 = cgw_csum_crc8_pos;
|
||||
else
|
||||
mod->csumfunc.crc8 = cgw_csum_crc8_neg;
|
||||
}
|
||||
|
||||
if (tb[CGW_CS_XOR]) {
|
||||
struct cgw_csum_xor *c = nla_data(tb[CGW_CS_XOR]);
|
||||
|
||||
err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
|
||||
c->result_idx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
|
||||
CGW_CS_XOR_LEN);
|
||||
|
||||
/*
|
||||
* select dedicated processing function to reduce
|
||||
* runtime operations in receive hot path.
|
||||
*/
|
||||
if (c->from_idx < 0 || c->to_idx < 0 ||
|
||||
c->result_idx < 0)
|
||||
mod->csumfunc.xor = cgw_csum_xor_rel;
|
||||
else if (c->from_idx <= c->to_idx)
|
||||
mod->csumfunc.xor = cgw_csum_xor_pos;
|
||||
else
|
||||
mod->csumfunc.xor = cgw_csum_xor_neg;
|
||||
}
|
||||
}
|
||||
|
||||
if (gwtype == CGW_TYPE_CAN_CAN) {
|
||||
|
||||
/* check CGW_TYPE_CAN_CAN specific attributes */
|
||||
|
||||
struct can_can_gw *ccgw = (struct can_can_gw *)gwtypeattr;
|
||||
memset(ccgw, 0, sizeof(*ccgw));
|
||||
|
||||
/* check for can_filter in attributes */
|
||||
if (tb[CGW_FILTER])
|
||||
nla_memcpy(&ccgw->filter, tb[CGW_FILTER],
|
||||
sizeof(struct can_filter));
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
/* specifying two interfaces is mandatory */
|
||||
if (!tb[CGW_SRC_IF] || !tb[CGW_DST_IF])
|
||||
return err;
|
||||
|
||||
ccgw->src_idx = nla_get_u32(tb[CGW_SRC_IF]);
|
||||
ccgw->dst_idx = nla_get_u32(tb[CGW_DST_IF]);
|
||||
|
||||
/* both indices set to 0 for flushing all routing entries */
|
||||
if (!ccgw->src_idx && !ccgw->dst_idx)
|
||||
return 0;
|
||||
|
||||
/* only one index set to 0 is an error */
|
||||
if (!ccgw->src_idx || !ccgw->dst_idx)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* add the checks for other gwtypes here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct rtcanmsg *r;
|
||||
struct cgw_job *gwj;
|
||||
u8 limhops = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (nlmsg_len(nlh) < sizeof(*r))
|
||||
return -EINVAL;
|
||||
|
||||
r = nlmsg_data(nlh);
|
||||
if (r->can_family != AF_CAN)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
/* so far we only support CAN -> CAN routings */
|
||||
if (r->gwtype != CGW_TYPE_CAN_CAN)
|
||||
return -EINVAL;
|
||||
|
||||
gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
|
||||
if (!gwj)
|
||||
return -ENOMEM;
|
||||
|
||||
gwj->handled_frames = 0;
|
||||
gwj->dropped_frames = 0;
|
||||
gwj->deleted_frames = 0;
|
||||
gwj->flags = r->flags;
|
||||
gwj->gwtype = r->gwtype;
|
||||
|
||||
err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw,
|
||||
&limhops);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
/* ifindex == 0 is not allowed for job creation */
|
||||
if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
|
||||
goto out;
|
||||
|
||||
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
|
||||
|
||||
if (!gwj->src.dev)
|
||||
goto out;
|
||||
|
||||
if (gwj->src.dev->type != ARPHRD_CAN)
|
||||
goto out;
|
||||
|
||||
gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx);
|
||||
|
||||
if (!gwj->dst.dev)
|
||||
goto out;
|
||||
|
||||
if (gwj->dst.dev->type != ARPHRD_CAN)
|
||||
goto out;
|
||||
|
||||
gwj->limit_hops = limhops;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
err = cgw_register_filter(gwj);
|
||||
if (!err)
|
||||
hlist_add_head_rcu(&gwj->list, &cgw_list);
|
||||
out:
|
||||
if (err)
|
||||
kmem_cache_free(cgw_cache, gwj);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void cgw_remove_all_jobs(void)
|
||||
{
|
||||
struct cgw_job *gwj = NULL;
|
||||
struct hlist_node *nx;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
||||
hlist_del(&gwj->list);
|
||||
cgw_unregister_filter(gwj);
|
||||
kmem_cache_free(cgw_cache, gwj);
|
||||
}
|
||||
}
|
||||
|
||||
static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct cgw_job *gwj = NULL;
|
||||
struct hlist_node *nx;
|
||||
struct rtcanmsg *r;
|
||||
struct cf_mod mod;
|
||||
struct can_can_gw ccgw;
|
||||
u8 limhops = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (nlmsg_len(nlh) < sizeof(*r))
|
||||
return -EINVAL;
|
||||
|
||||
r = nlmsg_data(nlh);
|
||||
if (r->can_family != AF_CAN)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
/* so far we only support CAN -> CAN routings */
|
||||
if (r->gwtype != CGW_TYPE_CAN_CAN)
|
||||
return -EINVAL;
|
||||
|
||||
err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* two interface indices both set to 0 => remove all entries */
|
||||
if (!ccgw.src_idx && !ccgw.dst_idx) {
|
||||
cgw_remove_all_jobs();
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* remove only the first matching entry */
|
||||
hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) {
|
||||
|
||||
if (gwj->flags != r->flags)
|
||||
continue;
|
||||
|
||||
if (gwj->limit_hops != limhops)
|
||||
continue;
|
||||
|
||||
if (memcmp(&gwj->mod, &mod, sizeof(mod)))
|
||||
continue;
|
||||
|
||||
/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
|
||||
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
|
||||
continue;
|
||||
|
||||
hlist_del(&gwj->list);
|
||||
cgw_unregister_filter(gwj);
|
||||
kmem_cache_free(cgw_cache, gwj);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __init int cgw_module_init(void)
|
||||
{
|
||||
/* sanitize given module parameter */
|
||||
max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS);
|
||||
|
||||
pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n",
|
||||
max_hops);
|
||||
|
||||
cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job),
|
||||
0, 0, NULL);
|
||||
|
||||
if (!cgw_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
/* set notifier */
|
||||
notifier.notifier_call = cgw_notifier;
|
||||
register_netdevice_notifier(¬ifier);
|
||||
|
||||
if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) {
|
||||
unregister_netdevice_notifier(¬ifier);
|
||||
kmem_cache_destroy(cgw_cache);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/* Only the first call to __rtnl_register can fail */
|
||||
__rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL);
|
||||
__rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __exit void cgw_module_exit(void)
|
||||
{
|
||||
rtnl_unregister_all(PF_CAN);
|
||||
|
||||
unregister_netdevice_notifier(¬ifier);
|
||||
|
||||
rtnl_lock();
|
||||
cgw_remove_all_jobs();
|
||||
rtnl_unlock();
|
||||
|
||||
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
||||
|
||||
kmem_cache_destroy(cgw_cache);
|
||||
}
|
||||
|
||||
module_init(cgw_module_init);
|
||||
module_exit(cgw_module_exit);
|
580
net/can/proc.c
Normal file
580
net/can/proc.c
Normal file
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
* proc.c - procfs support for Protocol family CAN core module
|
||||
*
|
||||
* Copyright (c) 2002-2007 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/can/core.h>
|
||||
|
||||
#include "af_can.h"
|
||||
|
||||
/*
|
||||
* proc filenames for the PF_CAN core
|
||||
*/
|
||||
|
||||
#define CAN_PROC_VERSION "version"
|
||||
#define CAN_PROC_STATS "stats"
|
||||
#define CAN_PROC_RESET_STATS "reset_stats"
|
||||
#define CAN_PROC_RCVLIST_ALL "rcvlist_all"
|
||||
#define CAN_PROC_RCVLIST_FIL "rcvlist_fil"
|
||||
#define CAN_PROC_RCVLIST_INV "rcvlist_inv"
|
||||
#define CAN_PROC_RCVLIST_SFF "rcvlist_sff"
|
||||
#define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
|
||||
#define CAN_PROC_RCVLIST_ERR "rcvlist_err"
|
||||
|
||||
static struct proc_dir_entry *can_dir;
|
||||
static struct proc_dir_entry *pde_version;
|
||||
static struct proc_dir_entry *pde_stats;
|
||||
static struct proc_dir_entry *pde_reset_stats;
|
||||
static struct proc_dir_entry *pde_rcvlist_all;
|
||||
static struct proc_dir_entry *pde_rcvlist_fil;
|
||||
static struct proc_dir_entry *pde_rcvlist_inv;
|
||||
static struct proc_dir_entry *pde_rcvlist_sff;
|
||||
static struct proc_dir_entry *pde_rcvlist_eff;
|
||||
static struct proc_dir_entry *pde_rcvlist_err;
|
||||
|
||||
static int user_reset;
|
||||
|
||||
static const char rx_list_name[][8] = {
|
||||
[RX_ERR] = "rx_err",
|
||||
[RX_ALL] = "rx_all",
|
||||
[RX_FIL] = "rx_fil",
|
||||
[RX_INV] = "rx_inv",
|
||||
};
|
||||
|
||||
/*
|
||||
* af_can statistics stuff
|
||||
*/
|
||||
|
||||
static void can_init_stats(void)
|
||||
{
|
||||
/*
|
||||
* This memset function is called from a timer context (when
|
||||
* can_stattimer is active which is the default) OR in a process
|
||||
* context (reading the proc_fs when can_stattimer is disabled).
|
||||
*/
|
||||
memset(&can_stats, 0, sizeof(can_stats));
|
||||
can_stats.jiffies_init = jiffies;
|
||||
|
||||
can_pstats.stats_reset++;
|
||||
|
||||
if (user_reset) {
|
||||
user_reset = 0;
|
||||
can_pstats.user_reset++;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
|
||||
unsigned long count)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (oldjif == newjif)
|
||||
return 0;
|
||||
|
||||
/* see can_stat_update() - this should NEVER happen! */
|
||||
if (count > (ULONG_MAX / HZ)) {
|
||||
printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
|
||||
count);
|
||||
return 99999999;
|
||||
}
|
||||
|
||||
rate = (count * HZ) / (newjif - oldjif);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
void can_stat_update(unsigned long data)
|
||||
{
|
||||
unsigned long j = jiffies; /* snapshot */
|
||||
|
||||
/* restart counting in timer context on user request */
|
||||
if (user_reset)
|
||||
can_init_stats();
|
||||
|
||||
/* restart counting on jiffies overflow */
|
||||
if (j < can_stats.jiffies_init)
|
||||
can_init_stats();
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (can_stats.rx_frames > (ULONG_MAX / HZ))
|
||||
can_init_stats();
|
||||
|
||||
/* prevent overflow in calc_rate() */
|
||||
if (can_stats.tx_frames > (ULONG_MAX / HZ))
|
||||
can_init_stats();
|
||||
|
||||
/* matches overflow - very improbable */
|
||||
if (can_stats.matches > (ULONG_MAX / 100))
|
||||
can_init_stats();
|
||||
|
||||
/* calc total values */
|
||||
if (can_stats.rx_frames)
|
||||
can_stats.total_rx_match_ratio = (can_stats.matches * 100) /
|
||||
can_stats.rx_frames;
|
||||
|
||||
can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j,
|
||||
can_stats.tx_frames);
|
||||
can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j,
|
||||
can_stats.rx_frames);
|
||||
|
||||
/* calc current values */
|
||||
if (can_stats.rx_frames_delta)
|
||||
can_stats.current_rx_match_ratio =
|
||||
(can_stats.matches_delta * 100) /
|
||||
can_stats.rx_frames_delta;
|
||||
|
||||
can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta);
|
||||
can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta);
|
||||
|
||||
/* check / update maximum values */
|
||||
if (can_stats.max_tx_rate < can_stats.current_tx_rate)
|
||||
can_stats.max_tx_rate = can_stats.current_tx_rate;
|
||||
|
||||
if (can_stats.max_rx_rate < can_stats.current_rx_rate)
|
||||
can_stats.max_rx_rate = can_stats.current_rx_rate;
|
||||
|
||||
if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio)
|
||||
can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio;
|
||||
|
||||
/* clear values for 'current rate' calculation */
|
||||
can_stats.tx_frames_delta = 0;
|
||||
can_stats.rx_frames_delta = 0;
|
||||
can_stats.matches_delta = 0;
|
||||
|
||||
/* restart timer (one second) */
|
||||
mod_timer(&can_stattimer, round_jiffies(jiffies + HZ));
|
||||
}
|
||||
|
||||
/*
|
||||
* proc read functions
|
||||
*/
|
||||
|
||||
static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct receiver *r;
|
||||
|
||||
hlist_for_each_entry_rcu(r, rx_list, list) {
|
||||
char *fmt = (r->can_id & CAN_EFF_FLAG)?
|
||||
" %-5s %08x %08x %pK %pK %8ld %s\n" :
|
||||
" %-5s %03x %08x %pK %pK %8ld %s\n";
|
||||
|
||||
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
|
||||
r->func, r->data, r->matches, r->ident);
|
||||
}
|
||||
}
|
||||
|
||||
static void can_print_recv_banner(struct seq_file *m)
|
||||
{
|
||||
/*
|
||||
* can1. 00000000 00000000 00000000
|
||||
* ....... 0 tp20
|
||||
*/
|
||||
seq_puts(m, " device can_id can_mask function"
|
||||
" userdata matches ident\n");
|
||||
}
|
||||
|
||||
static int can_stats_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames);
|
||||
seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames);
|
||||
seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
if (can_stattimer.function == can_stat_update) {
|
||||
seq_printf(m, " %8ld %% total match ratio (RXMR)\n",
|
||||
can_stats.total_rx_match_ratio);
|
||||
|
||||
seq_printf(m, " %8ld frames/s total tx rate (TXR)\n",
|
||||
can_stats.total_tx_rate);
|
||||
seq_printf(m, " %8ld frames/s total rx rate (RXR)\n",
|
||||
can_stats.total_rx_rate);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
seq_printf(m, " %8ld %% current match ratio (CRXMR)\n",
|
||||
can_stats.current_rx_match_ratio);
|
||||
|
||||
seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n",
|
||||
can_stats.current_tx_rate);
|
||||
seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n",
|
||||
can_stats.current_rx_rate);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
seq_printf(m, " %8ld %% max match ratio (MRXMR)\n",
|
||||
can_stats.max_rx_match_ratio);
|
||||
|
||||
seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n",
|
||||
can_stats.max_tx_rate);
|
||||
seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n",
|
||||
can_stats.max_rx_rate);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
seq_printf(m, " %8ld current receive list entries (CRCV)\n",
|
||||
can_pstats.rcv_entries);
|
||||
seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
|
||||
can_pstats.rcv_entries_max);
|
||||
|
||||
if (can_pstats.stats_reset)
|
||||
seq_printf(m, "\n %8ld statistic resets (STR)\n",
|
||||
can_pstats.stats_reset);
|
||||
|
||||
if (can_pstats.user_reset)
|
||||
seq_printf(m, " %8ld user statistic resets (USTR)\n",
|
||||
can_pstats.user_reset);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_stats_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_stats_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations can_stats_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_stats_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int can_reset_stats_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
user_reset = 1;
|
||||
|
||||
if (can_stattimer.function == can_stat_update) {
|
||||
seq_printf(m, "Scheduled statistic reset #%ld.\n",
|
||||
can_pstats.stats_reset + 1);
|
||||
|
||||
} else {
|
||||
if (can_stats.jiffies_init != jiffies)
|
||||
can_init_stats();
|
||||
|
||||
seq_printf(m, "Performed statistic reset #%ld.\n",
|
||||
can_pstats.stats_reset);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_reset_stats_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_reset_stats_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations can_reset_stats_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_reset_stats_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int can_version_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "%s\n", CAN_VERSION_STRING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_version_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_version_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations can_version_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_version_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static inline void can_rcvlist_proc_show_one(struct seq_file *m, int idx,
|
||||
struct net_device *dev,
|
||||
struct dev_rcv_lists *d)
|
||||
{
|
||||
if (!hlist_empty(&d->rx[idx])) {
|
||||
can_print_recv_banner(m);
|
||||
can_print_rcvlist(m, &d->rx[idx], dev);
|
||||
} else
|
||||
seq_printf(m, " (%s: no entry)\n", DNAME(dev));
|
||||
|
||||
}
|
||||
|
||||
static int can_rcvlist_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
/* double cast to prevent GCC warning */
|
||||
int idx = (int)(long)m->private;
|
||||
struct net_device *dev;
|
||||
struct dev_rcv_lists *d;
|
||||
|
||||
seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* receive list for 'all' CAN devices (dev == NULL) */
|
||||
d = &can_rx_alldev_list;
|
||||
can_rcvlist_proc_show_one(m, idx, NULL, d);
|
||||
|
||||
/* receive list for registered CAN devices */
|
||||
for_each_netdev_rcu(&init_net, dev) {
|
||||
if (dev->type == ARPHRD_CAN && dev->ml_priv)
|
||||
can_rcvlist_proc_show_one(m, idx, dev, dev->ml_priv);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_rcvlist_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_rcvlist_proc_show, PDE_DATA(inode));
|
||||
}
|
||||
|
||||
static const struct file_operations can_rcvlist_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_rcvlist_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static inline void can_rcvlist_proc_show_array(struct seq_file *m,
|
||||
struct net_device *dev,
|
||||
struct hlist_head *rcv_array,
|
||||
unsigned int rcv_array_sz)
|
||||
{
|
||||
unsigned int i;
|
||||
int all_empty = 1;
|
||||
|
||||
/* check whether at least one list is non-empty */
|
||||
for (i = 0; i < rcv_array_sz; i++)
|
||||
if (!hlist_empty(&rcv_array[i])) {
|
||||
all_empty = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!all_empty) {
|
||||
can_print_recv_banner(m);
|
||||
for (i = 0; i < rcv_array_sz; i++) {
|
||||
if (!hlist_empty(&rcv_array[i]))
|
||||
can_print_rcvlist(m, &rcv_array[i], dev);
|
||||
}
|
||||
} else
|
||||
seq_printf(m, " (%s: no entry)\n", DNAME(dev));
|
||||
}
|
||||
|
||||
static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct dev_rcv_lists *d;
|
||||
|
||||
/* RX_SFF */
|
||||
seq_puts(m, "\nreceive list 'rx_sff':\n");
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* sff receive list for 'all' CAN devices (dev == NULL) */
|
||||
d = &can_rx_alldev_list;
|
||||
can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));
|
||||
|
||||
/* sff receive list for registered CAN devices */
|
||||
for_each_netdev_rcu(&init_net, dev) {
|
||||
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
|
||||
d = dev->ml_priv;
|
||||
can_rcvlist_proc_show_array(m, dev, d->rx_sff,
|
||||
ARRAY_SIZE(d->rx_sff));
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_rcvlist_sff_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations can_rcvlist_sff_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_rcvlist_sff_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
|
||||
static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct dev_rcv_lists *d;
|
||||
|
||||
/* RX_EFF */
|
||||
seq_puts(m, "\nreceive list 'rx_eff':\n");
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* eff receive list for 'all' CAN devices (dev == NULL) */
|
||||
d = &can_rx_alldev_list;
|
||||
can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff));
|
||||
|
||||
/* eff receive list for registered CAN devices */
|
||||
for_each_netdev_rcu(&init_net, dev) {
|
||||
if (dev->type == ARPHRD_CAN && dev->ml_priv) {
|
||||
d = dev->ml_priv;
|
||||
can_rcvlist_proc_show_array(m, dev, d->rx_eff,
|
||||
ARRAY_SIZE(d->rx_eff));
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
seq_putc(m, '\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, can_rcvlist_eff_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations can_rcvlist_eff_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = can_rcvlist_eff_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* proc utility functions
|
||||
*/
|
||||
|
||||
static void can_remove_proc_readentry(const char *name)
|
||||
{
|
||||
if (can_dir)
|
||||
remove_proc_entry(name, can_dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* can_init_proc - create main CAN proc directory and procfs entries
|
||||
*/
|
||||
void can_init_proc(void)
|
||||
{
|
||||
/* create /proc/net/can directory */
|
||||
can_dir = proc_mkdir("can", init_net.proc_net);
|
||||
|
||||
if (!can_dir) {
|
||||
printk(KERN_INFO "can: failed to create /proc/net/can . "
|
||||
"CONFIG_PROC_FS missing?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* own procfs entries from the AF_CAN core */
|
||||
pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir,
|
||||
&can_version_proc_fops);
|
||||
pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir,
|
||||
&can_stats_proc_fops);
|
||||
pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir,
|
||||
&can_reset_stats_proc_fops);
|
||||
pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir,
|
||||
&can_rcvlist_proc_fops, (void *)RX_ERR);
|
||||
pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir,
|
||||
&can_rcvlist_proc_fops, (void *)RX_ALL);
|
||||
pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir,
|
||||
&can_rcvlist_proc_fops, (void *)RX_FIL);
|
||||
pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
|
||||
&can_rcvlist_proc_fops, (void *)RX_INV);
|
||||
pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
|
||||
&can_rcvlist_eff_proc_fops);
|
||||
pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
|
||||
&can_rcvlist_sff_proc_fops);
|
||||
}
|
||||
|
||||
/*
|
||||
* can_remove_proc - remove procfs entries and main CAN proc directory
|
||||
*/
|
||||
void can_remove_proc(void)
|
||||
{
|
||||
if (pde_version)
|
||||
can_remove_proc_readentry(CAN_PROC_VERSION);
|
||||
|
||||
if (pde_stats)
|
||||
can_remove_proc_readentry(CAN_PROC_STATS);
|
||||
|
||||
if (pde_reset_stats)
|
||||
can_remove_proc_readentry(CAN_PROC_RESET_STATS);
|
||||
|
||||
if (pde_rcvlist_err)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
|
||||
|
||||
if (pde_rcvlist_all)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
|
||||
|
||||
if (pde_rcvlist_fil)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
|
||||
|
||||
if (pde_rcvlist_inv)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
|
||||
|
||||
if (pde_rcvlist_eff)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
|
||||
|
||||
if (pde_rcvlist_sff)
|
||||
can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
|
||||
|
||||
if (can_dir)
|
||||
remove_proc_entry("can", init_net.proc_net);
|
||||
}
|
828
net/can/raw.c
Normal file
828
net/can/raw.c
Normal file
|
@ -0,0 +1,828 @@
|
|||
/*
|
||||
* raw.c - Raw sockets for protocol family CAN
|
||||
*
|
||||
* Copyright (c) 2002-2007 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/skb.h>
|
||||
#include <linux/can/raw.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#define CAN_RAW_VERSION CAN_VERSION
|
||||
static __initconst const char banner[] =
|
||||
KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
|
||||
|
||||
MODULE_DESCRIPTION("PF_CAN raw protocol");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
|
||||
MODULE_ALIAS("can-proto-1");
|
||||
|
||||
#define MASK_ALL 0
|
||||
|
||||
/*
|
||||
* A raw socket has a list of can_filters attached to it, each receiving
|
||||
* the CAN frames matching that filter. If the filter list is empty,
|
||||
* no CAN frames will be received by the socket. The default after
|
||||
* opening the socket, is to have one filter which receives all frames.
|
||||
* The filter list is allocated dynamically with the exception of the
|
||||
* list containing only one item. This common case is optimized by
|
||||
* storing the single filter in dfilter, to avoid using dynamic memory.
|
||||
*/
|
||||
|
||||
struct raw_sock {
|
||||
struct sock sk;
|
||||
int bound;
|
||||
int ifindex;
|
||||
struct notifier_block notifier;
|
||||
int loopback;
|
||||
int recv_own_msgs;
|
||||
int fd_frames;
|
||||
int count; /* number of active filters */
|
||||
struct can_filter dfilter; /* default/single filter */
|
||||
struct can_filter *filter; /* pointer to filter(s) */
|
||||
can_err_mask_t err_mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return pointer to store the extra msg flags for raw_recvmsg().
|
||||
* We use the space of one unsigned int beyond the 'struct sockaddr_can'
|
||||
* in skb->cb.
|
||||
*/
|
||||
static inline unsigned int *raw_flags(struct sk_buff *skb)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(skb->cb) <= (sizeof(struct sockaddr_can) +
|
||||
sizeof(unsigned int)));
|
||||
|
||||
/* return pointer after struct sockaddr_can */
|
||||
return (unsigned int *)(&((struct sockaddr_can *)skb->cb)[1]);
|
||||
}
|
||||
|
||||
static inline struct raw_sock *raw_sk(const struct sock *sk)
|
||||
{
|
||||
return (struct raw_sock *)sk;
|
||||
}
|
||||
|
||||
static void raw_rcv(struct sk_buff *oskb, void *data)
|
||||
{
|
||||
struct sock *sk = (struct sock *)data;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
struct sockaddr_can *addr;
|
||||
struct sk_buff *skb;
|
||||
unsigned int *pflags;
|
||||
|
||||
/* check the received tx sock reference */
|
||||
if (!ro->recv_own_msgs && oskb->sk == sk)
|
||||
return;
|
||||
|
||||
/* do not pass non-CAN2.0 frames to a legacy socket */
|
||||
if (!ro->fd_frames && oskb->len != CAN_MTU)
|
||||
return;
|
||||
|
||||
/* clone the given skb to be able to enqueue it into the rcv queue */
|
||||
skb = skb_clone(oskb, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Put the datagram to the queue so that raw_recvmsg() can
|
||||
* get it from there. We need to pass the interface index to
|
||||
* raw_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
|
||||
* containing the interface index.
|
||||
*/
|
||||
|
||||
BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
|
||||
addr = (struct sockaddr_can *)skb->cb;
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->can_family = AF_CAN;
|
||||
addr->can_ifindex = skb->dev->ifindex;
|
||||
|
||||
/* add CAN specific message flags for raw_recvmsg() */
|
||||
pflags = raw_flags(skb);
|
||||
*pflags = 0;
|
||||
if (oskb->sk)
|
||||
*pflags |= MSG_DONTROUTE;
|
||||
if (oskb->sk == sk)
|
||||
*pflags |= MSG_CONFIRM;
|
||||
|
||||
if (sock_queue_rcv_skb(sk, skb) < 0)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int raw_enable_filters(struct net_device *dev, struct sock *sk,
|
||||
struct can_filter *filter, int count)
|
||||
{
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
err = can_rx_register(dev, filter[i].can_id,
|
||||
filter[i].can_mask,
|
||||
raw_rcv, sk, "raw");
|
||||
if (err) {
|
||||
/* clean up successfully registered filters */
|
||||
while (--i >= 0)
|
||||
can_rx_unregister(dev, filter[i].can_id,
|
||||
filter[i].can_mask,
|
||||
raw_rcv, sk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
|
||||
can_err_mask_t err_mask)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (err_mask)
|
||||
err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
|
||||
raw_rcv, sk, "raw");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void raw_disable_filters(struct net_device *dev, struct sock *sk,
|
||||
struct can_filter *filter, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
|
||||
raw_rcv, sk);
|
||||
}
|
||||
|
||||
static inline void raw_disable_errfilter(struct net_device *dev,
|
||||
struct sock *sk,
|
||||
can_err_mask_t err_mask)
|
||||
|
||||
{
|
||||
if (err_mask)
|
||||
can_rx_unregister(dev, 0, err_mask | CAN_ERR_FLAG,
|
||||
raw_rcv, sk);
|
||||
}
|
||||
|
||||
static inline void raw_disable_allfilters(struct net_device *dev,
|
||||
struct sock *sk)
|
||||
{
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
|
||||
raw_disable_filters(dev, sk, ro->filter, ro->count);
|
||||
raw_disable_errfilter(dev, sk, ro->err_mask);
|
||||
}
|
||||
|
||||
static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
|
||||
{
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
int err;
|
||||
|
||||
err = raw_enable_filters(dev, sk, ro->filter, ro->count);
|
||||
if (!err) {
|
||||
err = raw_enable_errfilter(dev, sk, ro->err_mask);
|
||||
if (err)
|
||||
raw_disable_filters(dev, sk, ro->filter, ro->count);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_notifier(struct notifier_block *nb,
|
||||
unsigned long msg, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
|
||||
struct sock *sk = &ro->sk;
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (ro->ifindex != dev->ifindex)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (msg) {
|
||||
|
||||
case NETDEV_UNREGISTER:
|
||||
lock_sock(sk);
|
||||
/* remove current filters & unregister */
|
||||
if (ro->bound)
|
||||
raw_disable_allfilters(dev, sk);
|
||||
|
||||
if (ro->count > 1)
|
||||
kfree(ro->filter);
|
||||
|
||||
ro->ifindex = 0;
|
||||
ro->bound = 0;
|
||||
ro->count = 0;
|
||||
release_sock(sk);
|
||||
|
||||
sk->sk_err = ENODEV;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_error_report(sk);
|
||||
break;
|
||||
|
||||
case NETDEV_DOWN:
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_error_report(sk);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int raw_init(struct sock *sk)
|
||||
{
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
|
||||
ro->bound = 0;
|
||||
ro->ifindex = 0;
|
||||
|
||||
/* set default filter to single entry dfilter */
|
||||
ro->dfilter.can_id = 0;
|
||||
ro->dfilter.can_mask = MASK_ALL;
|
||||
ro->filter = &ro->dfilter;
|
||||
ro->count = 1;
|
||||
|
||||
/* set default loopback behaviour */
|
||||
ro->loopback = 1;
|
||||
ro->recv_own_msgs = 0;
|
||||
ro->fd_frames = 0;
|
||||
|
||||
/* set notifier */
|
||||
ro->notifier.notifier_call = raw_notifier;
|
||||
|
||||
register_netdevice_notifier(&ro->notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro;
|
||||
|
||||
if (!sk)
|
||||
return 0;
|
||||
|
||||
ro = raw_sk(sk);
|
||||
|
||||
unregister_netdevice_notifier(&ro->notifier);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
/* remove current filters & unregister */
|
||||
if (ro->bound) {
|
||||
if (ro->ifindex) {
|
||||
struct net_device *dev;
|
||||
|
||||
dev = dev_get_by_index(&init_net, ro->ifindex);
|
||||
if (dev) {
|
||||
raw_disable_allfilters(dev, sk);
|
||||
dev_put(dev);
|
||||
}
|
||||
} else
|
||||
raw_disable_allfilters(NULL, sk);
|
||||
}
|
||||
|
||||
if (ro->count > 1)
|
||||
kfree(ro->filter);
|
||||
|
||||
ro->ifindex = 0;
|
||||
ro->bound = 0;
|
||||
ro->count = 0;
|
||||
|
||||
sock_orphan(sk);
|
||||
sock->sk = NULL;
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
|
||||
{
|
||||
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
int ifindex;
|
||||
int err = 0;
|
||||
int notify_enetdown = 0;
|
||||
|
||||
if (len < sizeof(*addr))
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (ro->bound && addr->can_ifindex == ro->ifindex)
|
||||
goto out;
|
||||
|
||||
if (addr->can_ifindex) {
|
||||
struct net_device *dev;
|
||||
|
||||
dev = dev_get_by_index(&init_net, addr->can_ifindex);
|
||||
if (!dev) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
dev_put(dev);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (!(dev->flags & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
|
||||
ifindex = dev->ifindex;
|
||||
|
||||
/* filters set by default/setsockopt */
|
||||
err = raw_enable_allfilters(dev, sk);
|
||||
dev_put(dev);
|
||||
} else {
|
||||
ifindex = 0;
|
||||
|
||||
/* filters set by default/setsockopt */
|
||||
err = raw_enable_allfilters(NULL, sk);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
if (ro->bound) {
|
||||
/* unregister old filters */
|
||||
if (ro->ifindex) {
|
||||
struct net_device *dev;
|
||||
|
||||
dev = dev_get_by_index(&init_net, ro->ifindex);
|
||||
if (dev) {
|
||||
raw_disable_allfilters(dev, sk);
|
||||
dev_put(dev);
|
||||
}
|
||||
} else
|
||||
raw_disable_allfilters(NULL, sk);
|
||||
}
|
||||
ro->ifindex = ifindex;
|
||||
ro->bound = 1;
|
||||
}
|
||||
|
||||
out:
|
||||
release_sock(sk);
|
||||
|
||||
if (notify_enetdown) {
|
||||
sk->sk_err = ENETDOWN;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk->sk_error_report(sk);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
int *len, int peer)
|
||||
{
|
||||
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
|
||||
if (peer)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->can_family = AF_CAN;
|
||||
addr->can_ifindex = ro->ifindex;
|
||||
|
||||
*len = sizeof(*addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_setsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, unsigned int optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
struct can_filter *filter = NULL; /* dyn. alloc'ed filters */
|
||||
struct can_filter sfilter; /* single filter */
|
||||
struct net_device *dev = NULL;
|
||||
can_err_mask_t err_mask = 0;
|
||||
int count = 0;
|
||||
int err = 0;
|
||||
|
||||
if (level != SOL_CAN_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
switch (optname) {
|
||||
|
||||
case CAN_RAW_FILTER:
|
||||
if (optlen % sizeof(struct can_filter) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
count = optlen / sizeof(struct can_filter);
|
||||
|
||||
if (count > 1) {
|
||||
/* filter does not fit into dfilter => alloc space */
|
||||
filter = memdup_user(optval, optlen);
|
||||
if (IS_ERR(filter))
|
||||
return PTR_ERR(filter);
|
||||
} else if (count == 1) {
|
||||
if (copy_from_user(&sfilter, optval, sizeof(sfilter)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (ro->bound && ro->ifindex)
|
||||
dev = dev_get_by_index(&init_net, ro->ifindex);
|
||||
|
||||
if (ro->bound) {
|
||||
/* (try to) register the new filters */
|
||||
if (count == 1)
|
||||
err = raw_enable_filters(dev, sk, &sfilter, 1);
|
||||
else
|
||||
err = raw_enable_filters(dev, sk, filter,
|
||||
count);
|
||||
if (err) {
|
||||
if (count > 1)
|
||||
kfree(filter);
|
||||
goto out_fil;
|
||||
}
|
||||
|
||||
/* remove old filter registrations */
|
||||
raw_disable_filters(dev, sk, ro->filter, ro->count);
|
||||
}
|
||||
|
||||
/* remove old filter space */
|
||||
if (ro->count > 1)
|
||||
kfree(ro->filter);
|
||||
|
||||
/* link new filters to the socket */
|
||||
if (count == 1) {
|
||||
/* copy filter data for single filter */
|
||||
ro->dfilter = sfilter;
|
||||
filter = &ro->dfilter;
|
||||
}
|
||||
ro->filter = filter;
|
||||
ro->count = count;
|
||||
|
||||
out_fil:
|
||||
if (dev)
|
||||
dev_put(dev);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
break;
|
||||
|
||||
case CAN_RAW_ERR_FILTER:
|
||||
if (optlen != sizeof(err_mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&err_mask, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
err_mask &= CAN_ERR_MASK;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (ro->bound && ro->ifindex)
|
||||
dev = dev_get_by_index(&init_net, ro->ifindex);
|
||||
|
||||
/* remove current error mask */
|
||||
if (ro->bound) {
|
||||
/* (try to) register the new err_mask */
|
||||
err = raw_enable_errfilter(dev, sk, err_mask);
|
||||
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* remove old err_mask registration */
|
||||
raw_disable_errfilter(dev, sk, ro->err_mask);
|
||||
}
|
||||
|
||||
/* link new err_mask to the socket */
|
||||
ro->err_mask = err_mask;
|
||||
|
||||
out_err:
|
||||
if (dev)
|
||||
dev_put(dev);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
break;
|
||||
|
||||
case CAN_RAW_LOOPBACK:
|
||||
if (optlen != sizeof(ro->loopback))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&ro->loopback, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case CAN_RAW_RECV_OWN_MSGS:
|
||||
if (optlen != sizeof(ro->recv_own_msgs))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&ro->recv_own_msgs, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
case CAN_RAW_FD_FRAMES:
|
||||
if (optlen != sizeof(ro->fd_frames))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&ro->fd_frames, optval, optlen))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_getsockopt(struct socket *sock, int level, int optname,
|
||||
char __user *optval, int __user *optlen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
int len;
|
||||
void *val;
|
||||
int err = 0;
|
||||
|
||||
if (level != SOL_CAN_RAW)
|
||||
return -EINVAL;
|
||||
if (get_user(len, optlen))
|
||||
return -EFAULT;
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (optname) {
|
||||
|
||||
case CAN_RAW_FILTER:
|
||||
lock_sock(sk);
|
||||
if (ro->count > 0) {
|
||||
int fsize = ro->count * sizeof(struct can_filter);
|
||||
if (len > fsize)
|
||||
len = fsize;
|
||||
if (copy_to_user(optval, ro->filter, len))
|
||||
err = -EFAULT;
|
||||
} else
|
||||
len = 0;
|
||||
release_sock(sk);
|
||||
|
||||
if (!err)
|
||||
err = put_user(len, optlen);
|
||||
return err;
|
||||
|
||||
case CAN_RAW_ERR_FILTER:
|
||||
if (len > sizeof(can_err_mask_t))
|
||||
len = sizeof(can_err_mask_t);
|
||||
val = &ro->err_mask;
|
||||
break;
|
||||
|
||||
case CAN_RAW_LOOPBACK:
|
||||
if (len > sizeof(int))
|
||||
len = sizeof(int);
|
||||
val = &ro->loopback;
|
||||
break;
|
||||
|
||||
case CAN_RAW_RECV_OWN_MSGS:
|
||||
if (len > sizeof(int))
|
||||
len = sizeof(int);
|
||||
val = &ro->recv_own_msgs;
|
||||
break;
|
||||
|
||||
case CAN_RAW_FD_FRAMES:
|
||||
if (len > sizeof(int))
|
||||
len = sizeof(int);
|
||||
val = &ro->fd_frames;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
||||
if (put_user(len, optlen))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(optval, val, len))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t size)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct raw_sock *ro = raw_sk(sk);
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev;
|
||||
int ifindex;
|
||||
int err;
|
||||
|
||||
if (msg->msg_name) {
|
||||
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
|
||||
|
||||
if (msg->msg_namelen < sizeof(*addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (addr->can_family != AF_CAN)
|
||||
return -EINVAL;
|
||||
|
||||
ifindex = addr->can_ifindex;
|
||||
} else
|
||||
ifindex = ro->ifindex;
|
||||
|
||||
if (ro->fd_frames) {
|
||||
if (unlikely(size != CANFD_MTU && size != CAN_MTU))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (unlikely(size != CAN_MTU))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = dev_get_by_index(&init_net, ifindex);
|
||||
if (!dev)
|
||||
return -ENXIO;
|
||||
|
||||
skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
|
||||
msg->msg_flags & MSG_DONTWAIT, &err);
|
||||
if (!skb)
|
||||
goto put_dev;
|
||||
|
||||
can_skb_reserve(skb);
|
||||
can_skb_prv(skb)->ifindex = dev->ifindex;
|
||||
|
||||
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
|
||||
if (err < 0)
|
||||
goto free_skb;
|
||||
|
||||
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
|
||||
|
||||
skb->dev = dev;
|
||||
skb->sk = sk;
|
||||
skb->priority = sk->sk_priority;
|
||||
|
||||
err = can_send(skb, ro->loopback);
|
||||
|
||||
dev_put(dev);
|
||||
|
||||
if (err)
|
||||
goto send_failed;
|
||||
|
||||
return size;
|
||||
|
||||
free_skb:
|
||||
kfree_skb(skb);
|
||||
put_dev:
|
||||
dev_put(dev);
|
||||
send_failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t size, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
int noblock;
|
||||
|
||||
noblock = flags & MSG_DONTWAIT;
|
||||
flags &= ~MSG_DONTWAIT;
|
||||
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (!skb)
|
||||
return err;
|
||||
|
||||
if (size < skb->len)
|
||||
msg->msg_flags |= MSG_TRUNC;
|
||||
else
|
||||
size = skb->len;
|
||||
|
||||
err = memcpy_toiovec(msg->msg_iov, skb->data, size);
|
||||
if (err < 0) {
|
||||
skb_free_datagram(sk, skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
sock_recv_ts_and_drops(msg, sk, skb);
|
||||
|
||||
if (msg->msg_name) {
|
||||
__sockaddr_check_size(sizeof(struct sockaddr_can));
|
||||
msg->msg_namelen = sizeof(struct sockaddr_can);
|
||||
memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
|
||||
}
|
||||
|
||||
/* assign the flags that have been recorded in raw_rcv() */
|
||||
msg->msg_flags |= *(raw_flags(skb));
|
||||
|
||||
skb_free_datagram(sk, skb);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct proto_ops raw_ops = {
|
||||
.family = PF_CAN,
|
||||
.release = raw_release,
|
||||
.bind = raw_bind,
|
||||
.connect = sock_no_connect,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.accept = sock_no_accept,
|
||||
.getname = raw_getname,
|
||||
.poll = datagram_poll,
|
||||
.ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = raw_setsockopt,
|
||||
.getsockopt = raw_getsockopt,
|
||||
.sendmsg = raw_sendmsg,
|
||||
.recvmsg = raw_recvmsg,
|
||||
.mmap = sock_no_mmap,
|
||||
.sendpage = sock_no_sendpage,
|
||||
};
|
||||
|
||||
static struct proto raw_proto __read_mostly = {
|
||||
.name = "CAN_RAW",
|
||||
.owner = THIS_MODULE,
|
||||
.obj_size = sizeof(struct raw_sock),
|
||||
.init = raw_init,
|
||||
};
|
||||
|
||||
static const struct can_proto raw_can_proto = {
|
||||
.type = SOCK_RAW,
|
||||
.protocol = CAN_RAW,
|
||||
.ops = &raw_ops,
|
||||
.prot = &raw_proto,
|
||||
};
|
||||
|
||||
static __init int raw_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk(banner);
|
||||
|
||||
err = can_proto_register(&raw_can_proto);
|
||||
if (err < 0)
|
||||
printk(KERN_ERR "can: registration of raw protocol failed\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static __exit void raw_module_exit(void)
|
||||
{
|
||||
can_proto_unregister(&raw_can_proto);
|
||||
}
|
||||
|
||||
module_init(raw_module_init);
|
||||
module_exit(raw_module_exit);
|
Loading…
Add table
Add a link
Reference in a new issue