X-Git-Url: http://rtime.felk.cvut.cz/gitweb/socketcan-devel.git/blobdiff_plain/e7ef35d153849b3b0a2cc4ebc3677aaebfdb2891..05e2d5c0adda7e44b9afb6d6a17d1dbe90af4488:/kernel/2.6/net/can/bcm.c diff --git a/kernel/2.6/net/can/bcm.c b/kernel/2.6/net/can/bcm.c index c3d970c..11c5279 100644 --- a/kernel/2.6/net/can/bcm.c +++ b/kernel/2.6/net/can/bcm.c @@ -8,8 +8,7 @@ * 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. @@ -19,8 +18,8 @@ * * 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. @@ -45,48 +44,62 @@ #include #include #include +#include +#include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#endif #include -#include #include #include #include #include #include -#include -#include -#include +#include +#include +#include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) #include #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +#include "compat.h" +#endif -#include /* for RCSID. Removed by mkpatch script */ +#include /* 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 "); +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 */ @@ -99,20 +112,16 @@ struct bcm_op { 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; @@ -123,18 +132,8 @@ struct bcm_op { 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; @@ -142,105 +141,129 @@ struct bcm_sock { 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; -} + char ifname[IFNAMSIZ]; + struct sock *sk = (struct sock *)m->private; + struct bcm_sock *bo = bcm_sk(sk); + struct bcm_op *op; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + seq_printf(m, ">>> socket %p", sk->sk_socket); + seq_printf(m, " / sk %p", sk); + seq_printf(m, " / bo %p", bo); +#else + seq_printf(m, ">>> socket %pK", sk->sk_socket); + seq_printf(m, " / sk %pK", sk); + seq_printf(m, " / bo %pK", bo); #endif + seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); + seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex)); + seq_printf(m, " <<<\n"); -#define CFSIZ sizeof(struct can_frame) -#define OPSIZ sizeof(struct bcm_op) -#define MHSIZ sizeof(struct bcm_msg_head) + list_for_each_entry(op, &bo->rx_ops, list) { -/* - * rounded_tv2jif - calculate jiffies from timeval including optional up - * @tv: pointer to timeval - * - * Description: - * In opposite to 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; + unsigned long reduction; - if (sec > ULONG_MAX / HZ) - return ULONG_MAX; + /* print only active entries & prevent division by zero */ + if (!op->frames_abs) + continue; - /* round up to get at least the requested time */ - usec += 1000000 / HZ - 1; + 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)); - jif = usec / (1000000 / HZ); + if (op->kt_ival2.tv64) + seq_printf(m, "thr=%lld ", + (long long) + ktime_to_us(op->kt_ival2)); - if (sec * HZ > ULONG_MAX - jif) - return ULONG_MAX; + seq_printf(m, "# recv %ld (%ld) => reduction: ", + op->frames_filtered, op->frames_abs); - return jif + sec * HZ; -} + reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; -/* - * procfs functions - */ -static char *bcm_proc_getifname(int ifindex) -{ - struct net_device *dev; + seq_printf(m, "%s%ld%%\n", + (reduction == 100)?"near ":"", reduction); + } - if (!ifindex) - return "any"; + list_for_each_entry(op, &bo->tx_ops, list) { -#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; + 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)); + + 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; +} - return "???"; +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); @@ -253,7 +276,7 @@ static int bcm_read_proc(char *page, char **start, off_t off, 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) { @@ -266,17 +289,21 @@ static int bcm_read_proc(char *page, char **start, off_t off, 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: ", @@ -298,15 +325,17 @@ static int bcm_read_proc(char *page, char **start, off_t off, 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); @@ -323,6 +352,7 @@ static int bcm_read_proc(char *page, char **start, off_t off, *eof = 1; return len; } +#endif /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface @@ -334,25 +364,17 @@ static void bcm_can_tx(struct bcm_op *op) 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; @@ -385,11 +407,10 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, 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; @@ -397,11 +418,7 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, 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); @@ -417,14 +434,17 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, 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 } - /* restore originator for recvfrom() */ + /* + * Put the datagram to the queue so that bcm_recvmsg() can + * get it from there. We need to pass the interface index to + * bcm_recvmsg(). We pass a whole struct sockaddr_can in skb->cb + * containing the interface index. + */ + + BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can)); addr = (struct sockaddr_can *)skb->cb; memset(addr, 0, sizeof(*addr)); addr->can_family = AF_CAN; @@ -434,32 +454,23 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, 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; @@ -472,40 +483,36 @@ static void bcm_tx_timeout_handler(unsigned long data) } } - 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; } /* @@ -515,8 +522,6 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) { struct bcm_msg_head head; - op->j_lastmsg = jiffies; - /* update statistics */ op->frames_filtered++; @@ -524,9 +529,8 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) 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; @@ -539,6 +543,19 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) 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 @@ -546,46 +563,50 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *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, @@ -594,22 +615,14 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, int index, 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; } @@ -618,13 +631,11 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, int index, /* 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"); } /* @@ -635,29 +646,16 @@ static void bcm_rx_starttimer(struct bcm_op *op) if (op->flags & RX_NO_AUTOTIMER) return; - if (op->j_ival1) { - op->timer.expires = jiffies + op->j_ival1; - - DBG("adding rx timeout timer ival1. func=%p data=%p " - "exp=0x%08X\n", - op->timer.function, - (char *) op->timer.data, - (unsigned int) op->timer.expires); - - add_timer(&op->timer); - } + 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; @@ -667,6 +665,17 @@ static void bcm_rx_timeout_handler(unsigned long data) 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! */ @@ -674,135 +683,138 @@ static void bcm_rx_timeout_handler(unsigned long data) 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; } +} - DBG_FRAME("BCM: bcm_rx_handler: CAN frame", &rxframe); +/* + * 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; - if (op->can_id != rxframe.can_id) { - DBG("ERROR! Got wrong can_id %03X! Expected %03X.\n", - rxframe.can_id, op->can_id); + /* disable timeout */ + hrtimer_cancel(&op->timer); + + 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); } /* @@ -823,8 +835,14 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id, 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); @@ -859,8 +877,6 @@ static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex) 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 @@ -876,12 +892,8 @@ static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex) 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); @@ -910,8 +922,6 @@ static int bcm_delete_tx_op(struct list_head *ops, canid_t can_id, int ifindex) 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 */ @@ -929,14 +939,9 @@ static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head, { 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; @@ -957,14 +962,15 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, { 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 */ @@ -973,9 +979,6 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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 @@ -988,6 +991,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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; @@ -1004,9 +1011,6 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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 */ @@ -1023,6 +1027,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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); @@ -1041,20 +1049,18 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* 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); @@ -1078,50 +1084,30 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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) @@ -1139,30 +1125,29 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; int do_rx_register; - int err; + int err = 0; if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) { /* be robust against wrong usage ... */ msg_head->flags |= RX_FILTER_ID; - msg_head->nframes = 0; /* ignore trailing garbage */ + /* 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 @@ -1190,14 +1175,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } 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; @@ -1237,31 +1218,26 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } } + /* 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 tx_ops */ + /* add this bcm_op to the list of the rx_ops */ list_add(&op->list, &bo->rx_ops); /* call can_rx_register() */ @@ -1275,8 +1251,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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: @@ -1293,72 +1269,53 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* 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) { - can_rx_register(dev, op->can_id, - REGMASK(op->can_id), - bcm_rx_handler, op, IDENT); + err = can_rx_register(dev, op->can_id, + REGMASK(op->can_id), + bcm_rx_handler, op, + "bcm"); + op->rx_reg_dev = dev; dev_put(dev); } } else - can_rx_register(NULL, op->can_id, REGMASK(op->can_id), - bcm_rx_handler, op, IDENT); + err = can_rx_register(NULL, op->can_id, + REGMASK(op->can_id), + bcm_rx_handler, op, "bcm"); + if (err) { + /* this bcm rx op is broken -> remove it */ + list_del(&op->list); + bcm_remove_op(op); + return err; + } } return msg_head->nframes * CFSIZ + MHSIZ; @@ -1373,9 +1330,8 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) 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); @@ -1389,14 +1345,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) 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; @@ -1404,9 +1353,12 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) 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; } @@ -1422,10 +1374,12 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, 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 */ @@ -1434,26 +1388,23 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, 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; } @@ -1468,8 +1419,6 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, 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) { @@ -1509,15 +1458,14 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, 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; } @@ -1535,18 +1483,14 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, { 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 @@ -1598,9 +1542,6 @@ static int bcm_init(struct sock *sk) { 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; @@ -1623,10 +1564,13 @@ static int bcm_init(struct sock *sk) 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. */ @@ -1634,14 +1578,10 @@ static int bcm_release(struct socket *sock) 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. @@ -1655,11 +1595,7 @@ static int bcm_release(struct socket *sock) 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); @@ -1683,6 +1619,9 @@ static int bcm_release(struct socket *sock) bo->ifindex = 0; } + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); sock_put(sk); @@ -1696,6 +1635,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); + if (len < sizeof(*addr)) + return -EINVAL; + if (bo->bound) return -EISCONN; @@ -1703,19 +1645,11 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, 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; } @@ -1723,9 +1657,6 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, 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; @@ -1735,10 +1666,16 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, 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; @@ -1753,17 +1690,12 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, 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; @@ -1780,24 +1712,12 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, 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 = { +static const struct proto_ops bcm_ops = { .family = PF_CAN, .release = bcm_release, .bind = sock_no_bind, @@ -1805,8 +1725,8 @@ static struct proto_ops bcm_ops = { .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, @@ -1817,44 +1737,46 @@ static struct proto_ops bcm_ops = { .sendpage = sock_no_sendpage, }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) -static struct proto bcm_proto = { +static struct proto bcm_proto __read_mostly = { .name = "CAN_BCM", .owner = THIS_MODULE, .obj_size = sizeof(struct bcm_sock), .init = bcm_init, }; -static struct can_proto bcm_can_proto = { +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 = { - .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) { + int err; + printk(banner); - can_proto_register(&bcm_can_proto); + err = can_proto_register(&bcm_can_proto); + if (err < 0) { + printk(KERN_ERR "can: registration of bcm protocol failed\n"); + return err; + } /* create /proc/net/can-bcm directory */ - proc_dir = proc_mkdir("can-"IDENT, proc_net); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + proc_dir = proc_mkdir("can-bcm", init_net.proc_net); +#else + 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; } @@ -1864,7 +1786,11 @@ static void __exit bcm_module_exit(void) can_proto_unregister(&bcm_can_proto); if (proc_dir) - remove_proc_entry("can-"IDENT, proc_net); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + proc_net_remove(&init_net, "can-bcm"); +#else + proc_net_remove("can-bcm"); +#endif } module_init(bcm_module_init);