]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
Port kernel code to linux-2.6
authorthuermann <thuermann@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 24 Apr 2006 14:21:08 +0000 (14:21 +0000)
committerthuermann <thuermann@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 24 Apr 2006 14:21:08 +0000 (14:21 +0000)
git-svn-id: svn://svn.berlios.de//socketcan/trunk@2 030b6a49-0b11-0410-94ab-b0dab22257f2

21 files changed:
kernel/2.6/can/Makefile [new file with mode: 0644]
kernel/2.6/can/af_can.c [new file with mode: 0644]
kernel/2.6/can/af_can.h [new file with mode: 0644]
kernel/2.6/can/bcm.c [new file with mode: 0644]
kernel/2.6/can/bcm.h [new file with mode: 0644]
kernel/2.6/can/can.h [new file with mode: 0644]
kernel/2.6/can/can_ioctl.h [new file with mode: 0644]
kernel/2.6/can/proc.c [new file with mode: 0644]
kernel/2.6/can/raw.c [new file with mode: 0644]
kernel/2.6/can/raw.h [new file with mode: 0644]
kernel/2.6/can/vcan.c [new file with mode: 0644]
kernel/2.6/can/version.h [new file with mode: 0644]
kernel/2.6/drivers/sja1000/Makefile [new file with mode: 0644]
kernel/2.6/drivers/sja1000/can.h [new symlink]
kernel/2.6/drivers/sja1000/can_ioctl.h [new symlink]
kernel/2.6/drivers/sja1000/isa.c [new file with mode: 0644]
kernel/2.6/drivers/sja1000/proc.c [new file with mode: 0644]
kernel/2.6/drivers/sja1000/sja1000.c [new file with mode: 0644]
kernel/2.6/drivers/sja1000/sja1000.h [new file with mode: 0644]
kernel/2.6/drivers/sja1000/trajet-gw2.c [new file with mode: 0644]
kernel/2.6/drivers/sja1000/version.h [new symlink]

diff --git a/kernel/2.6/can/Makefile b/kernel/2.6/can/Makefile
new file mode 100644 (file)
index 0000000..d838bee
--- /dev/null
@@ -0,0 +1,72 @@
+#
+#  $Id: Makefile,v 2.0 2006/04/13 10:37:19 ethuerm Exp $
+#
+#  Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+
+
+
+MOD_GPL = can.o vcan.o can-raw.o can-bcm.o
+MOD_VW  = can-tpgen.o can-tp20.o can-tp16.o can-mcnet.o
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR = /usr/src/linux
+PWD       = $(shell pwd)
+
+default:
+       $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+
+clean:
+       $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean
+
+else
+
+obj-m := $(MOD_GPL)
+
+can-objs       := af_can.o proc.o
+can-bcm-objs   := bcm.o
+can-raw-objs   := raw.o
+
+can-tpgen-objs := tp_gen.o
+can-tp20-objs  := tp20.o
+can-tp16-objs  := tp16.o
+can-mcnet-objs := mcnet.o
+
+endif
diff --git a/kernel/2.6/can/af_can.c b/kernel/2.6/can/af_can.c
new file mode 100644 (file)
index 0000000..36a83a9
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ * af_can.c
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+#include "af_can.h"
+#include "version.h"
+
+RCSID("$Id: af_can.c,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+
+#define NAME "Volkswagen AG - Low Level CAN Framework (LLCF)"
+#define IDENT "af_can"
+static __initdata const char banner[] = BANNER(NAME);
+
+MODULE_DESCRIPTION(NAME);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
+             "Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+
+#ifdef DEBUG
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...)       (debug & 1 ? \
+                              (printk(KERN_DEBUG "CAN %s: ", __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
+
+static __init int  can_init(void);
+static __exit void can_exit(void);
+
+static int can_create(struct socket *sock, int protocol);
+static int can_notifier(struct notifier_block *nb,
+                       unsigned long msg, void *data);
+static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt, struct net_device *orig_dev);
+#else
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt);
+#endif
+static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb);
+static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct net_device *dev);
+
+struct notifier_list {
+    struct notifier_list *next;
+    struct net_device *dev;
+    void (*func)(unsigned long msg, void *data);
+    void *data;
+};
+
+static struct notifier_list *nlist;
+
+struct rcv_dev_list *rx_dev_list;
+struct rcv_dev_list rx_alldev_list;
+rwlock_t rcv_lists_lock = RW_LOCK_UNLOCKED;
+
+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,
+};
+
+static struct notifier_block can_netdev_notifier = {
+    .notifier_call = can_notifier,
+};
+
+static struct can_proto *proto_tab[CAN_MAX];
+
+extern struct timer_list stattimer; /* timer for statistics update */
+extern struct s_stats  stats;       /* statistics */
+extern struct s_pstats pstats;
+
+module_init(can_init);
+module_exit(can_exit);
+
+/**************************************************/
+/* af_can module init/exit functions              */
+/**************************************************/
+
+static __init int can_init(void)
+{
+    printk(banner);
+
+    if (stats_timer) {
+       /* statistics init */
+       init_timer(&stattimer);
+    }
+
+    /* 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)
+{
+    if (stats_timer) {
+       /* stop statistics 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);
+}
+
+/**************************************************/
+/* af_can protocol functions                      */
+/**************************************************/
+
+void can_proto_register(int proto, struct can_proto *cp)
+{
+    if (proto < 0 || proto >= CAN_MAX) {
+       printk(KERN_ERR "CAN: protocol number %d out of range\n", proto);
+       return;
+    }
+    if (proto_tab[proto]) {
+       printk(KERN_ERR "CAN: protocol %d already registered\n", proto);
+       return;
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+    if (proto_register(cp->prot, 0) != 0) {
+       return;
+    }
+#endif
+    proto_tab[proto] = cp;
+
+    /* use our generic ioctl function if the module doesn't bring its own */
+    if (!cp->ops->ioctl)
+       cp->ops->ioctl = can_ioctl;
+}
+
+void can_proto_unregister(int proto)
+{
+    struct can_proto *cp;
+
+    if (!(cp = proto_tab[proto])) {
+       printk(KERN_ERR "CAN: protocol %d is not registered\n", proto);
+       return;
+    }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+    proto_unregister(cp->prot);
+#endif
+    proto_tab[proto] = NULL;
+}
+
+void can_dev_register(struct net_device *dev,
+                     void (*func)(unsigned long msg, void *), void *data)
+{
+    struct notifier_list *p = kmalloc(GFP_KERNEL, sizeof(*p));
+
+    DBG("called for %s\n", dev->name);
+
+    if (!p)
+       return;
+    p->next = nlist;
+    p->dev  = dev;
+    p->func = func;
+    p->data = data;
+    nlist = p;
+}
+
+void can_dev_unregister(struct net_device *dev,
+                       void (*func)(unsigned long msg, void *), void *data)
+{
+    struct notifier_list *p, **q;
+
+    DBG("called for %s\n", dev->name);
+
+    for (q = &nlist; p = *q; q = &p->next) {
+       if (p->dev == dev && p->func == func && p->data == data) {
+           *q = p->next;
+           kfree(p);
+           return;
+       }
+    }
+}
+
+/**************************************************/
+/* af_can socket functions                        */
+/**************************************************/
+
+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;
+
+    DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+    sock->state = SS_UNCONNECTED;
+
+    switch (sock->type) {
+    case SOCK_SEQPACKET:
+       switch (protocol) {
+       case CAN_TP16:
+           break;
+       case CAN_TP20:
+           break;
+       case CAN_MCNET:
+           break;
+       case CAN_ISOTP:
+           break;
+       default:
+           return -EPROTONOSUPPORT;
+       }
+       break;
+    case SOCK_DGRAM:
+       switch (protocol) {
+       case CAN_BCM:
+           break;
+       case CAN_BAP:
+           break;
+       default:
+           return -EPROTONOSUPPORT;
+       }
+       break;
+    case SOCK_RAW:
+       switch (protocol) {
+       case CAN_RAW:
+           break;
+       default:
+           return -EPROTONOSUPPORT;
+       }
+       break;
+    default:
+       return -ESOCKTNOSUPPORT;
+       break;
+    }
+
+    DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+    /* try to load protocol module, when CONFIG_KMOD is defined */
+    if (!proto_tab[protocol]) {
+       char module_name[30];
+       sprintf(module_name, "can-proto-%d", protocol);
+       if (request_module(module_name) == -ENOSYS)
+           printk(KERN_INFO "af_can: request_module(%s) not implemented.\n",
+                  module_name);
+    }
+
+    /* check for success */
+    if (!(cp = proto_tab[protocol]))
+       return -EPROTONOSUPPORT;
+
+    sock->ops = cp->ops;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+    sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+    if (!sk)
+       goto oom;
+#else
+    sk = sk_alloc(PF_CAN, GFP_KERNEL, 1, 0);
+    if (!sk)
+       goto oom;
+    if (cp->obj_size &&
+       !(sk->sk_protinfo = kmalloc(cp->obj_size, GFP_KERNEL))) {
+       sk_free(sk);
+       goto oom;
+    }
+    sk_set_owner(sk, proto_tab[protocol]->owner);
+#endif
+    sock_init_data(sock, sk);
+    sk->sk_destruct = can_sock_destruct;
+
+    DBG("created sock: %p\n", sk);
+
+    return 0;
+
+ oom:
+    return -ENOMEM;
+}
+
+static int can_notifier(struct notifier_block *nb,
+                       unsigned long msg, void *data)
+{
+    struct net_device *dev = (struct net_device *)data;
+    struct notifier_list *p;
+
+    DBG("called for %s, msg = %lu\n", dev->name, msg);
+
+    for (p = nlist; p; p = p->next) {
+       if (p->dev == dev)
+           p->func(msg, p->data);
+    }
+    return 0;
+}
+
+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:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+       return -ENOIOCTLCMD;
+#else
+       return dev_ioctl(cmd, (void *)arg);
+#endif
+    }
+    return 0;
+}
+
+/**************************************************/
+/* af_can tx path                                 */
+/**************************************************/
+
+int can_send(struct sk_buff *skb)
+{
+    struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+    int err;
+
+    if (!(skb->dev->flags & IFF_UP))
+       err = -ENETDOWN;
+    else if ((err = dev_queue_xmit(skb)) > 0)  /* send to netdevice */
+       err = net_xmit_errno(err);
+
+    /* update statistics */
+    stats.tx_frames++;
+    stats.tx_frames_delta++;
+
+    newskb->protocol  = htons(ETH_P_CAN);
+    newskb->ip_summed = CHECKSUM_UNNECESSARY;
+    netif_rx(newskb);                          /* local loopback */
+
+    return err;
+}
+
+/**************************************************/
+/* af_can rx path                                 */
+/**************************************************/
+
+void 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 rcv_list *p, **q;
+    struct rcv_dev_list *d;
+
+    DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
+       dev, can_id, mask, func, data, ident);
+
+    write_lock(&rcv_lists_lock);
+
+    q = find_rcv_list(&can_id, &mask, dev);
+
+    if (!q) {
+       printk(KERN_ERR "CAN: receive list not found for "
+              "dev %s, id %03X, mask %03X, ident %s\n",
+              dev->name, can_id, mask, ident);
+       goto out;
+    }
+
+    /* insert   (dev,canid,mask) -> (func,data) */
+    if (!(p = kmalloc(sizeof(struct rcv_list), GFP_KERNEL)))
+       return;
+
+    p->can_id  = can_id;
+    p->mask    = mask;
+    p->matches = 0;
+    p->func    = func;
+    p->data    = data;
+    p->ident   = ident;
+    p->next = *q;
+    *q = p;
+
+    if (!dev)
+       d = &rx_alldev_list;
+    else
+       for (d = rx_dev_list; d; d = d->next)
+           if (d->dev == dev)
+               break;
+    d->entries++;
+
+    pstats.rcv_entries++;
+    if (pstats.rcv_entries_max < pstats.rcv_entries)
+       pstats.rcv_entries_max = pstats.rcv_entries;
+
+out:
+    write_unlock(&rcv_lists_lock);
+}
+
+void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
+       void (*func)(struct sk_buff *, void *), void *data)
+{
+    struct rcv_list *p, **q;
+    struct rcv_dev_list *d;
+
+    DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
+       dev, can_id, mask, func, data);
+
+    write_lock(&rcv_lists_lock);
+
+    q = find_rcv_list(&can_id, &mask, dev);
+
+    if (!q) {
+       printk(KERN_ERR "CAN: receive list not found for "
+              "dev %s, id %03X, mask %03X\n", dev->name, can_id, mask);
+       goto out;
+    }
+
+    for (; p = *q; q = &p->next) {
+       if (p->can_id == can_id && p->mask == mask
+           && p->func == func && p->data == data)
+           break;
+    }
+
+    if (!p) {
+       printk(KERN_ERR "CAN: receive list entry not found for "
+              "dev %s, id %03X, mask %03X\n", dev->name, can_id, mask);
+       goto out;
+    }
+
+    *q = p->next;
+    kfree(p);
+
+    if (pstats.rcv_entries > 0)
+       pstats.rcv_entries--;
+
+    if (!dev)
+       d = &rx_alldev_list;
+    else
+       for (d = rx_dev_list; d; d = d->next)
+           if (d->dev == dev)
+               break;
+    d->entries--;
+
+    if(!d->entries)
+       d->dev = NULL; /* mark unused */
+
+out:
+    write_unlock(&rcv_lists_lock);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt, struct net_device *orig_dev)
+#else
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+                  struct packet_type *pt)
+#endif
+{
+    struct rcv_dev_list *q;
+    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);
+
+    /* update statistics */
+    stats.rx_frames++;
+    stats.rx_frames_delta++;
+
+    matches = can_rcv_filter(&rx_alldev_list, skb);
+
+    /* find receive list for this device */
+    for (q = rx_dev_list; q; q = q->next)
+       if (q->dev == dev)
+           break;
+
+    if (q)
+       matches += can_rcv_filter(q, skb);
+
+    DBG("freeing skbuff %p\n", skb);
+    kfree_skb(skb);
+
+    if (matches > 0) {
+       stats.matches++;
+       stats.matches_delta++;
+    }
+
+    return 0;
+}
+
+
+static inline void deliver(struct sk_buff *skb, struct rcv_list *p)
+{
+    struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+    DBG("skbuff %p cloned to %p\n", skb, clone);
+    if (clone) {
+       p->func(clone, p->data);
+       p->matches++;    /* update specific statistics */
+    }
+}
+    
+static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb)
+{
+    struct rcv_list *p;
+    int matches = 0;
+    struct can_frame *cf = (struct can_frame*)skb->data;
+    canid_t can_id = cf->can_id;
+
+    if (q->entries == 0)
+       return 0;
+
+    /* check for unfiltered entries */
+    for (p = q->rx_all; p; p = p->next) {
+       DBG("match on rx_all skbuff %p\n", skb);
+       deliver(skb, p);
+       matches++;
+    }
+
+    /* check for can_id/mask entries */
+    for (p = q->rx_fil; p; p = p->next) {
+       if ((can_id & p->mask) == p->can_id) {
+           DBG("match on rx_fil skbuff %p\n", skb);
+           deliver(skb, p);
+           matches++;
+       }
+    }
+
+    /* check for inverted can_id/mask entries */
+    for (p = q->rx_inv; p; p = p->next) {
+       if ((can_id & p->mask) != p->can_id) {
+           DBG("match on rx_inv skbuff %p\n", skb);
+           deliver(skb, p);
+           matches++;
+       }
+    }
+
+    /* check CAN_ID specific entries */
+    if (can_id & CAN_EFF_FLAG) {
+       for (p = q->rx_eff; p; p = p->next) {
+           if (p->can_id == can_id) {
+               DBG("match on rx_eff skbuff %p\n", skb);
+               deliver(skb, p);
+               matches++;
+           }
+       }
+    } else {
+       for (p = q->rx_sff[can_id & CAN_SFF_MASK]; p; p = p->next) {
+           DBG("match on rx_sff skbuff %p\n", skb);
+           deliver(skb, p);
+           matches++;
+       }
+    }
+
+    return matches;
+}
+
+static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct net_device *dev)
+{
+    canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking values */
+    canid_t eff = *can_id & *mask & CAN_EFF_FLAG; /* correct EFF check? */
+    canid_t rtr = *can_id & *mask & CAN_RTR_FLAG; /* correct RTR check? */
+    struct rcv_dev_list *p;
+
+    /* make some paranoic operations */
+    if (*can_id & CAN_EFF_FLAG)
+       *mask &= (CAN_EFF_MASK | eff | rtr);
+    else
+       *mask &= (CAN_SFF_MASK | rtr);
+
+    *can_id &= *mask;
+
+    /* find receive list for this device */
+    if (!dev)
+       p = &rx_alldev_list;
+    else
+       for (p = rx_dev_list; p; p = p->next)
+           if (p->dev == dev)
+               break;
+
+    if (!p) {
+       /* arrange new rcv_dev_list for this device */
+
+       /* find deactivated receive list for this device */
+       for (p = rx_dev_list; p; p = p->next)
+           if (p->dev == NULL)
+               break;
+
+       if (p) {
+           DBG("reactivating rcv_dev_list for %s\n", dev->name); 
+           p->dev = dev;
+       } else {
+           /* create new rcv_dev_list for this device */
+           DBG("creating new rcv_dev_list for %s\n", dev->name);
+           if (!(p = kmalloc(sizeof(struct rcv_dev_list), GFP_KERNEL))) {
+               printk(KERN_ERR "CAN: allocation of receive list failed\n");
+               return NULL;
+           }
+           memset (p, 0, sizeof(struct rcv_dev_list));
+           p->dev      = dev;
+           p->next     = rx_dev_list;
+           rx_dev_list = p;
+       }
+    }
+
+    if (inv) /* inverse can_id/can_mask filter and RTR */
+       return &p->rx_inv;
+
+    if (*can_id & CAN_RTR_FLAG) /* positive filter RTR */
+       return &p->rx_fil;
+
+    if (!(*mask)) /* mask == 0 => no filter */
+       return &p->rx_all;
+
+    if (*can_id & CAN_EFF_FLAG) {
+       if (*mask == CAN_EFF_MASK) /* filter exact EFF can_id */
+           return &p->rx_eff;
+    } else {
+       if (*mask == CAN_SFF_MASK) /* filter exact SFF can_id */
+           return &p->rx_sff[*can_id];
+    }
+
+    return &p->rx_fil;  /* filter via can_id/can_mask */
+}
+
+/**************************************************/
+/* af_can utility stuff                           */
+/**************************************************/
+
+unsigned long timeval2jiffies(struct timeval *tv, int round_up)
+{
+    unsigned long jif;
+    unsigned long sec  = tv->tv_sec;
+    unsigned long usec = tv->tv_usec;
+
+    if (sec > ULONG_MAX / HZ)          /* check for overflow */
+       return ULONG_MAX;
+
+    if (round_up)                      /* any usec below one HZ? */
+       usec += 1000000 / HZ - 1;      /* pump it up */
+
+    jif = usec / (1000000 / HZ);
+
+    if (sec * HZ > ULONG_MAX - jif)    /* check for overflow */
+       return ULONG_MAX;
+    else
+       return jif + sec * HZ;
+}
+
+
+/**************************************************/
+/* af_can debugging stuff                         */
+/**************************************************/
+
+#ifdef DEBUG
+
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+    va_list ap;
+    int len;
+    int dlc, i;
+    char buf[1024];
+    
+    len = sprintf(buf, KERN_DEBUG);
+    va_start(ap, cf);
+    len += snprintf(buf + len, sizeof(buf) - 64, msg, ap);
+    buf[len++] = ':';
+    buf[len++] = ' ';
+    va_end(ap);
+
+    if ((dlc = cf->can_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);
+}
+
+void can_debug_skb(struct sk_buff *skb)
+{
+    int len, nbytes, i;
+    char buf[1024];
+
+    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 < sizeof(buf) - 16) {
+           len += sprintf(buf + len, " %02x", skb->head[i]);
+       } else {
+           len += sprintf(buf + len, "...");
+           break;
+       }
+    }
+    buf[len++] = '\n';
+    buf[len]   = '\0';
+    printk(buf);
+}
+
+EXPORT_SYMBOL(can_debug_cframe);
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/**************************************************/
+/* Exported symbols                               */
+/**************************************************/
+EXPORT_SYMBOL(can_proto_register);
+EXPORT_SYMBOL(can_proto_unregister);
+EXPORT_SYMBOL(can_rx_register);
+EXPORT_SYMBOL(can_rx_unregister);
+EXPORT_SYMBOL(can_dev_register);
+EXPORT_SYMBOL(can_dev_unregister);
+EXPORT_SYMBOL(can_send);
+EXPORT_SYMBOL(timeval2jiffies);
diff --git a/kernel/2.6/can/af_can.h b/kernel/2.6/can/af_can.h
new file mode 100644 (file)
index 0000000..c880c3c
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * af_can.h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef AF_CAN_H
+#define AF_CAN_H
+
+#ifdef __KERNEL__
+#include "version.h"
+RCSID("$Id: af_can.h,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+#endif
+
+#ifdef __KERNEL__
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if.h>
+#else
+#include <net/if.h>
+#endif
+
+#include "can.h"
+
+#define PF_CAN       30
+#define AF_CAN       PF_CAN
+
+#define CAN_TP16     1
+#define CAN_TP20     2
+#define CAN_RAW      3
+#define CAN_BCM      4
+#define CAN_MCNET    5
+#define CAN_ISOTP    6
+#define CAN_BAP      7
+#define CAN_MAX      8
+
+#define SOL_CAN_BASE 100
+
+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;
+    } can_addr;
+};
+
+struct can_filter {
+    canid_t can_id;
+    canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#ifdef __KERNEL__
+
+#define CAN_PROC_DIR "sys/net/can" /* /proc/... */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+struct can_proto {
+    struct proto_ops *ops;
+    struct proto     *prot;
+};
+#else
+struct can_proto {
+    struct proto_ops *ops;
+    struct module    *owner;
+    size_t           obj_size;
+};
+#endif
+
+void can_proto_register(int proto, struct can_proto *cp);
+void can_proto_unregister(int proto);
+void can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+       void (*func)(struct sk_buff *, void *), void *data, char *ident);
+void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
+       void (*func)(struct sk_buff *, void *), void *data);
+void can_dev_register(struct net_device *dev,
+       void (*func)(unsigned long msg, void *), void *data);
+void can_dev_unregister(struct net_device *dev,
+       void (*func)(unsigned long msg, void *), void *data);
+int  can_send(struct sk_buff *skb);
+
+unsigned long timeval2jiffies(struct timeval *tv, int round_up);
+
+void can_debug_skb(struct sk_buff *skb);
+void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+
+/* af_can rx dispatcher structures */
+
+struct rcv_list {
+    struct rcv_list *next;
+    canid_t can_id;
+    canid_t mask;
+    unsigned long matches;
+    void (*func)(struct sk_buff *, void *);
+    void *data;
+    char *ident;
+};
+
+struct rcv_dev_list {
+    struct rcv_dev_list *next;
+    struct net_device *dev;
+    struct rcv_list *rx_all;
+    struct rcv_list *rx_fil;
+    struct rcv_list *rx_inv;
+    struct rcv_list *rx_sff[0x800];
+    struct rcv_list *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 rcv_entries;
+    unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+void can_init_proc(void);
+void can_remove_proc(void);
+
+#endif
+
+#endif /* AF_CAN_H */
diff --git a/kernel/2.6/can/bcm.c b/kernel/2.6/can/bcm.c
new file mode 100644 (file)
index 0000000..21c7f93
--- /dev/null
@@ -0,0 +1,1396 @@
+/*
+ * bcm.c
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+#include "version.h"
+#include "bcm.h"
+
+RCSID("$Id: bcm.c,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+
+#ifdef DEBUG
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...)       (debug & 1 ? \
+                              (printk(KERN_DEBUG "BCM %s: ", __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
+
+/* use of last_frames[index].can_dlc */
+#define RX_RECV    0x40 /* received data for this element */
+#define RX_THR     0x80 /* this element has not been sent due to throttle functionality */
+#define BCM_CAN_DLC_MASK 0x0F /* clean flags by masking with BCM_CAN_DLC_MASK */
+
+#define NAME "Broadcast Manager (BCM) for LLCF"
+#define IDENT "bcm"
+static __initdata const char banner[] = BANNER(NAME);
+
+MODULE_DESCRIPTION(NAME);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+
+#define GET_U64(p) (*(unsigned long long*)(p)->data)
+
+struct bcm_op {
+    struct bcm_op *next;
+    canid_t can_id;
+    int flags;
+    unsigned long j_ival1, j_ival2, j_lastmsg;
+    unsigned long frames_abs, frames_filtered;
+    struct timeval ival1, ival2, stamp;
+    struct timer_list timer, thrtimer;
+    int count;
+    int nframes;
+    int currframe;
+    struct can_frame *frames;
+    struct can_frame *last_frames;
+    struct sock *sk;
+};
+
+struct bcm_user_data {
+    struct bcm_op *rx_ops;
+    struct bcm_op *tx_ops;
+    struct proc_dir_entry *bcm_proc_read;
+    char procname [9];
+};
+
+static struct proc_dir_entry *proc_dir = NULL;
+int bcm_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+static void bcm_notifier(unsigned long msg, void *data);
+static int bcm_release(struct socket *sock);
+static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+                      int flags);
+static unsigned int bcm_poll(struct file *file, struct socket *sock,
+                            poll_table *wait);
+static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size);
+static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size, int flags);
+
+static void bcm_tx_timeout_handler(unsigned long data);
+static void bcm_rx_handler(struct sk_buff *skb, void *op);
+static void bcm_rx_timeout_handler(unsigned long data);
+static void bcm_rx_thr_handler(unsigned long data);
+static struct bcm_op *bcm_find_op(struct bcm_op *ops, canid_t can_id);
+static void bcm_insert_op(struct bcm_op **ops, struct bcm_op *op);
+static void bcm_delete_tx_op(struct bcm_op **ops, canid_t can_id);
+static void bcm_delete_rx_op(struct bcm_op **ops, canid_t can_id);
+static void bcm_remove_op(struct bcm_op *op);
+static void bcm_can_tx(struct bcm_op *op);
+static void bcm_send_to_user(struct sock *sk, struct bcm_msg_head *head,
+                            struct can_frame *frames, struct timeval *tv);
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data);
+static void bcm_rx_starttimer(struct bcm_op *op);
+static void bcm_rx_update_and_send(struct bcm_op *op,
+                                  struct can_frame *lastdata,
+                                  struct can_frame *rxdata);
+static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
+                               struct can_frame *rxdata);
+
+static struct proto_ops bcm_ops = {
+    .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         = 0,
+    .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,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+
+struct bcm_sock {
+    struct sock          sk;
+    struct bcm_user_data opt;
+};
+
+#define bcm_sk(sk) ((struct bcm_user_data *)(sk)->sk_protinfo)
+
+static struct proto bcm_proto = {
+    .name     = "CAN_BCM",
+    .owner    = THIS_MODULE,
+    .obj_size = sizeof(struct bcm_sock),
+};
+
+static struct can_proto bcm_can_proto = {
+    .ops  = &bcm_ops,
+    .prot = &bcm_proto,
+};
+
+#else
+
+#define bcm_sk(sk) ((struct bcm_user_data *)(sk)->sk_protinfo)
+
+static struct can_proto bcm_can_proto = {
+    .ops      = &bcm_ops,
+    .owner    = THIS_MODULE,
+    .obj_size = 0,
+};
+
+#endif
+
+
+static int __init bcm_init(void)
+{
+    printk(banner);
+
+    can_proto_register(CAN_BCM, &bcm_can_proto);
+
+    /* create /proc/can/bcm directory */
+    proc_dir = proc_mkdir(CAN_PROC_DIR"/"IDENT, NULL);
+
+    if (proc_dir)
+       proc_dir->owner = THIS_MODULE;
+
+    return 0;
+}
+
+static void __exit bcm_exit(void)
+{
+    can_proto_unregister(CAN_BCM);
+
+    if (proc_dir)
+       remove_proc_entry(CAN_PROC_DIR"/"IDENT, NULL);
+
+}
+
+static void bcm_notifier(unsigned long msg, void *data)
+{
+    struct sock *sk = (struct sock *)data;
+
+    DBG("called for sock %p\n", sk);
+
+    switch (msg)
+    {
+    case NETDEV_UNREGISTER:
+       sk->sk_bound_dev_if = 0;
+    case NETDEV_DOWN:
+       sk->sk_err = ENETDOWN;
+       sk->sk_error_report(sk);
+    }
+}
+
+static int bcm_release(struct socket *sock)
+{
+    struct sock *sk = sock->sk;
+    struct bcm_user_data *ud = bcm_sk(sk);
+    struct bcm_op *op,*next;
+
+    /* many things to do here:
+       free all rx_ops and tx_ops, bcm_user_data structure,
+       can_rx_unregister(dev, canid, raw_rcv) and can-data in ?x_ops */
+
+    DBG("socket %p, sk %p\n", sock, sk);
+
+    /* remove userdata, bcm_ops, timer, etc. */
+
+    if (ud) {
+       for (op = ud->tx_ops; op ; op = next) {
+           DBG("removing tx_op (%p) for can_id <%03X>\n", op, op->can_id);
+           next = op->next;
+           bcm_remove_op(op);
+       }
+
+       for (op = ud->rx_ops; op ; op = next) {
+           DBG("removing rx_op (%p) for can_id <%03X>\n", op, op->can_id);
+           next = op->next;
+
+           if (sk->sk_bound_dev_if) {
+               struct net_device *dev = dev_get_by_index(sk->sk_bound_dev_if);
+               if (dev) {
+                   can_rx_unregister(dev, op->can_id, 0xFFFFFFFFU, bcm_rx_handler, op);
+                   dev_put(dev);
+               }
+           } else
+               DBG("sock %p not bound for can_rx_unregister()\n", sk);
+
+           bcm_remove_op(op);
+       }
+
+       if ((proc_dir) && (ud->bcm_proc_read)) {
+           remove_proc_entry(ud->procname, proc_dir);
+       }
+
+       kfree (ud);
+       sk->sk_protinfo = NULL;
+    }
+
+    if (sk->sk_bound_dev_if) {
+       struct net_device *dev = dev_get_by_index(sk->sk_bound_dev_if);
+       if (dev) {
+           can_dev_unregister(dev, bcm_notifier, sk);
+           dev_put(dev);
+       }
+    } else
+       DBG("sock %p not bound for can_dev_unregister()\n", 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 net_device *dev;
+    struct bcm_user_data *ud;
+
+    /* bind a device to this socket */
+
+    dev = dev_get_by_index(addr->can_ifindex);
+    if (!dev) {
+       DBG("could not find device %d\n", addr->can_ifindex);
+       return -ENODEV;
+    }
+    sk->sk_bound_dev_if = dev->ifindex;
+    can_dev_register(dev, bcm_notifier, sk);
+    dev_put(dev);
+
+    DBG("socket %p to device %s (idx %d)\n", sock, dev->name, dev->ifindex);
+
+    /* create struct for BCM-specific data for this socket */
+
+    if (!(ud = kmalloc(sizeof(struct bcm_user_data), GFP_KERNEL)))
+       return -ENOMEM;
+
+    /* intitial BCM operations */
+    ud->tx_ops = NULL;
+    ud->rx_ops = NULL;
+    ud->bcm_proc_read = NULL;
+
+    sk->sk_protinfo = ud;
+
+    if (proc_dir) {
+       sprintf(ud->procname, "%p", ud);
+       ud->bcm_proc_read = create_proc_read_entry(ud->procname, 0644,
+                                                  proc_dir, bcm_read_proc, ud);
+    }
+
+    return 0;
+}
+
+int bcm_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    struct bcm_user_data *ud = (struct bcm_user_data *) data;
+    struct bcm_op *op;
+    struct net_device *dev = NULL;
+
+    len += snprintf(page + len, PAGE_SIZE - len,">>> ud %p", ud);
+
+    if (ud->rx_ops) {
+       if (ud->rx_ops->sk->sk_bound_dev_if)
+           dev = dev_get_by_index(ud->rx_ops->sk->sk_bound_dev_if);
+       len += snprintf(page + len, PAGE_SIZE - len,
+                       " / sk %p / socket %p", ud->rx_ops->sk, ud->rx_ops->sk->sk_socket);
+    }
+    else
+       if (ud->tx_ops) {
+           if (ud->tx_ops->sk->sk_bound_dev_if)
+               dev = dev_get_by_index(ud->tx_ops->sk->sk_bound_dev_if);
+           len += snprintf(page + len, PAGE_SIZE - len,
+                           " / sk %p / socket %p", ud->tx_ops->sk, ud->tx_ops->sk->sk_socket);
+    }
+
+    if (dev) {
+       len += snprintf(page + len, PAGE_SIZE - len, " / %s", dev->name);
+       dev_put(dev);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+
+    for (op = ud->rx_ops; op && (len < PAGE_SIZE - 100); op = op->next) {
+
+       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 [%d]%c ",
+                       op->can_id, 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 - 100) /* 100 Bytes before end of buffer */
+         len += snprintf(page + len, PAGE_SIZE - len, "(..)\n"); /* mark output cutted off */
+    }
+
+    for (op = ud->tx_ops; op && (len < PAGE_SIZE - 100); op = op->next) {
+
+       len += snprintf(page + len, PAGE_SIZE - len, "tx_op: %03X [%d] ",
+                       op->can_id, 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) /* 100 Bytes before end of buffer */
+         len += snprintf(page + len, PAGE_SIZE - len, "(..)\n"); /* mark output cutted off */
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+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 int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size)
+{
+    struct bcm_msg_head msg_head;
+    int i;
+    struct bcm_op *op;
+    int err;
+    struct sock *sk = sock->sk;
+    struct bcm_user_data *ud = bcm_sk(sk);
+    char c;
+    int rbytes = 0; /* read bytes as return value */
+
+    /* read message head information */
+
+    if ((err = memcpy_fromiovec((unsigned char*)&msg_head, msg->msg_iov,
+                               sizeof(msg_head))) < 0)
+       return err;
+
+    DBG("opcode %d for can_id <%03X>\n", msg_head.opcode, msg_head.can_id);
+
+    if (!sk->sk_bound_dev_if) {
+       DBG("sock %p not bound\n", sk); /* and therefore ud not initialized */
+       return -ENOTCONN;
+    }
+
+    switch (msg_head.opcode) {
+
+    case TX_SETUP:
+
+       if (msg_head.nframes < 1) /* we need at least one can_frame */
+           return -EINVAL;
+
+       /* check the given can_id */
+
+       if (!(op = bcm_find_op(ud->tx_ops, msg_head.can_id))) {
+
+           /* insert new BCM operation for the given can_id */
+
+           if (!(op = kmalloc(sizeof(struct bcm_op), GFP_KERNEL)))
+               return -ENOMEM;
+
+           memset(op, 0, sizeof(struct bcm_op)); /* init to zero, e.g. for timers */
+
+           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 (!(op->frames = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL))) {
+               kfree(op);
+               return -ENOMEM;
+           }
+
+           for (i = 0; i < msg_head.nframes; i++) {
+               memcpy_fromiovec((unsigned char*)&op->frames[i], msg->msg_iov, sizeof(struct can_frame));
+               if (msg_head.flags & TX_CP_CAN_ID)
+                   op->frames[i].can_id = msg_head.can_id; /* copy can_id into frame */
+           }
+
+           op->last_frames = NULL; /* tx_ops never compare with previous received messages */
+
+           op->sk = sk; /* bcm_can_tx / bcm_tx_timeout_handler needs this */
+
+           init_timer(&op->timer); /* initialize uninitialized (kmalloc) structure */
+           init_timer(&op->thrtimer); /* currently unused in tx_ops */
+
+           op->timer.function = bcm_tx_timeout_handler; /* handler for tx_ops */
+           op->timer.data = (unsigned long)op; /* timer.data points to this op-structure */
+
+           /* add this bcm_op to the list of the tx_ops */
+           bcm_insert_op(&ud->tx_ops, op);
+
+       }
+       else {
+           /* 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? */
+           if (msg_head.nframes > op->nframes) {
+
+               /* yes => create new array */
+
+               struct can_frame *p;
+               if (!(p = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL)))
+                   return -ENOMEM;
+
+               kfree (op->frames);
+               op->frames = p;
+           }
+
+           /* update can_frames content */
+           for (i = 0; i < msg_head.nframes; i++) {
+               memcpy_fromiovec((unsigned char*)&op->frames[i], msg->msg_iov, sizeof(struct can_frame));
+               if (msg_head.flags & TX_CP_CAN_ID)
+                   op->frames[i].can_id = msg_head.can_id; /* copy can_id into frame */
+           }
+
+       }
+
+       if (op->nframes != msg_head.nframes) {
+           op->nframes   = msg_head.nframes;
+           op->currframe = 0; /* start multiple frame transmission with index 0 */
+       }
+
+       /* check flags */
+
+       op->flags = msg_head.flags;
+
+       if (op->flags & TX_RESET_MULTI_IDX)
+           op->currframe = 0; /* start multiple frame transmission with index 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 = timeval2jiffies(&msg_head.ival1, 1);
+           op->j_ival2 = timeval2jiffies(&msg_head.ival2, 1);
+
+           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);
+
+           op->flags |= TX_ANNOUNCE; /* spec: send can_frame when starting timer */
+           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);
+
+       rbytes = msg_head.nframes * sizeof(struct can_frame) + sizeof(struct bcm_msg_head);
+
+       break; /* TX_SETUP */
+
+    case TX_DELETE:
+
+       bcm_delete_tx_op(&ud->tx_ops, msg_head.can_id);
+
+       rbytes = sizeof(struct bcm_msg_head);
+
+       break; /* TX_DELETE */
+
+    case TX_READ:
+
+       /* reuse msg_head for the reply */
+       msg_head.opcode  = TX_STATUS; /* reply to TX_READ */
+       op = bcm_find_op(ud->tx_ops, msg_head.can_id);
+       c  = 'T'; /* for nice debug output ... */
+
+       goto TRX_READ;
+
+    case RX_READ:
+
+       /* reuse msg_head for the reply */
+       msg_head.opcode  = RX_STATUS; /* reply to RX_READ */
+       op = bcm_find_op(ud->rx_ops, msg_head.can_id);
+       c  = 'R'; /* for nice debug output ... */
+
+    TRX_READ:
+
+       /* check the given can_id */
+
+       if (!op) {
+           DBG("%cX_READ: did not find op for can_id <%03X>\n",
+               c, msg_head.can_id);
+
+           msg_head.flags   |= CMD_ERROR;
+           msg_head.nframes  = 0;
+           bcm_send_to_user(sk, &msg_head, NULL, NULL);
+       }
+       else {
+           DBG("%cX_READ: sending status for can_id <%03X>\n",
+               c, 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(sk, &msg_head, op->frames, NULL);
+       }
+
+       rbytes = sizeof(struct bcm_msg_head);
+
+       break; /* [T|R]X_READ */
+
+    case TX_SEND:
+       {
+           struct sk_buff *skb;
+           struct net_device *dev;
+           
+           /* just copy and send one can_frame */
+           
+           if (msg_head.nframes < 1) /* we need at least one can_frame */
+               return -EINVAL;
+
+           skb = alloc_skb(sizeof(struct can_frame), GFP_KERNEL);
+
+           if (!skb)
+               break;
+
+           memcpy_fromiovec(skb_put(skb, sizeof(struct can_frame)), msg->msg_iov, sizeof(struct can_frame));
+
+           DBG_FRAME("BCM: TX_SEND: sending frame",
+                     (struct can_frame *)skb->data);
+           dev = dev_get_by_index(sk->sk_bound_dev_if);
+
+           if (dev) {
+               skb->dev = dev;
+               can_send(skb);
+               dev_put(dev);
+           }
+
+           rbytes = sizeof(struct can_frame) + sizeof(struct bcm_msg_head);
+       }
+       break;
+
+    case RX_SETUP:
+
+       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");
+
+           msg_head.flags   |= CMD_ERROR; /* return msg_head back to sender */
+           msg_head.nframes  = 0;
+           bcm_send_to_user(sk, &msg_head, NULL, NULL);
+
+           rbytes = sizeof(struct bcm_msg_head);
+
+           break;
+       }
+
+       /* check the given can_id */
+
+       if (!(op = bcm_find_op(ud->rx_ops, msg_head.can_id))) {
+
+           /* insert new BCM operation for the given can_id */
+
+           if (!(op = kmalloc(sizeof(struct bcm_op), GFP_KERNEL)))
+               return -ENOMEM;
+
+           memset(op, 0, sizeof(struct bcm_op)); /* init to zero, e.g. for timers */
+
+           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 (op->nframes) {
+
+               /* create array for can_frames and copy the data */
+               if (!(op->frames = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL))) {
+                   kfree(op);
+                   return -ENOMEM;
+               }
+
+               for (i = 0; i < msg_head.nframes; i++)
+                   memcpy_fromiovec((unsigned char*)&op->frames[i], msg->msg_iov, sizeof(struct can_frame));
+
+               /* create array for received can_frames */
+               if (!(op->last_frames = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL))) {
+                   kfree(op->frames);
+                   kfree(op);
+                   return -ENOMEM;
+               }
+
+               /* clear received can_frames to indicate 'nothing received' */
+               memset(op->last_frames, 0, msg_head.nframes * sizeof(struct can_frame));
+           }
+           else {
+               op->frames = NULL;
+
+               /* even when we have the RX_FILTER_ID case, we need to store the last frame */
+               /* for the throttle functionality */
+
+               /* create array for received can_frames */
+               if (!(op->last_frames = kmalloc(sizeof(struct can_frame), GFP_KERNEL)))
+                   return -ENOMEM;
+
+               /* clear received can_frames to indicate 'nothing received' */
+               memset(op->last_frames, 0, sizeof(struct can_frame));
+           }
+
+           op->sk = sk; /* bcm_delete_rx_op() needs this */
+
+           init_timer(&op->timer); /* initialize uninitialized (kmalloc) structure */
+           init_timer(&op->thrtimer); /* init throttle timer for RX_CHANGED */
+
+           op->timer.function = bcm_rx_timeout_handler; /* handler for rx timeouts */
+           op->timer.data = (unsigned long)op; /* timer.data points to this op-structure */
+
+           op->thrtimer.function = bcm_rx_thr_handler; /* handler for RX_CHANGED throttle timeouts */
+           op->thrtimer.data = (unsigned long)op; /* timer.data points to this op-structure */
+           op->thrtimer.expires = 0; /* mark disabled timer */
+
+           /* add this bcm_op to the list of the tx_ops */
+           bcm_insert_op(&ud->rx_ops, op);
+
+           c=1; /* call can_rx_register() at end of RX_SETUP */
+
+       }
+       else {
+           /* 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? */
+           if (msg_head.nframes > op->nframes) {
+
+               /* yes => create new arrays */
+
+               struct can_frame *p;
+
+               if (!(p = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL)))
+                   return -ENOMEM;
+
+               if (op->frames)
+                   kfree (op->frames);
+               op->frames = p;
+
+               if (!(p = kmalloc(msg_head.nframes * sizeof(struct can_frame), GFP_KERNEL)))
+                   return -ENOMEM;
+               if (op->last_frames)
+                   kfree (op->last_frames);
+               op->last_frames = p;
+           }
+
+           if (msg_head.nframes) {
+               /* update can_frames content */
+               for (i = 0; i < msg_head.nframes; i++)
+                   memcpy_fromiovec((unsigned char*)&op->frames[i], msg->msg_iov, sizeof(struct can_frame));
+
+               /* clear received can_frames to indicate 'nothing received' */
+               memset(op->last_frames, 0, msg_head.nframes * sizeof(struct can_frame));
+           }
+
+           op->nframes = msg_head.nframes;
+           c=0; /* do not call can_rx_register() at end of RX_SETUP */
+
+       } /* if (!bcm_find_op(ud->tx_ops, msg_head.can_id)) */
+
+
+       /* 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->j_ival1 = timeval2jiffies(&msg_head.ival1, 1);
+               op->ival2   = msg_head.ival2;
+               op->j_ival2 = timeval2jiffies(&msg_head.ival2, 1);
+
+               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 for rx timeouts.\n");
+               }
+
+               /* free currently blocked msgs ? */
+               if (op->thrtimer.expires) { /* running throttle timer? */
+                   DBG("RX_SETUP: unblocking throttled msgs.\n");
+                   del_timer(&op->thrtimer);
+                   op->thrtimer.expires = jiffies + 2; /* send blocked msgs hereafter */
+                   add_timer(&op->thrtimer);
+               }
+               /* if (op->j_ival2) is zero, no (new) throttling will happen */
+               /* see 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 (c) {
+           struct net_device *dev = dev_get_by_index(sk->sk_bound_dev_if);
+
+           DBG("RX_SETUP: can_rx_register() for can_id <%03X>. rx_op is (%p)\n", op->can_id, op);
+
+           if (dev) {
+               can_rx_register(dev, op->can_id, 0xFFFFFFFFU, bcm_rx_handler, op, IDENT);
+               dev_put(dev);
+           }
+       }
+
+       rbytes = msg_head.nframes * sizeof(struct can_frame) + sizeof(struct bcm_msg_head);
+
+       break; /* RX_SETUP */
+
+    case RX_DELETE:
+
+       bcm_delete_rx_op(&ud->rx_ops, msg_head.can_id);
+
+       rbytes = sizeof(struct bcm_msg_head);
+
+       break; /* RX_DELETE */
+
+    default:
+
+       DBG("Unknown opcode %d\n", msg_head.opcode);
+
+       msg_head.flags   |= CMD_ERROR; /* return msg_head back to sender */
+       msg_head.nframes  = 0;
+       bcm_send_to_user(sk, &msg_head, NULL, NULL);
+
+       rbytes = sizeof(struct bcm_msg_head);
+
+       break;
+    }
+
+    return rbytes;
+}
+
+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;
+    if (!(skb = skb_recv_datagram(sk, flags, noblock, &error))) {
+       return error;
+    }
+
+    DBG("delivering skbuff %p\n", skb);
+    DBG_SKB(skb);
+
+    if (skb->len < size)
+       size = skb->len;
+    if ((err = memcpy_toiovec(msg->msg_iov, skb->data, size)) < 0) {
+       skb_free_datagram(sk, skb);
+       return err;
+    }
+
+    sock_recv_timestamp(msg, sk, skb);
+
+    DBG("freeing sock %p, skbuff %p\n", sk, skb);
+    skb_free_datagram(sk, skb);
+
+    return size;
+}
+
+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)) { /* create notification to user? */
+
+           struct bcm_msg_head msg_head;
+
+           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->sk, &msg_head, NULL, NULL);
+       }
+    }
+
+    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);
+
+       bcm_can_tx(op); /* send (next) frame */
+    }
+    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);
+
+           bcm_can_tx(op); /* send (next) frame */
+       }
+       else
+           DBG("no timer restart\n");
+    }
+
+    return;
+
+}
+
+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;
+
+    del_timer(&op->timer); /* disable timeout */
+
+    DBG("Called with bcm_op (%p)\n", op);
+
+    if (skb->len == sizeof(rxframe)) {
+       memcpy(&rxframe, skb->data, sizeof(rxframe));
+       skb_get_timestamp(skb, &op->stamp); /* save rx timestamp */
+       op->frames_abs++; /* statistics */
+       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");
+       bcm_can_tx(op); /* send op->frames[0] to CAN device */
+       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 */
+       /* MUX-mask is 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);
+    }
+}
+
+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)) { /* first time? */
+       DBG("first time :)\n");
+       bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+       return;
+    }
+
+    /* do a real check in can_data */
+
+    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 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");
+}
+
+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, sizeof(struct can_frame));
+    lastdata->can_dlc |= RX_RECV; /* mark as used */
+
+    /* throttle bcm_rx_changed ? */
+    if ((op->thrtimer.expires) || /* somebody else is already waiting OR */
+       ((op->j_ival2) && (nexttx > jiffies))) {      /* we have to wait */
+
+       lastdata->can_dlc |= RX_THR; /* mark as 'throttled' */
+
+       if (!(op->thrtimer.expires)) { /* start 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
+       bcm_rx_changed(op, rxdata); /* send RX_CHANGED to the user */
+}
+
+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);
+    }
+}
+
+
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
+{
+    struct bcm_msg_head head;
+
+    op->j_lastmsg = jiffies;
+    op->frames_filtered++; /* statistics */
+
+    if (op->frames_filtered > ULONG_MAX/100)
+       op->frames_filtered = op->frames_abs = 0; /* restart - spinlock ? */
+
+    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->sk, &head, data, &op->stamp);
+}
+
+
+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->sk, &msg_head, NULL, NULL);
+
+    /* no restart of the timer is done here! */
+
+    /* if the 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 * sizeof(struct can_frame));
+       DBG("RX_ANNOUNCE_RESTART\n");
+    }
+
+}
+
+static void bcm_rx_thr_handler(unsigned long data)
+{
+    struct bcm_op *op = (struct bcm_op*)data;
+    int i = 0;
+
+    op->thrtimer.expires = 0; /* mark disabled / consumed timer */
+
+    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]);
+       }
+    }
+}
+
+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);
+
+    skb = alloc_skb(sizeof(struct can_frame), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+
+    if (!skb)
+       return;
+
+    memcpy(skb_put(skb, sizeof(struct can_frame)), cf, sizeof(struct can_frame));
+
+    if (op->sk->sk_bound_dev_if) {
+       dev = dev_get_by_index(op->sk->sk_bound_dev_if);
+
+       if (dev) {
+           skb->dev = dev;
+           can_send(skb);
+           dev_put(dev);
+       }
+    }
+
+    op->currframe++;
+    op->frames_abs++; /* statistics */
+
+    /* reached last frame? */
+    if (op->currframe >= op->nframes)
+       op->currframe = 0;
+
+}
+
+static void bcm_send_to_user(struct sock *sk, struct bcm_msg_head *head,
+                            struct can_frame *frames, struct timeval *tv)
+{
+    struct sk_buff *skb;
+    struct can_frame *firstframe;
+    int datalen = head->nframes * sizeof(struct can_frame);
+    int err;
+
+    if (!sk) {
+       DBG("no sk available\n");
+       return;
+    }
+
+    skb = alloc_skb(sizeof(*head) + datalen, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+    memcpy(skb_put(skb, sizeof(*head)), head, sizeof(*head));
+    firstframe = (struct can_frame *) skb->tail; /* can_frames starting here */
+
+    if (tv)
+       skb_set_timestamp(skb, tv);
+
+    if (head->nframes){
+       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 ((err = sock_queue_rcv_skb(sk, skb)) < 0) {
+       DBG("sock_queue_rcv_skb failed: %d\n", err);
+       kfree_skb(skb);
+    }
+}
+
+static struct bcm_op *bcm_find_op(struct bcm_op *ops, canid_t can_id)
+{
+    struct bcm_op *p;
+
+    for (p = ops; p; p = p->next)
+       if (p->can_id == can_id)
+           return p;
+
+    return NULL;
+}
+
+static void bcm_delete_rx_op(struct bcm_op **ops, canid_t can_id)
+{
+    struct bcm_op *p, **q;
+
+    for (q = ops; p = *q; q = &p->next)
+       if (p->can_id == can_id) {
+           *q = p->next;
+           DBG("removing rx_op (%p) for can_id <%03X>\n", p, p->can_id);
+
+           if (p->sk->sk_bound_dev_if) {
+               struct net_device *dev = dev_get_by_index(p->sk->sk_bound_dev_if);
+               if (dev) {
+                   can_rx_unregister(dev, p->can_id, 0xFFFFFFFFU, bcm_rx_handler, p);
+                   dev_put(dev);
+               }
+           } else
+               DBG("sock %p not bound for can_rx_unregister()\n", p->sk);
+
+           bcm_remove_op(p);
+           return;
+       }
+}
+
+static void bcm_delete_tx_op(struct bcm_op **ops, canid_t can_id)
+{
+    struct bcm_op *p, **q;
+
+    for (q = ops; p = *q; q = &p->next)
+       if (p->can_id == can_id) {
+           *q = p->next;
+           DBG("removing rx_op (%p) for can_id <%03X>\n", p, p->can_id);
+           bcm_remove_op(p);
+           return;
+       }
+}
+
+static void bcm_remove_op(struct bcm_op *op)
+{
+    del_timer(&op->timer);
+    del_timer(&op->thrtimer);
+    if (op->frames)
+       kfree(op->frames);
+    if (op->last_frames)
+       kfree(op->last_frames);
+    kfree(op);
+
+    return;
+}
+
+static void bcm_insert_op(struct bcm_op **ops, struct bcm_op *op)
+{
+    op->next = *ops;
+    *ops = op;
+}
+
+module_init(bcm_init);
+module_exit(bcm_exit);
diff --git a/kernel/2.6/can/bcm.h b/kernel/2.6/can/bcm.h
new file mode 100644 (file)
index 0000000..8a2b4a4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * bcm.h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef BCM_H
+#define BCM_H
+
+#ifdef __KERNEL__
+#include "version.h"
+RCSID("$Id: bcm.h,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+#endif
+
+struct bcm_msg_head {
+    int opcode;                   /* command */
+    int flags;                    /* special flags */
+    int count;                    /* run 'count' times ival1 then ival2 */
+    struct timeval ival1, ival2;  /* intervals */
+    canid_t can_id;               /* 32 Bit SFF/EFF. MSB set at EFF */
+    int nframes;                  /* number of following can_frame's */
+    struct can_frame frames[0];
+};
+
+enum {NO_OP,
+      TX_SETUP, TX_DELETE, TX_READ, TX_SEND, RX_SETUP, RX_DELETE, RX_READ,
+      TX_STATUS, TX_EXPIRED, RX_STATUS, RX_TIMEOUT, RX_CHANGED};
+
+#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
+
+#define CMD_ERROR    0x8000
+
+#endif /* BCM_H */
diff --git a/kernel/2.6/can/can.h b/kernel/2.6/can/can.h
new file mode 100644 (file)
index 0000000..8960ee5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * can.h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#ifdef __KERNEL__
+#include "version.h"
+RCSID("$Id: can.h,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+#endif
+
+#include <linux/types.h>
+
+#define ETH_P_CAN    0x000c
+
+/* 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 */
+
+/* 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) */
+
+typedef __u32 canid_t;
+
+struct can_frame {
+    canid_t can_id;      /* 32 bit CAN_ID + EFF/RTR flags */
+    __u8    can_dlc;     /* data length code: 0 .. 8 */
+    __u8    data[8] __attribute__ ((aligned(8)));
+};
+
+#endif /* CAN_H */
diff --git a/kernel/2.6/can/can_ioctl.h b/kernel/2.6/can/can_ioctl.h
new file mode 100644 (file)
index 0000000..3d625b0
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * can_ioctl_h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef CAN_IOCTL_H
+#define CAN_IOCTL_H
+
+#ifdef __KERNEL__
+#include "version.h"
+RCSID("$Id: can_ioctl.h,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+#endif
+
+#include <linux/sockios.h>
+
+
+/* max. 16 private ioctls */
+
+#define SIOCSRATE      (SIOCDEVPRIVATE+0)
+#define SIOCGRATE      (SIOCDEVPRIVATE+1)
+
+#define SIOCSMODE      (SIOCDEVPRIVATE+2)
+#define SIOCGMODE      (SIOCDEVPRIVATE+3)
+
+#define SIOCSFILTER    (SIOCDEVPRIVATE+4)
+#define SIOCGFILTER    (SIOCDEVPRIVATE+5)
+
+#define SIOCSTRX       (SIOCDEVPRIVATE+6)
+#define SIOCGTRX       (SIOCDEVPRIVATE+7)
+
+#define SIOCGSTATUS    (SIOCDEVPRIVATE+8)
+#define SIOCGTRXSTATUS (SIOCDEVPRIVATE+9)
+
+#define SIOCGSTATS     (SIOCDEVPRIVATE+10)
+
+/* parameters for ioctls */
+
+/* baudrate for CAN-controller */
+#define RATE_SPEED 0 /* parameter is in bits/second (speed 0: autoprobe) */
+#define RATE_BTREG 1 /* parameter is controller specific bit-timing register */
+
+/* operation modes for CAN-controller */
+#define MODE_OFFLINE 0
+#define MODE_RX      1
+#define MODE_TX      2
+#define MODE_TRX     (MODE_TX | MODE_RX)
+#define MODE_LISTEN  4 /* no acknowledge on CAN layer */
+
+/* filter modes (may vary due to controller specific capabilities) */
+#define FILTER_CAPAB       0  /* get filter type capabilities (32 Bit value) */
+#define FILTER_MASK_VALUE  1  /* easy bit filter (see struct can_filter) */  
+#define FILTER_SFF_BITMASK 2  /* bitfield with 2048 bit SFF filter */
+
+                              /* filters 3 - 31 currently undefined */
+
+#define FILTER_MAX         31 /* max. filter type value */
+
+/* operation modes for CAN-transceiver */
+#define TRX_OPERATE 0 /* normal operation */
+#define TRX_STANDBY 1 /* standby */
+#define TRX_SLEEP   2 /* goto sleep */
+
+/* operating status of CAN-controller */
+#define STATUS_OK            0
+#define STATUS_WARNING       1 /* see parameter for additional info */
+#define STATUS_ERROR         2 /* see parameter for additional info */
+#define STATUS_ERROR_PASSIVE 3
+#define STATUS_BUS_OFF       4
+
+/* additional info for STATUS_ERROR */
+#define STATUS_ERR_BIT   0x00
+#define STATUS_ERR_FORM  0x01
+#define STATUS_ERR_STUFF 0x02
+#define STATUS_ERR_CRC   0x04
+#define STATUS_ERR_ACK   0x08
+#define STATUS_ERR_OTHER 0x10
+
+/* operating status of CAN-transceiver */
+#define TRXSTATUS_OK                 0x000
+#define TRXSTATUS_SLEEP              0x001
+#define TRXSTATUS_CANH_NO_WIRE       0x002
+#define TRXSTATUS_CANH_SHORT_TO_BAT  0x004
+#define TRXSTATUS_CANH_SHORT_TO_VCC  0x008
+#define TRXSTATUS_CANH_SHORT_TO_GND  0x010
+#define TRXSTATUS_CANL_NO_WIRE       0x020
+#define TRXSTATUS_CANL_SHORT_TO_BAT  0x040
+#define TRXSTATUS_CANL_SHORT_TO_VCC  0x080
+#define TRXSTATUS_CANL_SHORT_TO_GND  0x100
+#define TRXSTATUS_CANL_SHORT_TO_CANH 0x200
+
+struct can_device_stats {
+
+    int error_warning;
+    int data_overrun;
+    int wakeup;
+    int bus_error;
+    int error_passive;
+    int arbitration_lost;
+    int restarts;
+    int bus_error_at_init;
+
+};
+
+#endif /* CAN_IOCTL_H */
diff --git a/kernel/2.6/can/proc.c b/kernel/2.6/can/proc.c
new file mode 100644 (file)
index 0000000..4d17122
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * af_can_proc.c
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+
+#include "af_can.h"
+#include "version.h"
+
+RCSID("$Id: proc.c,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+
+/* proc filenames */
+
+#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"
+
+static void can_init_stats(int caller);
+static void can_stat_update(unsigned long data);
+
+static struct proc_dir_entry *can_create_proc_read_entry(const char *name, mode_t mode, read_proc_t* read_proc, void *data);
+static void can_remove_proc_entry(const char *name);
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, unsigned long count);
+
+static int can_proc_read_version(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_stats(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_reset_stats(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_rcvlist_all(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+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;
+
+struct timer_list stattimer; /* timer for statistics update */
+
+struct s_stats  stats; /* statistics */
+struct s_pstats pstats;
+
+extern struct rcv_dev_list *rx_dev_list; /* rx dispatcher structures */
+extern int stats_timer;                  /* module parameter. default: on */
+
+/**************************************************/
+/* procfs init / remove                           */
+/**************************************************/
+
+void can_init_proc(void)
+{
+
+    /* procfs init */
+
+    /* create /proc/can directory */
+    can_dir = proc_mkdir(CAN_PROC_DIR, NULL);
+
+    if (can_dir) {
+
+       can_dir->owner = THIS_MODULE;
+
+       /* own procfs entries from the AF_CAN core */
+       pde_version     = can_create_proc_read_entry(CAN_PROC_VERSION, 0644, can_proc_read_version, NULL);
+       pde_stats       = can_create_proc_read_entry(CAN_PROC_STATS, 0644, can_proc_read_stats, NULL);
+       pde_reset_stats = can_create_proc_read_entry(CAN_PROC_RESET_STATS, 0644, can_proc_read_reset_stats, NULL);
+       pde_rcvlist_all = can_create_proc_read_entry(CAN_PROC_RCVLIST_ALL, 0644, can_proc_read_rcvlist_all, NULL);
+       pde_rcvlist_fil = can_create_proc_read_entry(CAN_PROC_RCVLIST_FIL, 0644, can_proc_read_rcvlist_fil, NULL);
+       pde_rcvlist_inv = can_create_proc_read_entry(CAN_PROC_RCVLIST_INV, 0644, can_proc_read_rcvlist_inv, NULL);
+       pde_rcvlist_sff = can_create_proc_read_entry(CAN_PROC_RCVLIST_SFF, 0644, can_proc_read_rcvlist_sff, NULL);
+       pde_rcvlist_eff = can_create_proc_read_entry(CAN_PROC_RCVLIST_EFF, 0644, can_proc_read_rcvlist_eff, NULL);
+
+       if (stats_timer) {
+           /* the statistics are updated every second (timer triggered) */
+           stattimer.function = can_stat_update;
+           stattimer.data = 0;
+           stattimer.expires = jiffies + HZ; /* every second */
+           add_timer(&stattimer); /* start statistics timer */
+       }
+    } else
+       printk(KERN_INFO "af_can: failed to create CAN_PROC_DIR. CONFIG_PROC_FS missing?\n");
+}
+
+void can_remove_proc(void)
+{
+    /* procfs remove */
+    if (pde_version) {
+       can_remove_proc_entry(CAN_PROC_VERSION);
+    }
+
+    if (pde_stats) {
+       can_remove_proc_entry(CAN_PROC_STATS);
+    }
+
+    if (pde_reset_stats) {
+       can_remove_proc_entry(CAN_PROC_RESET_STATS);
+    }
+
+    if (pde_rcvlist_all) {
+       can_remove_proc_entry(CAN_PROC_RCVLIST_ALL);
+    }
+
+    if (pde_rcvlist_fil) {
+       can_remove_proc_entry(CAN_PROC_RCVLIST_FIL);
+    }
+
+    if (pde_rcvlist_inv) {
+       can_remove_proc_entry(CAN_PROC_RCVLIST_INV);
+    }
+
+    if (pde_rcvlist_sff) {
+       can_remove_proc_entry(CAN_PROC_RCVLIST_SFF);
+    }
+
+    if (pde_rcvlist_eff) {
+       can_remove_proc_entry(CAN_PROC_RCVLIST_EFF);
+    }
+
+    if (can_dir) {
+       remove_proc_entry(CAN_PROC_DIR, NULL);
+    }
+}
+
+/**************************************************/
+/* proc read functions                            */
+/**************************************************/
+
+int can_print_recv_list(char *page, int len, struct rcv_list *rx_list, struct net_device *dev)
+{
+    struct rcv_list *p;
+
+    if (rx_list) {
+       for (p = rx_list; p; p = p->next) {
+
+           /*                             can1.  00000000  00000000  00000000  .......0  tp20 */
+           if (p->can_id & CAN_EFF_FLAG) /* EFF & CAN_ID_ALL */
+               len += snprintf(page + len, PAGE_SIZE - len, "   %-5s  %08X  %08x  %08x  %08x  %8ld  %s\n",
+                              dev->name, p->can_id, p->mask, (unsigned int)p->func,
+                              (unsigned int)p->data, p->matches, p->ident);
+           else
+               len += snprintf(page + len, PAGE_SIZE - len, "   %-5s     %03X    %08x  %08x  %08x  %8ld  %s\n",
+                              dev->name, p->can_id, p->mask, (unsigned int)p->func,
+                              (unsigned int)p->data, p->matches, p->ident);
+
+           /* does a typical line fit into the current buffer? */
+           if (len > PAGE_SIZE - 100) { /* 100 Bytes before end of buffer */
+               len += snprintf(page + len, PAGE_SIZE - len, "   (..)\n"); /* mark output cutted off */
+               return len;
+           }
+       }
+    }
+
+    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;
+}
+
+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");
+
+    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);
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_reset_stats(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+
+    can_init_stats(1);
+
+    len += snprintf(page + len, PAGE_SIZE - len, "CAN statistic reset #%ld done.\n", pstats.stats_reset);
+
+    *eof = 1;
+    return len;
+}
+
+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,
+                   "%06X [ Volkswagen AG - Low Level CAN Framework (LLCF) v%s ]\n",
+                   LLCF_VERSION_CODE, VERSION);
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_rcvlist_all(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    struct rcv_dev_list *p;
+
+    /* RX_ALL */
+    len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_all':\n");
+
+    /* find receive list for this device */
+    for (p = rx_dev_list; p; p = p->next) {
+
+       if (p->rx_all) {
+           len = can_print_recv_banner(page, len);
+           len = can_print_recv_list(page, len, p->rx_all, p->dev);
+       } else
+           if (p->dev)
+               len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    struct rcv_dev_list *p;
+
+    /* RX_FIL */
+    len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_fil':\n");
+
+    /* find receive list for this device */
+    for (p = rx_dev_list; p; p = p->next) {
+
+       if (p->rx_fil) {
+           len = can_print_recv_banner(page, len);
+           len = can_print_recv_list(page, len, p->rx_fil, p->dev);
+       } else
+           if (p->dev)
+               len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    struct rcv_dev_list *p;
+
+    /* RX_INV */
+    len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_inv':\n");
+
+    /* find receive list for this device */
+    for (p = rx_dev_list; p; p = p->next) {
+
+       if (p->rx_inv) {
+           len = can_print_recv_banner(page, len);
+           len = can_print_recv_list(page, len, p->rx_inv, p->dev);
+       } else
+           if (p->dev)
+               len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    unsigned long id = 0;
+    int i;
+    struct rcv_dev_list *p;
+
+    /* RX_SFF */
+    len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_sff':\n");
+
+    /* find receive list for this device */
+    for (p = rx_dev_list; p; p = p->next) {
+
+       for(i=0; i<0x800; i++)
+           id |= (unsigned long) p->rx_sff[i]; /* check if any entry available */
+
+       if (id) {
+           len = can_print_recv_banner(page, len);
+           for(i=0; i<0x800; i++) {
+               if ((p->rx_sff[i]) && (len < PAGE_SIZE - 100))
+                   len = can_print_recv_list(page, len, p->rx_sff[i], p->dev);
+           }
+       } else
+           if (p->dev)
+               len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+    int len = 0;
+    struct rcv_dev_list *p;
+
+    /* RX_EFF */
+    len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_eff':\n");
+
+    /* find receive list for this device */
+    for (p = rx_dev_list; p; p = p->next) {
+
+       if (p->rx_eff) {
+           len = can_print_recv_banner(page, len);
+           len = can_print_recv_list(page, len, p->rx_eff, p->dev);
+       } else
+           if (p->dev)
+               len += snprintf(page + len, PAGE_SIZE - len, "  (%s: no entry)\n", p->dev->name);
+    }
+
+    len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+    *eof = 1;
+    return len;
+}
+
+/**************************************************/
+/* proc utility functions                         */
+/**************************************************/
+
+static struct proc_dir_entry *can_create_proc_read_entry(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_entry(const char *name)
+{
+    if (can_dir)
+       remove_proc_entry(name, can_dir);
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, unsigned long count){
+
+    unsigned long ret = 0;
+
+    if (oldjif == newjif)
+       return 0;
+
+    if (count > (ULONG_MAX / HZ)) { /* see can_rcv() - this should NEVER happen! */
+       printk(KERN_ERR "af_can: calc_rate: count exceeded! %ld\n", count);
+       return 99999999;
+    }
+
+    ret = ((count * HZ) / (newjif - oldjif));
+
+    return ret;
+};
+
+/**************************************************/
+/* af_can statistics stuff                        */
+/**************************************************/
+
+static void can_init_stats(int caller){
+
+    memset(&stats, 0, sizeof(stats));
+    stats.jiffies_init  = jiffies;
+    pstats.stats_reset++;
+};
+
+static void can_stat_update(unsigned long data){
+
+    unsigned long j = jiffies; /* snapshot */
+
+    //DBG("af_can: can_stat_update() jiffies = %ld\n", j);
+
+    if (j < stats.jiffies_init) /* jiffies overflow */
+       can_init_stats(2);
+
+    /* stats.rx_frames is the definitively max. statistic value */
+    if (stats.rx_frames > (ULONG_MAX / HZ)) /* prevent overflow in calc_rate() */
+       can_init_stats(3); /* restart */
+
+    if (stats.matches > (ULONG_MAX / 100)) /* matches overflow - very improbable */
+       can_init_stats(4);
+
+    /* 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 */
+    stattimer.expires = jiffies + HZ; /* every second */
+    add_timer(&stattimer);
+};
+
+/**************************************************/
+/* EOF                                            */
+/**************************************************/
diff --git a/kernel/2.6/can/raw.c b/kernel/2.6/can/raw.c
new file mode 100644 (file)
index 0000000..32e9bdf
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * raw.c
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+#include "version.h"
+#include "raw.h"
+
+RCSID("$Id: raw.c,v 2.0 2006/04/13 10:37:19 ethuerm Exp $");
+
+
+#define NAME "RAW sockets for LLCF"
+#define IDENT "raw"
+static __initdata const char banner[] = BANNER(NAME);
+
+MODULE_DESCRIPTION(NAME);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#ifdef DEBUG
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...)       (debug & 1 ? \
+                              (printk(KERN_DEBUG "RAW %s: ", __func__), \
+                               printk(args)) : 0)
+#define DBG_SKB(skb)       (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_SKB(skb)
+#endif
+
+static int raw_release(struct socket *sock);
+static int raw_bind   (struct socket *sock, struct sockaddr *uaddr, int len);
+static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
+                      int *len, int peer);
+static unsigned int raw_poll(struct file *file, struct socket *sock,
+                            poll_table *wait);
+static int raw_setsockopt(struct socket *sock, int level, int optname,
+                         char *optval, int optlen);
+static int raw_getsockopt(struct socket *sock, int level, int optname,
+                         char *optval, int *optlen);
+static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size);
+static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size, int flags);
+static void raw_rcv(struct sk_buff *skb, void *data);
+static void raw_notifier(unsigned long msg, void *data);
+
+static void raw_add_filters(struct net_device *dev, struct sock *sk);
+static void raw_remove_filters(struct net_device *dev, struct sock *sk);
+
+
+static struct proto_ops raw_ops = {
+    .family        = PF_CAN,
+    .release       = raw_release,
+    .bind          = raw_bind,
+    .connect       = sock_no_connect,
+    .socketpair    = sock_no_socketpair,
+    .accept        = sock_no_accept,
+    .getname       = raw_getname,
+    .poll          = raw_poll,
+    .ioctl         = 0,
+    .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,
+};
+
+
+struct raw_opt {
+    int bound;
+    int ifindex;
+    int count;
+    struct can_filter *filter;
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+
+struct raw_sock {
+    struct sock    sk;
+    struct raw_opt opt;
+};
+
+#define canraw_sk(sk) (&((struct raw_sock *)(sk))->opt)
+
+static struct proto raw_proto = {
+    .name     = "CAN_RAW",
+    .owner    = THIS_MODULE,
+    .obj_size = sizeof(struct raw_sock),
+};
+
+static struct can_proto raw_can_proto = {
+    .ops  = &raw_ops,
+    .prot = &raw_proto,
+};
+
+#else
+
+#define canraw_sk(sk) ((struct raw_opt *)(sk)->sk_protinfo)
+
+static struct can_proto raw_can_proto = {
+    .ops      = &raw_ops,
+    .owner    = THIS_MODULE,
+    .obj_size = sizeof(struct raw_opt),
+};
+
+#endif
+
+
+static __init int raw_init(void)
+{
+    printk(banner);
+
+    can_proto_register(CAN_RAW, &raw_can_proto);
+    return 0;
+}
+
+static __exit void raw_exit(void)
+{
+    can_proto_unregister(CAN_RAW);
+}
+
+static int raw_release(struct socket *sock)
+{
+    struct sock *sk = sock->sk;
+    struct net_device *dev = NULL;
+
+    DBG("socket %p, sk %p, refcnt %d\n", sock, sk,
+       atomic_read(&sk->sk_refcnt));
+
+    if (canraw_sk(sk)->bound && canraw_sk(sk)->ifindex)
+       dev = dev_get_by_index(canraw_sk(sk)->ifindex);
+
+    /* remove current filters & unregister */
+    if (canraw_sk(sk)->count > 0) {
+       if (canraw_sk(sk)->bound)
+           raw_remove_filters(dev, sk);
+       kfree(canraw_sk(sk)->filter);
+    } else if (canraw_sk(sk)->bound)
+       can_rx_unregister(dev, 0, 0, raw_rcv, sk);
+
+    if (dev) {
+       can_dev_unregister(dev, raw_notifier, sk);
+       dev_put(dev);
+    }
+
+    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 net_device *dev;
+
+    DBG("socket %p to device %d\n", sock, addr->can_ifindex);
+
+    if (len < sizeof(*addr))
+       return -EINVAL;
+
+    if (canraw_sk(sk)->bound) {
+#if 1
+       return -EOPNOTSUPP;
+#else
+       /* remove current bindings */
+       if (canraw_sk(sk)->ifindex) {
+           dev = dev_get_by_index(canraw_sk(sk)->ifindex);
+           ;  /* remove notifier */
+       } else
+           dev = NULL;
+       if (canraw_sk(sk)->count > 0) {
+           raw_remove_filters(sk);
+       } else {
+           can_rx_unregister(dev, 0, 0, raw_rcv, sk);
+       }
+       if (dev)
+           dev_put(dev);
+       canraw_sk(sk)->bound = 0;
+#endif
+    }
+
+    if (addr->can_ifindex) {
+       dev = dev_get_by_index(addr->can_ifindex);
+       if (!dev) {
+           DBG("could not find device %d\n", addr->can_ifindex);
+           return -ENODEV;
+       }
+       if (!(dev->flags & IFF_UP)) {
+           sk->sk_err = ENETDOWN;
+           sk->sk_error_report(sk);
+           goto out;
+       }
+       can_dev_register(dev, raw_notifier, sk);
+    } else
+       dev = NULL;
+
+    canraw_sk(sk)->ifindex = addr->can_ifindex;
+
+    if (canraw_sk(sk)->count > 0)   /* filters set by setsockopt */
+       raw_add_filters(dev, sk);
+    else
+       can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT);
+
+    canraw_sk(sk)->bound = 1;
+
+ out:
+    if (dev)
+       dev_put(dev);
+
+    return 0;
+}
+
+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;
+
+    if (peer)
+       return -EOPNOTSUPP;
+       
+    addr->can_family  = AF_CAN;
+    addr->can_ifindex = canraw_sk(sk)->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 *optval, int optlen)
+{
+    struct sock *sk = sock->sk;
+    struct can_filter *filter = NULL;
+    struct net_device *dev = NULL;
+    int count = 0;
+    int err;
+
+    if (level != SOL_CAN_RAW)
+       return -EINVAL;
+
+    switch (optname) {
+    case CAN_RAW_FILTER:
+       if (optlen) {
+           if (optlen % sizeof(struct can_filter) != 0)
+               return -EINVAL;
+           if (!(filter = kmalloc(optlen, GFP_KERNEL)))
+               return -ENOMEM;
+           if (err = copy_from_user(filter, optval, optlen)) {
+               kfree(filter);
+               return err;
+           }
+           count = optlen / sizeof(struct can_filter);
+       }
+
+       if (canraw_sk(sk)->bound && canraw_sk(sk)->ifindex)
+           dev = dev_get_by_index(canraw_sk(sk)->ifindex);
+
+       /* remove current filters & unregister */
+       if (canraw_sk(sk)->count > 0) {
+
+           if (canraw_sk(sk)->bound)
+               raw_remove_filters(dev, sk);
+           
+           kfree(canraw_sk(sk)->filter);
+           canraw_sk(sk)->count = 0;
+           canraw_sk(sk)->filter = NULL;
+       } else if (canraw_sk(sk)->bound)
+           can_rx_unregister(dev, 0, 0, raw_rcv, sk);
+
+       /* add new filters & register */
+       if (optlen) {
+           canraw_sk(sk)->filter = filter;
+           canraw_sk(sk)->count  = count;
+           if (canraw_sk(sk)->bound)
+               raw_add_filters(dev, sk);
+       } else if (canraw_sk(sk)->bound)
+           can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT);
+
+       if (dev)
+           dev_put(dev);
+
+       break;
+
+    default:
+       return -ENOPROTOOPT;
+    }
+    return 0;
+}
+
+static int raw_getsockopt(struct socket *sock, int level, int optname,
+                         char *optval, int *optlen)
+{
+    struct sock *sk = sock->sk;
+    struct can_filter *filter = canraw_sk(sk)->filter;
+    int len;
+
+    if (level != SOL_CAN_RAW)
+       return -EINVAL;
+
+    switch (optname) {
+    case CAN_RAW_FILTER:
+       if (get_user(len, optlen))
+           return -EFAULT;
+
+       if (filter) {
+           int filter_size = canraw_sk(sk)->count * sizeof(struct can_filter);
+           if (len < filter_size)
+               return -EINVAL;
+           if (len > filter_size)
+               len = filter_size;
+           if (copy_to_user(optval, filter, len))
+               return -EFAULT;
+       } else
+           len = 0;
+       if (put_user(len, optlen))
+           return -EFAULT;
+       break;
+
+    default:
+       return -ENOPROTOOPT;
+    }
+    return 0;
+}
+
+static void raw_add_filters(struct net_device *dev, struct sock *sk)
+{
+    struct can_filter *filter = canraw_sk(sk)->filter;
+    int i;
+
+    for (i = 0; i < canraw_sk(sk)->count; i++) {
+       can_rx_register(dev, filter[i].can_id, filter[i].can_mask,
+                       raw_rcv, sk, IDENT);
+       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);
+    }
+}
+
+static void raw_remove_filters(struct net_device *dev, struct sock *sk)
+{
+    struct can_filter *filter = canraw_sk(sk)->filter;
+    int i;
+
+    for (i = 0; i < canraw_sk(sk)->count; i++) {
+       can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
+                         raw_rcv, sk);
+       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);
+    }
+}
+
+static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
+                      struct msghdr *msg, size_t size)
+{
+    struct sock *sk = sock->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 = canraw_sk(sk)->ifindex;
+
+    if (!(dev = dev_get_by_index(ifindex))) {
+       DBG("device %d not found\n", ifindex);
+       return -ENXIO;
+    }
+
+    skb = alloc_skb(size, GFP_KERNEL);
+    if ((err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size)) < 0) {
+       kfree_skb(skb);
+       return err;
+    }
+    skb->dev = dev;
+
+    DBG("sending skbuff to interface %d\n", ifindex);
+    DBG_SKB(skb);
+
+    err = can_send(skb);
+
+    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;
+    if (!(skb = skb_recv_datagram(sk, flags, noblock, &error))) {
+       return error;
+    }
+
+    DBG("delivering skbuff %p\n", skb);
+    DBG_SKB(skb);
+
+    if (size < skb->len)
+       msg->msg_flags |= MSG_TRUNC;
+    else
+       size = skb->len;
+    if ((error = memcpy_toiovec(msg->msg_iov, skb->data, size)) < 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 void raw_rcv(struct sk_buff *skb, void *data)
+{
+    struct sock *sk = (struct sock*)data;
+    struct sockaddr_can *addr;
+    int error;
+
+    DBG("received skbuff %p, sk %p\n", skb, sk);
+    DBG_SKB(skb);
+
+    addr = (struct sockaddr_can *)skb->cb;
+    memset(addr, 0, sizeof(*addr));
+    addr->can_family  = AF_CAN;
+    addr->can_ifindex = skb->dev->ifindex;
+
+    if ((error = sock_queue_rcv_skb(sk, skb)) < 0) {
+       DBG("sock_queue_rcv_skb failed: %d\n", error);
+       DBG("freeing skbuff %p\n", skb);
+       kfree_skb(skb);
+    }
+}
+
+static void raw_notifier(unsigned long msg, void *data)
+{
+    struct sock *sk = (struct sock *)data;
+
+    DBG("called for sock %p\n", sk);
+
+    switch (msg) {
+    case NETDEV_UNREGISTER:
+       canraw_sk(sk)->ifindex = 0;
+       /* fallthrough */
+    case NETDEV_DOWN:
+       sk->sk_err = ENETDOWN;
+       sk->sk_error_report(sk);
+       break;
+    }
+}
+
+
+module_init(raw_init);
+module_exit(raw_exit);
diff --git a/kernel/2.6/can/raw.h b/kernel/2.6/can/raw.h
new file mode 100644 (file)
index 0000000..02969e4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * raw.h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef RAW_H
+#define RAW_H
+
+#ifdef __KERNEL__
+#include "version.h"
+RCSID("$Id: raw.h,v 2.0 2006/04/13 10:37:20 ethuerm Exp $");
+#endif
+
+#include "af_can.h"
+
+
+#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)
+#define CAN_RAW_FILTER 1
+
+#endif
diff --git a/kernel/2.6/can/vcan.c b/kernel/2.6/can/vcan.c
new file mode 100644 (file)
index 0000000..79375fe
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * vcan.c
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#include "af_can.h"
+#include "version.h"
+
+RCSID("$Id: vcan.c,v 2.0 2006/04/13 10:37:20 ethuerm Exp $");
+
+
+#define NAME "VCAN loopback interface for LLCF"
+static __initdata const char banner[] = BANNER(NAME);
+
+MODULE_DESCRIPTION(NAME);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#ifdef DEBUG
+static int debug = 0;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...)       (debug & 1 ? \
+                              (printk(KERN_DEBUG "VCAN %s: ", __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
+
+/* This 'undef' makes the vcan a kind of NULL device.  Since LLCF v0.6  */
+/* the local loopback is implemented in af_can.c for all interfaces.    */
+#undef  DO_LOOPBACK
+
+#define NDEVICES 4
+
+static struct net_device *vcan_devs[NDEVICES];
+
+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;
+}
+
+#ifdef DO_LOOPBACK
+
+static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
+{
+    struct net_device_stats *stats = netdev_priv(dev);
+    stats->rx_packets++;
+    stats->rx_bytes += skb->len;
+
+    skb->protocol  = htons(ETH_P_CAN);
+    skb->dev       = dev;
+    skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+    DBG("received skbuff on interface %d\n", dev->ifindex);
+    DBG_SKB(skb);
+
+    netif_rx(skb);
+}
+
+#endif
+
+
+static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
+{
+    struct net_device_stats *stats = netdev_priv(dev);
+
+    DBG("sending skbuff on interface %s\n", dev->name);
+    DBG_SKB(skb);
+    DBG_FRAME("VCAN: transmit CAN frame", (struct can_frame *)skb->data);
+
+#ifdef DO_LOOPBACK
+    if (atomic_read(&skb->users) != 1) {
+       struct sk_buff *old_skb = skb;
+       skb = skb_clone(old_skb, GFP_ATOMIC);
+       DBG("  freeing old skbuff %p, using new skbuff %p\n", old_skb, skb);
+       kfree_skb(old_skb);
+       if (!skb) {
+           return 0;
+       }
+    } else
+       skb_orphan(skb);
+#endif
+
+    stats->tx_packets++;
+    stats->tx_bytes += skb->len;
+#ifdef DO_LOOPBACK
+    vcan_rx(skb, dev);
+#else
+    stats->rx_packets++;
+    stats->rx_bytes += skb->len;
+    kfree_skb(skb);
+#endif
+    return 0;
+}
+
+static int vcan_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    return -EOPNOTSUPP;
+}
+
+static int vcan_rebuild_header(struct sk_buff *skb)
+{
+    DBG("called on skbuff %p\n", skb);
+    DBG_SKB(skb);
+    return 0;
+}
+
+static int vcan_header(struct sk_buff *skb, struct net_device *dev,
+                      unsigned short type, void *daddr, void *saddr,
+                      unsigned int len)
+{
+    DBG("called skbuff %p device %p\n", skb, dev);
+    DBG_SKB(skb);
+    return 0;
+}
+
+
+static struct net_device_stats *vcan_get_stats(struct net_device *dev)
+{
+    struct net_device_stats *stats = netdev_priv(dev);
+    return stats;
+}
+
+static void vcan_init(struct net_device *dev)
+{
+    DBG("dev %s\n", dev->name);
+
+    ether_setup(dev);
+
+    memset(dev->priv, 0, sizeof(struct net_device_stats));
+
+    dev->open              = vcan_open;
+    dev->stop              = vcan_stop;
+    dev->set_config        = NULL;
+    dev->hard_start_xmit   = vcan_tx;
+    dev->do_ioctl          = vcan_ioctl;
+    dev->get_stats         = vcan_get_stats;
+
+    dev->mtu               = sizeof(struct can_frame);
+    dev->flags             = IFF_LOOPBACK;
+    dev->hard_header       = vcan_header;
+    dev->rebuild_header    = vcan_rebuild_header;
+    dev->hard_header_cache = NULL;
+    dev->type              = ARPHRD_LOOPBACK;
+
+    SET_MODULE_OWNER(dev);
+}
+
+static __init int vcan_init_module(void)
+{
+    int i, ndev = 0, result;
+
+    printk(banner);
+
+    for (i = 0; i < NDEVICES; i++) {
+       if (!(vcan_devs[i] = alloc_netdev(sizeof(struct net_device_stats),
+                                         "vcan%d", vcan_init)))
+           printk(KERN_ERR "vcan: error allocating net_device\n");
+       else if (result = register_netdev(vcan_devs[i])) {
+           printk(KERN_ERR "vcan: error %d registering interface %s\n",
+                  result, vcan_devs[i]->name);
+           free_netdev(vcan_devs[i]);
+       } else {
+           DBG("successfully registered interface %s\n", vcan_devs[i]->name);
+           ndev++;
+       }
+    }
+    return ndev ? 0 : -ENODEV;
+}
+
+static __exit void vcan_cleanup_module(void)
+{
+    int i;
+    for (i = 0; i < NDEVICES; i++) {
+       unregister_netdev(vcan_devs[i]);
+       free_netdev(vcan_devs[i]);
+    }
+}
+
+module_init(vcan_init_module);
+module_exit(vcan_cleanup_module);
diff --git a/kernel/2.6/can/version.h b/kernel/2.6/can/version.h
new file mode 100644 (file)
index 0000000..1a6f0cb
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * version.h
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#define RCSID(s) asm(".section .rodata.str1.1,\"aMS\",@progbits,1\n\t" \
+                    ".string \"" s "\"\n\t.previous\n")
+
+RCSID("$Id: version.h,v 2.0 2006/04/13 10:37:20 ethuerm Exp $");
+
+#define MAJORVERSION 1
+#define MINORVERSION 1
+#define PATCHLEVEL   0
+#define EXTRAVERSION ""
+
+#define LLCF_VERSION_CODE (((MAJORVERSION) << 16) + ((MINORVERSION) << 8) \
+                               + (PATCHLEVEL))
+
+/* stringification:  these are the usual macros to stringify with macro
+   expansion.   The str() macro does the expansion, the xstr() macro is
+   for the actual stringification.
+*/
+#define str(arg) xstr(arg)
+#define xstr(arg) #arg
+
+#define VERSION str(MAJORVERSION) "." str(MINORVERSION) "." str(PATCHLEVEL) \
+       EXTRAVERSION
+#define BANNER(name) KERN_INFO name " v" VERSION "\n"
+
+#endif /* VERSION_H */
diff --git a/kernel/2.6/drivers/sja1000/Makefile b/kernel/2.6/drivers/sja1000/Makefile
new file mode 100644 (file)
index 0000000..8663401
--- /dev/null
@@ -0,0 +1,61 @@
+#
+#  $Id: Makefile,v 2.0 2006/04/13 10:37:21 ethuerm Exp $
+#
+#  Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR = /usr/src/linux
+PWD       = $(shell pwd)
+
+default:
+       $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
+
+clean:
+       $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean
+
+else
+
+obj-m := sja1000-isa.o sja1000-gw2.o
+
+sja1000-isa-objs := isa.o        sja1000.o proc.o
+sja1000-gw2-objs := trajet-gw2.o sja1000.o proc.o
+
+endif
diff --git a/kernel/2.6/drivers/sja1000/can.h b/kernel/2.6/drivers/sja1000/can.h
new file mode 120000 (symlink)
index 0000000..d4be190
--- /dev/null
@@ -0,0 +1 @@
+../../can/can.h
\ No newline at end of file
diff --git a/kernel/2.6/drivers/sja1000/can_ioctl.h b/kernel/2.6/drivers/sja1000/can_ioctl.h
new file mode 120000 (symlink)
index 0000000..75904a9
--- /dev/null
@@ -0,0 +1 @@
+../../can/can_ioctl.h
\ No newline at end of file
diff --git a/kernel/2.6/drivers/sja1000/isa.c b/kernel/2.6/drivers/sja1000/isa.c
new file mode 100644 (file)
index 0000000..147c40b
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * $Id: isa.c,v 2.0 2006/04/13 10:37:21 ethuerm Exp $
+ *
+ * isa.c - Philips SJA1000 network device driver for ISA CAN-Cards
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include "can.h"
+#include "can_ioctl.h" /* for struct can_device_stats */
+#include "sja1000.h"
+
+#define MAX_CAN                8
+#define CAN_DEV_NAME   "can%d"
+#define DRV_NAME        "sja1000-isa"
+
+#define DEFAULT_KBIT_PER_SEC 100
+#define SJA1000_HW_CLOCK 16000000
+
+/* driver and version information */
+static const char *drv_name    = DRV_NAME;
+static const char *drv_version = "0.0.10";
+static const char *drv_reldate = "2005-10-11";
+static const char *chip_name   = SJA1000_CHIP_NAME;
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF SJA1000 network device driver '" DRV_NAME "'");
+
+/* module parameters */
+static uint32_t base_addr[MAX_CAN] = { (uint32_t)0x2C0L, (uint32_t)0x320L, 0};
+
+static int irq[MAX_CAN] = { 10, 5, 0 };
+
+static int speed[MAX_CAN] = { DEFAULT_KBIT_PER_SEC, DEFAULT_KBIT_PER_SEC, 0};
+
+static int btr[MAX_CAN] = { 0 };
+static int rx_probe[MAX_CAN] = { 0 };
+
+static int clk = SJA1000_HW_CLOCK;
+static int debug = 0;
+static int restart_ms = 100;
+
+/* array of all can chips */
+static struct net_device       *can_dev[MAX_CAN];
+
+
+/* special functions to access the chips registers */
+static uint8_t reg_read(struct net_device *dev, int reg)
+{
+       return inb(dev->base_addr + reg);
+}
+
+static void reg_write(struct net_device *dev, int reg, uint8_t val)
+{
+       outb(val, dev->base_addr + reg);
+}
+
+MODULE_PARM(base_addr, "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(irq,       "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(speed,     "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(btr,       "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(rx_probe,  "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(clk, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(restart_ms, "i");
+
+static struct net_device* sja1000_isa_probe(uint32_t base, int irq, int speed,
+                                           int btr, int rx_probe, int clk,
+                                           int debug, int restart_ms)
+{
+       struct net_device       *dev;
+       struct can_priv         *priv;
+
+       if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_DEV_NAME,
+                                sja1000_setup))) {
+               printk(KERN_ERR "%s: out of memory\n", chip_name);
+               return NULL;
+       }
+
+       printk(KERN_INFO "%s: base 0x%X / irq %d / speed %d / btr 0x%X / rx_probe %d\n",
+              chip_name, base, irq, speed, btr, rx_probe);
+
+       /* fill net_device structure */
+
+       priv             = netdev_priv(dev);
+
+       dev->irq         = irq;
+       dev->base_addr   = base;
+
+       priv->reg_read   = reg_read;
+       priv->reg_write  = reg_write;
+
+       priv->speed      = speed;
+       priv->btr        = btr;
+       priv->rx_probe   = rx_probe;
+       priv->clock      = clk;
+       priv->restart_ms = restart_ms;
+       priv->debug      = debug;
+
+       if (REG_READ(0) == 0xFF)
+               goto free_dev;
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       /* go into Pelican mode, disable clkout, disable comparator */
+       REG_WRITE(REG_CDR, 0xCF);
+
+       /* output control */
+       /* connected to external transceiver */
+       REG_WRITE(REG_OCR, 0x1A);
+
+       printk(KERN_INFO "%s: %s found at 0x%X, irq is %d\n",
+              dev->name, chip_name, (uint32_t)dev->base_addr, dev->irq);
+
+       if (register_netdev(dev) == 0)
+               return dev;
+
+       printk(KERN_INFO "%s: probing failed\n", chip_name);
+ free_dev:
+       free_netdev(dev);
+       return NULL;
+}
+
+static __exit void sja1000_isa_cleanup_module(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_CAN; i++) {
+               if (can_dev[i] != NULL) {
+                       struct can_priv *priv = netdev_priv(can_dev[i]);
+                       unregister_netdev(can_dev[i]);
+                       del_timer(&priv->timer);
+                       release_region(base_addr[i], SJA1000_IO_SIZE_ISA);
+                       free_netdev(can_dev[i]);
+               }
+       }
+       sja1000_proc_delete(drv_name);
+}
+
+static __init int sja1000_isa_init_module(void)
+{
+       int i;
+       struct net_device *dev;
+
+       if (clk < 1000 ) /* MHz command line value */
+               clk *= 1000000;
+
+       if (clk < 1000000 ) /* kHz command line value */
+               clk *= 1000;
+
+       printk(KERN_INFO "%s - %s driver v%s (%s)\n",
+              chip_name, drv_name, drv_version, drv_reldate);
+       printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms] [debug %d]\n",
+              chip_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+       for (i = 0; base_addr[i]; i++) {
+               printk(KERN_DEBUG "%s: checking for %s on address 0x%X ...\n",
+                      chip_name, chip_name, base_addr[i]);
+               if (!request_region(base_addr[i], SJA1000_IO_SIZE_ISA, chip_name)) {
+                       printk(KERN_ERR "%s: memory already in use\n", chip_name);
+                       sja1000_isa_cleanup_module();
+                       return -EBUSY;
+               }
+               dev = sja1000_isa_probe(base_addr[i], irq[i], speed[i], btr[i], rx_probe[i], clk, debug, restart_ms);
+
+               if (dev != NULL) {
+                       can_dev[i] = dev;
+                       sja1000_proc_init(drv_name, can_dev, MAX_CAN);
+               } else {
+                       can_dev[i] = NULL;
+                       release_region(base_addr[i], SJA1000_IO_SIZE_ISA);
+               }
+       }
+       return 0;
+}
+
+module_init(sja1000_isa_init_module);
+module_exit(sja1000_isa_cleanup_module);
diff --git a/kernel/2.6/drivers/sja1000/proc.c b/kernel/2.6/drivers/sja1000/proc.c
new file mode 100644 (file)
index 0000000..2e5b767
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * $Id: proc.c,v 2.0 2006/04/13 10:37:21 ethuerm Exp $
+ *
+ * proc.c -  proc file system functions for SJA1000 CAN driver.
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include "can.h"
+#include "can_ioctl.h"
+#include "sja1000.h"
+
+static struct proc_dir_entry *pde       = NULL;
+static struct proc_dir_entry *pde_regs  = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static struct net_device **can_dev;
+static int max_devices;
+
+static int sja1000_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev;
+       int i;
+       struct can_priv *priv;
+       unsigned char stat;
+
+       len += snprintf(page + len, PAGE_SIZE - len, "CAN bus device statistics:\n");
+       len += snprintf(page + len, PAGE_SIZE - len, "       errwarn  overrun   wakeup   buserr   errpass  arbitr   restarts clock        baud\n");
+       for (i = 0; (i < max_devices) && (len < PAGE_SIZE - 200); i++) {
+               if (can_dev[i]) {
+                       dev = can_dev[i];
+                       stat = REG_READ(REG_SR);
+                       priv = netdev_priv(can_dev[i]);
+                       len += snprintf(page + len, PAGE_SIZE - len, "can%d: %8d %8d %8d %8d %8d %8d %8d %10d %8d\n", i,
+                                       priv->can_stats.error_warning,
+                                       priv->can_stats.data_overrun,
+                                       priv->can_stats.wakeup,
+                                       priv->can_stats.bus_error,
+                                       priv->can_stats.error_passive,
+                                       priv->can_stats.arbitration_lost,
+                                       priv->can_stats.restarts,
+                                       priv->clock,
+                                       priv->speed
+                                       );
+                       if (stat & 0x80) {
+                               len += snprintf(page + len, PAGE_SIZE - len, "can%d: bus status: BUS OFF, ", i);
+                       } else if (stat & 0x40) {
+                               len += snprintf(page + len, PAGE_SIZE - len, "can%d: bus status: ERROR PASSIVE, ", i);
+                       } else {
+                               len += snprintf(page + len, PAGE_SIZE - len, "can%d: bus status: OK, ", i);
+                       }
+                       len += snprintf(page + len, PAGE_SIZE - len, "RXERR: %d, TXERR: %d\n", REG_READ(REG_RXERR), REG_READ(REG_TXERR));
+               }
+       }
+
+       *eof = 1;
+       return len;
+}
+
+static int sja1000_proc_read_regs(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev;
+       int i;
+       struct can_priv   *priv;
+
+       len = sprintf(page, "SJA1000 registers:\n");
+       for (i = 0; (i < max_devices) && (len < PAGE_SIZE - 200); i++) {
+               if (can_dev[i]) {
+                       dev = can_dev[i];
+                       len += snprintf(page + len, PAGE_SIZE - len, "can%d SJA1000 registers:\n", i);
+
+                       priv = netdev_priv(can_dev[i]);
+                       len += snprintf(page + len, PAGE_SIZE - len, "00: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                                      REG_READ(0x00),
+                                      REG_READ(0x01),
+                                      REG_READ(0x02),
+                                      REG_READ(0x03),
+                                      REG_READ(0x04),
+                                      REG_READ(0x05),
+                                      REG_READ(0x06),
+                                      REG_READ(0x07),
+                                      REG_READ(0x08),
+                                      REG_READ(0x09),
+                                      REG_READ(0x0a),
+                                      REG_READ(0x0b),
+                                      REG_READ(0x0c),
+                                      REG_READ(0x0d),
+                                      REG_READ(0x0e),
+                                      REG_READ(0x0f)
+                                      );
+                       len += snprintf(page + len, PAGE_SIZE - len, "10: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                                      REG_READ(0x10),
+                                      REG_READ(0x11),
+                                      REG_READ(0x12),
+                                      REG_READ(0x13),
+                                      REG_READ(0x14),
+                                      REG_READ(0x15),
+                                      REG_READ(0x16),
+                                      REG_READ(0x17),
+                                      REG_READ(0x18),
+                                      REG_READ(0x19),
+                                      REG_READ(0x1a),
+                                      REG_READ(0x1b),
+                                      REG_READ(0x1c),
+                                      REG_READ(0x1d),
+                                      REG_READ(0x1e),
+                                      REG_READ(0x1f)
+                                      );
+               }
+       }
+
+       *eof = 1;
+       return len;
+}
+
+static int sja1000_proc_read_reset(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev;
+       int i;
+       struct can_priv   *priv;
+
+       len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+       for (i = 0; (i < max_devices) && (len < PAGE_SIZE - 200); i++) {
+               if (can_dev[i]) {
+                       dev = can_dev[i];
+                       priv = netdev_priv(can_dev[i]);
+                       if ((priv->state != STATE_UNINITIALIZED) && (priv->state != STATE_RESET_MODE)) {
+                               len += snprintf(page + len, PAGE_SIZE - len, "%s ", dev->name);
+                                dev->stop(dev);
+                                dev->open(dev);
+                               /* count number of restarts */
+                               priv->can_stats.restarts++;
+
+                       } else {
+                         len += snprintf(page + len, PAGE_SIZE - len, "(%s|%d) ", dev->name, priv->state);
+                       }
+               }
+       }
+
+       len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+       *eof = 1;
+       return len;
+}
+
+void sja1000_proc_init(const char *drv_name, struct net_device **dev, int max)
+{
+       char fname[256];
+
+       can_dev     = dev;
+       max_devices = max;
+
+       if (pde == NULL) {
+               sprintf(fname, PROCBASE "/%s", drv_name);
+               pde = create_proc_read_entry(fname, 0644, NULL,
+                                            sja1000_proc_read, NULL);
+       }
+       if (pde_regs == NULL) {
+               sprintf(fname, PROCBASE "/%s_regs", drv_name);
+               pde_regs = create_proc_read_entry(fname, 0644, NULL,
+                                            sja1000_proc_read_regs, NULL);
+       }
+       if (pde_reset == NULL) {
+               sprintf(fname, PROCBASE "/%s_reset", drv_name);
+               pde_reset = create_proc_read_entry(fname, 0644, NULL,
+                                            sja1000_proc_read_reset, NULL);
+       }
+}
+
+void sja1000_proc_delete(const char *drv_name)
+{
+       char fname[256];
+
+       if (pde) {
+               sprintf(fname, PROCBASE "/%s", drv_name);
+               remove_proc_entry(fname, NULL);
+       }
+       if (pde_regs) {
+               sprintf(fname, PROCBASE "/%s_regs", drv_name);
+               remove_proc_entry(fname, NULL);
+       }
+       if (pde_reset) {
+               sprintf(fname, PROCBASE "/%s_reset", drv_name);
+               remove_proc_entry(fname, NULL);
+       }
+}
diff --git a/kernel/2.6/drivers/sja1000/sja1000.c b/kernel/2.6/drivers/sja1000/sja1000.c
new file mode 100644 (file)
index 0000000..6906370
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * $Id: sja1000.c,v 2.0 2006/04/13 10:37:21 ethuerm Exp $
+ *
+ * sja1000.c -  Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include "can.h"
+#include "can_ioctl.h"
+#include "sja1000.h"
+
+#define DEBUG
+
+#ifdef DEBUG
+#define DBG(args...)   ((priv->debug > 0) ? printk(args) : 0)
+#define iDBG(args...)  ((priv->debug > 1) ? printk(args) : 0)  /* logging in interrupt context */
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)  /* logging in interrupt context */
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+
+static const char *chip_name   = SJA1000_CHIP_NAME;
+
+static const char *ecc_errors[] = {
+       NULL,
+       NULL,
+       "ID.28 to ID.28",
+       "start of frame",
+       "bit SRTR",
+       "bit IDE",
+       "ID.20 to ID.18",
+       "ID.17 to ID.13",
+       "CRC sequence",
+       "reserved bit 0",
+       "data field",
+       "data length code",
+       "bit RTR",
+       "reserved bit 1",
+       "ID.4 to ID.0",
+       "ID.12 to ID.5",
+       NULL,
+       "active error flag",
+       "intermission",
+       "tolerate dominant bits",
+       NULL,
+       NULL,
+       "passive error flag",
+       "error delimiter",
+       "CRC delimiter",
+       "acknowledge slot",
+       "end of frame",
+       "acknowledge delimiter",
+       "overload flag",
+       NULL,
+       NULL,
+       NULL
+};
+
+static const char *ecc_types[] = {
+       "bit error",
+       "form error",
+       "stuff error",
+       "other type of error"
+};
+
+/* declarations */
+
+static void can_restart_dev(unsigned long data);
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       
+       if (priv->state == STATE_UNINITIALIZED) /* no bla bla when restarting the device */
+               printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+                      dev->name, btr0, btr1);
+
+       REG_WRITE(REG_BTR0, btr0);
+       REG_WRITE(REG_BTR1, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       int error;
+       int brp;
+       int tseg;
+       int tseg1 = 0;
+       int tseg2 = 0;
+
+       int best_error = 1000000000;
+       int best_tseg = 0;
+       int best_brp = 0;
+       int best_baud = 0;
+
+       int SAM = (baud > 100000 ? 0 : 1);
+
+       clock >>= 1;
+
+       for (tseg = (0 + 0 + 2) * 2; tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1; tseg++) {
+               brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+               if ((brp > 0) && (brp <= 64)) {
+                       error = baud - clock / (brp * (1 + tseg / 2));
+                       if (error < 0) {
+                               error = -error;
+                       }
+                       if (error <= best_error) {
+                               best_error = error;
+                               best_tseg = tseg / 2;
+                               best_brp = brp - 1;
+                               best_baud = clock / (brp * (1 + tseg / 2));
+                       }
+               }
+       }
+       if (best_error && (baud / best_error < 10)) {
+               printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+                      dev->name, baud, clock * 2);
+               return;
+//             return -EINVAL;
+       }
+       tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+       if (tseg2 < 0) {
+               tseg2 = 0;
+       } else if (tseg2 > MAX_TSEG2) {
+               tseg2 = MAX_TSEG2;
+       }
+       tseg1 = best_tseg - tseg2 - 2;
+       if (tseg1 > MAX_TSEG1) {
+               tseg1 = MAX_TSEG1;
+               tseg2 = best_tseg - tseg1 - 2;
+       }
+
+       priv->btr = ((best_brp | JUMPWIDTH)<<8) + ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+       printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+              dev->name, best_baud, priv->btr);
+
+       set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+//     set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       unsigned char status = REG_READ(REG_MOD);
+       int i;
+
+       priv->can_stats.bus_error_at_init = priv->can_stats.bus_error;
+
+       /* disable interrupts */
+       REG_WRITE(REG_IER, IRQ_OFF);
+
+       for (i = 0; i < 10; i++) {
+               /* check reset bit */
+               if (status & MOD_RM) {
+                       if (i > 1) {
+                               iDBG(KERN_INFO "%s: %s looped %d times\n",
+                                       dev->name, __FUNCTION__, i);
+                       }
+                       priv->state = STATE_RESET_MODE;
+                       return 0;
+               }
+
+               REG_WRITE(REG_MOD, MOD_RM); /* reset chip */
+               status = REG_READ(REG_MOD);
+
+       }
+
+       printk(KERN_ERR "%s: setting sja1000 into reset mode failed!\n", dev->name);
+       return 1;
+
+}
+       
+static int set_normal_mode(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       unsigned char status = REG_READ(REG_MOD);
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               /* check reset bit */
+               if ((status & MOD_RM) == 0) {
+                       if (i > 1) {
+                               iDBG(KERN_INFO "%s: %s looped %d times\n",
+                                       dev->name, __FUNCTION__, i);
+                       }
+                       return 0;
+               }
+
+               REG_WRITE(REG_MOD, 0x00); /* set chip to normal mode */
+               status = REG_READ(REG_MOD);
+       }
+
+       printk(KERN_ERR "%s: setting sja1000 into normal mode failed!\n", dev->name);
+       return 1;
+
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       unsigned char status = REG_READ(REG_MOD);
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               /* check reset mode bit */
+               if ((status & MOD_RM) == 0) {
+                       if (i > 1) {
+                               iDBG(KERN_INFO "%s: %s looped %d times\n",
+                                       dev->name, __FUNCTION__, i);
+                       }
+                       return 0;
+               }
+
+               /* set listen only mode, clear reset */
+               REG_WRITE(REG_MOD, MOD_LOM);
+               status = REG_READ(REG_MOD);
+       }
+
+       printk(KERN_ERR "%s: setting sja1000 into listen mode failed!\n", dev->name);
+       return 1;
+
+}
+
+/*
+ * initialize SJA1000 chip:
+ *   - reset chip
+ *   - set output mode
+ *   - set baudrate
+ *   - enable interrupts
+ *   - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* go into Pelican mode, disable clkout, disable comparator */
+       REG_WRITE(REG_CDR, 0xCF);
+
+       /* set acceptance filter (accept all) */
+       REG_WRITE(REG_ACCC0, 0x00);
+       REG_WRITE(REG_ACCC1, 0x00);
+       REG_WRITE(REG_ACCC2, 0x00);
+       REG_WRITE(REG_ACCC3, 0x00);
+
+       REG_WRITE(REG_ACCM0, 0xFF);
+       REG_WRITE(REG_ACCM1, 0xFF);
+       REG_WRITE(REG_ACCM2, 0xFF);
+       REG_WRITE(REG_ACCM3, 0xFF);
+
+       /* set baudrate */
+       if (priv->btr) { /* no calculation when btr is provided */
+               set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+       } else {
+               if (priv->speed == 0) {
+                       priv->speed = DEFAULT_SPEED;
+               }
+               set_baud(dev, priv->speed * 1000, priv->clock);
+       }
+
+       /* output control */
+       REG_WRITE(REG_OCR, 0x1A);       /* connected to external transceiver */
+
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (priv->rx_probe)
+               chipset_init_rx(dev); /* wait for valid reception first */
+       else
+               chipset_init_trx(dev);
+
+       if ((wake) && netif_queue_stopped(dev))
+               netif_wake_queue(dev);
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       /* set registers */
+       chipset_init_regs(dev);
+
+       /* automatic bit rate detection */
+       set_listen_mode(dev);
+
+       priv->state = STATE_PROBE;
+
+       /* enable receive and error interrupts */
+       REG_WRITE(REG_IER, IRQ_RI | IRQ_EI);
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       /* set registers */
+       chipset_init_regs(dev);
+
+       /* leave reset mode */
+       set_normal_mode(dev);
+
+       priv->state = STATE_ACTIVE;
+
+       /* enable all interrupts */
+       REG_WRITE(REG_IER, IRQ_ALL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx  ff      ll   00 11 22 33 44 55 66 77
+ * [  can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct can_priv  *priv = netdev_priv(dev);
+       struct can_frame *cf   = (struct can_frame*)skb->data;
+       uint8_t fi;
+       uint8_t dlc;
+       canid_t id;
+       uint8_t dreg;
+       int     i;
+
+       netif_stop_queue(dev);
+
+       fi = dlc = cf->can_dlc;
+       id = cf->can_id;
+
+       if (id & CAN_RTR_FLAG)
+               fi |= FI_RTR;
+
+       if (id & CAN_EFF_FLAG) {
+               fi |= FI_FF;
+               dreg = EFF_BUF;
+               REG_WRITE(REG_FI, fi);
+               REG_WRITE(REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+               REG_WRITE(REG_ID2, (id & 0x001fe000) >> (5 + 8));
+               REG_WRITE(REG_ID3, (id & 0x00001fe0) >> 5);
+               REG_WRITE(REG_ID4, (id & 0x0000001f) << 3);
+       } else {
+               dreg = SFF_BUF;
+               REG_WRITE(REG_FI, fi);
+               REG_WRITE(REG_ID1, (id & 0x000007f8) >> 3);
+               REG_WRITE(REG_ID2, (id & 0x00000007) << 5);
+       }
+
+       for (i = 0; i < dlc; i++) {
+               REG_WRITE(dreg++, cf->data[i]);
+       }
+
+       REG_WRITE(REG_CMR, CMD_TR);
+
+       priv->stats.tx_bytes += dlc;
+
+       dev->trans_start = jiffies;
+
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       priv->stats.tx_errors++;
+
+       /* do not conflict with e.g. bus error handling */
+       if (!(priv->timer.expires)){ /* no restart on the run */
+               chipset_init_trx(dev); /* no tx queue wakeup */
+               netif_wake_queue(dev); /* wakeup here */
+       }
+       else
+               DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+                   dev->name, __FUNCTION__ );
+
+}
+
+static void can_restart_on(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (!(priv->timer.expires)){ /* no restart on the run */
+
+               set_reset_mode(dev);
+
+               priv->timer.function = can_restart_dev;
+               priv->timer.data = (unsigned long) dev;
+
+               /* restart chip on persistent error in <xxx> ms */
+               priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+               add_timer(&priv->timer);
+
+               iDBG(KERN_INFO "%s: %s start (%ld)\n",
+                    dev->name, __FUNCTION__ , jiffies);
+       } else
+               iDBG(KERN_INFO "%s: %s already (%ld)\n",
+                    dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+       struct net_device *dev = (struct net_device*) data;
+       struct can_priv *priv = netdev_priv(dev);
+
+       DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+             dev->name, jiffies);
+
+       /* mark inactive timer */
+       priv->timer.expires = 0;
+
+       if (priv->state != STATE_UNINITIALIZED) {
+
+               /* count number of restarts */
+               priv->can_stats.restarts++;
+       
+               chipset_init(dev, 1);
+       }
+}
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (priv->state != STATE_UNINITIALIZED) {
+
+               /* count number of restarts */
+               priv->can_stats.restarts++;
+       
+               chipset_init(dev, 1);
+       }
+}
+#endif
+
+static void can_rx(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       struct can_frame *cf;
+       struct sk_buff  *skb;
+       uint8_t fi;
+       uint8_t dreg;
+       canid_t id;
+       uint8_t dlc;
+       int     i;
+
+       skb = dev_alloc_skb(sizeof(struct can_frame));
+       if (skb == NULL) {
+               return;
+       }
+       skb->dev = dev;
+       skb->protocol = htons(ETH_P_CAN);
+
+       fi = REG_READ(REG_FI);
+       dlc = fi & 0x0F;
+
+       if (fi & FI_FF) {
+               /* extended frame format (EFF) */
+               dreg = EFF_BUF;
+               id = (REG_READ(REG_ID1) << (5+16))
+                       | (REG_READ(REG_ID2) << (5+8))
+                       | (REG_READ(REG_ID3) << 5)
+                       | (REG_READ(REG_ID4) >> 3);
+               id |= CAN_EFF_FLAG;
+       } else {
+               /* standard frame format (SFF) */
+               dreg = SFF_BUF;
+               id = (REG_READ(REG_ID1) << 3) | (REG_READ(REG_ID2) >> 5);
+       }
+
+       if (fi & FI_RTR)
+               id |= CAN_RTR_FLAG;
+
+       cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+       cf->can_id    = id;
+       cf->can_dlc   = dlc;
+       for (i = 0; i < dlc; i++) {
+               cf->data[i] = REG_READ(dreg++);
+       }
+       while (i < 8)
+               cf->data[i++] = 0;
+
+       /* release receive buffer */
+       REG_WRITE(REG_CMR, CMD_RRB);
+
+       netif_rx(skb);
+
+       dev->last_rx = jiffies;
+       priv->stats.rx_packets++;
+       priv->stats.rx_bytes += dlc;
+}
+
+static struct net_device_stats *can_get_stats(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* TODO: read statistics from chip */
+       return &priv->stats;
+}
+
+static int can_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       switch (cmd) {
+       case SIOCSRATE:
+               ;
+               return 0;
+       case SIOCGRATE:
+               ;
+               return 0;
+       }
+       return 0;
+}
+
+/*
+ * SJA1000 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device*)dev_id;
+       struct can_priv *priv = netdev_priv(dev);
+       uint8_t isrc, status, ecc, alc;
+       int n = 0;
+       
+       if (priv->state == STATE_UNINITIALIZED) {
+               printk(KERN_ERR "%s: %s: uninitialized controller!\n", dev->name, __FUNCTION__);
+               chipset_init(dev, 1); /* this should be possible at this stage */
+               return IRQ_NONE;
+       }
+
+       if (priv->state == STATE_RESET_MODE) {
+               iiDBG(KERN_ERR "%s: %s: controller is in reset mode! MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+                     dev->name, __FUNCTION__, REG_READ(REG_MOD), REG_READ(REG_IER), REG_READ(REG_IR), REG_READ(REG_SR));
+               return IRQ_NONE;
+       }
+
+       while ((isrc = REG_READ(REG_IR)) && (n < 20)) {
+               n++;
+               status = REG_READ(REG_SR);
+
+               if (isrc & IRQ_WUI) {
+                       /* wake-up interrupt */
+                       priv->can_stats.wakeup++;
+               }
+               if (isrc & IRQ_TI) {
+                       /* transmission complete interrupt */
+                       priv->stats.tx_packets++;
+                       netif_wake_queue(dev);
+               }
+               if (isrc & IRQ_RI) {
+                       /* receive interrupt */
+
+                       while (status & SR_RBS) {
+                               can_rx(dev);
+                               status = REG_READ(REG_SR);
+                       }
+                       if (priv->state == STATE_PROBE) { /* valid RX -> switch to trx-mode */
+                               iDBG(KERN_INFO "%s: RI #%d#\n", dev->name, n);
+                               chipset_init_trx(dev); /* no tx queue wakeup */
+                               break; /* check again after initializing the controller */
+                       }
+               }
+               if (isrc & IRQ_DOI) {
+                       /* data overrun interrupt */
+                       iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X status=0x%02X\n",
+                                       dev->name, isrc, status);
+                       iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+                       priv->can_stats.data_overrun++;
+                       REG_WRITE(REG_CMR, CMD_CDO); /* clear bit */
+               }
+               if (isrc & IRQ_EI) {
+                       /* error warning interrupt */
+                       iiDBG(KERN_INFO "%s: error warning isrc=0x%02X status=0x%02X\n",
+                                       dev->name, isrc, status);
+                       iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+                       priv->can_stats.error_warning++;
+                       if (status & SR_BS) {
+                               printk(KERN_INFO "%s: BUS OFF, restarting device\n", dev->name);
+                               can_restart_on(dev);
+                               return IRQ_HANDLED; /* controller has been restarted, so we leave here */
+                       } else if (status & SR_ES) {
+                               iDBG(KERN_INFO "%s: error\n", dev->name);
+                       }
+               }
+               if (isrc & IRQ_BEI) {
+                       /* bus error interrupt */
+                       iiDBG(KERN_INFO "%s: bus error isrc=0x%02X status=0x%02X\n",
+                                       dev->name, isrc, status);
+                       iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+                            priv->can_stats.bus_error - priv->can_stats.bus_error_at_init);
+                       priv->can_stats.bus_error++;
+                       ecc = REG_READ(REG_ECC);
+                       iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+                            dev->name, ecc,
+                            (ecc & ECC_DIR) ? "RX" : "TX",
+                            ecc_types[ecc >> ECC_ERR],
+                            ecc_errors[ecc & ECC_SEG]);
+                       
+                       /* when the bus errors flood the system, restart the controller */
+                       if (priv->can_stats.bus_error_at_init + MAX_BUS_ERRORS < priv->can_stats.bus_error) {
+                               iDBG(KERN_INFO "%s: heavy bus errors, restarting device\n", dev->name);
+                               can_restart_on(dev);
+                               return IRQ_HANDLED; /* controller has been restarted, so we leave here */
+                       }
+#if 1
+                       /* don't know, if this is a good idea, but it works fine ... */
+                       if (REG_READ(REG_RXERR) > 128) {
+                               iDBG(KERN_INFO "%s: RX_ERR > 128, restarting device\n", dev->name);
+                               can_restart_on(dev);
+                               return IRQ_HANDLED; /* controller has been restarted, so we leave here */
+                       }
+#endif
+               }
+               if (isrc & IRQ_EPI) {
+                       /* error passive interrupt */
+                       iiDBG(KERN_INFO "%s: error passive isrc=0x%02X status=0x%02X\n",
+                                       dev->name, isrc, status);
+                       iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+                       priv->can_stats.error_passive++;
+                       if (status & SR_ES) {
+                               iDBG(KERN_INFO "%s: -> ERROR PASSIVE, restarting device\n", dev->name);
+                               can_restart_on(dev);
+                               return IRQ_HANDLED; /* controller has been restarted, so we leave here */
+                       } else {
+                               iDBG(KERN_INFO "%s: -> ERROR ACTIVE\n", dev->name);
+                       }
+               }
+               if (isrc & IRQ_ALI) {
+                       /* arbitration lost interrupt */
+                       iiDBG(KERN_INFO "%s: error arbitration lost isrc=0x%02X status=0x%02X\n",
+                                       dev->name, isrc, status);
+                       iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+                       priv->can_stats.arbitration_lost++;
+                       alc = REG_READ(REG_ALC);
+                       iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+               }
+       }
+       if (n > 1) {
+               iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+       }
+
+       return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       priv->state = STATE_UNINITIALIZED;
+
+       /* register interrupt handler */
+       if (request_irq(dev->irq, &can_interrupt, SA_SHIRQ,
+                       dev->name, (void*)dev)) {
+               return -EAGAIN;
+       }
+
+       /* clear statistics */
+       memset(&priv->stats, 0, sizeof(priv->stats));
+
+       /* init chip */
+       chipset_init(dev, 0);
+       priv->open_time = jiffies;
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       priv->open_time = 0;
+
+       if (priv->timer.expires) {
+               del_timer(&priv->timer);
+               priv->timer.expires = 0;
+       }
+
+       free_irq(dev->irq, (void*)dev);
+       priv->state = STATE_UNINITIALIZED;
+
+       netif_stop_queue(dev);
+
+       return 0;
+}
+
+#if 0
+static uint8_t reg_read(struct net_device *dev, int reg)
+{
+       return readb(dev->base_addr + reg);
+}
+
+static void reg_write(struct net_device *dev, int reg, uint8_t val)
+{
+       writeb(val, dev->base_addr + reg);
+}
+
+static void test_if(struct net_device *dev)
+{
+       int i;
+       int j;
+       int x;
+
+       REG_WRITE(REG_CDR, 0xCF);
+       for (i = 0; i < 10000; i++) {
+               for (j = 0; j < 256; j++) {
+                       REG_WRITE(REG_EWL, j);
+                       x = REG_READ(REG_EWL);
+                       if (x != j) {
+                               printk(KERN_INFO "%s: is: %02X expected: %02X (%d)\n", dev->name, x, j, i);
+                       }
+               }
+       }
+}
+#endif
+
+void sja1000_setup(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* Fill in the the fields of the device structure
+          with CAN/LLCF generic values */
+
+       dev->change_mtu                 = NULL;
+       dev->hard_header                = NULL;
+       dev->rebuild_header             = NULL;
+       dev->set_mac_address            = NULL;
+       dev->hard_header_cache          = NULL;
+       dev->header_cache_update        = NULL;
+       dev->hard_header_parse          = NULL;
+
+       //      dev->type                       = ARPHRD_CAN;
+       dev->hard_header_len            = 0;
+       dev->mtu                        = sizeof(struct can_frame);
+       dev->addr_len                   = 0;
+       dev->tx_queue_len               = 10;
+
+       dev->flags                      = IFF_NOARP;
+
+       dev->open               = can_open;
+       dev->stop               = can_close;
+       dev->hard_start_xmit    = can_start_xmit;
+       dev->get_stats          = can_get_stats;
+       dev->do_ioctl           = can_ioctl;
+
+       dev->tx_timeout         = can_tx_timeout;
+       dev->watchdog_timeo     = TX_TIMEOUT;
+
+       init_timer(&priv->timer);
+       priv->timer.expires = 0;
+
+       //      SET_MODULE_OWNER(dev);
+}
diff --git a/kernel/2.6/drivers/sja1000/sja1000.h b/kernel/2.6/drivers/sja1000/sja1000.h
new file mode 100644 (file)
index 0000000..f2efb50
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * $Id: sja1000.h,v 2.0 2006/04/13 10:37:21 ethuerm Exp $
+ *
+ * sja1000.h -  Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#ifndef __SJA1000_H__
+#define __SJA1000_H__
+
+#define SJA1000_IO_SIZE_BASIC   0x20
+#define SJA1000_IO_SIZE_PELICAN 0x80
+
+#define SJA1000_IO_SIZE_ISA     0x20
+
+#define DEFAULT_SPEED  100 /* kBit/s */
+
+#define TX_TIMEOUT     (HZ/20) /* 50ms */ 
+#define RESTART_MS      100    /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS  200     /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD                0x00
+#define REG_CMR                0x01
+#define REG_SR         0x02
+#define REG_IR         0x03
+#define REG_IER                0x04
+#define REG_ALC                0x0B
+#define REG_ECC                0x0C
+#define REG_EWL                0x0D
+#define REG_RXERR      0x0E
+#define REG_TXERR      0x0F
+#define REG_ACCC0      0x10
+#define REG_ACCC1      0x11
+#define REG_ACCC2      0x12
+#define REG_ACCC3      0x13
+#define REG_ACCM0      0x14
+#define REG_ACCM1      0x15
+#define REG_ACCM2      0x16
+#define REG_ACCM3      0x17
+#define REG_RMC                0x1D
+#define REG_RBSA       0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0       0x06
+#define REG_BTR1       0x07
+#define REG_OCR                0x08
+#define REG_CDR                0x1F
+
+#define REG_FI         0x10
+#define SFF_BUF                0x13
+#define EFF_BUF                0x15
+
+#define FI_FF          0x80
+#define FI_RTR         0x40
+
+#define REG_ID1                0x11
+#define REG_ID2                0x12
+#define REG_ID3                0x13
+#define REG_ID4                0x14
+
+#define CAN_RAM                0x20
+
+/* mode register */
+#define MOD_RM         0x01
+#define MOD_LOM                0x02
+#define MOD_STM                0x04
+#define MOD_AFM                0x08
+#define MOD_SM         0x10
+
+/* commands */
+#define CMD_SRR                0x10
+#define CMD_CDO                0x08
+#define CMD_RRB                0x04
+#define CMD_AT         0x02
+#define CMD_TR         0x01
+
+/* interrupt sources */
+#define IRQ_BEI                0x80
+#define IRQ_ALI                0x40
+#define IRQ_EPI                0x20
+#define IRQ_WUI                0x10
+#define IRQ_DOI                0x08
+#define IRQ_EI         0x04
+#define IRQ_TI         0x02
+#define IRQ_RI         0x01
+#define IRQ_ALL                0xFF
+#define IRQ_OFF                0x00
+
+/* status register content */
+#define SR_BS          0x80
+#define SR_ES          0x40
+#define SR_TS          0x20
+#define SR_RS          0x10
+#define SR_TCS         0x08
+#define SR_TBS         0x04
+#define SR_DOS         0x02
+#define SR_RBS         0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_DIR                0x20
+#define ECC_SEG                0x1F
+#define ECC_ERR                6
+
+/* bus timing */
+#define MAX_TSEG1      15
+#define MAX_TSEG2       7
+#define SAMPLE_POINT   75
+#define JUMPWIDTH     0x40
+
+
+#define REG_READ(addr)         ((struct can_priv*)netdev_priv(dev))->reg_read(dev,addr)
+#define REG_WRITE(addr,data)   ((struct can_priv*)netdev_priv(dev))->reg_write(dev,addr,data)
+
+#define SJA1000_CHIP_NAME "sja1000"
+#define PROCBASE          "net/drivers" /* /proc/ ... */
+
+/*
+ * private data structure:
+ * reg_read and reg_write are functions to access the sja1000 registers
+ */
+struct can_priv {
+       struct net_device_stats stats;
+       long                    open_time;
+       int                     clock;
+       int                     restart_ms;
+       int                     debug;
+       int                     speed;
+       int                     btr;
+       int                     rx_probe;
+        struct timer_list       timer;
+       uint8_t                 (*reg_read)(struct net_device *dev, int reg);
+       void                    (*reg_write)(struct net_device *dev, int reg, uint8_t val);
+       struct can_device_stats can_stats;
+       int                     state;
+};
+
+#define STATE_UNINITIALIZED    0
+#define STATE_PROBE            1
+#define STATE_ACTIVE           2
+#define STATE_ERROR_ACTIVE     3
+#define STATE_ERROR_PASSIVE    4
+#define STATE_BUS_OFF          5
+#define STATE_RESET_MODE       6
+
+void sja1000_setup(struct net_device *dev);
+void sja1000_proc_init(const char *drv_name, struct net_device **dev, int max);
+void sja1000_proc_delete(const char *drv_name);
+
+#endif /* __SJA1000_H__ */
diff --git a/kernel/2.6/drivers/sja1000/trajet-gw2.c b/kernel/2.6/drivers/sja1000/trajet-gw2.c
new file mode 100644 (file)
index 0000000..51c8d2a
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * $Id: trajet-gw2.c,v 2.0 2006/04/13 10:37:22 ethuerm Exp $
+ *
+ * trajet-gw2.c - Philips SJA1000 network device driver for TRAJET.GW2
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2005 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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include "can.h"
+#include "can_ioctl.h" /* for struct can_device_stats */
+#include "sja1000.h"
+
+#define MAX_CAN                8
+#define CAN_DEV_NAME   "can%d"
+#define DRV_NAME        "sja1000-gw2"
+
+#define DEFAULT_KBIT_PER_SEC 500
+#define SJA1000_HW_CLOCK 20000000
+#define ADDR_GAP       1
+#define RSIZE          (SJA1000_IO_SIZE_PELICAN * (ADDR_GAP + 1))
+
+/* driver and version information */
+static const char *drv_name    = DRV_NAME;
+static const char *drv_version = "0.0.11";
+static const char *drv_reldate = "2005-10-11";
+static const char *chip_name   = SJA1000_CHIP_NAME;
+
+MODULE_AUTHOR("Matthias Brukner <M.Brukner@trajet.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF SJA1000 network device driver '" DRV_NAME "'");
+
+/* module parameters */
+static uint32_t base_addr[MAX_CAN] = {
+       (uint32_t)0xf0100200L,
+       (uint32_t)0xf0100300L,
+       (uint32_t)0xf0100400L,
+       (uint32_t)0xf0100500L,
+       0
+};
+static int irq[MAX_CAN] = { 26, 26, 26, 26, 0 };
+static int speed[MAX_CAN] = {
+       DEFAULT_KBIT_PER_SEC, DEFAULT_KBIT_PER_SEC,
+       DEFAULT_KBIT_PER_SEC, DEFAULT_KBIT_PER_SEC,
+       0
+};
+static int btr[MAX_CAN] = { 0 };
+static int rx_probe[MAX_CAN] = { 0 };
+
+static int clk = SJA1000_HW_CLOCK;
+static int debug = 0;
+static int restart_ms = 100;
+
+/* array of all can chips */
+static struct net_device       *can_dev[MAX_CAN];
+
+
+/* special functions to access the chips registers */
+static uint8_t reg_read(struct net_device *dev, int reg)
+{
+       static uint8_t val;
+
+       val = (uint8_t)readw(dev->base_addr + reg * (ADDR_GAP + 1) + ADDR_GAP);
+       rmb();
+
+       return val;
+}
+
+static void reg_write(struct net_device *dev, int reg, uint8_t val)
+{
+       writew(val, dev->base_addr + reg * 2 + 1);
+       wmb();
+}
+
+MODULE_PARM(base_addr, "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(irq,       "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(speed,     "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(btr,       "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(rx_probe,  "1-" __MODULE_STRING(MAX_CAN)"i");
+MODULE_PARM(clk, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(restart_ms, "i");
+
+static struct net_device* sja1000_gw2_probe(uint32_t base, int irq, int speed,
+                                           int btr, int rx_probe, int clk,
+                                           int debug, int restart_ms)
+{
+       struct net_device       *dev;
+       struct can_priv         *priv;
+
+       if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_DEV_NAME,
+                                sja1000_setup))) {
+               printk(KERN_ERR "%s: out of memory\n", chip_name);
+               return NULL;
+       }
+
+       printk(KERN_INFO "%s: base 0x%X / irq %d / speed %d / btr 0x%X / rx_probe %d\n",
+              chip_name, base, irq, speed, btr, rx_probe);
+
+       /* fill net_device structure */
+
+       priv             = netdev_priv(dev);
+
+       dev->irq         = irq;
+       dev->base_addr   = base;
+
+       priv->reg_read   = reg_read;
+       priv->reg_write  = reg_write;
+
+       priv->speed      = speed;
+       priv->btr        = btr;
+       priv->rx_probe   = rx_probe;
+       priv->clock      = clk;
+       priv->restart_ms = restart_ms;
+       priv->debug      = debug;
+
+       if (REG_READ(0) == 0xFF)
+               goto free_dev;
+
+       /* set chip into reset mode */
+       set_reset_mode(dev);
+
+       /* go into Pelican mode, disable clkout, disable comparator */
+       REG_WRITE(REG_CDR, 0xCF);
+
+       /* output control */
+       /* connected to external transceiver */
+       REG_WRITE(REG_OCR, 0x1A);
+
+       printk(KERN_INFO "%s: %s found at 0x%X, irq is %d\n",
+              dev->name, chip_name, (uint32_t)dev->base_addr, dev->irq);
+
+       if (register_netdev(dev) == 0)
+               return dev;
+
+       printk(KERN_INFO "%s: probing failed\n", chip_name);
+ free_dev:
+       free_netdev(dev);
+       return NULL;
+}
+
+static __exit void sja1000_gw2_cleanup_module(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_CAN; i++) {
+               if (can_dev[i] != NULL) {
+                       struct can_priv *priv = netdev_priv(can_dev[i]);
+                       unregister_netdev(can_dev[i]);
+                       del_timer(&priv->timer);
+                       iounmap((void*)can_dev[i]->base_addr);
+                       release_mem_region(base_addr[i], RSIZE);
+                       free_netdev(can_dev[i]);
+               }
+       }
+       sja1000_proc_delete(drv_name);
+}
+
+static __init int sja1000_gw2_init_module(void)
+{
+       int i;
+       struct net_device *dev;
+       void *base;
+
+       if (clk < 1000 ) /* MHz command line value */
+               clk *= 1000000;
+
+       if (clk < 1000000 ) /* kHz command line value */
+               clk *= 1000;
+
+       printk(KERN_INFO "%s - %s driver v%s (%s)\n",
+              chip_name, drv_name, drv_version, drv_reldate);
+       printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms] [debug %d]\n",
+              chip_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+       for (i = 0; base_addr[i]; i++) {
+               printk(KERN_DEBUG "%s: checking for %s on address 0x%X ...\n",
+                      chip_name, chip_name, base_addr[i]);
+               if (!request_mem_region(base_addr[i], RSIZE, chip_name)) {
+                       printk(KERN_ERR "%s: memory already in use\n", chip_name);
+                       sja1000_gw2_cleanup_module();
+                       return -EBUSY;
+               }
+               base = ioremap(base_addr[i], RSIZE);
+               dev = sja1000_gw2_probe((uint32_t)base, irq[i], speed[i], btr[i], rx_probe[i], clk, debug, restart_ms);
+               if (dev != NULL) {
+                       can_dev[i] = dev;
+                       sja1000_proc_init(drv_name, can_dev, MAX_CAN);
+               } else {
+                       can_dev[i] = NULL;
+                       iounmap(base);
+                       release_mem_region(base_addr[i], RSIZE);
+               }
+       }
+       return 0;
+}
+
+module_init(sja1000_gw2_init_module);
+module_exit(sja1000_gw2_cleanup_module);
diff --git a/kernel/2.6/drivers/sja1000/version.h b/kernel/2.6/drivers/sja1000/version.h
new file mode 120000 (symlink)
index 0000000..c4e22f8
--- /dev/null
@@ -0,0 +1 @@
+../../can/version.h
\ No newline at end of file