* [patch 2/7] CAN: Add PF_CAN core module
2007-05-16 14:51 [patch 0/7] CAN: Add new PF_CAN protocol family Urs Thuermann
@ 2007-05-16 14:51 ` Urs Thuermann
2007-05-16 16:35 ` Arnaldo Carvalho de Melo
2007-05-18 0:59 ` Paul E. McKenney
0 siblings, 2 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-05-16 14:51 UTC (permalink / raw)
To: netdev; +Cc: Thomas Gleixner, Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60451 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 98 ++++
include/linux/can/core.h | 88 +++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 1063 +++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 122 +++++
net/can/proc.c | 660 +++++++++++++++++++++++++++++
9 files changed, 2064 insertions(+)
Index: linux-2.6.22-rc1-git4/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/include/linux/can.h 2007-05-16 10:02:06.000000000 +0200
@@ -0,0 +1,98 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+typedef __u32 canid_t;
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ struct { int lcu, type; } bap;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: linux-2.6.22-rc1-git4/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/include/linux/can/core.h 2007-05-16 09:45:00.000000000 +0200
@@ -0,0 +1,88 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070508"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+#define CAN_PROC_DIR "net/can" /* /proc/... */
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_dev_register(struct net_device *dev,
+ void (*func)(unsigned long msg, void *),
+ void *data);
+
+extern int can_dev_unregister(struct net_device *dev,
+ void (*func)(unsigned long msg, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: linux-2.6.22-rc1-git4/net/Kconfig
===================================================================
--- linux-2.6.22-rc1-git4.orig/net/Kconfig 2007-05-16 09:44:54.000000000 +0200
+++ linux-2.6.22-rc1-git4/net/Kconfig 2007-05-16 09:45:00.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: linux-2.6.22-rc1-git4/net/Makefile
===================================================================
--- linux-2.6.22-rc1-git4.orig/net/Makefile 2007-05-16 09:44:54.000000000 +0200
+++ linux-2.6.22-rc1-git4/net/Makefile 2007-05-16 09:45:00.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: linux-2.6.22-rc1-git4/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/net/can/Kconfig 2007-05-16 10:01:14.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: linux-2.6.22-rc1-git4/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/net/can/Makefile 2007-05-16 10:01:14.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: linux-2.6.22-rc1-git4/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/net/can/af_can.c 2007-05-16 10:02:06.000000000 +0200
@@ -0,0 +1,1063 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+struct notifier {
+ struct list_head list;
+ struct net_device *dev;
+ void (*func)(unsigned long msg, void *data);
+ void *data;
+};
+
+static LIST_HEAD(notifier_list);
+static DEFINE_RWLOCK(notifier_lock);
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /* In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS)
+ printk(KERN_INFO "can: request_module(%s) not"
+ " implemented.\n", module_name);
+ else if (ret)
+ printk(KERN_ERR "can: request_module(%s) failed\n",
+ module_name);
+ }
+
+ /* check for success and correct type */
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
+ return -EPROTONOSUPPORT;
+
+ if (cp->capability >= 0 && !capable(cp->capability))
+ return -EPERM;
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * Return:
+ * 0 on success
+ * -ENETDOWN when the selected interface is down
+ * -ENOBUFS on full driver queue (see net_xmit_errno())
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ struct sock **tx_sk = (struct sock **)skb->cb;
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (loop) {
+ /* local loopback of sent CAN frames (default) */
+
+ /* indication for the CAN driver: do loopback */
+ *tx_sk = skb->sk;
+
+ /*
+ * The reference to the originating sock may be also required
+ * by the receiving socket to indicate (and ignore) his own
+ * sent data. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ */
+
+ /* interface not capabable to do the loopback itself? */
+ if (!(skb->dev->flags & IFF_LOOPBACK)) {
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ /* perform the local loopback here */
+ newskb->protocol = htons(ETH_P_CAN);
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(newskb);
+ }
+ } else {
+ /* indication for the CAN driver: no loopback required */
+ *tx_sk = NULL;
+ }
+
+ if (!(skb->dev->flags & IFF_UP))
+ return -ENETDOWN;
+
+ /* send to netdevice */
+ err = dev_queue_xmit(skb);
+ if (err > 0)
+ err = net_xmit_errno(err);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx_err;
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* reduce condition testing at receive time */
+ *can_id &= *mask;
+
+ /* inverse can_id/can_mask filter */
+ if (inv)
+ return &d->rx_inv;
+
+ /* mask == 0 => no condition testing at receive time */
+ if (!(*mask))
+ return &d->rx_all;
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx_eff;
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ return &d->rx_sff[*can_id];
+ }
+
+ /* default: filter via can_id/can_mask */
+ return &d->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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
+ dev, can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+static void can_rcv_lists_delete(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ kfree(d);
+}
+
+static void can_rx_delete(struct rcu_head *rp)
+{
+ struct receiver *r = container_of(rp, struct receiver, rcu);
+
+ kmem_cache_free(rcv_cache, r);
+}
+
+static void can_rx_delete_all(struct hlist_head *rl)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ hlist_for_each_entry_rcu(r, n, rl, list) {
+ hlist_del_rcu(&r->list);
+ call_rcu(&r->rcu, can_rx_delete);
+ }
+}
+
+/**
+ * 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.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ */
+int 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
+ dev, can_id, mask, func, data);
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ ret = -ENODEV;
+ 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(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ DBG("receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+ ret = -EINVAL;
+ r = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ out:
+ spin_unlock_bh(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx_err, list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx_all, list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx_fil, list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx_inv, list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx_eff, list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out "
+ "of range\n", proto);
+ return -EINVAL;
+ }
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already "
+ "registered\n", proto);
+ return -EBUSY;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+ return -ESRCH;
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(can_proto_unregister);
+
+/**
+ * can_dev_register - subscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on status change
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Invokes the callback function with the status 'msg' and the given
+ * parameter 'data' on a status change of the given CAN network device.
+ *
+ * Return:
+ * 0 on success
+ * -ENOMEM on missing mem to create subscription entry
+ * -ENODEV unknown device
+ */
+int can_dev_register(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
+{
+ struct notifier *n;
+
+ DBG("called for %s\n", dev->name);
+
+ if (!dev || dev->type != ARPHRD_CAN)
+ return -ENODEV;
+
+ n = kmalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ return -ENOMEM;
+
+ n->dev = dev;
+ n->func = func;
+ n->data = data;
+
+ write_lock(¬ifier_lock);
+ list_add(&n->list, ¬ifier_list);
+ write_unlock(¬ifier_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(can_dev_register);
+
+/**
+ * can_dev_unregister - unsubscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Removes subscription entry depending on given (subscription) values.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ */
+int can_dev_unregister(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
+{
+ struct notifier *n, *next;
+ int ret = -EINVAL;
+
+ DBG("called for %s\n", dev->name);
+
+ write_lock(¬ifier_lock);
+ list_for_each_entry_safe(n, next, ¬ifier_list, list) {
+ if (n->dev == dev && n->func == func && n->data == data) {
+ list_del(&n->list);
+ kfree(n);
+ ret = 0;
+ break;
+ }
+ }
+ write_unlock(¬ifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_dev_unregister);
+
+static int can_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct notifier *n;
+ struct dev_rcv_lists *d;
+ int i;
+
+ DBG("called for %s, msg = %lu\n", dev->name, msg);
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_REGISTER:
+
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR "can: allocation of receive "
+ "list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ hlist_del_rcu(&d->list);
+
+ /* remove all receivers hooked at this netdevice */
+ can_rx_delete_all(&d->rx_err);
+ can_rx_delete_all(&d->rx_all);
+ can_rx_delete_all(&d->rx_fil);
+ can_rx_delete_all(&d->rx_inv);
+ can_rx_delete_all(&d->rx_eff);
+ for (i = 0; i < 2048; i++)
+ can_rx_delete_all(&d->rx_sff[i]);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rcv_lists_delete);
+
+ break;
+ }
+
+ read_lock(¬ifier_lock);
+ list_for_each_entry(n, ¬ifier_list, list) {
+ if (n->dev == dev)
+ n->func(msg, n->data);
+ }
+ read_unlock(¬ifier_lock);
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static 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 = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert struct dev_rcv_lists for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = jiffies + HZ;
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: linux-2.6.22-rc1-git4/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/net/can/af_can.h 2007-05-16 10:02:06.000000000 +0200
@@ -0,0 +1,122 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx_err;
+ struct hlist_head rx_all;
+ struct hlist_head rx_fil;
+ struct hlist_head rx_inv;
+ struct hlist_head rx_sff[0x800];
+ struct hlist_head rx_eff;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: linux-2.6.22-rc1-git4/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc1-git4/net/can/proc.c 2007-05-16 10:02:06.000000000 +0200
@@ -0,0 +1,660 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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 = NULL;
+static struct proc_dir_entry *pde_version = NULL;
+static struct proc_dir_entry *pde_stats = NULL;
+static struct proc_dir_entry *pde_reset_stats = NULL;
+static struct proc_dir_entry *pde_rcvlist_all = NULL;
+static struct proc_dir_entry *pde_rcvlist_fil = NULL;
+static struct proc_dir_entry *pde_rcvlist_inv = NULL;
+static struct proc_dir_entry *pde_rcvlist_sff = NULL;
+static struct proc_dir_entry *pde_rcvlist_eff = NULL;
+static struct proc_dir_entry *pde_rcvlist_err = NULL;
+
+static int user_reset = 0;
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_rcv() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = jiffies + HZ;
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08x %08x %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned int)r->func, (unsigned int)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_all(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_ALL */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_all':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx_all)) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx_all, d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_FIL */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_fil':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx_fil)) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx_fil, d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_INV */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_inv':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx_inv)) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx_inv, d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_EFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_eff':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx_eff)) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx_eff, d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_err(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_ERR */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_err':\n");
+
+ /* find receive list for this device */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx_err)) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx_err, d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t* read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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_PROC_DIR, NULL);
+
+ if (!can_dir) {
+ printk(KERN_INFO "can: failed to create /proc/%s . "
+ "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
+ return;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION,
+ 0644, can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS,
+ 0644, can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS,
+ 0644, can_proc_read_reset_stats, NULL);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL,
+ 0644, can_proc_read_rcvlist_all, NULL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL,
+ 0644, can_proc_read_rcvlist_fil, NULL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV,
+ 0644, can_proc_read_rcvlist_inv, NULL);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF,
+ 0644, can_proc_read_rcvlist_sff, NULL);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF,
+ 0644, can_proc_read_rcvlist_eff, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR,
+ 0644, can_proc_read_rcvlist_err, NULL);
+}
+
+/*
+ * 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_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_sff)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
+
+ if (pde_rcvlist_eff)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
+
+ if (pde_rcvlist_err)
+ can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
+
+ if (can_dir)
+ remove_proc_entry(CAN_PROC_DIR, NULL);
+}
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-16 14:51 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-05-16 16:35 ` Arnaldo Carvalho de Melo
2007-05-16 19:14 ` Urs Thuermann
2007-05-18 0:59 ` Paul E. McKenney
1 sibling, 1 reply; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-05-16 16:35 UTC (permalink / raw)
To: Urs Thuermann; +Cc: netdev, Thomas Gleixner, Oliver Hartkopp, Urs Thuermann
On 5/16/07, Urs Thuermann <urs@isnogud.escape.de> wrote:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
>
> Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
<SNIP>
> +
> +/**
> + * struct sockaddr_can - the sockaddr structure for CAN sockets
> + * @can_family: address family number AF_CAN.
> + * @can_ifindex: CAN network interface index.
> + * @can_addr: transport protocol specific address, mostly CAN IDs.
> + */
> +struct sockaddr_can {
> + sa_family_t can_family;
> + int can_ifindex;
> + union {
> + struct { canid_t rx_id, tx_id; } tp16;
> + struct { canid_t rx_id, tx_id; } tp20;
> + struct { canid_t rx_id, tx_id; } mcnet;
> + struct { canid_t rx_id, tx_id; } isotp;
> + struct { int lcu, type; } bap;
> + } can_addr;
> +};
Can can_ifindex be turned into a unsigned short? That way we would
have it nicely packed, avoiding this hole:
[acme@filo examples]$ pahole can
/* <1c2> /home/acme/git/pahole/examples/can.c:5 */
struct sockaddr_can {
sa_family_t can_family; /* 0 2 */
/* XXX 2 bytes hole, try to pack */
int can_ifindex; /* 4 4 */
union {
struct {
canid_t rx_id; /* 8 4 */
canid_t tx_id; /* 12 4 */
} tp16; /* 8 */
struct {
canid_t rx_id; /* 8 4 */
canid_t tx_id; /* 12 4 */
} tp20; /* 8 */
struct {
canid_t rx_id; /* 8 4 */
canid_t tx_id; /* 12 4 */
} mcnet; /* 8 */
struct {
canid_t rx_id; /* 8 4 */
canid_t tx_id; /* 12 4 */
} isotp; /* 8 */
struct {
int lcu; /* 8 4 */
int type; /* 12 4 */
} bap; /* 8 */
} can_addr; /* 8 8 */
}; /* size: 16, cachelines: 1 */
/* sum members: 14, holes: 1, sum holes: 2 */
/* last cacheline: 16 bytes */
/* definitions: 1 */
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-16 16:35 ` Arnaldo Carvalho de Melo
@ 2007-05-16 19:14 ` Urs Thuermann
2007-05-16 20:51 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-05-16 19:14 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: netdev, Thomas Gleixner, Oliver Hartkopp
"Arnaldo Carvalho de Melo" <acme@ghostprotocols.net> writes:
> Can can_ifindex be turned into a unsigned short? That way we would
> have it nicely packed, avoiding this hole:
Since dev->ifindex is int and we have many assignments between
can_ifindex and dev->ifindex it would not make sense to define
can_ifindex as a short int. Also in user space, ifindex is int,
e.g. in struct ifreq. This would cause implicit truncating type casts
from int to short int (some compilers warn about this, haven't tried
GCC for a long time) or require many explicit type casts.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-16 19:14 ` Urs Thuermann
@ 2007-05-16 20:51 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-05-16 20:51 UTC (permalink / raw)
To: Urs Thuermann; +Cc: netdev, Thomas Gleixner, Oliver Hartkopp
On 16 May 2007 21:14:20 +0200, Urs Thuermann <urs@isnogud.escape.de> wrote:
> "Arnaldo Carvalho de Melo" <acme@ghostprotocols.net> writes:
>
> > Can can_ifindex be turned into a unsigned short? That way we would
> > have it nicely packed, avoiding this hole:
>
> Since dev->ifindex is int and we have many assignments between
> can_ifindex and dev->ifindex it would not make sense to define
> can_ifindex as a short int. Also in user space, ifindex is int,
> e.g. in struct ifreq. This would cause implicit truncating type casts
> from int to short int (some compilers warn about this, haven't tried
> GCC for a long time) or require many explicit type casts.
Quick answer, thank you.
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-16 14:51 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-05-16 16:35 ` Arnaldo Carvalho de Melo
@ 2007-05-18 0:59 ` Paul E. McKenney
2007-05-18 9:19 ` Oliver Hartkopp
1 sibling, 1 reply; 86+ messages in thread
From: Paul E. McKenney @ 2007-05-18 0:59 UTC (permalink / raw)
To: Urs Thuermann; +Cc: netdev, Thomas Gleixner, Oliver Hartkopp, Urs Thuermann
On Wed, May 16, 2007 at 04:51:02PM +0200, Urs Thuermann wrote:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
Interesting! One question called out below -- why do call_rcu() on each
piece of the struct dev_rcv_lists, instead of doing call_rcu() on the
whole thing and having the RCU callback free up the pieces? Given that
all the pieces are call_rcu()ed separately, there had better not be
persistent pointers to the pieces, right?
Doing it in one chunk would make the code a bit simpler and also reduce
the RCU overhead a bit.
Or am I missing something subtle here?
Thanx, Paul
> Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/can.h | 98 ++++
> include/linux/can/core.h | 88 +++
> net/Kconfig | 1
> net/Makefile | 1
> net/can/Kconfig | 25 +
> net/can/Makefile | 6
> net/can/af_can.c | 1063 +++++++++++++++++++++++++++++++++++++++++++++++
> net/can/af_can.h | 122 +++++
> net/can/proc.c | 660 +++++++++++++++++++++++++++++
> 9 files changed, 2064 insertions(+)
>
> Index: linux-2.6.22-rc1-git4/include/linux/can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/include/linux/can.h 2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,98 @@
> +/*
> + * linux/can.h
> + *
> + * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_H
> +#define CAN_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/* controller area network (CAN) kernel definitions */
> +
> +/* special address description flags for the CAN_ID */
> +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
> +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
> +#define CAN_ERR_FLAG 0x20000000U /* error frame */
> +
> +/* valid bits in CAN ID for frame formats */
> +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
> +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
> +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
> +
> +typedef __u32 canid_t;
> +typedef __u32 can_err_mask_t;
> +
> +/**
> + * struct can_frame - basic CAN frame structure
> + * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
> + * @can_dlc: the data length field of the CAN frame
> + * @data: the CAN frame payload.
> + */
> +struct can_frame {
> + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
> + __u8 can_dlc; /* data length code: 0 .. 8 */
> + __u8 data[8] __attribute__((aligned(8)));
> +};
> +
> +/* particular protocols of the protocol family PF_CAN */
> +#define CAN_RAW 1 /* RAW sockets */
> +#define CAN_BCM 2 /* Broadcast Manager */
> +#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
> +#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
> +#define CAN_MCNET 5 /* Bosch MCNet */
> +#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
> +#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
> +#define CAN_NPROTO 8
> +
> +#define SOL_CAN_BASE 100
> +
> +/**
> + * struct sockaddr_can - the sockaddr structure for CAN sockets
> + * @can_family: address family number AF_CAN.
> + * @can_ifindex: CAN network interface index.
> + * @can_addr: transport protocol specific address, mostly CAN IDs.
> + */
> +struct sockaddr_can {
> + sa_family_t can_family;
> + int can_ifindex;
> + union {
> + struct { canid_t rx_id, tx_id; } tp16;
> + struct { canid_t rx_id, tx_id; } tp20;
> + struct { canid_t rx_id, tx_id; } mcnet;
> + struct { canid_t rx_id, tx_id; } isotp;
> + struct { int lcu, type; } bap;
> + } can_addr;
> +};
> +
> +/**
> + * struct can_filter - CAN ID based filter in can_register().
> + * @can_id: relevant bits of CAN ID which are not masked out.
> + * @can_mask: CAN mask (see description)
> + *
> + * Description:
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + */
> +struct can_filter {
> + canid_t can_id;
> + canid_t can_mask;
> +};
> +
> +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
> +
> +#endif /* CAN_H */
> Index: linux-2.6.22-rc1-git4/include/linux/can/core.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/include/linux/can/core.h 2007-05-16 09:45:00.000000000 +0200
> @@ -0,0 +1,88 @@
> +/*
> + * linux/can/core.h
> + *
> + * Protoypes and definitions for CAN protocol modules using the PF_CAN core
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_CORE_H
> +#define CAN_CORE_H
> +
> +#include <linux/can.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +
> +#define CAN_VERSION "20070508"
> +
> +/* increment this number each time you change some user-space interface */
> +#define CAN_ABI_VERSION "8"
> +
> +#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
> +
> +#define DNAME(dev) ((dev) ? (dev)->name : "any")
> +
> +#define CAN_PROC_DIR "net/can" /* /proc/... */
> +
> +/**
> + * struct can_proto - CAN protocol structure
> + * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
> + * @protocol: protocol number in socket() syscall.
> + * @capability: capability needed to open the socket, or -1 for no restriction.
> + * @ops: pointer to struct proto_ops for sock->ops.
> + * @prot: pointer to struct proto structure.
> + */
> +struct can_proto {
> + int type;
> + int protocol;
> + int capability;
> + struct proto_ops *ops;
> + struct proto *prot;
> +};
> +
> +/* function prototypes for the CAN networklayer core (af_can.c) */
> +
> +extern int can_proto_register(struct can_proto *cp);
> +extern int can_proto_unregister(struct can_proto *cp);
> +
> +extern 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);
> +
> +extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
> + canid_t mask,
> + void (*func)(struct sk_buff *, void *),
> + void *data);
> +
> +extern int can_dev_register(struct net_device *dev,
> + void (*func)(unsigned long msg, void *),
> + void *data);
> +
> +extern int can_dev_unregister(struct net_device *dev,
> + void (*func)(unsigned long msg, void *),
> + void *data);
> +
> +extern int can_send(struct sk_buff *skb, int loop);
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +extern void can_debug_skb(struct sk_buff *skb);
> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
> +#define DBG(args...) (debug & 1 ? \
> + (printk(KERN_DEBUG "can-%s %s: ", \
> + IDENT, __func__), printk(args)) : 0)
> +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> +#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
> +#else
> +#define DBG(args...)
> +#define DBG_FRAME(args...)
> +#define DBG_SKB(skb)
> +#endif
> +
> +#endif /* CAN_CORE_H */
> Index: linux-2.6.22-rc1-git4/net/Kconfig
> ===================================================================
> --- linux-2.6.22-rc1-git4.orig/net/Kconfig 2007-05-16 09:44:54.000000000 +0200
> +++ linux-2.6.22-rc1-git4/net/Kconfig 2007-05-16 09:45:00.000000000 +0200
> @@ -210,6 +210,7 @@
> endmenu
>
> source "net/ax25/Kconfig"
> +source "net/can/Kconfig"
> source "net/irda/Kconfig"
> source "net/bluetooth/Kconfig"
> source "net/rxrpc/Kconfig"
> Index: linux-2.6.22-rc1-git4/net/Makefile
> ===================================================================
> --- linux-2.6.22-rc1-git4.orig/net/Makefile 2007-05-16 09:44:54.000000000 +0200
> +++ linux-2.6.22-rc1-git4/net/Makefile 2007-05-16 09:45:00.000000000 +0200
> @@ -34,6 +34,7 @@
> obj-$(CONFIG_NETROM) += netrom/
> obj-$(CONFIG_ROSE) += rose/
> obj-$(CONFIG_AX25) += ax25/
> +obj-$(CONFIG_CAN) += can/
> obj-$(CONFIG_IRDA) += irda/
> obj-$(CONFIG_BT) += bluetooth/
> obj-$(CONFIG_SUNRPC) += sunrpc/
> Index: linux-2.6.22-rc1-git4/net/can/Kconfig
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/Kconfig 2007-05-16 10:01:14.000000000 +0200
> @@ -0,0 +1,25 @@
> +#
> +# 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 at
> + 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.
> +
> +config CAN_DEBUG_CORE
> + bool "CAN Core debugging messages"
> + depends on CAN
> + ---help---
> + Say Y here if you want the CAN core to produce a bunch of debug
> + messages to the system log. Select this if you are having a
> + problem with CAN support and want to see more of what is going on.
> Index: linux-2.6.22-rc1-git4/net/can/Makefile
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/Makefile 2007-05-16 10:01:14.000000000 +0200
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the Linux Controller Area Network core.
> +#
> +
> +obj-$(CONFIG_CAN) += can.o
> +can-objs := af_can.o proc.o
> Index: linux-2.6.22-rc1-git4/net/can/af_can.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/af_can.c 2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,1063 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/kmod.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/spinlock.h>
> +#include <linux/rcupdate.h>
> +#include <linux/socket.h>
> +#include <linux/if_ether.h>
> +#include <linux/if_arp.h>
> +#include <linux/skbuff.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/can.h>
> +#include <linux/can/core.h>
> +#include <net/sock.h>
> +#include <asm/uaccess.h>
> +
> +#include "af_can.h"
> +
> +#define IDENT "core"
> +static __initdata 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);
> +
> +int stats_timer = 1; /* default: on */
> +module_param(stats_timer, int, S_IRUGO);
> +MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +static int debug = 0;
> +module_param(debug, int, S_IRUGO);
> +MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
> +#endif
> +
> +struct notifier {
> + struct list_head list;
> + struct net_device *dev;
> + void (*func)(unsigned long msg, void *data);
> + void *data;
> +};
> +
> +static LIST_HEAD(notifier_list);
> +static DEFINE_RWLOCK(notifier_lock);
> +
> +HLIST_HEAD(rx_dev_list);
> +static struct dev_rcv_lists rx_alldev_list;
> +static DEFINE_SPINLOCK(rcv_lists_lock);
> +
> +static struct kmem_cache *rcv_cache __read_mostly;
> +
> +/* table of registered CAN protocols */
> +static struct can_proto *proto_tab[CAN_NPROTO];
> +
> +struct timer_list stattimer; /* timer for statistics update */
> +struct s_stats stats; /* packet statistics */
> +struct s_pstats pstats; /* receive list statistics */
> +
> +/*
> + * af_can socket functions
> + */
> +
> +static 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;
> + }
> +}
> +
> +static void can_sock_destruct(struct sock *sk)
> +{
> + DBG("called for sock %p\n", sk);
> +
> + skb_queue_purge(&sk->sk_receive_queue);
> + if (sk->sk_protinfo)
> + kfree(sk->sk_protinfo);
> +}
> +
> +static int can_create(struct socket *sock, int protocol)
> +{
> + struct sock *sk;
> + struct can_proto *cp;
> + char module_name[sizeof("can-proto-000")];
> + int ret = 0;
> +
> + DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
> +
> + sock->state = SS_UNCONNECTED;
> +
> + if (protocol < 0 || protocol >= CAN_NPROTO)
> + return -EINVAL;
> +
> + DBG("looking up proto %d in proto_tab[]\n", protocol);
> +
> + /* try to load protocol module, when CONFIG_KMOD is defined */
> + if (!proto_tab[protocol]) {
> + sprintf(module_name, "can-proto-%d", protocol);
> + ret = request_module(module_name);
> +
> + /* In case of error we only print a message but don't
> + * return the error code immediately. Below we will
> + * return -EPROTONOSUPPORT
> + */
> + if (ret == -ENOSYS)
> + printk(KERN_INFO "can: request_module(%s) not"
> + " implemented.\n", module_name);
> + else if (ret)
> + printk(KERN_ERR "can: request_module(%s) failed\n",
> + module_name);
> + }
> +
> + /* check for success and correct type */
> + cp = proto_tab[protocol];
> + if (!cp || cp->type != sock->type)
> + return -EPROTONOSUPPORT;
> +
> + if (cp->capability >= 0 && !capable(cp->capability))
> + return -EPERM;
> +
> + sock->ops = cp->ops;
> +
> + sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
> + if (!sk)
> + return -ENOMEM;
> +
> + sock_init_data(sock, sk);
> + sk->sk_destruct = can_sock_destruct;
> +
> + DBG("created sock: %p\n", sk);
> +
> + if (sk->sk_prot->init)
> + ret = sk->sk_prot->init(sk);
> +
> + if (ret) {
> + /* release sk on errors */
> + sock_orphan(sk);
> + sock_put(sk);
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * 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!)
> + *
> + * Return:
> + * 0 on success
> + * -ENETDOWN when the selected interface is down
> + * -ENOBUFS on full driver queue (see net_xmit_errno())
> + */
> +int can_send(struct sk_buff *skb, int loop)
> +{
> + struct sock **tx_sk = (struct sock **)skb->cb;
> + int err;
> +
> + if (skb->dev->type != ARPHRD_CAN) {
> + kfree_skb(skb);
> + return -EPERM;
> + }
> +
> + if (loop) {
> + /* local loopback of sent CAN frames (default) */
> +
> + /* indication for the CAN driver: do loopback */
> + *tx_sk = skb->sk;
> +
> + /*
> + * The reference to the originating sock may be also required
> + * by the receiving socket to indicate (and ignore) his own
> + * sent data. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
> + */
> +
> + /* interface not capabable to do the loopback itself? */
> + if (!(skb->dev->flags & IFF_LOOPBACK)) {
> + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> +
> + /* perform the local loopback here */
> + newskb->protocol = htons(ETH_P_CAN);
> + newskb->ip_summed = CHECKSUM_UNNECESSARY;
> + netif_rx(newskb);
> + }
> + } else {
> + /* indication for the CAN driver: no loopback required */
> + *tx_sk = NULL;
> + }
> +
> + if (!(skb->dev->flags & IFF_UP))
> + return -ENETDOWN;
> +
> + /* send to netdevice */
> + err = dev_queue_xmit(skb);
> + if (err > 0)
> + err = net_xmit_errno(err);
> +
> + /* update statistics */
> + stats.tx_frames++;
> + stats.tx_frames_delta++;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_send);
> +
> +/*
> + * af_can rx path
> + */
> +
> +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /*
> + * find receive list for this device
> + *
> + * The hlist_for_each_entry*() macros curse through the list
> + * using the pointer variable n and set d to the containing
> + * struct in each list iteration. Therefore, after list
> + * iteration, d is unmodified when the list is empty, and it
> + * points to last list element, when the list is non-empty
> + * but no match in the loop body is found. I.e. d is *not*
> + * NULL when no match is found. We can, however, use the
> + * cursor variable n to decide if a match was found.
> + */
> +
> + hlist_for_each_entry(d, n, &rx_dev_list, list) {
> + if (d->dev == dev)
> + break;
> + }
> +
> + return n ? d : NULL;
> +}
> +
> +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 error frames */
> + if (*mask & CAN_ERR_FLAG) {
> + /* clear CAN_ERR_FLAG in list entry */
> + *mask &= CAN_ERR_MASK;
> + return &d->rx_err;
> + }
> +
> + /* ensure valid values in can_mask */
> + if (*mask & CAN_EFF_FLAG)
> + *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
> + else
> + *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
> +
> + /* reduce condition testing at receive time */
> + *can_id &= *mask;
> +
> + /* inverse can_id/can_mask filter */
> + if (inv)
> + return &d->rx_inv;
> +
> + /* mask == 0 => no condition testing at receive time */
> + if (!(*mask))
> + return &d->rx_all;
> +
> + /* use extra filterset for the subscription of exactly *ONE* can_id */
> + if (*can_id & CAN_EFF_FLAG) {
> + if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
> + /* RFC: a use-case for hash-tables in the future? */
> + return &d->rx_eff;
> + }
> + } else {
> + if (*mask == CAN_SFF_MASK)
> + return &d->rx_sff[*can_id];
> + }
> +
> + /* default: filter via can_id/can_mask */
> + return &d->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 indentification
> + *
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + *
> + * 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 ret = 0;
> +
> + /* insert new receiver (dev,canid,mask) -> (func,data) */
> +
> + DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
> + dev, can_id, mask, func, data, ident);
> +
> + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
> + if (!r)
> + return -ENOMEM;
> +
> + spin_lock_bh(&rcv_lists_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++;
> +
> + pstats.rcv_entries++;
> + if (pstats.rcv_entries_max < pstats.rcv_entries)
> + pstats.rcv_entries_max = pstats.rcv_entries;
> + } else {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + kmem_cache_free(rcv_cache, r);
> + ret = -ENODEV;
> + }
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_register);
> +
> +static void can_rcv_lists_delete(struct rcu_head *rp)
> +{
> + struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
> +
> + kfree(d);
> +}
> +
> +static void can_rx_delete(struct rcu_head *rp)
> +{
> + struct receiver *r = container_of(rp, struct receiver, rcu);
> +
> + kmem_cache_free(rcv_cache, r);
> +}
> +
> +static void can_rx_delete_all(struct hlist_head *rl)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> +
> + hlist_for_each_entry_rcu(r, n, rl, list) {
> + hlist_del_rcu(&r->list);
> + call_rcu(&r->rcu, can_rx_delete);
> + }
> +}
> +
> +/**
> + * 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.
> + *
> + * Return:
> + * 0 on success
> + * -EINVAL on missing subscription entry
> + * -ENODEV unknown device
> + */
> +int 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 hlist_node *next;
> + struct dev_rcv_lists *d;
> + int ret = 0;
> +
> + DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
> + dev, can_id, mask, func, data);
> +
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (!d) {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + ret = -ENODEV;
> + 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(r, next, rl, list) {
> + if (r->can_id == can_id && r->mask == mask
> + && r->func == func && r->data == data)
> + break;
> + }
> +
> + /*
> + * Check for bug in CAN protocol implementations:
> + * If no matching list item was found, the list cursor variable next
> + * will be NULL, while r will point to the last item of the list.
> + */
> +
> + if (!next) {
> + DBG("receive list entry not found for "
> + "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
> + ret = -EINVAL;
> + r = NULL;
> + goto out;
> + }
> +
> + hlist_del_rcu(&r->list);
> + d->entries--;
> +
> + if (pstats.rcv_entries > 0)
> + pstats.rcv_entries--;
> +
> + out:
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + /* schedule the receiver item for deletion */
> + if (r)
> + call_rcu(&r->rcu, can_rx_delete);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_unregister);
> +
> +static inline void deliver(struct sk_buff *skb, struct receiver *r)
> +{
> + struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
> +
> + DBG("skbuff %p cloned to %p\n", skb, clone);
> + if (clone) {
> + r->func(clone, r->data);
> + r->matches++;
> + }
> +}
> +
> +static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> + 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 frame entries only */
> + hlist_for_each_entry_rcu(r, n, &d->rx_err, list) {
> + if (can_id & r->mask) {
> + DBG("match on rx_err skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + return matches;
> + }
> +
> + /* check for unfiltered entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx_all, list) {
> + DBG("match on rx_all skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> +
> + /* check for can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx_fil, list) {
> + if ((can_id & r->mask) == r->can_id) {
> + DBG("match on rx_fil skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check for inverted can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx_inv, list) {
> + if ((can_id & r->mask) != r->can_id) {
> + DBG("match on rx_inv skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check CAN_ID specific entries */
> + if (can_id & CAN_EFF_FLAG) {
> + hlist_for_each_entry_rcu(r, n, &d->rx_eff, list) {
> + if (r->can_id == can_id) {
> + DBG("match on rx_eff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + } else {
> + can_id &= CAN_SFF_MASK;
> + hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
> + DBG("match on rx_sff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + return matches;
> +}
> +
> +static int can_rcv(struct sk_buff *skb, struct net_device *dev,
> + struct packet_type *pt, struct net_device *orig_dev)
> +{
> + struct dev_rcv_lists *d;
> + int matches;
> +
> + DBG("received skbuff on device %s, ptype %04x\n",
> + dev->name, ntohs(pt->type));
> + DBG_SKB(skb);
> + DBG_FRAME("af_can: can_rcv: received CAN frame",
> + (struct can_frame *)skb->data);
> +
> + if (dev->type != ARPHRD_CAN) {
> + kfree_skb(skb);
> + return 0;
> + }
> +
> + /* update statistics */
> + stats.rx_frames++;
> + stats.rx_frames_delta++;
> +
> + rcu_read_lock();
> +
> + /* deliver the packet to sockets listening on all devices */
> + matches = can_rcv_filter(&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();
> +
> + /* free the skbuff allocated by the netdevice driver */
> + DBG("freeing skbuff %p\n", skb);
> + kfree_skb(skb);
> +
> + if (matches > 0) {
> + stats.matches++;
> + stats.matches_delta++;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * af_can debugging stuff
> + */
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +
> +#define DBG_BSIZE 1024
> +
> +/**
> + * can_debug_cframe - print CAN frame
> + * @msg: pointer to message printed before the given CAN frame
> + * @cf: pointer to CAN frame
> + */
> +void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
> +{
> + va_list ap;
> + int len;
> + int dlc, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf, KERN_DEBUG);
> + va_start(ap, cf);
> + len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
> + buf[len++] = ':';
> + buf[len++] = ' ';
> + va_end(ap);
> +
> + dlc = cf->can_dlc;
> + if (dlc > 8)
> + dlc = 8;
> +
> + if (cf->can_id & CAN_EFF_FLAG)
> + len += sprintf(buf + len, "<%08X> [%X] ",
> + cf->can_id & CAN_EFF_MASK, dlc);
> + else
> + len += sprintf(buf + len, "<%03X> [%X] ",
> + cf->can_id & CAN_SFF_MASK, dlc);
> +
> + for (i = 0; i < dlc; i++)
> + len += sprintf(buf + len, "%02X ", cf->data[i]);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + len += sprintf(buf + len, "(RTR)");
> +
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_cframe);
> +
> +/**
> + * can_debug_skb - print socket buffer content to kernel log
> + * @skb: pointer to socket buffer
> + */
> +void can_debug_skb(struct sk_buff *skb)
> +{
> + int len, nbytes, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf,
> + KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
> + KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
> + "h,d,t,e,l: %p %+d %+d %+d, %d",
> + skb, skb->dev ? skb->dev->ifindex : -1,
> + ntohs(skb->protocol),
> + atomic_read(&skb->users),
> + atomic_read(&(skb_shinfo(skb)->dataref)),
> + skb_shinfo(skb)->nr_frags,
> + skb->head, skb->data - skb->head,
> + skb->tail - skb->head, skb->end - skb->head, skb->len);
> + nbytes = skb->end - skb->head;
> + for (i = 0; i < nbytes; i++) {
> + if (i % 16 == 0)
> + len += sprintf(buf + len, "\n" KERN_DEBUG " ");
> + if (len < DBG_BSIZE - 16) {
> + len += sprintf(buf + len, " %02x", skb->head[i]);
> + } else {
> + len += sprintf(buf + len, "...");
> + break;
> + }
> + }
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_skb);
> +
> +#endif
> +
> +/*
> + * 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(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> + int err = 0;
> +
> + if (proto < 0 || proto >= CAN_NPROTO) {
> + printk(KERN_ERR "can: protocol number %d out "
> + "of range\n", proto);
> + return -EINVAL;
> + }
> + if (proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d already "
> + "registered\n", proto);
> + return -EBUSY;
> + }
> +
> + err = proto_register(cp->prot, 0);
> + if (err < 0)
> + return err;
> +
> + proto_tab[proto] = cp;
> +
> + /* use generic ioctl function if the module doesn't bring its own */
> + if (!cp->ops->ioctl)
> + cp->ops->ioctl = can_ioctl;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_proto_register);
> +
> +/**
> + * can_proto_unregister - unregister CAN transport protocol
> + * @cp: pointer to CAN protocol structure
> + *
> + * Return:
> + * 0 on success
> + * -ESRCH protocol number was not registered
> + */
> +int can_proto_unregister(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> +
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d is not registered\n", proto);
> + return -ESRCH;
> + }
> + proto_unregister(cp->prot);
> + proto_tab[proto] = NULL;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(can_proto_unregister);
> +
> +/**
> + * can_dev_register - subscribe notifier for CAN device status changes
> + * @dev: pointer to netdevice
> + * @func: callback function on status change
> + * @data: returned parameter for callback function
> + *
> + * Description:
> + * Invokes the callback function with the status 'msg' and the given
> + * parameter 'data' on a status change of the given CAN network device.
> + *
> + * Return:
> + * 0 on success
> + * -ENOMEM on missing mem to create subscription entry
> + * -ENODEV unknown device
> + */
> +int can_dev_register(struct net_device *dev,
> + void (*func)(unsigned long msg, void *), void *data)
> +{
> + struct notifier *n;
> +
> + DBG("called for %s\n", dev->name);
> +
> + if (!dev || dev->type != ARPHRD_CAN)
> + return -ENODEV;
> +
> + n = kmalloc(sizeof(*n), GFP_KERNEL);
> + if (!n)
> + return -ENOMEM;
> +
> + n->dev = dev;
> + n->func = func;
> + n->data = data;
> +
> + write_lock(¬ifier_lock);
> + list_add(&n->list, ¬ifier_list);
> + write_unlock(¬ifier_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(can_dev_register);
> +
> +/**
> + * can_dev_unregister - unsubscribe notifier for CAN device status changes
> + * @dev: pointer to netdevice
> + * @func: callback function on filter match
> + * @data: returned parameter for callback function
> + *
> + * Description:
> + * Removes subscription entry depending on given (subscription) values.
> + *
> + * Return:
> + * 0 on success
> + * -EINVAL on missing subscription entry
> + */
> +int can_dev_unregister(struct net_device *dev,
> + void (*func)(unsigned long msg, void *), void *data)
> +{
> + struct notifier *n, *next;
> + int ret = -EINVAL;
> +
> + DBG("called for %s\n", dev->name);
> +
> + write_lock(¬ifier_lock);
> + list_for_each_entry_safe(n, next, ¬ifier_list, list) {
> + if (n->dev == dev && n->func == func && n->data == data) {
> + list_del(&n->list);
> + kfree(n);
> + ret = 0;
> + break;
> + }
> + }
> + write_unlock(¬ifier_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_dev_unregister);
> +
> +static int can_notifier(struct notifier_block *nb,
> + unsigned long msg, void *data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct notifier *n;
> + struct dev_rcv_lists *d;
> + int i;
> +
> + DBG("called for %s, msg = %lu\n", dev->name, msg);
> +
> + if (dev->type != ARPHRD_CAN)
> + return NOTIFY_DONE;
> +
> + switch (msg) {
> +
> + case NETDEV_REGISTER:
> +
> + /*
> + * create new dev_rcv_lists for this device
> + *
> + * N.B. zeroing the struct is the correct initialization
> + * for the embedded hlist_head structs.
> + * Another list type, e.g. list_head, would require
> + * explicit initialization.
> + */
> +
> + DBG("creating new dev_rcv_lists for %s\n", dev->name);
> +
> + d = kzalloc(sizeof(*d),
> + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
> + if (!d) {
> + printk(KERN_ERR "can: allocation of receive "
> + "list failed\n");
> + return NOTIFY_DONE;
> + }
> + d->dev = dev;
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&d->list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + break;
> +
> + case NETDEV_UNREGISTER:
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (d) {
> + hlist_del_rcu(&d->list);
> +
> + /* remove all receivers hooked at this netdevice */
> + can_rx_delete_all(&d->rx_err);
> + can_rx_delete_all(&d->rx_all);
> + can_rx_delete_all(&d->rx_fil);
> + can_rx_delete_all(&d->rx_inv);
> + can_rx_delete_all(&d->rx_eff);
> + for (i = 0; i < 2048; i++)
> + can_rx_delete_all(&d->rx_sff[i]);
Urk!!! That could be one heaping pile of callbacks!!! Why not just
do the single call_rcu() below, and take the struct rcu_dev_lists
apart in the RCU callback? Unless I am missing something, the
can_rx_delete_all() function feeds all the pieces to call_rcu() anyway,
so there should not be other CPUs digging through this stuff.
Or am I missing something subtle here?
> + } else
> + printk(KERN_ERR "can: notifier: receive list not "
> + "found for dev %s\n", dev->name);
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (d)
> + call_rcu(&d->rcu, can_rcv_lists_delete);
> +
> + break;
> + }
> +
> + read_lock(¬ifier_lock);
> + list_for_each_entry(n, ¬ifier_list, list) {
> + if (n->dev == dev)
> + n->func(msg, n->data);
> + }
> + read_unlock(¬ifier_lock);
> +
> + return NOTIFY_DONE;
> +}
> +
> +/*
> + * af_can module init/exit functions
> + */
> +
> +static struct packet_type can_packet = {
> + .type = __constant_htons(ETH_P_CAN),
> + .dev = NULL,
> + .func = can_rcv,
> +};
> +
> +static 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 = {
> + .notifier_call = can_notifier,
> +};
> +
> +static __init int can_init(void)
> +{
> + printk(banner);
> +
> + rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
> + 0, 0, NULL, NULL);
> + if (!rcv_cache)
> + return -ENOMEM;
> +
> + /*
> + * Insert struct dev_rcv_lists for reception on all devices.
> + * This struct is zero initialized which is correct for the
> + * embedded hlist heads, the dev pointer, and the entries counter.
> + */
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (stats_timer) {
> + /* the statistics are updated every second (timer triggered) */
> + init_timer(&stattimer);
> + stattimer.function = can_stat_update;
> + stattimer.data = 0;
> + /* update every second */
> + stattimer.expires = jiffies + HZ;
> + /* start statistics timer */
> + add_timer(&stattimer);
> + } else
> + stattimer.function = NULL;
> +
> + /* procfs init */
> + can_init_proc();
> +
> + /* protocol register */
> + sock_register(&can_family_ops);
> + register_netdevice_notifier(&can_netdev_notifier);
> + dev_add_pack(&can_packet);
> +
> + return 0;
> +}
> +
> +static __exit void can_exit(void)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n, *next;
> +
> + if (stats_timer)
> + del_timer(&stattimer);
> +
> + /* procfs remove */
> + can_remove_proc();
> +
> + /* protocol unregister */
> + dev_remove_pack(&can_packet);
> + unregister_netdevice_notifier(&can_netdev_notifier);
> + sock_unregister(PF_CAN);
> +
> + /* remove rx_dev_list */
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_del(&rx_alldev_list.list);
> + hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
> + hlist_del(&d->list);
> + kfree(d);
> + }
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + kmem_cache_destroy(rcv_cache);
> +}
> +
> +module_init(can_init);
> +module_exit(can_exit);
> Index: linux-2.6.22-rc1-git4/net/can/af_can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/af_can.h 2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,122 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#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;
> +};
> +
> +struct dev_rcv_lists {
> + struct hlist_node list;
> + struct rcu_head rcu;
> + struct net_device *dev;
> + struct hlist_head rx_err;
> + struct hlist_head rx_all;
> + struct hlist_head rx_fil;
> + struct hlist_head rx_inv;
> + struct hlist_head rx_sff[0x800];
> + struct hlist_head rx_eff;
> + int entries;
> +};
> +
> +/* statistic structures */
> +
> +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;
> +}; /* can be reset e.g. by can_init_stats() */
> +
> +struct s_pstats {
> + unsigned long stats_reset;
> + unsigned long user_reset;
> + unsigned long rcv_entries;
> + unsigned long rcv_entries_max;
> +}; /* persistent statistics */
> +
> +/* function prototypes for the CAN networklayer procfs (proc.c) */
> +extern void can_init_proc(void);
> +extern void can_remove_proc(void);
> +extern void can_stat_update(unsigned long data);
> +
> +/* structures and variables from af_can.c needed in proc.c for reading */
> +extern struct timer_list stattimer; /* timer for statistics update */
> +extern struct s_stats stats; /* packet statistics */
> +extern struct s_pstats pstats; /* receive list statistics */
> +extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
> +
> +#endif /* AF_CAN_H */
> Index: linux-2.6.22-rc1-git4/net/can/proc.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.22-rc1-git4/net/can/proc.c 2007-05-16 10:02:06.000000000 +0200
> @@ -0,0 +1,660 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/rcupdate.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 = NULL;
> +static struct proc_dir_entry *pde_version = NULL;
> +static struct proc_dir_entry *pde_stats = NULL;
> +static struct proc_dir_entry *pde_reset_stats = NULL;
> +static struct proc_dir_entry *pde_rcvlist_all = NULL;
> +static struct proc_dir_entry *pde_rcvlist_fil = NULL;
> +static struct proc_dir_entry *pde_rcvlist_inv = NULL;
> +static struct proc_dir_entry *pde_rcvlist_sff = NULL;
> +static struct proc_dir_entry *pde_rcvlist_eff = NULL;
> +static struct proc_dir_entry *pde_rcvlist_err = NULL;
> +
> +static int user_reset = 0;
> +
> +/*
> + * af_can statistics stuff
> + */
> +
> +static void can_init_stats(void)
> +{
> + /*
> + * This memset function is called from a timer context (when
> + * stattimer is active which is the default) OR in a process
> + * context (reading the proc_fs when stattimer is disabled).
> + */
> + memset(&stats, 0, sizeof(stats));
> + stats.jiffies_init = jiffies;
> +
> + pstats.stats_reset++;
> +
> + if (user_reset) {
> + user_reset = 0;
> + pstats.user_reset++;
> + }
> +}
> +
> +static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
> + unsigned long count)
> +{
> + unsigned long ret = 0;
> +
> + if (oldjif == newjif)
> + return 0;
> +
> + /* see can_rcv() - this should NEVER happen! */
> + if (count > (ULONG_MAX / HZ)) {
> + printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
> + count);
> + return 99999999;
> + }
> +
> + ret = (count * HZ) / (newjif - oldjif);
> +
> + return ret;
> +}
> +
> +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 < stats.jiffies_init)
> + can_init_stats();
> +
> + /* stats.rx_frames is the definitively max. statistic value */
> +
> + /* prevent overflow in calc_rate() */
> + if (stats.rx_frames > (ULONG_MAX / HZ))
> + can_init_stats();
> +
> + /* matches overflow - very improbable */
> + if (stats.matches > (ULONG_MAX / 100))
> + can_init_stats();
> +
> + /* calc total values */
> + if (stats.rx_frames)
> + stats.total_rx_match_ratio = (stats.matches * 100) /
> + stats.rx_frames;
> +
> + stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
> + stats.tx_frames);
> + stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
> + stats.rx_frames);
> +
> + /* calc current values */
> + if (stats.rx_frames_delta)
> + stats.current_rx_match_ratio =
> + (stats.matches_delta * 100) / stats.rx_frames_delta;
> +
> + stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
> + stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
> +
> + /* check / update maximum values */
> + if (stats.max_tx_rate < stats.current_tx_rate)
> + stats.max_tx_rate = stats.current_tx_rate;
> +
> + if (stats.max_rx_rate < stats.current_rx_rate)
> + stats.max_rx_rate = stats.current_rx_rate;
> +
> + if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
> + stats.max_rx_match_ratio = stats.current_rx_match_ratio;
> +
> + /* clear values for 'current rate' calculation */
> + stats.tx_frames_delta = 0;
> + stats.rx_frames_delta = 0;
> + stats.matches_delta = 0;
> +
> + /* restart timer (one second) */
> + stattimer.expires = jiffies + HZ;
> + add_timer(&stattimer);
> +}
> +
> +/*
> + * proc read functions
> + *
> + * From known use-cases we expect about 10 entries in a receive list to be
> + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
> + *
> + */
> +
> +static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
> + struct net_device *dev)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(r, n, rx_list, list) {
> + char *fmt = (r->can_id & CAN_EFF_FLAG)?
> + " %-5s %08X %08x %08x %08x %8ld %s\n" :
> + " %-5s %03X %08x %08x %08x %8ld %s\n";
> +
> + len += snprintf(page + len, PAGE_SIZE - len, fmt,
> + DNAME(dev), r->can_id, r->mask,
> + (unsigned int)r->func, (unsigned int)r->data,
> + r->matches, r->ident);
> +
> + /* does a typical line fit into the current buffer? */
> +
> + /* 100 Bytes before end of buffer */
> + if (len > PAGE_SIZE - 100) {
> + /* mark output cut off */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (..)\n");
> + break;
> + }
> + }
> + rcu_read_unlock();
> +
> + return len;
> +}
> +
> +static int can_print_recv_banner(char *page, int len)
> +{
> + /*
> + * can1. 00000000 00000000 00000000
> + * ....... 0 tp20
> + */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " device can_id can_mask function"
> + " userdata matches ident\n");
> +
> + return len;
> +}
> +
> +static int can_proc_read_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld transmitted frames (TXF)\n", stats.tx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld received frames (RXF)\n", stats.rx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld matched frames (RXMF)\n", stats.matches);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% total match ratio (RXMR)\n",
> + stats.total_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total tx rate (TXR)\n",
> + stats.total_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total rx rate (RXR)\n",
> + stats.total_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% current match ratio (CRXMR)\n",
> + stats.current_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current tx rate (CTXR)\n",
> + stats.current_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current rx rate (CRXR)\n",
> + stats.current_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% max match ratio (MRXMR)\n",
> + stats.max_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max tx rate (MTXR)\n",
> + stats.max_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max rx rate (MRXR)\n",
> + stats.max_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + }
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld current receive list entries (CRCV)\n",
> + pstats.rcv_entries);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld maximum receive list entries (MRCV)\n",
> + pstats.rcv_entries_max);
> +
> + if (pstats.stats_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\n %8ld statistic resets (STR)\n",
> + pstats.stats_reset);
> +
> + if (pstats.user_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld user statistic resets (USTR)\n",
> + pstats.user_reset);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_reset_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + user_reset = 1;
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Scheduled statistic reset #%ld.\n",
> + pstats.stats_reset + 1);
> +
> + } else {
> + if (stats.jiffies_init != jiffies)
> + can_init_stats();
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Performed statistic reset #%ld.\n",
> + pstats.stats_reset);
> + }
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_version(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
> + CAN_VERSION_STRING);
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_all(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_ALL */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_all':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx_all)) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx_all, d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_FIL */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_fil':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx_fil)) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx_fil, d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_INV */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_inv':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx_inv)) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx_inv, d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_SFF */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_sff':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> + int i, all_empty = 1;
> + /* check wether at least one list is non-empty */
> + for (i = 0; i < 0x800; i++)
> + if (!hlist_empty(&d->rx_sff[i])) {
> + all_empty = 0;
> + break;
> + }
> +
> + if (!all_empty) {
> + len = can_print_recv_banner(page, len);
> + for (i = 0; i < 0x800; i++) {
> + if (!hlist_empty(&d->rx_sff[i]) &&
> + len < PAGE_SIZE - 100)
> + len = can_print_rcvlist(page, len,
> + &d->rx_sff[i],
> + d->dev);
> + }
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_EFF */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_eff':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx_eff)) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx_eff, d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_err(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_ERR */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_err':\n");
> +
> + /* find receive list for this device */
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx_err)) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx_err, d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +/*
> + * proc utility functions
> + */
> +
> +static struct proc_dir_entry *can_create_proc_readentry(const char *name,
> + mode_t mode,
> + read_proc_t* read_proc,
> + void *data)
> +{
> + if (can_dir)
> + return create_proc_read_entry(name, mode, can_dir, read_proc,
> + data);
> + else
> + return NULL;
> +}
> +
> +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_PROC_DIR, NULL);
> +
> + if (!can_dir) {
> + printk(KERN_INFO "can: failed to create /proc/%s . "
> + "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
> + return;
> + }
> +
> + can_dir->owner = THIS_MODULE;
> +
> + /* own procfs entries from the AF_CAN core */
> + pde_version = can_create_proc_readentry(CAN_PROC_VERSION,
> + 0644, can_proc_read_version, NULL);
> + pde_stats = can_create_proc_readentry(CAN_PROC_STATS,
> + 0644, can_proc_read_stats, NULL);
> + pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS,
> + 0644, can_proc_read_reset_stats, NULL);
> + pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL,
> + 0644, can_proc_read_rcvlist_all, NULL);
> + pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL,
> + 0644, can_proc_read_rcvlist_fil, NULL);
> + pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV,
> + 0644, can_proc_read_rcvlist_inv, NULL);
> + pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF,
> + 0644, can_proc_read_rcvlist_sff, NULL);
> + pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF,
> + 0644, can_proc_read_rcvlist_eff, NULL);
> + pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR,
> + 0644, can_proc_read_rcvlist_err, NULL);
> +}
> +
> +/*
> + * 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_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_sff)
> + can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
> +
> + if (pde_rcvlist_eff)
> + can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
> +
> + if (pde_rcvlist_err)
> + can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
> +
> + if (can_dir)
> + remove_proc_entry(CAN_PROC_DIR, NULL);
> +}
>
> --
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-18 0:59 ` Paul E. McKenney
@ 2007-05-18 9:19 ` Oliver Hartkopp
2007-05-18 14:33 ` Paul E. McKenney
0 siblings, 1 reply; 86+ messages in thread
From: Oliver Hartkopp @ 2007-05-18 9:19 UTC (permalink / raw)
To: paulmck, Urs Thuermann
Cc: netdev, Thomas Gleixner, Oliver Hartkopp, Urs Thuermann
Hi Urs, Hello Paul,
i assume Paul refers to the can_rx_delete_all() function that adds each
receive list entry for rcu removal using the can_rx_delete RCU callback,
right?
So the idea would be to create a second RCU callback - e.g.
can_rx_delete_list() - that removes the complete list inside the RCU
callback?!?
The list removal would therefore be processed inside this new
can_rx_delete_list() in RCU context and not inside can_rx_delete_all().
@Paul: Was this your intention?
Best regards,
Oliver
Paul E. McKenney wrote:
> On Wed, May 16, 2007 at 04:51:02PM +0200, Urs Thuermann wrote:
>
>> This patch adds the CAN core functionality but no protocols or drivers.
>> No protocol implementations are included here. They come as separate
>> patches. Protocol numbers are already in include/linux/can.h.
>>
>
> Interesting! One question called out below -- why do call_rcu() on each
> piece of the struct dev_rcv_lists, instead of doing call_rcu() on the
> whole thing and having the RCU callback free up the pieces? Given that
> all the pieces are call_rcu()ed separately, there had better not be
> persistent pointers to the pieces, right?
>
> Doing it in one chunk would make the code a bit simpler and also reduce
> the RCU overhead a bit.
>
> Or am I missing something subtle here?
>
> Thanx, Paul
>
>
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-18 9:19 ` Oliver Hartkopp
@ 2007-05-18 14:33 ` Paul E. McKenney
2007-05-18 15:03 ` Oliver Hartkopp
2007-05-18 21:06 ` Oliver Hartkopp
0 siblings, 2 replies; 86+ messages in thread
From: Paul E. McKenney @ 2007-05-18 14:33 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Urs Thuermann, netdev, Thomas Gleixner, Oliver Hartkopp,
Urs Thuermann
On Fri, May 18, 2007 at 11:19:01AM +0200, Oliver Hartkopp wrote:
> Hi Urs, Hello Paul,
>
> i assume Paul refers to the can_rx_delete_all() function that adds each
> receive list entry for rcu removal using the can_rx_delete RCU callback,
> right?
>
> So the idea would be to create a second RCU callback - e.g.
> can_rx_delete_list() - that removes the complete list inside the RCU
> callback?!?
> The list removal would therefore be processed inside this new
> can_rx_delete_list() in RCU context and not inside can_rx_delete_all().
>
> @Paul: Was this your intention?
My intention was that the list-removing be placed into can_rcv_lists_delete(),
perhaps as follows:
static void can_rx_delete_all(struct hlist_head *rl)
{
struct receiver *r;
struct hlist_node *n;
hlist_for_each_entry(r, n, rl, list) {
hlist_del(&r->list);
kmem_cache_free(rcv_cache, r);
}
}
static void can_rcv_lists_delete(struct rcu_head *rp)
{
struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
/* remove all receivers hooked at this netdevice */
can_rx_delete_all(&d->rx_err);
can_rx_delete_all(&d->rx_all);
can_rx_delete_all(&d->rx_fil);
can_rx_delete_all(&d->rx_inv);
can_rx_delete_all(&d->rx_eff);
for (i = 0; i < 2048; i++)
can_rx_delete_all(&d->rx_sff[i]);
kfree(d);
}
Then the code in can_notifier() can reduce to the following:
if (d) {
hlist_del_rcu(&d->list);
/* used to be a string of can_rx_delete_all(). */
} else
printk(KERN_ERR "can: notifier: receive list not "
"found for dev %s\n", dev->name);
spin_lock_bh(&rcv_lists_lock);
if (d) {
call_rcu(&d->rcu, can_rcv_lists_delete);
}
This moves the traversal work into the callback function. This is not
a problem for CONFIG_PREEMPT_RT and non-CONFIG_PREEMPT, but not sure
about CONFIG_PREEMPT.
But it sure has the potential to cut down on a bunch of call_rcu()
work...
Thanx, Paul
> Best regards,
> Oliver
>
> Paul E. McKenney wrote:
> >On Wed, May 16, 2007 at 04:51:02PM +0200, Urs Thuermann wrote:
> >
> >>This patch adds the CAN core functionality but no protocols or drivers.
> >>No protocol implementations are included here. They come as separate
> >>patches. Protocol numbers are already in include/linux/can.h.
> >>
> >
> >Interesting! One question called out below -- why do call_rcu() on each
> >piece of the struct dev_rcv_lists, instead of doing call_rcu() on the
> >whole thing and having the RCU callback free up the pieces? Given that
> >all the pieces are call_rcu()ed separately, there had better not be
> >persistent pointers to the pieces, right?
> >
> >Doing it in one chunk would make the code a bit simpler and also reduce
> >the RCU overhead a bit.
> >
> >Or am I missing something subtle here?
> >
> > Thanx, Paul
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-18 14:33 ` Paul E. McKenney
@ 2007-05-18 15:03 ` Oliver Hartkopp
2007-05-18 21:06 ` Oliver Hartkopp
1 sibling, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-05-18 15:03 UTC (permalink / raw)
To: paulmck, Urs Thuermann
Cc: netdev, Thomas Gleixner, Oliver Hartkopp, Urs Thuermann
Hm, this is indeed one step further, than i thought :-)
Thanks for this nifty solution!
I will doublecheck your suggestion with Urs and then we'll change it in
our next patch update (after some more feedback on this mailing list).
Additional feedback is welcome.
Tnx & best regards,
Oliver
Paul E. McKenney wrote:
> On Fri, May 18, 2007 at 11:19:01AM +0200, Oliver Hartkopp wrote:
>
>> Hi Urs, Hello Paul,
>>
>> i assume Paul refers to the can_rx_delete_all() function that adds each
>> receive list entry for rcu removal using the can_rx_delete RCU callback,
>> right?
>>
>> So the idea would be to create a second RCU callback - e.g.
>> can_rx_delete_list() - that removes the complete list inside the RCU
>> callback?!?
>> The list removal would therefore be processed inside this new
>> can_rx_delete_list() in RCU context and not inside can_rx_delete_all().
>>
>> @Paul: Was this your intention?
>>
>
> My intention was that the list-removing be placed into can_rcv_lists_delete(),
> perhaps as follows:
>
> static void can_rx_delete_all(struct hlist_head *rl)
> {
> struct receiver *r;
> struct hlist_node *n;
>
> hlist_for_each_entry(r, n, rl, list) {
> hlist_del(&r->list);
> kmem_cache_free(rcv_cache, r);
> }
> }
>
> static void can_rcv_lists_delete(struct rcu_head *rp)
> {
> struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
>
> /* remove all receivers hooked at this netdevice */
> can_rx_delete_all(&d->rx_err);
> can_rx_delete_all(&d->rx_all);
> can_rx_delete_all(&d->rx_fil);
> can_rx_delete_all(&d->rx_inv);
> can_rx_delete_all(&d->rx_eff);
> for (i = 0; i < 2048; i++)
> can_rx_delete_all(&d->rx_sff[i]);
> kfree(d);
> }
>
> Then the code in can_notifier() can reduce to the following:
>
> if (d) {
> hlist_del_rcu(&d->list);
>
> /* used to be a string of can_rx_delete_all(). */
> } else
> printk(KERN_ERR "can: notifier: receive list not "
> "found for dev %s\n", dev->name);
>
> spin_lock_bh(&rcv_lists_lock);
>
> if (d) {
> call_rcu(&d->rcu, can_rcv_lists_delete);
> }
>
> This moves the traversal work into the callback function. This is not
> a problem for CONFIG_PREEMPT_RT and non-CONFIG_PREEMPT, but not sure
> about CONFIG_PREEMPT.
>
> But it sure has the potential to cut down on a bunch of call_rcu()
> work...
>
> Thanx, Paul
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [patch 2/7] CAN: Add PF_CAN core module
2007-05-18 14:33 ` Paul E. McKenney
2007-05-18 15:03 ` Oliver Hartkopp
@ 2007-05-18 21:06 ` Oliver Hartkopp
1 sibling, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-05-18 21:06 UTC (permalink / raw)
To: paulmck
Cc: Urs Thuermann, netdev, Thomas Gleixner, Oliver Hartkopp,
Urs Thuermann
Hello Paul,
as you may see in the attached SVN-log i changed some code according
your suggestions.
I additionally made some clarifications of function names.
If you would like to see the af_can.c completely please visit:
http://svn.berlios.de/svnroot/repos/socketcan/trunk/kernel/2.6/net/can/af_can.c
But this code in the projekt SVN is working for several kernel versions
(which is removed by some scripts when creating a LKML netdev patch) -
just for your information.
Best regards,
Oliver
-------- Original-Nachricht --------
Betreff: r311 - trunk/kernel/2.6/net/can
Datum: Fri, 18 May 2007 21:47:15 +0200
Von: hartkopp@mail.berlios.de
An: socketcan-commit@lists.berlios.de
Author: hartkopp
Date: 2007-05-18 21:47:14 +0200 (Fri, 18 May 2007)
New Revision: 311
Modified:
trunk/kernel/2.6/net/can/af_can.c
Log:
Updated RCU removal of dev_rcv_lists structures in the case of CAN-interfaces
going down (in can_notifier). Thanks to Paul E. McKenny (IBM) who gave this
hint on netdev kernel mailinglist.
Modified: trunk/kernel/2.6/net/can/af_can.c
===================================================================
--- trunk/kernel/2.6/net/can/af_can.c 2007-05-16 09:03:21 UTC (rev 310)
+++ trunk/kernel/2.6/net/can/af_can.c 2007-05-18 19:47:14 UTC (rev 311)
@@ -454,31 +454,48 @@
}
EXPORT_SYMBOL(can_rx_register);
-static void can_rcv_lists_delete(struct rcu_head *rp)
+static void can_rx_delete_list(struct hlist_head *rl)
{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ hlist_for_each_entry_rcu(r, n, rl, list) {
+ hlist_del_rcu(&r->list);
+ kmem_cache_free(rcv_cache, r);
+ }
+}
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+ int i;
+ /* remove all receivers hooked at this netdevice */
+ can_rx_delete_list(&d->rx_err);
+ can_rx_delete_list(&d->rx_all);
+ can_rx_delete_list(&d->rx_fil);
+ can_rx_delete_list(&d->rx_inv);
+ can_rx_delete_list(&d->rx_eff);
+
+ for (i = 0; i < 2048; i++)
+ can_rx_delete_list(&d->rx_sff[i]);
+
kfree(d);
}
-static void can_rx_delete(struct rcu_head *rp)
+/*
+ * 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);
}
-static void can_rx_delete_all(struct hlist_head *rl)
-{
- struct receiver *r;
- struct hlist_node *n;
-
- hlist_for_each_entry_rcu(r, n, rl, list) {
- hlist_del_rcu(&r->list);
- call_rcu(&r->rcu, can_rx_delete);
- }
-}
-
/**
* can_rx_unregister - unsubscribe CAN frames from a specific interface
* @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
@@ -556,7 +573,7 @@
/* schedule the receiver item for deletion */
if (r)
- call_rcu(&r->rcu, can_rx_delete);
+ call_rcu(&r->rcu, can_rx_delete_receiver);
return ret;
}
@@ -945,7 +962,6 @@
struct net_device *dev = (struct net_device *)data;
struct notifier *n;
struct dev_rcv_lists *d;
- int i;
DBG("called for %s, msg = %lu\n", dev->name, msg);
@@ -986,25 +1002,16 @@
spin_lock_bh(&rcv_lists_lock);
d = find_dev_rcv_lists(dev);
- if (d) {
+ if (d)
hlist_del_rcu(&d->list);
-
- /* remove all receivers hooked at this netdevice */
- can_rx_delete_all(&d->rx_err);
- can_rx_delete_all(&d->rx_all);
- can_rx_delete_all(&d->rx_fil);
- can_rx_delete_all(&d->rx_inv);
- can_rx_delete_all(&d->rx_eff);
- for (i = 0; i < 2048; i++)
- can_rx_delete_all(&d->rx_sff[i]);
- } else
+ else
printk(KERN_ERR "can: notifier: receive list not "
"found for dev %s\n", dev->name);
spin_unlock_bh(&rcv_lists_lock);
if (d)
- call_rcu(&d->rcu, can_rcv_lists_delete);
+ call_rcu(&d->rcu, can_rx_delete_device);
break;
}
_______________________________________________
Socketcan-commit mailing list
Socketcan-commit@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/socketcan-commit
^ permalink raw reply [flat|nested] 86+ messages in thread
* [patch 2/7] CAN: Add PF_CAN core module
2007-05-30 13:11 [patch 0/7] CAN: Add new PF_CAN protocol family, update Urs Thuermann
@ 2007-05-30 13:11 ` Urs Thuermann
0 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-05-30 13:11 UTC (permalink / raw)
To: David Miller; +Cc: Thomas Gleixner, Oliver Hartkopp, Urs Thuermann, netdev
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 62351 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 98 ++++
include/linux/can/core.h | 88 +++
include/linux/can/error.h | 95 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 1070 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 120 +++++
net/can/proc.c | 530 ++++++++++++++++++++++
10 files changed, 2034 insertions(+)
Index: linux-2.6.22-rc3/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/include/linux/can.h 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,98 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+typedef __u32 canid_t;
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ struct { int lcu, type; } bap;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: linux-2.6.22-rc3/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/include/linux/can/core.h 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,88 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070523"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+#define CAN_PROC_DIR "net/can" /* /proc/... */
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_dev_register(struct net_device *dev,
+ void (*func)(unsigned long msg, void *),
+ void *data);
+
+extern int can_dev_unregister(struct net_device *dev,
+ void (*func)(unsigned long msg, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: linux-2.6.22-rc3/net/Kconfig
===================================================================
--- linux-2.6.22-rc3.orig/net/Kconfig 2007-05-30 14:56:39.000000000 +0200
+++ linux-2.6.22-rc3/net/Kconfig 2007-05-30 14:57:59.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: linux-2.6.22-rc3/net/Makefile
===================================================================
--- linux-2.6.22-rc3.orig/net/Makefile 2007-05-30 14:56:39.000000000 +0200
+++ linux-2.6.22-rc3/net/Makefile 2007-05-30 14:57:59.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: linux-2.6.22-rc3/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/net/can/Kconfig 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: linux-2.6.22-rc3/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/net/can/Makefile 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: linux-2.6.22-rc3/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/net/can/af_can.c 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,1070 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+struct notifier {
+ struct list_head list;
+ struct net_device *dev;
+ void (*func)(unsigned long msg, void *data);
+ void *data;
+};
+
+static LIST_HEAD(notifier_list);
+static DEFINE_RWLOCK(notifier_lock);
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /* In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS)
+ printk(KERN_INFO "can: request_module(%s) not"
+ " implemented.\n", module_name);
+ else if (ret)
+ printk(KERN_ERR "can: request_module(%s) failed\n",
+ module_name);
+ }
+
+ /* check for success and correct type */
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
+ return -EPROTONOSUPPORT;
+
+ if (cp->capability >= 0 && !capable(cp->capability))
+ return -EPERM;
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * Return:
+ * 0 on success
+ * -ENETDOWN when the selected interface is down
+ * -ENOBUFS on full driver queue (see net_xmit_errno())
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ struct sock **tx_sk = (struct sock **)skb->cb;
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (loop) {
+ /* local loopback of sent CAN frames (default) */
+
+ /* indication for the CAN driver: do loopback */
+ *tx_sk = skb->sk;
+
+ /*
+ * The reference to the originating sock may be also required
+ * by the receiving socket to indicate (and ignore) his own
+ * sent data. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ */
+
+ /* interface not capabable to do the loopback itself? */
+ if (!(skb->dev->flags & IFF_LOOPBACK)) {
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ /* perform the local loopback here */
+ newskb->protocol = htons(ETH_P_CAN);
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(newskb);
+ }
+ } else {
+ /* indication for the CAN driver: no loopback required */
+ *tx_sk = NULL;
+ }
+
+ if (!(skb->dev->flags & IFF_UP))
+ return -ENETDOWN;
+
+ /* send to netdevice */
+ err = dev_queue_xmit(skb);
+ if (err > 0)
+ err = net_xmit_errno(err);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
+ dev, can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+static void can_rx_delete_list(struct hlist_head *rl)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ hlist_for_each_entry_rcu(r, n, rl, list) {
+ hlist_del_rcu(&r->list);
+ kmem_cache_free(rcv_cache, r);
+ }
+}
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+ int i;
+
+ /* remove all receivers hooked at this netdevice */
+ can_rx_delete_list(&d->rx[RX_ERR]);
+ can_rx_delete_list(&d->rx[RX_ALL]);
+ can_rx_delete_list(&d->rx[RX_FIL]);
+ can_rx_delete_list(&d->rx[RX_INV]);
+ can_rx_delete_list(&d->rx[RX_EFF]);
+
+ for (i = 0; i < 2048; i++)
+ can_rx_delete_list(&d->rx_sff[i]);
+
+ kfree(d);
+}
+
+/*
+ * 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.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ */
+int 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
+ dev, can_id, mask, func, data);
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ ret = -ENODEV;
+ 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(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ DBG("receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+ ret = -EINVAL;
+ r = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ out:
+ spin_unlock_bh(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out "
+ "of range\n", proto);
+ return -EINVAL;
+ }
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already "
+ "registered\n", proto);
+ return -EBUSY;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+ return -ESRCH;
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(can_proto_unregister);
+
+/**
+ * can_dev_register - subscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on status change
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Invokes the callback function with the status 'msg' and the given
+ * parameter 'data' on a status change of the given CAN network device.
+ *
+ * Return:
+ * 0 on success
+ * -ENOMEM on missing mem to create subscription entry
+ * -ENODEV unknown device
+ */
+int can_dev_register(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
+{
+ struct notifier *n;
+
+ DBG("called for %s\n", dev->name);
+
+ if (!dev || dev->type != ARPHRD_CAN)
+ return -ENODEV;
+
+ n = kmalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ return -ENOMEM;
+
+ n->dev = dev;
+ n->func = func;
+ n->data = data;
+
+ write_lock(¬ifier_lock);
+ list_add(&n->list, ¬ifier_list);
+ write_unlock(¬ifier_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(can_dev_register);
+
+/**
+ * can_dev_unregister - unsubscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Removes subscription entry depending on given (subscription) values.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ */
+int can_dev_unregister(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
+{
+ struct notifier *n, *next;
+ int ret = -EINVAL;
+
+ DBG("called for %s\n", dev->name);
+
+ write_lock(¬ifier_lock);
+ list_for_each_entry_safe(n, next, ¬ifier_list, list) {
+ if (n->dev == dev && n->func == func && n->data == data) {
+ list_del(&n->list);
+ kfree(n);
+ ret = 0;
+ break;
+ }
+ }
+ write_unlock(¬ifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_dev_unregister);
+
+static int can_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct notifier *n;
+ struct dev_rcv_lists *d;
+
+ DBG("called for %s, msg = %lu\n", dev->name, msg);
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_REGISTER:
+
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR "can: allocation of receive "
+ "list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d)
+ hlist_del_rcu(&d->list);
+ else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ read_lock(¬ifier_lock);
+ list_for_each_entry(n, ¬ifier_list, list) {
+ if (n->dev == dev)
+ n->func(msg, n->data);
+ }
+ read_unlock(¬ifier_lock);
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static 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 = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert struct dev_rcv_lists for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = jiffies + HZ;
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: linux-2.6.22-rc3/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/net/can/af_can.h 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: linux-2.6.22-rc3/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/net/can/proc.c 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,530 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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 = NULL;
+static struct proc_dir_entry *pde_version = NULL;
+static struct proc_dir_entry *pde_stats = NULL;
+static struct proc_dir_entry *pde_reset_stats = NULL;
+static struct proc_dir_entry *pde_rcvlist_all = NULL;
+static struct proc_dir_entry *pde_rcvlist_fil = NULL;
+static struct proc_dir_entry *pde_rcvlist_inv = NULL;
+static struct proc_dir_entry *pde_rcvlist_sff = NULL;
+static struct proc_dir_entry *pde_rcvlist_eff = NULL;
+static struct proc_dir_entry *pde_rcvlist_err = NULL;
+
+static int user_reset = 0;
+
+static const char *rx_list_name[] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_rcv() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = jiffies + HZ;
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08x %08x %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned int)r->func, (unsigned int)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int idx = (int)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t* read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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_PROC_DIR, NULL);
+
+ if (!can_dir) {
+ printk(KERN_INFO "can: failed to create /proc/%s . "
+ "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
+ return;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void*)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void*)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void*)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void*)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void*)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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_PROC_DIR, NULL);
+}
Index: linux-2.6.22-rc3/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc3/include/linux/can/error.h 2007-05-30 14:57:59.000000000 +0200
@@ -0,0 +1,95 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * $Id: error.h 162 2007-03-05 08:25:02Z hartkopp $
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [patch 2/7] CAN: Add PF_CAN core module
2007-06-22 3:44 [patch 0/7] CAN: Add new PF_CAN protocol family, try #3 Urs Thuermann
@ 2007-06-22 3:44 ` Urs Thuermann
0 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-06-22 3:44 UTC (permalink / raw)
To: David Miller; +Cc: Thomas Gleixner, Oliver Hartkopp, Urs Thuermann, netdev
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60556 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-Off-By: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-Off-By: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 98 ++++
include/linux/can/core.h | 80 +++
include/linux/can/error.h | 95 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 997 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 121 +++++
net/can/proc.c | 530 ++++++++++++++++++++++++
10 files changed, 1954 insertions(+)
Index: linux-2.6.22-rc5-git5/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/include/linux/can.h 2007-06-21 14:03:14.000000000 +0200
@@ -0,0 +1,98 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+typedef __u32 canid_t;
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ struct { int lcu, type; } bap;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: linux-2.6.22-rc5-git5/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/include/linux/can/core.h 2007-06-21 14:03:14.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070619"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+#define CAN_PROC_DIR "net/can" /* /proc/... */
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: linux-2.6.22-rc5-git5/net/Kconfig
===================================================================
--- linux-2.6.22-rc5-git5.orig/net/Kconfig 2007-06-21 14:02:05.000000000 +0200
+++ linux-2.6.22-rc5-git5/net/Kconfig 2007-06-21 14:03:14.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: linux-2.6.22-rc5-git5/net/Makefile
===================================================================
--- linux-2.6.22-rc5-git5.orig/net/Makefile 2007-06-21 14:02:05.000000000 +0200
+++ linux-2.6.22-rc5-git5/net/Makefile 2007-06-21 14:03:14.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: linux-2.6.22-rc5-git5/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/net/can/Kconfig 2007-06-21 15:34:24.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: linux-2.6.22-rc5-git5/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/net/can/Makefile 2007-06-21 15:34:24.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: linux-2.6.22-rc5-git5/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/net/can/af_can.c 2007-06-21 15:34:35.000000000 +0200
@@ -0,0 +1,997 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS)
+ printk(KERN_INFO "can: request_module(%s) not"
+ " implemented.\n", module_name);
+ else if (ret)
+ printk(KERN_ERR "can: request_module(%s) failed\n",
+ module_name);
+ }
+
+ /* check for success and correct type */
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
+ return -EPROTONOSUPPORT;
+
+ if (cp->capability >= 0 && !capable(cp->capability))
+ return -EPERM;
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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()
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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_LOOPBACK)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->iif = 0;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ */
+int 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ ret = -ENODEV;
+ 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(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ DBG("receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+ ret = -EINVAL;
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock_bh(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ clone->iif = skb->iif;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ return -EBUSY;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+ return -ESRCH;
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+
+ return 0;
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_REGISTER:
+
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static 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 = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = jiffies + HZ;
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: linux-2.6.22-rc5-git5/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/net/can/af_can.h 2007-06-21 14:03:14.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: linux-2.6.22-rc5-git5/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/net/can/proc.c 2007-06-21 14:03:14.000000000 +0200
@@ -0,0 +1,530 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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 = NULL;
+static struct proc_dir_entry *pde_version = NULL;
+static struct proc_dir_entry *pde_stats = NULL;
+static struct proc_dir_entry *pde_reset_stats = NULL;
+static struct proc_dir_entry *pde_rcvlist_all = NULL;
+static struct proc_dir_entry *pde_rcvlist_fil = NULL;
+static struct proc_dir_entry *pde_rcvlist_inv = NULL;
+static struct proc_dir_entry *pde_rcvlist_sff = NULL;
+static struct proc_dir_entry *pde_rcvlist_eff = NULL;
+static struct proc_dir_entry *pde_rcvlist_err = NULL;
+
+static int user_reset = 0;
+
+static const char *rx_list_name[] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_rcv() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = jiffies + HZ;
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08x %08x %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned int)r->func, (unsigned int)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int idx = (int)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t* read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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_PROC_DIR, NULL);
+
+ if (!can_dir) {
+ printk(KERN_INFO "can: failed to create /proc/%s . "
+ "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
+ return;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void*)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void*)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void*)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void*)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void*)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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_PROC_DIR, NULL);
+}
Index: linux-2.6.22-rc5-git5/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.22-rc5-git5/include/linux/can/error.h 2007-06-21 14:03:14.000000000 +0200
@@ -0,0 +1,95 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * $Id: error.h 162 2007-03-05 08:25:02Z hartkopp $
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [patch 2/7] CAN: Add PF_CAN core module
2007-08-04 2:06 [patch 0/7] CAN: Add new PF_CAN protocol family, try #5 Urs Thuermann
@ 2007-08-04 2:06 ` Urs Thuermann
0 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-08-04 2:06 UTC (permalink / raw)
To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60570 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 113 +++++
include/linux/can/core.h | 80 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 995 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 121 +++++
net/can/proc.c | 531 ++++++++++++++++++++++++
10 files changed, 1966 insertions(+)
Index: net-2.6/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can.h 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,113 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ struct { int lcu, type; } bap;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/core.h 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070802"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+#define CAN_PROC_DIR "net/can" /* /proc/... */
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6/net/Kconfig
===================================================================
--- net-2.6.orig/net/Kconfig 2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/net/Kconfig 2007-08-03 11:21:46.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6/net/Makefile
===================================================================
--- net-2.6.orig/net/Makefile 2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/net/Makefile 2007-08-03 11:21:46.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/Kconfig 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: net-2.6/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/Makefile 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/af_can.c 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,995 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS)
+ printk(KERN_INFO "can: request_module(%s) not"
+ " implemented.\n", module_name);
+ else if (ret)
+ printk(KERN_ERR "can: request_module(%s) failed\n",
+ module_name);
+ }
+
+ /* check for success and correct type */
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
+ return -EPROTONOSUPPORT;
+
+ if (cp->capability >= 0 && !capable(cp->capability))
+ return -EPERM;
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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()
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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_LOOPBACK)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ */
+int 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ ret = -ENODEV;
+ 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(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ DBG("receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+ ret = -EINVAL;
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock_bh(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ return -EBUSY;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+ return -ESRCH;
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+
+ return 0;
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_REGISTER:
+
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static 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 = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = jiffies + HZ;
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/af_can.h 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/proc.c 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,531 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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[] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_rcv() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = jiffies + HZ;
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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_PROC_DIR, NULL);
+
+ if (!can_dir) {
+ printk(KERN_INFO "can: failed to create /proc/%s . "
+ "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
+ return;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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_PROC_DIR, NULL);
+}
Index: net-2.6/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/error.h 2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-17 10:03 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #6 Urs Thuermann
@ 2007-09-17 10:03 ` Urs Thuermann
2007-09-17 15:50 ` Paul E. McKenney
2007-09-18 13:31 ` Patrick McHardy
0 siblings, 2 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-09-17 10:03 UTC (permalink / raw)
To: netdev
Cc: David Miller, Patrick McHardy, Thomas Gleixner, Oliver Hartkopp,
Urs Thuermann, Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60770 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 113 +++++
include/linux/can/core.h | 78 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 1002 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 121 +++++
net/can/proc.c | 531 ++++++++++++++++++++++++
10 files changed, 1971 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-09-17 10:27:09.000000000 +0200
@@ -0,0 +1,113 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ struct { int lcu, type; } bap;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-09-17 11:08:39.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070916"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-09-17 10:26:58.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-09-17 10:27:09.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-09-17 10:26:58.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-09-17 10:27:09.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-09-17 10:30:35.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-09-17 10:30:35.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-09-17 11:06:52.000000000 +0200
@@ -0,0 +1,1002 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS)
+ printk(KERN_INFO "can: request_module(%s) not"
+ " implemented.\n", module_name);
+ else if (ret)
+ printk(KERN_ERR "can: request_module(%s) failed\n",
+ module_name);
+ }
+
+ /* check for success and correct type */
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
+ return -EPROTONOSUPPORT;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ if (cp->capability >= 0 && !capable(cp->capability))
+ return -EPERM;
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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()
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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_LOOPBACK)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ */
+int 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+ int ret = 0;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ ret = -ENODEV;
+ 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(r, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ DBG("receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+ ret = -EINVAL;
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock_bh(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ return -EBUSY;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+ return -ESRCH;
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+
+ return 0;
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static 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 = {
+ .notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = jiffies + HZ;
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-09-17 10:27:09.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-09-17 11:07:19.000000000 +0200
@@ -0,0 +1,531 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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[] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ if (oldjif == newjif)
+ return 0;
+
+ /* see can_rcv() - this should NEVER happen! */
+ if (count > (ULONG_MAX / HZ)) {
+ printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+ count);
+ return 99999999;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = jiffies + HZ;
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-09-17 10:27:09.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-17 10:03 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-09-17 15:50 ` Paul E. McKenney
2007-09-18 13:31 ` Patrick McHardy
1 sibling, 0 replies; 86+ messages in thread
From: Paul E. McKenney @ 2007-09-17 15:50 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
On Mon, Sep 17, 2007 at 12:03:23PM +0200, Urs Thuermann wrote:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
Looks good to me!!!
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/can.h | 113 +++++
> include/linux/can/core.h | 78 +++
> include/linux/can/error.h | 93 ++++
> net/Kconfig | 1
> net/Makefile | 1
> net/can/Kconfig | 25 +
> net/can/Makefile | 6
> net/can/af_can.c | 1002 ++++++++++++++++++++++++++++++++++++++++++++++
> net/can/af_can.h | 121 +++++
> net/can/proc.c | 531 ++++++++++++++++++++++++
> 10 files changed, 1971 insertions(+)
>
> Index: net-2.6.24/include/linux/can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can.h 2007-09-17 10:27:09.000000000 +0200
> @@ -0,0 +1,113 @@
> +/*
> + * linux/can.h
> + *
> + * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_H
> +#define CAN_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/* controller area network (CAN) kernel definitions */
> +
> +/* special address description flags for the CAN_ID */
> +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
> +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
> +#define CAN_ERR_FLAG 0x20000000U /* error frame */
> +
> +/* valid bits in CAN ID for frame formats */
> +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
> +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
> +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
> +
> +/*
> + * Controller Area Network Identifier structure
> + *
> + * bit 0-28 : CAN identifier (11/29 bit)
> + * bit 29 : error frame flag (0 = data frame, 1 = error frame)
> + * bit 30 : remote transmission request flag (1 = rtr frame)
> + * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
> + */
> +typedef __u32 canid_t;
> +
> +/*
> + * Controller Area Network Error Frame Mask structure
> + *
> + * bit 0-28 : error class mask (see include/linux/can/error.h)
> + * bit 29-31 : set to zero
> + */
> +typedef __u32 can_err_mask_t;
> +
> +/**
> + * struct can_frame - basic CAN frame structure
> + * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
> + * @can_dlc: the data length field of the CAN frame
> + * @data: the CAN frame payload.
> + */
> +struct can_frame {
> + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
> + __u8 can_dlc; /* data length code: 0 .. 8 */
> + __u8 data[8] __attribute__((aligned(8)));
> +};
> +
> +/* particular protocols of the protocol family PF_CAN */
> +#define CAN_RAW 1 /* RAW sockets */
> +#define CAN_BCM 2 /* Broadcast Manager */
> +#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
> +#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
> +#define CAN_MCNET 5 /* Bosch MCNet */
> +#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
> +#define CAN_BAP 7 /* VAG Bedien- und Anzeigeprotokoll */
> +#define CAN_NPROTO 8
> +
> +#define SOL_CAN_BASE 100
> +
> +/**
> + * struct sockaddr_can - the sockaddr structure for CAN sockets
> + * @can_family: address family number AF_CAN.
> + * @can_ifindex: CAN network interface index.
> + * @can_addr: transport protocol specific address, mostly CAN IDs.
> + */
> +struct sockaddr_can {
> + sa_family_t can_family;
> + int can_ifindex;
> + union {
> + struct { canid_t rx_id, tx_id; } tp16;
> + struct { canid_t rx_id, tx_id; } tp20;
> + struct { canid_t rx_id, tx_id; } mcnet;
> + struct { canid_t rx_id, tx_id; } isotp;
> + struct { int lcu, type; } bap;
> + } can_addr;
> +};
> +
> +/**
> + * struct can_filter - CAN ID based filter in can_register().
> + * @can_id: relevant bits of CAN ID which are not masked out.
> + * @can_mask: CAN mask (see description)
> + *
> + * Description:
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + */
> +struct can_filter {
> + canid_t can_id;
> + canid_t can_mask;
> +};
> +
> +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
> +
> +#endif /* CAN_H */
> Index: net-2.6.24/include/linux/can/core.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can/core.h 2007-09-17 11:08:39.000000000 +0200
> @@ -0,0 +1,78 @@
> +/*
> + * linux/can/core.h
> + *
> + * Protoypes and definitions for CAN protocol modules using the PF_CAN core
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_CORE_H
> +#define CAN_CORE_H
> +
> +#include <linux/can.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +
> +#define CAN_VERSION "20070916"
> +
> +/* increment this number each time you change some user-space interface */
> +#define CAN_ABI_VERSION "8"
> +
> +#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
> +
> +#define DNAME(dev) ((dev) ? (dev)->name : "any")
> +
> +/**
> + * struct can_proto - CAN protocol structure
> + * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
> + * @protocol: protocol number in socket() syscall.
> + * @capability: capability needed to open the socket, or -1 for no restriction.
> + * @ops: pointer to struct proto_ops for sock->ops.
> + * @prot: pointer to struct proto structure.
> + */
> +struct can_proto {
> + int type;
> + int protocol;
> + int capability;
> + struct proto_ops *ops;
> + struct proto *prot;
> +};
> +
> +/* function prototypes for the CAN networklayer core (af_can.c) */
> +
> +extern int can_proto_register(struct can_proto *cp);
> +extern int can_proto_unregister(struct can_proto *cp);
> +
> +extern 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);
> +
> +extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
> + canid_t mask,
> + void (*func)(struct sk_buff *, void *),
> + void *data);
> +
> +extern int can_send(struct sk_buff *skb, int loop);
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +extern void can_debug_skb(struct sk_buff *skb);
> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
> +#define DBG(args...) (debug & 1 ? \
> + (printk(KERN_DEBUG "can-%s %s: ", \
> + IDENT, __func__), printk(args)) : 0)
> +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> +#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
> +#else
> +#define DBG(args...)
> +#define DBG_FRAME(args...)
> +#define DBG_SKB(skb)
> +#endif
> +
> +#endif /* CAN_CORE_H */
> Index: net-2.6.24/net/Kconfig
> ===================================================================
> --- net-2.6.24.orig/net/Kconfig 2007-09-17 10:26:58.000000000 +0200
> +++ net-2.6.24/net/Kconfig 2007-09-17 10:27:09.000000000 +0200
> @@ -210,6 +210,7 @@
> endmenu
>
> source "net/ax25/Kconfig"
> +source "net/can/Kconfig"
> source "net/irda/Kconfig"
> source "net/bluetooth/Kconfig"
> source "net/rxrpc/Kconfig"
> Index: net-2.6.24/net/Makefile
> ===================================================================
> --- net-2.6.24.orig/net/Makefile 2007-09-17 10:26:58.000000000 +0200
> +++ net-2.6.24/net/Makefile 2007-09-17 10:27:09.000000000 +0200
> @@ -34,6 +34,7 @@
> obj-$(CONFIG_NETROM) += netrom/
> obj-$(CONFIG_ROSE) += rose/
> obj-$(CONFIG_AX25) += ax25/
> +obj-$(CONFIG_CAN) += can/
> obj-$(CONFIG_IRDA) += irda/
> obj-$(CONFIG_BT) += bluetooth/
> obj-$(CONFIG_SUNRPC) += sunrpc/
> Index: net-2.6.24/net/can/Kconfig
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/Kconfig 2007-09-17 10:30:35.000000000 +0200
> @@ -0,0 +1,25 @@
> +#
> +# 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 at
> + 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.
> +
> +config CAN_DEBUG_CORE
> + bool "CAN Core debugging messages"
> + depends on CAN
> + ---help---
> + Say Y here if you want the CAN core to produce a bunch of debug
> + messages to the system log. Select this if you are having a
> + problem with CAN support and want to see more of what is going on.
> Index: net-2.6.24/net/can/Makefile
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/Makefile 2007-09-17 10:30:35.000000000 +0200
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the Linux Controller Area Network core.
> +#
> +
> +obj-$(CONFIG_CAN) += can.o
> +can-objs := af_can.o proc.o
> Index: net-2.6.24/net/can/af_can.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/af_can.c 2007-09-17 11:06:52.000000000 +0200
> @@ -0,0 +1,1002 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.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 <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "af_can.h"
> +
> +#define IDENT "core"
> +static __initdata 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);
> +
> +int stats_timer = 1; /* default: on */
> +module_param(stats_timer, int, S_IRUGO);
> +MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +static int debug;
> +module_param(debug, int, S_IRUGO);
> +MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
> +#endif
> +
> +HLIST_HEAD(rx_dev_list);
> +static struct dev_rcv_lists rx_alldev_list;
> +static DEFINE_SPINLOCK(rcv_lists_lock);
> +
> +static struct kmem_cache *rcv_cache __read_mostly;
> +
> +/* table of registered CAN protocols */
> +static struct can_proto *proto_tab[CAN_NPROTO];
> +
> +struct timer_list stattimer; /* timer for statistics update */
> +struct s_stats stats; /* packet statistics */
> +struct s_pstats pstats; /* receive list statistics */
> +
> +/*
> + * af_can socket functions
> + */
> +
> +static 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;
> + }
> +}
> +
> +static void can_sock_destruct(struct sock *sk)
> +{
> + DBG("called for sock %p\n", sk);
> +
> + skb_queue_purge(&sk->sk_receive_queue);
> + if (sk->sk_protinfo)
> + kfree(sk->sk_protinfo);
> +}
> +
> +static int can_create(struct net *net, struct socket *sock, int protocol)
> +{
> + struct sock *sk;
> + struct can_proto *cp;
> + char module_name[sizeof("can-proto-000")];
> + int ret = 0;
> +
> + DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
> +
> + sock->state = SS_UNCONNECTED;
> +
> + if (protocol < 0 || protocol >= CAN_NPROTO)
> + return -EINVAL;
> +
> + DBG("looking up proto %d in proto_tab[]\n", protocol);
> +
> + /* try to load protocol module, when CONFIG_KMOD is defined */
> + if (!proto_tab[protocol]) {
> + sprintf(module_name, "can-proto-%d", protocol);
> + ret = request_module(module_name);
> +
> + /*
> + * In case of error we only print a message but don't
> + * return the error code immediately. Below we will
> + * return -EPROTONOSUPPORT
> + */
> + if (ret == -ENOSYS)
> + printk(KERN_INFO "can: request_module(%s) not"
> + " implemented.\n", module_name);
> + else if (ret)
> + printk(KERN_ERR "can: request_module(%s) failed\n",
> + module_name);
> + }
> +
> + /* check for success and correct type */
> + cp = proto_tab[protocol];
> + if (!cp || cp->type != sock->type)
> + return -EPROTONOSUPPORT;
> +
> + if (net != &init_net)
> + return -EAFNOSUPPORT;
> +
> + if (cp->capability >= 0 && !capable(cp->capability))
> + return -EPERM;
> +
> + sock->ops = cp->ops;
> +
> + sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
> + if (!sk)
> + return -ENOMEM;
> +
> + sock_init_data(sock, sk);
> + sk->sk_destruct = can_sock_destruct;
> +
> + DBG("created sock: %p\n", sk);
> +
> + if (sk->sk_prot->init)
> + ret = sk->sk_prot->init(sk);
> +
> + if (ret) {
> + /* release sk on errors */
> + sock_orphan(sk);
> + sock_put(sk);
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * 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!)
> + *
> + * 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()
> + */
> +int can_send(struct sk_buff *skb, int loop)
> +{
> + int err;
> +
> + if (skb->dev->type != ARPHRD_CAN) {
> + kfree_skb(skb);
> + return -EPERM;
> + }
> +
> + if (!(skb->dev->flags & IFF_UP)) {
> + kfree_skb(skb);
> + return -ENETDOWN;
> + }
> +
> + skb->protocol = htons(ETH_P_CAN);
> +
> + 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_LOOPBACK)) {
> + /*
> + * If the interface is not capable to do loopback
> + * itself, we do it here.
> + */
> + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> +
> + if (!newskb) {
> + kfree_skb(skb);
> + return -ENOMEM;
> + }
> +
> + newskb->sk = skb->sk;
> + newskb->ip_summed = CHECKSUM_UNNECESSARY;
> + newskb->pkt_type = PACKET_BROADCAST;
> + netif_rx(newskb);
> + }
> + } 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);
> +
> + /* update statistics */
> + stats.tx_frames++;
> + stats.tx_frames_delta++;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_send);
> +
> +/*
> + * af_can rx path
> + */
> +
> +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /*
> + * find receive list for this device
> + *
> + * The hlist_for_each_entry*() macros curse through the list
> + * using the pointer variable n and set d to the containing
> + * struct in each list iteration. Therefore, after list
> + * iteration, d is unmodified when the list is empty, and it
> + * points to last list element, when the list is non-empty
> + * but no match in the loop body is found. I.e. d is *not*
> + * NULL when no match is found. We can, however, use the
> + * cursor variable n to decide if a match was found.
> + */
> +
> + hlist_for_each_entry(d, n, &rx_dev_list, list) {
> + if (d->dev == dev)
> + break;
> + }
> +
> + return n ? d : NULL;
> +}
> +
> +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 error frames */
> + if (*mask & CAN_ERR_FLAG) {
> + /* clear CAN_ERR_FLAG in list entry */
> + *mask &= CAN_ERR_MASK;
> + return &d->rx[RX_ERR];
> + }
> +
> + /* ensure valid values in can_mask */
> + if (*mask & CAN_EFF_FLAG)
> + *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
> + else
> + *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
> +
> + /* 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];
> +
> + /* use extra filterset for the subscription of exactly *ONE* can_id */
> + if (*can_id & CAN_EFF_FLAG) {
> + if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
> + /* RFC: a use-case for hash-tables in the future? */
> + return &d->rx[RX_EFF];
> + }
> + } else {
> + if (*mask == CAN_SFF_MASK)
> + 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 indentification
> + *
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + *
> + * 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 ret = 0;
> +
> + /* insert new receiver (dev,canid,mask) -> (func,data) */
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
> + "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
> +
> + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
> + if (!r)
> + return -ENOMEM;
> +
> + spin_lock_bh(&rcv_lists_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++;
> +
> + pstats.rcv_entries++;
> + if (pstats.rcv_entries_max < pstats.rcv_entries)
> + pstats.rcv_entries_max = pstats.rcv_entries;
> + } else {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + kmem_cache_free(rcv_cache, r);
> + ret = -ENODEV;
> + }
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_register);
> +
> +/*
> + * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
> + */
> +static void can_rx_delete_device(struct rcu_head *rp)
> +{
> + struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
> +
> + DBG("removing dev_rcv_list at %p\n", d);
> + kfree(d);
> +}
> +
> +/*
> + * 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);
> +
> + DBG("removing receiver at %p\n", r);
> + 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.
> + *
> + * Return:
> + * 0 on success
> + * -EINVAL on missing subscription entry
> + * -ENODEV unknown device
> + */
> +int 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 hlist_node *next;
> + struct dev_rcv_lists *d;
> + int ret = 0;
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
> + dev, DNAME(dev), can_id, mask, func, data);
> +
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (!d) {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + ret = -ENODEV;
> + 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(r, next, rl, list) {
> + if (r->can_id == can_id && r->mask == mask
> + && r->func == func && r->data == data)
> + break;
> + }
> +
> + /*
> + * Check for bug in CAN protocol implementations:
> + * If no matching list item was found, the list cursor variable next
> + * will be NULL, while r will point to the last item of the list.
> + */
> +
> + if (!next) {
> + DBG("receive list entry not found for "
> + "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
> + ret = -EINVAL;
> + r = NULL;
> + d = NULL;
> + goto out;
> + }
> +
> + hlist_del_rcu(&r->list);
> + d->entries--;
> +
> + if (pstats.rcv_entries > 0)
> + pstats.rcv_entries--;
> +
> + /* remove device structure requested by NETDEV_UNREGISTER */
> + if (d->remove_on_zero_entries && !d->entries) {
> + DBG("removing dev_rcv_list for %s on zero entries\n",
> + dev->name);
> + hlist_del_rcu(&d->list);
> + } else
> + d = NULL;
> +
> + out:
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + /* schedule the receiver item for deletion */
> + if (r)
> + call_rcu(&r->rcu, can_rx_delete_receiver);
> +
> + /* schedule the device structure for deletion */
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_unregister);
> +
> +static inline void deliver(struct sk_buff *skb, struct receiver *r)
> +{
> + struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
> +
> + DBG("skbuff %p cloned to %p\n", skb, clone);
> + if (clone) {
> + clone->sk = skb->sk;
> + r->func(clone, r->data);
> + r->matches++;
> + }
> +}
> +
> +static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> + 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 frame entries only */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
> + if (can_id & r->mask) {
> + DBG("match on rx_err skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + return matches;
> + }
> +
> + /* check for unfiltered entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
> + DBG("match on rx_all skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> +
> + /* check for can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
> + if ((can_id & r->mask) == r->can_id) {
> + DBG("match on rx_fil skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check for inverted can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
> + if ((can_id & r->mask) != r->can_id) {
> + DBG("match on rx_inv skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check CAN_ID specific entries */
> + if (can_id & CAN_EFF_FLAG) {
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
> + if (r->can_id == can_id) {
> + DBG("match on rx_eff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + } else {
> + can_id &= CAN_SFF_MASK;
> + hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
> + DBG("match on rx_sff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + return matches;
> +}
> +
> +static int can_rcv(struct sk_buff *skb, struct net_device *dev,
> + struct packet_type *pt, struct net_device *orig_dev)
> +{
> + struct dev_rcv_lists *d;
> + int matches;
> +
> + DBG("received skbuff on device %s, ptype %04x\n",
> + dev->name, ntohs(pt->type));
> + DBG_SKB(skb);
> + DBG_FRAME("af_can: can_rcv: received CAN frame",
> + (struct can_frame *)skb->data);
> +
> + if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
> + kfree_skb(skb);
> + return 0;
> + }
> +
> + /* update statistics */
> + stats.rx_frames++;
> + stats.rx_frames_delta++;
> +
> + rcu_read_lock();
> +
> + /* deliver the packet to sockets listening on all devices */
> + matches = can_rcv_filter(&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();
> +
> + /* free the skbuff allocated by the netdevice driver */
> + DBG("freeing skbuff %p\n", skb);
> + kfree_skb(skb);
> +
> + if (matches > 0) {
> + stats.matches++;
> + stats.matches_delta++;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * 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(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> + int err = 0;
> +
> + if (proto < 0 || proto >= CAN_NPROTO) {
> + printk(KERN_ERR "can: protocol number %d out of range\n",
> + proto);
> + return -EINVAL;
> + }
> + if (proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d already registered\n",
> + proto);
> + return -EBUSY;
> + }
> +
> + err = proto_register(cp->prot, 0);
> + if (err < 0)
> + return err;
> +
> + proto_tab[proto] = cp;
> +
> + /* use generic ioctl function if the module doesn't bring its own */
> + if (!cp->ops->ioctl)
> + cp->ops->ioctl = can_ioctl;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_proto_register);
> +
> +/**
> + * can_proto_unregister - unregister CAN transport protocol
> + * @cp: pointer to CAN protocol structure
> + *
> + * Return:
> + * 0 on success
> + * -ESRCH protocol number was not registered
> + */
> +int can_proto_unregister(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> +
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d is not registered\n", proto);
> + return -ESRCH;
> + }
> + proto_unregister(cp->prot);
> + proto_tab[proto] = NULL;
> +
> + return 0;
> +}
> +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 *data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct dev_rcv_lists *d;
> +
> + DBG("msg %ld for dev %p (%s idx %d)\n",
> + msg, dev, dev->name, dev->ifindex);
> +
> + if (dev->nd_net != &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
> + *
> + * N.B. zeroing the struct is the correct initialization
> + * for the embedded hlist_head structs.
> + * Another list type, e.g. list_head, would require
> + * explicit initialization.
> + */
> +
> + DBG("creating new dev_rcv_lists for %s\n", dev->name);
> +
> + d = kzalloc(sizeof(*d),
> + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
> + if (!d) {
> + printk(KERN_ERR
> + "can: allocation of receive list failed\n");
> + return NOTIFY_DONE;
> + }
> + d->dev = dev;
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&d->list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + break;
> +
> + case NETDEV_UNREGISTER:
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (d) {
> + DBG("remove dev_rcv_list for %s (%d entries)\n",
> + dev->name, d->entries);
> +
> + if (d->entries) {
> + d->remove_on_zero_entries = 1;
> + d = NULL;
> + } else
> + hlist_del_rcu(&d->list);
> + } else
> + printk(KERN_ERR "can: notifier: receive list not "
> + "found for dev %s\n", dev->name);
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +
> + break;
> + }
> +
> + return NOTIFY_DONE;
> +}
> +
> +/*
> + * af_can debugging stuff
> + */
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +
> +#define DBG_BSIZE 1024
> +
> +/**
> + * can_debug_cframe - print CAN frame
> + * @msg: pointer to message printed before the given CAN frame
> + * @cf: pointer to CAN frame
> + */
> +void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
> +{
> + va_list ap;
> + int len;
> + int dlc, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf, KERN_DEBUG);
> + va_start(ap, cf);
> + len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
> + buf[len++] = ':';
> + buf[len++] = ' ';
> + va_end(ap);
> +
> + dlc = cf->can_dlc;
> + if (dlc > 8)
> + dlc = 8;
> +
> + if (cf->can_id & CAN_EFF_FLAG)
> + len += sprintf(buf + len, "<%08X> [%X] ",
> + cf->can_id & CAN_EFF_MASK, dlc);
> + else
> + len += sprintf(buf + len, "<%03X> [%X] ",
> + cf->can_id & CAN_SFF_MASK, dlc);
> +
> + for (i = 0; i < dlc; i++)
> + len += sprintf(buf + len, "%02X ", cf->data[i]);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + len += sprintf(buf + len, "(RTR)");
> +
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_cframe);
> +
> +/**
> + * can_debug_skb - print socket buffer content to kernel log
> + * @skb: pointer to socket buffer
> + */
> +void can_debug_skb(struct sk_buff *skb)
> +{
> + int len, nbytes, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf,
> + KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
> + KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
> + "h,d,t,e,l: %p %+d %+d %+d, %d",
> + skb, skb->dev ? skb->dev->ifindex : -1,
> + ntohs(skb->protocol),
> + atomic_read(&skb->users),
> + atomic_read(&(skb_shinfo(skb)->dataref)),
> + skb_shinfo(skb)->nr_frags,
> + skb->head, skb->data - skb->head,
> + skb->tail - skb->head, skb->end - skb->head, skb->len);
> + nbytes = skb->end - skb->head;
> + for (i = 0; i < nbytes; i++) {
> + if (i % 16 == 0)
> + len += sprintf(buf + len, "\n" KERN_DEBUG " ");
> + if (len < DBG_BSIZE - 16) {
> + len += sprintf(buf + len, " %02x", skb->head[i]);
> + } else {
> + len += sprintf(buf + len, "...");
> + break;
> + }
> + }
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_skb);
> +
> +#endif
> +
> +/*
> + * af_can module init/exit functions
> + */
> +
> +static struct packet_type can_packet = {
> + .type = __constant_htons(ETH_P_CAN),
> + .dev = NULL,
> + .func = can_rcv,
> +};
> +
> +static 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 = {
> + .notifier_call = can_notifier,
> +};
> +
> +static __init int can_init(void)
> +{
> + printk(banner);
> +
> + rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
> + 0, 0, NULL);
> + if (!rcv_cache)
> + return -ENOMEM;
> +
> + /*
> + * Insert rx_alldev_list for reception on all devices.
> + * This struct is zero initialized which is correct for the
> + * embedded hlist heads, the dev pointer, and the entries counter.
> + */
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (stats_timer) {
> + /* the statistics are updated every second (timer triggered) */
> + init_timer(&stattimer);
> + stattimer.function = can_stat_update;
> + stattimer.data = 0;
> + /* update every second */
> + stattimer.expires = jiffies + HZ;
> + /* start statistics timer */
> + add_timer(&stattimer);
> + } else
> + stattimer.function = NULL;
> +
> + /* procfs init */
> + can_init_proc();
> +
> + /* protocol register */
> + sock_register(&can_family_ops);
> + register_netdevice_notifier(&can_netdev_notifier);
> + dev_add_pack(&can_packet);
> +
> + return 0;
> +}
> +
> +static __exit void can_exit(void)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n, *next;
> +
> + if (stats_timer)
> + del_timer(&stattimer);
> +
> + /* procfs remove */
> + can_remove_proc();
> +
> + /* protocol unregister */
> + dev_remove_pack(&can_packet);
> + unregister_netdevice_notifier(&can_netdev_notifier);
> + sock_unregister(PF_CAN);
> +
> + /* remove rx_dev_list */
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_del(&rx_alldev_list.list);
> + hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
> + hlist_del(&d->list);
> + kfree(d);
> + }
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + kmem_cache_destroy(rcv_cache);
> +}
> +
> +module_init(can_init);
> +module_exit(can_exit);
> Index: net-2.6.24/net/can/af_can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/af_can.h 2007-09-17 10:27:09.000000000 +0200
> @@ -0,0 +1,121 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#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;
> +};
> +
> +enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
> +
> +struct dev_rcv_lists {
> + struct hlist_node list;
> + struct rcu_head rcu;
> + struct net_device *dev;
> + struct hlist_head rx[RX_MAX];
> + struct hlist_head rx_sff[0x800];
> + int remove_on_zero_entries;
> + int entries;
> +};
> +
> +/* statistic structures */
> +
> +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;
> +}; /* can be reset e.g. by can_init_stats() */
> +
> +struct s_pstats {
> + unsigned long stats_reset;
> + unsigned long user_reset;
> + unsigned long rcv_entries;
> + unsigned long rcv_entries_max;
> +}; /* persistent statistics */
> +
> +/* function prototypes for the CAN networklayer procfs (proc.c) */
> +extern void can_init_proc(void);
> +extern void can_remove_proc(void);
> +extern void can_stat_update(unsigned long data);
> +
> +/* structures and variables from af_can.c needed in proc.c for reading */
> +extern struct timer_list stattimer; /* timer for statistics update */
> +extern struct s_stats stats; /* packet statistics */
> +extern struct s_pstats pstats; /* receive list statistics */
> +extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
> +
> +#endif /* AF_CAN_H */
> Index: net-2.6.24/net/can/proc.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/proc.c 2007-09-17 11:07:19.000000000 +0200
> @@ -0,0 +1,531 @@
> +/*
> + * 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, the following disclaimer and
> + * the referenced file 'COPYING'.
> + * 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 as distributed in the 'COPYING'
> + * file from the main directory of the linux kernel source.
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/rcupdate.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[] = {
> + [RX_ERR] = "rx_err",
> + [RX_ALL] = "rx_all",
> + [RX_FIL] = "rx_fil",
> + [RX_INV] = "rx_inv",
> + [RX_EFF] = "rx_eff",
> +};
> +
> +/*
> + * af_can statistics stuff
> + */
> +
> +static void can_init_stats(void)
> +{
> + /*
> + * This memset function is called from a timer context (when
> + * stattimer is active which is the default) OR in a process
> + * context (reading the proc_fs when stattimer is disabled).
> + */
> + memset(&stats, 0, sizeof(stats));
> + stats.jiffies_init = jiffies;
> +
> + pstats.stats_reset++;
> +
> + if (user_reset) {
> + user_reset = 0;
> + pstats.user_reset++;
> + }
> +}
> +
> +static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
> + unsigned long count)
> +{
> + unsigned long ret = 0;
> +
> + if (oldjif == newjif)
> + return 0;
> +
> + /* see can_rcv() - this should NEVER happen! */
> + if (count > (ULONG_MAX / HZ)) {
> + printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
> + count);
> + return 99999999;
> + }
> +
> + ret = (count * HZ) / (newjif - oldjif);
> +
> + return ret;
> +}
> +
> +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 < stats.jiffies_init)
> + can_init_stats();
> +
> + /* stats.rx_frames is the definitively max. statistic value */
> +
> + /* prevent overflow in calc_rate() */
> + if (stats.rx_frames > (ULONG_MAX / HZ))
> + can_init_stats();
> +
> + /* matches overflow - very improbable */
> + if (stats.matches > (ULONG_MAX / 100))
> + can_init_stats();
> +
> + /* calc total values */
> + if (stats.rx_frames)
> + stats.total_rx_match_ratio = (stats.matches * 100) /
> + stats.rx_frames;
> +
> + stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
> + stats.tx_frames);
> + stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
> + stats.rx_frames);
> +
> + /* calc current values */
> + if (stats.rx_frames_delta)
> + stats.current_rx_match_ratio =
> + (stats.matches_delta * 100) / stats.rx_frames_delta;
> +
> + stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
> + stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
> +
> + /* check / update maximum values */
> + if (stats.max_tx_rate < stats.current_tx_rate)
> + stats.max_tx_rate = stats.current_tx_rate;
> +
> + if (stats.max_rx_rate < stats.current_rx_rate)
> + stats.max_rx_rate = stats.current_rx_rate;
> +
> + if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
> + stats.max_rx_match_ratio = stats.current_rx_match_ratio;
> +
> + /* clear values for 'current rate' calculation */
> + stats.tx_frames_delta = 0;
> + stats.rx_frames_delta = 0;
> + stats.matches_delta = 0;
> +
> + /* restart timer (one second) */
> + stattimer.expires = jiffies + HZ;
> + add_timer(&stattimer);
> +}
> +
> +/*
> + * proc read functions
> + *
> + * From known use-cases we expect about 10 entries in a receive list to be
> + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
> + *
> + */
> +
> +static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
> + struct net_device *dev)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(r, n, rx_list, list) {
> + char *fmt = (r->can_id & CAN_EFF_FLAG)?
> + " %-5s %08X %08x %08x %08x %8ld %s\n" :
> + " %-5s %03X %08x %08lx %08lx %8ld %s\n";
> +
> + len += snprintf(page + len, PAGE_SIZE - len, fmt,
> + DNAME(dev), r->can_id, r->mask,
> + (unsigned long)r->func, (unsigned long)r->data,
> + r->matches, r->ident);
> +
> + /* does a typical line fit into the current buffer? */
> +
> + /* 100 Bytes before end of buffer */
> + if (len > PAGE_SIZE - 100) {
> + /* mark output cut off */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (..)\n");
> + break;
> + }
> + }
> + rcu_read_unlock();
> +
> + return len;
> +}
> +
> +static int can_print_recv_banner(char *page, int len)
> +{
> + /*
> + * can1. 00000000 00000000 00000000
> + * ....... 0 tp20
> + */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " device can_id can_mask function"
> + " userdata matches ident\n");
> +
> + return len;
> +}
> +
> +static int can_proc_read_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld transmitted frames (TXF)\n", stats.tx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld received frames (RXF)\n", stats.rx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld matched frames (RXMF)\n", stats.matches);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% total match ratio (RXMR)\n",
> + stats.total_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total tx rate (TXR)\n",
> + stats.total_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total rx rate (RXR)\n",
> + stats.total_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% current match ratio (CRXMR)\n",
> + stats.current_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current tx rate (CTXR)\n",
> + stats.current_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current rx rate (CRXR)\n",
> + stats.current_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% max match ratio (MRXMR)\n",
> + stats.max_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max tx rate (MTXR)\n",
> + stats.max_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max rx rate (MRXR)\n",
> + stats.max_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + }
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld current receive list entries (CRCV)\n",
> + pstats.rcv_entries);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld maximum receive list entries (MRCV)\n",
> + pstats.rcv_entries_max);
> +
> + if (pstats.stats_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\n %8ld statistic resets (STR)\n",
> + pstats.stats_reset);
> +
> + if (pstats.user_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld user statistic resets (USTR)\n",
> + pstats.user_reset);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_reset_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + user_reset = 1;
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Scheduled statistic reset #%ld.\n",
> + pstats.stats_reset + 1);
> +
> + } else {
> + if (stats.jiffies_init != jiffies)
> + can_init_stats();
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Performed statistic reset #%ld.\n",
> + pstats.stats_reset);
> + }
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_version(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
> + CAN_VERSION_STRING);
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + /* double cast to prevent GCC warning */
> + int idx = (int)(long)data;
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list '%s':\n", rx_list_name[idx]);
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx[idx])) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_SFF */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_sff':\n");
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> + int i, all_empty = 1;
> + /* check wether at least one list is non-empty */
> + for (i = 0; i < 0x800; i++)
> + if (!hlist_empty(&d->rx_sff[i])) {
> + all_empty = 0;
> + break;
> + }
> +
> + if (!all_empty) {
> + len = can_print_recv_banner(page, len);
> + for (i = 0; i < 0x800; i++) {
> + if (!hlist_empty(&d->rx_sff[i]) &&
> + len < PAGE_SIZE - 100)
> + len = can_print_rcvlist(page, len,
> + &d->rx_sff[i],
> + d->dev);
> + }
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +/*
> + * proc utility functions
> + */
> +
> +static struct proc_dir_entry *can_create_proc_readentry(const char *name,
> + mode_t mode,
> + read_proc_t *read_proc,
> + void *data)
> +{
> + if (can_dir)
> + return create_proc_read_entry(name, mode, can_dir, read_proc,
> + data);
> + else
> + return NULL;
> +}
> +
> +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;
> + }
> +
> + can_dir->owner = THIS_MODULE;
> +
> + /* own procfs entries from the AF_CAN core */
> + pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
> + can_proc_read_version, NULL);
> + pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
> + can_proc_read_stats, NULL);
> + pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
> + can_proc_read_reset_stats, NULL);
> + pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
> + can_proc_read_rcvlist, (void *)RX_ERR);
> + pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
> + can_proc_read_rcvlist, (void *)RX_ALL);
> + pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
> + can_proc_read_rcvlist, (void *)RX_FIL);
> + pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
> + can_proc_read_rcvlist, (void *)RX_INV);
> + pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
> + can_proc_read_rcvlist, (void *)RX_EFF);
> + pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
> + can_proc_read_rcvlist_sff, NULL);
> +}
> +
> +/*
> + * 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)
> + proc_net_remove(&init_net, "can");
> +}
> Index: net-2.6.24/include/linux/can/error.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can/error.h 2007-09-17 10:27:09.000000000 +0200
> @@ -0,0 +1,93 @@
> +/*
> + * linux/can/error.h
> + *
> + * Definitions of the CAN error frame to be filtered and passed to the user.
> + *
> + * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_ERROR_H
> +#define CAN_ERROR_H
> +
> +#define CAN_ERR_DLC 8 /* dlc for error frames */
> +
> +/* error class (mask) in can_id */
> +#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
> +#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
> +#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
> +#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
> +#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
> +#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
> +#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
> +#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
> +#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
> +
> +/* arbitration lost in bit ... / data[0] */
> +#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
> + /* else bit number in bitstream */
> +
> +/* error status of CAN-controller / data[1] */
> +#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
> +#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
> +#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
> +#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
> +#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
> +#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
> + /* (at least one error counter exceeds */
> + /* the protocol-defined level of 127) */
> +
> +/* error in CAN protocol (type) / data[2] */
> +#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
> +#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
> +#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
> +#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
> +#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
> +#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
> +#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
> +#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
> +
> +/* error in CAN protocol (location) / data[3] */
> +#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
> +#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
> +#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
> +#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
> +#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
> +#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
> +#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
> +#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
> +#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
> +#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
> +#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
> +#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
> +#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
> +#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
> +#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
> +#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
> +#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
> +#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
> +#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
> +
> +/* error status of CAN-transceiver / data[4] */
> +/* CANH CANL */
> +#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
> +#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
> +#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
> +
> +/* controller specific additional information / data[5..7] */
> +
> +#endif /* CAN_ERROR_H */
>
> --
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-17 10:03 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-17 15:50 ` Paul E. McKenney
@ 2007-09-18 13:31 ` Patrick McHardy
2007-09-18 14:54 ` Urs Thuermann
2007-09-18 21:20 ` Urs Thuermann
1 sibling, 2 replies; 86+ messages in thread
From: Patrick McHardy @ 2007-09-18 13:31 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Urs Thuermann wrote:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
Looks pretty good, please see below for a few comments (mostly minor
nitpicking, a few things that look like real bugs). Nothing that
couldn't be fixed after merging though.
> +++ net-2.6.24/include/linux/can.h 2007-09-17 10:27:09.000000000 +0200
> @@ -0,0 +1,113 @@
> +/*
> + * linux/can.h
> + *
> + * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_H
> +#define CAN_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/* controller area network (CAN) kernel definitions */
Is this file used only from within the kernel? If so you could use
the nicer-to-look-at u8/u16/u32 types instead of the double underscored
ones.
> +++ net-2.6.24/include/linux/can/core.h 2007-09-17 11:08:39.000000000 +0200
> @@ -0,0 +1,78 @@
> +
> +extern int can_proto_register(struct can_proto *cp);
> +extern int can_proto_unregister(struct can_proto *cp);
The callers of the unregister function don't check the return code,
and they can't handle errors anyways since they use it in the
module unload path, so making it void seems more appropriate
(and maybe a WARN_ON for the "not-found" case).
> +extern 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);
> +
> +extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
> + canid_t mask,
> + void (*func)(struct sk_buff *, void *),
> + void *data);
Same here, none of the callers check the return value and since
they're all declared as void they can't propagate any errors back.
> +++ net-2.6.24/net/can/af_can.c 2007-09-17 11:06:52.000000000 +0200
> @@ -0,0 +1,1002 @@
> +#include <linux/module.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 <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "af_can.h"
It seems most of the things declared in that file are only used within
af_can.c. Might be easier to read the code if you'd just move it over.
> +
> +#define IDENT "core"
> +static __initdata 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);
> +
> +int stats_timer = 1; /* default: on */
This seems to be only used in af_can.c, so it could be static.
__read_mostly also seems to be approriate. There are a few
more that look like they could be __read_mostly below, but
I'll skip these since you probably know better than me.
> +module_param(stats_timer, int, S_IRUGO);
> +MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +static int debug;
> +module_param(debug, int, S_IRUGO);
> +MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
> +#endif
> +
> +HLIST_HEAD(rx_dev_list);
Same here (static).
> +static struct dev_rcv_lists rx_alldev_list;
> +static DEFINE_SPINLOCK(rcv_lists_lock);
> +
> +static struct kmem_cache *rcv_cache __read_mostly;
> +
> +/* table of registered CAN protocols */
> +static struct can_proto *proto_tab[CAN_NPROTO];
> +
> +struct timer_list stattimer; /* timer for statistics update */
> +struct s_stats stats; /* packet statistics */
> +struct s_pstats pstats; /* receive list statistics */
> +
> +/*
> + * af_can socket functions
> + */
> +
> +static 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;
> + }
> +}
> +
> +static void can_sock_destruct(struct sock *sk)
> +{
> + DBG("called for sock %p\n", sk);
> +
> + skb_queue_purge(&sk->sk_receive_queue);
> + if (sk->sk_protinfo)
> + kfree(sk->sk_protinfo);
> +}
> +
> +static int can_create(struct net *net, struct socket *sock, int protocol)
> +{
> + struct sock *sk;
> + struct can_proto *cp;
> + char module_name[sizeof("can-proto-000")];
> + int ret = 0;
> +
> + DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
> +
> + sock->state = SS_UNCONNECTED;
> +
> + if (protocol < 0 || protocol >= CAN_NPROTO)
> + return -EINVAL;
> +
> + DBG("looking up proto %d in proto_tab[]\n", protocol);
> +
> + /* try to load protocol module, when CONFIG_KMOD is defined */
> + if (!proto_tab[protocol]) {
> + sprintf(module_name, "can-proto-%d", protocol);
> + ret = request_module(module_name);
> +
> + /*
> + * In case of error we only print a message but don't
> + * return the error code immediately. Below we will
> + * return -EPROTONOSUPPORT
> + */
> + if (ret == -ENOSYS)
> + printk(KERN_INFO "can: request_module(%s) not"
> + " implemented.\n", module_name);
> + else if (ret)
> + printk(KERN_ERR "can: request_module(%s) failed\n",
> + module_name);
Both of these printks seem to be user-triggerable, so they should
be rate-limited (or maybe get removed completely/changed to DBG).
> + }
> +
> + /* check for success and correct type */
> + cp = proto_tab[protocol];
What prevents the module from getting unloaded again (and using
a stale pointer)?
> + if (!cp || cp->type != sock->type)
> + return -EPROTONOSUPPORT;
> +
> + if (net != &init_net)
> + return -EAFNOSUPPORT;
Shouldn't this be done before attempting the module load?
> +
> + if (cp->capability >= 0 && !capable(cp->capability))
> + return -EPERM;
> +
> + sock->ops = cp->ops;
> +
> + sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
> + if (!sk)
> + return -ENOMEM;
> +
> + sock_init_data(sock, sk);
> + sk->sk_destruct = can_sock_destruct;
> +
> + DBG("created sock: %p\n", sk);
> +
> + if (sk->sk_prot->init)
> + ret = sk->sk_prot->init(sk);
> +
> + if (ret) {
> + /* release sk on errors */
> + sock_orphan(sk);
> + sock_put(sk);
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * 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!)
> + *
> + * 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()
> + */
> +int can_send(struct sk_buff *skb, int loop)
> +{
> + int err;
> +
> + if (skb->dev->type != ARPHRD_CAN) {
> + kfree_skb(skb);
> + return -EPERM;
EPERM doesn't seem like the best fit, but I don't have a better
suggestion myself at the moment.
> + }
> +
> + if (!(skb->dev->flags & IFF_UP)) {
> + kfree_skb(skb);
> + return -ENETDOWN;
> + }
> +
> + skb->protocol = htons(ETH_P_CAN);
> +
> + 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_LOOPBACK)) {
> + /*
> + * If the interface is not capable to do loopback
> + * itself, we do it here.
> + */
> + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> +
> + if (!newskb) {
> + kfree_skb(skb);
> + return -ENOMEM;
> + }
> +
> + newskb->sk = skb->sk;
> + newskb->ip_summed = CHECKSUM_UNNECESSARY;
> + newskb->pkt_type = PACKET_BROADCAST;
> + netif_rx(newskb);
So the intention here is to send the packet to the non-loopback device
and manually loop it, which means sending it twice?
> + }
> + } 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);
> +
> + /* update statistics */
> + stats.tx_frames++;
> + stats.tx_frames_delta++;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_send);
> +
> +/*
> + * af_can rx path
> + */
> +
> +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /*
> + * find receive list for this device
> + *
> + * The hlist_for_each_entry*() macros curse through the list
> + * using the pointer variable n and set d to the containing
> + * struct in each list iteration. Therefore, after list
> + * iteration, d is unmodified when the list is empty, and it
> + * points to last list element, when the list is non-empty
> + * but no match in the loop body is found. I.e. d is *not*
> + * NULL when no match is found. We can, however, use the
> + * cursor variable n to decide if a match was found.
> + */
> +
> + hlist_for_each_entry(d, n, &rx_dev_list, list) {
On the receive path you use RCU, so this should be
hlist_for_each_entry_rcu(), no? The bottem half disabling during
addition/removal also seems unnecessary, but I might be missing
something.
> + if (d->dev == dev)
> + break;
> + }
> +
> + return n ? d : NULL;
> +}
> +
> +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 error frames */
> + if (*mask & CAN_ERR_FLAG) {
> + /* clear CAN_ERR_FLAG in list entry */
> + *mask &= CAN_ERR_MASK;
> + return &d->rx[RX_ERR];
> + }
> +
> + /* ensure valid values in can_mask */
> + if (*mask & CAN_EFF_FLAG)
> + *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
> + else
> + *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
> +
> + /* 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];
> +
> + /* use extra filterset for the subscription of exactly *ONE* can_id */
> + if (*can_id & CAN_EFF_FLAG) {
> + if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
> + /* RFC: a use-case for hash-tables in the future? */
> + return &d->rx[RX_EFF];
> + }
> + } else {
> + if (*mask == CAN_SFF_MASK)
> + 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 indentification
> + *
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + *
> + * 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 ret = 0;
> +
> + /* insert new receiver (dev,canid,mask) -> (func,data) */
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
> + "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
> +
> + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
> + if (!r)
> + return -ENOMEM;
> +
> + spin_lock_bh(&rcv_lists_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++;
> +
> + pstats.rcv_entries++;
> + if (pstats.rcv_entries_max < pstats.rcv_entries)
> + pstats.rcv_entries_max = pstats.rcv_entries;
> + } else {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + kmem_cache_free(rcv_cache, r);
> + ret = -ENODEV;
> + }
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_register);
> +
> +/*
> + * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
> + */
> +static void can_rx_delete_device(struct rcu_head *rp)
> +{
> + struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
> +
> + DBG("removing dev_rcv_list at %p\n", d);
> + kfree(d);
> +}
> +
> +/*
> + * 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);
> +
> + DBG("removing receiver at %p\n", r);
> + 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.
> + *
> + * Return:
> + * 0 on success
> + * -EINVAL on missing subscription entry
> + * -ENODEV unknown device
> + */
> +int 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 hlist_node *next;
> + struct dev_rcv_lists *d;
> + int ret = 0;
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
> + dev, DNAME(dev), can_id, mask, func, data);
> +
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (!d) {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + ret = -ENODEV;
> + 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(r, next, rl, list) {
> + if (r->can_id == can_id && r->mask == mask
> + && r->func == func && r->data == data)
> + break;
> + }
> +
> + /*
> + * Check for bug in CAN protocol implementations:
> + * If no matching list item was found, the list cursor variable next
> + * will be NULL, while r will point to the last item of the list.
> + */
> +
> + if (!next) {
> + DBG("receive list entry not found for "
> + "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
> + ret = -EINVAL;
> + r = NULL;
> + d = NULL;
> + goto out;
> + }
> +
> + hlist_del_rcu(&r->list);
> + d->entries--;
> +
> + if (pstats.rcv_entries > 0)
> + pstats.rcv_entries--;
> +
> + /* remove device structure requested by NETDEV_UNREGISTER */
> + if (d->remove_on_zero_entries && !d->entries) {
> + DBG("removing dev_rcv_list for %s on zero entries\n",
> + dev->name);
> + hlist_del_rcu(&d->list);
> + } else
> + d = NULL;
> +
> + out:
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + /* schedule the receiver item for deletion */
> + if (r)
> + call_rcu(&r->rcu, can_rx_delete_receiver);
> +
> + /* schedule the device structure for deletion */
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(can_rx_unregister);
> +
> +static inline void deliver(struct sk_buff *skb, struct receiver *r)
> +{
> + struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
> +
> + DBG("skbuff %p cloned to %p\n", skb, clone);
> + if (clone) {
> + clone->sk = skb->sk;
> + r->func(clone, r->data);
> + r->matches++;
> + }
> +}
> +
> +static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> + 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 frame entries only */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
> + if (can_id & r->mask) {
> + DBG("match on rx_err skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + return matches;
> + }
> +
> + /* check for unfiltered entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
> + DBG("match on rx_all skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> +
> + /* check for can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
> + if ((can_id & r->mask) == r->can_id) {
> + DBG("match on rx_fil skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check for inverted can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
> + if ((can_id & r->mask) != r->can_id) {
> + DBG("match on rx_inv skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check CAN_ID specific entries */
> + if (can_id & CAN_EFF_FLAG) {
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
> + if (r->can_id == can_id) {
> + DBG("match on rx_eff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + } else {
> + can_id &= CAN_SFF_MASK;
> + hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
> + DBG("match on rx_sff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + return matches;
> +}
> +
> +static int can_rcv(struct sk_buff *skb, struct net_device *dev,
> + struct packet_type *pt, struct net_device *orig_dev)
> +{
> + struct dev_rcv_lists *d;
> + int matches;
> +
> + DBG("received skbuff on device %s, ptype %04x\n",
> + dev->name, ntohs(pt->type));
> + DBG_SKB(skb);
> + DBG_FRAME("af_can: can_rcv: received CAN frame",
> + (struct can_frame *)skb->data);
> +
> + if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
> + kfree_skb(skb);
> + return 0;
> + }
> +
> + /* update statistics */
> + stats.rx_frames++;
> + stats.rx_frames_delta++;
> +
> + rcu_read_lock();
> +
> + /* deliver the packet to sockets listening on all devices */
> + matches = can_rcv_filter(&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();
> +
> + /* free the skbuff allocated by the netdevice driver */
> + DBG("freeing skbuff %p\n", skb);
> + kfree_skb(skb);
> +
> + if (matches > 0) {
> + stats.matches++;
> + stats.matches_delta++;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * 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(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> + int err = 0;
> +
> + if (proto < 0 || proto >= CAN_NPROTO) {
> + printk(KERN_ERR "can: protocol number %d out of range\n",
> + proto);
> + return -EINVAL;
> + }
> + if (proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d already registered\n",
> + proto);
> + return -EBUSY;
> + }
> +
> + err = proto_register(cp->prot, 0);
> + if (err < 0)
> + return err;
> +
> + proto_tab[proto] = cp;
> +
> + /* use generic ioctl function if the module doesn't bring its own */
> + if (!cp->ops->ioctl)
> + cp->ops->ioctl = can_ioctl;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_proto_register);
> +
> +/**
> + * can_proto_unregister - unregister CAN transport protocol
> + * @cp: pointer to CAN protocol structure
> + *
> + * Return:
> + * 0 on success
> + * -ESRCH protocol number was not registered
> + */
> +int can_proto_unregister(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> +
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d is not registered\n", proto);
> + return -ESRCH;
> + }
> + proto_unregister(cp->prot);
> + proto_tab[proto] = NULL;
> +
> + return 0;
> +}
> +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 *data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct dev_rcv_lists *d;
> +
> + DBG("msg %ld for dev %p (%s idx %d)\n",
> + msg, dev, dev->name, dev->ifindex);
> +
> + if (dev->nd_net != &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
> + *
> + * N.B. zeroing the struct is the correct initialization
> + * for the embedded hlist_head structs.
> + * Another list type, e.g. list_head, would require
> + * explicit initialization.
> + */
> +
> + DBG("creating new dev_rcv_lists for %s\n", dev->name);
> +
> + d = kzalloc(sizeof(*d),
> + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
netdevice registration should never happen from interrupt handlers.
> + if (!d) {
> + printk(KERN_ERR
> + "can: allocation of receive list failed\n");
> + return NOTIFY_DONE;
> + }
> + d->dev = dev;
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&d->list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + break;
> +
> + case NETDEV_UNREGISTER:
> + spin_lock_bh(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (d) {
> + DBG("remove dev_rcv_list for %s (%d entries)\n",
> + dev->name, d->entries);
> +
> + if (d->entries) {
> + d->remove_on_zero_entries = 1;
> + d = NULL;
> + } else
> + hlist_del_rcu(&d->list);
> + } else
> + printk(KERN_ERR "can: notifier: receive list not "
> + "found for dev %s\n", dev->name);
> +
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +
> + break;
> + }
> +
> + return NOTIFY_DONE;
> +}
> +
> +/*
> + * af_can debugging stuff
> + */
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +
> +#define DBG_BSIZE 1024
> +
> +/**
> + * can_debug_cframe - print CAN frame
> + * @msg: pointer to message printed before the given CAN frame
> + * @cf: pointer to CAN frame
> + */
> +void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
> +{
> + va_list ap;
> + int len;
> + int dlc, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf, KERN_DEBUG);
> + va_start(ap, cf);
> + len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
> + buf[len++] = ':';
> + buf[len++] = ' ';
> + va_end(ap);
> +
> + dlc = cf->can_dlc;
> + if (dlc > 8)
> + dlc = 8;
> +
> + if (cf->can_id & CAN_EFF_FLAG)
> + len += sprintf(buf + len, "<%08X> [%X] ",
> + cf->can_id & CAN_EFF_MASK, dlc);
> + else
> + len += sprintf(buf + len, "<%03X> [%X] ",
> + cf->can_id & CAN_SFF_MASK, dlc);
> +
> + for (i = 0; i < dlc; i++)
> + len += sprintf(buf + len, "%02X ", cf->data[i]);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + len += sprintf(buf + len, "(RTR)");
> +
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_cframe);
> +
> +/**
> + * can_debug_skb - print socket buffer content to kernel log
> + * @skb: pointer to socket buffer
> + */
> +void can_debug_skb(struct sk_buff *skb)
> +{
> + int len, nbytes, i;
> + char *buf;
> +
> + buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
> + if (!buf)
> + return;
> +
> + len = sprintf(buf,
> + KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
> + KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
> + "h,d,t,e,l: %p %+d %+d %+d, %d",
> + skb, skb->dev ? skb->dev->ifindex : -1,
> + ntohs(skb->protocol),
> + atomic_read(&skb->users),
> + atomic_read(&(skb_shinfo(skb)->dataref)),
> + skb_shinfo(skb)->nr_frags,
> + skb->head, skb->data - skb->head,
> + skb->tail - skb->head, skb->end - skb->head, skb->len);
> + nbytes = skb->end - skb->head;
> + for (i = 0; i < nbytes; i++) {
> + if (i % 16 == 0)
> + len += sprintf(buf + len, "\n" KERN_DEBUG " ");
> + if (len < DBG_BSIZE - 16) {
> + len += sprintf(buf + len, " %02x", skb->head[i]);
> + } else {
> + len += sprintf(buf + len, "...");
> + break;
> + }
> + }
> + buf[len++] = '\n';
> + buf[len] = '\0';
> + printk(buf);
> + kfree(buf);
> +}
> +EXPORT_SYMBOL(can_debug_skb);
> +
> +#endif
> +
> +/*
> + * af_can module init/exit functions
> + */
> +
> +static struct packet_type can_packet = {
> + .type = __constant_htons(ETH_P_CAN),
> + .dev = NULL,
> + .func = can_rcv,
> +};
__read_mostly (for those below as well)?
> +
> +static 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 = {
> + .notifier_call = can_notifier,
> +};
> +
> +static __init int can_init(void)
> +{
> + printk(banner);
> +
> + rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
> + 0, 0, NULL);
> + if (!rcv_cache)
> + return -ENOMEM;
> +
> + /*
> + * Insert rx_alldev_list for reception on all devices.
> + * This struct is zero initialized which is correct for the
> + * embedded hlist heads, the dev pointer, and the entries counter.
> + */
> +
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + if (stats_timer) {
> + /* the statistics are updated every second (timer triggered) */
> + init_timer(&stattimer);
> + stattimer.function = can_stat_update;
> + stattimer.data = 0;
> + /* update every second */
> + stattimer.expires = jiffies + HZ;
round_jiffies?
> + /* start statistics timer */
> + add_timer(&stattimer);
> + } else
> + stattimer.function = NULL;
> +
> + /* procfs init */
> + can_init_proc();
> +
> + /* protocol register */
> + sock_register(&can_family_ops);
> + register_netdevice_notifier(&can_netdev_notifier);
> + dev_add_pack(&can_packet);
> +
> + return 0;
> +}
> +
> +static __exit void can_exit(void)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n, *next;
> +
> + if (stats_timer)
> + del_timer(&stattimer);
> +
> + /* procfs remove */
> + can_remove_proc();
> +
> + /* protocol unregister */
> + dev_remove_pack(&can_packet);
> + unregister_netdevice_notifier(&can_netdev_notifier);
> + sock_unregister(PF_CAN);
> +
> + /* remove rx_dev_list */
> + spin_lock_bh(&rcv_lists_lock);
> + hlist_del(&rx_alldev_list.list);
> + hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
> + hlist_del(&d->list);
> + kfree(d);
> + }
> + spin_unlock_bh(&rcv_lists_lock);
> +
> + kmem_cache_destroy(rcv_cache);
> +}
> +
> +module_init(can_init);
> +module_exit(can_exit);
> +++ net-2.6.24/net/can/proc.c 2007-09-17 11:07:19.000000000 +0200
> @@ -0,0 +1,531 @@
> +#include <linux/module.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/rcupdate.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[] = {
> + [RX_ERR] = "rx_err",
> + [RX_ALL] = "rx_all",
> + [RX_FIL] = "rx_fil",
> + [RX_INV] = "rx_inv",
> + [RX_EFF] = "rx_eff",
> +};
> +
> +/*
> + * af_can statistics stuff
> + */
> +
> +static void can_init_stats(void)
> +{
> + /*
> + * This memset function is called from a timer context (when
> + * stattimer is active which is the default) OR in a process
> + * context (reading the proc_fs when stattimer is disabled).
> + */
> + memset(&stats, 0, sizeof(stats));
> + stats.jiffies_init = jiffies;
> +
> + pstats.stats_reset++;
> +
> + if (user_reset) {
> + user_reset = 0;
> + pstats.user_reset++;
> + }
> +}
> +
> +static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
> + unsigned long count)
> +{
> + unsigned long ret = 0;
> +
> + if (oldjif == newjif)
> + return 0;
> +
> + /* see can_rcv() - this should NEVER happen! */
If I'm not mistaken this comment is outdated and should refer to
can_stat_update().
> + if (count > (ULONG_MAX / HZ)) {
> + printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
> + count);
> + return 99999999;
> + }
> +
> + ret = (count * HZ) / (newjif - oldjif);
> +
> + return ret;
> +}
> +
> +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 < stats.jiffies_init)
> + can_init_stats();
> +
> + /* stats.rx_frames is the definitively max. statistic value */
> +
> + /* prevent overflow in calc_rate() */
> + if (stats.rx_frames > (ULONG_MAX / HZ))
> + can_init_stats();
> +
> + /* matches overflow - very improbable */
> + if (stats.matches > (ULONG_MAX / 100))
> + can_init_stats();
> +
> + /* calc total values */
> + if (stats.rx_frames)
> + stats.total_rx_match_ratio = (stats.matches * 100) /
> + stats.rx_frames;
> +
> + stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
> + stats.tx_frames);
> + stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
> + stats.rx_frames);
> +
> + /* calc current values */
> + if (stats.rx_frames_delta)
> + stats.current_rx_match_ratio =
> + (stats.matches_delta * 100) / stats.rx_frames_delta;
> +
> + stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
> + stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
> +
> + /* check / update maximum values */
> + if (stats.max_tx_rate < stats.current_tx_rate)
> + stats.max_tx_rate = stats.current_tx_rate;
> +
> + if (stats.max_rx_rate < stats.current_rx_rate)
> + stats.max_rx_rate = stats.current_rx_rate;
> +
> + if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
> + stats.max_rx_match_ratio = stats.current_rx_match_ratio;
> +
> + /* clear values for 'current rate' calculation */
> + stats.tx_frames_delta = 0;
> + stats.rx_frames_delta = 0;
> + stats.matches_delta = 0;
> +
> + /* restart timer (one second) */
> + stattimer.expires = jiffies + HZ;
round_jiffies?
> + add_timer(&stattimer);
> +}
> +
> +/*
> + * proc read functions
> + *
> + * From known use-cases we expect about 10 entries in a receive list to be
> + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
Would be nicer to use seq_file (for all the proc stuff).
> + *
> + */
> +
> +static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
> + struct net_device *dev)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(r, n, rx_list, list) {
> + char *fmt = (r->can_id & CAN_EFF_FLAG)?
> + " %-5s %08X %08x %08x %08x %8ld %s\n" :
> + " %-5s %03X %08x %08lx %08lx %8ld %s\n";
> +
> + len += snprintf(page + len, PAGE_SIZE - len, fmt,
> + DNAME(dev), r->can_id, r->mask,
> + (unsigned long)r->func, (unsigned long)r->data,
> + r->matches, r->ident);
> +
> + /* does a typical line fit into the current buffer? */
> +
> + /* 100 Bytes before end of buffer */
> + if (len > PAGE_SIZE - 100) {
> + /* mark output cut off */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (..)\n");
> + break;
> + }
> + }
> + rcu_read_unlock();
> +
> + return len;
> +}
> +
> +static int can_print_recv_banner(char *page, int len)
> +{
> + /*
> + * can1. 00000000 00000000 00000000
> + * ....... 0 tp20
> + */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " device can_id can_mask function"
> + " userdata matches ident\n");
> +
> + return len;
> +}
> +
> +static int can_proc_read_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld transmitted frames (TXF)\n", stats.tx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld received frames (RXF)\n", stats.rx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld matched frames (RXMF)\n", stats.matches);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% total match ratio (RXMR)\n",
> + stats.total_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total tx rate (TXR)\n",
> + stats.total_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total rx rate (RXR)\n",
> + stats.total_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% current match ratio (CRXMR)\n",
> + stats.current_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current tx rate (CTXR)\n",
> + stats.current_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current rx rate (CRXR)\n",
> + stats.current_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% max match ratio (MRXMR)\n",
> + stats.max_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max tx rate (MTXR)\n",
> + stats.max_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max rx rate (MRXR)\n",
> + stats.max_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + }
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld current receive list entries (CRCV)\n",
> + pstats.rcv_entries);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld maximum receive list entries (MRCV)\n",
> + pstats.rcv_entries_max);
> +
> + if (pstats.stats_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\n %8ld statistic resets (STR)\n",
> + pstats.stats_reset);
> +
> + if (pstats.user_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld user statistic resets (USTR)\n",
> + pstats.user_reset);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_reset_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + user_reset = 1;
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Scheduled statistic reset #%ld.\n",
> + pstats.stats_reset + 1);
> +
> + } else {
> + if (stats.jiffies_init != jiffies)
> + can_init_stats();
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Performed statistic reset #%ld.\n",
> + pstats.stats_reset);
> + }
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_version(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
> + CAN_VERSION_STRING);
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + /* double cast to prevent GCC warning */
> + int idx = (int)(long)data;
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list '%s':\n", rx_list_name[idx]);
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx[idx])) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_SFF */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_sff':\n");
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> + int i, all_empty = 1;
> + /* check wether at least one list is non-empty */
> + for (i = 0; i < 0x800; i++)
> + if (!hlist_empty(&d->rx_sff[i])) {
> + all_empty = 0;
> + break;
> + }
> +
> + if (!all_empty) {
> + len = can_print_recv_banner(page, len);
> + for (i = 0; i < 0x800; i++) {
> + if (!hlist_empty(&d->rx_sff[i]) &&
> + len < PAGE_SIZE - 100)
> + len = can_print_rcvlist(page, len,
> + &d->rx_sff[i],
> + d->dev);
> + }
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +/*
> + * proc utility functions
> + */
> +
> +static struct proc_dir_entry *can_create_proc_readentry(const char *name,
> + mode_t mode,
> + read_proc_t *read_proc,
> + void *data)
> +{
> + if (can_dir)
> + return create_proc_read_entry(name, mode, can_dir, read_proc,
> + data);
> + else
> + return NULL;
> +}
> +
> +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;
> + }
> +
> + can_dir->owner = THIS_MODULE;
> +
> + /* own procfs entries from the AF_CAN core */
> + pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
> + can_proc_read_version, NULL);
> + pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
> + can_proc_read_stats, NULL);
> + pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
> + can_proc_read_reset_stats, NULL);
> + pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
> + can_proc_read_rcvlist, (void *)RX_ERR);
> + pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
> + can_proc_read_rcvlist, (void *)RX_ALL);
> + pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
> + can_proc_read_rcvlist, (void *)RX_FIL);
> + pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
> + can_proc_read_rcvlist, (void *)RX_INV);
> + pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
> + can_proc_read_rcvlist, (void *)RX_EFF);
> + pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
> + can_proc_read_rcvlist_sff, NULL);
> +}
> +
> +/*
> + * 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)
> + proc_net_remove(&init_net, "can");
> +}
> Index: net-2.6.24/include/linux/can/error.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can/error.h 2007-09-17 10:27:09.000000000 +0200
> @@ -0,0 +1,93 @@
> +/*
> + * linux/can/error.h
> + *
> + * Definitions of the CAN error frame to be filtered and passed to the user.
> + *
> + * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_ERROR_H
> +#define CAN_ERROR_H
> +
> +#define CAN_ERR_DLC 8 /* dlc for error frames */
> +
> +/* error class (mask) in can_id */
> +#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
> +#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
> +#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
> +#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
> +#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
> +#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
> +#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
> +#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
> +#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
> +
> +/* arbitration lost in bit ... / data[0] */
> +#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
> + /* else bit number in bitstream */
> +
> +/* error status of CAN-controller / data[1] */
> +#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
> +#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
> +#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
> +#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
> +#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
> +#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
> + /* (at least one error counter exceeds */
> + /* the protocol-defined level of 127) */
> +
> +/* error in CAN protocol (type) / data[2] */
> +#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
> +#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
> +#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
> +#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
> +#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
> +#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
> +#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
> +#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
> +
> +/* error in CAN protocol (location) / data[3] */
> +#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
> +#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
> +#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
> +#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
> +#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
> +#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
> +#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
> +#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
> +#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
> +#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
> +#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
> +#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
> +#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
> +#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
> +#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
> +#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
> +#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
> +#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
> +#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
> +#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
> +
> +/* error status of CAN-transceiver / data[4] */
> +/* CANH CANL */
> +#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
> +#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
> +#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
> +#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
> +#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
> +
> +/* controller specific additional information / data[5..7] */
> +
> +#endif /* CAN_ERROR_H */
>
> --
>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-18 13:31 ` Patrick McHardy
@ 2007-09-18 14:54 ` Urs Thuermann
2007-09-18 15:07 ` Patrick McHardy
2007-09-18 21:20 ` Urs Thuermann
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-18 14:54 UTC (permalink / raw)
To: Patrick McHardy
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Patrick McHardy <kaber@trash.net> writes:
> Looks pretty good, please see below for a few comments (mostly minor
> nitpicking, a few things that look like real bugs). Nothing that
> couldn't be fixed after merging though.
Thank you for your review. I'll go through it and your other mail
this evening/tonight. From a fast overview it looks like most issues
can be fixed/changed quite easily.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-18 14:54 ` Urs Thuermann
@ 2007-09-18 15:07 ` Patrick McHardy
0 siblings, 0 replies; 86+ messages in thread
From: Patrick McHardy @ 2007-09-18 15:07 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Urs Thuermann wrote:
> Patrick McHardy <kaber@trash.net> writes:
>
>
>>Looks pretty good, please see below for a few comments (mostly minor
>>nitpicking, a few things that look like real bugs). Nothing that
>>couldn't be fixed after merging though.
>
>
> Thank you for your review. I'll go through it and your other mail
> this evening/tonight. From a fast overview it looks like most issues
> can be fixed/changed quite easily.
Agreed. No objections to putting this in 2.6.24 from my side ..
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-18 13:31 ` Patrick McHardy
2007-09-18 14:54 ` Urs Thuermann
@ 2007-09-18 21:20 ` Urs Thuermann
2007-09-19 8:27 ` Patrick McHardy
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-18 21:20 UTC (permalink / raw)
To: Patrick McHardy
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Patrick McHardy <kaber@trash.net> writes:
> > +++ net-2.6.24/include/linux/can.h 2007-09-17 10:27:09.000000000 +0200
> Is this file used only from within the kernel? If so you could use
> the nicer-to-look-at u8/u16/u32 types instead of the double underscored
> ones.
No, this file contains the interface to user space.
> > +++ net-2.6.24/include/linux/can/core.h 2007-09-17 11:08:39.000000000 +0200
> > @@ -0,0 +1,78 @@
> > +
> > +extern int can_proto_register(struct can_proto *cp);
> > +extern int can_proto_unregister(struct can_proto *cp);
>
>
> The callers of the unregister function don't check the return code,
> and they can't handle errors anyways since they use it in the
> module unload path, so making it void seems more appropriate
> (and maybe a WARN_ON for the "not-found" case).
These functions have been declared returning void originally, but in
the review process with Thomas we changed them since they can fail.
Even if the current users, i.e. raw.c and bcm.c don't use the return
value, others might do. I therefore prefer to keep the int return
value. Rather we should check whether we can do something useful with
the return value in raw.c and bcm.c. At least we can put a WARN_ON
there.
> Same here, none of the callers check the return value and since
> they're all declared as void they can't propagate any errors back.
Same as above.
> > +#include "af_can.h"
>
>
> It seems most of the things declared in that file are only used within
> af_can.c. Might be easier to read the code if you'd just move it over.
af_can.h declares the interface between af_can.c and proc.c. There
should be nothing in af_can.h which is only used in af_can.c
> > +int stats_timer = 1; /* default: on */
>
>
> This seems to be only used in af_can.c, so it could be static.
> __read_mostly also seems to be approriate.
Right. Will be changed.
> There are a few more that look like they could be __read_mostly
> below, but I'll skip these since you probably know better than me.
Yes, I will check these also.
> > +HLIST_HEAD(rx_dev_list);
>
>
> Same here (static).
Can't be static since it's used in proc.c. But __read_mostly might
make sense.
What exactly is the effect of __read_mostly? Is that in a separate
ELF section? Where is that finally located?
> > + if (ret == -ENOSYS)
> > + printk(KERN_INFO "can: request_module(%s) not"
> > + " implemented.\n", module_name);
> > + else if (ret)
> > + printk(KERN_ERR "can: request_module(%s) failed\n",
> > + module_name);
>
>
> Both of these printks seem to be user-triggerable, so they should
> be rate-limited (or maybe get removed completely/changed to DBG).
Hm, I don't think DBG() would be right here, since the messages show
problems in the installation to the admin. OTOH, I see that a user
can flood the log by opening sockets with invalid proto numbers. Rate
limiting might solve this, or we should print the message only once
per proto number. I will think about this.
> > + /* check for success and correct type */
> > + cp = proto_tab[protocol];
>
>
> What prevents the module from getting unloaded again (and using
> a stale pointer)?
When the module is unloaded it calls can_proto_unregister() which
clears the pointer. Do you see a race condition here?
> > + if (!cp || cp->type != sock->type)
> > + return -EPROTONOSUPPORT;
> > +
> > + if (net != &init_net)
> > + return -EAFNOSUPPORT;
>
>
> Shouldn't this be done before attempting the module load?
Yes, you're right.
> > +int can_send(struct sk_buff *skb, int loop)
> > +{
> > + int err;
> > +
> > + if (skb->dev->type != ARPHRD_CAN) {
> > + kfree_skb(skb);
> > + return -EPERM;
>
>
> EPERM doesn't seem like the best fit, but I don't have a better
> suggestion myself at the moment.
We have chosen EPERM because a user shouldn't be allowed to open a raw
CAN socket and then send arbitrary frames to other non-CAN interfaces.
This is also because the raw and bcm protocols can be configured to
grant access to non-root users.
> > + if (!(skb->dev->flags & IFF_LOOPBACK)) {
> > + /*
> > + * If the interface is not capable to do loopback
> > + * itself, we do it here.
> > + */
> > + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> > +
> > + if (!newskb) {
> > + kfree_skb(skb);
> > + return -ENOMEM;
> > + }
> > +
> > + newskb->sk = skb->sk;
> > + newskb->ip_summed = CHECKSUM_UNNECESSARY;
> > + newskb->pkt_type = PACKET_BROADCAST;
> > + netif_rx(newskb);
>
>
> So the intention here is to send the packet to the non-loopback device
> and manually loop it, which means sending it twice?
CAN is a broadcast message network, so every frame should be (usually)
sent to all receivers, on remote hosts and to all local sockets. If
the driver for the interface is not able to loop back the frame for
local delivery, the can_send() function will do this as a fallback.
For real CAN devices it is preferred that the driver does loopback.
For vcan it makes no difference where loopback is done. The module
paramenter for vcan is therefore only useful to test and debug the CAN
core module. It is nothing a normal user will ever use.
> > +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
> > +{
> > + struct dev_rcv_lists *d;
> > + struct hlist_node *n;
> > +
> > + /*
> > + * find receive list for this device
> > + *
> > + * The hlist_for_each_entry*() macros curse through the list
> > + * using the pointer variable n and set d to the containing
> > + * struct in each list iteration. Therefore, after list
> > + * iteration, d is unmodified when the list is empty, and it
> > + * points to last list element, when the list is non-empty
> > + * but no match in the loop body is found. I.e. d is *not*
> > + * NULL when no match is found. We can, however, use the
> > + * cursor variable n to decide if a match was found.
> > + */
> > +
> > + hlist_for_each_entry(d, n, &rx_dev_list, list) {
>
>
> On the receive path you use RCU, so this should be
> hlist_for_each_entry_rcu(), no? The bottem half disabling during
> addition/removal also seems unnecessary, but I might be missing
> something.
find_dev_rcv_lists() is called in one place from can_rcv() with RCU
lock held, as you write. The other two calls to find_dev_rcv_lists()
are from can_rx_register/unregister() functions which change the
receive lists. Therefore, we can't only use RCU but need protection
against simultanous writes. We do this with the spin_lock_bh(). The
_bh variant, because can_rcv() runs in interrupt and we need to block
that. I thought this is pretty standard.
I'll check this again tomorrow, but I have put much time in these
locking issues already, changed it quite a few times and hoped to have
got it right finally.
...
I have just looked at the code again and I think we're right here.
Please reply if you think otherwise and we must fix it.
> > + case NETDEV_REGISTER:
> > +
> > + /*
> > + * create new dev_rcv_lists for this device
> > + *
> > + * N.B. zeroing the struct is the correct initialization
> > + * for the embedded hlist_head structs.
> > + * Another list type, e.g. list_head, would require
> > + * explicit initialization.
> > + */
> > +
> > + DBG("creating new dev_rcv_lists for %s\n", dev->name);
> > +
> > + d = kzalloc(sizeof(*d),
> > + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
>
>
> netdevice registration should never happen from interrupt handlers.
Hm, I seem to remember we had such an occurance with hot-pluggable
devices, i.e. USB. But I may be wrong. In what context is
NETDEV_REGISTER called for e.g. USB devices? Can we safely write
just GFP_KERNEL here?
> > +static struct packet_type can_packet = {
> > + .type = __constant_htons(ETH_P_CAN),
> > + .dev = NULL,
> > + .func = can_rcv,
> > +};
>
> __read_mostly (for those below as well)?
OK.
> > + stattimer.expires = jiffies + HZ;
>
> round_jiffies?
Yes. We don't depend on exact times relative to module load time, but
we would like to have the timer expirations 1 second apart. But we
would still get this with round_jiffies(), right?
> > +static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
> > + unsigned long count)
> > +{
> > + unsigned long ret = 0;
> > +
> > + if (oldjif == newjif)
> > + return 0;
> > +
> > + /* see can_rcv() - this should NEVER happen! */
>
>
> If I'm not mistaken this comment is outdated and should refer to
> can_stat_update().
Yep. Thanks.
> > + stattimer.expires = jiffies + HZ;
>
>
> round_jiffies?
Yes, like above.
> > + * proc read functions
> > + *
> > + * From known use-cases we expect about 10 entries in a receive list to be
> > + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
>
>
> Would be nicer to use seq_file (for all the proc stuff).
That has already been on my TODO list. There was some problem which I
don't remember ATM, which is why I dropped it temporarily. Now on my
TODO list again.
Thank you for your feedback.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-18 21:20 ` Urs Thuermann
@ 2007-09-19 8:27 ` Patrick McHardy
2007-09-20 8:53 ` Urs Thuermann
0 siblings, 1 reply; 86+ messages in thread
From: Patrick McHardy @ 2007-09-19 8:27 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Urs Thuermann wrote:
> Patrick McHardy <kaber@trash.net> writes:
>
>>>+HLIST_HEAD(rx_dev_list);
>>
>>
>>Same here (static).
>
>
> Can't be static since it's used in proc.c. But __read_mostly might
> make sense.
>
> What exactly is the effect of __read_mostly? Is that in a separate
> ELF section? Where is that finally located?
Its a seperate section to prevent false sharing.
>
>
>>>+ if (ret == -ENOSYS)
>>>+ printk(KERN_INFO "can: request_module(%s) not"
>>>+ " implemented.\n", module_name);
>>>+ else if (ret)
>>>+ printk(KERN_ERR "can: request_module(%s) failed\n",
>>>+ module_name);
>>
>>
>>Both of these printks seem to be user-triggerable, so they should
>>be rate-limited (or maybe get removed completely/changed to DBG).
>
>
> Hm, I don't think DBG() would be right here, since the messages show
> problems in the installation to the admin. OTOH, I see that a user
> can flood the log by opening sockets with invalid proto numbers. Rate
> limiting might solve this, or we should print the message only once
> per proto number. I will think about this.
IMO this is a perfectly normal condition (not finding a module).
Especially the !KMOD case is hardly an error.
>>>+ /* check for success and correct type */
>>>+ cp = proto_tab[protocol];
>>
>>
>>What prevents the module from getting unloaded again (and using
>>a stale pointer)?
>
>
> When the module is unloaded it calls can_proto_unregister() which
> clears the pointer. Do you see a race condition here?
Yes, you do request_module, load the module, get the cp pointer
from proto_tab, the module is unloaded again. cp points to
stable memory. Using module references would fix this.
>>>+ if (!(skb->dev->flags & IFF_LOOPBACK)) {
>>>+ /*
>>>+ * If the interface is not capable to do loopback
>>>+ * itself, we do it here.
>>>+ */
>>>+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
>>>+
>>>+ if (!newskb) {
>>>+ kfree_skb(skb);
>>>+ return -ENOMEM;
>>>+ }
>>>+
>>>+ newskb->sk = skb->sk;
>>>+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
>>>+ newskb->pkt_type = PACKET_BROADCAST;
>>>+ netif_rx(newskb);
>>
>>
>>So the intention here is to send the packet to the non-loopback device
>>and manually loop it, which means sending it twice?
>
>
> CAN is a broadcast message network, so every frame should be (usually)
> sent to all receivers, on remote hosts and to all local sockets. If
> the driver for the interface is not able to loop back the frame for
> local delivery, the can_send() function will do this as a fallback.
> For real CAN devices it is preferred that the driver does loopback.
> For vcan it makes no difference where loopback is done. The module
> paramenter for vcan is therefore only useful to test and debug the CAN
> core module. It is nothing a normal user will ever use.
Thanks for the explanation.
>>>+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
>>>+{
>>>+ struct dev_rcv_lists *d;
>>>+ struct hlist_node *n;
>>>+
>>>+ ....
>>>+
>>>+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
>>
>>
>>On the receive path you use RCU, so this should be
>>hlist_for_each_entry_rcu(), no? The bottem half disabling during
>>addition/removal also seems unnecessary, but I might be missing
>>something.
>
>
> find_dev_rcv_lists() is called in one place from can_rcv() with RCU
> lock held, as you write. The other two calls to find_dev_rcv_lists()
> are from can_rx_register/unregister() functions which change the
> receive lists. Therefore, we can't only use RCU but need protection
> against simultanous writes. We do this with the spin_lock_bh(). The
> _bh variant, because can_rcv() runs in interrupt and we need to block
> that. I thought this is pretty standard.
>
> I'll check this again tomorrow, but I have put much time in these
> locking issues already, changed it quite a few times and hoped to have
> got it right finally.
I'm not saying you should use *only* RCU, you need the lock
for additions/removal of course, but since the receive path
doesn't take that lock and relies on RCU, you need to use
the _rcu list walking variant to avoid races with concurrent
list changes.
>>>+ case NETDEV_REGISTER:
>>>+
>>>+ /*
>>>+ * create new dev_rcv_lists for this device
>>>+ *
>>>+ * N.B. zeroing the struct is the correct initialization
>>>+ * for the embedded hlist_head structs.
>>>+ * Another list type, e.g. list_head, would require
>>>+ * explicit initialization.
>>>+ */
>>>+
>>>+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
>>>+
>>>+ d = kzalloc(sizeof(*d),
>>>+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
>>
>>
>>netdevice registration should never happen from interrupt handlers.
>
>
> Hm, I seem to remember we had such an occurance with hot-pluggable
> devices, i.e. USB. But I may be wrong. In what context is
> NETDEV_REGISTER called for e.g. USB devices? Can we safely write
> just GFP_KERNEL here?
Yes. register_netdevice() takes the rtnl mutex, so it can only
be used in process context.
>>>+ stattimer.expires = jiffies + HZ;
>>
>>round_jiffies?
>
>
> Yes. We don't depend on exact times relative to module load time, but
> we would like to have the timer expirations 1 second apart. But we
> would still get this with round_jiffies(), right?
I don't think you will get *exactly* one second, but you also have
no guarantee for that now.
>>>+ * proc read functions
>>>+ *
>>>+ * From known use-cases we expect about 10 entries in a receive list to be
>>>+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
>>
>>
>>Would be nicer to use seq_file (for all the proc stuff).
>
>
> That has already been on my TODO list. There was some problem which I
> don't remember ATM, which is why I dropped it temporarily. Now on my
> TODO list again.
Thanks :)
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-19 8:27 ` Patrick McHardy
@ 2007-09-20 8:53 ` Urs Thuermann
2007-09-20 10:33 ` Patrick McHardy
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-20 8:53 UTC (permalink / raw)
To: Patrick McHardy
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Hi Patrick,
I have done allmost all changes to the code as you suggested. The
changes to use the return value of can_rx_register() also fixed a
minor flax with failing bind() and setsockopt() on raw sockets.
But there are two things left I would like to ask/understand:
Patrick McHardy <kaber@trash.net> writes:
> > When the module is unloaded it calls can_proto_unregister() which
> > clears the pointer. Do you see a race condition here?
>
> Yes, you do request_module, load the module, get the cp pointer
> from proto_tab, the module is unloaded again. cp points to
> stable memory. Using module references would fix this.
How would I use the module reference counter? Somehow with
try_module_get()? I have thought something like
cp = proto_tab[protocol];
if (!cp ...)
return ...;
if (!try_module_get(cp->prot->owner))
return ...;
sk = sk_alloc(...)
module_put(...);
return ret;
But here I see two problems:
1. Between the check !cp... and referencing cp->prot->owner the
module could get unloaded and the reference be invalid. Is there
some lock I can hold that prevents module unloading? I haven't
found something like this in include/linux/module.h
2. If the module gets unloaded after the first check and
request_module() but before the call to try_module_get() the
socket() syscall will return with error, although module auto
loading would normally be successful. How can I prevent that?
> > find_dev_rcv_lists() is called in one place from can_rcv() with RCU
> > lock held, as you write. The other two calls to find_dev_rcv_lists()
> > are from can_rx_register/unregister() functions which change the
> > receive lists. Therefore, we can't only use RCU but need protection
> > against simultanous writes. We do this with the spin_lock_bh(). The
> > _bh variant, because can_rcv() runs in interrupt and we need to block
> > that. I thought this is pretty standard.
> >
> > I'll check this again tomorrow, but I have put much time in these
> > locking issues already, changed it quite a few times and hoped to have
> > got it right finally.
>
>
> I'm not saying you should use *only* RCU, you need the lock
> for additions/removal of course, but since the receive path
> doesn't take that lock and relies on RCU, you need to use
> the _rcu list walking variant to avoid races with concurrent
> list changes.
I have no objections to add the _rcu suffix for the code changing the
receive lists, but I don't see why it's necessary. When I do a
spin_lock_bh() before writing, can't I be sure that there is no
interrupt routine running in parallel while I hold this spinlock? If
so, there is no reader in parallel because the can_rcv() function runs
in a softirq. I'd really like to understand why you think the writers
should also use the _rcu variant. I'm sorry if I miss something
obvious here, but could you try to explain it to me?
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 8:53 ` Urs Thuermann
@ 2007-09-20 10:33 ` Patrick McHardy
2007-09-20 11:30 ` Urs Thuermann
0 siblings, 1 reply; 86+ messages in thread
From: Patrick McHardy @ 2007-09-20 10:33 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Urs Thuermann wrote:
> Patrick McHardy <kaber@trash.net> writes:
>
>>>When the module is unloaded it calls can_proto_unregister() which
>>>clears the pointer. Do you see a race condition here?
>>
>>Yes, you do request_module, load the module, get the cp pointer
>>from proto_tab, the module is unloaded again. cp points to
>>stable memory. Using module references would fix this.
>
>
> How would I use the module reference counter? Somehow with
> try_module_get()? I have thought something like
>
> cp = proto_tab[protocol];
> if (!cp ...)
> return ...;
>
> if (!try_module_get(cp->prot->owner))
> return ...;
>
> sk = sk_alloc(...)
>
> module_put(...);
> return ret;
>
> But here I see two problems:
>
> 1. Between the check !cp... and referencing cp->prot->owner the
> module could get unloaded and the reference be invalid. Is there
> some lock I can hold that prevents module unloading? I haven't
> found something like this in include/linux/module.h
No, you need to add your own locking to prevent this, something
list this:
registration/unregistration:
take lock
change proto_tab[]
release lock
lookup:
take lock
cp = proto_tab[]
if (cp && !try_module_get(cp->owner))
cp = NULL
release lock
> 2. If the module gets unloaded after the first check and
> request_module() but before the call to try_module_get() the
> socket() syscall will return with error, although module auto
> loading would normally be successful. How can I prevent that?
Why do you want to prevent it? The admin unloaded the module,
so he apparently doesn't want the operation to succeed.
>>>find_dev_rcv_lists() is called in one place from can_rcv() with RCU
>>>lock held, as you write. The other two calls to find_dev_rcv_lists()
>>>are from can_rx_register/unregister() functions which change the
>>>receive lists. Therefore, we can't only use RCU but need protection
>>>against simultanous writes. We do this with the spin_lock_bh(). The
>>>_bh variant, because can_rcv() runs in interrupt and we need to block
>>>that. I thought this is pretty standard.
>>>
>>>I'll check this again tomorrow, but I have put much time in these
>>>locking issues already, changed it quite a few times and hoped to have
>>>got it right finally.
>>
>>
>>I'm not saying you should use *only* RCU, you need the lock
>>for additions/removal of course, but since the receive path
>>doesn't take that lock and relies on RCU, you need to use
>>the _rcu list walking variant to avoid races with concurrent
>>list changes.
>
>
> I have no objections to add the _rcu suffix for the code changing the
> receive lists, but I don't see why it's necessary. When I do a
> spin_lock_bh() before writing, can't I be sure that there is no
> interrupt routine running in parallel while I hold this spinlock? If
> so, there is no reader in parallel because the can_rcv() function runs
> in a softirq. I'd really like to understand why you think the writers
> should also use the _rcu variant.
I'm saying you need _rcu for the *read side*. All operations changing
the list already use the _rcu variants.
> I'm sorry if I miss something
> obvious here, but could you try to explain it to me?
spin_lock_bh only disables BHs locally, other CPUs can still process
softirqs. And since rcv_lists_lock is only used in process context,
the BH disabling is actually not even necessary.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 10:33 ` Patrick McHardy
@ 2007-09-20 11:30 ` Urs Thuermann
2007-09-20 11:43 ` Patrick McHardy
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-20 11:30 UTC (permalink / raw)
To: Patrick McHardy
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Patrick McHardy <kaber@trash.net> writes:
> No, you need to add your own locking to prevent this, something
> list this:
>
> registration/unregistration:
>
> take lock
> change proto_tab[]
> release lock
>
> lookup:
>
> take lock
> cp = proto_tab[]
> if (cp && !try_module_get(cp->owner))
> cp = NULL
> release lock
Ah, ok. Thanks for that hint. I will add it that way.
> > 2. If the module gets unloaded after the first check and
> > request_module() but before the call to try_module_get() the
> > socket() syscall will return with error, although module auto
> > loading would normally be successful. How can I prevent that?
>
>
> Why do you want to prevent it? The admin unloaded the module,
> so he apparently doesn't want the operation to succeed.
Well, unloading a module doesn't usually cause to operation to fail
when auto loading is enabled. It only wouldn't succeed when the
unload happens in the small window between test/request-module and
call to try_module_get(). This looks ugly to me. But the lock you
described above would also solve this.
> I'm saying you need _rcu for the *read side*. All operations changing
> the list already use the _rcu variants.
>
> > I'm sorry if I miss something
> > obvious here, but could you try to explain it to me?
>
>
> spin_lock_bh only disables BHs locally, other CPUs can still process
> softirqs. And since rcv_lists_lock is only used in process context,
> the BH disabling is actually not even necessary.
Well, I finally (hopefully) got it and I have changed the code
accordingly. Thanks for your explanation.
I will post our updated code again, probably today. The issues still
left are
* module parameter for loopback, but we want to keep that.
* configure option for allowing normal users access to raw and bcm CAN
sockets. I'll check how easily an (embedded) system can be set up
to run relevant/all processes with the CAP_NEW_RAW capability. I
would like to kill that configure option.
* seq_files for proc fs. On my TODO list.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 11:30 ` Urs Thuermann
@ 2007-09-20 11:43 ` Patrick McHardy
0 siblings, 0 replies; 86+ messages in thread
From: Patrick McHardy @ 2007-09-20 11:43 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Urs Thuermann wrote:
> I will post our updated code again, probably today. The issues still
> left are
>
> * module parameter for loopback, but we want to keep that.
No objections.
> * configure option for allowing normal users access to raw and bcm CAN
> sockets. I'll check how easily an (embedded) system can be set up
> to run relevant/all processes with the CAP_NEW_RAW capability. I
> would like to kill that configure option.
Great.
> * seq_files for proc fs. On my TODO list.
Sounds good, thanks.
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 18:43 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #7 Urs Thuermann
@ 2007-09-20 18:43 ` Urs Thuermann
2007-09-20 20:06 ` Joe Perches
2007-09-21 12:47 ` Patrick McHardy
0 siblings, 2 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-09-20 18:43 UTC (permalink / raw)
To: netdev
Cc: David Miller, Patrick McHardy, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 61076 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 78 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 1013 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 121 +++++
net/can/proc.c | 531 ++++++++++++++++++++++++
10 files changed, 1980 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070920"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "can-%s %s: ", \
+ IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-09-20 18:48:21.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-09-20 18:48:58.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-09-20 18:48:21.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-09-20 18:48:58.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,1013 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug __read_mostly;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (ret) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for success and correct type */
+ if (!cp || cp->type != sock->type) {
+ ret = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ ret = -EPERM;
+ goto errout;
+ }
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk) {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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_LOOPBACK)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+ va_list ap;
+ int len;
+ int dlc, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf, KERN_DEBUG);
+ va_start(ap, cf);
+ len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+ buf[len++] = ':';
+ buf[len++] = ' ';
+ va_end(ap);
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ len += sprintf(buf + len, "<%08X> [%X] ",
+ cf->can_id & CAN_EFF_MASK, dlc);
+ else
+ len += sprintf(buf + len, "<%03X> [%X] ",
+ cf->can_id & CAN_SFF_MASK, dlc);
+
+ for (i = 0; i < dlc; i++)
+ len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ len += sprintf(buf + len, "(RTR)");
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ int len, nbytes, i;
+ char *buf;
+
+ buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ len = sprintf(buf,
+ KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+ nbytes = skb->end - skb->head;
+ for (i = 0; i < nbytes; i++) {
+ if (i % 16 == 0)
+ len += sprintf(buf + len, "\n" KERN_DEBUG " ");
+ if (len < DBG_BSIZE - 16) {
+ len += sprintf(buf + len, " %02x", skb->head[i]);
+ } else {
+ len += sprintf(buf + len, "...");
+ break;
+ }
+ }
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ printk(buf);
+ kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,531 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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[] = {
+ [RX_ERR] = "rx_err",
+ [RX_ALL] = "rx_all",
+ [RX_FIL] = "rx_fil",
+ [RX_INV] = "rx_inv",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ 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;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* stats.rx_frames is the definitively max. statistic value */
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-09-20 18:48:58.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 18:43 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-09-20 20:06 ` Joe Perches
2007-09-20 20:27 ` Thomas Gleixner
` (2 more replies)
2007-09-21 12:47 ` Patrick McHardy
1 sibling, 3 replies; 86+ messages in thread
From: Joe Perches @ 2007-09-20 20:06 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
On Thu, 2007-09-20 at 20:43 +0200, Urs Thuermann wrote:
> +#define DBG(...) (debug & 1 ? \
> + (printk(KERN_DEBUG "can-%s %s: ", \
> + IDENT, __func__), printk(args)) : 0)
> +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> +#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
> +#else
> +#define DBG(args...)
> +#define DBG_FRAME(args...)
> +#define DBG_SKB(skb)
> +#endif
Shouldn't these be like the more common
#define DBG(fmt, args...) \
do { \
if (debug & 1) \
printk(KERN_DEBUG "can-%s %s: " fmt, IDENT, __func__, ##args); \
} while (0)
#define DBG_FRAME(cf, fmt, args...) \
do { \
if (debug & 2) \
can_debug_cframe(cf, fmt, ##args); \
} while (0)
#define DBG_SKB(skb) \
do { \
if (debug & 4) \
can_debug_skb(skb); \
while (0)
> void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
This prototype looks backwards to me.
msg is a printf format string.
I'd prefer something like this, which removes the unnecessary
kmalloc/kfree pairs or the equivalent conversions to functions.
#define can_debug_cframe(cf, fmt, arg...) \
do { \
char buf[20]; \
int dlc = cf->can_dlc; \
if (dlc > 8) \
dlc = 8; \
if (cf->can_id & CAN_EFF_FLAG) \
sprintf(buf, "<%08X> [%X] ", cf->can_id & CAN_EFF_MASK, dlc); \
else \
sprintf(buf, "<%03X> [%X] ", cf->can_id & CAN_SFF_MASK, dlc); \
printk(KERN_DEBUG fmt, ##arg); \
print_hex_dump(buf, DUMP_PREFIX_NONE, cf->data, dlc); \
} while (0)
and
#define can_debug_skb(skb) \
do { \
pr_debug("skbuff at %p, dev: %d, proto: %04x\n", \
skb, skb->dev ? skb->dev->ifindex : -1, \
ntohs(skb->protocol)); \
pr_debug("users: %d, dataref: %d, nr_frags: %d, " \
"h,d,t,e,l: %p %+d %+d %+d, %d\n", \
atomic_read(&skb->users), \
atomic_read(&(skb_shinfo(skb)->dataref)), \
skb_shinfo(skb)->nr_frags, \
skb->head, skb->data - skb->head, \
skb->tail - skb->head, skb->end - skb->head, skb->len); \
print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE, \
16, 1, skb->head, skb->end - skb->head); \
} while (0)
cheers, Joe
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 20:06 ` Joe Perches
@ 2007-09-20 20:27 ` Thomas Gleixner
2007-09-21 10:35 ` Urs Thuermann
2007-09-24 19:23 ` Urs Thuermann
2 siblings, 0 replies; 86+ messages in thread
From: Thomas Gleixner @ 2007-09-20 20:27 UTC (permalink / raw)
To: Joe Perches
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
On Thu, 2007-09-20 at 13:06 -0700, Joe Perches wrote:
> On Thu, 2007-09-20 at 20:43 +0200, Urs Thuermann wrote:
> > +#define DBG(...) (debug & 1 ? \
> > + (printk(KERN_DEBUG "can-%s %s: ", \
> > + IDENT, __func__), printk(args)) : 0)
> > +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> > +#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
> > +#else
> > +#define DBG(args...)
> > +#define DBG_FRAME(args...)
> > +#define DBG_SKB(skb)
> > +#endif
>
> Shouldn't these be like the more common
>
> #define DBG(fmt, args...) \
> I'd prefer something like this, which removes the unnecessary
> kmalloc/kfree pairs or the equivalent conversions to functions.
>
> #define can_debug_cframe(cf, fmt, arg...) \
That's even worse. If we want to have something better than the DBG
macros, then please convert the whole stuff into inline functions.
#ifdef DEBUG
static inline void debug_skb(struct sk_buff *skb)
{
if (debug & 4)
...
}
#else
static inline debug_skb(struct sk_buff *skb) {}
#endif
Thanks,
tglx
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 20:06 ` Joe Perches
2007-09-20 20:27 ` Thomas Gleixner
@ 2007-09-21 10:35 ` Urs Thuermann
2007-09-21 16:58 ` Joe Perches
2007-09-24 19:23 ` Urs Thuermann
2 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-21 10:35 UTC (permalink / raw)
To: Joe Perches
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Joe Perches <joe@perches.com> writes:
> On Thu, 2007-09-20 at 20:43 +0200, Urs Thuermann wrote:
> > +#define DBG(...) (debug & 1 ? \
> > + (printk(KERN_DEBUG "can-%s %s: ", \
> > + IDENT, __func__), printk(args)) : 0)
> > +#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
> > +#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
> > +#else
> > +#define DBG(args...)
> > +#define DBG_FRAME(args...)
> > +#define DBG_SKB(skb)
> > +#endif
>
> Shouldn't these be like the more common
>
> #define DBG(fmt, args...) \
When I wrote these macros we had one platform where we still used
linux-2.4 and gcc-2.95.3 and with that old gcc your macro def would
mean you can't use DBG with only one argument, like DBG("some string\n");
since gcc-2.95 would leave the , after __func__ followed by the
closing ")". I didn't find a way with gcc-2.95 to make the format
string a separate macro argument (which I also wanted). OK, that's
history now and linux-2.6 needs gcc-3/4 anyway, so I will change this.
> do { \
> if (debug & 1) \
> printk(KERN_DEBUG "can-%s %s: " fmt, IDENT, __func__, ##args); \
> } while (0)
I use "do { ... } while(0)" only for statements, not for expressions.
But I could have used the && instead of ?: operator. I don't think
the "do { ... } while(0)" looks nicer or has any other advantage.
> > void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
>
> This prototype looks backwards to me.
You mean the order or `msg' and `cf'? You want to switch them so that
the variable args follow immediately the format string? Might make
sense, OTOH we wanted to have the message as the first argument.
> I'd prefer something like this, which removes the unnecessary
> kmalloc/kfree pairs or the equivalent conversions to functions.
>
> #define can_debug_cframe(cf, fmt, arg...) \
> do { \
> char buf[20]; \
> int dlc = cf->can_dlc; \
> if (dlc > 8) \
> dlc = 8; \
> if (cf->can_id & CAN_EFF_FLAG) \
> sprintf(buf, "<%08X> [%X] ", cf->can_id & CAN_EFF_MASK, dlc); \
> else \
> sprintf(buf, "<%03X> [%X] ", cf->can_id & CAN_SFF_MASK, dlc); \
> printk(KERN_DEBUG fmt, ##arg); \
> print_hex_dump(buf, DUMP_PREFIX_NONE, cf->data, dlc); \
> } while (0)
Ah, I didn't know print_hex_dump(). That looks nicer. But as Thomas
mentioned, we shouldn't convert these functions into macros.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 18:43 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-20 20:06 ` Joe Perches
@ 2007-09-21 12:47 ` Patrick McHardy
2007-09-21 18:01 ` Urs Thuermann
1 sibling, 1 reply; 86+ messages in thread
From: Patrick McHardy @ 2007-09-21 12:47 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Urs Thuermann wrote:
> +static int can_create(struct net *net, struct socket *sock, int protocol)
> +{
> + ...
> +
> + spin_lock(&proto_tab_lock);
> + cp = proto_tab[protocol];
> + if (cp && !try_module_get(cp->prot->owner))
> + cp = NULL;
> + spin_unlock(&proto_tab_lock);
> +
> + /* check for success and correct type */
> + if (!cp || cp->type != sock->type) {
> + ret = -EPROTONOSUPPORT;
> + goto errout;
> + }
> +
> + if (cp->capability >= 0 && !capable(cp->capability)) {
> + ret = -EPERM;
> + goto errout;
> + }
> +
> + sock->ops = cp->ops;
You drop the module reference again when leaving this function.
So sock->ops might contain a stale pointer if the module is
unloaded after this. You need to either keep the module reference
while the socket is alive or remove stale references when
unregistering the protocol.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-21 10:35 ` Urs Thuermann
@ 2007-09-21 16:58 ` Joe Perches
0 siblings, 0 replies; 86+ messages in thread
From: Joe Perches @ 2007-09-21 16:58 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
On Fri, 2007-09-21 at 12:35 +0200, Urs Thuermann wrote:
> I didn't find a way with gcc-2.95 to make the format
> string a separate macro argument (which I also wanted).
The old 2.x GCC workaround was to use
#define DBG(fmt, arg) printk(fmt , ## arg)
adding a space before the last comma.
> I use "do { ... } while(0)" only for statements, not for expressions.
> But I could have used the && instead of ?: operator. I don't think
> the "do { ... } while(0)" looks nicer or has any other advantage.
It's more linux convention.
It allows the macro to be used in if-else constructs.
> > > void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
> > This prototype looks backwards to me.
> You mean the order or `msg' and `cf'?
Yes, I believe the can_frame* should be the first argument.
> Ah, I didn't know print_hex_dump(). That looks nicer. But as Thomas
> mentioned, we shouldn't convert these functions into macros.
The first print_hex_dump should actually be print_hex_dump_bytes.
I was typing what I hoped was a readable example.
I hope you do convert to functions, but not to inline functions.
cheers, Joe
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-21 12:47 ` Patrick McHardy
@ 2007-09-21 18:01 ` Urs Thuermann
2007-09-22 10:53 ` Patrick McHardy
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-21 18:01 UTC (permalink / raw)
To: Patrick McHardy
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Patrick McHardy <kaber@trash.net> writes:
> You drop the module reference again when leaving this function.
> So sock->ops might contain a stale pointer if the module is
> unloaded after this. You need to either keep the module reference
> while the socket is alive or remove stale references when
> unregistering the protocol.
I don't think that can happen. Before we drop the module reference we
call sk_alloc() which gets another module reference via its cp->prot
argument. If sk_alloc() fails we return with error from can_create()
I assume sock->ops won't be used after that.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-21 18:01 ` Urs Thuermann
@ 2007-09-22 10:53 ` Patrick McHardy
0 siblings, 0 replies; 86+ messages in thread
From: Patrick McHardy @ 2007-09-22 10:53 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Thomas Gleixner, Oliver Hartkopp,
Oliver Hartkopp
Urs Thuermann wrote:
> Patrick McHardy <kaber@trash.net> writes:
>
>
>>You drop the module reference again when leaving this function.
>>So sock->ops might contain a stale pointer if the module is
>>unloaded after this. You need to either keep the module reference
>>while the socket is alive or remove stale references when
>>unregistering the protocol.
>
>
> I don't think that can happen. Before we drop the module reference we
> call sk_alloc() which gets another module reference via its cp->prot
> argument. If sk_alloc() fails we return with error from can_create()
> I assume sock->ops won't be used after that.
You're right, that should be enough.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-20 20:06 ` Joe Perches
2007-09-20 20:27 ` Thomas Gleixner
2007-09-21 10:35 ` Urs Thuermann
@ 2007-09-24 19:23 ` Urs Thuermann
2 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-09-24 19:23 UTC (permalink / raw)
To: Joe Perches
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Joe Perches <joe@perches.com> writes:
> I'd prefer something like this, which removes the unnecessary
> kmalloc/kfree pairs or the equivalent conversions to functions.
I have changed this to a static buffer. Since this is only in
#ifdef CONFIG_CAN_DEBUG_CORE, it shouldn't hurt.
> #define can_debug_cframe(cf, fmt, arg...) \
> do { \
> char buf[20]; \
> int dlc = cf->can_dlc; \
> if (dlc > 8) \
> dlc = 8; \
> if (cf->can_id & CAN_EFF_FLAG) \
> sprintf(buf, "<%08X> [%X] ", cf->can_id & CAN_EFF_MASK, dlc); \
> else \
> sprintf(buf, "<%03X> [%X] ", cf->can_id & CAN_SFF_MASK, dlc); \
> printk(KERN_DEBUG fmt, ##arg); \
> print_hex_dump(buf, DUMP_PREFIX_NONE, cf->data, dlc); \
> } while (0)
The line isn't printed atomically, i.e. it could happen that something
is printed between the fmt+args and the hexdump, i.e inbetween the
line.
> and
>
> #define can_debug_skb(skb) \
> do { \
> pr_debug("skbuff at %p, dev: %d, proto: %04x\n", \
> skb, skb->dev ? skb->dev->ifindex : -1, \
> ntohs(skb->protocol)); \
> pr_debug("users: %d, dataref: %d, nr_frags: %d, " \
> "h,d,t,e,l: %p %+d %+d %+d, %d\n", \
> atomic_read(&skb->users), \
> atomic_read(&(skb_shinfo(skb)->dataref)), \
> skb_shinfo(skb)->nr_frags, \
> skb->head, skb->data - skb->head, \
> skb->tail - skb->head, skb->end - skb->head, skb->len); \
> print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE, \
> 16, 1, skb->head, skb->end - skb->head); \
> } while (0)
Here, the consecutive lines aren't printed atomically.
I think sprintf() to a buffer first and the do one printk() is
better. But I will use hex_dump_to_buffer().
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 12:20 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #8 Urs Thuermann
@ 2007-09-25 12:20 ` Urs Thuermann
2007-09-25 12:41 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-25 12:20 UTC (permalink / raw)
To: netdev
Cc: David Miller, Patrick McHardy, Joe Perches, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60373 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 77 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 973 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 121 +++++
net/can/proc.c | 533 +++++++++++++++++++++++++
10 files changed, 1941 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-09-25 13:14:46.000000000 +0200
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-09-25 13:22:22.000000000 +0200
@@ -0,0 +1,77 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070920"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
+#define DBG(fmt, args...) (debug & 1 ? printk(KERN_DEBUG "can-" IDENT \
+ " %s: " fmt, __func__, ##args) : 0)
+#define DBG_FRAME(fmt, cf) (debug & 2 ? can_debug_cframe(fmt, cf) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(fmt, args...)
+#define DBG_FRAME(fmt, cf)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-09-24 17:53:08.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-09-25 13:14:46.000000000 +0200
@@ -210,6 +210,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-09-24 17:53:08.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-09-25 13:14:46.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-09-25 13:14:46.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-09-25 13:14:46.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-09-25 13:22:22.000000000 +0200
@@ -0,0 +1,973 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug __read_mostly;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ if (sk->sk_protinfo)
+ kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (ret) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for success and correct type */
+ if (!cp || cp->type != sock->type) {
+ ret = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ ret = -EPERM;
+ goto errout;
+ }
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk) {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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_LOOPBACK)) {
+ /*
+ * If the interface is not capable to do loopback
+ * itself, we do it here.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf)
+{
+ char idbuf[12];
+ char hexbuf[28];
+ int dlc;
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
+ else
+ sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ sprintf(hexbuf, "(RTR)");
+ else
+ hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
+
+ printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d\n",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+
+ print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->head, skb->end - skb->head, 0);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-09-25 13:14:46.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-09-25 13:22:22.000000000 +0200
@@ -0,0 +1,533 @@
+/*
+ * 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, the following disclaimer and
+ * the referenced file 'COPYING'.
+ * 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 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ 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;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.tx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-09-25 13:14:46.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 12:20 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-09-25 12:41 ` Arnaldo Carvalho de Melo
2007-09-25 13:24 ` Urs Thuermann
0 siblings, 1 reply; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-09-25 12:41 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Joe Perches,
Thomas Gleixner, Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Em Tue, Sep 25, 2007 at 02:20:31PM +0200, Urs Thuermann escreveu:
> +
> +static void can_sock_destruct(struct sock *sk)
> +{
> + DBG("called for sock %p\n", sk);
> +
> + skb_queue_purge(&sk->sk_receive_queue);
> + if (sk->sk_protinfo)
> + kfree(sk->sk_protinfo);
> +}
Is it really needed to do this sk_protinfo check? You don't use it and
it is going to be removed (only user is one of the HAM radio protocols),
so it would be better to not add any more references to this struct sock
field.
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 12:41 ` Arnaldo Carvalho de Melo
@ 2007-09-25 13:24 ` Urs Thuermann
2007-09-25 15:33 ` Stephen Hemminger
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-09-25 13:24 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: netdev, David Miller, Patrick McHardy, Joe Perches,
Thomas Gleixner, Oliver Hartkopp, Oliver Hartkopp
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
> > + skb_queue_purge(&sk->sk_receive_queue);
> > + if (sk->sk_protinfo)
> > + kfree(sk->sk_protinfo);
> > +}
>
> Is it really needed to do this sk_protinfo check?
Thanks for finding this. This is from 2.6.12 times or so. We have
other CAN protocol (which we are not allowed to put under GPL)
implemenatations which still use the protinfo field. But we should
change those and I will delete this check.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 13:24 ` Urs Thuermann
@ 2007-09-25 15:33 ` Stephen Hemminger
2007-09-25 21:00 ` Urs Thuermann
0 siblings, 1 reply; 86+ messages in thread
From: Stephen Hemminger @ 2007-09-25 15:33 UTC (permalink / raw)
To: Urs Thuermann
Cc: Arnaldo Carvalho de Melo, netdev, David Miller, Patrick McHardy,
Joe Perches, Thomas Gleixner, Oliver Hartkopp, Oliver Hartkopp
On 25 Sep 2007 15:24:33 +0200
Urs Thuermann <urs@isnogud.escape.de> wrote:
> Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
>
> > > + skb_queue_purge(&sk->sk_receive_queue);
> > > + if (sk->sk_protinfo)
> > > + kfree(sk->sk_protinfo);
> > > +}
> >
> > Is it really needed to do this sk_protinfo check?
>
> Thanks for finding this. This is from 2.6.12 times or so. We have
> other CAN protocol (which we are not allowed to put under GPL)
> implemenatations which still use the protinfo field. But we should
> change those and I will delete this check.
>
> urs
Then please make all exported symbols marked EXPORT_SYMBOL_GPL to make
sure that the other CAN protocol can not reuse your infrastructure.
--
Stephen Hemminger <shemminger@linux-foundation.org>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 15:33 ` Stephen Hemminger
@ 2007-09-25 21:00 ` Urs Thuermann
2007-09-25 21:07 ` Stephen Hemminger
2007-09-25 21:09 ` David Miller
0 siblings, 2 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-09-25 21:00 UTC (permalink / raw)
To: Stephen Hemminger
Cc: netdev, David Miller, Patrick McHardy, Joe Perches,
Thomas Gleixner, Oliver Hartkopp, Oliver Hartkopp
Stephen Hemminger <shemminger@linux-foundation.org> writes:
> Then please make all exported symbols marked EXPORT_SYMBOL_GPL to make
> sure that the other CAN protocol can not reuse your infrastructure.
We don't want to force other CAN protocol implementations to be GPL
also. AFAIR from discussions on LKML, it was mostly agreed upon that
this decision is up to the authors of code.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 21:00 ` Urs Thuermann
@ 2007-09-25 21:07 ` Stephen Hemminger
2007-09-25 21:09 ` David Miller
1 sibling, 0 replies; 86+ messages in thread
From: Stephen Hemminger @ 2007-09-25 21:07 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Joe Perches,
Thomas Gleixner, Oliver Hartkopp, Oliver Hartkopp
On 25 Sep 2007 23:00:15 +0200
Urs Thuermann <urs@isnogud.escape.de> wrote:
> Stephen Hemminger <shemminger@linux-foundation.org> writes:
>
> > Then please make all exported symbols marked EXPORT_SYMBOL_GPL to
> > make sure that the other CAN protocol can not reuse your
> > infrastructure.
>
> We don't want to force other CAN protocol implementations to be GPL
> also. AFAIR from discussions on LKML, it was mostly agreed upon that
> this decision is up to the authors of code.
>
> urs
I just don't want proprietary drivers extensions in linux kernel.
EXPORT_SYMBOL has no meaning in other OS environments.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 21:00 ` Urs Thuermann
2007-09-25 21:07 ` Stephen Hemminger
@ 2007-09-25 21:09 ` David Miller
2007-09-28 16:27 ` Thomas Gleixner
1 sibling, 1 reply; 86+ messages in thread
From: David Miller @ 2007-09-25 21:09 UTC (permalink / raw)
To: urs; +Cc: shemminger, netdev, kaber, joe, tglx, oliver, oliver.hartkopp
From: Urs Thuermann <urs@isnogud.escape.de>
Date: 25 Sep 2007 23:00:15 +0200
> Stephen Hemminger <shemminger@linux-foundation.org> writes:
>
> > Then please make all exported symbols marked EXPORT_SYMBOL_GPL to make
> > sure that the other CAN protocol can not reuse your infrastructure.
>
> We don't want to force other CAN protocol implementations to be GPL
> also. AFAIR from discussions on LKML, it was mostly agreed upon that
> this decision is up to the authors of code.
To a certain extent, yes.
However, the core issue is whether anyone who uses the symbol
is creating a derivative work. If it is pretty clear that this
is the case, you really should mark the exported symbols GPL.
In my opinion, in this case it is pretty clear that any use of
these new symbols would be a derivative work and therefore they
all should be marked GPL.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-25 21:09 ` David Miller
@ 2007-09-28 16:27 ` Thomas Gleixner
2007-09-28 20:20 ` David Miller
0 siblings, 1 reply; 86+ messages in thread
From: Thomas Gleixner @ 2007-09-28 16:27 UTC (permalink / raw)
To: David Miller; +Cc: urs, shemminger, netdev, kaber, joe, oliver, oliver.hartkopp
On Tue, 2007-09-25 at 14:09 -0700, David Miller wrote:
> > > Then please make all exported symbols marked EXPORT_SYMBOL_GPL to make
> > > sure that the other CAN protocol can not reuse your infrastructure.
> >
> > We don't want to force other CAN protocol implementations to be GPL
> > also. AFAIR from discussions on LKML, it was mostly agreed upon that
> > this decision is up to the authors of code.
>
> To a certain extent, yes.
>
> However, the core issue is whether anyone who uses the symbol
> is creating a derivative work. If it is pretty clear that this
> is the case, you really should mark the exported symbols GPL.
>
> In my opinion, in this case it is pretty clear that any use of
> these new symbols would be a derivative work and therefore they
> all should be marked GPL.
Hmm, the code in question is dual licensed. So it's not that clear to
me.
But it's a legal grey area anyway if somebody loads non GPL code into
the kernel, though IANAL and we can spend years on this discussion.
I'm not inclined either way and we really should not make this a
religious question whether that code goes in or not, especially not when
we granted the mac80211 to export everything w/o _GPL suffix not too
long ago.
Thanks,
tglx
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-28 16:27 ` Thomas Gleixner
@ 2007-09-28 20:20 ` David Miller
2007-09-28 20:28 ` Thomas Gleixner
0 siblings, 1 reply; 86+ messages in thread
From: David Miller @ 2007-09-28 20:20 UTC (permalink / raw)
To: tglx; +Cc: urs, shemminger, netdev, kaber, joe, oliver, oliver.hartkopp
From: Thomas Gleixner <tglx@linutronix.de>
Date: Fri, 28 Sep 2007 18:27:19 +0200
> I'm not inclined either way and we really should not make this a
> religious question whether that code goes in or not, especially not when
> we granted the mac80211 to export everything w/o _GPL suffix not too
> long ago.
This is because a wireless driver is a driver. It can exist outside
of the kernel and it's mac80211 stack just like any other network
device driver. ANd the interfaces in mac80211 are such that the
driver doesn't explicitly go into the internals of the implementation.
That's not true with CAN.
With this CAN stuff, any driver you write for it is intimately
integrated into the design and architecture of the CAN subsystem. Any
such driver cannot stand on it's own. Look at how these drivers can
get into the internals.
If this code goes in without the _GPL() exports, that's fine, but it's
setting incorrect expectations for people who think they can write
binary-only drivers and link to these symbols.
And it will be the CAN folks who are guilty of setting these
false premises. Especially after I've explicitly warned about
it here.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-09-28 20:20 ` David Miller
@ 2007-09-28 20:28 ` Thomas Gleixner
0 siblings, 0 replies; 86+ messages in thread
From: Thomas Gleixner @ 2007-09-28 20:28 UTC (permalink / raw)
To: David Miller; +Cc: urs, shemminger, netdev, kaber, joe, oliver, oliver.hartkopp
On Fri, 2007-09-28 at 13:20 -0700, David Miller wrote:
> That's not true with CAN.
>
> With this CAN stuff, any driver you write for it is intimately
> integrated into the design and architecture of the CAN subsystem. Any
> such driver cannot stand on it's own. Look at how these drivers can
> get into the internals.
I'm just concerned about protocols, which have been designed and
implemented long ago outside of the kernel and are going to be wrapped
with glue code to fit into the socket can implementation. That's hard to
judge.
> If this code goes in without the _GPL() exports, that's fine, but it's
> setting incorrect expectations for people who think they can write
> binary-only drivers and link to these symbols.
>
> And it will be the CAN folks who are guilty of setting these
> false premises. Especially after I've explicitly warned about
> it here.
Fair enough.
tglx
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9
@ 2007-10-02 13:10 Urs Thuermann
2007-10-02 13:10 ` [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
` (6 more replies)
0 siblings, 7 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp
Hello Dave, hello Patrick,
this is the nineth post of the patch series that adds the PF_CAN
protocol family for the Controller Area Network.
Since our last post we have changed the following:
* Changes suggested by Arnaldo Carvalho de Melo:
- Use gfp_any() instead of checking in_interrupt().
- Don't kfree() the sk_protinfo field.
* Fixed licence text as pointed out by Yoshifuji Hideaki and Patrick McHardy.
* Added IFF_ECHO to include/linux/if.h and use that instead of IFF_LOOPBACK,
as suggested by Eric W. Biederman. IFF_LOOPBACK is only for the normal
loopback interface.
The changes in try #8 were:
* Some changes in debug code, following suggestions from Joe Perches:
- Remove dynamically allocated buffer for debug output.
- Use kernel functions for hexdumps.
- Don't interpret printf-style %-sequences in can_debug_cframe().
- Specify the fixed argument "fmt" to DBG() macro and use
GCC ## mechanism to remove , when args is empty.
* Removed CAN_RAW_USER and CAN_BCM_USER Kconfig options following a
suggestion from Patrick.
* Prevent overflow in statistics calculation.
* Minor optimization.
The changes in try #7 were:
* Changes suggested by Patrick:
- protect proto_tab[] by a lock.
- add _rcu to some hlist traversals.
- use printk_ratelimit() for module autoload failures.
- make can_proto_unregister() and can_rx_unregister() return void.
- use return value of can_proto_register() and can_rx_register()
(this also removed a flaw in behavior of raw_bind() and raw_setsockopt()
in case of failure to can_rx_register() their filters).
- call kzalloc() with GFP_KERNEL in case NETDEV_REGISTER.
- use round_jiffies() to calculate expiration times.
- make some variables static and/or __read_mostly.
- in can_create() check for net namespace before auto loading modules.
- add build time check for struct sizes.
- use skb_share_chack() in vcan.
- fixed some comments.
* Typos in documentation as pointed out by Randy Dunlap and Bill Fink.
The changes in try #6 were:
* Update code to work with namespaces in net-2.6.24.
* Remove SET_MODULE_OWNER() from vcan.
The changes in try #5 were:
* Remove slab destructor from calls to kmem_cache_alloc().
* Add comments about types defined in can.h.
* Update comment on vcan loopback module parameter.
* Fix typo in documentation.
The changes in try #4 were:
* Change vcan network driver to use the new RTNL API, as suggested by
Patrick.
* Revert our change to use skb->iif instead of skb->cb. After
discussion with Patrick and Jamal it turned out, our first
implementation was correct.
* Use skb_tail_pointer() instead of skb->tail directly.
* Coding style changes to satisfy linux/scripts/checkpatch.pl.
* Minor changes for 64-bit-cleanliness.
* Minor cleanup of #include's
The changes in try #3 were:
* Use sbk->sk and skb->pkt_type instead of skb->cb to pass loopback
flags and originating socket down to the driver and back to the
receiving socket. Thanks to Patrick McHardy for pointing out our
wrong use of sbk->cb.
* Use skb->iif instead of skb->cb to pass receiving interface from
raw_rcv() and bcm_rcv() up to raw_recvmsg() and bcm_recvmsg().
* Set skb->protocol when sending CAN frames to netdevices.
* Removed struct raw_opt and struct bcm_opt and integrated these
directly into struct raw_sock and bcm_sock resp., like most other
proto implementations do.
* We have found and fixed race conditions between raw_bind(),
raw_{set,get}sockopt() and raw_notifier(). This resulted in
- complete removal of our own notifier list infrastructure in
af_can.c. raw.c and bcm.c now use normal netdevice notifiers.
- removal of ro->lock spinlock. We use lock_sock(sk) now.
- changed deletion of dev_rcv_lists, which are now marked for
deletion in the netdevice notifier in af_can.c and are actually
deleted when all entries have been deleted using can_rx_unregister().
* Follow changes in 2.6.22 (e.g. ktime_t timestamps in skb).
* Removed obsolete code from vcan.c, as pointed out by Stephen Hemminger.
The changes in try #2 were:
* reduced RCU callback overhead when deleting receiver lists (thx to
feedback from Paul E. McKenney).
* eliminated some code duplication in net/can/proc.c.
* renamed slock-29 and sk_lock-29 to slock-AF_CAN and sk_lock-AF_CAN in
net/core/sock.c
* added entry for can.txt in Documentation/networking/00-INDEX
* added error frame definitions in include/linux/can/error.h, which are to
be used by CAN network drivers.
This patch series applies against net-2.6.24 and is derived from Subversion
revision r511 of http://svn.berlios.de/svnroot/repos/socketcan.
It can be found in the directory
http://svn.berlios.de/svnroot/repos/socketcan/trunk/patch-series/<version>.
Thanks very much for your work!
Best regards,
Urs Thuermann
Oliver Hartkopp
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 14:11 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
` (5 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 01-can-proto-numbers.diff --]
[-- Type: text/plain, Size: 5497 bytes --]
This patch adds a protocol/address family number, ARP hardware type,
ethernet packet type, and a line discipline number for the SocketCAN
implementation.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/if.h | 4 +++-
include/linux/if_arp.h | 1 +
include/linux/if_ether.h | 1 +
include/linux/socket.h | 2 ++
include/linux/tty.h | 3 ++-
net/core/sock.c | 4 ++--
6 files changed, 11 insertions(+), 4 deletions(-)
Index: net-2.6.24/include/linux/if_arp.h
===================================================================
--- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000 +0200
+++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
@@ -52,6 +52,7 @@
#define ARPHRD_ROSE 270
#define ARPHRD_X25 271 /* CCITT X.25 */
#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
+#define ARPHRD_CAN 280 /* Controller Area Network */
#define ARPHRD_PPP 512
#define ARPHRD_CISCO 513 /* Cisco HDLC */
#define ARPHRD_HDLC ARPHRD_CISCO
Index: net-2.6.24/include/linux/if_ether.h
===================================================================
--- net-2.6.24.orig/include/linux/if_ether.h 2007-10-02 12:10:51.000000000 +0200
+++ net-2.6.24/include/linux/if_ether.h 2007-10-02 12:11:01.000000000 +0200
@@ -90,6 +90,7 @@
#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */
+#define ETH_P_CAN 0x000C /* Controller Area Network */
#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/
#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */
#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */
Index: net-2.6.24/include/linux/socket.h
===================================================================
--- net-2.6.24.orig/include/linux/socket.h 2007-10-02 12:10:51.000000000 +0200
+++ net-2.6.24/include/linux/socket.h 2007-10-02 12:11:01.000000000 +0200
@@ -185,6 +185,7 @@
#define AF_PPPOX 24 /* PPPoX sockets */
#define AF_WANPIPE 25 /* Wanpipe API Sockets */
#define AF_LLC 26 /* Linux LLC */
+#define AF_CAN 29 /* Controller Area Network */
#define AF_TIPC 30 /* TIPC sockets */
#define AF_BLUETOOTH 31 /* Bluetooth sockets */
#define AF_IUCV 32 /* IUCV sockets */
@@ -220,6 +221,7 @@
#define PF_PPPOX AF_PPPOX
#define PF_WANPIPE AF_WANPIPE
#define PF_LLC AF_LLC
+#define PF_CAN AF_CAN
#define PF_TIPC AF_TIPC
#define PF_BLUETOOTH AF_BLUETOOTH
#define PF_IUCV AF_IUCV
Index: net-2.6.24/include/linux/tty.h
===================================================================
--- net-2.6.24.orig/include/linux/tty.h 2007-10-02 12:10:51.000000000 +0200
+++ net-2.6.24/include/linux/tty.h 2007-10-02 12:11:02.000000000 +0200
@@ -24,7 +24,7 @@
#define NR_PTYS CONFIG_LEGACY_PTY_COUNT /* Number of legacy ptys */
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 17
+#define NR_LDISCS 18
/* line disciplines */
#define N_TTY 0
@@ -45,6 +45,7 @@
#define N_SYNC_PPP 14 /* synchronous PPP */
#define N_HCI 15 /* Bluetooth HCI UART */
#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
+#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
Index: net-2.6.24/net/core/sock.c
===================================================================
--- net-2.6.24.orig/net/core/sock.c 2007-10-02 12:10:51.000000000 +0200
+++ net-2.6.24/net/core/sock.c 2007-10-02 12:11:02.000000000 +0200
@@ -154,7 +154,7 @@
"sk_lock-AF_ASH" , "sk_lock-AF_ECONET" , "sk_lock-AF_ATMSVC" ,
"sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" ,
"sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" ,
- "sk_lock-27" , "sk_lock-28" , "sk_lock-29" ,
+ "sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_MAX"
};
@@ -168,7 +168,7 @@
"slock-AF_ASH" , "slock-AF_ECONET" , "slock-AF_ATMSVC" ,
"slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" ,
"slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" ,
- "slock-27" , "slock-28" , "slock-29" ,
+ "slock-27" , "slock-28" , "slock-AF_CAN" ,
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_MAX"
};
Index: net-2.6.24/include/linux/if.h
===================================================================
--- net-2.6.24.orig/include/linux/if.h 2007-10-02 12:10:28.000000000 +0200
+++ net-2.6.24/include/linux/if.h 2007-10-02 12:09:34.000000000 +0200
@@ -50,7 +50,9 @@
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
#define IFF_DORMANT 0x20000 /* driver signals dormant */
-#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|\
+#define IFF_ECHO 0x40000 /* echo sent packets */
+
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
/* Private (from user) interface flags (netdevice->priv_flags). */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
2007-10-02 13:10 ` [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 14:38 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 3/7] CAN: Add raw protocol Urs Thuermann
` (4 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60169 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 77 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 970 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 120 +++++
net/can/proc.c | 532 +++++++++++++++++++++++++
10 files changed, 1936 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,77 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20071001"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
+#define DBG(fmt, args...) (debug & 1 ? printk(KERN_DEBUG "can-" IDENT \
+ " %s: " fmt, __func__, ##args) : 0)
+#define DBG_FRAME(fmt, cf) (debug & 2 ? can_debug_cframe(fmt, cf) : 0)
+#define DBG_SKB(skb) (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(fmt, args...)
+#define DBG_FRAME(fmt, cf)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-10-02 12:10:09.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-10-02 12:10:18.000000000 +0200
@@ -218,6 +218,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-10-02 12:10:09.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-10-02 12:10:18.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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 at
+ 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages to the system log. Select this if you are having a
+ problem with CAN support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,970 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata 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)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug __read_mostly;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (ret) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for success and correct type */
+ if (!cp || cp->type != sock->type) {
+ ret = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ ret = -EPERM;
+ goto errout;
+ }
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk) {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, rl, list) {
+ if (r->can_id == can_id && r->mask == mask
+ && r->func == func && r->data == data)
+ break;
+ }
+
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("af_can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf)
+{
+ char idbuf[12];
+ char hexbuf[28];
+ int dlc;
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
+ else
+ sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ sprintf(hexbuf, "(RTR)");
+ else
+ hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
+
+ printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d\n",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+
+ print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->head, skb->end - skb->head, 0);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ /* procfs init */
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ /* procfs remove */
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ 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;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.tx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-10-02 12:10:18.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 3/7] CAN: Add raw protocol
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
2007-10-02 13:10 ` [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
2007-10-02 13:10 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 14:30 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 4/7] CAN: Add broadcast manager (bcm) protocol Urs Thuermann
` (3 subsequent siblings)
6 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 03-can-raw-proto.diff --]
[-- Type: text/plain, Size: 22533 bytes --]
This patch adds the CAN raw protocol.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can/raw.h | 31 +
net/can/Kconfig | 11
net/can/Makefile | 3
net/can/raw.c | 821 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 866 insertions(+)
Index: net-2.6.24/include/linux/can/raw.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/raw.h 2007-10-02 08:33:39.000000000 +0200
@@ -0,0 +1,31 @@
+/*
+ * linux/can/raw.h
+ *
+ * Definitions for raw CAN sockets
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_RAW_H
+#define CAN_RAW_H
+
+#include <linux/can.h>
+
+#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)
+
+/* for socket options affecting the socket (not the global system) */
+
+enum {
+ CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */
+ CAN_RAW_ERR_FILTER, /* set filter for error frames */
+ CAN_RAW_LOOPBACK, /* local loopback (default:on) */
+ CAN_RAW_RECV_OWN_MSGS /* receive my own msgs (default:off) */
+};
+
+#endif
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- net-2.6.24.orig/net/can/Kconfig 2007-10-02 06:18:29.000000000 +0200
+++ net-2.6.24/net/can/Kconfig 2007-10-02 08:35:31.000000000 +0200
@@ -16,6 +16,17 @@
If you want CAN support, you should say Y here and also to the
specific driver for your controller(s) below.
+config CAN_RAW
+ tristate "Raw CAN Protocol (raw access with CAN-ID filtering)"
+ depends on CAN
+ default N
+ ---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 / Errorframes.
+ To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
+
config CAN_DEBUG_CORE
bool "CAN Core debugging messages"
depends on CAN
Index: net-2.6.24/net/can/Makefile
===================================================================
--- net-2.6.24.orig/net/can/Makefile 2007-10-02 06:18:29.000000000 +0200
+++ net-2.6.24/net/can/Makefile 2007-10-02 08:35:31.000000000 +0200
@@ -4,3 +4,6 @@
obj-$(CONFIG_CAN) += can.o
can-objs := af_can.o proc.o
+
+obj-$(CONFIG_CAN_RAW) += can-raw.o
+can-raw-objs := raw.o
Index: net-2.6.24/net/can/raw.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/raw.c 2007-10-02 08:35:48.000000000 +0200
@@ -0,0 +1,821 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <linux/net.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/raw.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#define IDENT "raw"
+#define CAN_RAW_VERSION CAN_VERSION
+static __initdata 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>");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+#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 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;
+};
+
+static inline struct raw_sock *raw_sk(const struct sock *sk)
+{
+ return (struct raw_sock *)sk;
+}
+
+static void raw_rcv(struct sk_buff *skb, void *data)
+{
+ struct sock *sk = (struct sock *)data;
+ struct raw_sock *ro = raw_sk(sk);
+ struct sockaddr_can *addr;
+ int error;
+
+ DBG("received skbuff %p, sk %p\n", skb, sk);
+ DBG_SKB(skb);
+
+ if (!ro->recv_own_msgs) {
+ /* check the received tx sock reference */
+ if (skb->sk == sk) {
+ DBG("trashed own tx msg\n");
+ kfree_skb(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;
+
+ error = sock_queue_rcv_skb(sk, skb);
+ if (error < 0) {
+ DBG("sock_queue_rcv_skb failed: %d\n", error);
+ DBG("freeing skbuff %p\n", skb);
+ 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++) {
+ DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
+ filter[i].can_id, filter[i].can_mask,
+ filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
+
+ err = can_rx_register(dev, filter[i].can_id,
+ filter[i].can_mask,
+ raw_rcv, sk, IDENT);
+
+ 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, IDENT);
+
+ 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++) {
+ DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
+ filter[i].can_id, filter[i].can_mask,
+ filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
+
+ 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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
+ struct sock *sk = &ro->sk;
+
+ DBG("msg %ld for dev %p (%s idx %d) sk %p ro->ifindex %d\n",
+ msg, dev, dev->name, dev->ifindex, sk, ro->ifindex);
+
+ if (dev->nd_net != &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;
+
+ /* 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 = raw_sk(sk);
+
+ DBG("socket %p, sk %p, refcnt %d\n", sock, sk,
+ atomic_read(&sk->sk_refcnt));
+
+ 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;
+
+ 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;
+
+ DBG("socket %p to device %d\n", sock, addr->can_ifindex);
+
+ 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) {
+ DBG("could not find device %d\n", addr->can_ifindex);
+ err = -ENODEV;
+ goto out;
+ }
+ if (dev->type != ARPHRD_CAN) {
+ DBG("device %d no CAN device\n", addr->can_ifindex);
+ 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;
+
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = ro->ifindex;
+
+ *len = sizeof(*addr);
+
+ return 0;
+}
+
+static unsigned int raw_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ DBG("socket %p\n", sock);
+
+ mask = datagram_poll(file, sock, wait);
+ return mask;
+}
+
+static int raw_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, 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;
+ if (optlen < 0)
+ 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 = kmalloc(optlen, GFP_KERNEL);
+ if (!filter)
+ return -ENOMEM;
+
+ err = copy_from_user(filter, optval, optlen);
+ if (err) {
+ kfree(filter);
+ return err;
+ }
+ } else if (count == 1) {
+ err = copy_from_user(&sfilter, optval, optlen);
+ if (err)
+ return err;
+ }
+
+ 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;
+
+ err = copy_from_user(&err_mask, optval, optlen);
+ if (err)
+ return err;
+
+ 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;
+
+ err = copy_from_user(&ro->loopback, optval, optlen);
+
+ break;
+
+ case CAN_RAW_RECV_OWN_MSGS:
+ if (optlen != sizeof(ro->recv_own_msgs))
+ return -EINVAL;
+
+ err = copy_from_user(&ro->recv_own_msgs, optval, optlen);
+
+ 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;
+ err = copy_to_user(optval, ro->filter, len);
+ } 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;
+
+ 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;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ if (msg->msg_name) {
+ struct sockaddr_can *addr =
+ (struct sockaddr_can *)msg->msg_name;
+
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ ifindex = addr->can_ifindex;
+ } else
+ ifindex = ro->ifindex;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ DBG("device %d not found\n", ifindex);
+ return -ENXIO;
+ }
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ dev_put(dev);
+ return -ENOMEM;
+ }
+
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (err < 0) {
+ kfree_skb(skb);
+ dev_put(dev);
+ return err;
+ }
+ skb->dev = dev;
+ skb->sk = sk;
+
+ DBG("sending skbuff to interface %d\n", ifindex);
+ DBG_SKB(skb);
+
+ err = can_send(skb, ro->loopback);
+
+ dev_put(dev);
+
+ if (err)
+ return err;
+
+ return size;
+}
+
+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 error = 0;
+ int noblock;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ noblock = flags & MSG_DONTWAIT;
+ flags &= ~MSG_DONTWAIT;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &error);
+ if (!skb)
+ return error;
+
+ DBG("delivering skbuff %p\n", skb);
+ DBG_SKB(skb);
+
+ if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ size = skb->len;
+
+ error = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (error < 0) {
+ skb_free_datagram(sk, skb);
+ return error;
+ }
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (msg->msg_name) {
+ msg->msg_namelen = sizeof(struct sockaddr_can);
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ }
+
+ DBG("freeing sock %p, skbuff %p\n", sk, skb);
+ skb_free_datagram(sk, skb);
+
+ return size;
+}
+
+static struct proto_ops raw_ops __read_mostly = {
+ .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 = raw_poll,
+ .ioctl = NULL, /* 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 struct can_proto raw_can_proto __read_mostly = {
+ .type = SOCK_RAW,
+ .protocol = CAN_RAW,
+ .capability = -1,
+ .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);
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 4/7] CAN: Add broadcast manager (bcm) protocol
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
` (2 preceding siblings ...)
2007-10-02 13:10 ` [PATCH 3/7] CAN: Add raw protocol Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 13:10 ` [PATCH 5/7] CAN: Add virtual CAN netdevice driver Urs Thuermann
` (2 subsequent siblings)
6 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 04-can-bcm-proto.diff --]
[-- Type: text/plain, Size: 49986 bytes --]
This patch adds the CAN broadcast manager (bcm) protocol.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can/bcm.h | 65 +
net/can/Kconfig | 13
net/can/Makefile | 3
net/can/bcm.c | 1774 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1855 insertions(+)
Index: net-2.6.24/include/linux/can/bcm.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/bcm.h 2007-10-02 08:33:40.000000000 +0200
@@ -0,0 +1,65 @@
+/*
+ * linux/can/bcm.h
+ *
+ * Definitions for CAN Broadcast Manager (BCM)
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_BCM_H
+#define CAN_BCM_H
+
+/**
+ * struct bcm_msg_head - head of messages to/from the broadcast manager
+ * @opcode: opcode, see enum below.
+ * @flags: special flags, see below.
+ * @count: number of frames to send before changing interval.
+ * @ival1: interval for the first @count frames.
+ * @ival2: interval for the following frames.
+ * @can_id: CAN ID of frames to be sent or received.
+ * @nframes: number of frames appended to the message head.
+ * @frames: array of CAN frames.
+ */
+struct bcm_msg_head {
+ int opcode;
+ int flags;
+ int count;
+ struct timeval ival1, ival2;
+ canid_t can_id;
+ int nframes;
+ struct can_frame frames[0];
+};
+
+enum {
+ TX_SETUP = 1, /* create (cyclic) transmission task */
+ TX_DELETE, /* remove (cyclic) transmission task */
+ TX_READ, /* read properties of (cyclic) transmission task */
+ TX_SEND, /* send one CAN frame */
+ RX_SETUP, /* create RX content filter subscription */
+ RX_DELETE, /* remove RX content filter subscription */
+ RX_READ, /* read properties of RX content filter subscription */
+ TX_STATUS, /* reply to TX_READ request */
+ TX_EXPIRED, /* notification on performed transmissions (count=0) */
+ RX_STATUS, /* reply to RX_READ request */
+ RX_TIMEOUT, /* cyclic message is absent */
+ RX_CHANGED /* updated CAN frame (detected content change) */
+};
+
+#define SETTIMER 0x0001
+#define STARTTIMER 0x0002
+#define TX_COUNTEVT 0x0004
+#define TX_ANNOUNCE 0x0008
+#define TX_CP_CAN_ID 0x0010
+#define RX_FILTER_ID 0x0020
+#define RX_CHECK_DLC 0x0040
+#define RX_NO_AUTOTIMER 0x0080
+#define RX_ANNOUNCE_RESUME 0x0100
+#define TX_RESET_MULTI_IDX 0x0200
+#define RX_RTR_FRAME 0x0400
+
+#endif /* CAN_BCM_H */
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- net-2.6.24.orig/net/can/Kconfig 2007-10-02 08:33:39.000000000 +0200
+++ net-2.6.24/net/can/Kconfig 2007-10-02 08:33:40.000000000 +0200
@@ -27,6 +27,19 @@
socket has several filter options e.g. ID-Masking / Errorframes.
To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
+config CAN_BCM
+ tristate "Broadcast Manager CAN Protocol (with content filtering)"
+ depends on CAN
+ default N
+ ---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_DEBUG_CORE
bool "CAN Core debugging messages"
depends on CAN
Index: net-2.6.24/net/can/Makefile
===================================================================
--- net-2.6.24.orig/net/can/Makefile 2007-10-02 08:33:39.000000000 +0200
+++ net-2.6.24/net/can/Makefile 2007-10-02 08:33:40.000000000 +0200
@@ -7,3 +7,6 @@
obj-$(CONFIG_CAN_RAW) += can-raw.o
can-raw-objs := raw.o
+
+obj-$(CONFIG_CAN_BCM) += can-bcm.o
+can-bcm-objs := bcm.o
Index: net-2.6.24/net/can/bcm.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/bcm.c 2007-10-02 08:35:04.000000000 +0200
@@ -0,0 +1,1774 @@
+/*
+ * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <linux/net.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/bcm.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+/* use of last_frames[index].can_dlc */
+#define RX_RECV 0x40 /* received data for this element */
+#define RX_THR 0x80 /* element not been sent due to throttle feature */
+#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
+
+/* get best masking value for can_rx_register() for a given single can_id */
+#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
+
+#define IDENT "bcm"
+#define CAN_BCM_VERSION CAN_VERSION
+static __initdata const char banner[] = KERN_INFO
+ "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+/* easy access to can_frame payload */
+static inline u64 GET_U64(const struct can_frame *cp)
+{
+ return *(u64 *)cp->data;
+}
+
+struct bcm_op {
+ struct list_head list;
+ int ifindex;
+ canid_t can_id;
+ int flags;
+ unsigned long j_ival1, j_ival2, j_lastmsg;
+ unsigned long frames_abs, frames_filtered;
+ struct timer_list timer, thrtimer;
+ struct timeval ival1, ival2;
+ ktime_t rx_stamp;
+ int rx_ifindex;
+ int count;
+ int nframes;
+ int currframe;
+ struct can_frame *frames;
+ struct can_frame *last_frames;
+ struct can_frame sframe;
+ struct can_frame last_sframe;
+ struct sock *sk;
+ struct net_device *rx_reg_dev;
+};
+
+static struct proc_dir_entry *proc_dir;
+
+struct bcm_sock {
+ struct sock sk;
+ int bound;
+ int ifindex;
+ struct notifier_block notifier;
+ struct list_head rx_ops;
+ struct list_head tx_ops;
+ unsigned long dropped_usr_msgs;
+ struct proc_dir_entry *bcm_proc_read;
+ char procname [9]; /* pointer printed in ASCII with \0 */
+};
+
+static inline struct bcm_sock *bcm_sk(const struct sock *sk)
+{
+ return (struct bcm_sock *)sk;
+}
+
+#define CFSIZ sizeof(struct can_frame)
+#define OPSIZ sizeof(struct bcm_op)
+#define MHSIZ sizeof(struct bcm_msg_head)
+
+/*
+ * rounded_tv2jif - calculate jiffies from timeval including optional up
+ * @tv: pointer to timeval
+ *
+ * Description:
+ * Unlike timeval_to_jiffies() provided in include/linux/jiffies.h, this
+ * function is intentionally more relaxed on precise timer ticks to get
+ * exact one jiffy for requested 1000us on a 1000HZ machine.
+ * This code is to be removed when upgrading to kernel hrtimer.
+ *
+ * Return:
+ * calculated jiffies (max: ULONG_MAX)
+ */
+static unsigned long rounded_tv2jif(const struct timeval *tv)
+{
+ unsigned long sec = tv->tv_sec;
+ unsigned long usec = tv->tv_usec;
+ unsigned long jif;
+
+ if (sec > ULONG_MAX / HZ)
+ return ULONG_MAX;
+
+ /* round up to get at least the requested time */
+ usec += 1000000 / HZ - 1;
+
+ jif = usec / (1000000 / HZ);
+
+ if (sec * HZ > ULONG_MAX - jif)
+ return ULONG_MAX;
+
+ return jif + sec * HZ;
+}
+
+/*
+ * procfs functions
+ */
+static char *bcm_proc_getifname(int ifindex)
+{
+ struct net_device *dev;
+
+ if (!ifindex)
+ return "any";
+
+ dev = __dev_get_by_index(&init_net, ifindex); /* no usage counting */
+ if (dev)
+ return dev->name;
+
+ return "???";
+}
+
+static int bcm_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct sock *sk = (struct sock *)data;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+
+ len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p",
+ sk->sk_socket);
+ len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo);
+ len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
+ bo->dropped_usr_msgs);
+ len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
+ bcm_proc_getifname(bo->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+
+ list_for_each_entry(op, &bo->rx_ops, list) {
+
+ unsigned long reduction;
+
+ /* print only active entries & prevent division by zero */
+ if (!op->frames_abs)
+ continue;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "rx_op: %03X %-5s ",
+ op->can_id, bcm_proc_getifname(op->ifindex));
+ len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
+ op->nframes,
+ (op->flags & RX_CHECK_DLC)?'d':' ');
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "timeo=%ld ", op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "thr=%ld ", op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "# recv %ld (%ld) => reduction: ",
+ op->frames_filtered, op->frames_abs);
+
+ reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n",
+ (reduction == 100)?"near ":"", reduction);
+
+ if (len > PAGE_SIZE - 200) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ list_for_each_entry(op, &bo->tx_ops, list) {
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "tx_op: %03X %s [%d] ",
+ op->can_id, bcm_proc_getifname(op->ifindex),
+ op->nframes);
+ if (op->j_ival1)
+ len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ",
+ op->j_ival1);
+
+ if (op->j_ival2)
+ len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ",
+ op->j_ival2);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
+ op->frames_abs);
+
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+ break;
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ * of the given bcm tx op
+ */
+static void bcm_can_tx(struct bcm_op *op)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct can_frame *cf = &op->frames[op->currframe];
+
+ DBG_FRAME("BCM: bcm_can_tx: sending frame", cf);
+
+ /* no target device? => exit */
+ if (!op->ifindex)
+ return;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (!dev) {
+ /* RFC: should this bcm_op remove itself here? */
+ return;
+ }
+
+ skb = alloc_skb(CFSIZ, gfp_any());
+ if (!skb)
+ goto out;
+
+ memcpy(skb_put(skb, CFSIZ), cf, CFSIZ);
+
+ /* send with loopback */
+ skb->dev = dev;
+ skb->sk = op->sk;
+ can_send(skb, 1);
+
+ /* update statistics */
+ op->currframe++;
+ op->frames_abs++;
+
+ /* reached last frame? */
+ if (op->currframe >= op->nframes)
+ op->currframe = 0;
+ out:
+ dev_put(dev);
+}
+
+/*
+ * bcm_send_to_user - send a BCM message to the userspace
+ * (consisting of bcm_msg_head + x CAN frames)
+ */
+static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
+ struct can_frame *frames, int has_timestamp)
+{
+ struct sk_buff *skb;
+ struct can_frame *firstframe;
+ struct sockaddr_can *addr;
+ struct sock *sk = op->sk;
+ int datalen = head->nframes * CFSIZ;
+ int err;
+
+ skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
+ if (!skb)
+ return;
+
+ memcpy(skb_put(skb, sizeof(*head)), head, sizeof(*head));
+
+ if (head->nframes) {
+ /* can_frames starting here */
+ firstframe = (struct can_frame *) skb_tail_pointer(skb);
+
+ memcpy(skb_put(skb, datalen), frames, datalen);
+
+ /*
+ * the BCM uses the can_dlc-element of the can_frame
+ * structure for internal purposes. This is only
+ * relevant for updates that are generated by the
+ * BCM, where nframes is 1
+ */
+ if (head->nframes == 1)
+ firstframe->can_dlc &= BCM_CAN_DLC_MASK;
+ }
+
+ if (has_timestamp) {
+ /* restore rx timestamp */
+ skb->tstamp = op->rx_stamp;
+ }
+
+ /*
+ * Put the datagram to the queue so that bcm_recvmsg() can
+ * get it from there. We need to pass the interface index to
+ * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
+ * containing the interface index.
+ */
+
+ 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 = op->rx_ifindex;
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (err < 0) {
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ DBG("sock_queue_rcv_skb failed: %d\n", err);
+ kfree_skb(skb);
+ /* don't care about overflows in this statistic */
+ bo->dropped_usr_msgs++;
+ }
+}
+
+/*
+ * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
+ */
+static void bcm_tx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+
+ DBG("Called with bcm_op %p\n", op);
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ op->count--;
+ if (!op->count && (op->flags & TX_COUNTEVT)) {
+ struct bcm_msg_head msg_head;
+
+ /* create notification to user */
+ DBG("sending TX_EXPIRED for can_id %03X\n",
+ op->can_id);
+
+ msg_head.opcode = TX_EXPIRED;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+ }
+ }
+
+ DBG("count=%d j_ival1=%ld j_ival2=%ld\n",
+ op->count, op->j_ival1, op->j_ival2);
+
+ if (op->j_ival1 && (op->count > 0)) {
+
+ op->timer.expires = jiffies + op->j_ival1;
+ add_timer(&op->timer);
+
+ DBG("adding timer ival1. func=%p data=%p exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+
+ } else {
+ if (op->j_ival2) {
+ op->timer.expires = jiffies + op->j_ival2;
+ add_timer(&op->timer);
+
+ DBG("adding timer ival2. func=%p data=%p exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+
+ } else
+ DBG("no timer restart\n");
+ }
+
+ return;
+}
+
+/*
+ * bcm_rx_changed - create a RX_CHANGED notification due to changed content
+ */
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
+{
+ struct bcm_msg_head head;
+
+ op->j_lastmsg = jiffies;
+
+ /* update statistics */
+ op->frames_filtered++;
+
+ /* prevent statistics overflow */
+ if (op->frames_filtered > ULONG_MAX/100)
+ op->frames_filtered = op->frames_abs = 0;
+
+ DBG("setting j_lastmsg to 0x%08X for rx_op %p\n",
+ (unsigned int) op->j_lastmsg, op);
+ DBG("sending notification\n");
+
+ head.opcode = RX_CHANGED;
+ head.flags = op->flags;
+ head.count = op->count;
+ head.ival1 = op->ival1;
+ head.ival2 = op->ival2;
+ head.can_id = op->can_id;
+ head.nframes = 1;
+
+ bcm_send_to_user(op, &head, data, 1);
+}
+
+/*
+ * bcm_rx_update_and_send - process a detected relevant receive content change
+ * 1. update the last received data
+ * 2. send a notification to the user (if possible)
+ */
+static void bcm_rx_update_and_send(struct bcm_op *op,
+ struct can_frame *lastdata,
+ struct can_frame *rxdata)
+{
+ unsigned long nexttx = op->j_lastmsg + op->j_ival2;
+
+ memcpy(lastdata, rxdata, CFSIZ);
+
+ /* mark as used */
+ lastdata->can_dlc |= RX_RECV;
+
+ /* throttle bcm_rx_changed ? */
+ if ((op->thrtimer.expires) ||
+ ((op->j_ival2) && (nexttx > jiffies))) {
+ /* we are already waiting OR we have to start waiting */
+
+ /* mark as 'throttled' */
+ lastdata->can_dlc |= RX_THR;
+
+ if (!(op->thrtimer.expires)) {
+ /* start the timer only the first time */
+ op->thrtimer.expires = nexttx;
+ add_timer(&op->thrtimer);
+
+ DBG("adding thrtimer. func=%p data=%p exp=0x%08X\n",
+ op->thrtimer.function,
+ (char *) op->thrtimer.data,
+ (unsigned int) op->thrtimer.expires);
+ }
+
+ } else {
+ /* send RX_CHANGED to the user immediately */
+ bcm_rx_changed(op, rxdata);
+ }
+}
+
+/*
+ * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
+ * received data stored in op->last_frames[]
+ */
+static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
+ struct can_frame *rxdata)
+{
+ /*
+ * no one uses the MSBs of can_dlc for comparation,
+ * so we use it here to detect the first time of reception
+ */
+
+ if (!(op->last_frames[index].can_dlc & RX_RECV)) {
+ /* received data for the first time => send update to user */
+ DBG("first time :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ /* do a real check in can_frame data section */
+
+ DBG("op->frames[index].data = 0x%016llx\n",
+ GET_U64(&op->frames[index]));
+ DBG("op->last_frames[index].data = 0x%016llx\n",
+ GET_U64(&op->last_frames[index]));
+ DBG("rxdata->data = 0x%016llx\n", GET_U64(rxdata));
+
+ if ((GET_U64(&op->frames[index]) & GET_U64(rxdata)) !=
+ (GET_U64(&op->frames[index]) & GET_U64(&op->last_frames[index]))) {
+ DBG("relevant data change :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+ return;
+ }
+
+ if (op->flags & RX_CHECK_DLC) {
+ /* do a real check in can_frame dlc */
+ if (rxdata->can_dlc != (op->last_frames[index].can_dlc &
+ BCM_CAN_DLC_MASK)) {
+ DBG("dlc change :)\n");
+ bcm_rx_update_and_send(op, &op->last_frames[index],
+ rxdata);
+ return;
+ }
+ }
+ DBG("no relevant change :(\n");
+}
+
+/*
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ */
+static void bcm_rx_starttimer(struct bcm_op *op)
+{
+ if (op->flags & RX_NO_AUTOTIMER)
+ return;
+
+ if (op->j_ival1) {
+ op->timer.expires = jiffies + op->j_ival1;
+
+ DBG("adding rx timeout timer ival1. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ add_timer(&op->timer);
+ }
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static void bcm_rx_timeout_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct bcm_msg_head msg_head;
+
+ DBG("sending RX_TIMEOUT for can_id %03X. op is %p\n", op->can_id, op);
+
+ msg_head.opcode = RX_TIMEOUT;
+ msg_head.flags = op->flags;
+ msg_head.count = op->count;
+ msg_head.ival1 = op->ival1;
+ msg_head.ival2 = op->ival2;
+ msg_head.can_id = op->can_id;
+ msg_head.nframes = 0;
+
+ bcm_send_to_user(op, &msg_head, NULL, 0);
+
+ /* no restart of the timer is done here! */
+
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+ /* clear received can_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, op->nframes * CFSIZ);
+ DBG("RX_ANNOUNCE_RESTART\n");
+ }
+}
+
+/*
+ * bcm_rx_thr_handler - the time for blocked content updates is over now:
+ * Check for throttled data and send it to the userspace
+ */
+static void bcm_rx_thr_handler(unsigned long data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ int i = 0;
+
+ /* mark disabled / consumed timer */
+ op->thrtimer.expires = 0;
+
+ if (op->nframes > 1) {
+ DBG("sending MUX RX_CHANGED for can_id %03X. op is %p\n",
+ op->can_id, op);
+ /* for MUX filter we start at index 1 */
+ for (i = 1; i < op->nframes; i++) {
+ if ((op->last_frames) &&
+ (op->last_frames[i].can_dlc & RX_THR)) {
+ op->last_frames[i].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[i]);
+ }
+ }
+
+ } else {
+ DBG("sending simple RX_CHANGED for can_id %03X. op is %p\n",
+ op->can_id, op);
+ /* for RX_FILTER_ID and simple filter */
+ if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) {
+ op->last_frames[0].can_dlc &= ~RX_THR;
+ bcm_rx_changed(op, &op->last_frames[0]);
+ }
+ }
+}
+
+/*
+ * bcm_rx_handler - handle a CAN frame receiption
+ */
+static void bcm_rx_handler(struct sk_buff *skb, void *data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ struct can_frame rxframe;
+ int i;
+
+ /* disable timeout */
+ del_timer(&op->timer);
+
+ DBG("Called with bcm_op %p\n", op);
+
+ if (skb->len == sizeof(rxframe)) {
+ memcpy(&rxframe, skb->data, sizeof(rxframe));
+ /* save rx timestamp */
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+ /* update statistics */
+ op->frames_abs++;
+ kfree_skb(skb);
+ DBG("got can_frame with can_id %03X\n", rxframe.can_id);
+
+ } else {
+ DBG("Wrong skb->len = %d\n", skb->len);
+ kfree_skb(skb);
+ return;
+ }
+
+ DBG_FRAME("BCM: bcm_rx_handler: CAN frame", &rxframe);
+
+ if (op->can_id != rxframe.can_id) {
+ DBG("ERROR! Got wrong can_id %03X! Expected %03X.\n",
+ rxframe.can_id, op->can_id);
+ return;
+ }
+
+ if (op->flags & RX_RTR_FRAME) {
+ /* send reply for RTR-request */
+ DBG("RTR-request\n");
+
+ /* send op->frames[0] to CAN device */
+ bcm_can_tx(op);
+ return;
+ }
+
+ if (op->flags & RX_FILTER_ID) {
+ /* the easiest case */
+ DBG("Easy does it with RX_FILTER_ID\n");
+
+ bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes == 1) {
+ /* simple compare with index 0 */
+ DBG("Simple compare\n");
+
+ bcm_rx_cmp_to_index(op, 0, &rxframe);
+ bcm_rx_starttimer(op);
+ return;
+ }
+
+ if (op->nframes > 1) {
+ /* multiplex compare */
+ DBG("Multiplex compare\n");
+
+ /*
+ * find the first multiplex mask that fits.
+ * Remark: The MUX-mask is stored in index 0
+ */
+
+ for (i = 1; i < op->nframes; i++) {
+ if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+ (GET_U64(&op->frames[0]) &
+ GET_U64(&op->frames[i]))) {
+ DBG("found MUX index %d\n", i);
+ bcm_rx_cmp_to_index(op, i, &rxframe);
+ break;
+ }
+ }
+ bcm_rx_starttimer(op);
+ }
+}
+
+/*
+ * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
+ */
+static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id,
+ int ifindex)
+{
+ struct bcm_op *op;
+
+ list_for_each_entry(op, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex))
+ return op;
+ }
+
+ return NULL;
+}
+
+static void bcm_remove_op(struct bcm_op *op)
+{
+ del_timer(&op->timer);
+ del_timer(&op->thrtimer);
+
+ if ((op->frames) && (op->frames != &op->sframe))
+ kfree(op->frames);
+
+ if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+ kfree(op->last_frames);
+
+ kfree(op);
+
+ return;
+}
+
+static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+{
+ if (op->rx_reg_dev == dev) {
+ can_rx_unregister(dev, op->can_id, REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ /* mark as removed subscription */
+ op->rx_reg_dev = NULL;
+ } else
+ printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+ "mismatch %p %p\n", op->rx_reg_dev, dev);
+}
+
+/*
+ * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
+ */
+static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+ DBG("removing rx_op %p for can_id %03X\n",
+ op, op->can_id);
+
+ /*
+ * Don't care if we're bound or not (due to netdev
+ * problems) can_rx_unregister() is always a save
+ * thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net,
+ op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
+ */
+static int bcm_delete_tx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+ struct bcm_op *op, *n;
+
+ list_for_each_entry_safe(op, n, ops, list) {
+ if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+ DBG("removing rx_op %p for can_id %03X\n",
+ op, op->can_id);
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return 1; /* done */
+ }
+ }
+
+ return 0; /* not found */
+}
+
+/*
+ * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
+ */
+static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
+ int ifindex)
+{
+ struct bcm_op *op = bcm_find_op(ops, msg_head->can_id, ifindex);
+
+ if (!op) {
+ DBG("TRX_READ: did not find op for can_id %03X\n",
+ msg_head->can_id);
+ return -EINVAL;
+ }
+
+ DBG("TRX_READ: sending status for can_id %03X\n",
+ msg_head->can_id);
+ /* put current values into msg_head */
+ msg_head->flags = op->flags;
+ msg_head->count = op->count;
+ msg_head->ival1 = op->ival1;
+ msg_head->ival2 = op->ival2;
+ msg_head->nframes = op->nframes;
+
+ bcm_send_to_user(op, msg_head, op->frames, 0);
+
+ return MHSIZ;
+}
+
+/*
+ * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
+ */
+static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int i, err;
+
+ /* we need a real device to send frames */
+ if (!ifindex)
+ return -ENODEV;
+
+ /* we need at least one can_frame */
+ if (msg_head->nframes < 1)
+ return -EINVAL;
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex);
+
+ if (op) {
+ /* update existing BCM operation */
+
+ DBG("TX_SETUP: modifying existing tx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ /* update can_frames content */
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+ if (err < 0)
+ return err;
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ DBG("TX_SETUP: creating new tx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ op->can_id = msg_head->can_id;
+
+ /* create array for can_frames and copy the data */
+ if (msg_head->nframes > 1) {
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+ } else
+ op->frames = &op->sframe;
+
+ for (i = 0; i < msg_head->nframes; i++) {
+ err = memcpy_fromiovec((u8 *)&op->frames[i],
+ msg->msg_iov, CFSIZ);
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ kfree(op);
+ return err;
+ }
+
+ if (msg_head->flags & TX_CP_CAN_ID) {
+ /* copy can_id into frame */
+ op->frames[i].can_id = msg_head->can_id;
+ }
+ }
+
+ /* tx_ops never compare with previous received messages */
+ op->last_frames = NULL;
+
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+ op->sk = sk;
+
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kmalloc) structure */
+ init_timer(&op->timer);
+
+ /* currently unused in tx_ops */
+ init_timer(&op->thrtimer);
+
+ /* handler for tx_ops */
+ op->timer.function = bcm_tx_timeout_handler;
+
+ /* timer.data points to this op-structure */
+ op->timer.data = (unsigned long)op;
+
+ /* add this bcm_op to the list of the tx_ops */
+ list_add(&op->list, &bo->tx_ops);
+
+ } /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
+
+ if (op->nframes != msg_head->nframes) {
+ op->nframes = msg_head->nframes;
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ /* check flags */
+
+ op->flags = msg_head->flags;
+
+ if (op->flags & TX_RESET_MULTI_IDX) {
+ /* start multiple frame transmission with index 0 */
+ op->currframe = 0;
+ }
+
+ if (op->flags & SETTIMER) {
+ /* set timer values */
+
+ op->count = msg_head->count;
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ DBG("TX_SETUP: SETTIMER count=%d j_ival1=%ld j_ival2=%ld\n",
+ op->count, op->j_ival1, op->j_ival2);
+
+ /* disable an active timer due to zero values? */
+ if (!op->j_ival1 && !op->j_ival2) {
+ del_timer(&op->timer);
+ DBG("TX_SETUP: SETTIMER disabled timer.\n");
+ }
+ }
+
+ if ((op->flags & STARTTIMER) &&
+ ((op->j_ival1 && op->count) || op->j_ival2)) {
+
+ del_timer(&op->timer);
+
+ /* spec: send can_frame when starting timer */
+ op->flags |= TX_ANNOUNCE;
+
+ if (op->j_ival1 && (op->count > 0)) {
+ op->timer.expires = jiffies + op->j_ival1;
+ /* op->count-- is done in bcm_tx_timeout_handler */
+ DBG("TX_SETUP: adding timer ival1. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ } else {
+ op->timer.expires = jiffies + op->j_ival2;
+ DBG("TX_SETUP: adding timer ival2. func=%p data=%p "
+ "exp=0x%08X\n",
+ op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+ }
+
+ add_timer(&op->timer);
+ }
+
+ if (op->flags & TX_ANNOUNCE)
+ bcm_can_tx(op);
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+ int ifindex, struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
+ int do_rx_register;
+ int err = 0;
+
+ if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+ /* be robust against wrong usage ... */
+ msg_head->flags |= RX_FILTER_ID;
+ msg_head->nframes = 0; /* ignore trailing garbage */
+ }
+
+ if ((msg_head->flags & RX_RTR_FRAME) &&
+ ((msg_head->nframes != 1) ||
+ (!(msg_head->can_id & CAN_RTR_FLAG)))) {
+
+ DBG("RX_SETUP: bad RX_RTR_FRAME setup!\n");
+ return -EINVAL;
+ }
+
+ /* check the given can_id */
+ op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex);
+ if (op) {
+ /* update existing BCM operation */
+
+ DBG("RX_SETUP: modifying existing rx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ /*
+ * Do we need more space for the can_frames than currently
+ * allocated? -> This is a _really_ unusual use-case and
+ * therefore (complexity / locking) it is not supported.
+ */
+ if (msg_head->nframes > op->nframes)
+ return -E2BIG;
+
+ if (msg_head->nframes) {
+ /* update can_frames content */
+ err = memcpy_fromiovec((u8 *)op->frames,
+ msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0)
+ return err;
+
+ /* clear last_frames to indicate 'nothing received' */
+ memset(op->last_frames, 0, msg_head->nframes * CFSIZ);
+ }
+
+ op->nframes = msg_head->nframes;
+
+ /* Only an update -> do not call can_rx_register() */
+ do_rx_register = 0;
+
+ } else {
+ /* insert new BCM operation for the given can_id */
+
+ op = kzalloc(OPSIZ, GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
+ DBG("RX_SETUP: creating new rx_op %p for can_id %03X\n",
+ op, msg_head->can_id);
+
+ op->can_id = msg_head->can_id;
+ op->nframes = msg_head->nframes;
+
+ if (msg_head->nframes > 1) {
+ /* create array for can_frames and copy the data */
+ op->frames = kmalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->frames) {
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ /* create and init array for received can_frames */
+ op->last_frames = kzalloc(msg_head->nframes * CFSIZ,
+ GFP_KERNEL);
+ if (!op->last_frames) {
+ kfree(op->frames);
+ kfree(op);
+ return -ENOMEM;
+ }
+
+ } else {
+ op->frames = &op->sframe;
+ op->last_frames = &op->last_sframe;
+ }
+
+ if (msg_head->nframes) {
+ err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov,
+ msg_head->nframes * CFSIZ);
+ if (err < 0) {
+ if (op->frames != &op->sframe)
+ kfree(op->frames);
+ if (op->last_frames != &op->last_sframe)
+ kfree(op->last_frames);
+ kfree(op);
+ return err;
+ }
+ }
+
+ op->sk = sk;
+ op->ifindex = ifindex;
+
+ /* initialize uninitialized (kzalloc) structure */
+ init_timer(&op->timer);
+
+ /* init throttle timer for RX_CHANGED */
+ init_timer(&op->thrtimer);
+
+ /* handler for rx timeouts */
+ op->timer.function = bcm_rx_timeout_handler;
+
+ /* timer.data points to this op-structure */
+ op->timer.data = (unsigned long)op;
+
+ /* handler for RX_CHANGED throttle timeouts */
+ op->thrtimer.function = bcm_rx_thr_handler;
+
+ /* timer.data points to this op-structure */
+ op->thrtimer.data = (unsigned long)op;
+
+ /* mark disabled timer */
+ op->thrtimer.expires = 0;
+
+ /* add this bcm_op to the list of the rx_ops */
+ list_add(&op->list, &bo->rx_ops);
+
+ /* call can_rx_register() */
+ do_rx_register = 1;
+
+ } /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+ /* check flags */
+ op->flags = msg_head->flags;
+
+ if (op->flags & RX_RTR_FRAME) {
+
+ /* no timers in RTR-mode */
+ del_timer(&op->thrtimer);
+ del_timer(&op->timer);
+
+ /*
+ * funny feature in RX(!)_SETUP only for RTR-mode:
+ * copy can_id into frame BUT without RTR-flag to
+ * prevent a full-load-loopback-test ... ;-]
+ */
+ if ((op->flags & TX_CP_CAN_ID) ||
+ (op->frames[0].can_id == op->can_id))
+ op->frames[0].can_id = op->can_id & ~CAN_RTR_FLAG;
+
+ } else {
+ if (op->flags & SETTIMER) {
+
+ /* set timer value */
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->ival2;
+ op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+ op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+ DBG("RX_SETUP: SETTIMER j_ival1=%ld j_ival2=%ld\n",
+ op->j_ival1, op->j_ival2);
+
+ /* disable an active timer due to zero value? */
+ if (!op->j_ival1) {
+ del_timer(&op->timer);
+ DBG("RX_SETUP: disabled timer rx timeouts.\n");
+ }
+
+ /* free currently blocked msgs ? */
+ if (op->thrtimer.expires) {
+ DBG("RX_SETUP: unblocking throttled msgs.\n");
+ del_timer(&op->thrtimer);
+ /* send blocked msgs hereafter */
+ op->thrtimer.expires = jiffies + 2;
+ add_timer(&op->thrtimer);
+ }
+ /*
+ * if (op->j_ival2) is zero, no (new) throttling
+ * will happen. For details see functions
+ * bcm_rx_update_and_send() and bcm_rx_thr_handler()
+ */
+ }
+
+ if ((op->flags & STARTTIMER) && op->j_ival1) {
+
+ del_timer(&op->timer);
+ op->timer.expires = jiffies + op->j_ival1;
+
+ DBG("RX_SETUP: adding timer ival1. func=%p data=%p"
+ " exp=0x%08X\n",
+ (char *) op->timer.function,
+ (char *) op->timer.data,
+ (unsigned int) op->timer.expires);
+
+ add_timer(&op->timer);
+ }
+ }
+
+ /* now we can register for can_ids, if we added a new bcm_op */
+ if (do_rx_register) {
+ DBG("RX_SETUP: can_rx_register() for can_id %03X. "
+ "rx_op is %p\n", op->can_id, op);
+
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (dev) {
+ err = can_rx_register(dev, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op,
+ IDENT);
+
+ op->rx_reg_dev = dev;
+ dev_put(dev);
+ }
+
+ } else
+ err = can_rx_register(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op, IDENT);
+ if (err) {
+ /* this bcm rx op is broken -> remove it */
+ list_del(&op->list);
+ bcm_remove_op(op);
+ return err;
+ }
+ }
+
+ return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
+ */
+static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
+{
+ struct sk_buff *skb;
+ struct net_device *dev;
+ int err;
+
+ /* just copy and send one can_frame */
+
+ if (!ifindex) /* we need a real device to send frames */
+ return -ENODEV;
+
+ skb = alloc_skb(CFSIZ, GFP_KERNEL);
+
+ if (!skb)
+ return -ENOMEM;
+
+ err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ DBG_FRAME("BCM: TX_SEND: sending frame",
+ (struct can_frame *)skb->data);
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ skb->dev = dev;
+ skb->sk = sk;
+ can_send(skb, 1); /* send with loopback */
+ dev_put(dev);
+
+ return CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_sendmsg - process BCM commands (opcodes) from the userspace
+ */
+static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+ struct bcm_msg_head msg_head;
+ int ret; /* read bytes or error codes as return value */
+
+ if (!bo->bound) {
+ DBG("sock %p not bound\n", sk);
+ return -ENOTCONN;
+ }
+
+ /* check for alternative ifindex for this bcm_op */
+
+ if (!ifindex && msg->msg_name) {
+ /* no bound device as default => check msg_name */
+ struct sockaddr_can *addr =
+ (struct sockaddr_can *)msg->msg_name;
+
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ ifindex = addr->can_ifindex; /* ifindex from sendto() */
+
+ if (ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev) {
+ DBG("device %d not found\n", ifindex);
+ return -ENODEV;
+ }
+
+ if (dev->type != ARPHRD_CAN) {
+ DBG("device %d no CAN device\n", ifindex);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ dev_put(dev);
+ }
+ }
+
+ /* read message head information */
+
+ ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ);
+ if (ret < 0)
+ return ret;
+
+ DBG("opcode %d for can_id %03X\n", msg_head.opcode, msg_head.can_id);
+
+ lock_sock(sk);
+
+ switch (msg_head.opcode) {
+
+ case TX_SETUP:
+ ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case RX_SETUP:
+ ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
+ break;
+
+ case TX_DELETE:
+ if (bcm_delete_tx_op(&bo->tx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case RX_DELETE:
+ if (bcm_delete_rx_op(&bo->rx_ops, msg_head.can_id, ifindex))
+ ret = MHSIZ;
+ else
+ ret = -EINVAL;
+ break;
+
+ case TX_READ:
+ /* reuse msg_head for the reply to TX_READ */
+ msg_head.opcode = TX_STATUS;
+ ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
+ break;
+
+ case RX_READ:
+ /* reuse msg_head for the reply to RX_READ */
+ msg_head.opcode = RX_STATUS;
+ ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
+ break;
+
+ case TX_SEND:
+ /* we need at least one can_frame */
+ if (msg_head.nframes < 1)
+ ret = -EINVAL;
+ else
+ ret = bcm_tx_send(msg, ifindex, sk);
+ break;
+
+ default:
+ DBG("Unknown opcode %d\n", msg_head.opcode);
+ ret = -EINVAL;
+ break;
+ }
+
+ release_sock(sk);
+
+ return ret;
+}
+
+/*
+ * notification handler for netdevice status changes
+ */
+static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
+ void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
+ struct sock *sk = &bo->sk;
+ struct bcm_op *op;
+ int notify_enodev = 0;
+
+ DBG("msg %ld for dev %p (%s idx %d) sk %p bo->ifindex %d\n",
+ msg, dev, dev->name, dev->ifindex, sk, bo->ifindex);
+
+ if (dev->nd_net != &init_net)
+ return NOTIFY_DONE;
+
+ if (dev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+ /* remove device specific receive entries */
+ list_for_each_entry(op, &bo->rx_ops, list)
+ if (op->rx_reg_dev == dev)
+ bcm_rx_unreg(dev, op);
+
+ /* remove device reference, if this is our bound device */
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ notify_enodev = 1;
+ }
+
+ release_sock(sk);
+
+ if (notify_enodev) {
+ sk->sk_err = ENODEV;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ break;
+
+ case NETDEV_DOWN:
+ if (bo->bound && bo->ifindex == dev->ifindex) {
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * initial settings for all BCM sockets to be set at socket creation time
+ */
+static int bcm_init(struct sock *sk)
+{
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ bo->bound = 0;
+ bo->ifindex = 0;
+ bo->dropped_usr_msgs = 0;
+ bo->bcm_proc_read = NULL;
+
+ INIT_LIST_HEAD(&bo->tx_ops);
+ INIT_LIST_HEAD(&bo->rx_ops);
+
+ /* set notifier */
+ bo->notifier.notifier_call = bcm_notifier;
+
+ register_netdevice_notifier(&bo->notifier);
+
+ return 0;
+}
+
+/*
+ * standard socket functions
+ */
+static int bcm_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op, *next;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ /* remove bcm_ops, timer, rx_unregister(), etc. */
+
+ unregister_netdevice_notifier(&bo->notifier);
+
+ lock_sock(sk);
+
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
+ DBG("removing tx_op %p for can_id %03X\n", op, op->can_id);
+ bcm_remove_op(op);
+ }
+
+ list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+ DBG("removing rx_op %p for can_id %03X\n", op, op->can_id);
+
+ /*
+ * Don't care if we're bound or not (due to netdev problems)
+ * can_rx_unregister() is always a save thing to do here.
+ */
+ if (op->ifindex) {
+ /*
+ * Only remove subscriptions that had not
+ * been removed due to NETDEV_UNREGISTER
+ * in bcm_notifier()
+ */
+ if (op->rx_reg_dev) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, op->ifindex);
+ if (dev) {
+ bcm_rx_unreg(dev, op);
+ dev_put(dev);
+ }
+ }
+ } else
+ can_rx_unregister(NULL, op->can_id,
+ REGMASK(op->can_id),
+ bcm_rx_handler, op);
+
+ bcm_remove_op(op);
+ }
+
+ /* remove procfs entry */
+ if (proc_dir && bo->bcm_proc_read)
+ remove_proc_entry(bo->procname, proc_dir);
+
+ /* remove device reference */
+ if (bo->bound) {
+ bo->bound = 0;
+ bo->ifindex = 0;
+ }
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+ int flags)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct bcm_sock *bo = bcm_sk(sk);
+
+ if (bo->bound)
+ return -EISCONN;
+
+ /* bind a device to this socket */
+ if (addr->can_ifindex) {
+ struct net_device *dev;
+
+ dev = dev_get_by_index(&init_net, addr->can_ifindex);
+ if (!dev) {
+ DBG("could not find device index %d\n",
+ addr->can_ifindex);
+ return -ENODEV;
+ }
+
+ if (dev->type != ARPHRD_CAN) {
+ DBG("device %d no CAN device\n", addr->can_ifindex);
+ dev_put(dev);
+ return -ENODEV;
+ }
+
+ bo->ifindex = dev->ifindex;
+ dev_put(dev);
+
+ DBG("socket %p bound to device %s (idx %d)\n",
+ sock, dev->name, dev->ifindex);
+
+ } else {
+ /* no interface reference for ifindex = 0 ('any' CAN device) */
+ bo->ifindex = 0;
+ }
+
+ bo->bound = 1;
+
+ if (proc_dir) {
+ /* unique socket address as filename */
+ sprintf(bo->procname, "%p", sock);
+ bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
+ proc_dir,
+ bcm_read_proc, sk);
+ }
+
+ return 0;
+}
+
+static int bcm_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 error = 0;
+ int noblock;
+ int err;
+
+ DBG("socket %p, sk %p\n", sock, sk);
+
+ noblock = flags & MSG_DONTWAIT;
+ flags &= ~MSG_DONTWAIT;
+ skb = skb_recv_datagram(sk, flags, noblock, &error);
+ if (!skb)
+ return error;
+
+ DBG("delivering skbuff %p\n", skb);
+ DBG_SKB(skb);
+
+ if (skb->len < size)
+ size = skb->len;
+
+ err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (err < 0) {
+ skb_free_datagram(sk, skb);
+ return err;
+ }
+
+ sock_recv_timestamp(msg, sk, skb);
+
+ if (msg->msg_name) {
+ msg->msg_namelen = sizeof(struct sockaddr_can);
+ memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ }
+
+ DBG("freeing sock %p, skbuff %p\n", sk, skb);
+ skb_free_datagram(sk, skb);
+
+ return size;
+}
+
+static unsigned int bcm_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ DBG("socket %p\n", sock);
+
+ mask = datagram_poll(file, sock, wait);
+ return mask;
+}
+
+static struct proto_ops bcm_ops __read_mostly = {
+ .family = PF_CAN,
+ .release = bcm_release,
+ .bind = sock_no_bind,
+ .connect = bcm_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = bcm_poll,
+ .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = bcm_sendmsg,
+ .recvmsg = bcm_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto bcm_proto __read_mostly = {
+ .name = "CAN_BCM",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct bcm_sock),
+ .init = bcm_init,
+};
+
+static struct can_proto bcm_can_proto __read_mostly = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_BCM,
+ .capability = -1,
+ .ops = &bcm_ops,
+ .prot = &bcm_proto,
+};
+
+static int __init bcm_module_init(void)
+{
+ int err;
+
+ printk(banner);
+
+ err = can_proto_register(&bcm_can_proto);
+ if (err < 0) {
+ printk(KERN_ERR "can: registration of bcm protocol failed\n");
+ return err;
+ }
+
+ /* create /proc/net/can-bcm directory */
+ proc_dir = proc_mkdir("can-"IDENT, init_net.proc_net);
+
+ if (proc_dir)
+ proc_dir->owner = THIS_MODULE;
+
+ return 0;
+}
+
+static void __exit bcm_module_exit(void)
+{
+ can_proto_unregister(&bcm_can_proto);
+
+ if (proc_dir)
+ proc_net_remove(&init_net, "can-"IDENT);
+}
+
+module_init(bcm_module_init);
+module_exit(bcm_module_exit);
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
` (3 preceding siblings ...)
2007-10-02 13:10 ` [PATCH 4/7] CAN: Add broadcast manager (bcm) protocol Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 14:20 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 6/7] CAN: Add maintainer entries Urs Thuermann
2007-10-02 13:10 ` [PATCH 7/7] CAN: Add documentation Urs Thuermann
6 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 05-can-vcan-driver.diff --]
[-- Type: text/plain, Size: 9042 bytes --]
This patch adds the virtual CAN bus (vcan) network driver.
The vcan device is just a loopback device for CAN frames, no
real CAN hardware is involved.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
drivers/net/Makefile | 1
drivers/net/can/Kconfig | 25 +++++
drivers/net/can/Makefile | 5 +
drivers/net/can/vcan.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++
net/can/Kconfig | 3
5 files changed, 241 insertions(+)
Index: net-2.6.24/drivers/net/Makefile
===================================================================
--- net-2.6.24.orig/drivers/net/Makefile 2007-09-20 23:46:01.000000000 +0200
+++ net-2.6.24/drivers/net/Makefile 2007-10-02 12:03:30.000000000 +0200
@@ -12,6 +12,7 @@
obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_CHELSIO_T3) += cxgb3/
obj-$(CONFIG_EHEA) += ehea/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_ATL1) += atl1/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
Index: net-2.6.24/drivers/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/drivers/net/can/Kconfig 2007-10-02 12:03:30.000000000 +0200
@@ -0,0 +1,25 @@
+menu "CAN Device Drivers"
+ depends on CAN
+
+config CAN_VCAN
+ tristate "Virtual Local CAN Interface (vcan)"
+ depends on CAN
+ default N
+ ---help---
+ Similar to the network loopback devices, vcan offers a
+ virtual local CAN interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called vcan.
+
+config CAN_DEBUG_DEVICES
+ bool "CAN devices debugging messages"
+ depends on CAN
+ default N
+ ---help---
+ Say Y here if you want the CAN device drivers to produce a bunch of
+ debug messages to the system log. Select this if you are having
+ a problem with CAN support and want to see more of what is going
+ on.
+
+endmenu
Index: net-2.6.24/drivers/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/drivers/net/can/Makefile 2007-10-02 12:03:30.000000000 +0200
@@ -0,0 +1,5 @@
+#
+# Makefile for the Linux Controller Area Network drivers.
+#
+
+obj-$(CONFIG_CAN_VCAN) += vcan.o
Index: net-2.6.24/drivers/net/can/vcan.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/drivers/net/can/vcan.c 2007-10-02 12:03:30.000000000 +0200
@@ -0,0 +1,207 @@
+/*
+ * vcan.c - Virtual CAN interface
+ *
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <net/rtnetlink.h>
+
+static __initdata const char banner[] =
+ KERN_INFO "vcan: Virtual CAN interface driver\n";
+
+MODULE_DESCRIPTION("virtual CAN interface");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int debug;
+module_param(debug, int, S_IRUGO);
+#endif
+
+/* To be moved to linux/can/dev.h */
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(fmt, args...) (debug & 1 ? \
+ printk(KERN_DEBUG "vcan %s: " fmt, \
+ __func__, ##args) : 0)
+#else
+#define DBG(fmt, args...)
+#endif
+
+
+/*
+ * CAN test feature:
+ * Enable the echo on driver level for testing the CAN core echo modes.
+ * See Documentation/networking/can.txt for details.
+ */
+
+static int echo; /* echo testing. Default: 0 (Off) */
+module_param(echo, int, S_IRUGO);
+MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
+
+
+static int vcan_open(struct net_device *dev)
+{
+ DBG("%s: interface up\n", dev->name);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int vcan_stop(struct net_device *dev)
+{
+ DBG("%s: interface down\n", dev->name);
+
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ DBG("received skbuff on interface %d\n", dev->ifindex);
+
+ netif_rx(skb);
+}
+
+static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ int loop;
+
+ DBG("sending skbuff on interface %s\n", dev->name);
+
+ stats->tx_packets++;
+ stats->tx_bytes += skb->len;
+
+ /* set flag whether this packet has to be looped back */
+ loop = skb->pkt_type == PACKET_LOOPBACK;
+
+ if (!echo) {
+ /* no echo handling available inside this driver */
+
+ if (loop) {
+ /*
+ * only count the packets here, because the
+ * CAN core already did the echo for us
+ */
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+ }
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* perform standard echo handling for CAN network interfaces */
+
+ if (loop) {
+ struct sock *srcsk = skb->sk;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return 0;
+
+ /* receive with packet counting */
+ skb->sk = srcsk;
+ vcan_rx(skb, dev);
+ } else {
+ /* no looped packets => no counting */
+ kfree_skb(skb);
+ }
+ return 0;
+}
+
+static void vcan_setup(struct net_device *dev)
+{
+ DBG("dev %s\n", dev->name);
+
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_NOARP;
+
+ /* set flags according to driver capabilities */
+ if (echo)
+ dev->flags |= IFF_ECHO;
+
+ dev->open = vcan_open;
+ dev->stop = vcan_stop;
+ dev->hard_start_xmit = vcan_tx;
+ dev->destructor = free_netdev;
+}
+
+static struct rtnl_link_ops vcan_link_ops __read_mostly = {
+ .kind = "vcan",
+ .setup = vcan_setup,
+};
+
+static __init int vcan_init_module(void)
+{
+ printk(banner);
+
+ if (echo)
+ printk(KERN_INFO "vcan: enabled echo on driver level.\n");
+
+ return rtnl_link_register(&vcan_link_ops);
+}
+
+static __exit void vcan_cleanup_module(void)
+{
+ rtnl_link_unregister(&vcan_link_ops);
+}
+
+module_init(vcan_init_module);
+module_exit(vcan_cleanup_module);
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- net-2.6.24.orig/net/can/Kconfig 2007-10-02 12:03:28.000000000 +0200
+++ net-2.6.24/net/can/Kconfig 2007-10-02 12:03:30.000000000 +0200
@@ -47,3 +47,6 @@
Say Y here if you want the CAN core to produce a bunch of debug
messages to the system log. Select this if you are having a
problem with CAN support and want to see more of what is going on.
+
+
+source "drivers/net/can/Kconfig"
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 6/7] CAN: Add maintainer entries
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
` (4 preceding siblings ...)
2007-10-02 13:10 ` [PATCH 5/7] CAN: Add virtual CAN netdevice driver Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
2007-10-02 13:10 ` [PATCH 7/7] CAN: Add documentation Urs Thuermann
6 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 06-can-maintainers.diff --]
[-- Type: text/plain, Size: 1734 bytes --]
This patch adds entries in the CREDITS and MAINTAINERS file for CAN.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
CREDITS | 16 ++++++++++++++++
MAINTAINERS | 9 +++++++++
2 files changed, 25 insertions(+)
Index: net-2.6.24/CREDITS
===================================================================
--- net-2.6.24.orig/CREDITS 2007-09-20 06:37:28.000000000 +0200
+++ net-2.6.24/CREDITS 2007-10-02 12:03:43.000000000 +0200
@@ -1347,6 +1347,14 @@
S: 5623 HZ Eindhoven
S: The Netherlands
+N: Oliver Hartkopp
+E: oliver.hartkopp@volkswagen.de
+W: http://www.volkswagen.de
+D: Controller Area Network (network layer core)
+S: Brieffach 1776
+S: 38436 Wolfsburg
+S: Germany
+
N: Andrew Haylett
E: ajh@primag.co.uk
D: Selection mechanism
@@ -3300,6 +3308,14 @@
S: F-35042 Rennes Cedex
S: France
+N: Urs Thuermann
+E: urs.thuermann@volkswagen.de
+W: http://www.volkswagen.de
+D: Controller Area Network (network layer core)
+S: Brieffach 1776
+S: 38436 Wolfsburg
+S: Germany
+
N: Jon Tombs
E: jon@gte.esi.us.es
W: http://www.esi.us.es/~jon
Index: net-2.6.24/MAINTAINERS
===================================================================
--- net-2.6.24.orig/MAINTAINERS 2007-09-28 18:01:52.000000000 +0200
+++ net-2.6.24/MAINTAINERS 2007-10-02 12:03:43.000000000 +0200
@@ -991,6 +991,15 @@
L: video4linux-list@redhat.com
S: Maintained
+CAN NETWORK LAYER
+P: Urs Thuermann
+M: urs.thuermann@volkswagen.de
+P: Oliver Hartkopp
+M: oliver.hartkopp@volkswagen.de
+L: socketcan-core@lists.berlios.de
+W: http://developer.berlios.de/projects/socketcan/
+S: Maintained
+
CALGARY x86-64 IOMMU
P: Muli Ben-Yehuda
M: muli@il.ibm.com
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 7/7] CAN: Add documentation
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
` (5 preceding siblings ...)
2007-10-02 13:10 ` [PATCH 6/7] CAN: Add maintainer entries Urs Thuermann
@ 2007-10-02 13:10 ` Urs Thuermann
6 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 13:10 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, YOSHIFUJI Hideaki, Arnaldo Carvalho de Melo,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
[-- Attachment #1: 07-can-doc.diff --]
[-- Type: text/plain, Size: 29570 bytes --]
This patch adds documentation for the PF_CAN protocol family.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
Documentation/networking/00-INDEX | 2
Documentation/networking/can.txt | 637 ++++++++++++++++++++++++++++++++++++++
2 files changed, 639 insertions(+)
Index: net-2.6.24/Documentation/networking/can.txt
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/Documentation/networking/can.txt 2007-10-02 08:43:50.000000000 +0200
@@ -0,0 +1,637 @@
+============================================================================
+
+can.txt
+
+Readme file for the Controller Area Network Protocol Family (aka Socket CAN)
+
+This file contains
+
+ 1 Overview / What is Socket CAN
+
+ 2 Motivation / Why using the socket API
+
+ 3 Socket CAN concept
+ 3.1 receive lists
+ 3.2 local loopback of sent frames
+ 3.3 network security issues (capabilities)
+ 3.4 network problem notifications
+
+ 4 How to use Socket CAN
+ 4.1 RAW protocol sockets with can_filters (SOCK_RAW)
+ 4.1.1 RAW socket option CAN_RAW_FILTER
+ 4.1.2 RAW socket option CAN_RAW_ERR_FILTER
+ 4.1.3 RAW socket option CAN_RAW_LOOPBACK
+ 4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
+ 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+ 4.3 connected transport protocols (SOCK_SEQPACKET)
+ 4.4 unconnected transport protocols (SOCK_DGRAM)
+
+ 5 Socket CAN core module
+ 5.1 can.ko module params
+ 5.2 procfs content
+ 5.3 writing own CAN protocol modules
+
+ 6 CAN network drivers
+ 6.1 general settings
+ 6.2 local loopback of sent frames
+ 6.3 CAN controller hardware filters
+ 6.4 currently supported CAN hardware
+ 6.5 todo
+
+ 7 Credits
+
+============================================================================
+
+1. Overview / What is Socket CAN
+--------------------------------
+
+The socketcan package is an implementation of CAN protocols
+(Controller Area Network) for Linux. CAN is a networking technology
+which has widespread use in automation, embedded devices, and
+automotive fields. While there have been other CAN implementations
+for Linux based on character devices, Socket CAN uses the Berkeley
+socket API, the Linux network stack and implements the CAN device
+drivers as network interfaces. The CAN socket API has been designed
+as similar as possible to the TCP/IP protocols to allow programmers,
+familiar with network programming, to easily learn how to use CAN
+sockets.
+
+2. Motivation / Why using the socket API
+----------------------------------------
+
+There have been CAN implementations for Linux before Socket CAN so the
+question arises, why we have started another project. Most existing
+implementations come as a device driver for some CAN hardware, they
+are based on character devices and provide comparatively little
+functionality. Usually, there is only a hardware-specific device
+driver which provides a character device interface to send and
+receive raw CAN frames, directly to/from the controller hardware.
+Queueing of frames and higher-level transport protocols like ISO-TP
+have to be implemented in user space applications. Also, most
+character-device implementations support only one single process to
+open the device at a time, similar to a serial interface. Exchanging
+the CAN controller requires employment of another device driver and
+often the need for adaption of large parts of the application to the
+new driver's API.
+
+Socket CAN was designed to overcome all of these limitations. A new
+protocol family has been implemented which provides a socket interface
+to user space applications and which builds upon the Linux network
+layer, so to use all of the provided queueing functionality. A device
+driver for CAN controller hardware registers itself with the Linux
+network layer as a network device, so that CAN frames from the
+controller can be passed up to the network layer and on to the CAN
+protocol family module and also vice-versa. Also, the protocol family
+module provides an API for transport protocol modules to register, so
+that any number of transport protocols can be loaded or unloaded
+dynamically. In fact, the can core module alone does not provide any
+protocol and cannot be used without loading at least one additional
+protocol module. Multiple sockets can be opened at the same time,
+on different or the same protocol module and they can listen/send
+frames on different or the same CAN IDs. Several sockets listening on
+the same interface for frames with the same CAN ID are all passed the
+same received matching CAN frames. An application wishing to
+communicate using a specific transport protocol, e.g. ISO-TP, just
+selects that protocol when opening the socket, and then can read and
+write application data byte streams, without having to deal with
+CAN-IDs, frames, etc.
+
+Similar functionality visible from user-space could be provided by a
+character device, too, but this would lead to a technically inelegant
+solution for a couple of reasons:
+
+* Intricate usage. Instead of passing a protocol argument to
+ socket(2) and using bind(2) to select a CAN interface and CAN ID, an
+ application would have to do all these operations using ioctl(2)s.
+
+* Code duplication. A character device cannot make use of the Linux
+ network queueing code, so all that code would have to be duplicated
+ for CAN networking.
+
+* Abstraction. In most existing character-device implementations, the
+ hardware-specific device driver for a CAN controller directly
+ provides the character device for the application to work with.
+ This is at least very unusual in Unix systems for both, char and
+ block devices. For example you don't have a character device for a
+ certain UART of a serial interface, a certain sound chip in your
+ computer, a SCSI or IDE controller providing access to your hard
+ disk or tape streamer device. Instead, you have abstraction layers
+ which provide a unified character or block device interface to the
+ application on the one hand, and a interface for hardware-specific
+ device drivers on the other hand. These abstractions are provided
+ by subsystems like the tty layer, the audio subsystem or the SCSI
+ and IDE subsystems for the devices mentioned above.
+
+ The easiest way to implement a CAN device driver is as a character
+ device without such a (complete) abstraction layer, as is done by most
+ existing drivers. The right way, however, would be to add such a
+ layer with all the functionality like registering for certain CAN
+ IDs, supporting several open file descriptors and (de)multiplexing
+ CAN frames between them, (sophisticated) queueing of CAN frames, and
+ providing an API for device drivers to register with. However, then
+ it would be no more difficult, or may be even easier, to use the
+ networking framework provided by the Linux kernel, and this is what
+ Socket CAN does.
+
+ The use of the networking framework of the Linux kernel is just the
+ natural and most appropriate way to implement CAN for Linux.
+
+3. Socket CAN concept
+---------------------
+
+ As described in chapter 2 it is the main goal of Socket CAN to
+ provide a socket interface to user space applications which builds
+ upon the Linux network layer. In contrast to the commonly known
+ TCP/IP and ethernet networking, the CAN bus is a broadcast-only(!)
+ medium that has no MAC-layer addressing like ethernet. The CAN-identifier
+ (can_id) is used for arbitration on the CAN-bus. Therefore the CAN-IDs
+ have to be chosen uniquely on the bus. When designing a CAN-ECU
+ network the CAN-IDs are mapped to be sent by a specific ECU.
+ For this reason a CAN-ID can be treated best as a kind of source address.
+
+ 3.1 receive lists
+
+ The network transparent access of multiple applications leads to the
+ problem that different applications may be interested in the same
+ CAN-IDs from the same CAN network interface. The Socket CAN core
+ module - which implements the protocol family CAN - provides several
+ high efficient receive lists for this reason. If e.g. a user space
+ application opens a CAN RAW socket, the raw protocol module itself
+ requests the (range of) CAN-IDs from the Socket CAN core that are
+ requested by the user. The subscription and unsubscription of
+ CAN-IDs can be done for specific CAN interfaces or for all(!) known
+ CAN interfaces with the can_rx_(un)register() functions provided to
+ CAN protocol modules by the SocketCAN core (see chapter 5).
+ To optimize the CPU usage at runtime the receive lists are split up
+ into several specific lists per device that match the requested
+ filter complexity for a given use-case.
+
+ 3.2 local loopback of sent frames
+
+ As known from other networking concepts the data exchanging
+ applications may run on the same or different nodes without any
+ change (except for the according addressing information):
+
+ ___ ___ ___ _______ ___
+ | _ | | _ | | _ | | _ _ | | _ |
+ ||A|| ||B|| ||C|| ||A| |B|| ||C||
+ |___| |___| |___| |_______| |___|
+ | | | | |
+ -----------------(1)- CAN bus -(2)---------------
+
+ To ensure that application A receives the same information in the
+ example (2) as it would receive in example (1) there is need for
+ some kind of local loopback of the sent CAN frames on the appropriate
+ node.
+
+ The Linux network devices (by default) just can handle the
+ transmission and reception of media dependent frames. Due to the
+ arbritration on the CAN bus the transmission of a low prio CAN-ID
+ may be delayed by the reception of a high prio CAN frame. To
+ reflect the correct* traffic on the node the loopback of the sent
+ data has to be performed right after a successful transmission. If
+ the CAN network interface is not capable of performing the loopback for
+ some reason the SocketCAN core can do this task as a fallback solution.
+ See chapter 6.2 for details (recommended).
+
+ The loopback functionality is enabled by default to reflect standard
+ networking behaviour for CAN applications. Due to some requests from
+ the RT-SocketCAN group the loopback optionally may be disabled for each
+ separate socket. See sockopts from the CAN RAW sockets in chapter 4.1.
+
+ * = you really like to have this when you're running analyser tools
+ like 'candump' or 'cansniffer' on the (same) node.
+
+ 3.3 network security issues (capabilities)
+
+ The Controller Area Network is a local field bus transmitting only
+ broadcast messages without any routing and security concepts.
+ In the majority of cases the user application has to deal with
+ raw CAN frames. Therefore it might be reasonable NOT to restrict
+ the CAN access only to the user root, as known from other networks.
+ Since the currently implemented CAN_RAW and CAN_BCM sockets can only
+ send and receive frames to/from CAN interfaces it does not affect
+ security of others networks to allow all users to access the CAN.
+ To enable non-root users to access CAN_RAW and CAN_BCM protocol
+ sockets the Kconfig options CAN_RAW_USER and/or CAN_BCM_USER may be
+ selected at kernel compile time.
+
+ 3.4 network problem notifications
+
+ The use of the CAN bus may lead to several problems on the physical
+ and media access control layer. Detecting and logging of these lower
+ layer problems is a vital requirement for CAN users to identify
+ hardware issues on the physical transceiver layer as well as
+ arbitration problems and error frames caused by the different
+ ECUs. The occurrence of detected errors are important for diagnosis
+ and have to be logged together with the exact timestamp. For this
+ reason the CAN interface driver can generate so called Error Frames
+ that can optionally be passed to the user application in the same
+ way as other CAN frames. Whenever an error on the physical layer
+ or the MAC layer is detected (e.g. by the CAN controller) the driver
+ creates an appropriate error frame. Error frames can be requested by
+ the user application using the common CAN filter mechanisms. Inside
+ this filter definition the (interested) type of errors may be
+ selected. The reception of error frames is disabled by default.
+
+4. How to use Socket CAN
+------------------------
+
+ Like TCP/IP, you first need to open a socket for communicating over a
+ CAN network. Since Socket CAN implements a new protocol family, you
+ need to pass PF_CAN as the first argument to the socket(2) system
+ call. Currently, there are two CAN protocols to choose from, the raw
+ socket protocol and the broadcast manager (BCM). So to open a socket,
+ you would write
+
+ s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+
+ and
+
+ s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
+
+ respectively. After the successful creation of the socket, you would
+ normally use the bind(2) system call to bind the socket to a CAN
+ interface (which is different from TCP/IP due to different addressing
+ - see chapter 3). After binding (CAN_RAW) or connecting (CAN_BCM)
+ the socket, you can read(2) and write(2) from/to the socket or use
+ send(2), sendto(2), sendmsg(2) and the recv* counterpart operations
+ on the socket as usual. There are also CAN specific socket options
+ described below.
+
+ The basic CAN frame structure and the sockaddr structure are defined
+ in include/linux/can.h:
+
+ struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+ };
+
+ The alignment of the (linear) payload data[] to a 64bit boundary
+ allows the user to define own structs and unions to easily access the
+ CAN payload. There is no given byteorder on the CAN bus by
+ default. A read(2) system call on a CAN_RAW socket transfers a
+ struct can_frame to the user space.
+
+ The sockaddr_can structure has an interface index like the
+ PF_PACKET socket, that also binds to a specific interface:
+
+ struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct { canid_t rx_id, tx_id; } tp16;
+ struct { canid_t rx_id, tx_id; } tp20;
+ struct { canid_t rx_id, tx_id; } mcnet;
+ struct { canid_t rx_id, tx_id; } isotp;
+ } can_addr;
+ };
+
+ To determine the interface index an appropriate ioctl() has to
+ be used (example for CAN_RAW sockets without error checking):
+
+ int s;
+ struct sockaddr_can addr;
+ struct ifreq ifr;
+
+ s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+
+ strcpy(ifr.ifr_name, "can0" );
+ ioctl(s, SIOCGIFINDEX, &ifr);
+
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ bind(s, (struct sockaddr *)&addr, sizeof(addr));
+
+ (..)
+
+ To bind a socket to all(!) CAN interfaces the interface index must
+ be 0 (zero). In this case the socket receives CAN frames from every
+ enabled CAN interface. To determine the originating CAN interface
+ the system call recvfrom(2) may be used instead of read(2). To send
+ on a socket that is bound to 'any' interface sendto(2) is needed to
+ specify the outgoing interface.
+
+ Reading CAN frames from a bound CAN_RAW socket (see above) consists
+ of reading a struct can_frame:
+
+ struct can_frame frame;
+
+ nbytes = read(s, &frame, sizeof(struct can_frame));
+
+ if (nbytes < 0) {
+ perror("can raw socket read");
+ return 1;
+ }
+
+ /* paraniod check ... */
+ if (nbytes < sizeof(struct can_frame)) {
+ fprintf(stderr, "read: incomplete CAN frame\n");
+ return 1;
+ }
+
+ /* do something with the received CAN frame */
+
+ Writing CAN frames can be done similarly, with the write(2) system call:
+
+ nbytes = write(s, &frame, sizeof(struct can_frame));
+
+ When the CAN interface is bound to 'any' existing CAN interface
+ (addr.can_ifindex = 0) it is recommended to use recvfrom(2) if the
+ information about the originating CAN interface is needed:
+
+ struct sockaddr_can addr;
+ struct ifreq ifr;
+ socklen_t len = sizeof(addr);
+ struct can_frame frame;
+
+ nbytes = recvfrom(s, &frame, sizeof(struct can_frame),
+ 0, (struct sockaddr*)&addr, &len);
+
+ /* get interface name of the received CAN frame */
+ ifr.ifr_ifindex = addr.can_ifindex;
+ ioctl(s, SIOCGIFNAME, &ifr);
+ printf("Received a CAN frame from interface %s", ifr.ifr_name);
+
+ To write CAN frames on sockets bound to 'any' CAN interface the
+ outgoing interface has to be defined certainly.
+
+ strcpy(ifr.ifr_name, "can0");
+ ioctl(s, SIOCGIFINDEX, &ifr);
+ addr.can_ifindex = ifr.ifr_ifindex;
+ addr.can_family = AF_CAN;
+
+ nbytes = sendto(s, &frame, sizeof(struct can_frame),
+ 0, (struct sockaddr*)&addr, sizeof(addr));
+
+ 4.1 RAW protocol sockets with can_filters (SOCK_RAW)
+
+ Using CAN_RAW sockets is extensively comparable to the commonly
+ known access to CAN character devices. To meet the new possibilities
+ provided by the multi user SocketCAN approach, some reasonable
+ defaults are set at RAW socket binding time:
+
+ - The filters are set to exactly one filter receiving everything
+ - The socket only receives valid data frames (=> no error frames)
+ - The loopback of sent CAN frames is enabled (see chapter 3.2)
+ - The socket does not receive its own sent frames (in loopback mode)
+
+ These default settings may be changed before or after binding the socket.
+ To use the referenced definitions of the socket options for CAN_RAW
+ sockets, include <linux/can/raw.h>.
+
+ 4.1.1 RAW socket option CAN_RAW_FILTER
+
+ The reception of CAN frames using CAN_RAW sockets can be controlled
+ by defining 0 .. n filters with the CAN_RAW_FILTER socket option.
+
+ The CAN filter structure is defined in include/linux/can.h:
+
+ struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+ };
+
+ A filter matches, when
+
+ <received_can_id> & mask == can_id & mask
+
+ which is analogous to known CAN controllers hardware filter semantics.
+ The filter can be inverted in this semantic, when the CAN_INV_FILTER
+ bit is set in can_id element of the can_filter structure. In
+ contrast to CAN controller hardware filters the user may set 0 .. n
+ receive filters for each open socket separately:
+
+ struct can_filter rfilter[2];
+
+ rfilter[0].can_id = 0x123;
+ rfilter[0].can_mask = CAN_SFF_MASK;
+ rfilter[1].can_id = 0x200;
+ rfilter[1].can_mask = 0x700;
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
+
+ To disable the reception of CAN frames on the selected CAN_RAW socket:
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
+
+ To set the filters to zero filters is quite obsolete as not read
+ data causes the raw socket to discard the received CAN frames. But
+ having this 'send only' use-case we may remove the receive list in the
+ Kernel to save a little (really a very little!) CPU usage.
+
+ 4.1.2 RAW socket option CAN_RAW_ERR_FILTER
+
+ As described in chapter 3.4 the CAN interface driver can generate so
+ called Error Frames that can optionally be passed to the user
+ application in the same way as other CAN frames. The possible
+ errors are divided into different error classes that may be filtered
+ using the appropriate error mask. To register for every possible
+ error condition CAN_ERR_MASK can be used as value for the error mask.
+ The values for the error mask are defined in linux/can/error.h .
+
+ can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+ &err_mask, sizeof(err_mask));
+
+ 4.1.3 RAW socket option CAN_RAW_LOOPBACK
+
+ To meet multi user needs the local loopback is enabled by default
+ (see chapter 3.2 for details). But in some embedded use-cases
+ (e.g. when only one application uses the CAN bus) this loopback
+ functionality can be disabled (separately for each socket):
+
+ int loopback = 0; /* 0 = disabled, 1 = enabled (default) */
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
+
+ 4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
+
+ When the local loopback is enabled, all the sent CAN frames are
+ looped back to the open CAN sockets that registered for the CAN
+ frames' CAN-ID on this given interface to meet the multi user
+ needs. The reception of the CAN frames on the same socket that was
+ sending the CAN frame is assumed to be unwanted and therefore
+ disabled by default. This default behaviour may be changed on
+ demand:
+
+ int recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
+ &recv_own_msgs, sizeof(recv_own_msgs));
+
+ 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+ 4.3 connected transport protocols (SOCK_SEQPACKET)
+ 4.4 unconnected transport protocols (SOCK_DGRAM)
+
+
+5. Socket CAN core module
+-------------------------
+
+ The Socket CAN core module implements the protocol family
+ PF_CAN. CAN protocol modules are loaded by the core module at
+ runtime. The core module provides an interface for CAN protocol
+ modules to subscribe needed CAN IDs (see chapter 3.1).
+
+ 5.1 can.ko module params
+
+ - stats_timer: To calculate the Socket CAN core statistics
+ (e.g. current/maximum frames per second) this 1 second timer is
+ invoked at can.ko module start time by default. This timer can be
+ disabled by using stattimer=0 on the module comandline.
+
+ - debug: When the Kconfig option CONFIG_CAN_DEBUG_CORE is set at
+ compile time, the debug output code is compiled into the module.
+ debug = 0x01 => print general debug information
+ debug = 0x02 => print content of processed CAN frames
+ debug = 0x04 => print content of processed socket buffers
+
+ It is possible to use ORed values e.g. 3 or 7 for an output of
+ all available debug information. Using 0x02 and 0x04 may flood
+ your kernel log - so be careful.
+
+ 5.2 procfs content
+
+ As described in chapter 3.1 the Socket CAN core uses several filter
+ lists to deliver received CAN frames to CAN protocol modules. These
+ receive lists, their filters and the count of filter matches can be
+ checked in the appropriate receive list. All entries contain the
+ device and a protocol module identifier:
+
+ foo@bar:~$ cat /proc/net/can/rcvlist_all
+
+ receive list 'rx_all':
+ (vcan3: no entry)
+ (vcan2: no entry)
+ (vcan1: no entry)
+ device can_id can_mask function userdata matches ident
+ vcan0 000 00000000 f88e6370 f6c6f400 0 raw
+ (any: no entry)
+
+ In this example an application requests any CAN traffic from vcan0.
+
+ rcvlist_all - list for unfiltered entries (no filter operations)
+ rcvlist_eff - list for single extended frame (EFF) entries
+ rcvlist_err - list for error frames masks
+ rcvlist_fil - list for mask/value filters
+ rcvlist_inv - list for mask/value filters (inverse semantic)
+ rcvlist_sff - list for single standard frame (SFF) entries
+
+ Additional procfs files in /proc/net/can
+
+ stats - Socket CAN core statistics (rx/tx frames, match ratios, ...)
+ reset_stats - manual statistic reset
+ version - prints the Socket CAN core version and the ABI version
+
+ 5.3 writing own CAN protocol modules
+
+ To implement a new protocol in the protocol family PF_CAN a new
+ protocol has to be defined in include/linux/can.h .
+ The prototypes and definitions to use the Socket CAN core can be
+ accessed by including include/linux/can/core.h .
+ In addition to functions that register the CAN protocol and the
+ CAN device notifier chain there are functions to subscribe CAN
+ frames received by CAN interfaces and to send CAN frames:
+
+ can_rx_register - subscribe CAN frames from a specific interface
+ can_rx_unregister - unsubscribe CAN frames from a specific interface
+ can_send - transmit a CAN frame (optional with local loopback)
+
+ For details see the kerneldoc documentation in net/can/af_can.c or
+ the source code of net/can/raw.c or net/can/bcm.c .
+
+6. CAN network drivers
+----------------------
+
+ Writing a CAN network device driver is much easier than writing a
+ CAN character device driver. Similar to other known network device
+ drivers you mainly have to deal with:
+
+ - TX: Put the CAN frame from the socket buffer to the CAN controller.
+ - RX: Put the CAN frame from the CAN controller to the socket buffer.
+
+ See e.g. at Documentation/networking/netdevices.txt . The differences
+ for writing CAN network device driver are described below:
+
+ 6.1 general settings
+
+ dev->type = ARPHRD_CAN; /* the netdevice hardware type */
+ dev->flags = IFF_NOARP; /* CAN has no arp */
+
+ dev->mtu = sizeof(struct can_frame);
+
+ The struct can_frame is the payload of each socket buffer in the
+ protocol family PF_CAN.
+
+ 6.2 local loopback of sent frames
+
+ As described in chapter 3.2 the CAN network device driver should
+ support a local loopback functionality similar to the local echo
+ e.g. of tty devices. In this case the driver flag IFF_ECHO has to be
+ set to prevent the PF_CAN core from locally echoing sent frames
+ (aka loopback) as fallback solution:
+
+ dev->flags = (IFF_NOARP | IFF_ECHO);
+
+ 6.3 CAN controller hardware filters
+
+ To reduce the interrupt load on deep embedded systems some CAN
+ controllers support the filtering of CAN IDs or ranges of CAN IDs.
+ These hardware filter capabilities vary from controller to
+ controller and have to be identified as not feasible in a multi-user
+ networking approach. The use of the very controller specific
+ hardware filters could make sense in a very dedicated use-case, as a
+ filter on driver level would affect all users in the multi-user
+ system. The high efficient filter sets inside the PF_CAN core allow
+ to set different multiple filters for each socket separately.
+ Therefore the use of hardware filters goes to the category 'handmade
+ tuning on deep embedded systems'. The author is running a MPC603e
+ @133MHz with four SJA1000 CAN controllers from 2002 under heavy bus
+ load without any problems ...
+
+ 6.4 currently supported CAN hardware (September 2007)
+
+ On the project website http://developer.berlios.de/projects/socketcan
+ there are different drivers available:
+
+ vcan: Virtual CAN interface driver (if no real hardware is available)
+ sja1000: Philips SJA1000 CAN controller (recommended)
+ i82527: Intel i82527 CAN controller
+ mscan: Motorola/Freescale CAN controller (e.g. inside SOC MPC5200)
+ ccan: CCAN controller core (e.g. inside SOC h7202)
+ slcan: For a bunch of CAN adaptors that are attached via a
+ serial line ASCII protocol (for serial / USB adaptors)
+
+ Additionally the different CAN adaptors (ISA/PCI/PCMCIA/USB/Parport)
+ from PEAK Systemtechnik support the CAN netdevice driver model
+ since Linux driver v6.0: http://www.peak-system.com/linux/index.htm
+
+ Please check the Mailing Lists on the berlios OSS project website.
+
+ 6.5 todo (September 2007)
+
+ The configuration interface for CAN network drivers is still an open
+ issue that has not been finalized in the socketcan project. Also the
+ idea of having a library module (candev.ko) that holds functions
+ that are needed by all CAN netdevices is not ready to ship.
+ Your contribution is welcome.
+
+7. Credits
+----------
+
+ Oliver Hartkopp (PF_CAN core, filters, drivers, bcm)
+ Urs Thuermann (PF_CAN core, kernel integration, socket interfaces, raw, vcan)
+ Jan Kizka (RT-SocketCAN core, Socket-API reconciliation)
+ Wolfgang Grandegger (RT-SocketCAN core & drivers, Raw Socket-API reviews)
+ Robert Schwebel (design reviews, PTXdist integration)
+ Marc Kleine-Budde (design reviews, Kernel 2.6 cleanups, drivers)
+ Benedikt Spranger (reviews)
+ Thomas Gleixner (LKML reviews, coding style, posting hints)
+ Andrey Volkov (kernel subtree structure, ioctls, mscan driver)
+ Matthias Brukner (first SJA1000 CAN netdevice implementation Q2/2003)
+ Klaus Hitschler (PEAK driver integration)
+ Uwe Koppe (CAN netdevices with PF_PACKET approach)
+ Michael Schulze (driver layer loopback requirement, RT CAN drivers review)
Index: net-2.6.24/Documentation/networking/00-INDEX
===================================================================
--- net-2.6.24.orig/Documentation/networking/00-INDEX 2007-09-25 15:28:50.000000000 +0200
+++ net-2.6.24/Documentation/networking/00-INDEX 2007-10-02 08:43:21.000000000 +0200
@@ -26,6 +26,8 @@
- info on the driver for Baycom style amateur radio modems
bridge.txt
- where to get user space programs for ethernet bridging with Linux.
+can.txt
+ - documentation on CAN protocol family.
comx.txt
- info on drivers for COMX line of synchronous serial adapters.
cops.txt
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 13:10 ` [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
@ 2007-10-02 14:11 ` Arnaldo Carvalho de Melo
2007-10-02 14:27 ` Urs Thuermann
2007-10-02 14:42 ` Oliver Hartkopp
0 siblings, 2 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:11 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
YOSHIFUJI Hideaki, Eric W. Biederman, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 03:10:07PM +0200, Urs Thuermann escreveu:
> This patch adds a protocol/address family number, ARP hardware type,
> ethernet packet type, and a line discipline number for the SocketCAN
> implementation.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/if.h | 4 +++-
> include/linux/if_arp.h | 1 +
> include/linux/if_ether.h | 1 +
> include/linux/socket.h | 2 ++
> include/linux/tty.h | 3 ++-
> net/core/sock.c | 4 ++--
> 6 files changed, 11 insertions(+), 4 deletions(-)
>
> Index: net-2.6.24/include/linux/if_arp.h
> ===================================================================
> --- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000 +0200
> +++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
> @@ -52,6 +52,7 @@
> #define ARPHRD_ROSE 270
> #define ARPHRD_X25 271 /* CCITT X.25 */
> #define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
> +#define ARPHRD_CAN 280 /* Controller Area Network */
Is 280 used in other OS? Just curious as why not using 273
> #define ARPHRD_PPP 512
> #define ARPHRD_CISCO 513 /* Cisco HDLC */
> #define ARPHRD_HDLC ARPHRD_CISCO
> Index: net-2.6.24/include/linux/if_ether.h
> ===================================================================
> --- net-2.6.24.orig/include/linux/if_ether.h 2007-10-02 12:10:51.000000000 +0200
> +++ net-2.6.24/include/linux/if_ether.h 2007-10-02 12:11:01.000000000 +0200
> @@ -90,6 +90,7 @@
> #define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
> #define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
> #define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */
> +#define ETH_P_CAN 0x000C /* Controller Area Network */
> #define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/
> #define ETH_P_TR_802_2 0x0011 /* 802.2 frames */
> #define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */
> Index: net-2.6.24/include/linux/socket.h
> ===================================================================
> --- net-2.6.24.orig/include/linux/socket.h 2007-10-02 12:10:51.000000000 +0200
> +++ net-2.6.24/include/linux/socket.h 2007-10-02 12:11:01.000000000 +0200
> @@ -185,6 +185,7 @@
> #define AF_PPPOX 24 /* PPPoX sockets */
> #define AF_WANPIPE 25 /* Wanpipe API Sockets */
> #define AF_LLC 26 /* Linux LLC */
> +#define AF_CAN 29 /* Controller Area Network */
Ditto
> #define AF_TIPC 30 /* TIPC sockets */
> #define AF_BLUETOOTH 31 /* Bluetooth sockets */
> #define AF_IUCV 32 /* IUCV sockets */
> @@ -220,6 +221,7 @@
> #define PF_PPPOX AF_PPPOX
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 13:10 ` [PATCH 5/7] CAN: Add virtual CAN netdevice driver Urs Thuermann
@ 2007-10-02 14:20 ` Arnaldo Carvalho de Melo
2007-10-02 15:07 ` Oliver Hartkopp
` (2 more replies)
0 siblings, 3 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:20 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
YOSHIFUJI Hideaki, Eric W. Biederman, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
> This patch adds the virtual CAN bus (vcan) network driver.
> The vcan device is just a loopback device for CAN frames, no
> real CAN hardware is involved.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> drivers/net/Makefile | 1
> drivers/net/can/Kconfig | 25 +++++
> drivers/net/can/Makefile | 5 +
> drivers/net/can/vcan.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++
> net/can/Kconfig | 3
> 5 files changed, 241 insertions(+)
>
> Index: net-2.6.24/drivers/net/Makefile
> ===================================================================
> --- net-2.6.24.orig/drivers/net/Makefile 2007-09-20 23:46:01.000000000 +0200
> +++ net-2.6.24/drivers/net/Makefile 2007-10-02 12:03:30.000000000 +0200
> @@ -12,6 +12,7 @@
> obj-$(CONFIG_CHELSIO_T1) += chelsio/
> obj-$(CONFIG_CHELSIO_T3) += cxgb3/
> obj-$(CONFIG_EHEA) += ehea/
> +obj-$(CONFIG_CAN) += can/
> obj-$(CONFIG_BONDING) += bonding/
> obj-$(CONFIG_ATL1) += atl1/
> obj-$(CONFIG_GIANFAR) += gianfar_driver.o
> Index: net-2.6.24/drivers/net/can/Kconfig
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/drivers/net/can/Kconfig 2007-10-02 12:03:30.000000000 +0200
> @@ -0,0 +1,25 @@
> +menu "CAN Device Drivers"
> + depends on CAN
> +
> +config CAN_VCAN
> + tristate "Virtual Local CAN Interface (vcan)"
> + depends on CAN
> + default N
> + ---help---
> + Similar to the network loopback devices, vcan offers a
> + virtual local CAN interface.
> +
> + This driver can also be built as a module. If so, the module
> + will be called vcan.
> +
> +config CAN_DEBUG_DEVICES
> + bool "CAN devices debugging messages"
> + depends on CAN
> + default N
> + ---help---
> + Say Y here if you want the CAN device drivers to produce a bunch of
> + debug messages to the system log. Select this if you are having
> + a problem with CAN support and want to see more of what is going
> + on.
> +
> +endmenu
> Index: net-2.6.24/drivers/net/can/Makefile
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/drivers/net/can/Makefile 2007-10-02 12:03:30.000000000 +0200
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for the Linux Controller Area Network drivers.
> +#
> +
> +obj-$(CONFIG_CAN_VCAN) += vcan.o
> Index: net-2.6.24/drivers/net/can/vcan.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/drivers/net/can/vcan.c 2007-10-02 12:03:30.000000000 +0200
> @@ -0,0 +1,207 @@
> +/*
> + * vcan.c - Virtual CAN interface
> + *
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/if_arp.h>
> +#include <linux/if_ether.h>
> +#include <linux/can.h>
> +#include <net/rtnetlink.h>
> +
> +static __initdata const char banner[] =
> + KERN_INFO "vcan: Virtual CAN interface driver\n";
> +
> +MODULE_DESCRIPTION("virtual CAN interface");
> +MODULE_LICENSE("Dual BSD/GPL");
> +MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
> +
> +#ifdef CONFIG_CAN_DEBUG_DEVICES
> +static int debug;
> +module_param(debug, int, S_IRUGO);
> +#endif
Can debug be a boolean? Like its counterpart on DCCP:
net/dccp/proto.c:
module_param(dccp_debug, bool, 0444);
Where we also use a namespace prefix, for those of us who use ctags or
cscope.
> +
> +/* To be moved to linux/can/dev.h */
Is this comment still valid? If so can this move happen now? If not I
think it would be better to stick a "FIXME: " just before it, no?
> +#ifdef CONFIG_CAN_DEBUG_DEVICES
> +#define DBG(fmt, args...) (debug & 1 ? \
> + printk(KERN_DEBUG "vcan %s: " fmt, \
> + __func__, ##args) : 0)
> +#else
> +#define DBG(fmt, args...)
> +#endif
> +
> +
> +/*
> + * CAN test feature:
> + * Enable the echo on driver level for testing the CAN core echo modes.
> + * See Documentation/networking/can.txt for details.
> + */
> +
> +static int echo; /* echo testing. Default: 0 (Off) */
> +module_param(echo, int, S_IRUGO);
> +MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
echo also seems to be a boolean
> +
> +
> +static int vcan_open(struct net_device *dev)
> +{
> + DBG("%s: interface up\n", dev->name);
> +
> + netif_start_queue(dev);
> + return 0;
> +}
> +
> +static int vcan_stop(struct net_device *dev)
> +{
> + DBG("%s: interface down\n", dev->name);
> +
> + netif_stop_queue(dev);
> + return 0;
> +}
Thinking out loud: I guess these days we can try to reduce the clutter
on the source code for things like "hey, I entered function foo" using
simple systemtap scripts, that could even be shipped with the kernel
sources. Not something pressing right now, just a suggestion.
> +static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct net_device_stats *stats = &dev->stats;
> +
> + stats->rx_packets++;
> + stats->rx_bytes += skb->len;
> +
> + skb->protocol = htons(ETH_P_CAN);
> + skb->pkt_type = PACKET_BROADCAST;
> + skb->dev = dev;
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> +
> + DBG("received skbuff on interface %d\n", dev->ifindex);
> +
> + netif_rx(skb);
> +}
> +
> +static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct net_device_stats *stats = &dev->stats;
> + int loop;
> +
> + DBG("sending skbuff on interface %s\n", dev->name);
> +
> + stats->tx_packets++;
> + stats->tx_bytes += skb->len;
> +
> + /* set flag whether this packet has to be looped back */
> + loop = skb->pkt_type == PACKET_LOOPBACK;
> +
> + if (!echo) {
> + /* no echo handling available inside this driver */
> +
> + if (loop) {
> + /*
> + * only count the packets here, because the
> + * CAN core already did the echo for us
> + */
> + stats->rx_packets++;
> + stats->rx_bytes += skb->len;
> + }
> + kfree_skb(skb);
> + return 0;
> + }
> +
> + /* perform standard echo handling for CAN network interfaces */
> +
> + if (loop) {
> + struct sock *srcsk = skb->sk;
> +
> + skb = skb_share_check(skb, GFP_ATOMIC);
> + if (!skb)
> + return 0;
> +
> + /* receive with packet counting */
> + skb->sk = srcsk;
> + vcan_rx(skb, dev);
> + } else {
> + /* no looped packets => no counting */
> + kfree_skb(skb);
> + }
> + return 0;
> +}
> +
> +static void vcan_setup(struct net_device *dev)
> +{
> + DBG("dev %s\n", dev->name);
> +
> + dev->type = ARPHRD_CAN;
> + dev->mtu = sizeof(struct can_frame);
> + dev->hard_header_len = 0;
> + dev->addr_len = 0;
> + dev->tx_queue_len = 0;
> + dev->flags = IFF_NOARP;
> +
> + /* set flags according to driver capabilities */
> + if (echo)
> + dev->flags |= IFF_ECHO;
> +
> + dev->open = vcan_open;
> + dev->stop = vcan_stop;
> + dev->hard_start_xmit = vcan_tx;
> + dev->destructor = free_netdev;
> +}
> +
> +static struct rtnl_link_ops vcan_link_ops __read_mostly = {
> + .kind = "vcan",
> + .setup = vcan_setup,
> +};
> +
> +static __init int vcan_init_module(void)
> +{
> + printk(banner);
> +
> + if (echo)
> + printk(KERN_INFO "vcan: enabled echo on driver level.\n");
> +
> + return rtnl_link_register(&vcan_link_ops);
> +}
> +
> +static __exit void vcan_cleanup_module(void)
> +{
> + rtnl_link_unregister(&vcan_link_ops);
> +}
> +
> +module_init(vcan_init_module);
> +module_exit(vcan_cleanup_module);
> Index: net-2.6.24/net/can/Kconfig
> ===================================================================
> --- net-2.6.24.orig/net/can/Kconfig 2007-10-02 12:03:28.000000000 +0200
> +++ net-2.6.24/net/can/Kconfig 2007-10-02 12:03:30.000000000 +0200
> @@ -47,3 +47,6 @@
> Say Y here if you want the CAN core to produce a bunch of debug
> messages to the system log. Select this if you are having a
> problem with CAN support and want to see more of what is going on.
> +
> +
> +source "drivers/net/can/Kconfig"
>
> --
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 14:11 ` Arnaldo Carvalho de Melo
@ 2007-10-02 14:27 ` Urs Thuermann
2007-10-02 14:43 ` Arnaldo Carvalho de Melo
2007-10-02 14:42 ` Oliver Hartkopp
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-02 14:27 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
> > --- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000 +0200
> > +++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
> > @@ -52,6 +52,7 @@
> > #define ARPHRD_ROSE 270
> > #define ARPHRD_X25 271 /* CCITT X.25 */
> > #define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
> > +#define ARPHRD_CAN 280 /* Controller Area Network */
>
> Is 280 used in other OS? Just curious as why not using 273
When we first implemented PF_CAN a couple of years ago, we wanted to
avoid a clash with other ARPHRD_* defines which might be added, so we
skipped some numbers after the last used one. I don't care what
number ARPHRD_CAN is, we can use 273.
> > --- net-2.6.24.orig/include/linux/socket.h 2007-10-02 12:10:51.000000000 +0200
> > +++ net-2.6.24/include/linux/socket.h 2007-10-02 12:11:01.000000000 +0200
> > @@ -185,6 +185,7 @@
> > #define AF_PPPOX 24 /* PPPoX sockets */
> > #define AF_WANPIPE 25 /* Wanpipe API Sockets */
> > #define AF_LLC 26 /* Linux LLC */
> > +#define AF_CAN 29 /* Controller Area Network */
>
> Ditto
>
> > #define AF_TIPC 30 /* TIPC sockets */
> > #define AF_BLUETOOTH 31 /* Bluetooth sockets */
> > #define AF_IUCV 32 /* IUCV sockets */
For the same reason as above, we didn't use 27, but the last unused
without modifying AF_MAX. First, we had AF_CAN == 30, then TIPC used
that number and we changed AF_CAN to 29. Changing again would mean an
ABI change and would break user apps. If there is a pressing reason I
wouldn't mind personally, but it would probably upset quite a number
of users of our code. It seems common now to allocate these numbers
from the top in decreasing order.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 3/7] CAN: Add raw protocol
2007-10-02 13:10 ` [PATCH 3/7] CAN: Add raw protocol Urs Thuermann
@ 2007-10-02 14:30 ` Arnaldo Carvalho de Melo
2007-10-02 14:53 ` Oliver Hartkopp
2007-10-04 11:52 ` Urs Thuermann
0 siblings, 2 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:30 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
YOSHIFUJI Hideaki, Eric W. Biederman, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 03:10:09PM +0200, Urs Thuermann escreveu:
> This patch adds the CAN raw protocol.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/can/raw.h | 31 +
> net/can/Kconfig | 11
> net/can/Makefile | 3
> net/can/raw.c | 821 ++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 866 insertions(+)
>
> Index: net-2.6.24/include/linux/can/raw.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can/raw.h 2007-10-02 08:33:39.000000000 +0200
> @@ -0,0 +1,31 @@
> +/*
> + * linux/can/raw.h
> + *
> + * Definitions for raw CAN sockets
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_RAW_H
> +#define CAN_RAW_H
> +
> +#include <linux/can.h>
> +
> +#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)
> +
> +/* for socket options affecting the socket (not the global system) */
> +
> +enum {
> + CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */
> + CAN_RAW_ERR_FILTER, /* set filter for error frames */
> + CAN_RAW_LOOPBACK, /* local loopback (default:on) */
> + CAN_RAW_RECV_OWN_MSGS /* receive my own msgs (default:off) */
> +};
> +
> +#endif
> Index: net-2.6.24/net/can/Kconfig
> ===================================================================
> --- net-2.6.24.orig/net/can/Kconfig 2007-10-02 06:18:29.000000000 +0200
> +++ net-2.6.24/net/can/Kconfig 2007-10-02 08:35:31.000000000 +0200
> @@ -16,6 +16,17 @@
> If you want CAN support, you should say Y here and also to the
> specific driver for your controller(s) below.
>
> +config CAN_RAW
> + tristate "Raw CAN Protocol (raw access with CAN-ID filtering)"
> + depends on CAN
> + default N
> + ---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 / Errorframes.
> + To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
> +
> config CAN_DEBUG_CORE
> bool "CAN Core debugging messages"
> depends on CAN
> Index: net-2.6.24/net/can/Makefile
> ===================================================================
> --- net-2.6.24.orig/net/can/Makefile 2007-10-02 06:18:29.000000000 +0200
> +++ net-2.6.24/net/can/Makefile 2007-10-02 08:35:31.000000000 +0200
> @@ -4,3 +4,6 @@
>
> obj-$(CONFIG_CAN) += can.o
> can-objs := af_can.o proc.o
> +
> +obj-$(CONFIG_CAN_RAW) += can-raw.o
> +can-raw-objs := raw.o
> Index: net-2.6.24/net/can/raw.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/net/can/raw.c 2007-10-02 08:35:48.000000000 +0200
> @@ -0,0 +1,821 @@
> +/*
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/uio.h>
> +#include <linux/poll.h>
> +#include <linux/net.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/raw.h>
> +#include <net/sock.h>
> +#include <net/net_namespace.h>
> +
> +#define IDENT "raw"
> +#define CAN_RAW_VERSION CAN_VERSION
> +static __initdata 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>");
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +static int debug;
> +module_param(debug, int, S_IRUGO);
> +MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
> +#endif
> +
> +#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 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;
> +};
> +
> +static inline struct raw_sock *raw_sk(const struct sock *sk)
> +{
> + return (struct raw_sock *)sk;
> +}
What if I want to do some kernel module that uses INET raw sockets
(include/net/icmp.h) and CAN raw sockets? Namespace collision, could you
please use can_raw_ for this namespace?
> +static void raw_rcv(struct sk_buff *skb, void *data)
> +{
> + struct sock *sk = (struct sock *)data;
> + struct raw_sock *ro = raw_sk(sk);
> + struct sockaddr_can *addr;
> + int error;
> +
> + DBG("received skbuff %p, sk %p\n", skb, sk);
> + DBG_SKB(skb);
> +
> + if (!ro->recv_own_msgs) {
> + /* check the received tx sock reference */
> + if (skb->sk == sk) {
> + DBG("trashed own tx msg\n");
> + kfree_skb(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;
> +
> + error = sock_queue_rcv_skb(sk, skb);
> + if (error < 0) {
> + DBG("sock_queue_rcv_skb failed: %d\n", error);
> + DBG("freeing skbuff %p\n", skb);
> + 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++) {
> + DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
> + filter[i].can_id, filter[i].can_mask,
> + filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
> +
> + err = can_rx_register(dev, filter[i].can_id,
> + filter[i].can_mask,
> + raw_rcv, sk, IDENT);
> +
> + 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, IDENT);
> +
> + 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++) {
> + DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
> + filter[i].can_id, filter[i].can_mask,
> + filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
> +
> + 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 *data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
> + struct sock *sk = &ro->sk;
> +
> + DBG("msg %ld for dev %p (%s idx %d) sk %p ro->ifindex %d\n",
> + msg, dev, dev->name, dev->ifindex, sk, ro->ifindex);
> +
> + if (dev->nd_net != &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;
> +
> + /* 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 = raw_sk(sk);
> +
> + DBG("socket %p, sk %p, refcnt %d\n", sock, sk,
> + atomic_read(&sk->sk_refcnt));
> +
> + 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;
> +
> + 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;
> +
> + DBG("socket %p to device %d\n", sock, addr->can_ifindex);
> +
> + 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) {
> + DBG("could not find device %d\n", addr->can_ifindex);
> + err = -ENODEV;
> + goto out;
> + }
> + if (dev->type != ARPHRD_CAN) {
> + DBG("device %d no CAN device\n", addr->can_ifindex);
> + 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;
> +
> + addr->can_family = AF_CAN;
> + addr->can_ifindex = ro->ifindex;
> +
> + *len = sizeof(*addr);
> +
> + return 0;
> +}
> +
> +static unsigned int raw_poll(struct file *file, struct socket *sock,
> + poll_table *wait)
> +{
> + unsigned int mask = 0;
> +
> + DBG("socket %p\n", sock);
> +
> + mask = datagram_poll(file, sock, wait);
> + return mask;
What is the value of 'mask' here? Leftover from debugging?
> +}
> +
> +static int raw_setsockopt(struct socket *sock, int level, int optname,
> + char __user *optval, 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;
> + if (optlen < 0)
> + 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 = kmalloc(optlen, GFP_KERNEL);
> + if (!filter)
> + return -ENOMEM;
> +
> + err = copy_from_user(filter, optval, optlen);
> + if (err) {
> + kfree(filter);
> + return err;
> + }
> + } else if (count == 1) {
> + err = copy_from_user(&sfilter, optval, optlen);
> + if (err)
> + return err;
> + }
> +
> + lock_sock(sk);
> +
> + if (ro->bound && ro->ifindex)
> + dev = dev_get_by_index(&init_net, ro->ifindex);
dev_get_by_index can fail, are you sure that raw_enable_filters can cope
with this possibility?
> + 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;
> +
> + err = copy_from_user(&err_mask, optval, optlen);
> + if (err)
> + return err;
> +
> + 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;
> +
> + err = copy_from_user(&ro->loopback, optval, optlen);
> +
> + break;
> +
> + case CAN_RAW_RECV_OWN_MSGS:
> + if (optlen != sizeof(ro->recv_own_msgs))
> + return -EINVAL;
> +
> + err = copy_from_user(&ro->recv_own_msgs, optval, optlen);
> +
> + 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;
> + err = copy_to_user(optval, ro->filter, len);
> + } 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;
> +
> + 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;
> +
> + DBG("socket %p, sk %p\n", sock, sk);
> +
> + if (msg->msg_name) {
> + struct sockaddr_can *addr =
> + (struct sockaddr_can *)msg->msg_name;
> +
> + if (addr->can_family != AF_CAN)
> + return -EINVAL;
> +
> + ifindex = addr->can_ifindex;
> + } else
> + ifindex = ro->ifindex;
> +
> + dev = dev_get_by_index(&init_net, ifindex);
> + if (!dev) {
> + DBG("device %d not found\n", ifindex);
> + return -ENXIO;
> + }
> +
> + skb = alloc_skb(size, GFP_KERNEL);
> + if (!skb) {
> + dev_put(dev);
> + return -ENOMEM;
> + }
> +
> + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
> + if (err < 0) {
> + kfree_skb(skb);
> + dev_put(dev);
> + return err;
> + }
> + skb->dev = dev;
> + skb->sk = sk;
> +
> + DBG("sending skbuff to interface %d\n", ifindex);
> + DBG_SKB(skb);
> +
> + err = can_send(skb, ro->loopback);
> +
> + dev_put(dev);
> +
> + if (err)
> + return err;
> +
> + return size;
> +}
> +
> +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 error = 0;
> + int noblock;
> +
> + DBG("socket %p, sk %p\n", sock, sk);
> +
> + noblock = flags & MSG_DONTWAIT;
> + flags &= ~MSG_DONTWAIT;
> +
> + skb = skb_recv_datagram(sk, flags, noblock, &error);
> + if (!skb)
> + return error;
> +
> + DBG("delivering skbuff %p\n", skb);
> + DBG_SKB(skb);
> +
> + if (size < skb->len)
> + msg->msg_flags |= MSG_TRUNC;
> + else
> + size = skb->len;
> +
> + error = memcpy_toiovec(msg->msg_iov, skb->data, size);
> + if (error < 0) {
> + skb_free_datagram(sk, skb);
> + return error;
> + }
> +
> + sock_recv_timestamp(msg, sk, skb);
> +
> + if (msg->msg_name) {
> + msg->msg_namelen = sizeof(struct sockaddr_can);
> + memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
> + }
> +
> + DBG("freeing sock %p, skbuff %p\n", sk, skb);
> + skb_free_datagram(sk, skb);
> +
> + return size;
> +}
> +
> +static struct proto_ops raw_ops __read_mostly = {
> + .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 = raw_poll,
> + .ioctl = NULL, /* 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 struct can_proto raw_can_proto __read_mostly = {
> + .type = SOCK_RAW,
> + .protocol = CAN_RAW,
> + .capability = -1,
> + .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);
>
> --
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-02 13:10 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-10-02 14:38 ` Arnaldo Carvalho de Melo
2007-10-02 16:09 ` Oliver Hartkopp
2007-10-04 11:51 ` Urs Thuermann
0 siblings, 2 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:38 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
YOSHIFUJI Hideaki, Eric W. Biederman, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 03:10:08PM +0200, Urs Thuermann escreveu:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/can.h | 111 +++++
> include/linux/can/core.h | 77 +++
> include/linux/can/error.h | 93 ++++
> net/Kconfig | 1
> net/Makefile | 1
> net/can/Kconfig | 25 +
> net/can/Makefile | 6
> net/can/af_can.c | 970 ++++++++++++++++++++++++++++++++++++++++++++++
> net/can/af_can.h | 120 +++++
> net/can/proc.c | 532 +++++++++++++++++++++++++
> 10 files changed, 1936 insertions(+)
>
> Index: net-2.6.24/include/linux/can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can.h 2007-10-02 12:10:18.000000000 +0200
> @@ -0,0 +1,111 @@
> +/*
> + * linux/can.h
> + *
> + * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_H
> +#define CAN_H
> +
> +#include <linux/types.h>
> +#include <linux/socket.h>
> +
> +/* controller area network (CAN) kernel definitions */
> +
> +/* special address description flags for the CAN_ID */
> +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
> +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
> +#define CAN_ERR_FLAG 0x20000000U /* error frame */
> +
> +/* valid bits in CAN ID for frame formats */
> +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
> +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
> +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
> +
> +/*
> + * Controller Area Network Identifier structure
> + *
> + * bit 0-28 : CAN identifier (11/29 bit)
> + * bit 29 : error frame flag (0 = data frame, 1 = error frame)
> + * bit 30 : remote transmission request flag (1 = rtr frame)
> + * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
> + */
> +typedef __u32 canid_t;
> +
> +/*
> + * Controller Area Network Error Frame Mask structure
> + *
> + * bit 0-28 : error class mask (see include/linux/can/error.h)
> + * bit 29-31 : set to zero
> + */
> +typedef __u32 can_err_mask_t;
> +
> +/**
> + * struct can_frame - basic CAN frame structure
> + * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
> + * @can_dlc: the data length field of the CAN frame
> + * @data: the CAN frame payload.
> + */
> +struct can_frame {
> + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
> + __u8 can_dlc; /* data length code: 0 .. 8 */
> + __u8 data[8] __attribute__((aligned(8)));
> +};
> +
> +/* particular protocols of the protocol family PF_CAN */
> +#define CAN_RAW 1 /* RAW sockets */
> +#define CAN_BCM 2 /* Broadcast Manager */
> +#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
> +#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
> +#define CAN_MCNET 5 /* Bosch MCNet */
> +#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
> +#define CAN_NPROTO 7
> +
> +#define SOL_CAN_BASE 100
> +
> +/**
> + * struct sockaddr_can - the sockaddr structure for CAN sockets
> + * @can_family: address family number AF_CAN.
> + * @can_ifindex: CAN network interface index.
> + * @can_addr: transport protocol specific address, mostly CAN IDs.
> + */
> +struct sockaddr_can {
> + sa_family_t can_family;
> + int can_ifindex;
> + union {
> + struct { canid_t rx_id, tx_id; } tp16;
> + struct { canid_t rx_id, tx_id; } tp20;
> + struct { canid_t rx_id, tx_id; } mcnet;
> + struct { canid_t rx_id, tx_id; } isotp;
> + } can_addr;
Again being curious, what is the value of this union of all its members
have the same definition? Backward source code compatibility?
> +};
> +
> +/**
> + * struct can_filter - CAN ID based filter in can_register().
> + * @can_id: relevant bits of CAN ID which are not masked out.
> + * @can_mask: CAN mask (see description)
> + *
> + * Description:
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + */
> +struct can_filter {
> + canid_t can_id;
> + canid_t can_mask;
> +};
> +
> +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
> +
> +#endif /* CAN_H */
> Index: net-2.6.24/include/linux/can/core.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.24/include/linux/can/core.h 2007-10-02 12:10:18.000000000 +0200
> @@ -0,0 +1,77 @@
> +/*
> + * linux/can/core.h
> + *
> + * Protoypes and definitions for CAN protocol modules using the PF_CAN core
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_CORE_H
> +#define CAN_CORE_H
> +
> +#include <linux/can.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +
> +#define CAN_VERSION "20071001"
> +
> +/* increment this number each time you change some user-space interface */
> +#define CAN_ABI_VERSION "8"
> +
> +#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
> +
> +#define DNAME(dev) ((dev) ? (dev)->name : "any")
> +
> +/**
> + * struct can_proto - CAN protocol structure
> + * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
> + * @protocol: protocol number in socket() syscall.
> + * @capability: capability needed to open the socket, or -1 for no restriction.
> + * @ops: pointer to struct proto_ops for sock->ops.
> + * @prot: pointer to struct proto structure.
> + */
> +struct can_proto {
> + int type;
> + int protocol;
> + int capability;
> + struct proto_ops *ops;
> + struct proto *prot;
> +};
> +
> +/* function prototypes for the CAN networklayer core (af_can.c) */
> +
> +extern int can_proto_register(struct can_proto *cp);
> +extern void can_proto_unregister(struct can_proto *cp);
We have proto registering infrastructure for bluetooth, inet and now
CAN, have you looked at:
struct inet_protosw;
proto_{register,unregister}, etc?
You have been thru many iterations already, sigh, I should have looked
at this before :-\
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 14:11 ` Arnaldo Carvalho de Melo
2007-10-02 14:27 ` Urs Thuermann
@ 2007-10-02 14:42 ` Oliver Hartkopp
2007-10-02 14:51 ` Arnaldo Carvalho de Melo
1 sibling, 1 reply; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-02 14:42 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Arnaldo Carvalho de Melo wrote:
> Em Tue, Oct 02, 2007 at 03:10:07PM +0200, Urs Thuermann escreveu:
>
>>
>> Index: net-2.6.24/include/linux/if_arp.h
>> ===================================================================
>> --- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000 +0200
>> +++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
>> @@ -52,6 +52,7 @@
>> #define ARPHRD_ROSE 270
>> #define ARPHRD_X25 271 /* CCITT X.25 */
>> #define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
>> +#define ARPHRD_CAN 280 /* Controller Area Network */
>>
>
> Is 280 used in other OS? Just curious as why not using 273
>
>
All these definitions (PF_*, AF_*, ARPHRD_* ) are operation system
specific (that's why you find it in /usr/include/linux/*.h :)
I just googled for AF_INET and found that e.g.
AF_APPLETALK is "16" in winsock.h and "5" in include/linux/socket.h
The reason to use 280 instead of 273 was, that all the 27x stuff was
dedicated to the X.25 domain. So to start with a new 280 looked
reasonable to me.
At the end of the ARPHRD_* definitions there's currently many 'change
traffic' due to IEEE80211.
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 14:27 ` Urs Thuermann
@ 2007-10-02 14:43 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:43 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Em Tue, Oct 02, 2007 at 04:27:40PM +0200, Urs Thuermann escreveu:
> Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
>
> > > --- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000 +0200
> > > +++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
> > > @@ -52,6 +52,7 @@
> > > #define ARPHRD_ROSE 270
> > > #define ARPHRD_X25 271 /* CCITT X.25 */
> > > #define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
> > > +#define ARPHRD_CAN 280 /* Controller Area Network */
> >
> > Is 280 used in other OS? Just curious as why not using 273
>
> When we first implemented PF_CAN a couple of years ago, we wanted to
> avoid a clash with other ARPHRD_* defines which might be added, so we
> skipped some numbers after the last used one. I don't care what
> number ARPHRD_CAN is, we can use 273.
>
> > > --- net-2.6.24.orig/include/linux/socket.h 2007-10-02 12:10:51.000000000 +0200
> > > +++ net-2.6.24/include/linux/socket.h 2007-10-02 12:11:01.000000000 +0200
> > > @@ -185,6 +185,7 @@
> > > #define AF_PPPOX 24 /* PPPoX sockets */
> > > #define AF_WANPIPE 25 /* Wanpipe API Sockets */
> > > #define AF_LLC 26 /* Linux LLC */
> > > +#define AF_CAN 29 /* Controller Area Network */
> >
> > Ditto
> >
> > > #define AF_TIPC 30 /* TIPC sockets */
> > > #define AF_BLUETOOTH 31 /* Bluetooth sockets */
> > > #define AF_IUCV 32 /* IUCV sockets */
>
> For the same reason as above, we didn't use 27, but the last unused
> without modifying AF_MAX. First, we had AF_CAN == 30, then TIPC used
> that number and we changed AF_CAN to 29. Changing again would mean an
> ABI change and would break user apps. If there is a pressing reason I
> wouldn't mind personally, but it would probably upset quite a number
> of users of our code. It seems common now to allocate these numbers
> from the top in decreasing order.
Not a problem to have the hole, eventually we'll find something to put
there.
As I said, just curiosity, but can you see a AF_NETBEUI implementation
around? I'm just joking, but you could have reserved it and avoided the
clash with TIPC (that I don't remember if has made any reservation).
Protocol number allocation with collision detection is worse than doing
avoidance 8-)
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN
2007-10-02 14:42 ` Oliver Hartkopp
@ 2007-10-02 14:51 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 14:51 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 04:42:24PM +0200, Oliver Hartkopp escreveu:
> Arnaldo Carvalho de Melo wrote:
>> Em Tue, Oct 02, 2007 at 03:10:07PM +0200, Urs Thuermann escreveu:
>>
>>>
>>> Index: net-2.6.24/include/linux/if_arp.h
>>> ===================================================================
>>> --- net-2.6.24.orig/include/linux/if_arp.h 2007-10-02 12:10:51.000000000
>>> +0200
>>> +++ net-2.6.24/include/linux/if_arp.h 2007-10-02 12:11:01.000000000 +0200
>>> @@ -52,6 +52,7 @@
>>> #define ARPHRD_ROSE 270
>>> #define ARPHRD_X25 271 /* CCITT X.25 */
>>> #define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
>>> +#define ARPHRD_CAN 280 /* Controller Area Network */
>>>
>>
>> Is 280 used in other OS? Just curious as why not using 273
>>
>>
>
> All these definitions (PF_*, AF_*, ARPHRD_* ) are operation system specific
> (that's why you find it in /usr/include/linux/*.h :)
I understand that, but couldn't find other reason for it not to be the
next in line (273), so thought that one could think it would be nice to
have the same number accross OSes :) But you have provided one, too much
changes in this area (TIPC, PF_CAN, etc) happening and causing clashes.
> I just googled for AF_INET and found that e.g.
> AF_APPLETALK is "16" in winsock.h and "5" in include/linux/socket.h
>
> The reason to use 280 instead of 273 was, that all the 27x stuff was
> dedicated to the X.25 domain. So to start with a new 280 looked reasonable
> to me.
>
> At the end of the ARPHRD_* definitions there's currently many 'change
> traffic' due to IEEE80211.
OK.
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 3/7] CAN: Add raw protocol
2007-10-02 14:30 ` Arnaldo Carvalho de Melo
@ 2007-10-02 14:53 ` Oliver Hartkopp
2007-10-04 11:52 ` Urs Thuermann
1 sibling, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-02 14:53 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Arnaldo Carvalho de Melo wrote:
> Em Tue, Oct 02, 2007 at 03:10:09PM +0200, Urs Thuermann escreveu:
>
>
>> +
>> + if (ro->bound && ro->ifindex)
>> + dev = dev_get_by_index(&init_net, ro->ifindex);
>>
>
> dev_get_by_index can fail, are you sure that raw_enable_filters can cope
> with this possibility?
>
>
When dev_get_by_index() fails, the netdev is not there anymore.
In this case the netdev notifier handler already set ro->bound to 0.
Additionally raw_enable_filters() can handle *dev even when it's NULL.
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 14:20 ` Arnaldo Carvalho de Melo
@ 2007-10-02 15:07 ` Oliver Hartkopp
2007-10-02 16:46 ` Arnaldo Carvalho de Melo
2007-10-02 21:02 ` Oliver Hartkopp
2007-10-04 11:52 ` Urs Thuermann
2 siblings, 1 reply; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-02 15:07 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Arnaldo Carvalho de Melo wrote:
> Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
>
>
>> +
>> +/* To be moved to linux/can/dev.h */
>>
>
> Is this comment still valid? If so can this move happen now? If not I
> think it would be better to stick a "FIXME: " just before it, no?
>
>
Bringing all the current available CAN network device drivers into
Kernel style qualitiy is a TODO for the time after the PF_CAN core is
mainlined.
When more than this single vcan CAN netdev driver is part of the Kernel
it makes sense to put several things (like the common configuration
interface and commonly used library funtions for CAN drivers) into
linux/can/dev.h. And at that time this currently local DEBUG definition
should go there togehther with the other stuff.
Please think of all the comments, if we created a single dev.h file with
a single DEBUG definition used by a single vcan.c file ;-)
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-02 14:38 ` Arnaldo Carvalho de Melo
@ 2007-10-02 16:09 ` Oliver Hartkopp
2007-10-04 11:51 ` Urs Thuermann
1 sibling, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-02 16:09 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Arnaldo Carvalho de Melo wrote:
> Em Tue, Oct 02, 2007 at 03:10:08PM +0200, Urs Thuermann escreveu:
>
>> +
>> +/**
>> + * struct sockaddr_can - the sockaddr structure for CAN sockets
>> + * @can_family: address family number AF_CAN.
>> + * @can_ifindex: CAN network interface index.
>> + * @can_addr: transport protocol specific address, mostly CAN IDs.
>> + */
>> +struct sockaddr_can {
>> + sa_family_t can_family;
>> + int can_ifindex;
>> + union {
>> + struct { canid_t rx_id, tx_id; } tp16;
>> + struct { canid_t rx_id, tx_id; } tp20;
>> + struct { canid_t rx_id, tx_id; } mcnet;
>> + struct { canid_t rx_id, tx_id; } isotp;
>> + } can_addr;
>>
>
> Again being curious, what is the value of this union of all its members
> have the same definition? Backward source code compatibility?
>
>
Yes. You're right here. It is a prerequisite for a CAN transportprotocol
like the standardized ISO-TP i plan to submit here until december.
Indeed the union should be replaced by something like
struct sockaddr_can {
sa_family_t can_family;
int can_ifindex;
union {
struct { canid_t rx_id, tx_id; } tp;
} can_addr;
};
The union was the idea, if there is any other protocol for CAN that
need's other adressing information. I personally do not know any
protocol. Maybe CANOpen has different addressing requirements here. And
in that case a new protocol could just could add things to the union to
meet it's own needs.
>
> You have been thru many iterations already, sigh, I should have looked
> at this before :-\
>
>
Yeah :-) I thought, you were just done last time.
Btw. Thanks for your review and your good feedback.
Regards,
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 15:07 ` Oliver Hartkopp
@ 2007-10-02 16:46 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 16:46 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 05:07:40PM +0200, Oliver Hartkopp escreveu:
> Arnaldo Carvalho de Melo wrote:
>> Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
>>
>>> +
>>> +/* To be moved to linux/can/dev.h */
>>>
>>
>> Is this comment still valid? If so can this move happen now? If not I
>> think it would be better to stick a "FIXME: " just before it, no?
>>
>>
>
> Bringing all the current available CAN network device drivers into Kernel
> style qualitiy is a TODO for the time after the PF_CAN core is mainlined.
>
> When more than this single vcan CAN netdev driver is part of the Kernel it
> makes sense to put several things (like the common configuration interface
> and commonly used library funtions for CAN drivers) into linux/can/dev.h.
> And at that time this currently local DEBUG definition should go there
> togehther with the other stuff.
>
> Please think of all the comments, if we created a single dev.h file with a
> single DEBUG definition used by a single vcan.c file ;-)
Don't get to defensive. You know a lot more than me about the code you
worked for that many years. Its just that it looked suspicious for a
casual reviewer. :-)
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 14:20 ` Arnaldo Carvalho de Melo
2007-10-02 15:07 ` Oliver Hartkopp
@ 2007-10-02 21:02 ` Oliver Hartkopp
2007-10-02 21:43 ` Arnaldo Carvalho de Melo
2007-10-02 21:52 ` Stephen Hemminger
2007-10-04 11:52 ` Urs Thuermann
2 siblings, 2 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-02 21:02 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
Arnaldo Carvalho de Melo wrote:
> Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
>
>> +
>> +#ifdef CONFIG_CAN_DEBUG_DEVICES
>> +static int debug;
>> +module_param(debug, int, S_IRUGO);
>> +#endif
>>
>
> Can debug be a boolean? Like its counterpart on DCCP:
>
> net/dccp/proto.c:
>
> module_param(dccp_debug, bool, 0444);
>
'debug' should remain an integer to be able to specifiy debug-levels or
bit-fields for different Debug outputs.
> Where we also use a namespace prefix, for those of us who use ctags or
> cscope.
>
Even if i don't have any general objections to rename this 'debug' to
'vcan_debug', it looks like an 'overnamed' module parameter for me. Is
this a genereal naming scheme recommendation for debug module_params?
>
>> +/*
>> + * CAN test feature:
>> + * Enable the echo on driver level for testing the CAN core echo modes.
>> + * See Documentation/networking/can.txt for details.
>> + */
>> +
>> +static int echo; /* echo testing. Default: 0 (Off) */
>> +module_param(echo, int, S_IRUGO);
>> +MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
>>
>
> echo also seems to be a boolean
>
>
Yes. This is definitely a boolean candidate. We'll change that.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 21:02 ` Oliver Hartkopp
@ 2007-10-02 21:43 ` Arnaldo Carvalho de Melo
2007-10-02 21:50 ` David Miller
2007-10-02 21:52 ` Stephen Hemminger
1 sibling, 1 reply; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-02 21:43 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Thomas Gleixner, YOSHIFUJI Hideaki, Eric W. Biederman,
Oliver Hartkopp, Urs Thuermann
Em Tue, Oct 02, 2007 at 11:02:53PM +0200, Oliver Hartkopp escreveu:
> Arnaldo Carvalho de Melo wrote:
>> Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
>>
>>> +
>>> +#ifdef CONFIG_CAN_DEBUG_DEVICES
>>> +static int debug;
>>> +module_param(debug, int, S_IRUGO);
>>> +#endif
>>>
>>
>> Can debug be a boolean? Like its counterpart on DCCP:
>>
>> net/dccp/proto.c:
>>
>> module_param(dccp_debug, bool, 0444);
>>
>
> 'debug' should remain an integer to be able to specifiy debug-levels or
> bit-fields for different Debug outputs.
>
>> Where we also use a namespace prefix, for those of us who use ctags or
>> cscope.
>>
>
> Even if i don't have any general objections to rename this 'debug' to
> 'vcan_debug', it looks like an 'overnamed' module parameter for me. Is this
> a genereal naming scheme recommendation for debug module_params?
[acme@mica linux-2.6.23-rc9-rt1]$ find . -name "*.c" | xargs grep
'module_param(.\+debug,' | wc -l
112
[acme@mica linux-2.6.23-rc9-rt1]$ find . -name "*.c" | xargs grep
'module_param(debug,' | wc -l
233
[acme@mica linux-2.6.23-rc9-rt1]$
I think that helping ctags to find the definition for the debug variable
to see, for instance, if it is a bitmask or a boolean without having to
chose from tons of 'debug' variables is a good thing.
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 21:43 ` Arnaldo Carvalho de Melo
@ 2007-10-02 21:50 ` David Miller
2007-10-03 7:06 ` Oliver Hartkopp
0 siblings, 1 reply; 86+ messages in thread
From: David Miller @ 2007-10-02 21:50 UTC (permalink / raw)
To: acme
Cc: oliver, urs, netdev, kaber, tglx, yoshfuji, ebiederm,
oliver.hartkopp, urs.thuermann
From: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Date: Tue, 2 Oct 2007 18:43:25 -0300
> I think that helping ctags to find the definition for the debug variable
> to see, for instance, if it is a bitmask or a boolean without having to
> chose from tons of 'debug' variables is a good thing.
I completely agree.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 21:02 ` Oliver Hartkopp
2007-10-02 21:43 ` Arnaldo Carvalho de Melo
@ 2007-10-02 21:52 ` Stephen Hemminger
2007-10-02 22:04 ` David Miller
1 sibling, 1 reply; 86+ messages in thread
From: Stephen Hemminger @ 2007-10-02 21:52 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Arnaldo Carvalho de Melo, Urs Thuermann, netdev, David Miller,
Patrick McHardy, Thomas Gleixner, YOSHIFUJI Hideaki,
Eric W. Biederman, Oliver Hartkopp, Oliver Hartkopp,
Urs Thuermann
On Tue, 02 Oct 2007 23:02:53 +0200
Oliver Hartkopp <oliver@hartkopp.net> wrote:
> Arnaldo Carvalho de Melo wrote:
> > Em Tue, Oct 02, 2007 at 03:10:11PM +0200, Urs Thuermann escreveu:
> >
> >> +
> >> +#ifdef CONFIG_CAN_DEBUG_DEVICES
> >> +static int debug;
> >> +module_param(debug, int, S_IRUGO);
> >> +#endif
> >>
> >
> > Can debug be a boolean? Like its counterpart on DCCP:
> >
> > net/dccp/proto.c:
> >
> > module_param(dccp_debug, bool, 0444);
> >
>
> 'debug' should remain an integer to be able to specifiy debug-levels or
> bit-fields for different Debug outputs.
>
> > Where we also use a namespace prefix, for those of us who use ctags or
> > cscope.
> >
>
> Even if i don't have any general objections to rename this 'debug' to
> 'vcan_debug', it looks like an 'overnamed' module parameter for me. Is
> this a genereal naming scheme recommendation for debug module_params?
>
Please consider using netif_msg_xxx() and module parameter to set
default message level, like other real network drivers already do.
--
Stephen Hemminger <shemminger@linux-foundation.org>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 21:52 ` Stephen Hemminger
@ 2007-10-02 22:04 ` David Miller
2007-10-03 17:47 ` Oliver Hartkopp
0 siblings, 1 reply; 86+ messages in thread
From: David Miller @ 2007-10-02 22:04 UTC (permalink / raw)
To: shemminger
Cc: oliver, acme, urs, netdev, kaber, tglx, yoshfuji, ebiederm,
oliver.hartkopp, urs.thuermann
From: Stephen Hemminger <shemminger@linux-foundation.org>
Date: Tue, 2 Oct 2007 14:52:36 -0700
> Please consider using netif_msg_xxx() and module parameter to set
> default message level, like other real network drivers already do.
I keep seeing this recommendation, but the two supposedly most mature
and actively used drivers in the tree, tg3 and e1000 and e1000e, all
do not use this scheme.
In fact there are tons of drivers that even hook up the ethtool
msg_level setting function and never even use the value.
If people aren't using netif_msg_xxx() and the ethtool msg_level
facilities properly, it's because there is a severe dearth of good
example drivers to learn about it from.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 21:50 ` David Miller
@ 2007-10-03 7:06 ` Oliver Hartkopp
0 siblings, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-03 7:06 UTC (permalink / raw)
To: David Miller, acme
Cc: urs, netdev, kaber, tglx, yoshfuji, ebiederm, oliver.hartkopp,
urs.thuermann
David Miller wrote:
> From: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
> Date: Tue, 2 Oct 2007 18:43:25 -0300
>
>
>> I think that helping ctags to find the definition for the debug variable
>> to see, for instance, if it is a bitmask or a boolean without having to
>> chose from tons of 'debug' variables is a good thing.
>>
>
> I completely agree.
>
OK. No problem if it's helpful. We'll change debug to vcan_debug.
Thanks.
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 22:04 ` David Miller
@ 2007-10-03 17:47 ` Oliver Hartkopp
0 siblings, 0 replies; 86+ messages in thread
From: Oliver Hartkopp @ 2007-10-03 17:47 UTC (permalink / raw)
To: David Miller, shemminger
Cc: acme, urs, netdev, kaber, tglx, yoshfuji, ebiederm,
oliver.hartkopp, urs.thuermann
David Miller wrote:
> From: Stephen Hemminger <shemminger@linux-foundation.org>
> Date: Tue, 2 Oct 2007 14:52:36 -0700
>
>
>> Please consider using netif_msg_xxx() and module parameter to set
>> default message level, like other real network drivers already do.
>>
>
> I keep seeing this recommendation, but the two supposedly most mature
> and actively used drivers in the tree, tg3 and e1000 and e1000e, all
> do not use this scheme.
>
> In fact there are tons of drivers that even hook up the ethtool
> msg_level setting function and never even use the value.
>
> If people aren't using netif_msg_xxx() and the ethtool msg_level
> facilities properly, it's because there is a severe dearth of good
> example drivers to learn about it from.
>
The currently available CAN netdevice drivers do not have a common debug
concept neither any runtime control mechanism for this debugging. So
netif_msg_xxx() is definitely worth to look at instead of creating any
new stuff in this direction, before posting any 'real' CAN network
driver here.
Thanks very much for that hint!
Oliver
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-02 14:38 ` Arnaldo Carvalho de Melo
2007-10-02 16:09 ` Oliver Hartkopp
@ 2007-10-04 11:51 ` Urs Thuermann
2007-10-04 13:40 ` Arnaldo Carvalho de Melo
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-10-04 11:51 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
> > +struct sockaddr_can {
> > + sa_family_t can_family;
> > + int can_ifindex;
> > + union {
> > + struct { canid_t rx_id, tx_id; } tp16;
> > + struct { canid_t rx_id, tx_id; } tp20;
> > + struct { canid_t rx_id, tx_id; } mcnet;
> > + struct { canid_t rx_id, tx_id; } isotp;
> > + } can_addr;
>
> Again being curious, what is the value of this union of all its members
> have the same definition? Backward source code compatibility?
As Oliver already wrote, different CAN transport protocols may use
different sockaddr structures. Therefore, we have made can_addr a
union. The four we have defined already, all look the same, but
other, future protocols may define a different structure.
> > +struct can_proto {
> > + int type;
> > + int protocol;
> > + int capability;
> > + struct proto_ops *ops;
> > + struct proto *prot;
> > +};
> > +
> > +/* function prototypes for the CAN networklayer core (af_can.c) */
> > +
> > +extern int can_proto_register(struct can_proto *cp);
> > +extern void can_proto_unregister(struct can_proto *cp);
>
> We have proto registering infrastructure for bluetooth, inet and now
> CAN, have you looked at:
>
> struct inet_protosw;
> proto_{register,unregister}, etc?
Yes, I know inet_protosw and inet_{,un}register_protosw(). But we
can't use inet_register_protosw().
And can_proto_register() does use proto_register(). What exactly do
you want to suggest?
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 3/7] CAN: Add raw protocol
2007-10-02 14:30 ` Arnaldo Carvalho de Melo
2007-10-02 14:53 ` Oliver Hartkopp
@ 2007-10-04 11:52 ` Urs Thuermann
1 sibling, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-04 11:52 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
> > +static inline struct raw_sock *raw_sk(const struct sock *sk)
> > +{
> > + return (struct raw_sock *)sk;
> > +}
>
>
> What if I want to do some kernel module that uses INET raw sockets
> (include/net/icmp.h) and CAN raw sockets? Namespace collision, could you
> please use can_raw_ for this namespace?
raw_sk is static so you can't use in another file where you include
include/net/icmp.h. There is no collision. Also, since it's inline
you won't even see it in a symbol table.
Hm, it's more than 10 years that I've tested ctags(1) and etags(1)
with several identical static names in different files and I don't
remember my results. Do these tools have a problem with multiple
defs? I think they shouldn't since C is explicitly designed for that.
> > +static unsigned int raw_poll(struct file *file, struct socket *sock,
> > + poll_table *wait)
> > +{
> > + unsigned int mask = 0;
> > +
> > + DBG("socket %p\n", sock);
> > +
> > + mask = datagram_poll(file, sock, wait);
> > + return mask;
>
> What is the value of 'mask' here? Leftover from debugging?
Ah, yes. We should remove it.
> > +static int raw_setsockopt(struct socket *sock, int level, int optname,
> > + char __user *optval, int optlen)
> > +{
> > + lock_sock(sk);
> > +
> > + if (ro->bound && ro->ifindex)
> > + dev = dev_get_by_index(&init_net, ro->ifindex);
>
> dev_get_by_index can fail, are you sure that raw_enable_filters can cope
> with this possibility?
When ro->ifindex != 0, the call to dev_get_by_index() shouldn't fail.
We also use lock_sock() here and in NETDEV_UNREGISTER, so there should
be no problem.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 5/7] CAN: Add virtual CAN netdevice driver
2007-10-02 14:20 ` Arnaldo Carvalho de Melo
2007-10-02 15:07 ` Oliver Hartkopp
2007-10-02 21:02 ` Oliver Hartkopp
@ 2007-10-04 11:52 ` Urs Thuermann
2 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-04 11:52 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
> > +#ifdef CONFIG_CAN_DEBUG_DEVICES
> > +static int debug;
> > +module_param(debug, int, S_IRUGO);
> > +#endif
>
> Can debug be a boolean? Like its counterpart on DCCP:
debug used to a bit mask, like it still is in core.h. You can see
this in the test
debug & 1 ? ... : ...
below. Only the test for bit 0 is left, so we could change it to bool.
> net/dccp/proto.c:
>
> module_param(dccp_debug, bool, 0444);
>
> Where we also use a namespace prefix, for those of us who use ctags or
> cscope.
I think ctags should be able to handle multiple identical static
symbols. Isn't it? I find it somewhat clumsy to write
modprobe vcan vcan_debug=1
I think it would be nice to change the module_param() macro so that
you can name the module argument and the corresponding variable
independently, like
module_param(can_debug, "debug", bool, 0444);
OK, forget that last paragraph. I've looked at the definition of
module_param() and have seen that we have module_param_named(). I
think we should use that.
> > +
> > +/* To be moved to linux/can/dev.h */
>
> Is this comment still valid? If so can this move happen now? If not I
> think it would be better to stick a "FIXME: " just before it, no?
OK.
> > +static int echo; /* echo testing. Default: 0 (Off) */
> > +module_param(echo, int, S_IRUGO);
> > +MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)");
>
> echo also seems to be a boolean
ACK.
> > +static int vcan_open(struct net_device *dev)
> > +{
> > + DBG("%s: interface up\n", dev->name);
> > +
> > + netif_start_queue(dev);
> > + return 0;
> > +}
> > +
> > +static int vcan_stop(struct net_device *dev)
> > +{
> > + DBG("%s: interface down\n", dev->name);
> > +
> > + netif_stop_queue(dev);
> > + return 0;
> > +}
>
> Thinking out loud: I guess these days we can try to reduce the clutter
> on the source code for things like "hey, I entered function foo" using
> simple systemtap scripts, that could even be shipped with the kernel
> sources. Not something pressing right now, just a suggestion.
I've never heard of systemtap before. I've ust looked at its overview
web page which sounds promising. I think I'll check it out when time
permits...
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-04 11:51 ` Urs Thuermann
@ 2007-10-04 13:40 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 86+ messages in thread
From: Arnaldo Carvalho de Melo @ 2007-10-04 13:40 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp
Em Thu, Oct 04, 2007 at 01:51:47PM +0200, Urs Thuermann escreveu:
> Arnaldo Carvalho de Melo <acme@ghostprotocols.net> writes:
>
> > > +struct sockaddr_can {
> > > + sa_family_t can_family;
> > > + int can_ifindex;
> > > + union {
> > > + struct { canid_t rx_id, tx_id; } tp16;
> > > + struct { canid_t rx_id, tx_id; } tp20;
> > > + struct { canid_t rx_id, tx_id; } mcnet;
> > > + struct { canid_t rx_id, tx_id; } isotp;
> > > + } can_addr;
> >
> > Again being curious, what is the value of this union of all its members
> > have the same definition? Backward source code compatibility?
>
> As Oliver already wrote, different CAN transport protocols may use
> different sockaddr structures. Therefore, we have made can_addr a
> union. The four we have defined already, all look the same, but
> other, future protocols may define a different structure.
>
> > > +struct can_proto {
> > > + int type;
> > > + int protocol;
> > > + int capability;
> > > + struct proto_ops *ops;
> > > + struct proto *prot;
> > > +};
> > > +
> > > +/* function prototypes for the CAN networklayer core (af_can.c) */
> > > +
> > > +extern int can_proto_register(struct can_proto *cp);
> > > +extern void can_proto_unregister(struct can_proto *cp);
> >
> > We have proto registering infrastructure for bluetooth, inet and now
> > CAN, have you looked at:
> >
> > struct inet_protosw;
> > proto_{register,unregister}, etc?
>
> Yes, I know inet_protosw and inet_{,un}register_protosw(). But we
> can't use inet_register_protosw().
>
> And can_proto_register() does use proto_register(). What exactly do
> you want to suggest?
Sorry, I was in a hurry and didn't completed my thoughts on how to share
more code and data structures.
My first reaction was: hey, struct can_proto has almost the same
definition as struct inet_protosw, and can_proto_register() looks like
inet_register_protosw().
can_proto_register() calls proto_register, inet_register_protosw
doesn't. But protocols such as DCCP and SCTP, call both
inet_register_protosw and proto_register. Perhaps we can make
inet_register_protosw behave like can_proto_register and do the
proto_register(inet_protosw->prot) for us.
Looking at inet_init in net/ipv4/af_inet.c we see that we do the same
for udp, tcp and raw too. There we also call proto_register +
inet_register_protosw.
See the possibilites for code sharing? Having just one way of
registering protocols would reduce complexity for new protocol writers
and for people that browse the code only when trying to fix some problem
and don't want to get lost in many ways of doing the same thing.
struct can_proto could be removed and struct inet_protosw could be
renamed to reflect the fact that it is, after all, not inet specific at
all.
So this is not something to "fix" on your implementation. It looks OK.
But we could use more hands on reducing complexity on the Linux network
protocol infrastructure and you would get free fixes and improvements
when people improve the inet protocols 8)
DCCP has been collecting dividends for quite a while for working like
that 8)
- Arnaldo
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-10-05 10:49 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #10 Urs Thuermann
@ 2007-10-05 10:49 ` Urs Thuermann
0 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-10-05 10:49 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Thomas Gleixner, Arnaldo Carvalho de Melo, Oliver Hartkopp,
Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60163 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 78 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 969 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 120 +++++
net/can/proc.c | 532 +++++++++++++++++++++++++
10 files changed, 1936 insertions(+)
Index: net-2.6.24/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.24/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/core.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20071004"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
+#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
+ KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
+ __func__, ##args) : 0)
+#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
+#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(fmt, args...)
+#define DBG_FRAME(fmt, cf)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.24/net/Kconfig
===================================================================
--- net-2.6.24.orig/net/Kconfig 2007-10-05 08:11:10.000000000 +0200
+++ net-2.6.24/net/Kconfig 2007-10-05 08:11:15.000000000 +0200
@@ -218,6 +218,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.24/net/Makefile
===================================================================
--- net-2.6.24.orig/net/Makefile 2007-10-05 08:11:10.000000000 +0200
+++ net-2.6.24/net/Makefile 2007-10-05 08:11:15.000000000 +0200
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.24/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Kconfig 2007-10-05 11:16:25.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages. Select this if you are having a problem with CAN
+ support and want to see more of what is going on.
Index: net-2.6.24/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/Makefile 2007-10-05 11:15:08.000000000 +0200
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.24/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.c 2007-10-05 11:09:07.000000000 +0200
@@ -0,0 +1,969 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+static __initdata 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)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+#define DBG_PREFIX "can"
+#define DBG_VAR can_debug
+static int can_debug __read_mostly;
+module_param_named(debug, can_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int ret = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ ret = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (ret == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (ret) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for success and correct type */
+ if (!cp || cp->type != sock->type) {
+ ret = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ ret = -EPERM;
+ goto errout;
+ }
+
+ sock->ops = cp->ops;
+
+ sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, 1);
+ if (!sk) {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ sock_init_data(sock, sk);
+ sk->sk_destruct = can_sock_destruct;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ ret = sk->sk_prot->init(sk);
+
+ if (ret) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ return ret;
+}
+
+/*
+ * 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+
+ 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.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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 ret = 0;
+
+ /* insert new receiver (dev,canid,mask) -> (func,data) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ ret = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, 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:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf)
+{
+ char idbuf[12];
+ char hexbuf[28];
+ int dlc;
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
+ else
+ sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ sprintf(hexbuf, "(RTR)");
+ else
+ hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
+
+ printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d\n",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+
+ print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->head, skb->end - skb->head, 0);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.24/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/af_can.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.24/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/net/can/proc.c 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ pstats.user_reset++;
+ }
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+ unsigned long count)
+{
+ unsigned long ret = 0;
+
+ 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;
+ }
+
+ ret = (count * HZ) / (newjif - oldjif);
+
+ return ret;
+}
+
+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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.tx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.24/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.24/include/linux/can/error.h 2007-10-05 08:11:15.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-14 12:13 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25 Urs Thuermann
@ 2007-11-14 12:13 ` Urs Thuermann
2007-11-14 21:38 ` Stephen Hemminger
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-11-14 12:13 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy; +Cc: Urs Thuermann, Oliver Hartkopp
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60282 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 78 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 25 +
net/can/Makefile | 6
net/can/af_can.c | 975 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 120 +++++
net/can/proc.c | 532 +++++++++++++++++++++++++
10 files changed, 1942 insertions(+)
Index: net-2.6.25/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can.h 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.25/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can/core.h 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,78 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20071027"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
+#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
+ KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
+ __func__, ##args) : 0)
+#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
+#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(fmt, args...)
+#define DBG_FRAME(fmt, cf)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6.25/net/Kconfig
===================================================================
--- net-2.6.25.orig/net/Kconfig 2007-11-14 13:04:26.000000000 +0100
+++ net-2.6.25/net/Kconfig 2007-11-14 13:04:49.000000000 +0100
@@ -218,6 +218,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.25/net/Makefile
===================================================================
--- net-2.6.25.orig/net/Makefile 2007-11-14 13:04:26.000000000 +0100
+++ net-2.6.25/net/Makefile 2007-11-14 13:04:49.000000000 +0100
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.25/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/Kconfig 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,25 @@
+#
+# 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.
+
+config CAN_DEBUG_CORE
+ bool "CAN Core debugging messages"
+ depends on CAN
+ ---help---
+ Say Y here if you want the CAN core to produce a bunch of debug
+ messages. Select this if you are having a problem with CAN
+ support and want to see more of what is going on.
Index: net-2.6.25/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/Makefile 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.25/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/af_can.c 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,975 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+static __initdata 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)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+#define DBG_PREFIX "can"
+#define DBG_VAR can_debug
+static int can_debug __read_mostly;
+module_param_named(debug, can_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(proto_tab_lock);
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats stats; /* packet statistics */
+struct s_pstats pstats; /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ DBG("called for sock %p\n", sk);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int err = 0;
+
+ DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ err = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (err == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (err) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for available protocol and correct usage */
+
+ if (!cp)
+ return -EPROTONOSUPPORT;
+
+ if (cp->type != sock->type) {
+ err = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ err = -EPERM;
+ 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;
+
+ DBG("created sock: %p\n", sk);
+
+ if (sk->sk_prot->init)
+ err = sk->sk_prot->init(sk);
+
+ if (err) {
+ /* release sk on errors */
+ sock_orphan(sk);
+ sock_put(sk);
+ }
+
+ errout:
+ module_put(cp->prot->owner);
+ 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+ 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.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ stats.tx_frames++;
+ stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d = NULL;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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) */
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+ "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&rcv_lists_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++;
+
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
+ DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ kmem_cache_free(rcv_cache, r);
+ err = -ENODEV;
+ }
+
+ spin_unlock(&rcv_lists_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ DBG("removing dev_rcv_list at %p\n", d);
+ kfree(d);
+}
+
+/*
+ * 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);
+
+ DBG("removing receiver at %p\n", r);
+ 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+ dev, DNAME(dev), can_id, mask, func, data);
+
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, 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:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ goto out;
+ }
+
+ hlist_del_rcu(&r->list);
+ d->entries--;
+
+ if (pstats.rcv_entries > 0)
+ pstats.rcv_entries--;
+
+ /* remove device structure requested by NETDEV_UNREGISTER */
+ if (d->remove_on_zero_entries && !d->entries) {
+ DBG("removing dev_rcv_list for %s on zero entries\n",
+ dev->name);
+ hlist_del_rcu(&d->list);
+ } else
+ d = NULL;
+
+ out:
+ spin_unlock(&rcv_lists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ DBG("skbuff %p cloned to %p\n", skb, clone);
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+ if (can_id & r->mask) {
+ DBG("match on rx_err skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ return matches;
+ }
+
+ /* check for unfiltered entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+ DBG("match on rx_all skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+ if ((can_id & r->mask) == r->can_id) {
+ DBG("match on rx_fil skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check for inverted can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ DBG("match on rx_inv skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ DBG("match on rx_eff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ DBG("match on rx_sff skbuff %p\n", skb);
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ DBG("received skbuff on device %s, ptype %04x\n",
+ dev->name, ntohs(pt->type));
+ DBG_SKB(skb);
+ DBG_FRAME("can: can_rcv: received CAN frame",
+ (struct can_frame *)skb->data);
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* update statistics */
+ stats.rx_frames++;
+ stats.rx_frames_delta++;
+
+ rcu_read_lock();
+
+ /* deliver the packet to sockets listening on all devices */
+ matches = can_rcv_filter(&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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ DBG("freeing skbuff %p\n", skb);
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ stats.matches++;
+ stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ DBG("msg %ld for dev %p (%s idx %d)\n",
+ msg, dev, dev->name, dev->ifindex);
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&d->list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ DBG("remove dev_rcv_list for %s (%d entries)\n",
+ dev->name, d->entries);
+
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&rcv_lists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf)
+{
+ char idbuf[12];
+ char hexbuf[28];
+ int dlc;
+
+ dlc = cf->can_dlc;
+ if (dlc > 8)
+ dlc = 8;
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
+ else
+ sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ sprintf(hexbuf, "(RTR)");
+ else
+ hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
+
+ printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
+ KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
+ "h,d,t,e,l: %p %+d %+d %+d, %d\n",
+ skb, skb->dev ? skb->dev->ifindex : -1,
+ ntohs(skb->protocol),
+ atomic_read(&skb->users),
+ atomic_read(&(skb_shinfo(skb)->dataref)),
+ skb_shinfo(skb)->nr_frags,
+ skb->head, skb->data - skb->head,
+ skb->tail - skb->head, skb->end - skb->head, skb->len);
+
+ print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
+ 16, 1, skb->head, skb->end - skb->head, 0);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock(&rcv_lists_lock);
+
+ if (stats_timer) {
+ /* the statistics are updated every second (timer triggered) */
+ init_timer(&stattimer);
+ stattimer.function = can_stat_update;
+ stattimer.data = 0;
+ /* update every second */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ /* start statistics timer */
+ add_timer(&stattimer);
+ } else
+ stattimer.function = NULL;
+
+ can_init_proc();
+
+ /* protocol register */
+ sock_register(&can_family_ops);
+ register_netdevice_notifier(&can_netdev_notifier);
+ dev_add_pack(&can_packet);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&stattimer);
+
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove rx_dev_list */
+ spin_lock(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&rcv_lists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.25/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/af_can.h 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ int remove_on_zero_entries;
+ int entries;
+};
+
+/* statistic structures */
+
+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;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+ unsigned long stats_reset;
+ unsigned long user_reset;
+ unsigned long rcv_entries;
+ unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats stats; /* packet statistics */
+extern struct s_pstats pstats; /* receive list statistics */
+extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6.25/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/proc.c 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,532 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+ /*
+ * This memset function is called from a timer context (when
+ * stattimer is active which is the default) OR in a process
+ * context (reading the proc_fs when stattimer is disabled).
+ */
+ memset(&stats, 0, sizeof(stats));
+ stats.jiffies_init = jiffies;
+
+ pstats.stats_reset++;
+
+ if (user_reset) {
+ user_reset = 0;
+ 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 < stats.jiffies_init)
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.rx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* prevent overflow in calc_rate() */
+ if (stats.tx_frames > (ULONG_MAX / HZ))
+ can_init_stats();
+
+ /* matches overflow - very improbable */
+ if (stats.matches > (ULONG_MAX / 100))
+ can_init_stats();
+
+ /* calc total values */
+ if (stats.rx_frames)
+ stats.total_rx_match_ratio = (stats.matches * 100) /
+ stats.rx_frames;
+
+ stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+ stats.tx_frames);
+ stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+ stats.rx_frames);
+
+ /* calc current values */
+ if (stats.rx_frames_delta)
+ stats.current_rx_match_ratio =
+ (stats.matches_delta * 100) / stats.rx_frames_delta;
+
+ stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+ stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+ /* check / update maximum values */
+ if (stats.max_tx_rate < stats.current_tx_rate)
+ stats.max_tx_rate = stats.current_tx_rate;
+
+ if (stats.max_rx_rate < stats.current_rx_rate)
+ stats.max_rx_rate = stats.current_rx_rate;
+
+ if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+ stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+ /* clear values for 'current rate' calculation */
+ stats.tx_frames_delta = 0;
+ stats.rx_frames_delta = 0;
+ stats.matches_delta = 0;
+
+ /* restart timer (one second) */
+ stattimer.expires = round_jiffies(jiffies + HZ);
+ add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n", stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ pstats.rcv_entries_max);
+
+ if (pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ pstats.stats_reset);
+
+ if (pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ pstats.stats_reset + 1);
+
+ } else {
+ if (stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.25/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can/error.h 2007-11-14 13:04:49.000000000 +0100
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-14 12:13 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
@ 2007-11-14 21:38 ` Stephen Hemminger
2007-11-15 7:40 ` Oliver Hartkopp
2007-11-15 11:36 ` Urs Thuermann
0 siblings, 2 replies; 86+ messages in thread
From: Stephen Hemminger @ 2007-11-14 21:38 UTC (permalink / raw)
To: Urs Thuermann
Cc: netdev, David Miller, Patrick McHardy, Urs Thuermann,
Oliver Hartkopp
On Wed, 14 Nov 2007 13:13:41 +0100
Urs Thuermann <urs.thuermann@gmx.de> wrote:
> This patch adds the CAN core functionality but no protocols or drivers.
> No protocol implementations are included here. They come as separate
> patches. Protocol numbers are already in include/linux/can.h.
>
> Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
>
> ---
> include/linux/can.h | 111 +++++
> include/linux/can/core.h | 78 +++
> include/linux/can/error.h | 93 ++++
> net/Kconfig | 1
> net/Makefile | 1
> net/can/Kconfig | 25 +
> net/can/Makefile | 6
> net/can/af_can.c | 975 ++++++++++++++++++++++++++++++++++++++++++++++
> net/can/af_can.h | 120 +++++
> net/can/proc.c | 532 +++++++++++++++++++++++++
> 10 files changed, 1942 insertions(+)
> Index: net-2.6.25/include/linux/can/core.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/include/linux/can/core.h 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,78 @@
> +/*
> + * linux/can/core.h
> + *
> + * Protoypes and definitions for CAN protocol modules using the PF_CAN core
> + *
> + * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
> + * Urs Thuermann <urs.thuermann@volkswagen.de>
> + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
> + * All rights reserved.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#ifndef CAN_CORE_H
> +#define CAN_CORE_H
> +
> +#include <linux/can.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +
> +#define CAN_VERSION "20071027"
> +
> +/* increment this number each time you change some user-space interface */
> +#define CAN_ABI_VERSION "8"
> +
> +#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
> +
> +#define DNAME(dev) ((dev) ? (dev)->name : "any")
> +
> +/**
> + * struct can_proto - CAN protocol structure
> + * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
> + * @protocol: protocol number in socket() syscall.
> + * @capability: capability needed to open the socket, or -1 for no restriction.
> + * @ops: pointer to struct proto_ops for sock->ops.
> + * @prot: pointer to struct proto structure.
> + */
> +struct can_proto {
> + int type;
> + int protocol;
> + int capability;
> + struct proto_ops *ops;
> + struct proto *prot;
> +};
> +
> +/* function prototypes for the CAN networklayer core (af_can.c) */
> +
> +extern int can_proto_register(struct can_proto *cp);
> +extern void can_proto_unregister(struct can_proto *cp);
> +
> +extern 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);
> +
> +extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
> + canid_t mask,
> + void (*func)(struct sk_buff *, void *),
> + void *data);
> +
> +extern int can_send(struct sk_buff *skb, int loop);
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +extern void can_debug_skb(struct sk_buff *skb);
> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
> +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
> + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
> + __func__, ##args) : 0)
> +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
> +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
> +#else
> +#define DBG(fmt, args...)
> +#define DBG_FRAME(fmt, cf)
> +#define DBG_SKB(skb)
> +#endif
This non-standard debugging seems like it needs a better interface.
Also, need paren's around (DBG_VAR & 1) and don't use UPPERCASE for
variable names.
> +
> +#endif /* CAN_CORE_H */
> Index: net-2.6.25/net/Kconfig
> ===================================================================
> --- net-2.6.25.orig/net/Kconfig 2007-11-14 13:04:26.000000000 +0100
> +++ net-2.6.25/net/Kconfig 2007-11-14 13:04:49.000000000 +0100
> @@ -218,6 +218,7 @@
> endmenu
>
> source "net/ax25/Kconfig"
> +source "net/can/Kconfig"
> source "net/irda/Kconfig"
> source "net/bluetooth/Kconfig"
> source "net/rxrpc/Kconfig"
> Index: net-2.6.25/net/Makefile
> ===================================================================
> --- net-2.6.25.orig/net/Makefile 2007-11-14 13:04:26.000000000 +0100
> +++ net-2.6.25/net/Makefile 2007-11-14 13:04:49.000000000 +0100
> @@ -34,6 +34,7 @@
> obj-$(CONFIG_NETROM) += netrom/
> obj-$(CONFIG_ROSE) += rose/
> obj-$(CONFIG_AX25) += ax25/
> +obj-$(CONFIG_CAN) += can/
> obj-$(CONFIG_IRDA) += irda/
> obj-$(CONFIG_BT) += bluetooth/
> obj-$(CONFIG_SUNRPC) += sunrpc/
> Index: net-2.6.25/net/can/Kconfig
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/net/can/Kconfig 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,25 @@
> +#
> +# 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.
> +
> +config CAN_DEBUG_CORE
> + bool "CAN Core debugging messages"
> + depends on CAN
> + ---help---
> + Say Y here if you want the CAN core to produce a bunch of debug
> + messages. Select this if you are having a problem with CAN
> + support and want to see more of what is going on.
> Index: net-2.6.25/net/can/Makefile
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/net/can/Makefile 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for the Linux Controller Area Network core.
> +#
> +
> +obj-$(CONFIG_CAN) += can.o
> +can-objs := af_can.o proc.o
> Index: net-2.6.25/net/can/af_can.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/net/can/af_can.c 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,975 @@
> +/*
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.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 <net/net_namespace.h>
> +#include <net/sock.h>
> +
> +#include "af_can.h"
> +
> +static __initdata 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)");
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +#define DBG_PREFIX "can"
> +#define DBG_VAR can_debug
> +static int can_debug __read_mostly;
> +module_param_named(debug, can_debug, int, S_IRUGO);
> +MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
> +#endif
> +
> +HLIST_HEAD(rx_dev_list);
Please either make rx_dev_list static or call it can_rx_dev_list
to avoid name conflices.
> +static struct dev_rcv_lists rx_alldev_list;
> +static DEFINE_SPINLOCK(rcv_lists_lock);
> +
> +static struct kmem_cache *rcv_cache __read_mostly;
> +
> +/* table of registered CAN protocols */
> +static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
> +static DEFINE_SPINLOCK(proto_tab_lock);
> +
> +struct timer_list stattimer; /* timer for statistics update */
> +struct s_stats stats; /* packet statistics */
> +struct s_pstats pstats; /* receive list statistics */
More global variables without prefix.
> +/*
> + * af_can socket functions
> + */
> +
> +static 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;
> + }
> +}
> +
> +static void can_sock_destruct(struct sock *sk)
> +{
> + DBG("called for sock %p\n", sk);
> +
> + skb_queue_purge(&sk->sk_receive_queue);
> +}
> +
> +static int can_create(struct net *net, struct socket *sock, int protocol)
> +{
> + struct sock *sk;
> + struct can_proto *cp;
> + char module_name[sizeof("can-proto-000")];
> + int err = 0;
> +
> + DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
> +
> + sock->state = SS_UNCONNECTED;
> +
> + if (protocol < 0 || protocol >= CAN_NPROTO)
> + return -EINVAL;
> +
> + if (net != &init_net)
> + return -EAFNOSUPPORT;
> +
> + DBG("looking up proto %d in proto_tab[]\n", protocol);
> +
> + /* try to load protocol module, when CONFIG_KMOD is defined */
> + if (!proto_tab[protocol]) {
> + sprintf(module_name, "can-proto-%d", protocol);
> + err = request_module(module_name);
> +
> + /*
> + * In case of error we only print a message but don't
> + * return the error code immediately. Below we will
> + * return -EPROTONOSUPPORT
> + */
> + if (err == -ENOSYS) {
> + if (printk_ratelimit())
> + printk(KERN_INFO "can: request_module(%s)"
> + " not implemented.\n", module_name);
> + } else if (err) {
> + if (printk_ratelimit())
> + printk(KERN_ERR "can: request_module(%s)"
> + " failed.\n", module_name);
> + }
> + }
> +
> + spin_lock(&proto_tab_lock);
> + cp = proto_tab[protocol];
> + if (cp && !try_module_get(cp->prot->owner))
> + cp = NULL;
> + spin_unlock(&proto_tab_lock);
> +
> + /* check for available protocol and correct usage */
> +
> + if (!cp)
> + return -EPROTONOSUPPORT;
> +
> + if (cp->type != sock->type) {
> + err = -EPROTONOSUPPORT;
> + goto errout;
> + }
> +
> + if (cp->capability >= 0 && !capable(cp->capability)) {
> + err = -EPERM;
> + 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;
> +
> + DBG("created sock: %p\n", sk);
> +
> + if (sk->sk_prot->init)
> + err = sk->sk_prot->init(sk);
> +
> + if (err) {
> + /* release sk on errors */
> + sock_orphan(sk);
> + sock_put(sk);
> + }
> +
> + errout:
> + module_put(cp->prot->owner);
> + 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!)
> + *
> + * 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
> + */
> +int can_send(struct sk_buff *skb, int loop)
> +{
> + int err;
> +
> + if (skb->dev->type != ARPHRD_CAN) {
> + kfree_skb(skb);
> + return -EPERM;
> + }
> +
> + if (!(skb->dev->flags & IFF_UP)) {
> + kfree_skb(skb);
> + return -ENETDOWN;
> + }
> +
> + skb->protocol = htons(ETH_P_CAN);
> + 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.
> + */
> + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
> +
> + if (!newskb) {
> + kfree_skb(skb);
> + return -ENOMEM;
> + }
> +
> + newskb->sk = skb->sk;
> + newskb->ip_summed = CHECKSUM_UNNECESSARY;
> + newskb->pkt_type = PACKET_BROADCAST;
> + netif_rx(newskb);
> + }
> + } 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);
> +
> + /* update statistics */
> + stats.tx_frames++;
> + stats.tx_frames_delta++;
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_send);
> +
> +/*
> + * af_can rx path
> + */
> +
> +static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
> +{
> + struct dev_rcv_lists *d = NULL;
> + struct hlist_node *n;
> +
> + /*
> + * find receive list for this device
> + *
> + * The hlist_for_each_entry*() macros curse through the list
> + * using the pointer variable n and set d to the containing
> + * struct in each list iteration. Therefore, after list
> + * iteration, d is unmodified when the list is empty, and it
> + * points to last list element, when the list is non-empty
> + * but no match in the loop body is found. I.e. d is *not*
> + * NULL when no match is found. We can, however, use the
> + * cursor variable n to decide if a match was found.
> + */
> +
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> + if (d->dev == dev)
> + break;
> + }
> +
> + return n ? d : NULL;
> +}
> +
> +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 error frames */
> + if (*mask & CAN_ERR_FLAG) {
> + /* clear CAN_ERR_FLAG in list entry */
> + *mask &= CAN_ERR_MASK;
> + return &d->rx[RX_ERR];
> + }
> +
> + /* ensure valid values in can_mask */
> + if (*mask & CAN_EFF_FLAG)
> + *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
> + else
> + *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
> +
> + /* 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];
> +
> + /* use extra filterset for the subscription of exactly *ONE* can_id */
> + if (*can_id & CAN_EFF_FLAG) {
> + if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
> + /* RFC: a use-case for hash-tables in the future? */
> + return &d->rx[RX_EFF];
> + }
> + } else {
> + if (*mask == CAN_SFF_MASK)
> + 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 indentification
> + *
> + * 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 frames (CAN_ERR_FLAG bit set in mask).
> + *
> + * 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) */
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
> + "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
> +
> + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
> + if (!r)
> + return -ENOMEM;
> +
> + spin_lock(&rcv_lists_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++;
> +
> + pstats.rcv_entries++;
> + if (pstats.rcv_entries_max < pstats.rcv_entries)
> + pstats.rcv_entries_max = pstats.rcv_entries;
> + } else {
> + DBG("receive list not found for dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + kmem_cache_free(rcv_cache, r);
> + err = -ENODEV;
> + }
> +
> + spin_unlock(&rcv_lists_lock);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_rx_register);
> +
> +/*
> + * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
> + */
> +static void can_rx_delete_device(struct rcu_head *rp)
> +{
> + struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
> +
> + DBG("removing dev_rcv_list at %p\n", d);
> + kfree(d);
> +}
> +
> +/*
> + * 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);
> +
> + DBG("removing receiver at %p\n", r);
> + 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 hlist_node *next;
> + struct dev_rcv_lists *d;
> +
> + DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
> + dev, DNAME(dev), can_id, mask, func, data);
> +
> + spin_lock(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (!d) {
> + printk(KERN_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, next, 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:
> + * If no matching list item was found, the list cursor variable next
> + * will be NULL, while r will point to the last item of the list.
> + */
> +
> + if (!next) {
> + printk(KERN_ERR "BUG: receive list entry not found for "
> + "dev %s, id %03X, mask %03X\n",
> + DNAME(dev), can_id, mask);
> + r = NULL;
> + d = NULL;
> + goto out;
> + }
> +
> + hlist_del_rcu(&r->list);
> + d->entries--;
> +
> + if (pstats.rcv_entries > 0)
> + pstats.rcv_entries--;
> +
> + /* remove device structure requested by NETDEV_UNREGISTER */
> + if (d->remove_on_zero_entries && !d->entries) {
> + DBG("removing dev_rcv_list for %s on zero entries\n",
> + dev->name);
> + hlist_del_rcu(&d->list);
> + } else
> + d = NULL;
> +
> + out:
> + spin_unlock(&rcv_lists_lock);
> +
> + /* schedule the receiver item for deletion */
> + if (r)
> + call_rcu(&r->rcu, can_rx_delete_receiver);
> +
> + /* schedule the device structure for deletion */
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +}
> +EXPORT_SYMBOL(can_rx_unregister);
> +
> +static inline void deliver(struct sk_buff *skb, struct receiver *r)
> +{
> + struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
> +
> + DBG("skbuff %p cloned to %p\n", skb, clone);
> + if (clone) {
> + clone->sk = skb->sk;
> + r->func(clone, r->data);
> + r->matches++;
> + }
> +}
> +
> +static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> + 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 frame entries only */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
> + if (can_id & r->mask) {
> + DBG("match on rx_err skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + return matches;
> + }
> +
> + /* check for unfiltered entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
> + DBG("match on rx_all skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> +
> + /* check for can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
> + if ((can_id & r->mask) == r->can_id) {
> + DBG("match on rx_fil skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check for inverted can_id/mask entries */
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
> + if ((can_id & r->mask) != r->can_id) {
> + DBG("match on rx_inv skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + /* check CAN_ID specific entries */
> + if (can_id & CAN_EFF_FLAG) {
> + hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
> + if (r->can_id == can_id) {
> + DBG("match on rx_eff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> + } else {
> + can_id &= CAN_SFF_MASK;
> + hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
> + DBG("match on rx_sff skbuff %p\n", skb);
> + deliver(skb, r);
> + matches++;
> + }
> + }
> +
> + return matches;
> +}
> +
> +static int can_rcv(struct sk_buff *skb, struct net_device *dev,
> + struct packet_type *pt, struct net_device *orig_dev)
> +{
> + struct dev_rcv_lists *d;
> + int matches;
> +
> + DBG("received skbuff on device %s, ptype %04x\n",
> + dev->name, ntohs(pt->type));
> + DBG_SKB(skb);
> + DBG_FRAME("can: can_rcv: received CAN frame",
> + (struct can_frame *)skb->data);
> +
> + if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
> + kfree_skb(skb);
> + return 0;
> + }
> +
> + /* update statistics */
> + stats.rx_frames++;
> + stats.rx_frames_delta++;
> +
> + rcu_read_lock();
> +
> + /* deliver the packet to sockets listening on all devices */
> + matches = can_rcv_filter(&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();
> +
> + /* free the skbuff allocated by the netdevice driver */
> + DBG("freeing skbuff %p\n", skb);
> + kfree_skb(skb);
> +
> + if (matches > 0) {
> + stats.matches++;
> + stats.matches_delta++;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * 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(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> + int err = 0;
> +
> + if (proto < 0 || proto >= CAN_NPROTO) {
> + printk(KERN_ERR "can: protocol number %d out of range\n",
> + proto);
> + return -EINVAL;
> + }
> +
> + spin_lock(&proto_tab_lock);
> + if (proto_tab[proto]) {
> + printk(KERN_ERR "can: protocol %d already registered\n",
> + proto);
> + err = -EBUSY;
> + goto errout;
> + }
> +
> + err = proto_register(cp->prot, 0);
> + if (err < 0)
> + goto errout;
> +
> + proto_tab[proto] = cp;
> +
> + /* use generic ioctl function if the module doesn't bring its own */
> + if (!cp->ops->ioctl)
> + cp->ops->ioctl = can_ioctl;
> +
> + errout:
> + spin_unlock(&proto_tab_lock);
> +
> + return err;
> +}
> +EXPORT_SYMBOL(can_proto_register);
> +
> +/**
> + * can_proto_unregister - unregister CAN transport protocol
> + * @cp: pointer to CAN protocol structure
> + */
> +void can_proto_unregister(struct can_proto *cp)
> +{
> + int proto = cp->protocol;
> +
> + spin_lock(&proto_tab_lock);
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
> + proto);
> + }
> + proto_unregister(cp->prot);
> + proto_tab[proto] = NULL;
> + spin_unlock(&proto_tab_lock);
> +}
> +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 *data)
> +{
> + struct net_device *dev = (struct net_device *)data;
> + struct dev_rcv_lists *d;
> +
> + DBG("msg %ld for dev %p (%s idx %d)\n",
> + msg, dev, dev->name, dev->ifindex);
> +
> + if (dev->nd_net != &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
> + *
> + * N.B. zeroing the struct is the correct initialization
> + * for the embedded hlist_head structs.
> + * Another list type, e.g. list_head, would require
> + * explicit initialization.
> + */
> +
> + DBG("creating new dev_rcv_lists for %s\n", dev->name);
> +
> + d = kzalloc(sizeof(*d), GFP_KERNEL);
> + if (!d) {
> + printk(KERN_ERR
> + "can: allocation of receive list failed\n");
> + return NOTIFY_DONE;
> + }
> + d->dev = dev;
> +
> + spin_lock(&rcv_lists_lock);
> + hlist_add_head_rcu(&d->list, &rx_dev_list);
> + spin_unlock(&rcv_lists_lock);
> +
> + break;
> +
> + case NETDEV_UNREGISTER:
> + spin_lock(&rcv_lists_lock);
> +
> + d = find_dev_rcv_lists(dev);
> + if (d) {
> + DBG("remove dev_rcv_list for %s (%d entries)\n",
> + dev->name, d->entries);
> +
> + if (d->entries) {
> + d->remove_on_zero_entries = 1;
> + d = NULL;
> + } else
> + hlist_del_rcu(&d->list);
> + } else
> + printk(KERN_ERR "can: notifier: receive list not "
> + "found for dev %s\n", dev->name);
> +
> + spin_unlock(&rcv_lists_lock);
> +
> + if (d)
> + call_rcu(&d->rcu, can_rx_delete_device);
> +
> + break;
> + }
> +
> + return NOTIFY_DONE;
> +}
> +
> +/*
> + * af_can debugging stuff
> + */
> +
> +#ifdef CONFIG_CAN_DEBUG_CORE
> +
> +/**
> + * can_debug_cframe - print CAN frame
> + * @msg: pointer to message printed before the given CAN frame
> + * @cf: pointer to CAN frame
> + */
> +void can_debug_cframe(const char *msg, struct can_frame *cf)
> +{
> + char idbuf[12];
> + char hexbuf[28];
> + int dlc;
> +
> + dlc = cf->can_dlc;
> + if (dlc > 8)
> + dlc = 8;
> +
> + if (cf->can_id & CAN_EFF_FLAG)
> + sprintf(idbuf, "<%08X>", cf->can_id & CAN_EFF_MASK);
> + else
> + sprintf(idbuf, "<%03X>", cf->can_id & CAN_SFF_MASK);
> +
> + if (cf->can_id & CAN_RTR_FLAG)
> + sprintf(hexbuf, "(RTR)");
> + else
> + hex_dump_to_buffer(cf->data, dlc, 16, 1, hexbuf, 28, 0);
> +
> + printk(KERN_DEBUG "%s: %s [%d] %s\n", msg, idbuf, dlc, hexbuf);
> +}
> +EXPORT_SYMBOL(can_debug_cframe);
> +
> +/**
> + * can_debug_skb - print socket buffer content to kernel log
> + * @skb: pointer to socket buffer
> + */
> +void can_debug_skb(struct sk_buff *skb)
> +{
> + printk(KERN_DEBUG " skbuff at %p, dev: %d, proto: %04x\n"
> + KERN_DEBUG " users: %d, dataref: %d, nr_frags: %d, "
> + "h,d,t,e,l: %p %+d %+d %+d, %d\n",
> + skb, skb->dev ? skb->dev->ifindex : -1,
> + ntohs(skb->protocol),
> + atomic_read(&skb->users),
> + atomic_read(&(skb_shinfo(skb)->dataref)),
> + skb_shinfo(skb)->nr_frags,
> + skb->head, skb->data - skb->head,
> + skb->tail - skb->head, skb->end - skb->head, skb->len);
> +
> + print_hex_dump(KERN_DEBUG, "skb_head: ", DUMP_PREFIX_NONE,
> + 16, 1, skb->head, skb->end - skb->head, 0);
> +}
> +EXPORT_SYMBOL(can_debug_skb);
> +
> +#endif
> +
> +/*
> + * af_can module init/exit functions
> + */
> +
> +static struct packet_type can_packet __read_mostly = {
> + .type = __constant_htons(ETH_P_CAN),
> + .dev = NULL,
> + .func = can_rcv,
> +};
> +
> +static struct net_proto_family can_family_ops __read_mostly = {
> + .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)
> +{
> + printk(banner);
> +
> + rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
> + 0, 0, NULL);
> + if (!rcv_cache)
> + return -ENOMEM;
> +
> + /*
> + * Insert rx_alldev_list for reception on all devices.
> + * This struct is zero initialized which is correct for the
> + * embedded hlist heads, the dev pointer, and the entries counter.
> + */
> +
> + spin_lock(&rcv_lists_lock);
> + hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
> + spin_unlock(&rcv_lists_lock);
> +
> + if (stats_timer) {
> + /* the statistics are updated every second (timer triggered) */
> + init_timer(&stattimer);
> + stattimer.function = can_stat_update;
> + stattimer.data = 0;
> + /* update every second */
> + stattimer.expires = round_jiffies(jiffies + HZ);
> + /* start statistics timer */
> + add_timer(&stattimer);
> + } else
> + stattimer.function = NULL;
> +
> + can_init_proc();
> +
> + /* protocol register */
> + sock_register(&can_family_ops);
> + register_netdevice_notifier(&can_netdev_notifier);
> + dev_add_pack(&can_packet);
> +
> + return 0;
> +}
> +
> +static __exit void can_exit(void)
> +{
> + struct dev_rcv_lists *d;
> + struct hlist_node *n, *next;
> +
> + if (stats_timer)
> + del_timer(&stattimer);
> +
> + can_remove_proc();
> +
> + /* protocol unregister */
> + dev_remove_pack(&can_packet);
> + unregister_netdevice_notifier(&can_netdev_notifier);
> + sock_unregister(PF_CAN);
> +
> + /* remove rx_dev_list */
> + spin_lock(&rcv_lists_lock);
> + hlist_del(&rx_alldev_list.list);
> + hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
> + hlist_del(&d->list);
> + kfree(d);
> + }
> + spin_unlock(&rcv_lists_lock);
> +
> + kmem_cache_destroy(rcv_cache);
> +}
> +
> +module_init(can_init);
> +module_exit(can_exit);
> Index: net-2.6.25/net/can/af_can.h
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/net/can/af_can.h 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,120 @@
> +/*
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#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;
> +};
> +
> +enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
> +
> +struct dev_rcv_lists {
> + struct hlist_node list;
> + struct rcu_head rcu;
> + struct net_device *dev;
> + struct hlist_head rx[RX_MAX];
> + struct hlist_head rx_sff[0x800];
> + int remove_on_zero_entries;
> + int entries;
> +};
> +
> +/* statistic structures */
> +
> +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;
> +}; /* can be reset e.g. by can_init_stats() */
> +
> +struct s_pstats {
> + unsigned long stats_reset;
> + unsigned long user_reset;
> + unsigned long rcv_entries;
> + unsigned long rcv_entries_max;
> +}; /* persistent statistics */
> +
> +/* function prototypes for the CAN networklayer procfs (proc.c) */
> +extern void can_init_proc(void);
> +extern void can_remove_proc(void);
> +extern void can_stat_update(unsigned long data);
> +
> +/* structures and variables from af_can.c needed in proc.c for reading */
> +extern struct timer_list stattimer; /* timer for statistics update */
> +extern struct s_stats stats; /* packet statistics */
> +extern struct s_pstats pstats; /* receive list statistics */
> +extern struct hlist_head rx_dev_list; /* rx dispatcher structures */
> +
> +#endif /* AF_CAN_H */
> Index: net-2.6.25/net/can/proc.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ net-2.6.25/net/can/proc.c 2007-11-14 13:04:49.000000000 +0100
> @@ -0,0 +1,532 @@
> +/*
> + * 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.
> + *
> + * Send feedback to <socketcan-users@lists.berlios.de>
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/proc_fs.h>
> +#include <linux/list.h>
> +#include <linux/rcupdate.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",
> + [RX_EFF] = "rx_eff",
> +};
> +
> +/*
> + * af_can statistics stuff
> + */
> +
> +static void can_init_stats(void)
> +{
> + /*
> + * This memset function is called from a timer context (when
> + * stattimer is active which is the default) OR in a process
> + * context (reading the proc_fs when stattimer is disabled).
> + */
> + memset(&stats, 0, sizeof(stats));
> + stats.jiffies_init = jiffies;
> +
> + pstats.stats_reset++;
> +
> + if (user_reset) {
> + user_reset = 0;
> + 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 < stats.jiffies_init)
> + can_init_stats();
> +
> + /* prevent overflow in calc_rate() */
> + if (stats.rx_frames > (ULONG_MAX / HZ))
> + can_init_stats();
> +
> + /* prevent overflow in calc_rate() */
> + if (stats.tx_frames > (ULONG_MAX / HZ))
> + can_init_stats();
> +
> + /* matches overflow - very improbable */
> + if (stats.matches > (ULONG_MAX / 100))
> + can_init_stats();
> +
> + /* calc total values */
> + if (stats.rx_frames)
> + stats.total_rx_match_ratio = (stats.matches * 100) /
> + stats.rx_frames;
> +
> + stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
> + stats.tx_frames);
> + stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
> + stats.rx_frames);
> +
> + /* calc current values */
> + if (stats.rx_frames_delta)
> + stats.current_rx_match_ratio =
> + (stats.matches_delta * 100) / stats.rx_frames_delta;
> +
> + stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
> + stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
> +
> + /* check / update maximum values */
> + if (stats.max_tx_rate < stats.current_tx_rate)
> + stats.max_tx_rate = stats.current_tx_rate;
> +
> + if (stats.max_rx_rate < stats.current_rx_rate)
> + stats.max_rx_rate = stats.current_rx_rate;
> +
> + if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
> + stats.max_rx_match_ratio = stats.current_rx_match_ratio;
> +
> + /* clear values for 'current rate' calculation */
> + stats.tx_frames_delta = 0;
> + stats.rx_frames_delta = 0;
> + stats.matches_delta = 0;
> +
> + /* restart timer (one second) */
> + stattimer.expires = round_jiffies(jiffies + HZ);
> + add_timer(&stattimer);
> +}
> +
> +/*
> + * proc read functions
> + *
> + * From known use-cases we expect about 10 entries in a receive list to be
> + * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
> + *
> + */
> +
> +static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
> + struct net_device *dev)
> +{
> + struct receiver *r;
> + struct hlist_node *n;
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(r, n, rx_list, list) {
> + char *fmt = (r->can_id & CAN_EFF_FLAG)?
> + " %-5s %08X %08x %08x %08x %8ld %s\n" :
> + " %-5s %03X %08x %08lx %08lx %8ld %s\n";
> +
> + len += snprintf(page + len, PAGE_SIZE - len, fmt,
> + DNAME(dev), r->can_id, r->mask,
> + (unsigned long)r->func, (unsigned long)r->data,
> + r->matches, r->ident);
> +
> + /* does a typical line fit into the current buffer? */
> +
> + /* 100 Bytes before end of buffer */
> + if (len > PAGE_SIZE - 100) {
> + /* mark output cut off */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (..)\n");
> + break;
> + }
> + }
> + rcu_read_unlock();
> +
> + return len;
> +}
> +
> +static int can_print_recv_banner(char *page, int len)
> +{
> + /*
> + * can1. 00000000 00000000 00000000
> + * ....... 0 tp20
> + */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " device can_id can_mask function"
> + " userdata matches ident\n");
> +
> + return len;
> +}
> +
> +static int can_proc_read_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld transmitted frames (TXF)\n", stats.tx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld received frames (RXF)\n", stats.rx_frames);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld matched frames (RXMF)\n", stats.matches);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% total match ratio (RXMR)\n",
> + stats.total_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total tx rate (TXR)\n",
> + stats.total_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s total rx rate (RXR)\n",
> + stats.total_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% current match ratio (CRXMR)\n",
> + stats.current_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current tx rate (CTXR)\n",
> + stats.current_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s current rx rate (CRXR)\n",
> + stats.current_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld %% max match ratio (MRXMR)\n",
> + stats.max_rx_match_ratio);
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max tx rate (MTXR)\n",
> + stats.max_tx_rate);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld frames/s max rx rate (MRXR)\n",
> + stats.max_rx_rate);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> + }
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld current receive list entries (CRCV)\n",
> + pstats.rcv_entries);
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld maximum receive list entries (MRCV)\n",
> + pstats.rcv_entries_max);
> +
> + if (pstats.stats_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\n %8ld statistic resets (STR)\n",
> + pstats.stats_reset);
> +
> + if (pstats.user_reset)
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " %8ld user statistic resets (USTR)\n",
> + pstats.user_reset);
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
The read interface should use seq_file interface rather than
formatting into page buffer.
> +static int can_proc_read_reset_stats(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + user_reset = 1;
> +
> + if (stattimer.function == can_stat_update) {
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Scheduled statistic reset #%ld.\n",
> + pstats.stats_reset + 1);
> +
> + } else {
> + if (stats.jiffies_init != jiffies)
> + can_init_stats();
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "Performed statistic reset #%ld.\n",
> + pstats.stats_reset);
> + }
> +
> + *eof = 1;
> + return len;
> +}
Why not have a write interface to do the reset?
> +static int can_proc_read_version(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
> + CAN_VERSION_STRING);
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + /* double cast to prevent GCC warning */
> + int idx = (int)(long)data;
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list '%s':\n", rx_list_name[idx]);
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> +
> + if (!hlist_empty(&d->rx[idx])) {
> + len = can_print_recv_banner(page, len);
> + len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
> + int count, int *eof, void *data)
> +{
> + int len = 0;
> + struct dev_rcv_lists *d;
> + struct hlist_node *n;
> +
> + /* RX_SFF */
> + len += snprintf(page + len, PAGE_SIZE - len,
> + "\nreceive list 'rx_sff':\n");
> +
> + rcu_read_lock();
> + hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
> + int i, all_empty = 1;
> + /* check wether at least one list is non-empty */
> + for (i = 0; i < 0x800; i++)
> + if (!hlist_empty(&d->rx_sff[i])) {
> + all_empty = 0;
> + break;
> + }
> +
> + if (!all_empty) {
> + len = can_print_recv_banner(page, len);
> + for (i = 0; i < 0x800; i++) {
> + if (!hlist_empty(&d->rx_sff[i]) &&
> + len < PAGE_SIZE - 100)
> + len = can_print_rcvlist(page, len,
> + &d->rx_sff[i],
> + d->dev);
> + }
> + } else
> + len += snprintf(page + len, PAGE_SIZE - len,
> + " (%s: no entry)\n", DNAME(d->dev));
> +
> + /* exit on end of buffer? */
> + if (len > PAGE_SIZE - 100)
> + break;
> + }
> + rcu_read_unlock();
> +
> + len += snprintf(page + len, PAGE_SIZE - len, "\n");
> +
> + *eof = 1;
> + return len;
> +}
> +
> +/*
> + * proc utility functions
> + */
> +
> +static struct proc_dir_entry *can_create_proc_readentry(const char *name,
> + mode_t mode,
> + read_proc_t *read_proc,
> + void *data)
> +{
> + if (can_dir)
> + return create_proc_read_entry(name, mode, can_dir, read_proc,
> + data);
> + else
> + return NULL;
> +}
> +
> +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;
> + }
> +
> + can_dir->owner = THIS_MODULE;
> +
> + /* own procfs entries from the AF_CAN core */
> + pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
> + can_proc_read_version, NULL);
> + pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
> + can_proc_read_stats, NULL);
> + pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
> + can_proc_read_reset_stats, NULL);
> + pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
> + can_proc_read_rcvlist, (void *)RX_ERR);
> + pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
> + can_proc_read_rcvlist, (void *)RX_ALL);
> + pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
> + can_proc_read_rcvlist, (void *)RX_FIL);
> + pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
> + can_proc_read_rcvlist, (void *)RX_INV);
> + pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
> + can_proc_read_rcvlist, (void *)RX_EFF);
> + pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
> + can_proc_read_rcvlist_sff, NULL);
> +}
> +
> +/*
> + * 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)
> + proc_net_remove(&init_net, "can");
> +}
>
Output from checkpatch:
WARNING: do not add new typedefs
#116: FILE: include/linux/can.h:41:
+typedef __u32 canid_t;
WARNING: do not add new typedefs
#124: FILE: include/linux/can.h:49:
+typedef __u32 can_err_mask_t;
ERROR: use tabs not spaces
#498: FILE: net/can/af_can.c:159:
+^I^I^I^I " not implemented.\n", module_name);$
WARNING: braces {} are not necessary for single statement blocks
#1080: FILE: net/can/af_can.c:741:
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
total: 1 errors, 3 warnings, 1954 lines checked
Your patch has style problems, please review. If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
--
Stephen Hemminger <shemminger@linux-foundation.org>
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-14 21:38 ` Stephen Hemminger
@ 2007-11-15 7:40 ` Oliver Hartkopp
2007-11-15 8:04 ` Joe Perches
2007-11-15 11:36 ` Urs Thuermann
1 sibling, 1 reply; 86+ messages in thread
From: Oliver Hartkopp @ 2007-11-15 7:40 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Urs Thuermann, netdev, David Miller, Patrick McHardy,
Urs Thuermann, Oliver Hartkopp
Stephen Hemminger wrote:
>> +#ifdef CONFIG_CAN_DEBUG_CORE
>> +extern void can_debug_skb(struct sk_buff *skb);
>> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
>> +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
>> + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
>> + __func__, ##args) : 0)
>> +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
>> +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
>> +#else
>> +#define DBG(fmt, args...)
>> +#define DBG_FRAME(fmt, cf)
>> +#define DBG_SKB(skb)
>> +#endif
>>
>
>
> This non-standard debugging seems like it needs a better interface.
> Also, need paren's around (DBG_VAR & 1) and don't use UPPERCASE for
> variable names.
>
Of course we do not use UPPERCASE variable names ;-)
The DBG_VAR define points to a module specific named debug variable,
e.g. in can-raw :
+#ifdef CONFIG_CAN_DEBUG_CORE
+#define DBG_PREFIX "can-raw"
+#define DBG_VAR raw_debug
+static int raw_debug;
+module_param_named(debug, raw_debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
This has been introduced in try#10 to have module specific debug
variable names requested by Arnaldo and Dave.
Regarding the paren's: I love adding paren's, Urs doesn't. But in this
case, there should be no reason for them - or am i wrong here?
>
> Output from checkpatch:
>
> WARNING: do not add new typedefs
> #116: FILE: include/linux/can.h:41:
> +typedef __u32 canid_t;
>
> WARNING: do not add new typedefs
> #124: FILE: include/linux/can.h:49:
> +typedef __u32 can_err_mask_t;
>
>
These definitions are structure definitions and pretty ok for that
reason - we had that discussion.
Please look into include/linux/can.h for details and if it looks ok for
you also then.
> ERROR: use tabs not spaces
> #498: FILE: net/can/af_can.c:159:
> +^I^I^I^I " not implemented.\n", module_name);$
>
Oops! Will be fixed.
> WARNING: braces {} are not necessary for single statement blocks
> #1080: FILE: net/can/af_can.c:741:
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
> + proto);
> + }
>
>
Dito.
Thanks for the review, Stephen.
I'll go through your other remarks with Urs today.
Oliver.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 7:40 ` Oliver Hartkopp
@ 2007-11-15 8:04 ` Joe Perches
2007-11-15 11:51 ` Urs Thuermann
0 siblings, 1 reply; 86+ messages in thread
From: Joe Perches @ 2007-11-15 8:04 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Stephen Hemminger, Urs Thuermann, netdev, David Miller,
Patrick McHardy, Urs Thuermann, Oliver Hartkopp
On Thu, 2007-11-15 at 08:40 +0100, Oliver Hartkopp wrote:
> Stephen Hemminger wrote:
> >> +#ifdef CONFIG_CAN_DEBUG_CORE
> >> +extern void can_debug_skb(struct sk_buff *skb);
> >> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
> >> +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
> >> + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
> >> + __func__, ##args) : 0)
> >> +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
> >> +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
> >> +#else
> >> +#define DBG(fmt, args...)
> >> +#define DBG_FRAME(fmt, cf)
> >> +#define DBG_SKB(skb)
> >> +#endif
I would prefer the more frequently used macro style:
#define DBG(fmt, args...) \
do { if (DBG_VAR & 1) printk(KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
__func__, ##args); } while (0)
#define DBG_FRAME(fmt, cf) \
do { if (DBG_VAR & 2) can_debug_cframe(fmt, cf); } while (0)
#define DBG_SKB(skb) \
do { if (DBG_VAR & 4) can_debug_skb(skb); } while (0)
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-14 21:38 ` Stephen Hemminger
2007-11-15 7:40 ` Oliver Hartkopp
@ 2007-11-15 11:36 ` Urs Thuermann
2007-11-15 15:09 ` Sam Ravnborg
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-11-15 11:36 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev, David Miller, Patrick McHardy, Oliver Hartkopp
Stephen Hemminger <shemminger@linux-foundation.org> writes:
> > +#ifdef CONFIG_CAN_DEBUG_CORE
> > +extern void can_debug_skb(struct sk_buff *skb);
> > +extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
> > +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
> > + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
> > + __func__, ##args) : 0)
> > +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
> > +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
> > +#else
> > +#define DBG(fmt, args...)
> > +#define DBG_FRAME(fmt, cf)
> > +#define DBG_SKB(skb)
> > +#endif
>
>
> This non-standard debugging seems like it needs a better interface.
> Also, need paren's around (DBG_VAR & 1) and don't use UPPERCASE for
> variable names.
No additional parenthesis is needed here. ?: is the lowest precedence
operator above assignment and ,. Also, DBG_VAR is no variable name.
It's a macro that expands to a variable name like can_debug, raw_debug
or bcm_debug.
> > +HLIST_HEAD(rx_dev_list);
>
> Please either make rx_dev_list static or call it can_rx_dev_list
> to avoid name conflices.
>
>
> > +static struct dev_rcv_lists rx_alldev_list;
> > +static DEFINE_SPINLOCK(rcv_lists_lock);
> > +
> > +static struct kmem_cache *rcv_cache __read_mostly;
> > +
> > +/* table of registered CAN protocols */
> > +static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
> > +static DEFINE_SPINLOCK(proto_tab_lock);
> > +
> > +struct timer_list stattimer; /* timer for statistics update */
> > +struct s_stats stats; /* packet statistics */
> > +struct s_pstats pstats; /* receive list statistics */
>
> More global variables without prefix.
These variables are not exported with EXPORT_SYMBOL, so there should
be no name conflict. They cannot be made static because they are used
in af_can.c and proc.c. Nevertheless we can prefix them with can_ if
you still think it's necessary.
> > +static int can_proc_read_stats(char *page, char **start, off_t off,
> > + int count, int *eof, void *data)
> > +{
> > +}
>
> The read interface should use seq_file interface rather than
> formatting into page buffer.
Why? For this simple function a page buffer is enough space and the
seq_file API would require more effort. IMHO, seq_files offer
advantages if the proc file shows some sequence of data generated in
an iteration through some loop (see below).
> > +static int can_proc_read_reset_stats(char *page, char **start, off_t off,
> > + int count, int *eof, void *data)
> > +{
> > +}
>
> Why not have a write interface to do the reset?
I haven't looked into writable proc files yet. Will do so.
> > +static int can_proc_read_rcvlist(char *page, char **start, off_t off,
> > + int count, int *eof, void *data)
> > +{
> > + /* double cast to prevent GCC warning */
> > + int idx = (int)(long)data;
> > +}
This is were I would prefer sequence files. However, the seq file
interface doesn't allow me to pass additional info like the `data'
argument. This means I would have to write separate functions
instead.
> Output from checkpatch:
>
> WARNING: do not add new typedefs
> #116: FILE: include/linux/can.h:41:
> +typedef __u32 canid_t;
>
> WARNING: do not add new typedefs
> #124: FILE: include/linux/can.h:49:
> +typedef __u32 can_err_mask_t;
These typedef were considered OK in previous discussions on the list.
> ERROR: use tabs not spaces
> #498: FILE: net/can/af_can.c:159:
> +^I^I^I^I " not implemented.\n", module_name);$
Fixed.
> WARNING: braces {} are not necessary for single statement blocks
> #1080: FILE: net/can/af_can.c:741:
> + if (!proto_tab[proto]) {
> + printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
> + proto);
> + }
Hm, isn't it common to use braces for single statements if they span
more than one line?
Thanks for your review.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 8:04 ` Joe Perches
@ 2007-11-15 11:51 ` Urs Thuermann
2007-11-15 12:05 ` David Miller
0 siblings, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-11-15 11:51 UTC (permalink / raw)
To: Joe Perches
Cc: Oliver Hartkopp, Stephen Hemminger, netdev, David Miller,
Patrick McHardy, Oliver Hartkopp
Joe Perches <joe@perches.com> writes:
> On Thu, 2007-11-15 at 08:40 +0100, Oliver Hartkopp wrote:
> > Stephen Hemminger wrote:
> > >> +#ifdef CONFIG_CAN_DEBUG_CORE
> > >> +extern void can_debug_skb(struct sk_buff *skb);
> > >> +extern void can_debug_cframe(const char *msg, struct can_frame *cframe);
> > >> +#define DBG(fmt, args...) (DBG_VAR & 1 ? printk( \
> > >> + KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
> > >> + __func__, ##args) : 0)
> > >> +#define DBG_FRAME(fmt, cf) (DBG_VAR & 2 ? can_debug_cframe(fmt, cf) : 0)
> > >> +#define DBG_SKB(skb) (DBG_VAR & 4 ? can_debug_skb(skb) : 0)
> > >> +#else
> > >> +#define DBG(fmt, args...)
> > >> +#define DBG_FRAME(fmt, cf)
> > >> +#define DBG_SKB(skb)
> > >> +#endif
>
> I would prefer the more frequently used macro style:
>
> #define DBG(fmt, args...) \
> do { if (DBG_VAR & 1) printk(KERN_DEBUG DBG_PREFIX ": %s: " fmt, \
> __func__, ##args); } while (0)
>
> #define DBG_FRAME(fmt, cf) \
> do { if (DBG_VAR & 2) can_debug_cframe(fmt, cf); } while (0)
>
> #define DBG_SKB(skb) \
> do { if (DBG_VAR & 4) can_debug_skb(skb); } while (0)
I prefer our code because it is shorter (fits into one line) and can
be used anywhere where an expression is allowed compared to only where
a statement is allowed. Actually, I first had
#define DBG( ... ) ((debug & 1) && printk( ... ))
and so on, but that didn't work with can_debug_{cframe,sbk} since they
return void.
Admitted, the benefit of expr vs. statement is really negligible and
since this issue has come up several times I will change these macros
using do-while.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 11:51 ` Urs Thuermann
@ 2007-11-15 12:05 ` David Miller
2007-11-15 15:11 ` Sam Ravnborg
2007-11-16 14:33 ` Urs Thuermann
0 siblings, 2 replies; 86+ messages in thread
From: David Miller @ 2007-11-15 12:05 UTC (permalink / raw)
To: urs.thuermann; +Cc: joe, oliver, shemminger, netdev, kaber, oliver.hartkopp
From: Urs Thuermann <urs.thuermann@gmx.de>
Date: 15 Nov 2007 12:51:34 +0100
> I prefer our code because it is shorter (fits into one line) and can
> be used anywhere where an expression is allowed compared to only where
> a statement is allowed. Actually, I first had
>
> #define DBG( ... ) ((debug & 1) && printk( ... ))
>
> and so on, but that didn't work with can_debug_{cframe,sbk} since they
> return void.
>
> Admitted, the benefit of expr vs. statement is really negligible and
> since this issue has come up several times I will change these macros
> using do-while.
I really frown upon these local debugging macros people tend to want
to submit with their changes.
It really craps up the tree, even though it might be useful to you.
So please remove this stuff or replace the debugging statements
with some generic kernel debugging facility, there are several.
Thank you.
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 11:36 ` Urs Thuermann
@ 2007-11-15 15:09 ` Sam Ravnborg
0 siblings, 0 replies; 86+ messages in thread
From: Sam Ravnborg @ 2007-11-15 15:09 UTC (permalink / raw)
To: Urs Thuermann
Cc: Stephen Hemminger, netdev, David Miller, Patrick McHardy,
Oliver Hartkopp
> > > +
> > > +struct timer_list stattimer; /* timer for statistics update */
> > > +struct s_stats stats; /* packet statistics */
> > > +struct s_pstats pstats; /* receive list statistics */
> >
> > More global variables without prefix.
>
> These variables are not exported with EXPORT_SYMBOL, so there should
> be no name conflict. They cannot be made static because they are used
> in af_can.c and proc.c. Nevertheless we can prefix them with can_ if
> you still think it's necessary.
When this is build-in they will be in the global kernel namespace.
So please add can_ prefix.
Sam
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 12:05 ` David Miller
@ 2007-11-15 15:11 ` Sam Ravnborg
2007-11-16 14:33 ` Urs Thuermann
1 sibling, 0 replies; 86+ messages in thread
From: Sam Ravnborg @ 2007-11-15 15:11 UTC (permalink / raw)
To: David Miller
Cc: urs.thuermann, joe, oliver, shemminger, netdev, kaber,
oliver.hartkopp
On Thu, Nov 15, 2007 at 04:05:30AM -0800, David Miller wrote:
> From: Urs Thuermann <urs.thuermann@gmx.de>
> Date: 15 Nov 2007 12:51:34 +0100
>
> > I prefer our code because it is shorter (fits into one line) and can
> > be used anywhere where an expression is allowed compared to only where
> > a statement is allowed. Actually, I first had
> >
> > #define DBG( ... ) ((debug & 1) && printk( ... ))
> >
> > and so on, but that didn't work with can_debug_{cframe,sbk} since they
> > return void.
> >
> > Admitted, the benefit of expr vs. statement is really negligible and
> > since this issue has come up several times I will change these macros
> > using do-while.
>
> I really frown upon these local debugging macros people tend to want
> to submit with their changes.
>
> It really craps up the tree, even though it might be useful to you.
>
> So please remove this stuff or replace the debugging statements
> with some generic kernel debugging facility, there are several.
It would be usefull if someone could make a short intro to the preferred
ones and we could stuff it in Documentation/*
Had same comment but had nowhere to point the can guys at.
Sam
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-15 12:05 ` David Miller
2007-11-15 15:11 ` Sam Ravnborg
@ 2007-11-16 14:33 ` Urs Thuermann
2007-11-16 23:42 ` David Miller
1 sibling, 1 reply; 86+ messages in thread
From: Urs Thuermann @ 2007-11-16 14:33 UTC (permalink / raw)
To: David Miller; +Cc: joe, oliver, shemminger, netdev, kaber, oliver.hartkopp
David Miller <davem@davemloft.net> writes:
> I really frown upon these local debugging macros people tend to want
> to submit with their changes.
>
> It really craps up the tree, even though it might be useful to you.
We have now removed the debug code completely. The code is quite
stable and we won't need these remnants from early days anymore.
> So please remove this stuff or replace the debugging statements
> with some generic kernel debugging facility, there are several.
I am not aware of any useful kernel facilities to replace our debug
macros, i.e. printing of debug messages, controlled by a bit mask
passed in as a module parameter, hexdumping of fames, etc. Of course,
there are other things like kprobes but sometimes printk(KERN_DEBUG...)
is more convenient and that's why people write their own DBG() macros.
urs
^ permalink raw reply [flat|nested] 86+ messages in thread
* [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-16 15:02 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25, update Urs Thuermann
@ 2007-11-16 15:02 ` Urs Thuermann
0 siblings, 0 replies; 86+ messages in thread
From: Urs Thuermann @ 2007-11-16 15:02 UTC (permalink / raw)
To: netdev, David Miller, Patrick McHardy
Cc: Stephen Hemminger, Joe Perches, Sam Ravnborg, Thomas Gleixner,
Oliver Hartkopp, Oliver Hartkopp, Urs Thuermann
[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 56529 bytes --]
This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here. They come as separate
patches. Protocol numbers are already in include/linux/can.h.
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>
---
include/linux/can.h | 111 +++++
include/linux/can/core.h | 64 +++
include/linux/can/error.h | 93 ++++
net/Kconfig | 1
net/Makefile | 1
net/can/Kconfig | 17
net/can/Makefile | 6
net/can/af_can.c | 861 ++++++++++++++++++++++++++++++++++++++++++++++
net/can/af_can.h | 122 ++++++
net/can/proc.c | 533 ++++++++++++++++++++++++++++
10 files changed, 1809 insertions(+)
Index: net-2.6.25/include/linux/can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can.h 2007-11-16 11:11:50.000000000 +0100
@@ -0,0 +1,111 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6.25/include/linux/can/core.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can/core.h 2007-11-16 14:38:25.000000000 +0100
@@ -0,0 +1,64 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20071116"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type: type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol: protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops: pointer to struct proto_ops for sock->ops.
+ * @prot: pointer to struct proto structure.
+ */
+struct can_proto {
+ int type;
+ int protocol;
+ int capability;
+ struct proto_ops *ops;
+ struct proto *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern void can_proto_unregister(struct can_proto *cp);
+
+extern 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);
+
+extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
+ canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#endif /* CAN_CORE_H */
Index: net-2.6.25/net/Kconfig
===================================================================
--- net-2.6.25.orig/net/Kconfig 2007-11-15 21:47:52.000000000 +0100
+++ net-2.6.25/net/Kconfig 2007-11-16 11:11:50.000000000 +0100
@@ -218,6 +218,7 @@
endmenu
source "net/ax25/Kconfig"
+source "net/can/Kconfig"
source "net/irda/Kconfig"
source "net/bluetooth/Kconfig"
source "net/rxrpc/Kconfig"
Index: net-2.6.25/net/Makefile
===================================================================
--- net-2.6.25.orig/net/Makefile 2007-11-15 21:47:52.000000000 +0100
+++ net-2.6.25/net/Makefile 2007-11-16 11:11:51.000000000 +0100
@@ -34,6 +34,7 @@
obj-$(CONFIG_NETROM) += netrom/
obj-$(CONFIG_ROSE) += rose/
obj-$(CONFIG_AX25) += ax25/
+obj-$(CONFIG_CAN) += can/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
Index: net-2.6.25/net/can/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/Kconfig 2007-11-16 14:34:20.000000000 +0100
@@ -0,0 +1,17 @@
+#
+# 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.
Index: net-2.6.25/net/can/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/Makefile 2007-11-16 14:25:56.000000000 +0100
@@ -0,0 +1,6 @@
+#
+# Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN) += can.o
+can-objs := af_can.o proc.o
Index: net-2.6.25/net/can/af_can.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/af_can.c 2007-11-16 14:27:26.000000000 +0100
@@ -0,0 +1,861 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.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 <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+static __initdata 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)");
+
+HLIST_HEAD(can_rx_dev_list);
+static 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 struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static DEFINE_SPINLOCK(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
+ */
+
+static 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;
+ }
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int can_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct can_proto *cp;
+ char module_name[sizeof("can-proto-000")];
+ int err = 0;
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol >= CAN_NPROTO)
+ return -EINVAL;
+
+ if (net != &init_net)
+ return -EAFNOSUPPORT;
+
+ /* try to load protocol module, when CONFIG_KMOD is defined */
+ if (!proto_tab[protocol]) {
+ sprintf(module_name, "can-proto-%d", protocol);
+ err = request_module(module_name);
+
+ /*
+ * In case of error we only print a message but don't
+ * return the error code immediately. Below we will
+ * return -EPROTONOSUPPORT
+ */
+ if (err == -ENOSYS) {
+ if (printk_ratelimit())
+ printk(KERN_INFO "can: request_module(%s)"
+ " not implemented.\n", module_name);
+ } else if (err) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "can: request_module(%s)"
+ " failed.\n", module_name);
+ }
+ }
+
+ spin_lock(&proto_tab_lock);
+ cp = proto_tab[protocol];
+ if (cp && !try_module_get(cp->prot->owner))
+ cp = NULL;
+ spin_unlock(&proto_tab_lock);
+
+ /* check for available protocol and correct usage */
+
+ if (!cp)
+ return -EPROTONOSUPPORT;
+
+ if (cp->type != sock->type) {
+ err = -EPROTONOSUPPORT;
+ goto errout;
+ }
+
+ if (cp->capability >= 0 && !capable(cp->capability)) {
+ err = -EPERM;
+ 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:
+ module_put(cp->prot->owner);
+ 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!)
+ *
+ * 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
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+ int err;
+
+ if (skb->dev->type != ARPHRD_CAN) {
+ kfree_skb(skb);
+ return -EPERM;
+ }
+
+ if (!(skb->dev->flags & IFF_UP)) {
+ kfree_skb(skb);
+ return -ENETDOWN;
+ }
+
+ skb->protocol = htons(ETH_P_CAN);
+ 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.
+ */
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ if (!newskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ newskb->sk = skb->sk;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ newskb->pkt_type = PACKET_BROADCAST;
+ netif_rx(newskb);
+ }
+ } 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);
+
+ /* update statistics */
+ can_stats.tx_frames++;
+ can_stats.tx_frames_delta++;
+
+ return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+ struct dev_rcv_lists *d = NULL;
+ struct hlist_node *n;
+
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
+ */
+
+ hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
+ if (d->dev == dev)
+ break;
+ }
+
+ return n ? d : NULL;
+}
+
+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 error frames */
+ if (*mask & CAN_ERR_FLAG) {
+ /* clear CAN_ERR_FLAG in list entry */
+ *mask &= CAN_ERR_MASK;
+ return &d->rx[RX_ERR];
+ }
+
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+ /* 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];
+
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
+ if (*can_id & CAN_EFF_FLAG) {
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
+ return &d->rx[RX_EFF];
+ }
+ } else {
+ if (*mask == CAN_SFF_MASK)
+ 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 indentification
+ *
+ * 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 frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * 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) */
+
+ 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_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+ struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+ kfree(d);
+}
+
+/*
+ * 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 hlist_node *next;
+ struct dev_rcv_lists *d;
+
+ spin_lock(&can_rcvlists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
+ printk(KERN_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, next, 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:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
+ */
+
+ if (!next) {
+ printk(KERN_ERR "BUG: receive list entry not found for "
+ "dev %s, id %03X, mask %03X\n",
+ DNAME(dev), can_id, mask);
+ r = NULL;
+ d = NULL;
+ 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)
+ hlist_del_rcu(&d->list);
+ else
+ d = NULL;
+
+ out:
+ spin_unlock(&can_rcvlists_lock);
+
+ /* schedule the receiver item for deletion */
+ if (r)
+ call_rcu(&r->rcu, can_rx_delete_receiver);
+
+ /* schedule the device structure for deletion */
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+ struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+ if (clone) {
+ clone->sk = skb->sk;
+ r->func(clone, r->data);
+ r->matches++;
+ }
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+ 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 frame entries only */
+ hlist_for_each_entry_rcu(r, n, &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, n, &d->rx[RX_ALL], list) {
+ deliver(skb, r);
+ matches++;
+ }
+
+ /* check for can_id/mask entries */
+ hlist_for_each_entry_rcu(r, n, &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, n, &d->rx[RX_INV], list) {
+ if ((can_id & r->mask) != r->can_id) {
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ /* check CAN_ID specific entries */
+ if (can_id & CAN_EFF_FLAG) {
+ hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+ if (r->can_id == can_id) {
+ deliver(skb, r);
+ matches++;
+ }
+ }
+ } else {
+ can_id &= CAN_SFF_MASK;
+ hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+ deliver(skb, r);
+ matches++;
+ }
+ }
+
+ return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct dev_rcv_lists *d;
+ int matches;
+
+ if (dev->type != ARPHRD_CAN || dev->nd_net != &init_net) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* 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();
+
+ /* free the skbuff allocated by the netdevice driver */
+ kfree_skb(skb);
+
+ if (matches > 0) {
+ can_stats.matches++;
+ can_stats.matches_delta++;
+ }
+
+ return 0;
+}
+
+/*
+ * 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(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+ int err = 0;
+
+ if (proto < 0 || proto >= CAN_NPROTO) {
+ printk(KERN_ERR "can: protocol number %d out of range\n",
+ proto);
+ return -EINVAL;
+ }
+
+ spin_lock(&proto_tab_lock);
+ if (proto_tab[proto]) {
+ printk(KERN_ERR "can: protocol %d already registered\n",
+ proto);
+ err = -EBUSY;
+ goto errout;
+ }
+
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ goto errout;
+
+ proto_tab[proto] = cp;
+
+ /* use generic ioctl function if the module doesn't bring its own */
+ if (!cp->ops->ioctl)
+ cp->ops->ioctl = can_ioctl;
+
+ errout:
+ spin_unlock(&proto_tab_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ */
+void can_proto_unregister(struct can_proto *cp)
+{
+ int proto = cp->protocol;
+
+ spin_lock(&proto_tab_lock);
+ if (!proto_tab[proto]) {
+ printk(KERN_ERR "BUG: can: protocol %d is not registered\n",
+ proto);
+ }
+ proto_unregister(cp->prot);
+ proto_tab[proto] = NULL;
+ spin_unlock(&proto_tab_lock);
+}
+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 *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct dev_rcv_lists *d;
+
+ if (dev->nd_net != &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
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR
+ "can: allocation of receive list failed\n");
+ return NOTIFY_DONE;
+ }
+ d->dev = dev;
+
+ spin_lock(&can_rcvlists_lock);
+ hlist_add_head_rcu(&d->list, &can_rx_dev_list);
+ spin_unlock(&can_rcvlists_lock);
+
+ break;
+
+ case NETDEV_UNREGISTER:
+ spin_lock(&can_rcvlists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ if (d->entries) {
+ d->remove_on_zero_entries = 1;
+ d = NULL;
+ } else
+ hlist_del_rcu(&d->list);
+ } else
+ printk(KERN_ERR "can: notifier: receive list not "
+ "found for dev %s\n", dev->name);
+
+ spin_unlock(&can_rcvlists_lock);
+
+ if (d)
+ call_rcu(&d->rcu, can_rx_delete_device);
+
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet __read_mostly = {
+ .type = __constant_htons(ETH_P_CAN),
+ .dev = NULL,
+ .func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops __read_mostly = {
+ .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)
+{
+ printk(banner);
+
+ rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+ 0, 0, NULL);
+ if (!rcv_cache)
+ return -ENOMEM;
+
+ /*
+ * Insert can_rx_alldev_list for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock(&can_rcvlists_lock);
+ hlist_add_head_rcu(&can_rx_alldev_list.list, &can_rx_dev_list);
+ spin_unlock(&can_rcvlists_lock);
+
+ 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);
+
+ return 0;
+}
+
+static __exit void can_exit(void)
+{
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
+ del_timer(&can_stattimer);
+
+ can_remove_proc();
+
+ /* protocol unregister */
+ dev_remove_pack(&can_packet);
+ unregister_netdevice_notifier(&can_netdev_notifier);
+ sock_unregister(PF_CAN);
+
+ /* remove can_rx_dev_list */
+ spin_lock(&can_rcvlists_lock);
+ hlist_del(&can_rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &can_rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock(&can_rcvlists_lock);
+
+ kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6.25/net/can/af_can.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/af_can.h 2007-11-16 14:27:20.000000000 +0100
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#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;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+ struct hlist_node list;
+ struct rcu_head rcu;
+ struct net_device *dev;
+ struct hlist_head rx[RX_MAX];
+ struct hlist_head rx_sff[0x800];
+ 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;
+};
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern 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 */
Index: net-2.6.25/net/can/proc.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/net/can/proc.c 2007-11-16 14:27:37.000000000 +0100
@@ -0,0 +1,533 @@
+/*
+ * 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.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.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",
+ [RX_EFF] = "rx_eff",
+};
+
+/*
+ * 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
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+ struct net_device *dev)
+{
+ struct receiver *r;
+ struct hlist_node *n;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(r, n, rx_list, list) {
+ char *fmt = (r->can_id & CAN_EFF_FLAG)?
+ " %-5s %08X %08x %08x %08x %8ld %s\n" :
+ " %-5s %03X %08x %08lx %08lx %8ld %s\n";
+
+ len += snprintf(page + len, PAGE_SIZE - len, fmt,
+ DNAME(dev), r->can_id, r->mask,
+ (unsigned long)r->func, (unsigned long)r->data,
+ r->matches, r->ident);
+
+ /* does a typical line fit into the current buffer? */
+
+ /* 100 Bytes before end of buffer */
+ if (len > PAGE_SIZE - 100) {
+ /* mark output cut off */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (..)\n");
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+ /*
+ * can1. 00000000 00000000 00000000
+ * ....... 0 tp20
+ */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " device can_id can_mask function"
+ " userdata matches ident\n");
+
+ return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld transmitted frames (TXF)\n",
+ can_stats.tx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld received frames (RXF)\n", can_stats.rx_frames);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld matched frames (RXMF)\n", can_stats.matches);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ if (can_stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% total match ratio (RXMR)\n",
+ can_stats.total_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total tx rate (TXR)\n",
+ can_stats.total_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s total rx rate (RXR)\n",
+ can_stats.total_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% current match ratio (CRXMR)\n",
+ can_stats.current_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current tx rate (CTXR)\n",
+ can_stats.current_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s current rx rate (CRXR)\n",
+ can_stats.current_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld %% max match ratio (MRXMR)\n",
+ can_stats.max_rx_match_ratio);
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max tx rate (MTXR)\n",
+ can_stats.max_tx_rate);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld frames/s max rx rate (MRXR)\n",
+ can_stats.max_rx_rate);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld current receive list entries (CRCV)\n",
+ can_pstats.rcv_entries);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld maximum receive list entries (MRCV)\n",
+ can_pstats.rcv_entries_max);
+
+ if (can_pstats.stats_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\n %8ld statistic resets (STR)\n",
+ can_pstats.stats_reset);
+
+ if (can_pstats.user_reset)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " %8ld user statistic resets (USTR)\n",
+ can_pstats.user_reset);
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ user_reset = 1;
+
+ if (can_stattimer.function == can_stat_update) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Scheduled statistic reset #%ld.\n",
+ can_pstats.stats_reset + 1);
+
+ } else {
+ if (can_stats.jiffies_init != jiffies)
+ can_init_stats();
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "Performed statistic reset #%ld.\n",
+ can_pstats.stats_reset);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+ CAN_VERSION_STRING);
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* double cast to prevent GCC warning */
+ int idx = (int)(long)data;
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list '%s':\n", rx_list_name[idx]);
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
+
+ if (!hlist_empty(&d->rx[idx])) {
+ len = can_print_recv_banner(page, len);
+ len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct dev_rcv_lists *d;
+ struct hlist_node *n;
+
+ /* RX_SFF */
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "\nreceive list 'rx_sff':\n");
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
+ int i, all_empty = 1;
+ /* check wether at least one list is non-empty */
+ for (i = 0; i < 0x800; i++)
+ if (!hlist_empty(&d->rx_sff[i])) {
+ all_empty = 0;
+ break;
+ }
+
+ if (!all_empty) {
+ len = can_print_recv_banner(page, len);
+ for (i = 0; i < 0x800; i++) {
+ if (!hlist_empty(&d->rx_sff[i]) &&
+ len < PAGE_SIZE - 100)
+ len = can_print_rcvlist(page, len,
+ &d->rx_sff[i],
+ d->dev);
+ }
+ } else
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " (%s: no entry)\n", DNAME(d->dev));
+
+ /* exit on end of buffer? */
+ if (len > PAGE_SIZE - 100)
+ break;
+ }
+ rcu_read_unlock();
+
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+ *eof = 1;
+ return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+ mode_t mode,
+ read_proc_t *read_proc,
+ void *data)
+{
+ if (can_dir)
+ return create_proc_read_entry(name, mode, can_dir, read_proc,
+ data);
+ else
+ return NULL;
+}
+
+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;
+ }
+
+ can_dir->owner = THIS_MODULE;
+
+ /* own procfs entries from the AF_CAN core */
+ pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+ can_proc_read_version, NULL);
+ pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+ can_proc_read_stats, NULL);
+ pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+ can_proc_read_reset_stats, NULL);
+ pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+ can_proc_read_rcvlist, (void *)RX_ERR);
+ pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+ can_proc_read_rcvlist, (void *)RX_ALL);
+ pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+ can_proc_read_rcvlist, (void *)RX_FIL);
+ pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+ can_proc_read_rcvlist, (void *)RX_INV);
+ pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+ can_proc_read_rcvlist, (void *)RX_EFF);
+ pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+ can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * 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)
+ proc_net_remove(&init_net, "can");
+}
Index: net-2.6.25/include/linux/can/error.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ net-2.6.25/include/linux/can/error.h 2007-11-16 11:11:51.000000000 +0100
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+ /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+ /* (at least one error counter exceeds */
+ /* the protocol-defined level of 127) */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */
--
^ permalink raw reply [flat|nested] 86+ messages in thread
* Re: [PATCH 2/7] CAN: Add PF_CAN core module
2007-11-16 14:33 ` Urs Thuermann
@ 2007-11-16 23:42 ` David Miller
0 siblings, 0 replies; 86+ messages in thread
From: David Miller @ 2007-11-16 23:42 UTC (permalink / raw)
To: urs.thuermann; +Cc: joe, oliver, shemminger, netdev, kaber, oliver.hartkopp
From: Urs Thuermann <urs.thuermann@gmx.de>
Date: 16 Nov 2007 15:33:08 +0100
> I am not aware of any useful kernel facilities to replace our debug
> macros, i.e. printing of debug messages, controlled by a bit mask
> passed in as a module parameter, hexdumping of fames, etc. Of course,
> there are other things like kprobes but sometimes printk(KERN_DEBUG...)
> is more convenient and that's why people write their own DBG() macros.
At the simplest level there is pr_debug() which replaces the
usual "print if DEBUG is defined" that lots of people use.
For device drivers we have something similar in dev_vdbg() et
al.
For bitmask based enabling yes it would be nice if there were
something more. But you can also get this behavior by using
DEBUG, the above interfaces, and adjusting the kernel log level
to suit what you're trying to view.
^ permalink raw reply [flat|nested] 86+ messages in thread
end of thread, other threads:[~2007-11-16 23:42 UTC | newest]
Thread overview: 86+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-10-02 13:10 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #9 Urs Thuermann
2007-10-02 13:10 ` [PATCH 1/7] CAN: Allocate protocol numbers for PF_CAN Urs Thuermann
2007-10-02 14:11 ` Arnaldo Carvalho de Melo
2007-10-02 14:27 ` Urs Thuermann
2007-10-02 14:43 ` Arnaldo Carvalho de Melo
2007-10-02 14:42 ` Oliver Hartkopp
2007-10-02 14:51 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-10-02 14:38 ` Arnaldo Carvalho de Melo
2007-10-02 16:09 ` Oliver Hartkopp
2007-10-04 11:51 ` Urs Thuermann
2007-10-04 13:40 ` Arnaldo Carvalho de Melo
2007-10-02 13:10 ` [PATCH 3/7] CAN: Add raw protocol Urs Thuermann
2007-10-02 14:30 ` Arnaldo Carvalho de Melo
2007-10-02 14:53 ` Oliver Hartkopp
2007-10-04 11:52 ` Urs Thuermann
2007-10-02 13:10 ` [PATCH 4/7] CAN: Add broadcast manager (bcm) protocol Urs Thuermann
2007-10-02 13:10 ` [PATCH 5/7] CAN: Add virtual CAN netdevice driver Urs Thuermann
2007-10-02 14:20 ` Arnaldo Carvalho de Melo
2007-10-02 15:07 ` Oliver Hartkopp
2007-10-02 16:46 ` Arnaldo Carvalho de Melo
2007-10-02 21:02 ` Oliver Hartkopp
2007-10-02 21:43 ` Arnaldo Carvalho de Melo
2007-10-02 21:50 ` David Miller
2007-10-03 7:06 ` Oliver Hartkopp
2007-10-02 21:52 ` Stephen Hemminger
2007-10-02 22:04 ` David Miller
2007-10-03 17:47 ` Oliver Hartkopp
2007-10-04 11:52 ` Urs Thuermann
2007-10-02 13:10 ` [PATCH 6/7] CAN: Add maintainer entries Urs Thuermann
2007-10-02 13:10 ` [PATCH 7/7] CAN: Add documentation Urs Thuermann
-- strict thread matches above, loose matches on Subject: below --
2007-11-16 15:02 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25, update Urs Thuermann
2007-11-16 15:02 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-11-14 12:13 [PATCH 0/7] CAN: New PF_CAN protocol family for 2.6.25 Urs Thuermann
2007-11-14 12:13 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-11-14 21:38 ` Stephen Hemminger
2007-11-15 7:40 ` Oliver Hartkopp
2007-11-15 8:04 ` Joe Perches
2007-11-15 11:51 ` Urs Thuermann
2007-11-15 12:05 ` David Miller
2007-11-15 15:11 ` Sam Ravnborg
2007-11-16 14:33 ` Urs Thuermann
2007-11-16 23:42 ` David Miller
2007-11-15 11:36 ` Urs Thuermann
2007-11-15 15:09 ` Sam Ravnborg
2007-10-05 10:49 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #10 Urs Thuermann
2007-10-05 10:49 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-25 12:20 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #8 Urs Thuermann
2007-09-25 12:20 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-25 12:41 ` Arnaldo Carvalho de Melo
2007-09-25 13:24 ` Urs Thuermann
2007-09-25 15:33 ` Stephen Hemminger
2007-09-25 21:00 ` Urs Thuermann
2007-09-25 21:07 ` Stephen Hemminger
2007-09-25 21:09 ` David Miller
2007-09-28 16:27 ` Thomas Gleixner
2007-09-28 20:20 ` David Miller
2007-09-28 20:28 ` Thomas Gleixner
2007-09-20 18:43 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #7 Urs Thuermann
2007-09-20 18:43 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-20 20:06 ` Joe Perches
2007-09-20 20:27 ` Thomas Gleixner
2007-09-21 10:35 ` Urs Thuermann
2007-09-21 16:58 ` Joe Perches
2007-09-24 19:23 ` Urs Thuermann
2007-09-21 12:47 ` Patrick McHardy
2007-09-21 18:01 ` Urs Thuermann
2007-09-22 10:53 ` Patrick McHardy
2007-09-17 10:03 [PATCH 0/7] CAN: Add new PF_CAN protocol family, try #6 Urs Thuermann
2007-09-17 10:03 ` [PATCH 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-09-17 15:50 ` Paul E. McKenney
2007-09-18 13:31 ` Patrick McHardy
2007-09-18 14:54 ` Urs Thuermann
2007-09-18 15:07 ` Patrick McHardy
2007-09-18 21:20 ` Urs Thuermann
2007-09-19 8:27 ` Patrick McHardy
2007-09-20 8:53 ` Urs Thuermann
2007-09-20 10:33 ` Patrick McHardy
2007-09-20 11:30 ` Urs Thuermann
2007-09-20 11:43 ` Patrick McHardy
2007-08-04 2:06 [patch 0/7] CAN: Add new PF_CAN protocol family, try #5 Urs Thuermann
2007-08-04 2:06 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-06-22 3:44 [patch 0/7] CAN: Add new PF_CAN protocol family, try #3 Urs Thuermann
2007-06-22 3:44 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-05-30 13:11 [patch 0/7] CAN: Add new PF_CAN protocol family, update Urs Thuermann
2007-05-30 13:11 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-05-16 14:51 [patch 0/7] CAN: Add new PF_CAN protocol family Urs Thuermann
2007-05-16 14:51 ` [patch 2/7] CAN: Add PF_CAN core module Urs Thuermann
2007-05-16 16:35 ` Arnaldo Carvalho de Melo
2007-05-16 19:14 ` Urs Thuermann
2007-05-16 20:51 ` Arnaldo Carvalho de Melo
2007-05-18 0:59 ` Paul E. McKenney
2007-05-18 9:19 ` Oliver Hartkopp
2007-05-18 14:33 ` Paul E. McKenney
2007-05-18 15:03 ` Oliver Hartkopp
2007-05-18 21:06 ` Oliver Hartkopp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).