summaryrefslogtreecommitdiffstats
path: root/drivers/media/IR/ir-nec-decoder.c
blob: 16360eb4055b69ec8ea3d46ab37ded3ed2e8d476 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* ir-raw-event.c - handle IR Pulse/Space event
 *
 * Copyright (C) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
 *
 * This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include <media/ir-core.h>

/* Start time: 4.5 ms  */
#define MIN_START_TIME	3900000
#define MAX_START_TIME	5100000

/* Pulse time: 560 us  */
#define MIN_PULSE_TIME	460000
#define MAX_PULSE_TIME	660000

/* Bit 1 space time: 2.25ms-560 us */
#define MIN_BIT1_TIME	1490000
#define MAX_BIT1_TIME	1890000

/* Bit 0 space time: 1.12ms-560 us */
#define MIN_BIT0_TIME	360000
#define MAX_BIT0_TIME	760000


/** Decode NEC pulsecode. This code can take up to 76.5 ms to run.
	Unfortunately, using IRQ to decode pulse didn't work, since it uses
	a pulse train of 38KHz. This means one pulse on each 52 us
*/

int ir_nec_decode(struct input_dev *input_dev,
		  struct ir_raw_event *evs,
		  int len)
{
	int i, count = -1;
	int ircode = 0, not_code = 0;
#if 0
	/* Needed only after porting the event code to the decoder */
	struct ir_input_dev *ir = input_get_drvdata(input_dev);
#endif

	/* Be sure that the first event is an start one and is a pulse */
	for (i = 0; i < len; i++) {
		if (evs[i].type & (IR_START_EVENT | IR_PULSE))
			break;
	}
	i++;	/* First event doesn't contain data */

	if (i >= len)
		return 0;

	/* First space should have 4.5 ms otherwise is not NEC protocol */
	if ((evs[i].delta.tv_nsec < MIN_START_TIME) |
	    (evs[i].delta.tv_nsec > MAX_START_TIME) |
	    (evs[i].type != IR_SPACE))
		goto err;

	/*
	 * FIXME: need to implement the repeat sequence
	 */

	count = 0;
	for (i++; i < len; i++) {
		int bit;

		if ((evs[i].delta.tv_nsec < MIN_PULSE_TIME) |
		    (evs[i].delta.tv_nsec > MAX_PULSE_TIME) |
		    (evs[i].type != IR_PULSE))
			goto err;

		if (++i >= len)
			goto err;
		if (evs[i].type != IR_SPACE)
			goto err;

		if ((evs[i].delta.tv_nsec > MIN_BIT1_TIME) &&
		    (evs[i].delta.tv_nsec < MAX_BIT1_TIME))
			bit = 1;
		else if ((evs[i].delta.tv_nsec > MIN_BIT0_TIME) &&
			 (evs[i].delta.tv_nsec < MAX_BIT0_TIME))
			bit = 0;
		else
			goto err;

		if (bit) {
			int shift = count;
			/* Address first, then command */
			if (shift < 8) {
				shift += 8;
				ircode |= 1 << shift;
			} else if (shift < 16) {
				not_code |= 1 << shift;
			} else if (shift < 24) {
				shift -= 16;
				ircode |= 1 << shift;
			} else {
				shift -= 24;
				not_code |= 1 << shift;
			}
		}
		if (++count == 32)
			break;
	}

	/*
	 * Fixme: may need to accept Extended NEC protocol?
	 */
	if ((ircode & ~not_code) != ircode) {
		IR_dprintk(1, "NEC checksum error: code 0x%04x, not-code 0x%04x\n",
			   ircode, not_code);
		return -EINVAL;
	}

	IR_dprintk(1, "NEC scancode 0x%04x\n", ircode);

	return ircode;
err:
	IR_dprintk(1, "NEC decoded failed at bit %d while decoding %luus time\n",
		   count, (evs[i].delta.tv_nsec + 500) / 1000);

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(ir_nec_decode);