#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+#include <linux/hrtimer.h>
+#endif
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/uio.h>
#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
(CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
-#define CAN_BCM_VERSION CAN_VERSION
+#define CAN_BCM_VERSION "20080415"
static __initdata const char banner[] = KERN_INFO
"can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
int ifindex;
canid_t can_id;
int flags;
- unsigned long j_ival1, j_ival2, j_lastmsg;
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;
+ struct hrtimer timer, thrtimer;
+ 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;
#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
return jif + sec * HZ;
}
+#endif
/*
* procfs functions
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 ",
+ ktime_to_us(op->kt_ival1));
+
+ if (op->kt_ival2.tv64)
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "thr=%lld ",
+ 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: ",
"tx_op: %03X %s [%d] ",
op->can_id, bcm_proc_getifname(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 ",
+ ktime_to_us(op->kt_ival1));
+
+ if (op->kt_ival2.tv64)
+ len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ",
+ 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);
/*
* 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)
{
struct bcm_op *op = (struct bcm_op *)data;
if (op->j_ival1 && (op->count > 0)) {
+#endif
op->count--;
if (!op->count && (op->flags & TX_COUNTEVT)) {
}
}
+#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;
+
+ } else {
+ if (op->kt_ival2.tv64) {
+
+ /* send (next) frame */
+ bcm_can_tx(op);
+ hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+ ret = HRTIMER_RESTART;
+ }
+ }
+
+ return ret;
+#else
if (op->j_ival1 && (op->count > 0)) {
/* send (next) frame */
}
return;
+#endif
}
/*
{
struct bcm_msg_head head;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
op->j_lastmsg = jiffies;
+#endif
/* update statistics */
op->frames_filtered++;
bcm_send_to_user(op, &head, data, 1);
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+/* TODO: move to linux/hrtimer.h */
+static inline int hrtimer_callback_running(struct hrtimer *timer)
+{
+ return timer->state & HRTIMER_STATE_CALLBACK;
+}
+#endif
+
/*
* bcm_rx_update_and_send - process a detected relevant receive content change
* 1. update the last received data
struct can_frame *lastdata,
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;
+
+ /* throtteling mode inactive OR data update already on the run ? */
+ if (!op->kt_ival2.tv64 || hrtimer_callback_running(&op->thrtimer)) {
+ /* send RX_CHANGED to the user immediately */
+ bcm_rx_changed(op, rxdata);
+ return;
+ }
+
+ if (hrtimer_active(&op->thrtimer)) {
+ /* mark as 'throttled' */
+ lastdata->can_dlc |= RX_THR;
+ 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;
+ }
+
+ 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;
+ 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 */
+ bcm_rx_changed(op, rxdata);
+ op->kt_lastmsg = ktime_get();
+#else
unsigned long nexttx = op->j_lastmsg + op->j_ival2;
memcpy(lastdata, rxdata, CFSIZ);
/* send RX_CHANGED to the user immediately */
bcm_rx_changed(op, rxdata);
}
+#endif
}
/*
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)
{
struct bcm_op *op = (struct bcm_op *)data;
+#endif
struct bcm_msg_head msg_head;
msg_head.opcode = RX_TIMEOUT;
/* 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_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)) {
+ hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
+ return HRTIMER_RESTART;
+ } else {
+ /* rearm throttle handling */
+ op->kt_lastmsg = ktime_set(0, 0);
+ return HRTIMER_NORESTART;
+ }
+}
+#else
static void bcm_rx_thr_handler(unsigned long data)
{
struct bcm_op *op = (struct bcm_op *)data;
op->thrtimer.expires = 0;
}
}
+#endif
/*
* bcm_rx_handler - handle a CAN frame receiption
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));
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->frames) && (op->frames != &op->sframe))
kfree(op->frames);
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);
/* currently unused in tx_ops */
init_timer(&op->thrtimer);
+#endif
/* add this bcm_op to the list of the tx_ops */
list_add(&op->list, &bo->tx_ops);
op->currframe = 0;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ if (op->flags & SETTIMER) {
+ /* set timer values */
+ op->count = msg_head->count;
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->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->kt_ival1.tv64 && !op->kt_ival2.tv64)
+ hrtimer_cancel(&op->timer);
+ }
+
+ if ((op->flags & STARTTIMER) &&
+ ((op->kt_ival1.tv64 && op->count) || op->kt_ival2.tv64)) {
+
+ /* 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 */
+ 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;
} else
mod_timer(&op->timer, jiffies + op->j_ival2);
}
+#endif
if (op->flags & TX_ANNOUNCE)
bcm_can_tx(op);
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;
+
+ 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);
/* mark disabled timer */
op->thrtimer.expires = 0;
+#endif
/* 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 */
+#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:
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 */
+ op->ival1 = msg_head->ival1;
+ op->ival2 = msg_head->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->kt_ival1.tv64)
+ hrtimer_cancel(&op->timer);
+
+ /*
+ * 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);
+ }
+
+ 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 */
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 */