Teach drivers/media/IR/ir-raw-event.c to use durations
Subsystems (2)
PATCH 1/1 Teach drivers/media/IR/ir-raw-event.c to use durations
drivers/media/IR/ir-raw-event.c is currently written with the assumption that all "raw" hardware will generate events only on state change (i.e. when a pulse or space starts).
However, some hardware (like mceusb, probably the most popular IR receiver out there) only generates duration data (and that data is buffered so using any kind of timing on the data is futile).
Furthermore, using signed int's to represent pulse/space durations is a well-known approach when writing ir decoders.
With this patch:
s64 int's are used to represent pulse/space durations in ns
a workqueue is used to decode the ir protocols outside of interrupt context
#defines are added to make decoders clearer
decoder reset is implemented by passing a zero duration to the kfifo queue
and decoders are updated accordingly
(checkpatch is content with everything but the line lengths, Mauro, feel free to fixup those if you feel it is important)
drivers/media/IR/ir-raw-event.c +86 -57
@@ -15,9 +15,10 @@#include <media/ir-core.h> #include <linux/workqueue.h> #include <linux/spinlock.h> +#include <linux/sched.h> -/* Define the max number of bit transitions per IR keycode */ -#define MAX_IR_EVENT_SIZE 256 +/* Define the max number of pulse/space transitions to buffer */ +#define MAX_IR_EVENT_SIZE 512/* Used to handle IR raw handler extensions */ static LIST_HEAD (ir_raw_handler_list);
@@ -53,19 +54,30 @@/* Used to load the decoders */ static struct work_struct wq_load; +static void ir_raw_event_work (struct work_struct *work) +{ +s64 d; +struct ir_raw_event_ctrl *raw = + container_of(work, struct ir_raw_event_ctrl, rx_work); + +while (kfifo_out (&raw->kfifo, &d,sizeof (d)) ==sizeof (d)) +RUN_DECODER (decode, raw->input_dev, d); +} +int ir_raw_event_register (struct input_dev *input_dev) {struct ir_input_dev *ir =input_get_drvdata (input_dev); -int rc, size; +int rc; ir->raw =kzalloc (sizeof (*ir->raw),GFP_KERNEL );if (!ir->raw)return -ENOMEM ; - size =sizeof (struct ir_raw_event ) *MAX_IR_EVENT_SIZE *2 ; - size =roundup_pow_of_two (size); + ir->raw->input_dev = input_dev; +INIT_WORK (&ir->raw->rx_work, ir_raw_event_work); - rc =kfifo_alloc (&ir->raw->kfifo, size,GFP_KERNEL ); + rc =kfifo_alloc (&ir->raw->kfifo,sizeof (s64) *MAX_IR_EVENT_SIZE , +GFP_KERNEL );if (rc <0 ) {kfree (ir->raw); ir->raw =NULL ;
@@ -90,6 +102,7 @@if (!ir->raw)return ; +cancel_work_sync (&ir->raw->rx_work);RUN_DECODER (raw_unregister, input_dev);kfifo_free (&ir->raw->kfifo);
@@ -97,74 +110,90 @@ ir->raw =NULL ; } -int ir_raw_event_store (struct input_dev *input_dev,enum raw_event_type type) +/** +* ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders +* @input_dev: the struct input_dev device descriptor +* @duration: duration of the pulse or space in ns +* +* This routine (which may be called from an interrupt context) stores a +* pulse/space duration for the raw ir decoding state machines. Pulses are +* signalled as positive values and spaces as negative values. A zero value +* will reset the decoding state machines. +*/ +int ir_raw_event_store (struct input_dev *input_dev,s64 duration) { -struct ir_input_dev *ir =input_get_drvdata (input_dev); -struct timespec ts; -struct ir_raw_event event; -int rc; +struct ir_input_dev *ir =input_get_drvdata (input_dev);if (!ir->raw)return -EINVAL ; - event.type = type; - event.delta.tv_sec =0 ; - event.delta.tv_nsec =0 ; +if (kfifo_in (&ir->raw->kfifo, &duration,sizeof (duration)) !=sizeof (duration)) +return -ENOMEM ; -ktime_get_ts (&ts); +return 0 ; +} +EXPORT_SYMBOL_GPL (ir_raw_event_store); -if (timespec_equal (&ir->raw->last_event, &event.delta)) - event.type |=IR_START_EVENT ; -else - event.delta =timespec_sub (ts, ir->raw->last_event); +/** +* ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space +* @input_dev: the struct input_dev device descriptor +* @type: the type of the event that has occurred +* +* This routine (which may be called from an interrupt context) is used to +* store the beginning of an ir pulse or space (or the start/end of ir +* reception) for the raw ir decoding state machines. This is used by +* hardware which does not provide durations directly but only interrupts +* (or similar events) on state change. +*/ +int ir_raw_event_store_edge (struct input_dev *input_dev,enum raw_event_type type) +{ +struct ir_input_dev *ir =input_get_drvdata (input_dev); +ktime_t now; +s64 delta;/* ns */ +int rc =0 ; + +if (!ir->raw) +return -EINVAL ; -memcpy (&ir->raw->last_event, &ts,sizeof (ts)); + now =ktime_get (); + delta =ktime_to_ns (ktime_sub (now, ir->raw->last_event)); -if (event.delta.tv_sec) { - event.type |=IR_START_EVENT ; - event.delta.tv_sec =0 ; - event.delta.tv_nsec =0 ; - } +/* Check for a long duration since last event or if we're +* being called for the first time, note that delta can't +* possibly be negative. +*/ +if (delta >NSEC_PER_SEC || !ir->raw->last_type) + type |=IR_START_EVENT ; -kfifo_in (&ir->raw->kfifo, &event,sizeof (event)); +if (type &IR_START_EVENT ) +ir_raw_event_reset (input_dev); +else if (ir->raw->last_type &IR_SPACE ) + rc =ir_raw_event_store (input_dev, -delta); +else if (ir->raw->last_type &IR_PULSE ) + rc =ir_raw_event_store (input_dev, delta); +else +return 0 ; + ir->raw->last_event = now; + ir->raw->last_type = type;return rc; } -EXPORT_SYMBOL_GPL (ir_raw_event_store); +EXPORT_SYMBOL_GPL (ir_raw_event_store_edge); -int ir_raw_event_handle (struct input_dev *input_dev) +/** +* ir_raw_event_handle() - schedules the decoding of stored ir data +* @input_dev: the struct input_dev device descriptor +* +* This routine will signal the workqueue to start decoding stored ir data. +*/ +void ir_raw_event_handle (struct input_dev *input_dev) { -struct ir_input_dev *ir =input_get_drvdata (input_dev); -int rc; -struct ir_raw_event ev; -int len, i; - -/* -* Store the events into a temporary buffer. This allows calling more than -* one decoder to deal with the received data -*/ - len =kfifo_len (&ir->raw->kfifo) /sizeof (ev); -if (!len) -return 0 ; - -for (i =0 ; i < len; i++) { - rc =kfifo_out (&ir->raw->kfifo, &ev,sizeof (ev)); -if (rc !=sizeof (ev)) { -IR_dprintk (1 ,"overflow error: received %d instead of %zd\n" , - rc,sizeof (ev)); -return -EINVAL ; - } -IR_dprintk (2 ,"event type %d, time before event: %07luus\n" , - ev.type, (ev.delta.tv_nsec +500 ) /1000 ); - rc =RUN_DECODER (decode, input_dev, &ev); - } +struct ir_input_dev *ir =input_get_drvdata (input_dev); -/* -* Call all ir decoders. This allows decoding the same event with -* more than one protocol handler. -*/ +if (!ir->raw) +return ; -return rc; +schedule_work (&ir->raw->rx_work); }EXPORT_SYMBOL_GPL (ir_raw_event_handle);
include/media/ir-core.h +36 -11
70 lines not on this page · raw message
drivers/media/IR/ir-nec-decoder.c +97 -148
317 lines not on this page · raw message
drivers/media/IR/ir-rc5-decoder.c +76 -76
205 lines not on this page · raw message
drivers/media/video/saa7134/saa7134-input.c +1 -1
@@ -1018,7 +1018,7 @@saa_clearb (SAA7134_GPIO_GPMODE3 ,SAA7134_GPIO_GPRESCAN );saa_setb (SAA7134_GPIO_GPMODE3 ,SAA7134_GPIO_GPRESCAN ); space =saa_readl (SAA7134_GPIO_GPSTATUS0 >>2 ) & ir->mask_keydown; -ir_raw_event_store (dev->remote->dev, space ?IR_SPACE :IR_PULSE ); +ir_raw_event_store_edge (dev->remote->dev, space ?IR_SPACE :IR_PULSE );/*
Signature · 5 lines
David Härdeman -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 3 lines · David Härdeman
With this patch:
s64 int's are used to represent pulse/space durations in ns
If performing divides on 64 bit numbers, please check to make sure your code compiles, links, and loads on a 32-bit system.
We've had problems in the past in where gcc will build the module to reference __udivdi3 under 32-bit kernels; but that symbol is not in the kernel.
Search for 'do_div' in:
linux/drivers/media/video/cx18/cx18-av-core.c
for a simple example divide that works on both 64 and 32 bit machines.
Regards,
Andy
Quoted 4 lines · Andy Walls
Quoted 2 lines · David Härdeman
With this patch:
Quoted 8 lines
Quoted 1 line
s64 int's are used to represent pulse/space durations in ns
If performing divides on 64 bit numbers, please check to make sure your code compiles, links, and loads on a 32-bit system.
We've had problems in the past in where gcc will build the module to reference
__udivdi3under 32-bit kernels; but that symbol is not in the kernel.
Good catch!
Quoted 5 lines
Search for '
do_div' in:linux/drivers/media/video/cx18/cx18-av-core.cfor a simple example divide that works on both 64 and 32 bit machines.
Unfortunately, not all gcc versions complain about the lack of the __udivi32 library. Some (like the one I run on my desktop), will add some inlined assembler code for it, instead of requiring some library to do the division.
We'll likely only discover such bugs only after sending the code to linux-next (done later yesterday - but I suspect that it were pulled only today from my tree) and receiving a complain for the ones that run those robots that test hundreds of different CONFIG_foo options.
So, I suspect that we'll have some of such complaints by tomorrow...
Signature · 6 lines
Cheers, Mauro -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html