]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
backport from the 2.6 version:
authorthuermann <thuermann@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 27 Nov 2006 11:45:55 +0000 (11:45 +0000)
committerthuermann <thuermann@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 27 Nov 2006 11:45:55 +0000 (11:45 +0000)
- add 'loop' parameter to can_send().
- do loopback only if the interface hasn't IFF_LOOPBACK set.
- add struct can_proto and use that in can_proto_{,un}register().
- define type, protocol, and capability fields in struct can_proto instead
  of doing a switch() on type and proto in can_create().
- call protocol init function if defined.
- split off new function find_dev_rcv_lists() from find_rcv_list().
- rename struct rcv_list and rcv_dev_list to receiver and dev_rcv_lists.
- move memory allocation out of spin-locked region.
- rename struct notifier_list to notifier, and variable nlist to notifier_list.
- use kernel list implementation for notifier_list.
- protect access to notifier list by holding notifier_lock.
- use kmem_caches for struct receiver instead of kmalloc.
- do allocation/freeing of dev_rcv_lists in NETDEV_{,UN}REGISTER.
  + new element pprev in struct dev_rcv_lists to ease removal of list elements.
- rename CAN_MAX to CAN_NPROTO.
- rename some local variables for readability.
- rename raw_{init,exit}() to raw_module_{init,exit}().
- new function raw_init() to be called from can_create().
- return EINVAL from raw_bind() if already bound.

still TODO
- new ioctl's in raw.c: CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS.
- allow setsockopt in raw.c to set 0 filters.

git-svn-id: svn://svn.berlios.de//socketcan/trunk@103 030b6a49-0b11-0410-94ab-b0dab22257f2

kernel/2.4/can/af_can.c
kernel/2.4/can/af_can.h
kernel/2.4/can/bcm.c
kernel/2.4/can/proc.c
kernel/2.4/can/raw.c

index 8e2e9d473a482decfa0cbf319f0f09575d63bd06..b08a4f876055c66a3f0133789e25406829b35a6c 100644 (file)
 #define EXPORT_SYMTAB
 
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/kmod.h>
 #include <linux/init.h>
+#include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/socket.h>
 #include <linux/skbuff.h>
@@ -58,6 +60,7 @@
 #include "af_can.h"
 #include "version.h"
 
+
 RCSID("$Id$");
 
 #define NAME "Volkswagen AG - Low Level CAN Framework (LLCF)"
@@ -69,12 +72,12 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
              "Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
 
-MODULE_PARM(stats_timer, "1i");
 int stats_timer = 1; /* default: on */
+MODULE_PARM(stats_timer, "1i");
 
 #ifdef DEBUG
-MODULE_PARM(debug, "1i");
 static int debug = 0;
+MODULE_PARM(debug, "1i");
 #define DBG(args...)       (debug & 1 ? \
                               (printk(KERN_DEBUG "CAN %s: ", __func__), \
                                printk(args)) : 0)
@@ -95,23 +98,28 @@ static int can_notifier(struct notifier_block *nb,
 static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 static int can_rcv(struct sk_buff *skb, struct net_device *dev,
                   struct packet_type *pt);
-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;
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb);
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev);
+static struct receiver **find_rcv_list(canid_t *can_id, canid_t *mask,
+                                      struct dev_rcv_lists *d);
+static void can_rx_delete_all(struct receiver **rl);
+
+struct notifier {
+       struct list_head list;
        struct net_device *dev;
        void (*func)(unsigned long msg, void *data);
        void *data;
 };
 
-static struct notifier_list *nlist;
+static LIST_HEAD(notifier_list);
+static rwlock_t notifier_lock = RW_LOCK_UNLOCKED;
 
-struct rcv_dev_list *rx_dev_list;
-struct rcv_dev_list rx_alldev_list;
+struct dev_rcv_lists *rx_dev_list;
+struct dev_rcv_lists rx_alldev_list;
 rwlock_t rcv_lists_lock = RW_LOCK_UNLOCKED;
 
+static kmem_cache_t *rcv_cache;
+
 static struct packet_type can_packet = {
        .type = __constant_htons(ETH_P_CAN),
        .dev  = NULL,
@@ -123,15 +131,17 @@ static struct net_proto_family can_family_ops = {
        .create = can_create,
 };
 
+/* notifier block for netdevice event */
 static struct notifier_block can_netdev_notifier = {
        .notifier_call = can_notifier,
 };
 
-static struct proto_ops *proto_tab[CAN_MAX];
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
 
 extern struct timer_list stattimer; /* timer for statistics update */
-extern struct s_stats  stats;       /* statistics */
-extern struct s_pstats pstats;
+extern struct s_stats  stats;       /* packet statistics */
+extern struct s_pstats pstats;      /* receive list statistics */
 
 module_init(can_init);
 module_exit(can_exit);
@@ -144,6 +154,11 @@ static __init int can_init(void)
 {
        printk(banner);
 
+       rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+                                     0, 0, NULL, NULL);
+       if (!rcv_cache)
+               return -ENOMEM;
+
        if (stats_timer) {
                /* statistics init */
                init_timer(&stattimer);
@@ -174,15 +189,18 @@ static __exit void can_exit(void)
        dev_remove_pack(&can_packet);
        unregister_netdevice_notifier(&can_netdev_notifier);
        sock_unregister(PF_CAN);
+
+       kmem_cache_destroy(rcv_cache);
 }
 
 /**************************************************/
 /* af_can protocol functions                      */
 /**************************************************/
 
-void can_proto_register(int proto, struct proto_ops *ops)
+void can_proto_register(struct can_proto *cp)
 {
-       if (proto < 0 || proto >= CAN_MAX) {
+       int proto = cp->protocol;
+       if (proto < 0 || proto >= CAN_NPROTO) {
                printk(KERN_ERR "CAN: protocol number %d out of range\n", proto);
                return;
        }
@@ -190,15 +208,16 @@ void can_proto_register(int proto, struct proto_ops *ops)
                printk(KERN_ERR "CAN: protocol %d already registered\n", proto);
                return;
        }
-       proto_tab[proto] = ops;
+       proto_tab[proto] = cp;
 
        /* use our generic ioctl function if the module doesn't bring its own */
-       if (!ops->ioctl)
-               ops->ioctl = can_ioctl;
+       if (!cp->ops->ioctl)
+               cp->ops->ioctl = can_ioctl;
 }
 
-void can_proto_unregister(int proto)
+void can_proto_unregister(struct can_proto *cp)
 {
+       int proto = cp->protocol;
        if (!proto_tab[proto]) {
                printk(KERN_ERR "CAN: protocol %d is not registered\n", proto);
                return;
@@ -209,33 +228,38 @@ void can_proto_unregister(int proto)
 void can_dev_register(struct net_device *dev,
                      void (*func)(unsigned long msg, void *), void *data)
 {
-       struct notifier_list *p = kmalloc(sizeof(*p), GFP_KERNEL);
+       struct notifier *n;
 
        DBG("called for %s\n", dev->name);
 
-       if (!p)
+       if (!(n = kmalloc(sizeof(*n), GFP_KERNEL)))
                return;
-       p->next = nlist;
-       p->dev  = dev;
-       p->func = func;
-       p->data = data;
-       nlist = p;
+
+       n->dev  = dev;
+       n->func = func;
+       n->data = data;
+
+       write_lock(&notifier_lock);
+       list_add(&n->list, &notifier_list);
+       write_unlock(&notifier_lock);
 }
 
 void can_dev_unregister(struct net_device *dev,
                        void (*func)(unsigned long msg, void *), void *data)
 {
-       struct notifier_list *p, **q;
+       struct notifier *n, *next;
 
        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;
+       write_lock(&notifier_lock);
+       list_for_each_entry_safe(n, next, &notifier_list, list) {
+               if (n->dev == dev && n->func == func && n->data == data) {
+                       list_del(&n->list);
+                       kfree(n);
+                       break;
                }
        }
+       write_unlock(&notifier_lock);
 }
 
 /**************************************************/
@@ -244,56 +268,23 @@ void can_dev_unregister(struct net_device *dev,
 
 static void can_sock_destruct(struct sock *sk)
 {
+       DBG("called for sock %p\n", sk);
+
        skb_queue_purge(&sk->receive_queue);
 }
 
 static int can_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
+       struct can_proto *cp;
+       int ret;
 
        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:
-                       if (!capable(CAP_NET_RAW))
-                               return -EPERM;
-                       break;
-               default:
-                       return -EPROTONOSUPPORT;
-               }
-               break;
-       default:
-               return -ESOCKTNOSUPPORT;
-               break;
-       }
+       if (protocol < 0 || protocol >= CAN_NPROTO)
+               return -EINVAL;
 
        DBG("looking up proto %d in proto_tab[]\n", protocol);
 
@@ -306,11 +297,14 @@ static int can_create(struct socket *sock, int protocol)
                               module_name);
        }
 
-       /* check for success */
-       if (!proto_tab[protocol])
+       /* check for success and correct type */
+       if (!(cp = proto_tab[protocol]) || cp->type != sock->type)
                return -EPROTONOSUPPORT;
 
-       sock->ops = proto_tab[protocol];
+       if (cp->capability >= 0 && !capable(cp->capability))
+               return -EPERM;
+
+       sock->ops = cp->ops;
 
        if (!(sk = sk_alloc(PF_CAN, GFP_KERNEL, 1)))
                goto oom;
@@ -320,6 +314,16 @@ static int can_create(struct socket *sock, int protocol)
 
        DBG("created sock: %p\n", sk);
 
+       ret = 0;
+       if (cp->init)
+               ret = cp->init(sk);
+       if (ret) {
+               /* we must release sk */
+               sock_orphan(sk);
+               sock_put(sk);
+               return ret;
+       }
+
        return 0;
 
  oom:
@@ -330,15 +334,78 @@ 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;
+       struct notifier *n;
 
        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);
+#if 0
+       if (dev->type != ARPHRD_CAN)
+               return NOTIFY_DONE;
+#endif
+
+       switch (msg) {
+               struct dev_rcv_lists *d;
+               int i;
+
+       case NETDEV_REGISTER:
+
+               /* create new dev_rcv_lists for this device */
+
+               DBG("creating new dev_rcv_lists for %s\n", dev->name);
+               if (!(d = kmalloc(sizeof(*d), GFP_KERNEL))) {
+                       printk(KERN_ERR "CAN: allocation of receive list failed\n");
+                       return NOTIFY_DONE;
+               }
+               memset(d, 0, sizeof(*d));
+               d->dev = dev;
+
+               /* insert d into the list */
+               write_lock_bh(&rcv_lists_lock);
+               d->next        = rx_dev_list;
+               d->pprev       = &rx_dev_list;
+               rx_dev_list    = d;
+               d->next->pprev = &d->next;
+               write_unlock_bh(&rcv_lists_lock);
+
+               break;
+
+       case NETDEV_UNREGISTER:
+               write_lock_bh(&rcv_lists_lock);
+
+               if (!(d = find_dev_rcv_lists(dev))) {
+                       printk(KERN_ERR "CAN: notifier: receive list not "
+                              "found for dev %s\n", dev->name);
+                       goto unreg_out;
+               }
+
+               /* remove d from the list */
+               *d->pprev = d->next;
+               d->next->pprev = d->pprev;
+
+               /* remove all receivers hooked at this netdevice */
+               can_rx_delete_all(&d->rx_err);
+               can_rx_delete_all(&d->rx_all);
+               can_rx_delete_all(&d->rx_fil);
+               can_rx_delete_all(&d->rx_inv);
+               can_rx_delete_all(&d->rx_eff);
+               for (i = 0; i < 2048; i++)
+                       can_rx_delete_all(&d->rx_sff[i]);
+               kfree(d);
+
+       unreg_out:
+               write_unlock_bh(&rcv_lists_lock);
+
+               break;
        }
-       return 0;
+
+       read_lock(&notifier_lock);
+       list_for_each_entry(n, &notifier_list, list) {
+               if (n->dev == dev)
+                       n->func(msg, n->data);
+       }
+       read_unlock(&notifier_lock);
+
+       return NOTIFY_DONE;
 }
 
 static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
@@ -363,11 +430,23 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 /* af_can tx path                                 */
 /**************************************************/
 
-int can_send(struct sk_buff *skb)
+int can_send(struct sk_buff *skb, int loop)
 {
-       struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
        int err;
 
+       if (loop) { /* local loopback (default) */
+               *(struct sock **)skb->cb = skb->sk; /* tx sock reference */
+
+                /* interface not capabable to do the loopback itself? */
+                if (!(skb->dev->flags & IFF_LOOPBACK)) {
+                       struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+                       newskb->protocol  = htons(ETH_P_CAN);
+                       newskb->ip_summed = CHECKSUM_UNNECESSARY;
+                       netif_rx(newskb); /* perform local loopback here */
+               }
+       } else
+                *(struct sock **)skb->cb = NULL; /* no loopback required */
+
        if (!(skb->dev->flags & IFF_UP))
                err = -ENETDOWN;
        else if ((err = dev_queue_xmit(skb)) > 0)  /* send to netdevice */
@@ -377,10 +456,6 @@ int can_send(struct sk_buff *skb)
        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;
 }
 
@@ -392,100 +467,107 @@ 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;
+       struct receiver *r, **rl;
+       struct dev_rcv_lists *d;
+
+       /* insert new receiver  (dev,canid,mask) -> (func,data) */
 
        DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
            dev, can_id, mask, func, data, ident);
 
-       write_lock_bh(&rcv_lists_lock);
+       if (!(r = kmem_cache_alloc(rcv_cache, GFP_KERNEL)))
+               goto out;
 
-       q = find_rcv_list(&can_id, &mask, dev);
+       write_lock_bh(&rcv_lists_lock);
 
-       if (!q) {
+       if (!(d = find_dev_rcv_lists(dev))) {
                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;
+                      "dev %s, id %03X, mask %03X\n", dev->name, can_id, mask);
+               kmem_cache_free(rcv_cache, r);
+               goto out_unlock;
        }
 
-       /* insert   (dev,canid,mask) -> (func,data) */
-       if (!(p = kmalloc(sizeof(struct rcv_list), GFP_KERNEL)))
-               goto out;
+       rl = find_rcv_list(&can_id, &mask, d);
 
-       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;
+       r->can_id  = can_id;
+       r->mask    = mask;
+       r->matches = 0;
+       r->func    = func;
+       r->data    = data;
+       r->ident   = ident;
 
-       if (!dev)
-               d = &rx_alldev_list;
-       else
-               for (d = rx_dev_list; d; d = d->next)
-                       if (d->dev == dev)
-                               break;
+       r->next = *rl;
+       *rl = r;
        d->entries++;
 
        pstats.rcv_entries++;
        if (pstats.rcv_entries_max < pstats.rcv_entries)
                pstats.rcv_entries_max = pstats.rcv_entries;
 
- out:
+ out_unlock:
        write_unlock_bh(&rcv_lists_lock);
+ out:
+       return;
+}
+
+static void can_rx_delete_all(struct receiver **rl)
+{
+       struct receiver *r, *n;
+
+       for (r = *rl; r; r = n) {
+               n = r->next;
+               kfree(r);
+       }
+       *rl = NULL;
 }
 
 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;
+       struct receiver *r, **rl;
+       struct dev_rcv_lists *d;
 
        DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
            dev, can_id, mask, func, data);
 
        write_lock_bh(&rcv_lists_lock);
 
-       q = find_rcv_list(&can_id, &mask, dev);
-
-       if (!q) {
+       if (!(d = find_dev_rcv_lists(dev))) {
                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)
+       rl = find_rcv_list(&can_id, &mask, d);
+
+       /*  Search the receiver list for the item to delete.  This should
+        *  exist, since no receiver may be unregistered that hasn't
+        *  been registered before.
+        */
+
+       for (; r = *rl; rl = &r->next) {
+               if (r->can_id == can_id && r->mask == mask
+                   && r->func == func && r->data == data)
                        break;
        }
 
-       if (!p) {
+       /*  Check for bug in CAN protocol implementations:
+        *  If no matching list item was found, r is NULL.
+        */
+
+       if (!r) {
                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);
+       *rl = r->next;
+       kmem_cache_free(rcv_cache, r);
+       d->entries--;
 
        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_bh(&rcv_lists_lock);
 }
@@ -493,7 +575,7 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
 static int can_rcv(struct sk_buff *skb, struct net_device *dev,
                   struct packet_type *pt)
 {
-       struct rcv_dev_list *q;
+       struct dev_rcv_lists *d;
        int matches;
 
        DBG("received skbuff on device %s, ptype %04x\n",
@@ -508,15 +590,12 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
 
        read_lock(&rcv_lists_lock);
 
+       /* deliver the packet to sockets listening on all devices */
        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);
+       if ((d = find_dev_rcv_lists(dev)))
+               matches += can_rcv_filter(d, skb);
 
        read_unlock(&rcv_lists_lock);
 
@@ -532,32 +611,32 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
 }
 
 
-static inline void deliver(struct sk_buff *skb, struct rcv_list *p)
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
 {
        struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
        DBG("skbuff %p cloned to %p\n", skb, clone);
        if (clone) {
-               p->func(clone, p->data);
-               p->matches++;    /* update specific statistics */
+               r->func(clone, r->data);
+               r->matches++;    /* update specific statistics */
        }
 }
 
-static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb)
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
 {
-       struct rcv_list *p;
+       struct receiver *r;
        int matches = 0;
        struct can_frame *cf = (struct can_frame*)skb->data;
        canid_t can_id = cf->can_id;
 
-       if (q->entries == 0)
+       if (d->entries == 0)
                return 0;
 
        if (can_id & CAN_ERR_FLAG) {
                /* check for error frame entries only */
-               for (p = q->rx_err; p; p = p->next) {
-                       if (can_id & p->mask) {
+               for (r = d->rx_err; r; r = r->next) {
+                       if (can_id & r->mask) {
                                DBG("match on rx_err skbuff %p\n", skb);
-                               deliver(skb, p);
+                               deliver(skb, r);
                                matches++;
                        }
                }
@@ -565,43 +644,43 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb)
        }
 
        /* check for unfiltered entries */
-       for (p = q->rx_all; p; p = p->next) {
+       for (r = d->rx_all; r; r = r->next) {
                DBG("match on rx_all skbuff %p\n", skb);
-               deliver(skb, p);
+               deliver(skb, r);
                matches++;
        }
 
        /* check for can_id/mask entries */
-       for (p = q->rx_fil; p; p = p->next) {
-               if ((can_id & p->mask) == p->can_id) {
+       for (r = d->rx_fil; r; r = r->next) {
+               if ((can_id & r->mask) == r->can_id) {
                        DBG("match on rx_fil skbuff %p\n", skb);
-                       deliver(skb, p);
+                       deliver(skb, r);
                        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) {
+       for (r = d->rx_inv; r; r = r->next) {
+               if ((can_id & r->mask) != r->can_id) {
                        DBG("match on rx_inv skbuff %p\n", skb);
-                       deliver(skb, p);
+                       deliver(skb, r);
                        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) {
+               for (r = d->rx_eff; r; r = r->next) {
+                       if (r->can_id == can_id) {
                                DBG("match on rx_eff skbuff %p\n", skb);
-                               deliver(skb, p);
+                               deliver(skb, r);
                                matches++;
                        }
                }
        } else {
-               for (p = q->rx_sff[can_id & CAN_SFF_MASK]; p; p = p->next) {
+               for (r = d->rx_sff[can_id & CAN_SFF_MASK]; r; r = r->next) {
                        DBG("match on rx_sff skbuff %p\n", skb);
-                       deliver(skb, p);
+                       deliver(skb, r);
                        matches++;
                }
        }
@@ -610,16 +689,30 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb)
        return matches;
 }
 
-static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask,
-                                      struct net_device *dev)
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+       struct dev_rcv_lists *d;
+
+       /* find receive list for this device */
+
+       if (!dev)
+               return &rx_alldev_list;
+
+       for (d = rx_dev_list; d; d = d->next)
+               if (d->dev == dev)
+                       break;
+
+       return d;
+}
+
+static struct receiver **find_rcv_list(canid_t *can_id, canid_t *mask,
+                                      struct dev_rcv_lists *d)
 {
        canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking 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? */
        canid_t err = *mask & CAN_ERR_FLAG; /* mask for error frames only */
 
-       struct rcv_dev_list *p;
-
        /* make some paranoic operations */
        if (*can_id & CAN_EFF_FLAG)
                *mask &= (CAN_EFF_MASK | eff | rtr);
@@ -628,61 +721,27 @@ static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask,
 
        *can_id &= *mask;
 
-       /* find receive list for this device */
-       if (!dev)
-               p = &rx_alldev_list;
-       else {
-               /* find the list for dev or an unused list entry, otherwise */
-               struct rcv_dev_list *q;
-               p = NULL;
-               for (q = rx_dev_list; q; q = q->next)
-                       if (!q->dev)
-                               p = q;
-                       else if (q->dev == dev) {
-                               p = q;
-                               break;
-                       }
-
-               if (p && !p->dev) {
-                       DBG("reactivating rcv_dev_list for %s\n", dev->name);
-                       p->dev = dev;
-               }
-       }
-
-       if (!p) {
-               /* 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 (err) /* error frames */
-               return &p->rx_err;
+               return &d->rx_err;
 
        if (inv) /* inverse can_id/can_mask filter and RTR */
-               return &p->rx_inv;
+               return &d->rx_inv;
 
        if (*can_id & CAN_RTR_FLAG) /* positive filter RTR */
-               return &p->rx_fil;
+               return &d->rx_fil;
 
        if (!(*mask)) /* mask == 0 => no filter */
-               return &p->rx_all;
+               return &d->rx_all;
 
        if (*can_id & CAN_EFF_FLAG) {
                if (*mask == CAN_EFF_MASK) /* filter exact EFF can_id */
-                       return &p->rx_eff;
+                       return &d->rx_eff;
        } else {
                if (*mask == CAN_SFF_MASK) /* filter exact SFF can_id */
-                       return &p->rx_sff[*can_id];
+                       return &d->rx_sff[*can_id];
        }
 
-       return &p->rx_fil;  /* filter via can_id/can_mask */
+       return &d->rx_fil;  /* filter via can_id/can_mask */
 }
 
 /**************************************************/
index 52790f26929812dc8108ef092fbce93ccc7e94a6..b8da8b99c807287de257b69e3dace8047b2ff71f 100644 (file)
@@ -74,7 +74,7 @@ RCSID("$Id$");
 #define CAN_MCNET      5 /* Bosch MCNet */
 #define CAN_ISOTP      6 /* ISO 15765-2 Transport Protocol */
 #define CAN_BAP                7 /* VAG Bedien- und Anzeigeprotokoll */
-#define CAN_MAX                8
+#define CAN_NPROTO     8
 
 #define SOL_CAN_BASE 100
 
@@ -101,8 +101,17 @@ struct can_filter {
 
 #define CAN_PROC_DIR "net/can" /* /proc/... */
 
-void can_proto_register(int proto, struct proto_ops *ops);
-void can_proto_unregister(int proto);
+struct can_proto {
+       int              type;
+       int              protocol;
+       int              capability;
+       struct proto_ops *ops;
+       int              (*init)(struct sock *sk);
+       size_t           obj_size;
+};
+
+void can_proto_register(struct can_proto *cp);
+void can_proto_unregister(struct can_proto *cp);
 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);
@@ -112,7 +121,7 @@ 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);
+int  can_send(struct sk_buff *skb, int loop);
 
 unsigned long timeval2jiffies(struct timeval *tv, int round_up);
 
@@ -121,8 +130,8 @@ void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
 
 /* af_can rx dispatcher structures */
 
-struct rcv_list {
-       struct rcv_list *next;
+struct receiver {
+       struct receiver *next;
        canid_t can_id;
        canid_t mask;
        unsigned long matches;
@@ -131,15 +140,16 @@ struct rcv_list {
        char *ident;
 };
 
-struct rcv_dev_list {
-       struct rcv_dev_list *next;
+struct dev_rcv_lists {
+       struct dev_rcv_lists *next;
+       struct dev_rcv_lists **pprev;
        struct net_device *dev;
-       struct rcv_list *rx_err;
-       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;
+       struct receiver *rx_err;
+       struct receiver *rx_all;
+       struct receiver *rx_fil;
+       struct receiver *rx_inv;
+       struct receiver *rx_sff[0x800];
+       struct receiver *rx_eff;
        int entries;
 };
 
index 42c7c163e232c7d10b6191e169eb01542f513a4a..a1ecd9167a98f874deacaca798f09df772bf7f68 100644 (file)
@@ -166,11 +166,26 @@ static struct proto_ops bcm_ops = {
        .sendpage      = sock_no_sendpage,
 };
 
+#ifdef CONFIG_CAN_BCM_USER
+#define BCM_CAP CAP_NET_RAW
+#else
+#define BCM_CAP (-1)
+#endif
+
+static struct can_proto bcm_can_proto = {
+       .type       = SOCK_DGRAM,
+       .protocol   = CAN_BCM,
+       .capability = BCM_CAP,
+       .ops        = &bcm_ops,
+       .obj_size   = sizeof(struct bcm_user_data),
+       .init       = NULL,
+};
+
 static int __init bcm_init(void)
 {
        printk(banner);
 
-       can_proto_register(CAN_BCM, &bcm_ops);
+       can_proto_register(&bcm_can_proto);
 
        /* create /proc/can/bcm directory */
        proc_dir = proc_mkdir(CAN_PROC_DIR"/"IDENT, NULL);
@@ -183,7 +198,7 @@ static int __init bcm_init(void)
 
 static void __exit bcm_exit(void)
 {
-       can_proto_unregister(CAN_BCM);
+       can_proto_unregister(&bcm_can_proto);
 
        if (proc_dir)
                remove_proc_entry(CAN_PROC_DIR"/"IDENT, NULL);
@@ -642,7 +657,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, int size,
 
                if (dev) {
                        skb->dev = dev;
-                       can_send(skb);
+                       can_send(skb, 1);
                        dev_put(dev);
                }
 
@@ -1243,7 +1258,7 @@ static void bcm_can_tx(struct bcm_op *op)
 
                if (dev) {
                        skb->dev = dev;
-                       can_send(skb);
+                       can_send(skb, 1);
                        dev_put(dev);
                }
        }
index a1743b91aeffdfaa9251ae932d69e1f4d756fa76..771a83baee53a1a9886bda2c8f7dc3fa49a5c604 100644 (file)
@@ -106,8 +106,8 @@ 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 */
+extern struct dev_rcv_lists *rx_dev_list; /* rx dispatcher structures */
+extern int stats_timer;                   /* module parameter. default: on */
 
 /**************************************************/
 /* procfs init / remove                           */
@@ -196,20 +196,20 @@ void can_remove_proc(void)
 /* proc read functions                            */
 /**************************************************/
 
-static int can_print_recv_list(char *page, int len, struct rcv_list *rx_list,
+static int can_print_recv_list(char *page, int len, struct receiver *rx_list,
                               struct net_device *dev)
 {
-       struct rcv_list *p;
+       struct receiver *r;
 
-       for (p = rx_list; p; p = p->next) {
-               char *fmt = p->can_id & CAN_EFF_FLAG ? /* EFF & CAN_ID_ALL */
+       for (r = rx_list; r; r = r->next) {
+               char *fmt = r->can_id & CAN_EFF_FLAG ? /* EFF & CAN_ID_ALL */
                        "   %-5s  %08X  %08x  %08x  %08x  %8ld  %s\n" :
                        "   %-5s     %03X    %08x  %08x  %08x  %8ld  %s\n";
 
                len += snprintf(page + len, PAGE_SIZE - len, fmt,
-                               dev->name, p->can_id, p->mask,
-                               (unsigned int)p->func, (unsigned int)p->data,
-                               p->matches, p->ident);
+                               dev->name, r->can_id, r->mask,
+                               (unsigned int)r->func, (unsigned int)r->data,
+                               r->matches, r->ident);
 
                /* does a typical line fit into the current buffer? */
                /* 100 Bytes before end of buffer */
@@ -217,7 +217,7 @@ static int can_print_recv_list(char *page, int len, struct rcv_list *rx_list,
                        /* mark output cut off */
                        len += snprintf(page + len, PAGE_SIZE - len,
                                        "   (..)\n");
-                       return len;
+                       break;
                }
        }
 
@@ -350,7 +350,7 @@ static 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;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -359,14 +359,14 @@ static int can_proc_read_rcvlist_all(char *page, char **start, off_t off,
                        "\nreceive list 'rx_all':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
 
-               if (p->rx_all) {
+               if (d->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 = can_print_recv_list(page, len, d->rx_all, d->dev);
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
@@ -381,7 +381,7 @@ static 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;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -390,14 +390,14 @@ static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off,
                        "\nreceive list 'rx_fil':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
 
-               if (p->rx_fil) {
+               if (d->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 = can_print_recv_list(page, len, d->rx_fil, d->dev);
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
@@ -412,7 +412,7 @@ static 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;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -421,14 +421,14 @@ static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off,
                        "\nreceive list 'rx_inv':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
 
-               if (p->rx_inv) {
+               if (d->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 = can_print_recv_list(page, len, d->rx_inv, d->dev);
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
@@ -443,7 +443,7 @@ static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
                                     int count, int *eof, void *data)
 {
        int len = 0;
-       struct rcv_dev_list *p;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -452,11 +452,11 @@ static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
                        "\nreceive list 'rx_sff':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
                int i, all_empty = 1;
                /* check wether at least one list is non-empty */
                for (i = 0; i < 0x800; i++)
-                       if (p->rx_sff[i]) {
+                       if (d->rx_sff[i]) {
                                all_empty = 0;
                                break;
                        }
@@ -464,12 +464,12 @@ static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
                if (!all_empty) {
                        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);
+                               if (d->rx_sff[i] && len < PAGE_SIZE - 100)
+                                       len = can_print_recv_list(page, len, d->rx_sff[i], d->dev);
                        }
-               } else if (p->dev)
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
@@ -484,7 +484,7 @@ static 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;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -493,14 +493,14 @@ static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off,
                        "\nreceive list 'rx_eff':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
 
-               if (p->rx_eff) {
+               if (d->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 = can_print_recv_list(page, len, d->rx_eff, d->dev);
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
@@ -515,7 +515,7 @@ static int can_proc_read_rcvlist_err(char *page, char **start, off_t off,
                                     int count, int *eof, void *data)
 {
        int len = 0;
-       struct rcv_dev_list *p;
+       struct dev_rcv_lists *d;
 
        MOD_INC_USE_COUNT;
 
@@ -524,14 +524,14 @@ static int can_proc_read_rcvlist_err(char *page, char **start, off_t off,
                        "\nreceive list 'rx_err':\n");
 
        /* find receive list for this device */
-       for (p = rx_dev_list; p; p = p->next) {
+       for (d = rx_dev_list; d; d = d->next) {
 
-               if (p->rx_err) {
+               if (d->rx_err) {
                        len = can_print_recv_banner(page, len);
-                       len = can_print_recv_list(page, len, p->rx_err, p->dev);
-               } else if (p->dev)
+                       len = can_print_recv_list(page, len, d->rx_err, d->dev);
+               } else if (d->dev)
                        len += snprintf(page + len, PAGE_SIZE - len,
-                                       "  (%s: no entry)\n", p->dev->name);
+                                       "  (%s: no entry)\n", d->dev->name);
        }
 
        len += snprintf(page + len, PAGE_SIZE - len, "\n");
index d6315b842ca717d9393d3b2038356541a16b8081..6c48cf422c6b62c1c244c23991655fde7847b454 100644 (file)
@@ -67,8 +67,8 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
 
 #ifdef DEBUG
-MODULE_PARM(debug, "1i");
 static int debug = 0;
+MODULE_PARM(debug, "1i");
 #define DBG(args...)       (debug & 1 ? \
                               (printk(KERN_DEBUG "RAW %s: ", __func__), \
                                printk(args)) : 0)
@@ -78,6 +78,7 @@ static int debug = 0;
 #define DBG_SKB(skb)
 #endif
 
+static int raw_init(struct sock *sk);
 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,
@@ -98,21 +99,6 @@ 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);
 
-/*  this struct is part of struct sock in the place of union tp_pinfo,
- *  which is initialized to zero for each newly allocated struct sock.
- */
-
-struct canraw_opt {
-       int bound;
-       int ifindex;
-       int count;
-       struct can_filter *filter;
-       can_err_mask_t err_mask;
-};
-
-#define canraw_sk(sk) ((struct canraw_opt *)&(sk)->tp_pinfo)
-
-#define MASK_ALL 0
 
 static struct proto_ops raw_ops = {
        .family        = PF_CAN,
@@ -134,17 +120,60 @@ static struct proto_ops raw_ops = {
        .sendpage      = sock_no_sendpage,
 };
 
-static __init int raw_init(void)
+/*  this struct is part of struct sock in the place of union tp_pinfo,
+ *  which is initialized to zero for each newly allocated struct sock.
+ */
+
+struct canraw_opt {
+       int bound;
+       int ifindex;
+       int count;
+       int loopback;
+       int recv_own_msgs;
+       struct can_filter *filter;
+       can_err_mask_t err_mask;
+};
+
+#ifdef CONFIG_CAN_RAW_USER
+#define RAW_CAP CAP_NET_RAW
+#else
+#define RAW_CAP (-1)
+#endif
+
+#define canraw_sk(sk) ((struct canraw_opt *)&(sk)->tp_pinfo)
+
+static struct can_proto raw_can_proto = {
+       .type       = SOCK_RAW,
+       .protocol   = CAN_RAW,
+       .capability = RAW_CAP,
+       .ops        = &raw_ops,
+       .obj_size   = sizeof(struct canraw_opt),
+       .init       = raw_init,
+};
+
+#define MASK_ALL 0
+
+static __init int raw_module_init(void)
 {
        printk(banner);
 
-       can_proto_register(CAN_RAW, &raw_ops);
+       can_proto_register(&raw_can_proto);
        return 0;
 }
 
-static __exit void raw_exit(void)
+static __exit void raw_module_exit(void)
 {
-       can_proto_unregister(CAN_RAW);
+       can_proto_unregister(&raw_can_proto);
+}
+
+static int raw_init(struct sock *sk)
+{
+       canraw_sk(sk)->bound         = 0;
+       canraw_sk(sk)->count         = 0;
+       canraw_sk(sk)->loopback      = 1;
+       canraw_sk(sk)->recv_own_msgs = 0;
+
+       return 0;
 }
 
 static int raw_release(struct socket *sock)
@@ -152,7 +181,8 @@ static int raw_release(struct socket *sock)
        struct sock *sk = sock->sk;
        struct net_device *dev = NULL;
 
-       DBG("socket %p, sk %p\n", sock, sk);
+       DBG("socket %p, sk %p, refcnt %d\n", sock, sk,
+           atomic_read(&sk->refcnt));
 
        if (canraw_sk(sk)->bound && canraw_sk(sk)->ifindex)
                dev = dev_get_by_index(canraw_sk(sk)->ifindex);
@@ -192,7 +222,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
 
        if (canraw_sk(sk)->bound) {
 #if 1
-               return -EOPNOTSUPP;
+               return -EINVAL;
 #else
                /* remove current bindings */
                if (canraw_sk(sk)->ifindex) {
@@ -292,7 +322,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                                return -EINVAL;
                        if (!(filter = kmalloc(optlen, GFP_KERNEL)))
                                return -ENOMEM;
-                       if (err = copy_from_user(filter, optval, optlen)) {
+                       if ((err = copy_from_user(filter, optval, optlen))) {
                                kfree(filter);
                                return err;
                        }
@@ -309,18 +339,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                                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, MASK_ALL, 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)
+               canraw_sk(sk)->filter = filter;
+               canraw_sk(sk)->count  = count;
+               if (canraw_sk(sk)->bound && count > 0)
+                       raw_add_filters(dev, sk);
+               else if (canraw_sk(sk)->bound)
                        can_rx_register(dev, 0, MASK_ALL, raw_rcv, sk, IDENT);
 
                if (dev)
@@ -332,7 +359,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
                if (optlen) {
                        if (optlen != sizeof(err_mask))
                                return -EINVAL;
-                       if (err = copy_from_user(&err_mask, optval, optlen)) {
+                       if ((err = copy_from_user(&err_mask, optval, optlen))) {
                                return err;
                        }
                }
@@ -389,8 +416,10 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
                                return -EFAULT;
                } else
                        len = 0;
+
                if (put_user(len, optlen))
                        return -EFAULT;
+
                break;
 
        case CAN_RAW_ERR_FILTER:
@@ -475,11 +504,12 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, int size,
                return err;
        }
        skb->dev = dev;
+       skb->sk  = sk;
 
        DBG("sending skbuff to interface %d\n", ifindex);
        DBG_SKB(skb);
 
-       err = can_send(skb);
+       err = can_send(skb, canraw_sk(sk)->loopback);
 
        dev_put(dev);
 
@@ -539,6 +569,14 @@ static void raw_rcv(struct sk_buff *skb, void *data)
        DBG("received skbuff %p, sk %p\n", skb, sk);
        DBG_SKB(skb);
 
+       if (!canraw_sk(sk)->recv_own_msgs) {
+               if (*(struct sock **)skb->cb == sk) { /* tx sock reference */
+                       DBG("trashed own tx msg\n");
+                       kfree_skb(skb);
+                       return;
+               }
+       }
+
        addr = (struct sockaddr_can *)skb->cb;
        memset(addr, 0, sizeof(*addr));
        addr->can_family  = AF_CAN;
@@ -560,6 +598,7 @@ static void raw_notifier(unsigned long msg, void *data)
        switch (msg) {
        case NETDEV_UNREGISTER:
                canraw_sk(sk)->ifindex = 0;
+               canraw_sk(sk)->bound   = 0;
                /* fallthrough */
        case NETDEV_DOWN:
                sk->err = ENETDOWN;
@@ -569,5 +608,5 @@ static void raw_notifier(unsigned long msg, void *data)
 }
 
 
-module_init(raw_init);
-module_exit(raw_exit);
+module_init(raw_module_init);
+module_exit(raw_module_exit);