/*
- * af_can.c
+ * af_can.c - Protocol family CAN core module
+ * (used by different CAN protocol modules)
*
- * Copyright (c) 2002-2005 Volkswagen Group Electronic Research
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
*
*/
-#include <linux/config.h>
+#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include <linux/can.h>
+#include <linux/can/core.h>
#include <linux/can/version.h>
#include "af_can.h"
RCSID("$Id$");
-#define NAME "Volkswagen AG - Low Level CAN Framework (LLCF)"
#define IDENT "af_can"
-static __initdata const char banner[] = BANNER(NAME);
+static __initdata const char banner[] =
+ KERN_INFO "CAN: Controller Area Network PF_CAN core " CAN_VERSION "\n";
-MODULE_DESCRIPTION(NAME);
+MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
"Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
static rwlock_t notifier_lock = RW_LOCK_UNLOCKED;
HLIST_HEAD(rx_dev_list);
-struct dev_rcv_lists rx_alldev_list;
+static struct dev_rcv_lists rx_alldev_list;
static spinlock_t rcv_lists_lock = SPIN_LOCK_UNLOCKED;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static struct kmem_cache *rcv_cache __read_mostly;
+#else
static kmem_cache_t *rcv_cache;
+#endif
static struct packet_type can_packet = {
.type = __constant_htons(ETH_P_CAN),
extern struct timer_list stattimer; /* timer for statistics update */
extern struct s_stats stats; /* packet statistics */
extern struct s_pstats pstats; /* receive list statistics */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static void *kzalloc(size_t size, unsigned int __nocast flags)
+{
+ void *ret = kmalloc(size, flags);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
module_init(can_init);
module_exit(can_exit);
-/**************************************************/
-/* af_can module init/exit functions */
-/**************************************************/
+/*
+ * af_can module init/exit functions
+ */
static __init int can_init(void)
{
if (!rcv_cache)
return -ENOMEM;
- if (stats_timer) {
- /* statistics init */
+ /*
+ * Insert struct dev_rcv_lists for reception on all devices.
+ * This struct is zero initialized which is correct for the
+ * embedded hlist heads, the dev pointer, and the entries counter.
+ */
+
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+ spin_unlock_bh(&rcv_lists_lock);
+
+ if (stats_timer)
init_timer(&stattimer);
- }
/* procfs init */
can_init_proc();
static __exit void can_exit(void)
{
- if (stats_timer) {
- /* stop statistics timer */
+ struct dev_rcv_lists *d;
+ struct hlist_node *n, *next;
+
+ if (stats_timer)
del_timer(&stattimer);
- }
/* procfs remove */
can_remove_proc();
unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN);
+ /* remove rx_dev_list */
+ spin_lock_bh(&rcv_lists_lock);
+ hlist_del(&rx_alldev_list.list);
+ hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+ hlist_del(&d->list);
+ kfree(d);
+ }
+ spin_unlock_bh(&rcv_lists_lock);
+
kmem_cache_destroy(rcv_cache);
}
-/**************************************************/
-/* af_can protocol functions */
-/**************************************************/
+/*
+ * af_can protocol functions
+ */
-void can_proto_register(struct can_proto *cp)
+/**
+ * can_proto_register - register CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL invalid (out of range) protocol number
+ * -EBUSY protocol already in use
+ * -ENOBUF if proto_register() fails
+ **/
+
+int can_proto_register(struct can_proto *cp)
{
int proto = cp->protocol;
+ int err = 0;
+
if (proto < 0 || proto >= CAN_NPROTO) {
- printk(KERN_ERR "CAN: protocol number %d out of range\n", proto);
- return;
+ printk(KERN_ERR "CAN: protocol number %d out "
+ "of range\n", proto);
+ return -EINVAL;
}
if (proto_tab[proto]) {
- printk(KERN_ERR "CAN: protocol %d already registered\n", proto);
- return;
+ printk(KERN_ERR "CAN: protocol %d already "
+ "registered\n", proto);
+ return -EBUSY;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
- if (proto_register(cp->prot, 0) != 0) {
- return;
- }
+ err = proto_register(cp->prot, 0);
+ if (err < 0)
+ return err;
#endif
proto_tab[proto] = cp;
- /* use our generic ioctl function if the module doesn't bring its own */
+ /* use generic ioctl function if the module doesn't bring its own */
if (!cp->ops->ioctl)
cp->ops->ioctl = can_ioctl;
+
+ return err;
}
+EXPORT_SYMBOL(can_proto_register);
-void can_proto_unregister(struct can_proto *cp)
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ * 0 on success
+ * -ESRCH protocol number was not registered
+ **/
+
+int can_proto_unregister(struct can_proto *cp)
{
int proto = cp->protocol;
+
if (!proto_tab[proto]) {
printk(KERN_ERR "CAN: protocol %d is not registered\n", proto);
- return;
+ return -ESRCH;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
proto_unregister(cp->prot);
#endif
proto_tab[proto] = NULL;
+
+ return 0;
}
+EXPORT_SYMBOL(can_proto_unregister);
-void can_dev_register(struct net_device *dev,
- void (*func)(unsigned long msg, void *), void *data)
+/**
+ * can_dev_register - subscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on status change
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Invokes the callback function with the status 'msg' and the given
+ * parameter 'data' on a status change of the given CAN network device.
+ *
+ * Return:
+ * 0 on success
+ * -ENOMEM on missing mem to create subscription entry
+ * -ENODEV unknown device
+ **/
+
+int can_dev_register(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
{
struct notifier *n;
DBG("called for %s\n", dev->name);
- if (!(n = kmalloc(sizeof(*n), GFP_KERNEL)))
- return;
+ if (!dev || dev->type != ARPHRD_CAN)
+ return -ENODEV;
+
+ n = kmalloc(sizeof(*n), GFP_KERNEL);
+ if (!n)
+ return -ENOMEM;
n->dev = dev;
n->func = func;
write_lock(¬ifier_lock);
list_add(&n->list, ¬ifier_list);
write_unlock(¬ifier_lock);
+
+ return 0;
}
+EXPORT_SYMBOL(can_dev_register);
-void can_dev_unregister(struct net_device *dev,
- void (*func)(unsigned long msg, void *), void *data)
+/**
+ * can_dev_unregister - unsubscribe notifier for CAN device status changes
+ * @dev: pointer to netdevice
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Removes subscription entry depending on given (subscription) values.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ **/
+
+int can_dev_unregister(struct net_device *dev,
+ void (*func)(unsigned long msg, void *), void *data)
{
struct notifier *n, *next;
+ int ret = -EINVAL;
DBG("called for %s\n", dev->name);
if (n->dev == dev && n->func == func && n->data == data) {
list_del(&n->list);
kfree(n);
+ ret = 0;
break;
}
}
write_unlock(¬ifier_lock);
+
+ return ret;
}
+EXPORT_SYMBOL(can_dev_unregister);
-/**************************************************/
-/* af_can socket functions */
-/**************************************************/
+/*
+ * af_can socket functions
+ */
static void can_sock_destruct(struct sock *sk)
{
{
struct sock *sk;
struct can_proto *cp;
- int ret;
+ char module_name[30];
+ int ret = 0;
DBG("socket %p, type %d, proto %d\n", sock, sock->type, 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 "CAN: request_module(%s) not implemented.\n",
- module_name);
+ printk(KERN_INFO "CAN: request_module(%s) not"
+ " implemented.\n", module_name);
}
/* check for success and correct type */
- if (!(cp = proto_tab[protocol]) || cp->type != sock->type)
+ cp = proto_tab[protocol];
+ if (!cp || cp->type != sock->type)
return -EPROTONOSUPPORT;
if (cp->capability >= 0 && !capable(cp->capability))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
if (!sk)
- goto oom;
+ return -ENOMEM;
#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;
+ return -ENOMEM;
+
+ if (cp->obj_size) {
+ sk->sk_protinfo = kmalloc(cp->obj_size, GFP_KERNEL);
+ if (!sk->sk_protinfo) {
+ sk_free(sk);
+ return -ENOMEM;
+ }
}
sk_set_owner(sk, proto_tab[protocol]->owner);
#endif
DBG("created sock: %p\n", sk);
- ret = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
if (sk->sk_prot->init)
ret = sk->sk_prot->init(sk);
if (cp->init)
ret = cp->init(sk);
#endif
+
if (ret) {
- /* we must release sk */
+ /* release sk on errors */
sock_orphan(sk);
sock_put(sk);
- return ret;
}
- return 0;
-
- oom:
- return -ENOMEM;
+ return ret;
}
static int can_notifier(struct notifier_block *nb,
{
struct net_device *dev = (struct net_device *)data;
struct notifier *n;
+ struct dev_rcv_lists *d;
+ int i;
DBG("called for %s, msg = %lu\n", dev->name, msg);
-#if 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 */
+ /*
+ * create new dev_rcv_lists for this device
+ *
+ * N.B. zeroing the struct is the correct initialization
+ * for the embedded hlist_head structs.
+ * Another list type, e.g. list_head, would require
+ * explicit initialization.
+ */
DBG("creating new dev_rcv_lists for %s\n", dev->name);
- if (!(d = kmalloc(sizeof(*d), GFP_KERNEL))) {
- printk(KERN_ERR "CAN: allocation of receive list failed\n");
+
+ d = kzalloc(sizeof(*d),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (!d) {
+ printk(KERN_ERR "CAN: allocation of receive "
+ "list failed\n");
return NOTIFY_DONE;
}
- /* N.B. zeroing the struct is the correct initialization
- for the embedded hlist_head structs.
- Another list type, e.g. list_head, would require
- explicit initialization. */
- memset(d, 0, sizeof(*d));
d->dev = dev;
- spin_lock(&rcv_lists_lock);
+ spin_lock_bh(&rcv_lists_lock);
hlist_add_head_rcu(&d->list, &rx_dev_list);
- spin_unlock(&rcv_lists_lock);
+ spin_unlock_bh(&rcv_lists_lock);
break;
case NETDEV_UNREGISTER:
- spin_lock(&rcv_lists_lock);
-
- if (!(d = find_dev_rcv_lists(dev))) {
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ hlist_del_rcu(&d->list);
+
+ /* remove all receivers hooked at this netdevice */
+ can_rx_delete_all(&d->rx_err);
+ can_rx_delete_all(&d->rx_all);
+ can_rx_delete_all(&d->rx_fil);
+ can_rx_delete_all(&d->rx_inv);
+ can_rx_delete_all(&d->rx_eff);
+ for (i = 0; i < 2048; i++)
+ can_rx_delete_all(&d->rx_sff[i]);
+ } else
printk(KERN_ERR "CAN: notifier: receive list not "
"found for dev %s\n", dev->name);
- goto unreg_out;
- }
-
- hlist_del_rcu(&d->list);
- /* remove all receivers hooked at this netdevice */
- can_rx_delete_all(&d->rx_err);
- can_rx_delete_all(&d->rx_all);
- can_rx_delete_all(&d->rx_fil);
- can_rx_delete_all(&d->rx_inv);
- can_rx_delete_all(&d->rx_eff);
- for (i = 0; i < 2048; i++)
- can_rx_delete_all(&d->rx_sff[i]);
-
- unreg_out:
- spin_unlock(&rcv_lists_lock);
+ spin_unlock_bh(&rcv_lists_lock);
if (d)
call_rcu(&d->rcu, can_rcv_lists_delete);
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;
return dev_ioctl(cmd, (void __user *)arg);
#endif
}
- return 0;
}
-/**************************************************/
-/* af_can tx path */
-/**************************************************/
+/*
+ * af_can tx path
+ */
+
+/**
+ * can_send - transmit a CAN frame (optional with local loopback)
+ * @skb: pointer to socket buffer with CAN frame in data section
+ * @loop: loopback for listeners on local CAN sockets (recommended default!)
+ *
+ * Return:
+ * 0 on success
+ * -ENETDOWN when the selected interface is down
+ * -ENOBUFS on full driver queue (see net_xmit_errno())
+ **/
int can_send(struct sk_buff *skb, int loop)
{
int err;
- if (loop) { /* local loopback (default) */
- *(struct sock **)skb->cb = skb->sk; /* tx sock reference */
+ if (loop) {
+ /* local loopback of sent CAN frames (default) */
+
+ /* indication for the CAN driver: do loopback */
+ *(struct sock **)skb->cb = skb->sk;
+
+ /*
+ * The reference to the originating sock may be also required
+ * by the receiving socket to indicate (and ignore) his own
+ * sent data. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+ */
/* interface not capabable to do the loopback itself? */
if (!(skb->dev->flags & IFF_LOOPBACK)) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+ /* perform the local loopback here */
newskb->protocol = htons(ETH_P_CAN);
newskb->ip_summed = CHECKSUM_UNNECESSARY;
- netif_rx(newskb); /* perform local loopback here */
+ netif_rx(newskb);
}
- } else
- *(struct sock **)skb->cb = NULL; /* no loopback required */
+ } else {
+ /* indication for the CAN driver: no loopback required */
+ *(struct sock **)skb->cb = NULL;
+ }
if (!(skb->dev->flags & IFF_UP))
- err = -ENETDOWN;
- else if ((err = dev_queue_xmit(skb)) > 0) /* send to netdevice */
+ return -ENETDOWN;
+
+ /* send to netdevice */
+ err = dev_queue_xmit(skb);
+ if (err > 0)
err = net_xmit_errno(err);
/* update statistics */
return err;
}
+EXPORT_SYMBOL(can_send);
-/**************************************************/
-/* af_can rx path */
-/**************************************************/
+/*
+ * af_can rx path
+ */
+
+/**
+ * can_rx_register - subscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier (see description)
+ * @mask: CAN mask (see description)
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ * @ident: string for calling module indentification
+ *
+ * Description:
+ * Invokes the callback function with the received sk_buff and the given
+ * parameter 'data' on a matching receive filter. A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * Return:
+ * 0 on success
+ * -ENOMEM on missing cache mem to create subscription entry
+ * -ENODEV unknown device
+ **/
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data,
DBG("dev %p, id %03X, mask %03X, callback %p, data %p, ident %s\n",
dev, can_id, mask, func, data, ident);
- if (!(r = kmem_cache_alloc(rcv_cache, GFP_KERNEL))) {
- ret = -ENOMEM;
- goto out;
- }
+ r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock_bh(&rcv_lists_lock);
+
+ d = find_dev_rcv_lists(dev);
+ if (d) {
+ rl = find_rcv_list(&can_id, &mask, d);
+
+ r->can_id = can_id;
+ r->mask = mask;
+ r->matches = 0;
+ r->func = func;
+ r->data = data;
+ r->ident = ident;
- spin_lock(&rcv_lists_lock);
+ hlist_add_head_rcu(&r->list, rl);
+ d->entries++;
- if (!(d = find_dev_rcv_lists(dev))) {
+ pstats.rcv_entries++;
+ if (pstats.rcv_entries_max < pstats.rcv_entries)
+ pstats.rcv_entries_max = pstats.rcv_entries;
+ } else {
DBG("receive list not found for dev %s, id %03X, mask %03X\n",
- dev->name, can_id, mask);
+ DNAME(dev), can_id, mask);
kmem_cache_free(rcv_cache, r);
ret = -ENODEV;
- goto out_unlock;
}
- rl = find_rcv_list(&can_id, &mask, d);
-
- r->can_id = can_id;
- r->mask = mask;
- r->matches = 0;
- r->func = func;
- r->data = data;
- r->ident = ident;
-
- hlist_add_head_rcu(&r->list, rl);
- d->entries++;
+ spin_unlock_bh(&rcv_lists_lock);
- pstats.rcv_entries++;
- if (pstats.rcv_entries_max < pstats.rcv_entries)
- pstats.rcv_entries_max = pstats.rcv_entries;
-
- out_unlock:
- spin_unlock(&rcv_lists_lock);
- out:
return ret;
}
+EXPORT_SYMBOL(can_rx_register);
static void can_rcv_lists_delete(struct rcu_head *rp)
{
}
}
+/**
+ * can_rx_unregister - unsubscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier
+ * @mask: CAN mask
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ * Removes subscription entry depending on given (subscription) values.
+ *
+ * Return:
+ * 0 on success
+ * -EINVAL on missing subscription entry
+ * -ENODEV unknown device
+ **/
+
int can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data)
{
- struct receiver *r;
+ struct receiver *r = NULL;
struct hlist_head *rl;
struct hlist_node *next;
struct dev_rcv_lists *d;
DBG("dev %p, id %03X, mask %03X, callback %p, data %p\n",
dev, can_id, mask, func, data);
- r = NULL;
-
- spin_lock(&rcv_lists_lock);
+ spin_lock_bh(&rcv_lists_lock);
- if (!(d = find_dev_rcv_lists(dev))) {
+ d = find_dev_rcv_lists(dev);
+ if (!d) {
DBG("receive list not found for dev %s, id %03X, mask %03X\n",
- dev->name, can_id, mask);
+ DNAME(dev), can_id, mask);
ret = -ENODEV;
goto out;
}
rl = find_rcv_list(&can_id, &mask, d);
- /* Search the receiver list for the item to delete. This should
- * exist, since no receiver may be unregistered that hasn't
- * been registered before.
+ /*
+ * Search the receiver list for the item to delete. This should
+ * exist, since no receiver may be unregistered that hasn't
+ * been registered before.
*/
hlist_for_each_entry(r, next, rl, list) {
break;
}
- /* Check for bug in CAN protocol implementations:
- * If no matching list item was found, the list cursor variable next
- * will be NULL, while r will point to the last item of the list.
+ /*
+ * Check for bug in CAN protocol implementations:
+ * If no matching list item was found, the list cursor variable next
+ * will be NULL, while r will point to the last item of the list.
*/
if (!next) {
DBG("receive list entry not found for "
- "dev %s, id %03X, mask %03X\n", dev->name, can_id, mask);
+ "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
ret = -EINVAL;
r = NULL;
goto out;
pstats.rcv_entries--;
out:
- spin_unlock(&rcv_lists_lock);
+ spin_unlock_bh(&rcv_lists_lock);
/* schedule the receiver item for deletion */
if (r)
return ret;
}
+EXPORT_SYMBOL(can_rx_unregister);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
matches = can_rcv_filter(&rx_alldev_list, skb);
/* find receive list for this device */
- if ((d = find_dev_rcv_lists(dev)))
+ d = find_dev_rcv_lists(dev);
+ if (d)
matches += can_rcv_filter(d, skb);
rcu_read_unlock();
struct dev_rcv_lists *d;
struct hlist_node *n;
- /* find receive list for this device */
-
- if (!dev)
- return &rx_alldev_list;
-
- /* The hlist_for_each_entry*() macros curse through the list
- * using the pointer variable n and set d to the containing
- * struct in each list iteration. Therefore, after list
- * iteration, d is unmodified when the list is empty, and it
- * points to last list element, when the list is non-empty
- * but no match in the loop body is found. I.e. d is *not*
- * NULL when no match is found. We can, however, use the
- * cursor variable n to decide if a match was found.
+ /*
+ * find receive list for this device
+ *
+ * The hlist_for_each_entry*() macros curse through the list
+ * using the pointer variable n and set d to the containing
+ * struct in each list iteration. Therefore, after list
+ * iteration, d is unmodified when the list is empty, and it
+ * points to last list element, when the list is non-empty
+ * but no match in the loop body is found. I.e. d is *not*
+ * NULL when no match is found. We can, however, use the
+ * cursor variable n to decide if a match was found.
*/
- hlist_for_each_entry(d, n, &rx_dev_list, list)
+ hlist_for_each_entry(d, n, &rx_dev_list, list) {
if (d->dev == dev)
break;
+ }
return n ? d : NULL;
}
static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
struct dev_rcv_lists *d)
{
- canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking 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 */
-
- /* make some paranoic operations */
- if (*can_id & CAN_EFF_FLAG)
- *mask &= (CAN_EFF_MASK | eff | rtr);
- else
- *mask &= (CAN_SFF_MASK | rtr);
+ canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
- *can_id &= *mask;
-
- if (err) /* error frames */
+ if (*mask & CAN_ERR_FLAG) { /* filter error frames */
+ *mask &= CAN_ERR_MASK; /* clear CAN_ERR_FLAG in list entry */
return &d->rx_err;
+ }
- if (inv) /* inverse can_id/can_mask filter and RTR */
- return &d->rx_inv;
+ /* ensure valid values in can_mask */
+ if (*mask & CAN_EFF_FLAG)
+ *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+ else
+ *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
- if (*can_id & CAN_RTR_FLAG) /* positive filter RTR */
- return &d->rx_fil;
+ *can_id &= *mask; /* reduce condition testing at receive time */
- if (!(*mask)) /* mask == 0 => no filter */
+ if (inv) /* inverse can_id/can_mask filter */
+ return &d->rx_inv;
+
+ if (!(*mask)) /* mask == 0 => no condition testing at receive time */
return &d->rx_all;
+ /* use extra filterset for the subscription of exactly *ONE* can_id */
if (*can_id & CAN_EFF_FLAG) {
- if (*mask == CAN_EFF_MASK) /* filter exact EFF can_id */
+ if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+ /* RFC: a use-case for hash-tables in the future? */
return &d->rx_eff;
+ }
} else {
- if (*mask == CAN_SFF_MASK) /* filter exact SFF can_id */
+ if (*mask == CAN_SFF_MASK)
return &d->rx_sff[*can_id];
}
- return &d->rx_fil; /* filter via can_id/can_mask */
+ return &d->rx_fil; /* default: filter via can_id/can_mask */
}
-/**************************************************/
-/* af_can utility stuff */
-/**************************************************/
+/*
+ * af_can utility stuff
+ */
+
+/**
+ * timeval2jiffies - calculate jiffies from timeval including optional round up
+ * @tv: pointer to timeval
+ * @round_up: return at least 1 jiffie
+ *
+ * Return:
+ * calculated jiffies (max: ULONG_MAX)
+ **/
unsigned long timeval2jiffies(struct timeval *tv, int round_up)
{
unsigned long sec = tv->tv_sec;
unsigned long usec = tv->tv_usec;
- if (sec > ULONG_MAX / HZ) /* check for overflow */
+ /* check for overflow */
+ if (sec > ULONG_MAX / HZ)
return ULONG_MAX;
- if (round_up) /* any usec below one HZ? */
- usec += 1000000 / HZ - 1; /* pump it up */
+ /* any usec below one HZ? => pump it up */
+ if (round_up)
+ usec += 1000000 / HZ - 1;
jif = usec / (1000000 / HZ);
- if (sec * HZ > ULONG_MAX - jif) /* check for overflow */
+ /* check for overflow */
+ if (sec * HZ > ULONG_MAX - jif)
return ULONG_MAX;
else
return jif + sec * HZ;
}
+EXPORT_SYMBOL(timeval2jiffies);
-
-/**************************************************/
-/* af_can debugging stuff */
-/**************************************************/
+/*
+ * af_can debugging stuff
+ */
#ifdef CONFIG_CAN_DEBUG_CORE
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ **/
+
void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
{
va_list ap;
buf[len++] = ' ';
va_end(ap);
- if ((dlc = cf->can_dlc) > 8)
+ dlc = cf->can_dlc;
+ if (dlc > 8)
dlc = 8;
if (cf->can_id & CAN_EFF_FLAG)
buf[len] = '\0';
printk(buf);
}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ **/
void can_debug_skb(struct sk_buff *skb)
{
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);