V4L/DVB: ir-core: Add logic to decode IR protocols at the IR core
[safe/jmp/linux-2.6] / drivers / media / IR / ir-raw-event.c
1 /* ir-raw-event.c - handle IR Pulse/Space event
2  *
3  * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  */
14
15 #include <media/ir-core.h>
16
17 /* Define the max number of bit transitions per IR keycode */
18 #define MAX_IR_EVENT_SIZE       256
19
20 int ir_raw_event_register(struct input_dev *input_dev)
21 {
22         struct ir_input_dev *ir = input_get_drvdata(input_dev);
23         int rc, size;
24
25         ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL);
26
27         size = sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE * 2;
28         size = roundup_pow_of_two(size);
29
30         rc = kfifo_alloc(&ir->raw->kfifo, size, GFP_KERNEL);
31
32         return rc;
33 }
34 EXPORT_SYMBOL_GPL(ir_raw_event_register);
35
36 void ir_raw_event_unregister(struct input_dev *input_dev)
37 {
38         struct ir_input_dev *ir = input_get_drvdata(input_dev);
39
40         if (!ir->raw)
41                 return;
42
43         kfifo_free(&ir->raw->kfifo);
44         kfree(ir->raw);
45         ir->raw = NULL;
46 }
47 EXPORT_SYMBOL_GPL(ir_raw_event_unregister);
48
49 int ir_raw_event_store(struct input_dev *input_dev, enum raw_event_type type)
50 {
51         struct ir_input_dev     *ir = input_get_drvdata(input_dev);
52         struct timespec         ts;
53         struct ir_raw_event     event;
54         int                     rc;
55
56         if (!ir->raw)
57                 return -EINVAL;
58
59         event.type = type;
60         event.delta.tv_sec = 0;
61         event.delta.tv_nsec = 0;
62
63         ktime_get_ts(&ts);
64
65         if (timespec_equal(&ir->raw->last_event, &event.delta))
66                 event.type |= IR_START_EVENT;
67         else
68                 event.delta = timespec_sub(ts, ir->raw->last_event);
69
70         memcpy(&ir->raw->last_event, &ts, sizeof(ts));
71
72         if (event.delta.tv_sec) {
73                 event.type |= IR_START_EVENT;
74                 event.delta.tv_sec = 0;
75                 event.delta.tv_nsec = 0;
76         }
77
78         kfifo_in(&ir->raw->kfifo, &event, sizeof(event));
79
80         return rc;
81 }
82 EXPORT_SYMBOL_GPL(ir_raw_event_store);
83
84 int ir_raw_event_handle(struct input_dev *input_dev)
85 {
86         struct ir_input_dev             *ir = input_get_drvdata(input_dev);
87         int                             rc;
88         struct ir_raw_event             *evs;
89         int                             len, i;
90
91         /*
92          * Store the events into a temporary buffer. This allows calling more than
93          * one decoder to deal with the received data
94          */
95         len = kfifo_len(&ir->raw->kfifo) / sizeof(*evs);
96         if (!len)
97                 return 0;
98         evs = kmalloc(len * sizeof(*evs), GFP_ATOMIC);
99
100         for (i = 0; i < len; i++) {
101                 rc = kfifo_out(&ir->raw->kfifo, &evs[i], sizeof(*evs));
102                 if (rc != sizeof(*evs)) {
103                         IR_dprintk(1, "overflow error: received %d instead of %zd\n",
104                                    rc, sizeof(*evs));
105                         return -EINVAL;
106                 }
107                 IR_dprintk(2, "event type %d, time before event: %07luus\n",
108                         evs[i].type, (evs[i].delta.tv_nsec + 500) / 1000);
109         }
110
111         rc = ir_nec_decode(input_dev, evs, len);
112
113         kfree(evs);
114
115         return rc;
116 }
117 EXPORT_SYMBOL_GPL(ir_raw_event_handle);