X-Git-Url: http://rtime.felk.cvut.cz/gitweb/socketcan-devel.git/blobdiff_plain/012bb19c23be934c49266a2b5f0301fed9103767..55b7bae29a3225bf5922d8d7ce3e2ff467650f05:/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 1401b3d..9633398 100644 --- a/kernel/2.6/net/can/bcm.c +++ b/kernel/2.6/net/can/bcm.c @@ -44,20 +44,22 @@ #include #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +#include #include -#endif #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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) #include @@ -66,25 +68,39 @@ #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 CAN_BCM_VERSION "20080415" +#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"); + +#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 */ static inline u64 GET_U64(const struct can_frame *cp) @@ -96,21 +112,16 @@ struct bcm_op { struct list_head list; int ifindex; canid_t can_id; - int flags; + u32 flags; unsigned long frames_abs, frames_filtered; struct timeval ival1, ival2; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) struct hrtimer timer, thrtimer; + struct tasklet_struct tsklet, thrtsklet; ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg; -#else - struct timer_list timer, thrtimer; - struct timeval rx_stamp; - unsigned long j_ival1, j_ival2, j_lastmsg; -#endif 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; @@ -122,11 +133,7 @@ struct bcm_op { static struct proc_dir_entry *proc_dir; 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; @@ -134,78 +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 } #define CFSIZ sizeof(struct can_frame) #define OPSIZ sizeof(struct bcm_op) #define MHSIZ sizeof(struct bcm_msg_head) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) /* - * 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) + * procfs functions */ -static unsigned long rounded_tv2jif(const struct timeval *tv) +static char *bcm_proc_getifname(char *result, int ifindex) { - unsigned long sec = tv->tv_sec; - unsigned long usec = tv->tv_usec; - unsigned long jif; + struct net_device *dev; - if (sec > ULONG_MAX / HZ) - return ULONG_MAX; + if (!ifindex) + return "any"; - /* round up to get at least the requested time */ - usec += 1000000 / HZ - 1; + 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); - jif = usec / (1000000 / HZ); + return result; +} - if (sec * HZ > ULONG_MAX - jif) - return ULONG_MAX; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int bcm_proc_show(struct seq_file *m, void *v) +{ + char ifname[IFNAMSIZ]; + struct sock *sk = (struct sock *)m->private; + struct bcm_sock *bo = bcm_sk(sk); + struct bcm_op *op; - return jif + sec * HZ; -} +#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"); -/* - * procfs functions - */ -static char *bcm_proc_getifname(int ifindex) -{ - struct net_device *dev; + list_for_each_entry(op, &bo->rx_ops, list) { - if (!ifindex) - return "any"; + unsigned long reduction; - /* no usage counting */ - dev = __dev_get_by_index(&init_net, ifindex); - if (dev) - return dev->name; + /* print only active entries & prevent division by zero */ + if (!op->frames_abs) + continue; + + 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 (op->kt_ival2.tv64) + seq_printf(m, "thr=%lld ", + (long long) + ktime_to_us(op->kt_ival2)); + + seq_printf(m, "# recv %ld (%ld) => reduction: ", + op->frames_filtered, op->frames_abs); + + reduction = 100 - (op->frames_filtered * 100) / op->frames_abs; + + seq_printf(m, "%s%ld%%\n", + (reduction == 100)?"near ":"", reduction); + } + + 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)); + + if (op->kt_ival2.tv64) + seq_printf(m, "t2=%lld ", + (long long) ktime_to_us(op->kt_ival2)); - return "???"; + 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); @@ -218,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) { @@ -231,11 +289,10 @@ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) if (op->kt_ival1.tv64) len += snprintf(page + len, PAGE_SIZE - len, "timeo=%lld ", @@ -247,15 +304,6 @@ static int bcm_read_proc(char *page, char **start, off_t off, "thr=%lld ", (long long) ktime_to_us(op->kt_ival2)); -#else - if (op->j_ival1) - len += snprintf(page + len, PAGE_SIZE - len, - "timeo=%ld ", op->j_ival1); - - if (op->j_ival2) - len += snprintf(page + len, PAGE_SIZE - len, - "thr=%ld ", op->j_ival2); -#endif len += snprintf(page + len, PAGE_SIZE - len, "# recv %ld (%ld) => reduction: ", @@ -277,10 +325,10 @@ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) if (op->kt_ival1.tv64) len += snprintf(page + len, PAGE_SIZE - len, "t1=%lld ", (long long) ktime_to_us(op->kt_ival1)); @@ -288,15 +336,6 @@ static int bcm_read_proc(char *page, char **start, off_t off, if (op->kt_ival2.tv64) len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ", (long long) ktime_to_us(op->kt_ival2)); -#else - 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); -#endif len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n", op->frames_abs); @@ -313,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 @@ -367,7 +407,7 @@ 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, gfp_any()); @@ -378,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); @@ -398,11 +434,7 @@ 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 } /* @@ -428,27 +460,15 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head, } } -/* - * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) -{ - struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); - enum hrtimer_restart ret = HRTIMER_NORESTART; - - if (op->kt_ival1.tv64 && (op->count > 0)) { -#else -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; - if (op->j_ival1 && (op->count > 0)) { -#endif + 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 */ msg_head.opcode = TX_EXPIRED; @@ -461,45 +481,35 @@ static void bcm_tx_timeout_handler(unsigned long data) bcm_send_to_user(op, &msg_head, NULL, 0); } - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) - if (op->kt_ival1.tv64 && (op->count > 0)) { /* send (next) frame */ bcm_can_tx(op); - hrtimer_forward(hrtimer, ktime_get(), op->kt_ival1); - ret = HRTIMER_RESTART; + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival1), + HRTIMER_MODE_ABS); } else { if (op->kt_ival2.tv64) { /* send (next) frame */ bcm_can_tx(op); - hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); - ret = HRTIMER_RESTART; + hrtimer_start(&op->timer, + ktime_add(ktime_get(), op->kt_ival2), + HRTIMER_MODE_ABS); } } +} - return ret; -#else - if (op->j_ival1 && (op->count > 0)) { - - /* send (next) frame */ - bcm_can_tx(op); - mod_timer(&op->timer, jiffies + op->j_ival1); - - } else { - if (op->j_ival2) { +/* + * 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); - /* send (next) frame */ - bcm_can_tx(op); - mod_timer(&op->timer, jiffies + op->j_ival2); - } - } + tasklet_schedule(&op->tsklet); - return; -#endif + return HRTIMER_NORESTART; } /* @@ -509,10 +519,6 @@ static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data) { struct bcm_msg_head head; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) - op->j_lastmsg = jiffies; -#endif - /* update statistics */ op->frames_filtered++; @@ -520,6 +526,9 @@ 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; + /* this element is not throttled anymore */ + data->can_dlc &= (BCM_CAN_DLC_MASK|RX_RECV); + head.opcode = RX_CHANGED; head.flags = op->flags; head.count = op->count; @@ -551,38 +560,32 @@ static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) */ static void bcm_rx_update_and_send(struct bcm_op *op, struct can_frame *lastdata, - struct can_frame *rxdata) + const struct can_frame *rxdata) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) memcpy(lastdata, rxdata, CFSIZ); - /* mark as used */ - lastdata->can_dlc |= RX_RECV; + /* mark as used and throttled by default */ + lastdata->can_dlc |= (RX_RECV|RX_THR); - /* throtteling mode inactive OR data update already on the run ? */ - if (!op->kt_ival2.tv64 || hrtimer_callback_running(&op->thrtimer)) { + /* throtteling mode inactive ? */ + if (!op->kt_ival2.tv64) { /* send RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); + bcm_rx_changed(op, lastdata); return; } - if (hrtimer_active(&op->thrtimer)) { - /* mark as 'throttled' */ - lastdata->can_dlc |= RX_THR; + /* with active throttling timer we are just done here */ + if (hrtimer_active(&op->thrtimer)) return; - } - if (!op->kt_lastmsg.tv64) { - /* send first RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); - op->kt_lastmsg = ktime_get(); - return; - } + /* 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)) { - /* mark as 'throttled' and start timer */ - lastdata->can_dlc |= RX_THR; + /* 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); @@ -590,42 +593,17 @@ static void bcm_rx_update_and_send(struct bcm_op *op, } /* the gap was that big, that throttling was not needed here */ - bcm_rx_changed(op, rxdata); +rx_changed_settime: + bcm_rx_changed(op, lastdata); op->kt_lastmsg = ktime_get(); -#else - 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 'throttled' */ - lastdata->can_dlc |= RX_THR; - - if (!(op->thrtimer.expires)) { - /* start the timer only the first time */ - mod_timer(&op->thrtimer, nexttx); - } - - } else { - /* send RX_CHANGED to the user immediately */ - bcm_rx_changed(op, rxdata); - } -#endif } /* * 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, @@ -665,29 +643,16 @@ static void bcm_rx_starttimer(struct bcm_op *op) if (op->flags & RX_NO_AUTOTIMER) return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) if (op->kt_ival1.tv64) hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); -#else - if (op->j_ival1) - mod_timer(&op->timer, jiffies + op->j_ival1); -#endif } -/* - * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) -{ - struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer); -#else -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; -#endif struct bcm_msg_head msg_head; + /* create notification to user */ msg_head.opcode = RX_TIMEOUT; msg_head.flags = op->flags; msg_head.count = op->count; @@ -697,6 +662,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! */ @@ -705,54 +681,68 @@ static void bcm_rx_timeout_handler(unsigned long data) /* clear received can_frames to indicate 'nothing received' */ memset(op->last_frames, 0, op->nframes * CFSIZ); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) return HRTIMER_NORESTART; -#endif +} + +/* + * bcm_rx_do_flush - helper for bcm_rx_thr_flush + */ +static inline int bcm_rx_do_flush(struct bcm_op *op, int update, + unsigned int index) +{ + 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; } /* * 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) +static int bcm_rx_thr_flush(struct bcm_op *op, int update) { int updated = 0; if (op->nframes > 1) { - int i; + 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]); - updated++; - } - } + for (i = 1; i < op->nframes; i++) + updated += bcm_rx_do_flush(op, update, i); } else { /* 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++; - } + updated += bcm_rx_do_flush(op, update, 0); } return updated; } +static void bcm_rx_thr_tsklet(unsigned long data) +{ + struct bcm_op *op = (struct bcm_op *)data; + + /* push the changed data to the userspace */ + bcm_rx_thr_flush(op, 1); +} + /* * bcm_rx_thr_handler - the time for blocked content updates is over now: * Check for throttled data and send it to the userspace */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); - if (bcm_rx_thr_flush(op)) { + tasklet_schedule(&op->thrtsklet); + + if (bcm_rx_thr_flush(op, 0)) { hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2); return HRTIMER_RESTART; } else { @@ -761,19 +751,6 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) return HRTIMER_NORESTART; } } -#else -static void bcm_rx_thr_handler(unsigned long data) -{ - struct bcm_op *op = (struct bcm_op *)data; - - if (bcm_rx_thr_flush(op)) - mod_timer(&op->thrtimer, jiffies + op->j_ival2); - else { - /* mark disabled / consumed timer */ - op->thrtimer.expires = 0; - } -} -#endif /* * bcm_rx_handler - handle a CAN frame receiption @@ -781,37 +758,21 @@ static void bcm_rx_thr_handler(unsigned long data) static void bcm_rx_handler(struct sk_buff *skb, void *data) { struct bcm_op *op = (struct bcm_op *)data; - struct can_frame rxframe; - int i; + const struct can_frame *rxframe = (struct can_frame *)skb->data; + unsigned int i; /* disable timeout */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) hrtimer_cancel(&op->timer); -#else - del_timer(&op->timer); -#endif - - 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); - } else { - kfree_skb(skb); + if (op->can_id != rxframe->can_id) return; - } - if (op->can_id != rxframe.can_id) - return; + /* save rx timestamp */ + op->rx_stamp = skb->tstamp; + /* save originator for recvfrom() */ + op->rx_ifindex = skb->dev->ifindex; + /* update statistics */ + op->frames_abs++; if (op->flags & RX_RTR_FRAME) { /* send reply for RTR-request (placed in op->frames[0]) */ @@ -821,16 +782,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) if (op->flags & RX_FILTER_ID) { /* the easiest case */ - 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 */ - 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) { @@ -842,15 +801,17 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) */ 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]))) { - 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); } /* @@ -871,13 +832,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) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) hrtimer_cancel(&op->timer); hrtimer_cancel(&op->thrtimer); -#else - del_timer(&op->timer); - del_timer(&op->thrtimer); -#endif + + 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); @@ -997,14 +959,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 */ @@ -1086,19 +1049,15 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_tx_timeout_handler; - /* currently unused in tx_ops */ - hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); -#else - setup_timer(&op->timer, bcm_tx_timeout_handler, - (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 */ - init_timer(&op->thrtimer); -#endif + 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); @@ -1120,7 +1079,6 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->currframe = 0; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) if (op->flags & SETTIMER) { /* set timer values */ op->count = msg_head->count; @@ -1140,44 +1098,21 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* spec: send can_frame when starting timer */ op->flags |= TX_ANNOUNCE; - if (op->kt_ival1.tv64 && (op->count > 0)) { - /* op->count-- is done in bcm_tx_timeout_handler */ + /* only start timer when having more frames than sent below */ + if (op->kt_ival1.tv64 && (op->count > 1)) { + /* op->count-- is done in bcm_tx_timeout_tsklet */ hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); } else hrtimer_start(&op->timer, op->kt_ival2, HRTIMER_MODE_REL); } -#else - 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); - - /* disable an active timer due to zero values? */ - if (!op->j_ival1 && !op->j_ival2) - del_timer(&op->timer); - } - - if ((op->flags & STARTTIMER) && - ((op->j_ival1 && op->count) || op->j_ival2)) { - - /* spec: send can_frame when starting timer */ - op->flags |= TX_ANNOUNCE; - - if (op->j_ival1 && (op->count > 0)) { - /* op->count-- is done in bcm_tx_timeout_handler */ - mod_timer(&op->timer, jiffies + op->j_ival1); - } else - mod_timer(&op->timer, jiffies + op->j_ival2); - } -#endif - if (op->flags & TX_ANNOUNCE) + if (op->flags & TX_ANNOUNCE) { bcm_can_tx(op); + if (op->kt_ival1.tv64 && (op->count > 0)) + op->count--; + } return msg_head->nframes * CFSIZ + MHSIZ; } @@ -1200,6 +1135,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, 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)))) @@ -1285,23 +1224,19 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->ifindex = ifindex; /* initialize uninitialized (kzalloc) structure */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_rx_timeout_handler; + /* initialize tasklet for rx timeout notification */ + tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet, + (unsigned long) op); + hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->thrtimer.function = bcm_rx_thr_handler; -#else - setup_timer(&op->timer, bcm_rx_timeout_handler, - (unsigned long)op); - /* init throttle timer for RX_CHANGED */ - setup_timer(&op->thrtimer, bcm_rx_thr_handler, - (unsigned long)op); - - /* mark disabled timer */ - op->thrtimer.expires = 0; -#endif + /* 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); @@ -1317,13 +1252,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 */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) hrtimer_cancel(&op->thrtimer); hrtimer_cancel(&op->timer); -#else - del_timer(&op->thrtimer); - del_timer(&op->timer); -#endif /* * funny feature in RX(!)_SETUP only for RTR-mode: @@ -1335,7 +1265,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->frames[0].can_id = op->can_id & ~CAN_RTR_FLAG; } else { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) if (op->flags & SETTIMER) { /* set timer value */ @@ -1354,37 +1283,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, */ op->kt_lastmsg = ktime_set(0, 0); hrtimer_cancel(&op->thrtimer); - bcm_rx_thr_flush(op); + bcm_rx_thr_flush(op, 1); } if ((op->flags & STARTTIMER) && op->kt_ival1.tv64) hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL); -#else - if (op->flags & SETTIMER) { - - /* 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); - - /* disable an active timer due to zero value? */ - if (!op->j_ival1) - del_timer(&op->timer); - - /* - * In any case cancel the throttle timer, flush - * potentially blocked msgs and reset throttle handling - */ - del_timer(&op->thrtimer); - bcm_rx_thr_flush(op); - op->thrtimer.expires = 0; - } - - if ((op->flags & STARTTIMER) && op->j_ival1) - mod_timer(&op->timer, jiffies + op->j_ival1); -#endif } /* now we can register for can_ids, if we added a new bcm_op */ @@ -1485,6 +1389,9 @@ 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; @@ -1552,7 +1459,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, break; case TX_SEND: - /* we need at exactly one can_frame behind the msg head */ + /* we need exactly one can_frame behind the msg head */ if ((msg_head.nframes != 1) || (size != CFSIZ + MHSIZ)) ret = -EINVAL; else @@ -1577,16 +1484,12 @@ 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) - if (dev_net(dev) != &init_net) + 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) @@ -1640,9 +1543,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; @@ -1665,9 +1565,14 @@ 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; + if (sk == NULL) + return 0; + + bo = bcm_sk(sk); + /* remove bcm_ops, timer, rx_unregister(), etc. */ unregister_netdevice_notifier(&bo->notifier); @@ -1715,6 +1620,9 @@ static int bcm_release(struct socket *sock) bo->ifindex = 0; } + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); sock_put(sk); @@ -1728,6 +1636,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; @@ -1756,10 +1667,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; @@ -1801,7 +1718,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, return size; } -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, @@ -1810,7 +1727,7 @@ static struct proto_ops bcm_ops __read_mostly = { .accept = sock_no_accept, .getname = sock_no_getname, .poll = datagram_poll, - .ioctl = NULL, /* use can_ioctl() from af_can.c */ + .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */ .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, @@ -1821,7 +1738,6 @@ static struct proto_ops bcm_ops __read_mostly = { .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, @@ -1829,24 +1745,15 @@ static struct proto bcm_proto __read_mostly = { .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, +#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 = -1, - .ops = &bcm_ops, - .owner = THIS_MODULE, - .obj_size = sizeof(struct bcm_sock), - .init = bcm_init, -}; -#endif static int __init bcm_module_init(void) { @@ -1867,8 +1774,10 @@ static int __init bcm_module_init(void) 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; }