* 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'.
+ * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 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.
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
*
* The provided data structures and external interfaces from this code
* are not restricted to be used by modules with a GPL compatible license.
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
+#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include <linux/seq_file.h>
+#endif
#include <linux/uio.h>
-#include <linux/poll.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/bcm.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/bcm.h>
#include <net/sock.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include "compat.h"
+#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
+/*
+ * To send multiple CAN frame content within TX_SETUP or to filter
+ * CAN messages with multiplex index within RX_SETUP, the number of
+ * different filters is limited to 256 due to the one byte index value.
+ */
+#define MAX_NFRAMES 256
+
/* use of last_frames[index].can_dlc */
#define RX_RECV 0x40 /* received data for this element */
#define RX_THR 0x80 /* element not been sent due to throttle feature */
#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
/* get best masking value for can_rx_register() for a given single can_id */
-#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
- (CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
+#define REGMASK(id) ((id & CAN_EFF_FLAG) ? \
+ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \
+ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
-#define IDENT "bcm"
#define CAN_BCM_VERSION CAN_VERSION
static __initdata const char banner[] = KERN_INFO
- "can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+ "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n";
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_ALIAS("can-proto-2");
-#ifdef CONFIG_CAN_DEBUG_CORE
-static int debug;
-module_param(debug, int, S_IRUGO);
-MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#error This code only supports Kernel versions 2.6.22+
+#error For older 2.6 Kernels please use bcm-prior-2-6-22.c instead of bcm.c
#endif
/* easy access to can_frame payload */
struct list_head list;
int ifindex;
canid_t can_id;
- int flags;
- unsigned long j_ival1, j_ival2, j_lastmsg;
+ u32 flags;
unsigned long frames_abs, frames_filtered;
- struct timer_list timer, thrtimer;
struct timeval ival1, ival2;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
- ktime_t rx_stamp;
-#else
- struct timeval rx_stamp;
-#endif
+ struct hrtimer timer, thrtimer;
+ struct tasklet_struct tsklet, thrtsklet;
+ ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
int rx_ifindex;
- int count;
- int nframes;
- int currframe;
+ u32 count;
+ u32 nframes;
+ u32 currframe;
struct can_frame *frames;
struct can_frame *last_frames;
struct can_frame sframe;
static struct proc_dir_entry *proc_dir;
-#ifdef CONFIG_CAN_BCM_USER
-#define BCM_CAP (-1)
-#else
-#define BCM_CAP CAP_NET_RAW
-#endif
-
struct bcm_sock {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
struct sock sk;
-#else
- struct sock *sk;
-#endif
int bound;
int ifindex;
struct notifier_block notifier;
struct list_head tx_ops;
unsigned long dropped_usr_msgs;
struct proc_dir_entry *bcm_proc_read;
- char procname [9]; /* pointer printed in ASCII with \0 */
+ char procname [32]; /* inode number in decimal with \0 */
};
static inline struct bcm_sock *bcm_sk(const struct sock *sk)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
return (struct bcm_sock *)sk;
-#else
- return (struct bcm_sock *)sk->sk_protinfo;
-#endif
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
-static void *kzalloc(size_t size, unsigned int __nocast flags)
+#define CFSIZ sizeof(struct can_frame)
+#define OPSIZ sizeof(struct bcm_op)
+#define MHSIZ sizeof(struct bcm_msg_head)
+
+/*
+ * procfs functions
+ */
+static char *bcm_proc_getifname(char *result, int ifindex)
{
- void *ret = kmalloc(size, flags);
+ struct net_device *dev;
- if (ret)
- memset(ret, 0, size);
+ if (!ifindex)
+ return "any";
- return ret;
-}
+ read_lock(&dev_base_lock);
+ dev = __dev_get_by_index(&init_net, ifindex);
+ if (dev)
+ strcpy(result, dev->name);
+ else
+ strcpy(result, "???");
+ read_unlock(&dev_base_lock);
-static inline void skb_get_timestamp(const struct sk_buff *skb,
- struct timeval *stamp)
-{
- stamp->tv_sec = skb->stamp.tv_sec;
- stamp->tv_usec = skb->stamp.tv_usec;
+ return result;
}
-static inline void skb_set_timestamp(struct sk_buff *skb,
- const struct timeval *stamp)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+static int bcm_proc_show(struct seq_file *m, void *v)
{
- skb->stamp.tv_sec = stamp->tv_sec;
- skb->stamp.tv_usec = stamp->tv_usec;
-}
-#endif
+ char ifname[IFNAMSIZ];
+ struct sock *sk = (struct sock *)m->private;
+ struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_op *op;
-#define CFSIZ sizeof(struct can_frame)
-#define OPSIZ sizeof(struct bcm_op)
-#define MHSIZ sizeof(struct bcm_msg_head)
+ seq_printf(m, ">>> socket %p", sk->sk_socket);
+ seq_printf(m, " / sk %p", sk);
+ seq_printf(m, " / bo %p", bo);
+ seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
+ seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex));
+ seq_printf(m, " <<<\n");
-/*
- * rounded_tv2jif - calculate jiffies from timeval including optional up
- * @tv: pointer to timeval
- *
- * Description:
- * Unlike timeval_to_jiffies() provided in include/linux/jiffies.h, this
- * function is intentionally more relaxed on precise timer ticks to get
- * exact one jiffy for requested 1000us on a 1000HZ machine.
- * This code is to be removed when upgrading to kernel hrtimer.
- *
- * Return:
- * calculated jiffies (max: ULONG_MAX)
- */
-static unsigned long rounded_tv2jif(const struct timeval *tv)
-{
- unsigned long sec = tv->tv_sec;
- unsigned long usec = tv->tv_usec;
- unsigned long jif;
+ list_for_each_entry(op, &bo->rx_ops, list) {
- if (sec > ULONG_MAX / HZ)
- return ULONG_MAX;
+ unsigned long reduction;
- /* round up to get at least the requested time */
- usec += 1000000 / HZ - 1;
+ /* print only active entries & prevent division by zero */
+ if (!op->frames_abs)
+ continue;
- jif = usec / (1000000 / HZ);
+ seq_printf(m, "rx_op: %03X %-5s ",
+ op->can_id, bcm_proc_getifname(ifname, op->ifindex));
+ seq_printf(m, "[%u]%c ", op->nframes,
+ (op->flags & RX_CHECK_DLC)?'d':' ');
+ if (op->kt_ival1.tv64)
+ seq_printf(m, "timeo=%lld ",
+ (long long)
+ ktime_to_us(op->kt_ival1));
- if (sec * HZ > ULONG_MAX - jif)
- return ULONG_MAX;
+ if (op->kt_ival2.tv64)
+ seq_printf(m, "thr=%lld ",
+ (long long)
+ ktime_to_us(op->kt_ival2));
- return jif + sec * HZ;
-}
+ seq_printf(m, "# recv %ld (%ld) => reduction: ",
+ op->frames_filtered, op->frames_abs);
-/*
- * procfs functions
- */
-static char *bcm_proc_getifname(int ifindex)
-{
- struct net_device *dev;
+ reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
- if (!ifindex)
- return "any";
+ seq_printf(m, "%s%ld%%\n",
+ (reduction == 100)?"near ":"", reduction);
+ }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- dev = __dev_get_by_index(&init_net, ifindex); /* no usage counting */
-#else
- dev = __dev_get_by_index(ifindex); /* no usage counting */
-#endif
- if (dev)
- return dev->name;
+ list_for_each_entry(op, &bo->tx_ops, list) {
+
+ seq_printf(m, "tx_op: %03X %s [%u] ",
+ op->can_id,
+ bcm_proc_getifname(ifname, op->ifindex),
+ op->nframes);
+
+ if (op->kt_ival1.tv64)
+ seq_printf(m, "t1=%lld ",
+ (long long) ktime_to_us(op->kt_ival1));
- return "???";
+ if (op->kt_ival2.tv64)
+ seq_printf(m, "t2=%lld ",
+ (long long) ktime_to_us(op->kt_ival2));
+
+ seq_printf(m, "# sent %ld\n", op->frames_abs);
+ }
+ seq_putc(m, '\n');
+ return 0;
}
+static int bcm_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations bcm_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#else
static int bcm_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
+ char ifname[IFNAMSIZ];
int len = 0;
struct sock *sk = (struct sock *)data;
struct bcm_sock *bo = bcm_sk(sk);
len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
bo->dropped_usr_msgs);
len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
- bcm_proc_getifname(bo->ifindex));
+ bcm_proc_getifname(ifname, bo->ifindex));
len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
list_for_each_entry(op, &bo->rx_ops, list) {
len += snprintf(page + len, PAGE_SIZE - len,
"rx_op: %03X %-5s ",
- op->can_id, bcm_proc_getifname(op->ifindex));
+ op->can_id, bcm_proc_getifname(ifname, op->ifindex));
len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
op->nframes,
(op->flags & RX_CHECK_DLC)?'d':' ');
- if (op->j_ival1)
+ if (op->kt_ival1.tv64)
len += snprintf(page + len, PAGE_SIZE - len,
- "timeo=%ld ", op->j_ival1);
+ "timeo=%lld ",
+ (long long)
+ ktime_to_us(op->kt_ival1));
- if (op->j_ival2)
+ if (op->kt_ival2.tv64)
len += snprintf(page + len, PAGE_SIZE - len,
- "thr=%ld ", op->j_ival2);
+ "thr=%lld ",
+ (long long)
+ ktime_to_us(op->kt_ival2));
len += snprintf(page + len, PAGE_SIZE - len,
"# recv %ld (%ld) => reduction: ",
len += snprintf(page + len, PAGE_SIZE - len,
"tx_op: %03X %s [%d] ",
- op->can_id, bcm_proc_getifname(op->ifindex),
+ op->can_id,
+ bcm_proc_getifname(ifname, op->ifindex),
op->nframes);
- if (op->j_ival1)
- len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ",
- op->j_ival1);
- if (op->j_ival2)
- len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ",
- op->j_ival2);
+ if (op->kt_ival1.tv64)
+ len += snprintf(page + len, PAGE_SIZE - len, "t1=%lld ",
+ (long long) ktime_to_us(op->kt_ival1));
+
+ if (op->kt_ival2.tv64)
+ len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ",
+ (long long) ktime_to_us(op->kt_ival2));
len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
op->frames_abs);
*eof = 1;
return len;
}
+#endif
/*
* bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
struct net_device *dev;
struct can_frame *cf = &op->frames[op->currframe];
- DBG_FRAME("BCM: bcm_can_tx: sending frame", cf);
-
/* no target device? => exit */
if (!op->ifindex)
return;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, op->ifindex);
-#else
- dev = dev_get_by_index(op->ifindex);
-#endif
if (!dev) {
/* RFC: should this bcm_op remove itself here? */
return;
}
- skb = alloc_skb(CFSIZ,
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-
+ skb = alloc_skb(CFSIZ, gfp_any());
if (!skb)
goto out;
struct can_frame *firstframe;
struct sockaddr_can *addr;
struct sock *sk = op->sk;
- int datalen = head->nframes * CFSIZ;
+ unsigned int datalen = head->nframes * CFSIZ;
int err;
- skb = alloc_skb(sizeof(*head) + datalen,
- in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ skb = alloc_skb(sizeof(*head) + datalen, gfp_any());
if (!skb)
return;
if (head->nframes) {
/* can_frames starting here */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
- firstframe = (struct can_frame *) skb_tail_pointer(skb);
-#else
- firstframe = (struct can_frame *) skb->tail;
-#endif
+ firstframe = (struct can_frame *)skb_tail_pointer(skb);
memcpy(skb_put(skb, datalen), frames, datalen);
if (has_timestamp) {
/* restore rx timestamp */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
skb->tstamp = op->rx_stamp;
-#else
- skb_set_timestamp(skb, &op->rx_stamp);
-#endif
}
/*
if (err < 0) {
struct bcm_sock *bo = bcm_sk(sk);
- DBG("sock_queue_rcv_skb failed: %d\n", err);
kfree_skb(skb);
/* don't care about overflows in this statistic */
bo->dropped_usr_msgs++;
}
}
-/*
- * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
- */
-static void bcm_tx_timeout_handler(unsigned long data)
+static void bcm_tx_timeout_tsklet(unsigned long data)
{
struct bcm_op *op = (struct bcm_op *)data;
+ struct bcm_msg_head msg_head;
- DBG("Called with bcm_op %p\n", op);
-
- if (op->j_ival1 && (op->count > 0)) {
+ if (op->kt_ival1.tv64 && (op->count > 0)) {
op->count--;
if (!op->count && (op->flags & TX_COUNTEVT)) {
- struct bcm_msg_head msg_head;
/* create notification to user */
- DBG("sending TX_EXPIRED for can_id %03X\n",
- op->can_id);
-
msg_head.opcode = TX_EXPIRED;
msg_head.flags = op->flags;
msg_head.count = op->count;
}
}
- 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);
+ if (op->kt_ival1.tv64 && (op->count > 0)) {
/* send (next) frame */
bcm_can_tx(op);
+ hrtimer_start(&op->timer,
+ ktime_add(ktime_get(), op->kt_ival1),
+ HRTIMER_MODE_ABS);
} 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);
+ if (op->kt_ival2.tv64) {
/* send (next) frame */
bcm_can_tx(op);
-
- } else
- DBG("no timer restart\n");
+ hrtimer_start(&op->timer,
+ ktime_add(ktime_get(), op->kt_ival2),
+ HRTIMER_MODE_ABS);
+ }
}
+}
- return;
+/*
+ * bcm_tx_timeout_handler - performs cyclic CAN frame transmissions
+ */
+static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+
+ tasklet_schedule(&op->tsklet);
+
+ return HRTIMER_NORESTART;
}
/*
{
struct bcm_msg_head head;
- op->j_lastmsg = jiffies;
-
/* update statistics */
op->frames_filtered++;
if (op->frames_filtered > ULONG_MAX/100)
op->frames_filtered = op->frames_abs = 0;
- DBG("setting j_lastmsg to 0x%08X for rx_op %p\n",
- (unsigned int) op->j_lastmsg, op);
- DBG("sending notification\n");
+ /* this element is not throttled anymore */
+ data->can_dlc &= (BCM_CAN_DLC_MASK|RX_RECV);
head.opcode = RX_CHANGED;
head.flags = op->flags;
bcm_send_to_user(op, &head, data, 1);
}
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,25)
+/* is part of linux/hrtimer.h since 2.6.26 */
+static inline int hrtimer_callback_running(struct hrtimer *timer)
+{
+ return timer->state & HRTIMER_STATE_CALLBACK;
+}
+#endif
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,22)
+static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier)
+{
+ return ktime_to_us(ktime_sub(later, earlier));
+}
+#endif
/*
* bcm_rx_update_and_send - process a detected relevant receive content change
* 1. update the last received data
*/
static void bcm_rx_update_and_send(struct bcm_op *op,
struct can_frame *lastdata,
- struct can_frame *rxdata)
+ const struct can_frame *rxdata)
{
- unsigned long nexttx = op->j_lastmsg + op->j_ival2;
-
memcpy(lastdata, rxdata, CFSIZ);
- /* mark as used */
- lastdata->can_dlc |= RX_RECV;
-
- /* throttle bcm_rx_changed ? */
- if ((op->thrtimer.expires) ||
- ((op->j_ival2) && (nexttx > jiffies))) {
- /* we are already waiting OR we have to start waiting */
+ /* mark as used and throttled by default */
+ lastdata->can_dlc |= (RX_RECV|RX_THR);
- /* mark as 'throttled' */
- lastdata->can_dlc |= RX_THR;
-
- if (!(op->thrtimer.expires)) {
- /* start the timer only the first time */
- op->thrtimer.expires = nexttx;
- add_timer(&op->thrtimer);
+ /* throtteling mode inactive ? */
+ if (!op->kt_ival2.tv64) {
+ /* send RX_CHANGED to the user immediately */
+ bcm_rx_changed(op, lastdata);
+ return;
+ }
- DBG("adding thrtimer. func=%p data=%p exp=0x%08X\n",
- op->thrtimer.function,
- (char *) op->thrtimer.data,
- (unsigned int) op->thrtimer.expires);
- }
+ /* with active throttling timer we are just done here */
+ if (hrtimer_active(&op->thrtimer))
+ return;
- } else {
- /* send RX_CHANGED to the user immediately */
- bcm_rx_changed(op, rxdata);
+ /* first receiption with enabled throttling mode */
+ if (!op->kt_lastmsg.tv64)
+ goto rx_changed_settime;
+
+ /* got a second frame inside a potential throttle period? */
+ if (ktime_us_delta(ktime_get(), op->kt_lastmsg) <
+ ktime_to_us(op->kt_ival2)) {
+ /* do not send the saved data - only start throttle timer */
+ hrtimer_start(&op->thrtimer,
+ ktime_add(op->kt_lastmsg, op->kt_ival2),
+ HRTIMER_MODE_ABS);
+ return;
}
+
+ /* the gap was that big, that throttling was not needed here */
+rx_changed_settime:
+ bcm_rx_changed(op, lastdata);
+ op->kt_lastmsg = ktime_get();
}
/*
* bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
* received data stored in op->last_frames[]
*/
-static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
- struct can_frame *rxdata)
+static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
+ const struct can_frame *rxdata)
{
/*
* no one uses the MSBs of can_dlc for comparation,
if (!(op->last_frames[index].can_dlc & RX_RECV)) {
/* received data for the first time => send update to user */
- DBG("first time :)\n");
bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
return;
}
/* do a real check in can_frame data section */
- DBG("op->frames[index].data = 0x%016llx\n",
- GET_U64(&op->frames[index]));
- DBG("op->last_frames[index].data = 0x%016llx\n",
- GET_U64(&op->last_frames[index]));
- DBG("rxdata->data = 0x%016llx\n", GET_U64(rxdata));
-
if ((GET_U64(&op->frames[index]) & GET_U64(rxdata)) !=
(GET_U64(&op->frames[index]) & GET_U64(&op->last_frames[index]))) {
- DBG("relevant data change :)\n");
bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
return;
}
/* do a real check in can_frame dlc */
if (rxdata->can_dlc != (op->last_frames[index].can_dlc &
BCM_CAN_DLC_MASK)) {
- DBG("dlc change :)\n");
bcm_rx_update_and_send(op, &op->last_frames[index],
rxdata);
return;
}
}
- DBG("no relevant change :(\n");
}
/*
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);
- }
+ if (op->kt_ival1.tv64)
+ hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL);
}
-/*
- * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
- */
-static void bcm_rx_timeout_handler(unsigned long data)
+static void bcm_rx_timeout_tsklet(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);
-
+ /* create notification to user */
msg_head.opcode = RX_TIMEOUT;
msg_head.flags = op->flags;
msg_head.count = op->count;
msg_head.nframes = 0;
bcm_send_to_user(op, &msg_head, NULL, 0);
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
+
+ /* schedule before NET_RX_SOFTIRQ */
+ tasklet_hi_schedule(&op->tsklet);
/* no restart of the timer is done here! */
if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
/* clear received can_frames to indicate 'nothing received' */
memset(op->last_frames, 0, op->nframes * CFSIZ);
- DBG("RX_ANNOUNCE_RESTART\n");
}
+
+ return HRTIMER_NORESTART;
}
/*
- * bcm_rx_thr_handler - the time for blocked content updates is over now:
- * Check for throttled data and send it to the userspace
+ * bcm_rx_do_flush - helper for bcm_rx_thr_flush
*/
-static void bcm_rx_thr_handler(unsigned long data)
+static inline int bcm_rx_do_flush(struct bcm_op *op, int update,
+ unsigned int index)
{
- struct bcm_op *op = (struct bcm_op *)data;
- int i = 0;
+ if ((op->last_frames) && (op->last_frames[index].can_dlc & RX_THR)) {
+ if (update)
+ bcm_rx_changed(op, &op->last_frames[index]);
+ return 1;
+ }
+ return 0;
+}
- /* mark disabled / consumed timer */
- op->thrtimer.expires = 0;
+/*
+ * bcm_rx_thr_flush - Check for throttled data and send it to the userspace
+ *
+ * update == 0 : just check if throttled data is available (any irq context)
+ * update == 1 : check and send throttled data to userspace (soft_irq context)
+ */
+static int bcm_rx_thr_flush(struct bcm_op *op, int update)
+{
+ int updated = 0;
if (op->nframes > 1) {
- DBG("sending MUX RX_CHANGED for can_id %03X. op is %p\n",
- op->can_id, op);
+ unsigned int i;
+
/* 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]);
- }
- }
+ for (i = 1; i < op->nframes; i++)
+ updated += bcm_rx_do_flush(op, update, 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]);
- }
+ updated += bcm_rx_do_flush(op, update, 0);
}
+
+ return updated;
}
-/*
- * bcm_rx_handler - handle a CAN frame receiption
- */
-static void bcm_rx_handler(struct sk_buff *skb, void *data)
+static void bcm_rx_thr_tsklet(unsigned long data)
{
struct bcm_op *op = (struct bcm_op *)data;
- struct can_frame rxframe;
- int i;
- /* disable timeout */
- del_timer(&op->timer);
+ /* push the changed data to the userspace */
+ bcm_rx_thr_flush(op, 1);
+}
- DBG("Called with bcm_op %p\n", op);
+/*
+ * bcm_rx_thr_handler - the time for blocked content updates is over now:
+ * Check for throttled data and send it to the userspace
+ */
+static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
+{
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
- if (skb->len == sizeof(rxframe)) {
- memcpy(&rxframe, skb->data, sizeof(rxframe));
- /* save rx timestamp */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
- op->rx_stamp = skb->tstamp;
-#else
- skb_get_timestamp(skb, &op->rx_stamp);
-#endif
- /* save originator for recvfrom() */
- op->rx_ifindex = skb->dev->ifindex;
- /* update statistics */
- op->frames_abs++;
- kfree_skb(skb);
- DBG("got can_frame with can_id %03X\n", rxframe.can_id);
+ tasklet_schedule(&op->thrtsklet);
+ if (bcm_rx_thr_flush(op, 0)) {
+ hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+ return HRTIMER_RESTART;
} else {
- DBG("Wrong skb->len = %d\n", skb->len);
- kfree_skb(skb);
- return;
+ /* rearm throttle handling */
+ op->kt_lastmsg = ktime_set(0, 0);
+ return HRTIMER_NORESTART;
}
+}
+
+/*
+ * bcm_rx_handler - handle a CAN frame receiption
+ */
+static void bcm_rx_handler(struct sk_buff *skb, void *data)
+{
+ struct bcm_op *op = (struct bcm_op *)data;
+ const struct can_frame *rxframe = (struct can_frame *)skb->data;
+ unsigned int i;
- DBG_FRAME("BCM: bcm_rx_handler: CAN frame", &rxframe);
+ /* disable timeout */
+ hrtimer_cancel(&op->timer);
- if (op->can_id != rxframe.can_id) {
- DBG("ERROR! Got wrong can_id %03X! Expected %03X.\n",
- rxframe.can_id, op->can_id);
+ if (op->can_id != rxframe->can_id)
return;
- }
- if (op->flags & RX_RTR_FRAME) {
- /* send reply for RTR-request */
- DBG("RTR-request\n");
+ /* save rx timestamp */
+ op->rx_stamp = skb->tstamp;
+ /* save originator for recvfrom() */
+ op->rx_ifindex = skb->dev->ifindex;
+ /* update statistics */
+ op->frames_abs++;
- /* send op->frames[0] to CAN device */
+ if (op->flags & RX_RTR_FRAME) {
+ /* send reply for RTR-request (placed in op->frames[0]) */
bcm_can_tx(op);
return;
}
if (op->flags & RX_FILTER_ID) {
/* the easiest case */
- DBG("Easy does it with RX_FILTER_ID\n");
-
- bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
- bcm_rx_starttimer(op);
- return;
+ bcm_rx_update_and_send(op, &op->last_frames[0], rxframe);
+ goto rx_starttimer;
}
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;
+ bcm_rx_cmp_to_index(op, 0, rxframe);
+ goto rx_starttimer;
}
if (op->nframes > 1) {
- /* multiplex compare */
- DBG("Multiplex compare\n");
-
/*
+ * multiplex compare
+ *
* find the first multiplex mask that fits.
* Remark: The MUX-mask is stored in index 0
*/
for (i = 1; i < op->nframes; i++) {
- if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+ 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);
+ bcm_rx_cmp_to_index(op, i, rxframe);
break;
}
}
- bcm_rx_starttimer(op);
}
+
+rx_starttimer:
+ bcm_rx_starttimer(op);
}
/*
static void bcm_remove_op(struct bcm_op *op)
{
- del_timer(&op->timer);
- del_timer(&op->thrtimer);
+ hrtimer_cancel(&op->timer);
+ hrtimer_cancel(&op->thrtimer);
+
+ if (op->tsklet.func)
+ tasklet_kill(&op->tsklet);
+
+ if (op->thrtsklet.func)
+ tasklet_kill(&op->thrtsklet);
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
list_for_each_entry_safe(op, n, ops, list) {
if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
- DBG("removing rx_op %p for can_id %03X\n",
- op, op->can_id);
/*
* Don't care if we're bound or not (due to netdev
if (op->rx_reg_dev) {
struct net_device *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net,
op->ifindex);
-#else
- dev = dev_get_by_index(op->ifindex);
-#endif
if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
list_for_each_entry_safe(op, n, ops, list) {
if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
- DBG("removing rx_op %p for can_id %03X\n",
- op, op->can_id);
list_del(&op->list);
bcm_remove_op(op);
return 1; /* done */
{
struct bcm_op *op = bcm_find_op(ops, msg_head->can_id, ifindex);
- if (!op) {
- DBG("TRX_READ: did not find op for can_id %03X\n",
- msg_head->can_id);
+ if (!op)
return -EINVAL;
- }
- DBG("TRX_READ: sending status for can_id %03X\n",
- msg_head->can_id);
/* put current values into msg_head */
msg_head->flags = op->flags;
msg_head->count = op->count;
{
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
- int i, err;
+ unsigned int i;
+ int err;
/* we need a real device to send frames */
if (!ifindex)
return -ENODEV;
- /* we need at least one can_frame */
- if (msg_head->nframes < 1)
+ /* check nframes boundaries - we need at least one can_frame */
+ if (msg_head->nframes < 1 || msg_head->nframes > MAX_NFRAMES)
return -EINVAL;
/* check the given can_id */
if (op) {
/* update existing BCM operation */
- DBG("TX_SETUP: modifying existing tx_op %p for can_id %03X\n",
- op, msg_head->can_id);
-
/*
* Do we need more space for the can_frames than currently
* allocated? -> This is a _really_ unusual use-case and
for (i = 0; i < msg_head->nframes; i++) {
err = memcpy_fromiovec((u8 *)&op->frames[i],
msg->msg_iov, CFSIZ);
+
+ if (op->frames[i].can_dlc > 8)
+ err = -EINVAL;
+
if (err < 0)
return err;
if (!op)
return -ENOMEM;
- DBG("TX_SETUP: creating new tx_op %p for can_id %03X\n",
- op, msg_head->can_id);
-
op->can_id = msg_head->can_id;
/* create array for can_frames and copy the data */
for (i = 0; i < msg_head->nframes; i++) {
err = memcpy_fromiovec((u8 *)&op->frames[i],
msg->msg_iov, CFSIZ);
+
+ if (op->frames[i].can_dlc > 8)
+ err = -EINVAL;
+
if (err < 0) {
if (op->frames != &op->sframe)
kfree(op->frames);
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
-
op->ifindex = ifindex;
- /* initialize uninitialized (kmalloc) structure */
- init_timer(&op->timer);
-
- /* currently unused in tx_ops */
- init_timer(&op->thrtimer);
-
- /* handler for tx_ops */
+ /* initialize uninitialized (kzalloc) structure */
+ hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->timer.function = bcm_tx_timeout_handler;
- /* timer.data points to this op-structure */
- op->timer.data = (unsigned long)op;
+ /* initialize tasklet for tx countevent notification */
+ tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet,
+ (unsigned long) op);
+
+ /* currently unused in tx_ops */
+ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
/* add this bcm_op to the list of the tx_ops */
list_add(&op->list, &bo->tx_ops);
if (op->flags & SETTIMER) {
/* set timer values */
-
op->count = msg_head->count;
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
- op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
- op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
-
- DBG("TX_SETUP: SETTIMER count=%d j_ival1=%ld j_ival2=%ld\n",
- op->count, op->j_ival1, op->j_ival2);
+ op->kt_ival1 = timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = timeval_to_ktime(msg_head->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->kt_ival1.tv64 && !op->kt_ival2.tv64)
+ hrtimer_cancel(&op->timer);
}
if ((op->flags & STARTTIMER) &&
- ((op->j_ival1 && op->count) || op->j_ival2)) {
-
- del_timer(&op->timer);
+ ((op->kt_ival1.tv64 && op->count) || op->kt_ival2.tv64)) {
/* spec: send can_frame when starting timer */
op->flags |= TX_ANNOUNCE;
- if (op->j_ival1 && (op->count > 0)) {
- op->timer.expires = jiffies + op->j_ival1;
+ if (op->kt_ival1.tv64 && (op->count > 0)) {
/* 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);
+ hrtimer_start(&op->timer, op->kt_ival1,
+ HRTIMER_MODE_REL);
+ } else
+ hrtimer_start(&op->timer, op->kt_ival2,
+ HRTIMER_MODE_REL);
}
if (op->flags & TX_ANNOUNCE)
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 */
+ /* ignore trailing garbage */
+ msg_head->nframes = 0;
}
+ /* the first element contains the mux-mask => MAX_NFRAMES + 1 */
+ if (msg_head->nframes > MAX_NFRAMES + 1)
+ return -EINVAL;
+
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->can_id & CAN_RTR_FLAG))))
return -EINVAL;
- }
/* check the given can_id */
op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex);
if (op) {
/* update existing BCM operation */
- DBG("RX_SETUP: modifying existing rx_op %p for can_id %03X\n",
- op, msg_head->can_id);
-
/*
* Do we need more space for the can_frames than currently
* allocated? -> This is a _really_ unusual use-case and
} else {
/* insert new BCM operation for the given can_id */
-
op = kzalloc(OPSIZ, GFP_KERNEL);
if (!op)
return -ENOMEM;
- DBG("RX_SETUP: creating new rx_op %p for can_id %03X\n",
- op, msg_head->can_id);
-
op->can_id = msg_head->can_id;
op->nframes = msg_head->nframes;
}
}
+ /* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
op->ifindex = ifindex;
/* initialize uninitialized (kzalloc) structure */
- init_timer(&op->timer);
-
- /* init throttle timer for RX_CHANGED */
- init_timer(&op->thrtimer);
-
- /* handler for rx timeouts */
+ hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->timer.function = bcm_rx_timeout_handler;
- /* timer.data points to this op-structure */
- op->timer.data = (unsigned long)op;
+ /* initialize tasklet for rx timeout notification */
+ tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet,
+ (unsigned long) op);
- /* handler for RX_CHANGED throttle timeouts */
+ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->thrtimer.function = bcm_rx_thr_handler;
- /* timer.data points to this op-structure */
- op->thrtimer.data = (unsigned long)op;
-
- /* mark disabled timer */
- op->thrtimer.expires = 0;
+ /* initialize tasklet for rx throttle handling */
+ tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet,
+ (unsigned long) op);
/* add this bcm_op to the list of the rx_ops */
list_add(&op->list, &bo->rx_ops);
if (op->flags & RX_RTR_FRAME) {
/* no timers in RTR-mode */
- del_timer(&op->thrtimer);
- del_timer(&op->timer);
+ hrtimer_cancel(&op->thrtimer);
+ hrtimer_cancel(&op->timer);
/*
* funny feature in RX(!)_SETUP only for RTR-mode:
/* set timer value */
op->ival1 = msg_head->ival1;
op->ival2 = msg_head->ival2;
- op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
- op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
-
- DBG("RX_SETUP: SETTIMER j_ival1=%ld j_ival2=%ld\n",
- op->j_ival1, op->j_ival2);
+ op->kt_ival1 = timeval_to_ktime(msg_head->ival1);
+ op->kt_ival2 = timeval_to_ktime(msg_head->ival2);
/* disable an active timer due to zero value? */
- if (!op->j_ival1) {
- del_timer(&op->timer);
- DBG("RX_SETUP: disabled timer rx timeouts.\n");
- }
+ if (!op->kt_ival1.tv64)
+ hrtimer_cancel(&op->timer);
- /* free currently blocked msgs ? */
- if (op->thrtimer.expires) {
- DBG("RX_SETUP: unblocking throttled msgs.\n");
- del_timer(&op->thrtimer);
- /* send blocked msgs hereafter */
- op->thrtimer.expires = jiffies + 2;
- add_timer(&op->thrtimer);
- }
/*
- * if (op->j_ival2) is zero, no (new) throttling
- * will happen. For details see functions
- * bcm_rx_update_and_send() and bcm_rx_thr_handler()
+ * In any case cancel the throttle timer, flush
+ * potentially blocked msgs and reset throttle handling
*/
+ op->kt_lastmsg = ktime_set(0, 0);
+ hrtimer_cancel(&op->thrtimer);
+ bcm_rx_thr_flush(op, 1);
}
- 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);
- }
+ if ((op->flags & STARTTIMER) && op->kt_ival1.tv64)
+ hrtimer_start(&op->timer, op->kt_ival1,
+ HRTIMER_MODE_REL);
}
/* now we can register for can_ids, if we added a new bcm_op */
if (do_rx_register) {
- DBG("RX_SETUP: can_rx_register() for can_id %03X. "
- "rx_op is %p\n", op->can_id, op);
-
if (ifindex) {
struct net_device *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, ifindex);
-#else
- dev = dev_get_by_index(ifindex);
-#endif
if (dev) {
err = can_rx_register(dev, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
- IDENT);
+ "bcm");
op->rx_reg_dev = dev;
dev_put(dev);
} else
err = can_rx_register(NULL, op->can_id,
REGMASK(op->can_id),
- bcm_rx_handler, op, IDENT);
+ bcm_rx_handler, op, "bcm");
if (err) {
/* this bcm rx op is broken -> remove it */
list_del(&op->list);
struct net_device *dev;
int err;
- /* just copy and send one can_frame */
-
- if (!ifindex) /* we need a real device to send frames */
+ /* we need a real device to send frames */
+ if (!ifindex)
return -ENODEV;
skb = alloc_skb(CFSIZ, GFP_KERNEL);
return err;
}
- DBG_FRAME("BCM: TX_SEND: sending frame",
- (struct can_frame *)skb->data);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, ifindex);
-#else
- dev = dev_get_by_index(ifindex);
-#endif
if (!dev) {
kfree_skb(skb);
return -ENODEV;
skb->dev = dev;
skb->sk = sk;
- can_send(skb, 1); /* send with loopback */
+ err = can_send(skb, 1); /* send with loopback */
dev_put(dev);
+ if (err)
+ return err;
+
return CFSIZ + MHSIZ;
}
struct bcm_msg_head msg_head;
int ret; /* read bytes or error codes as return value */
- if (!bo->bound) {
- DBG("sock %p not bound\n", sk);
+ if (!bo->bound)
return -ENOTCONN;
- }
+
+ /* check for valid message length from userspace */
+ if (size < MHSIZ || (size - MHSIZ) % CFSIZ)
+ return -EINVAL;
/* check for alternative ifindex for this bcm_op */
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
+ if (msg->msg_namelen < sizeof(*addr))
+ return -EINVAL;
+
if (addr->can_family != AF_CAN)
return -EINVAL;
- ifindex = addr->can_ifindex; /* ifindex from sendto() */
+ /* ifindex from sendto() */
+ ifindex = addr->can_ifindex;
if (ifindex) {
struct net_device *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, ifindex);
-#else
- dev = dev_get_by_index(ifindex);
-#endif
- if (!dev) {
- DBG("device %d not found\n", ifindex);
+ if (!dev)
return -ENODEV;
- }
if (dev->type != ARPHRD_CAN) {
- DBG("device %d no CAN device\n", ifindex);
dev_put(dev);
return -ENODEV;
}
if (ret < 0)
return ret;
- DBG("opcode %d for can_id %03X\n", msg_head.opcode, msg_head.can_id);
-
lock_sock(sk);
switch (msg_head.opcode) {
break;
case TX_SEND:
- /* we need at least one can_frame */
- if (msg_head.nframes < 1)
+ /* we need exactly one can_frame behind the msg head */
+ if ((msg_head.nframes != 1) || (size != CFSIZ + MHSIZ))
ret = -EINVAL;
else
ret = bcm_tx_send(msg, ifindex, sk);
break;
default:
- DBG("Unknown opcode %d\n", msg_head.opcode);
ret = -EINVAL;
break;
}
{
struct net_device *dev = (struct net_device *)data;
struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
struct sock *sk = &bo->sk;
-#else
- struct sock *sk = bo->sk;
-#endif
struct bcm_op *op;
int notify_enodev = 0;
- DBG("msg %ld for dev %p (%s idx %d) sk %p bo->ifindex %d\n",
- msg, dev, dev->name, dev->ifindex, sk, bo->ifindex);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
if (dev->nd_net != &init_net)
return NOTIFY_DONE;
#endif
{
struct bcm_sock *bo = bcm_sk(sk);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
- bo->sk = sk;
-#endif
bo->bound = 0;
bo->ifindex = 0;
bo->dropped_usr_msgs = 0;
static int bcm_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct bcm_sock *bo = bcm_sk(sk);
+ struct bcm_sock *bo;
struct bcm_op *op, *next;
- DBG("socket %p, sk %p\n", sock, sk);
+ if (sk == NULL)
+ return 0;
+
+ bo = bcm_sk(sk);
/* remove bcm_ops, timer, rx_unregister(), etc. */
lock_sock(sk);
- list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
- DBG("removing tx_op %p for can_id %03X\n", op, op->can_id);
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list)
bcm_remove_op(op);
- }
list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
- DBG("removing rx_op %p for can_id %03X\n", op, op->can_id);
-
/*
* Don't care if we're bound or not (due to netdev problems)
* can_rx_unregister() is always a save thing to do here.
if (op->rx_reg_dev) {
struct net_device *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, op->ifindex);
-#else
- dev = dev_get_by_index(op->ifindex);
-#endif
if (dev) {
bcm_rx_unreg(dev, op);
dev_put(dev);
bo->ifindex = 0;
}
+ sock_orphan(sk);
+ sock->sk = NULL;
+
release_sock(sk);
sock_put(sk);
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
if (bo->bound)
return -EISCONN;
if (addr->can_ifindex) {
struct net_device *dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
dev = dev_get_by_index(&init_net, addr->can_ifindex);
-#else
- dev = dev_get_by_index(addr->can_ifindex);
-#endif
- if (!dev) {
- DBG("could not find device index %d\n",
- addr->can_ifindex);
+ if (!dev)
return -ENODEV;
- }
if (dev->type != ARPHRD_CAN) {
- DBG("device %d no CAN device\n", addr->can_ifindex);
dev_put(dev);
return -ENODEV;
}
bo->ifindex = dev->ifindex;
dev_put(dev);
- DBG("socket %p bound to device %s (idx %d)\n",
- sock, dev->name, dev->ifindex);
-
} else {
/* no interface reference for ifindex = 0 ('any' CAN device) */
bo->ifindex = 0;
if (proc_dir) {
/* unique socket address as filename */
- sprintf(bo->procname, "%p", sock);
+ sprintf(bo->procname, "%lu", sock_i_ino(sk));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+ bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
+ proc_dir,
+ &bcm_proc_fops, sk);
+#else
bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
proc_dir,
bcm_read_proc, sk);
+#endif
}
return 0;
int noblock;
int err;
- DBG("socket %p, sk %p\n", sock, sk);
-
noblock = flags & MSG_DONTWAIT;
flags &= ~MSG_DONTWAIT;
skb = skb_recv_datagram(sk, flags, noblock, &error);
if (!skb)
return error;
- DBG("delivering skbuff %p\n", skb);
- DBG_SKB(skb);
-
if (skb->len < size)
size = skb->len;
memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
}
- DBG("freeing sock %p, skbuff %p\n", sk, skb);
skb_free_datagram(sk, skb);
return size;
}
-static unsigned int bcm_poll(struct file *file, struct socket *sock,
- poll_table *wait)
-{
- unsigned int mask = 0;
-
- DBG("socket %p\n", sock);
-
- mask = datagram_poll(file, sock, wait);
- return mask;
-}
-
-static struct proto_ops bcm_ops __read_mostly = {
+static const struct proto_ops bcm_ops = {
.family = PF_CAN,
.release = bcm_release,
.bind = sock_no_bind,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
- .poll = bcm_poll,
- .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .poll = datagram_poll,
+ .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.sendpage = sock_no_sendpage,
};
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
static struct proto bcm_proto __read_mostly = {
.name = "CAN_BCM",
.owner = THIS_MODULE,
.init = bcm_init,
};
-static struct can_proto bcm_can_proto __read_mostly = {
+static const struct can_proto bcm_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_BCM,
- .capability = BCM_CAP,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+ .capability = -1,
+#endif
.ops = &bcm_ops,
.prot = &bcm_proto,
};
-#else
-static struct can_proto bcm_can_proto __read_mostly = {
- .type = SOCK_DGRAM,
- .protocol = CAN_BCM,
- .capability = BCM_CAP,
- .ops = &bcm_ops,
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct bcm_sock),
- .init = bcm_init,
-};
-#endif
static int __init bcm_module_init(void)
{
/* create /proc/net/can-bcm directory */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- proc_dir = proc_mkdir("can-"IDENT, init_net.proc_net);
+ proc_dir = proc_mkdir("can-bcm", init_net.proc_net);
#else
- proc_dir = proc_mkdir("can-"IDENT, proc_net);
+ proc_dir = proc_mkdir("can-bcm", proc_net);
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
if (proc_dir)
proc_dir->owner = THIS_MODULE;
+#endif
return 0;
}
if (proc_dir)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- proc_net_remove(&init_net, "can-"IDENT);
+ proc_net_remove(&init_net, "can-bcm");
#else
- proc_net_remove("can-"IDENT);
+ proc_net_remove("can-bcm");
#endif
}