* GNU General Public License for more details.
*/
-#include <media/ir-core.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include "ir-core-priv.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);
_sumrc; \
})
-
+#ifdef MODULE
/* Used to load the decoders */
static struct work_struct wq_load;
+#endif
+
+static void ir_raw_event_work(struct work_struct *work)
+{
+ struct ir_raw_event ev;
+ struct ir_raw_event_ctrl *raw =
+ container_of(work, struct ir_raw_event_ctrl, rx_work);
+
+ while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev))
+ RUN_DECODER(decode, raw->input_dev, ev);
+}
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;
return rc;
}
-EXPORT_SYMBOL_GPL(ir_raw_event_register);
void ir_raw_event_unregister(struct input_dev *input_dev)
{
if (!ir->raw)
return;
+ cancel_work_sync(&ir->raw->rx_work);
RUN_DECODER(raw_unregister, input_dev);
kfifo_free(&ir->raw->kfifo);
kfree(ir->raw);
ir->raw = NULL;
}
-EXPORT_SYMBOL_GPL(ir_raw_event_unregister);
-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
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * 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, struct ir_raw_event *ev)
{
- 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, ev, sizeof(*ev)) != sizeof(*ev))
+ 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 */
+ struct ir_raw_event ev;
+ int rc = 0;
- memcpy(&ir->raw->last_event, &ts, sizeof(ts));
+ if (!ir->raw)
+ return -EINVAL;
- if (event.delta.tv_sec) {
- event.type |= IR_START_EVENT;
- event.delta.tv_sec = 0;
- event.delta.tv_nsec = 0;
- }
+ now = ktime_get();
+ delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
- kfifo_in(&ir->raw->kfifo, &event, sizeof(event));
+ /* 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.
+ */
+ ev.duration = 0;
+ if (delta > IR_MAX_DURATION || !ir->raw->last_type)
+ type |= IR_START_EVENT;
+ else
+ ev.duration = delta;
+
+ if (type & IR_START_EVENT)
+ ir_raw_event_reset(input_dev);
+ else if (ir->raw->last_type & IR_SPACE) {
+ ev.pulse = false;
+ rc = ir_raw_event_store(input_dev, &ev);
+ } else if (ir->raw->last_type & IR_PULSE) {
+ ev.pulse = true;
+ rc = ir_raw_event_store(input_dev, &ev);
+ } 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);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
+#ifdef MODULE
static void init_decoders(struct work_struct *work)
{
/* Load the decoder modules */
load_nec_decode();
load_rc5_decode();
+ load_rc6_decode();
+ load_jvc_decode();
+ load_sony_decode();
/* If needed, we may later add some init code. In this case,
it is needed to change the CONFIG_MODULE test at ir-core.h
*/
}
+#endif
void ir_raw_init(void)
{