Browse Source

easyvdr-lirc - uploaded2-3-base-stable-PPA

mango 10 months ago
parent
commit
ea4a1669fc

+ 1
- 0
e/easyvdr-lirc/.pc/.quilt_patches View File

@@ -0,0 +1 @@
1
+debian/patches

+ 1
- 0
e/easyvdr-lirc/.pc/.quilt_series View File

@@ -0,0 +1 @@
1
+series

+ 1
- 0
e/easyvdr-lirc/.pc/.version View File

@@ -0,0 +1 @@
1
+2

+ 1
- 0
e/easyvdr-lirc/.pc/applied-patches View File

@@ -0,0 +1 @@
1
+lirc-0.9.0.patch

+ 0
- 0
e/easyvdr-lirc/.pc/lirc-0.9.0.patch/.timestamp View File


+ 1821
- 0
e/easyvdr-lirc/.pc/lirc-0.9.0.patch/configure.ac
File diff suppressed because it is too large
View File


+ 381
- 0
e/easyvdr-lirc/.pc/lirc-0.9.0.patch/daemons/hw_mplay.c View File

@@ -0,0 +1,381 @@
1
+/****************************************************************************
2
+ ** hw_mplay.c **************************************************************
3
+ ****************************************************************************
4
+ *
5
+ * LIRC driver for Vlsys mplay usb ftdi serial port remote control.
6
+ *
7
+ * Driver inspire from hw_accent et hw_alsa_usb.
8
+ *
9
+ * The vlsys mplay is a remote control with an Ir receiver connected to the
10
+ * usb bus via a ftdi driver. The device communicate with the host at 38400
11
+ * 8N1.
12
+ * 
13
+ * For each keypress on the remote controle, one code byte is transmitted
14
+ * follow by regular fixe code byte for repetition if the key is held-down.
15
+ * For example, if you press key 1, the remote first send 0x4d (key code)
16
+ * and next regulary send 0x7e (repetetion code) as you held-down the key.
17
+ * For key 2 you get 0x4e 0x7e 0x7e ...
18
+ *
19
+ * Copyright (c) 2007 Benoit Laurent <ben905@free.fr>
20
+ *
21
+ * This program is free software; you can redistribute it and/or modify
22
+ * it under the terms of the GNU General Public License as published by
23
+ * the Free Software Foundation; either version 2 of the License, or
24
+ * (at your option) any later version.
25
+ *
26
+ * This program is distributed in the hope that it will be useful,
27
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29
+ * GNU General Public License for more details.
30
+ *
31
+ * You should have received a copy of the GNU General Public License
32
+ * along with this program; if not, write to the Free Software
33
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
34
+ */
35
+
36
+/* Functions available for logging (see tools/lircrcd.c).
37
+ *
38
+ * NOTE: if compiled without the DEBUG option and with SYSLOG,
39
+ * you cannot control the amount of debug info sent to syslog,
40
+ * even the LOG_DEBUG messages will be logged.
41
+ *
42
+ * void logprintf(int priority, const char *format, ...)
43
+ *     Calls the syslog(3) function.
44
+ *
45
+ * void logperror(int priority, const char *s)
46
+ *    Uses the syslog(3) to print a message followed by the error message
47
+ *    strerror (%m) associated to the present errno.
48
+ *
49
+ * void LOGPRINTF(int priority, const char *format, ...)
50
+ *    Calls logprintf(), but only if compiled with DEBUG option.
51
+ *
52
+ * void LOGPERROR(int priority, const char *s)
53
+ *    Calls logperror(), but only if compiled with DEBUG option.
54
+ */
55
+
56
+#ifdef HAVE_CONFIG_H
57
+#  include <config.h>
58
+#endif
59
+
60
+#ifndef LIRC_IRTTY
61
+#define LIRC_IRTTY "/dev/ttyUSB0"
62
+#endif
63
+
64
+#include <stdio.h>
65
+#include <sys/types.h>
66
+#include <fcntl.h>
67
+#include <termios.h>
68
+
69
+#include "lircd.h"
70
+#include "hardware.h"
71
+#include "serial.h"
72
+
73
+#include "hw_mplay.h"
74
+
75
+/* The mplay code length in bit */
76
+#define MPLAY_CODE_LENGTH 8
77
+
78
+/* Code value send by the mplay to indicate a repeatition of the last code */
79
+#define MPLAY_REPEAT_CODE 0x7e
80
+
81
+/* Mplay serial baud rate */
82
+#define MPLAY_BAUD_RATE 38400
83
+
84
+/* Max time in micro seconde between the reception of repetition code. After
85
+   this time, we ignore the key repeat */
86
+#define MAX_TIME_BETWEEN_TWO_REPETITION_CODE 500000
87
+
88
+/**************************************************************************
89
+ * Definition of local struct that permit to save data from call to call
90
+ * of the driver.
91
+ **************************************************************************/
92
+static struct {
93
+	/* the last receive code */
94
+	ir_code rc_code;
95
+	/* Int wich indicate that the last reception was a repetition */
96
+	int repeat_flag;
97
+	/* Date of the last reception */
98
+	struct timeval last_reception_time;
99
+	/* Flag wich indicate a timeout between the reception of repetition
100
+	   Some time the receiver lost a key code and only recieved
101
+	   the associated repetition code. Then the driver interpret
102
+	   this repetition as a repetition of the last receive key code
103
+	   and not the lost one (ex: you press Volume+ after Volume-
104
+	   and the sound continu to go down). To avoid this problem
105
+	   we set a max time between two repetition. */
106
+	int timeout_repetition_flag;
107
+} mplay_local_data = {
108
+	0, 0, {
109
+0, 0}, 0};
110
+
111
+/**************************************************************************
112
+ * Definition of the standard internal hardware interface
113
+ * use by lirc for the mplay device
114
+ **************************************************************************/
115
+struct hardware hw_mplay = {
116
+	LIRC_IRTTY,		/* default device */
117
+	-1,			/* fd */
118
+	LIRC_CAN_REC_LIRCCODE,	/* features */
119
+	0,			/* send_mode */
120
+	LIRC_MODE_LIRCCODE,	/* rec_mode */
121
+	MPLAY_CODE_LENGTH,	/* code_length */
122
+	mplay_init,		/* init_func */
123
+	mplay_deinit,		/* deinit_func */
124
+	NULL,			/* send_func */
125
+	mplay_rec,		/* rec_func */
126
+	mplay_decode,		/* decode_func */
127
+	NULL,			/* ioctl_func */
128
+	NULL,			/* readdata */
129
+	"mplay"
130
+};
131
+
132
+/**************************************************************************
133
+ * Definition of the standard internal hardware interface
134
+ * use by lirc for the mplay v2 (Monueal Moncaso) devices
135
+ **************************************************************************/
136
+struct hardware hw_mplay2 = {
137
+	LIRC_IRTTY,		/* default device */
138
+	-1,			/* fd */
139
+	LIRC_CAN_REC_LIRCCODE,	/* features */
140
+	0,			/* send_mode */
141
+	LIRC_MODE_LIRCCODE,	/* rec_mode */
142
+	MPLAY_CODE_LENGTH,	/* code_length */
143
+	mplay2_init,		/* init_func */
144
+	mplay_deinit,		/* deinit_func */
145
+	NULL,			/* send_func */
146
+	mplay_rec,		/* rec_func */
147
+	mplay_decode,		/* decode_func */
148
+	NULL,			/* ioctl_func */
149
+	NULL,			/* readdata */
150
+	"mplay2"
151
+};
152
+
153
+/**************************************************************************
154
+ * Lock and initialize the serial port.
155
+ * This function is called by the LIRC daemon when the first client
156
+ * registers itself.
157
+ * Return 1 on success, 0 on error.
158
+ **************************************************************************/
159
+int mplay_init(void)
160
+{
161
+	int result = 1;
162
+	LOGPRINTF(1, "Entering mplay_init()");
163
+	/* Creation of a lock file for the port */
164
+	if (!tty_create_lock(hw.device)) {
165
+		logprintf(LOG_ERR, "Could not create the lock file");
166
+		LOGPRINTF(1, "Could not create the lock file");
167
+		result = 0;
168
+	}
169
+	/* Try to open serial port */
170
+	else if ((hw.fd = open(hw.device, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
171
+		logprintf(LOG_ERR, "Could not open the serial port");
172
+		LOGPRINTF(1, "Could not open the serial port");
173
+		mplay_deinit();
174
+		result = 0;
175
+	}
176
+	/* Serial port configuration */
177
+	else if (!tty_reset(hw.fd) || !tty_setbaud(hw.fd, MPLAY_BAUD_RATE)) {
178
+		logprintf(LOG_ERR, "could not configure the serial port for '%s'", hw.device);
179
+		LOGPRINTF(1, "could not configure the serial port for '%s'", hw.device);
180
+		mplay_deinit();
181
+	}
182
+	return result;
183
+}
184
+
185
+int mplay2_init(void)
186
+{
187
+	struct termios portset;
188
+	signed int len;
189
+	char buf = 0x96;
190
+	char psResponse[11];
191
+
192
+	LOGPRINTF(1, "Entering mplay_init()");
193
+	/* Creation of a lock file for the port */
194
+	if (!tty_create_lock(hw.device)) {
195
+		logprintf(LOG_ERR, "Could not create the lock file");
196
+		LOGPRINTF(1, "Could not create the lock file");
197
+		return 0;
198
+	}
199
+
200
+	LOGPRINTF(0, "open serial port");
201
+	/* Try to open serial port (Monueal Moncaso 312 device doesn't like O_NONBLOCK */
202
+	if ((hw.fd = open(hw.device, O_RDWR | O_NOCTTY)) < 0) {
203
+		logprintf(LOG_ERR, "Could not open the serial port");
204
+		LOGPRINTF(1, "Could not open the serial port");
205
+		tty_delete_lock();
206
+		return 0;
207
+	}
208
+
209
+	/* Get serial device parameters */
210
+	if (tcgetattr(hw.fd, &portset) < 0) {
211
+		logprintf(LOG_ERR, "Could not get serial port attributes");
212
+		LOGPRINTF(1, "Could not get serial port attributes");
213
+		mplay_deinit();
214
+		return 0;
215
+	}
216
+
217
+	/* use own termios struct instead of using tty_reset , Moncaso doesn't like TCSAFLUSH */
218
+	portset.c_cflag &= ~PARENB;
219
+	portset.c_cflag &= ~CSTOPB;
220
+	portset.c_cflag &= ~CSIZE;
221
+	portset.c_cflag = B57600 | CS8;
222
+	portset.c_cflag |= (CLOCAL | CREAD);
223
+	portset.c_iflag |= (IXON | IXOFF | IXANY);
224
+	portset.c_oflag &= ~OPOST;
225
+	portset.c_lflag &= ~(ICANON | ECHOE | ECHO | ISIG);
226
+	portset.c_cc[VSTART] = 0x11;
227
+	portset.c_cc[VSTOP] = 0x13;
228
+	portset.c_cc[VEOF] = 0x20;
229
+	portset.c_cc[VMIN] = 1;
230
+	portset.c_cc[VTIME] = 3;
231
+
232
+	if (tcsetattr(hw.fd, TCSANOW, &portset) < 0) {
233
+		logprintf(LOG_ERR, "Error setting TCSANOW mode of serial device");
234
+		LOGPRINTF(1, "Error setting TCSANOW mode of serial device");
235
+		mplay_deinit();
236
+		return 0;
237
+	}
238
+
239
+	len = write(hw.fd, &buf, 1);
240
+	if (len < 0) {
241
+		LOGPRINTF(LOG_ERR, "couldn't write to device");
242
+		mplay_deinit();
243
+		return 0;
244
+	}
245
+
246
+	len = read(hw.fd, &psResponse, 11);
247
+	if (len < 0) {
248
+		LOGPRINTF(1, "No data recieved during reading");
249
+		mplay_deinit();
250
+		return 0;
251
+	} else
252
+		LOGPRINTF(1, "read chars: %s", psResponse);
253
+
254
+	if (tcgetattr(hw.fd, &portset) < 0) {
255
+		logprintf(LOG_ERR, "Could not get serial port attributes");
256
+		LOGPRINTF(1, "Could not get serial port attributes");
257
+		mplay_deinit();
258
+		return 0;
259
+	}
260
+
261
+	portset.c_cflag &= ~PARENB;
262
+	portset.c_cflag &= ~CSTOPB;
263
+	portset.c_cflag &= ~CSIZE;
264
+	portset.c_cflag = B57600 | CS8;
265
+	portset.c_cflag |= (CLOCAL | CREAD);
266
+	portset.c_iflag |= (IXON | IXOFF | IXANY);
267
+	portset.c_oflag &= ~OPOST;
268
+	portset.c_lflag &= ~(ICANON | ECHOE | ECHO | ISIG);
269
+	portset.c_cc[VSTART] = 0x11;
270
+	portset.c_cc[VSTOP] = 0x13;
271
+	portset.c_cc[VEOF] = 0x1C;
272
+	portset.c_cc[VMIN] = 1;
273
+	portset.c_cc[VTIME] = 3;
274
+
275
+	if (tcsetattr(hw.fd, TCSANOW, &portset) < 0) {
276
+		logprintf(LOG_ERR, "Error setting TCSANOW mode of serial device");
277
+		LOGPRINTF(1, "Error setting TCSANOW mode of serial device");
278
+		mplay_deinit();
279
+		return 0;
280
+	}
281
+
282
+	return 1;
283
+}
284
+
285
+/**************************************************************************
286
+ * Close and release the serial line.
287
+ **************************************************************************/
288
+int mplay_deinit(void)
289
+{
290
+	LOGPRINTF(1, "Entering mplay_deinit()");
291
+	close(hw.fd);
292
+	tty_delete_lock();
293
+	hw.fd = -1;
294
+	return (1);
295
+}
296
+
297
+/**************************************************************************
298
+ * Receive a code (1 byte) from the remote.
299
+ * This function is called by the LIRC daemon when I/O is pending
300
+ * from a registered client, e.g. irw.
301
+ *
302
+ * return NULL if nothing have been received or a lirc code
303
+ **************************************************************************/
304
+char *mplay_rec(struct ir_remote *remotes)
305
+{
306
+	unsigned char rc_code;
307
+	signed int len;
308
+	struct timeval current_time;
309
+	LOGPRINTF(1, "Entering mplay_rec()");
310
+	len = read(hw.fd, &rc_code, 1);
311
+	gettimeofday(&current_time, NULL);
312
+	if (len != 1) {
313
+		/* Something go wrong during the read, we close the device 
314
+		   for prevent endless looping when the device 
315
+		   is disconnected */
316
+		LOGPRINTF(1, "Reading error in mplay_rec()");
317
+		mplay_deinit();
318
+		return NULL;
319
+	} else {
320
+		/* We have received a code */
321
+		if (rc_code == MPLAY_REPEAT_CODE) {
322
+			if (mplay_local_data.timeout_repetition_flag == 1) {
323
+				/* We ignore the repetition */
324
+				return NULL;
325
+			} else {
326
+				if (time_elapsed(&mplay_local_data.last_reception_time, &current_time) <=
327
+				    MAX_TIME_BETWEEN_TWO_REPETITION_CODE) {
328
+					/* This reception is a repeat */
329
+					mplay_local_data.repeat_flag = 1;
330
+					/* We save the reception time */
331
+					mplay_local_data.last_reception_time = current_time;
332
+				} else {
333
+					/* To much time between repetition, 
334
+					   the receiver have  probably miss 
335
+					   a valide key code. We ignore the 
336
+					   repetition */
337
+					mplay_local_data.timeout_repetition_flag = 1;
338
+					mplay_local_data.repeat_flag = 0;
339
+					return NULL;
340
+				}
341
+			}
342
+		} else {
343
+			/* This is a new code */
344
+			mplay_local_data.rc_code = rc_code;
345
+			mplay_local_data.repeat_flag = 0;
346
+			mplay_local_data.timeout_repetition_flag = 0;
347
+			mplay_local_data.last_reception_time = current_time;
348
+		}
349
+		LOGPRINTF(1, "code: %u", (unsigned int)mplay_local_data.rc_code);
350
+		LOGPRINTF(1, "repeat_flag: %d", mplay_local_data.repeat_flag);
351
+		return decode_all(remotes);
352
+	}
353
+}
354
+
355
+/**************************************************************************
356
+ * This function is called by the LIRC daemon during the transform of a
357
+ * received code into an lirc event.
358
+ *
359
+ * It gets the global variable code (remote keypress code).
360
+ *
361
+ * It returns:
362
+ *  prep                Code prefix (zero for this LIRC driver)
363
+ *  codep               Code of keypress
364
+ *  postp               Trailing code (zero for this LIRC dirver)
365
+ *  repeat_flagp        True if the keypress is a repeated keypress
366
+ *  min_remaining_gapp  Min extimated time gap remaining before next code
367
+ *  max_remaining_gapp  Max extimated time gap remaining before next code
368
+ **************************************************************************/
369
+int mplay_decode(struct ir_remote *remote, ir_code * prep, ir_code * codep, ir_code * postp, int *repeat_flagp,
370
+		 lirc_t * min_remaining_gapp, lirc_t * max_remaining_gapp)
371
+{
372
+	LOGPRINTF(1, "Entering mplay_decode(), code = %u\n", (unsigned int)mplay_local_data.rc_code);
373
+
374
+	if (!map_code(remote, prep, codep, postp, 0, 0, MPLAY_CODE_LENGTH, mplay_local_data.rc_code, 0, 0)) {
375
+		return (0);
376
+	}
377
+	*repeat_flagp = mplay_local_data.repeat_flag;
378
+	*min_remaining_gapp = 0;
379
+	*max_remaining_gapp = 0;
380
+	return 1;
381
+}

+ 38
- 0
e/easyvdr-lirc/.pc/lirc-0.9.0.patch/daemons/hw_mplay.h View File

@@ -0,0 +1,38 @@
1
+/****************************************************************************
2
+ ** hw_mplay.h **************************************************************
3
+ ****************************************************************************
4
+ *
5
+ * LIRC driver for Vlsys mplay usb ftdi serial port remote control.
6
+ * 
7
+ * Author:	Benoit Laurent <ben905@free.fr>
8
+ *
9
+ * This program is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License
20
+ * along with this program; if not, write to the Free Software
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ *
23
+ */
24
+
25
+#ifndef HW_MPLAY_H
26
+#define HW_MPLAY_H
27
+
28
+#include "drivers/lirc.h"
29
+
30
+extern int mplay_decode(struct ir_remote *remote, ir_code * prep, ir_code * codep, ir_code * postp, int *repeat_flagp,
31
+			lirc_t * min_remaining_gapp, lirc_t * max_remaining_gapp);
32
+
33
+extern int mplay_init(void);
34
+extern int mplay2_init(void);
35
+extern int mplay_deinit(void);
36
+extern char *mplay_rec(struct ir_remote *remotes);
37
+
38
+#endif

+ 7
- 1
e/easyvdr-lirc/configure.ac View File

@@ -467,6 +467,8 @@ atwf83_lib=""
467 467
 awlibusb_lib=""
468 468
 dfclibusb_lib=""
469 469
 srm7500libusb_lib=""
470
+mplay_lib=""
471
+mplay2_lib=""
470 472
 hw_module="hw_default.o receive.o transmit.o"
471 473
 HW_DEFAULT="hw_default"
472 474
 kernel_module=""
@@ -638,9 +640,11 @@ if test "$driver" = "userspace" -o "$driver" = "all"; then
638 640
 		;;
639 641
 	mplay)
640 642
 		hw_module="${hw_module} hw_mplay.o serial.o"
643
+		mplay_lib="-lpthread -lrt"
641 644
 		;;
642 645
 	mplay2)
643 646
 		hw_module="${hw_module} hw_mplay.o serial.o"
647
+		mplay2_lib="-lpthread -lrt"
644 648
 		;;
645 649
 	pcmak*)
646 650
 		hw_module="${hw_module} hw_pcmak.o serial.o"
@@ -1189,6 +1193,7 @@ if test "$driver" = "mplay"; then
1189 1193
   hw_module="hw_mplay.o serial.o"
1190 1194
   HW_DEFAULT="hw_mplay"
1191 1195
   lircd_conf="vlsystem/lircd.conf.mplay"
1196
+  mplay_lib="-lpthread -lrt"
1192 1197
 fi
1193 1198
 
1194 1199
 if test "$driver" = "mplay2"; then
@@ -1196,6 +1201,7 @@ if test "$driver" = "mplay2"; then
1196 1201
   hw_module="hw_mplay.o serial.o"
1197 1202
   HW_DEFAULT="hw_mplay2"
1198 1203
   lircd_conf="vlsystem/lircd.conf.mplay"
1204
+  mplay2_lib="-lpthread -lrt"
1199 1205
 fi
1200 1206
 
1201 1207
 if test "$driver" = "nslu2"; then
@@ -1669,7 +1675,7 @@ if ! echo ${hw_module}|grep " receive.o" >/dev/null; then
1669 1675
 	receive="receive.o"
1670 1676
 fi
1671 1677
 
1672
-hw_module_libs="${alsa_lib} ${atilibusb_lib} ${awlibusb_lib} ${caraca_lib} ${commandir_lib} ${dfclibusb_lib} ${ftdi_lib} ${iguanaIR_lib} ${irman_lib} ${portaudio_lib} ${srm7500libusb_lib} ${atwf83_lib}"
1678
+hw_module_libs="${alsa_lib} ${atilibusb_lib} ${awlibusb_lib} ${caraca_lib} ${commandir_lib} ${dfclibusb_lib} ${ftdi_lib} ${iguanaIR_lib} ${irman_lib} ${portaudio_lib} ${srm7500libusb_lib} ${atwf83_lib} ${mplay_lib} ${mplay2_lib}"
1673 1679
 
1674 1680
 dnl tell the Makefiles what we decided
1675 1681
 AC_SUBST(daemon)

+ 645
- 178
e/easyvdr-lirc/daemons/hw_mplay.c View File

@@ -12,11 +12,14 @@
12 12
  * 
13 13
  * For each keypress on the remote controle, one code byte is transmitted
14 14
  * follow by regular fixe code byte for repetition if the key is held-down.
15
- * For example, if you press key 1, the remote first send 0x4d (key code)
16
- * and next regulary send 0x7e (repetetion code) as you held-down the key.
17
- * For key 2 you get 0x4e 0x7e 0x7e ...
15
+ * For example, if you press key 1, the remote first send 0x4d (key code) and
16
+ * next regulary send 0x7e (repetetion code) as you held-down the key. For
17
+ * key 2 you get 0x4e 0x7e 0x7e ...
18 18
  *
19 19
  * Copyright (c) 2007 Benoit Laurent <ben905@free.fr>
20
+ * Copyright (c) 2011,2012 
21
+ *                    Wolfgang Hauck <wolfgang.hauck@gmx.de>
22
+ *                    (Added support for wheel/knob.)
20 23
  *
21 24
  * This program is free software; you can redistribute it and/or modify
22 25
  * it under the terms of the GNU General Public License as published by
@@ -35,9 +38,9 @@
35 38
 
36 39
 /* Functions available for logging (see tools/lircrcd.c).
37 40
  *
38
- * NOTE: if compiled without the DEBUG option and with SYSLOG,
39
- * you cannot control the amount of debug info sent to syslog,
40
- * even the LOG_DEBUG messages will be logged.
41
+ * NOTE: if compiled without the DEBUG option and with SYSLOG, you cannot
42
+ * control the amount of debug info sent to syslog, even the LOG_DEBUG
43
+ * messages will be logged.
41 44
  *
42 45
  * void logprintf(int priority, const char *format, ...)
43 46
  *     Calls the syslog(3) function.
@@ -61,10 +64,16 @@
61 64
 #define LIRC_IRTTY "/dev/ttyUSB0"
62 65
 #endif
63 66
 
64
-#include <stdio.h>
65
-#include <sys/types.h>
67
+#include <errno.h>
68
+#include <stdio.h> 
69
+#include <stdint.h>
70
+#include <pthread.h>
71
+#include <time.h>
66 72
 #include <fcntl.h>
67 73
 #include <termios.h>
74
+#include <sys/types.h>
75
+#include <sys/timerfd.h>
76
+#include <sys/ioctl.h>
68 77
 
69 78
 #include "lircd.h"
70 79
 #include "hardware.h"
@@ -72,18 +81,82 @@
72 81
 
73 82
 #include "hw_mplay.h"
74 83
 
84
+
75 85
 /* The mplay code length in bit */
76 86
 #define MPLAY_CODE_LENGTH 8
77 87
 
88
+/* Wheel angle period */
89
+#define MPLAY_ANGLE_PERIOD 4
90
+
91
+/* There was no latest action */
92
+#define MPLAY_ACTION_NONE -1
93
+/* Latest relevant action was wheel turn */
94
+#define MPLAY_ACTION_WHEEL 0
95
+/* Latest relevant action was button press */
96
+#define MPLAY_ACTION_BUTTON 1
97
+
98
+/* IR termination code sent by listener thread in error cases */
99
+#define MPLAY_CODE_ERROR 0xFF
100
+/* Code if no action took place */
101
+#define MPLAY_CODE_NOP 0x00
102
+/* Code sent to LIRC framework for counter-clockwise rotation */
103
+#define MPLAY_CODE_TURN_LEFT 0x80
104
+/* Code sent to LIRC framework for clockwise rotation */
105
+#define MPLAY_CODE_TURN_RIGHT 0x81
106
+/* Code sent to LIRC framework for knob press */
107
+#define MPLAY_CODE_KNOB 0x82
78 108
 /* Code value send by the mplay to indicate a repeatition of the last code */
79
-#define MPLAY_REPEAT_CODE 0x7e
109
+#define MPLAY_CODE_REPEAT 0x7E
110
+
111
+/* Period of listener thread in nanoseconds in idle phase (40ms) */
112
+#define MPLAY_LISTENER_PERIOD_IDLE 40000000
113
+/* Period of listener thread in nanoseconds when wheel is busy (2.5ms) */
114
+#define MPLAY_LISTENER_PERIOD_BUSY 2500000
115
+/* Counter for poll actions during busy phase until going idle */
116
+#define MPLAY_LISTENER_COUNTER_WAIT_WHEEL 64
117
+/* Counter for poll actions during busy phase until going idle */
118
+#define MPLAY_LISTENER_COUNTER_WAIT_BUTTON 8
119
+/* Bit position for rotation sensor A */
120
+#define MPLAY_ROTATION_SENSOR_POS_A 8
121
+/* Mask for rotation sensor A */
122
+#define MPLAY_ROTATION_SENSOR_MASK_A (1 << MPLAY_ROTATION_SENSOR_POS_A)
123
+/* Bit position for rotation sensor B */
124
+#define MPLAY_ROTATION_SENSOR_POS_B 5
125
+/* Mask for rotation sensor B */
126
+#define MPLAY_ROTATION_SENSOR_MASK_B (1 << MPLAY_ROTATION_SENSOR_POS_B)
127
+/* Mask for rotation sensor */
128
+#define MPLAY_ROTATION_SENSOR_MASK (MPLAY_ROTATION_SENSOR_MASK_A | MPLAY_ROTATION_SENSOR_MASK_B)
80 129
 
81 130
 /* Mplay serial baud rate */
82 131
 #define MPLAY_BAUD_RATE 38400
83 132
 
84
-/* Max time in micro seconde between the reception of repetition code. After
85
-   this time, we ignore the key repeat */
86
-#define MAX_TIME_BETWEEN_TWO_REPETITION_CODE 500000
133
+/* Mplay2 serial baud rate */
134
+#define MPLAY2_BAUD_RATE 57600
135
+
136
+/* Mplay2 initialisation character sent to device */
137
+#define MPLAY2_INIT_CHAR 0x96
138
+
139
+/* Mplay2 initialisation length of response to initialisation character */
140
+#define MPLAY2_INIT_RESPONSE_LENGTH 11
141
+
142
+/* Min time in micro seconds between button presses (125ms). Faster button
143
+ * presses are ignored because they are likely to be generated by bouncing
144
+ * effects. Here, only the knob is subject to bouncing effects. */
145
+#define MIN_TIME_BETWEEN_PRESSES 125000
146
+/* If two knob presses occur within 400ms, this is interpreted as
147
+ * repetition. */
148
+#define MAX_TIME_KNOB_PRESS_IS_REPETITION 400000
149
+
150
+/* Max time in microseconds between the reception of repetition code (400ms).
151
+ * After this time, we ignore key repetitions. */
152
+#define MAX_TIME_BETWEEN_TWO_REPETITION_CODE 400000
153
+
154
+/* Convert serial line status to gray code */
155
+#define MPLAY_STATUS_TO_GRAY(s)						\
156
+	((((s) & MPLAY_ROTATION_SENSOR_MASK_A) >> (MPLAY_ROTATION_SENSOR_POS_A - 1)) | \
157
+	 (((s) & MPLAY_ROTATION_SENSOR_MASK_B) >> (MPLAY_ROTATION_SENSOR_POS_B)))
158
+/* Convert gray code to binary */
159
+#define MPLAY_GRAY_TO_BIN(g) ((g) ^ ((g) >> 1))
87 160
 
88 161
 /**************************************************************************
89 162
  * Definition of local struct that permit to save data from call to call
@@ -96,17 +169,41 @@ static struct {
96 169
 	int repeat_flag;
97 170
 	/* Date of the last reception */
98 171
 	struct timeval last_reception_time;
99
-	/* Flag wich indicate a timeout between the reception of repetition
100
-	   Some time the receiver lost a key code and only recieved
101
-	   the associated repetition code. Then the driver interpret
102
-	   this repetition as a repetition of the last receive key code
103
-	   and not the lost one (ex: you press Volume+ after Volume-
104
-	   and the sound continu to go down). To avoid this problem
105
-	   we set a max time between two repetition. */
172
+	/* Flag wich indicates a timeout between the reception of repetitions, or detects
173
+	 * spurious knob presses following shortly after knob has actually been pressed.
174
+	 *
175
+	 * Sometimes the receiver loses a key code and only receives the associated repetition
176
+	 * code. Then the driver interprets this repetition as a repetition of the last received
177
+	 * key code and not of the lost one (e.g. you press volume+ after volume- and the sound
178
+	 * continues to go down). To avoid this problem we set a max time between two
179
+	 * repetition.
180
+	 *
181
+	 * As another phenomen, the knob produces too many codes following the first knob press
182
+	 * to shortly. So, there has to be a minimum distance between button presses. */
106 183
 	int timeout_repetition_flag;
107
-} mplay_local_data = {
108
-	0, 0, {
109
-0, 0}, 0};
184
+	/* MPLAY_ACTION_WHEEL if wheel was turned latest, 
185
+	 * MPLAY_ACTION_BUTTON if remote control button was pressed latest,
186
+	 * MPLAY_ACTION_NONE otherwise. */
187
+	int latest_action;
188
+	/* Latest pressed button other than knob (for handling of repeat sequences) */
189
+	unsigned char latest_button;
190
+        /* File descriptor of serial port where IR is attached to */
191
+	int fd;
192
+        /* File descriptors of pipe into LIRC framework. */
193
+	int pipefd[2];
194
+	/* ID of threat that listens on the serial port. */
195
+	pthread_t tid;
196
+} mplayfamily_local_data = {
197
+	.rc_code = 0,
198
+	.repeat_flag = 0,
199
+	.last_reception_time = {0, 0},
200
+	.timeout_repetition_flag = 0,
201
+ 	.latest_action = MPLAY_ACTION_NONE,
202
+	.latest_button = MPLAY_CODE_ERROR,
203
+	.fd = -1,
204
+	.pipefd =  {-1, -1},
205
+	.tid = -1
206
+};
110 207
 
111 208
 /**************************************************************************
112 209
  * Definition of the standard internal hardware interface
@@ -119,11 +216,11 @@ struct hardware hw_mplay = {
119 216
 	0,			/* send_mode */
120 217
 	LIRC_MODE_LIRCCODE,	/* rec_mode */
121 218
 	MPLAY_CODE_LENGTH,	/* code_length */
122
-	mplay_init,		/* init_func */
123
-	mplay_deinit,		/* deinit_func */
219
+	mplay_init,             /* init_func */
220
+	mplayfamily_deinit,	/* deinit_func */
124 221
 	NULL,			/* send_func */
125
-	mplay_rec,		/* rec_func */
126
-	mplay_decode,		/* decode_func */
222
+	mplayfamily_rec,	/* rec_func */
223
+	mplayfamily_decode,	/* decode_func */
127 224
 	NULL,			/* ioctl_func */
128 225
 	NULL,			/* readdata */
129 226
 	"mplay"
@@ -131,7 +228,7 @@ struct hardware hw_mplay = {
131 228
 
132 229
 /**************************************************************************
133 230
  * Definition of the standard internal hardware interface
134
- * use by lirc for the mplay v2 (Monueal Moncaso) devices
231
+ * use by lirc for the mplay v2 (Monueal MonCaso) devices
135 232
  **************************************************************************/
136 233
 struct hardware hw_mplay2 = {
137 234
 	LIRC_IRTTY,		/* default device */
@@ -141,213 +238,582 @@ struct hardware hw_mplay2 = {
141 238
 	LIRC_MODE_LIRCCODE,	/* rec_mode */
142 239
 	MPLAY_CODE_LENGTH,	/* code_length */
143 240
 	mplay2_init,		/* init_func */
144
-	mplay_deinit,		/* deinit_func */
241
+	mplayfamily_deinit,	/* deinit_func */
145 242
 	NULL,			/* send_func */
146
-	mplay_rec,		/* rec_func */
147
-	mplay_decode,		/* decode_func */
243
+	mplayfamily_rec,	/* rec_func */
244
+	mplayfamily_decode,	/* decode_func */
148 245
 	NULL,			/* ioctl_func */
149 246
 	NULL,			/* readdata */
150 247
 	"mplay2"
151 248
 };
152 249
 
250
+
153 251
 /**************************************************************************
154
- * Lock and initialize the serial port.
155
- * This function is called by the LIRC daemon when the first client
156
- * registers itself.
252
+ * Initialises mplay receiver.
157 253
  * Return 1 on success, 0 on error.
158 254
  **************************************************************************/
159
-int mplay_init(void)
255
+static int mplay_init_receiver(void)
160 256
 {
161
-	int result = 1;
162
-	LOGPRINTF(1, "Entering mplay_init()");
163
-	/* Creation of a lock file for the port */
164
-	if (!tty_create_lock(hw.device)) {
165
-		logprintf(LOG_ERR, "Could not create the lock file");
166
-		LOGPRINTF(1, "Could not create the lock file");
167
-		result = 0;
168
-	}
169
-	/* Try to open serial port */
170
-	else if ((hw.fd = open(hw.device, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
171
-		logprintf(LOG_ERR, "Could not open the serial port");
172
-		LOGPRINTF(1, "Could not open the serial port");
173
-		mplay_deinit();
174
-		result = 0;
257
+	return 1;
258
+}
259
+
260
+/**************************************************************************
261
+ * Sends initialisation character to  MonCaso 312/320 IR device
262
+ * (helper function for mplay2_init).
263
+ * Return 1 on success, 0 on error.
264
+ **************************************************************************/
265
+static int mplay2_send_init_char(void)
266
+{
267
+	const char init = MPLAY2_INIT_CHAR;
268
+
269
+	if (write(mplayfamily_local_data.fd, &init, 1) < 0) {
270
+		return 0;
271
+	} else {
272
+		return 1;
175 273
 	}
176
-	/* Serial port configuration */
177
-	else if (!tty_reset(hw.fd) || !tty_setbaud(hw.fd, MPLAY_BAUD_RATE)) {
178
-		logprintf(LOG_ERR, "could not configure the serial port for '%s'", hw.device);
179
-		LOGPRINTF(1, "could not configure the serial port for '%s'", hw.device);
180
-		mplay_deinit();
274
+}
275
+
276
+/**************************************************************************
277
+ * Retrieves initialisation response from MonCaso 312/320 IR device
278
+ * (helper function for mplay2_init).
279
+ * MonCaso 320 returns ".M428.M428.".
280
+ * Return 1 on success, 0 on error.
281
+ **************************************************************************/
282
+static int mplay2_retrieve_init_response(void)
283
+{
284
+	int i;
285
+	char response[MPLAY2_INIT_RESPONSE_LENGTH + 1]; /* contains string terminating zero */
286
+
287
+	/* Reset response buffer */
288
+	memset(response, 0, sizeof(response));
289
+
290
+	/* Read-function blocks until characters arrive from serial device */
291
+	fcntl(mplayfamily_local_data.fd, F_SETFL, 0);
292
+	/* Get response to initialisation character */
293
+	for (i = 0; i < MPLAY2_INIT_RESPONSE_LENGTH; i++) {
294
+		/* Get next character (blocks until arrival in order to avoid polling) */
295
+		if (read(mplayfamily_local_data.fd, &response[i], 1) < 0) {
296
+			return 0;
297
+		}
181 298
 	}
182
-	return result;
299
+	/* Restore non-blocking behaviour */
300
+	fcntl(mplayfamily_local_data.fd, F_SETFL, FNDELAY);
301
+	LOGPRINTF(1, "Device initialisation response: %s", response);
302
+
303
+	return 1;
183 304
 }
184 305
 
185
-int mplay2_init(void)
306
+/**************************************************************************
307
+ * Initialises MonCaso 312/320 IR receiver.
308
+ * Return 1 on success, 0 on error.
309
+ **************************************************************************/
310
+static int mplay2_init_receiver(void)
186 311
 {
187
-	struct termios portset;
188
-	signed int len;
189
-	char buf = 0x96;
190
-	char psResponse[11];
312
+	return mplay2_send_init_char() && mplay2_retrieve_init_response();
313
+}
191 314
 
192
-	LOGPRINTF(1, "Entering mplay_init()");
193
-	/* Creation of a lock file for the port */
194
-	if (!tty_create_lock(hw.device)) {
195
-		logprintf(LOG_ERR, "Could not create the lock file");
196
-		LOGPRINTF(1, "Could not create the lock file");
315
+/**************************************************************************
316
+ * Cleans up resources used by the listener thread.
317
+ **************************************************************************/
318
+static void mplayfamily_listen_cleanup(void* arg)
319
+{
320
+	close((int) arg);
321
+}
322
+
323
+/**************************************************************************
324
+ * Sets period for polling loop in listener thread.
325
+ * Returns 1 on success, 0 on error; errno is set.
326
+ **************************************************************************/
327
+static int mplayfamily_set_listener_period(int fd, unsigned int period)
328
+{
329
+	struct timespec now;
330
+	struct itimerspec p;
331
+	if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) {
197 332
 		return 0;
198 333
 	}
199
-
200
-	LOGPRINTF(0, "open serial port");
201
-	/* Try to open serial port (Monueal Moncaso 312 device doesn't like O_NONBLOCK */
202
-	if ((hw.fd = open(hw.device, O_RDWR | O_NOCTTY)) < 0) {
203
-		logprintf(LOG_ERR, "Could not open the serial port");
204
-		LOGPRINTF(1, "Could not open the serial port");
205
-		tty_delete_lock();
334
+	p.it_interval.tv_sec = 0;
335
+	p.it_interval.tv_nsec = period;
336
+	p.it_value.tv_sec = now.tv_sec;
337
+	p.it_value.tv_nsec = now.tv_nsec;
338
+	if (timerfd_settime (fd, TFD_TIMER_ABSTIME, &p, NULL) < 0) {
206 339
 		return 0;
207 340
 	}
341
+	return 1;
342
+}
208 343
 
209
-	/* Get serial device parameters */
210
-	if (tcgetattr(hw.fd, &portset) < 0) {
211
-		logprintf(LOG_ERR, "Could not get serial port attributes");
212
-		LOGPRINTF(1, "Could not get serial port attributes");
213
-		mplay_deinit();
214
-		return 0;
344
+/**************************************************************************
345
+ * Gets a wheel action.
346
+ * The function returns a virtual button press if the absolute angle has
347
+ * changed and is a multiple of 4. The absolute value is constructed from
348
+ * the sensor measure values by tracking the turn actions. If a wheel
349
+ * action has taken place, the function re-triggers the busy phase by
350
+ * setting the busy counter to a maximum and reducing the polling period.
351
+ *
352
+ * Parameters: 
353
+ *   File descriptor of periodic timer;
354
+ *   counter (in/out), used to control idle/busy polling phases;
355
+ *   angle measured by sensor(in/out), values 0, 1, 2, 3; 
356
+ *   absolute angle (in/out), tracks sensor measurement values;
357
+ *   event angle (in/out), when turn took place.
358
+ *
359
+ * Returns:
360
+ *   MPLAY_CODE_TURN_LEFT, MPLAY_CODE_TURN_RIGHT, 
361
+ *   MPLAY_CODE_NOP, MPLAY_CODE_ERROR.
362
+ **************************************************************************/
363
+static unsigned char mplayfamily_get_wheel(
364
+	int fd, unsigned int *counter, 
365
+	unsigned int *sensor, unsigned int *absolute, unsigned int *event)
366
+{
367
+	unsigned char code = MPLAY_CODE_NOP;
368
+	unsigned int status; /* Status of wheel sensors */
369
+	unsigned int new; /* new angle */
370
+
371
+	/* Read status of angle sensor */
372
+	if (ioctl(mplayfamily_local_data.fd, TIOCMGET, &status) < 0) {
373
+		logperror(LOG_ERR, "mplay listener ioctl failed");
374
+		return MPLAY_CODE_ERROR;
375
+	}
376
+	/* Evaluate wheel status */
377
+	else {
378
+		new = MPLAY_GRAY_TO_BIN(MPLAY_STATUS_TO_GRAY(status));
379
+		/* Check if wheel has been turned */
380
+		if (new != *sensor) {
381
+			signed int diff;
382
+			unsigned int direction = (new - *sensor + MPLAY_ANGLE_PERIOD) % MPLAY_ANGLE_PERIOD;
383
+			LOGPRINTF(3, "mplay wheel reports angle %d", new);
384
+			if (direction > MPLAY_ANGLE_PERIOD / 2) {
385
+				(*absolute)--;
386
+				LOGPRINTF(3, "mplay wheel turned left");
387
+			} else if (direction < MPLAY_ANGLE_PERIOD / 2) {
388
+				(*absolute)++;
389
+				LOGPRINTF(3, "mplay wheel turned right");
390
+			} else {
391
+				*absolute += (*absolute - new) % MPLAY_ANGLE_PERIOD;
392
+				LOGPRINTF(3, "mplay wheel turn skipped");
393
+			}
394
+			diff = *absolute - *event;
395
+			LOGPRINTF(3, "mplay wheel absolute %u, diff %d", *absolute, diff);
396
+			if (diff % MPLAY_ANGLE_PERIOD == 0) {
397
+				if (diff > 0) {
398
+					LOGPRINTF(2, "mplay wheel clockwise");
399
+					*event = *absolute;
400
+					code = MPLAY_CODE_TURN_RIGHT;
401
+				} else if (diff < 0) {
402
+					LOGPRINTF(2, "mplay wheel counter clockwise");
403
+					*event = *absolute;
404
+					code = MPLAY_CODE_TURN_LEFT;
405
+				}
406
+			}
407
+			*sensor = new;
408
+			if (*counter == 0) {
409
+				/* Something has happened, so return to busy polling mode;
410
+				 * which is only necessary if busy period has expired. */
411
+			    if (!mplayfamily_set_listener_period(fd, MPLAY_LISTENER_PERIOD_BUSY)) {
412
+				    logperror(LOG_ERR, "mplay listener could not set listener period");
413
+				}
414
+				LOGPRINTF(2, "mplay polls with busy rate, wheel has been turned");
415
+			}
416
+			/* Stay alert for a while, enter busy phase */
417
+			*counter = MPLAY_LISTENER_COUNTER_WAIT_WHEEL;
418
+		}
215 419
 	}
216 420
 
217
-	/* use own termios struct instead of using tty_reset , Moncaso doesn't like TCSAFLUSH */
218
-	portset.c_cflag &= ~PARENB;
219
-	portset.c_cflag &= ~CSTOPB;
220
-	portset.c_cflag &= ~CSIZE;
221
-	portset.c_cflag = B57600 | CS8;
222
-	portset.c_cflag |= (CLOCAL | CREAD);
223
-	portset.c_iflag |= (IXON | IXOFF | IXANY);
224
-	portset.c_oflag &= ~OPOST;
225
-	portset.c_lflag &= ~(ICANON | ECHOE | ECHO | ISIG);
226
-	portset.c_cc[VSTART] = 0x11;
227
-	portset.c_cc[VSTOP] = 0x13;
228
-	portset.c_cc[VEOF] = 0x20;
229
-	portset.c_cc[VMIN] = 1;
230
-	portset.c_cc[VTIME] = 3;
231
-
232
-	if (tcsetattr(hw.fd, TCSANOW, &portset) < 0) {
233
-		logprintf(LOG_ERR, "Error setting TCSANOW mode of serial device");
234
-		LOGPRINTF(1, "Error setting TCSANOW mode of serial device");
235
-		mplay_deinit();
236
-		return 0;
421
+	if (code != MPLAY_CODE_NOP) {
422
+		mplayfamily_local_data.latest_action = MPLAY_ACTION_WHEEL;
423
+		LOGPRINTF(3, "get wheel: latest action %u, latest button 0x%02x", 
424
+			  mplayfamily_local_data.latest_action, mplayfamily_local_data.latest_button);
237 425
 	}
238 426
 
239
-	len = write(hw.fd, &buf, 1);
240
-	if (len < 0) {
241
-		LOGPRINTF(LOG_ERR, "couldn't write to device");
242
-		mplay_deinit();
243
-		return 0;
427
+	return code;
428
+}
429
+
430
+/**************************************************************************
431
+ * Returns button presses as read from the serial line.
432
+ * The repeat code 0x7E is replaced by the button code, if it conflicts
433
+ * with wheel actions (repeat code would refer to wheel actions instead of
434
+ * button presses). If the knob has been pressed, the function re-triggers
435
+ * the busy phase in order to catch all spurious knob presses caused by
436
+ * bouncing effects
437
+ *
438
+ * Parameters: 
439
+ *   File descriptor of periodic timer;
440
+ *   counter (in/out), used to control idle/busy polling phases;
441
+ **************************************************************************/
442
+static unsigned char mplayfamily_get_button(int fd, unsigned int *counter)
443
+{
444
+	unsigned char code = MPLAY_CODE_NOP;
445
+	signed int len; /* length of last received button message from serial port */
446
+
447
+	/* Read any pressed button from IR receiver */
448
+	if ((len = read(mplayfamily_local_data.fd, &code, sizeof(code))) == sizeof(code)) {
449
+		LOGPRINTF(2, "mplay listener received 0x%02x", code);
450
+		if (code == MPLAY_CODE_REPEAT) {
451
+			if (mplayfamily_local_data.latest_action == MPLAY_ACTION_WHEEL) {
452
+				LOGPRINTF(2, "mplay replaces code by 0x%02x", code);
453
+				code = mplayfamily_local_data.latest_button;
454
+				mplayfamily_local_data.latest_action = MPLAY_ACTION_BUTTON;
455
+			}
456
+		} else if (code != MPLAY_CODE_KNOB) {
457
+			mplayfamily_local_data.latest_button = code;
458
+			mplayfamily_local_data.latest_action = MPLAY_ACTION_BUTTON;
459
+		} else {
460
+			mplayfamily_local_data.latest_action = MPLAY_ACTION_WHEEL;
461
+			if (*counter < MPLAY_LISTENER_COUNTER_WAIT_BUTTON) {
462
+				if (*counter == 0) {			
463
+					/* Knob has been pressed, 
464
+					 * so return to busy polling mode to catch spurious presses,
465
+					 * but only if busy period has expired. */
466
+					if (!mplayfamily_set_listener_period(fd, MPLAY_LISTENER_PERIOD_BUSY)) {
467
+						logperror(LOG_ERR, "mplay listener could not set listener period");
468
+					}
469
+					LOGPRINTF(2, "mplay polls with busy rate, knob has been pressed");
470
+				}
471
+				/* Stay alert for a while, enter busy phase */
472
+				*counter = MPLAY_LISTENER_COUNTER_WAIT_BUTTON;
473
+			}
474
+		}
475
+		LOGPRINTF(3, "mplay get button: latest action %u, latest button 0x%02x", 
476
+			  mplayfamily_local_data.latest_action, mplayfamily_local_data.latest_button);
477
+	}
478
+	/* Handle read error */
479
+	else if (len < 0) {
480
+		/* Check if actually an error has occurred */
481
+		if (errno != EAGAIN && errno != EWOULDBLOCK) {
482
+			logperror(LOG_ERR, "mplay listener serial port read error");
483
+			code = MPLAY_CODE_ERROR;
484
+		}
244 485
 	}
245 486
 
246
-	len = read(hw.fd, &psResponse, 11);
247
-	if (len < 0) {
248
-		LOGPRINTF(1, "No data recieved during reading");
249
-		mplay_deinit();
250
-		return 0;
251
-	} else
252
-		LOGPRINTF(1, "read chars: %s", psResponse);
487
+	return code;
488
+}
253 489
 
254
-	if (tcgetattr(hw.fd, &portset) < 0) {
255
-		logprintf(LOG_ERR, "Could not get serial port attributes");
256
-		LOGPRINTF(1, "Could not get serial port attributes");
257
-		mplay_deinit();
258
-		return 0;
490
+/**************************************************************************
491
+ * Polls for button presses and wheel actions; they are piped into the LIRC
492
+ * framework where LIRC accesses the read end of a pipe. The function
493
+ * implements a polling loop and is called in a polling thread, which is
494
+ * cancellation safe.
495
+ *
496
+ * The function reads every MPLAY_LISTENER_PERIOD_IDLE milliseconds from
497
+ * the serial port and evaluates the serial port status lines. If the wheel
498
+ * is turned or the knob is pressed, the polling period is temporarily
499
+ * shortened to MPLAY_LISTENER_PERIOD_BUSY. Wheel turns are reported as
500
+ * remote control button presses delivering the values MPLAY_CODE_TURN_LEFT
501
+ * and MPLAY_CODE_TURN_RIGHT.
502
+ *
503
+ * Button presses from the remote control are read from the serial port.
504
+ * Note: Repeat sequences can be interrupted by wheel actions, but no
505
+ * button is discarded (one repeat code 0x7E is replaced by the original
506
+ * button code having started the repeat sequence).
507
+ *
508
+ * Wheel turns are reported as angles 0, 1, 3, 4 given in gray code, which
509
+ * is delivered in two status lines of the serial port (see masks
510
+ * MPLAY_ROTATION_SENSOR_MASK_A and MPLAY_ROTATION_SENSOR_MASK_B).
511
+ *
512
+ * The wheel has a rasterisation, and if the the wheel snaps in at angles 0
513
+ * a button press is simulated (provided a minimal angle distance is
514
+ * given). The rasterisation has the additional advantage that it prevents
515
+ * a jumping between left/right turns (caused by measurement errors and
516
+ * mechanical imprecisions) when turning in only one direction.
517
+ **************************************************************************/
518
+static void *mplayfamily_listen(void* arg)
519
+{
520
+	unsigned int status; /* status of wheel sensors */
521
+	unsigned int sensor; /* current angle position 0, 1, 2 or 3 measured by sensor */
522
+	unsigned int absolute; /* absolute angle position */
523
+	unsigned int turned; /* angle where a wheel button press was generated */
524
+	int fd; /* file descriptor polling timer */
525
+	uint64_t expired;
526
+	unsigned char code_wheel, code_button; /* button presses */
527
+	unsigned int counter = 1; /* controls duration of idle and busy polling phase */
528
+	
529
+	LOGPRINTF(1, "Entering mplayfamily_listen()");
530
+
531
+	/* Read status of rotation sensor for the first time */
532
+	if (ioctl(mplayfamily_local_data.fd, TIOCMGET, &status) < 0) {
533
+		LOGPERROR(LOG_ERR, "mplay listener ioctl failed");
534
+	}
535
+	sensor = MPLAY_GRAY_TO_BIN(MPLAY_STATUS_TO_GRAY(status));
536
+	absolute = sensor;
537
+	turned = absolute;
538
+	LOGPRINTF(3, "mplay sensor %u", sensor);
539
+
540
+	/* Create periodic timer for polling loop */
541
+	if ((fd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
542
+		logperror(LOG_ERR, "mplay listener could not create timer");
543
+		return NULL;
259 544
 	}
545
+	pthread_cleanup_push(mplayfamily_listen_cleanup, (void*) fd);
260 546
 
261
-	portset.c_cflag &= ~PARENB;
262
-	portset.c_cflag &= ~CSTOPB;
263
-	portset.c_cflag &= ~CSIZE;
264
-	portset.c_cflag = B57600 | CS8;
265
-	portset.c_cflag |= (CLOCAL | CREAD);
266
-	portset.c_iflag |= (IXON | IXOFF | IXANY);
267
-	portset.c_oflag &= ~OPOST;
268
-	portset.c_lflag &= ~(ICANON | ECHOE | ECHO | ISIG);
269
-	portset.c_cc[VSTART] = 0x11;
270
-	portset.c_cc[VSTOP] = 0x13;
271
-	portset.c_cc[VEOF] = 0x1C;
272
-	portset.c_cc[VMIN] = 1;
273
-	portset.c_cc[VTIME] = 3;
274
-
275
-	if (tcsetattr(hw.fd, TCSANOW, &portset) < 0) {
276
-		logprintf(LOG_ERR, "Error setting TCSANOW mode of serial device");
277
-		LOGPRINTF(1, "Error setting TCSANOW mode of serial device");
278
-		mplay_deinit();
547
+	/* Poll for button presses and wheel actions */
548
+	while (1) {
549
+		/* If busy phase expires return to idle period */
550
+		if (counter > 0) {
551
+			counter--;
552
+			if (counter == 0) {
553
+				if (!mplayfamily_set_listener_period(fd, MPLAY_LISTENER_PERIOD_IDLE)) {
554
+					logperror(LOG_ERR, "mplay listener could not set listener period");
555
+				}
556
+				LOGPRINTF(2, "mplay polls with idle rate");
557
+			}
558
+		}
559
+		/* Wait for next event of periodic polling timer */
560
+		if (read(fd, &expired, sizeof(uint64_t)) != sizeof(uint64_t)) {
561
+			logperror(LOG_ERR, "mplay listener timer failed");
562
+			goto poll_exit;
563
+		}
564
+		/* Evaluate wheel status */
565
+		if ((code_wheel = mplayfamily_get_wheel(fd, &counter, &sensor, &absolute, &turned))
566
+		    != MPLAY_CODE_NOP) {
567
+			/* Pass wheel event to LIRC framework via pipe */
568
+			if (write(mplayfamily_local_data.pipefd[1], &code_wheel, sizeof(code_wheel)) < 0) {
569
+				logperror(LOG_ERR, "mplay listener pipe write error");
570
+				goto poll_exit;
571
+			} else if(code_wheel == MPLAY_CODE_ERROR) {
572
+				goto poll_exit;
573
+			}
574
+		}
575
+		/* Process all pending button presses and pass them to the LIRC framework via pipe */
576
+		if ((code_button = mplayfamily_get_button(fd, &counter)) != MPLAY_CODE_NOP) {
577
+			/* Pass button event to LIRC framework via pipe */
578
+			if (write(mplayfamily_local_data.pipefd[1], &code_button, sizeof(code_button)) < 0) {
579
+				logperror(LOG_ERR, "mplay listener pipe write error");
580
+			} else if (code_button == MPLAY_CODE_ERROR) {
581
+				goto poll_exit;
582
+			}
583
+		}
584
+	}
585
+
586
+  poll_exit:
587
+
588
+	pthread_cleanup_pop(1);
589
+
590
+	LOGPRINTF(1, "Leaving mplayfamily_listen()");
591
+
592
+	return NULL;
593
+}
594
+
595
+/**************************************************************************
596
+ * Locks and initialises a receiver of the mplay family.
597
+ *
598
+ * Unless the driver specified has appended the option "nowheel" (example:
599
+ * --device="/dev/ttyUSB0,nowheel") the function installs a pipe from a
600
+ * polling listener thread into the LIRC framework. The listener thread
601
+ * watches the serial port, generates remote control button presses (with
602
+ * wheel actions) and pipes them into the LIRC framework.
603
+ *
604
+ * Otherwise the LIRC framework reads directly from the serial port (using
605
+ * select, thus blocking until actually a button press arrives).
606
+ *
607
+ * The first alternative using the listener thread consumes more CPU
608
+ * resources.
609
+ *
610
+ * Parameters: Function to intialise receiver, baud rate for serial port.
611
+ *
612
+ * Return 1 on success, 0 on error.
613
+ **************************************************************************/
614
+static int mplayfamily_init(int (*init_receiver)(void), int baud)
615
+{
616
+	char device[128];
617
+	char* separator;
618
+	int nowheel = 0;
619
+	int result = 1;
620
+	
621
+	LOGPRINTF(1, "Entering mplayfamily_init()");
622
+
623
+	/* Extract device name and option */
624
+	LOGPRINTF(1, "Device string '%s'", hw.device);
625
+	strncpy(device, hw.device, sizeof(device));
626
+	device[sizeof(device) - 1] = 0;
627
+	/* A comma announces an option. */
628
+	separator = strchr(device, ',');
629
+	if (separator != NULL) {
630
+		LOGPRINTF(1, "Found option string '%s'", separator + 1);
631
+		*separator = 0;
632
+		nowheel = strcmp(separator + 1, "nowheel") == 0;
633
+	}
634
+	LOGPRINTF(1, 
635
+		  "Using device path '%s' (wheel disabled state is %d)", 
636
+		  device, nowheel);
637
+
638
+	/* Creation of pipe between this driver and LIRC framework */
639
+	if (!nowheel && pipe(mplayfamily_local_data.pipefd) == -1) {
640
+		logprintf(LOG_ERR, "Could not create pipe");
641
+		result = 0;
642
+	}
643
+	/* Creation of a lock file for serial port */
644
+	else if (!tty_create_lock(device)) {
645
+		logprintf(LOG_ERR, "Could not create lock file for '%s'", device);
646
+		result = 0;
647
+	}
648
+	/* Try to open serial port */
649
+	else if ((mplayfamily_local_data.fd = open(device, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
650
+		logprintf(LOG_ERR, "Could not open serial port '%s'", device);
651
+		result =  0;
652
+	}
653
+	/* Serial port configuration */
654
+	else if (!tty_reset(mplayfamily_local_data.fd) || 
655
+		 !tty_setbaud(mplayfamily_local_data.fd, baud)) {
656
+		logprintf(LOG_ERR, "Could not configure serial port '%s'", device);
657
+		result = 0;
658
+	}
659
+	/* Initialise a receiver of the mplay family */
660
+	else if (!init_receiver()) {
661
+		logprintf(LOG_ERR, "Could not initialise device");
662
+		result = 0;
663
+	}
664
+	/* Install serial port listener */
665
+	else if (!nowheel && pthread_create(&mplayfamily_local_data.tid, NULL, mplayfamily_listen, NULL)) {
666
+		logprintf(LOG_ERR, "Could not create \"listener thread\"");
279 667
 		return 0;
280 668
 	}
281 669
 
282
-	return 1;
670
+	/* Clean up if an error has occured */
671
+	if (result == 0) {
672
+		logperror(LOG_ERR, "mplayfamily_init()");
673
+		mplayfamily_deinit();
674
+	}
675
+
676
+	/* Redirect reads from serial port to pipe if wheel should be supported*/
677
+	hw.fd = nowheel ? mplayfamily_local_data.fd : mplayfamily_local_data.pipefd[0];
678
+	return result;
679
+}
680
+
681
+/**************************************************************************
682
+ * Lock and initialize the serial port for a mplay receiver.
683
+ * This function is called by the LIRC daemon when the first client
684
+ * registers itself.
685
+ * Return 1 on success, 0 on error.
686
+ **************************************************************************/
687
+int mplay_init(void)
688
+{
689
+	LOGPRINTF(1, "Entering mplay_init()");
690
+	return mplayfamily_init(mplay_init_receiver, MPLAY_BAUD_RATE);
691
+}
692
+
693
+/**************************************************************************
694
+ * Locks and initialises the serial port, MonCaso 312/320 variant.
695
+ * This function is called by the LIRC daemon when the first client
696
+ * registers itself.
697
+ * Return 1 on success, 0 on error.
698
+ **************************************************************************/
699
+int mplay2_init(void)
700
+{
701
+    LOGPRINTF(1, "Entering mplay2_init()");
702
+    return mplayfamily_init(mplay2_init_receiver, MPLAY2_BAUD_RATE);
283 703
 }
284 704
 
285 705
 /**************************************************************************
286
- * Close and release the serial line.
706
+ * Close serial line and pipe; and release the serial line.
287 707
  **************************************************************************/
288
-int mplay_deinit(void)
708
+int mplayfamily_deinit(void)
289 709
 {
290
-	LOGPRINTF(1, "Entering mplay_deinit()");
291
-	close(hw.fd);
292
-	tty_delete_lock();
293
-	hw.fd = -1;
294
-	return (1);
710
+	LOGPRINTF(1, "Entering mplayfamily_deinit()");
711
+	if (mplayfamily_local_data.tid != -1) {
712
+		if (pthread_cancel(mplayfamily_local_data.tid) < 0) {
713
+			logperror(LOG_ERR, "mplay could not cancel listener");
714
+			return 0;
715
+		}
716
+		pthread_join(mplayfamily_local_data.tid, NULL);
717
+		mplayfamily_local_data.tid = -1;		
718
+	}
719
+	if (mplayfamily_local_data.pipefd[0] != -1) {
720
+		close (mplayfamily_local_data.pipefd[0]);
721
+		mplayfamily_local_data.pipefd[0] = -1;
722
+	}
723
+	if (mplayfamily_local_data.pipefd[1] != -1) {
724
+		close (mplayfamily_local_data.pipefd[1]);
725
+		mplayfamily_local_data.pipefd[1] = -1;
726
+	}
727
+	if (hw.fd != -1) {
728
+		close(hw.fd);
729
+		tty_delete_lock();
730
+		hw.fd = -1;
731
+		mplayfamily_local_data.fd = -1;
732
+	}
733
+	return 1;
295 734
 }
296 735
 
297 736
 /**************************************************************************
298 737
  * Receive a code (1 byte) from the remote.
299
- * This function is called by the LIRC daemon when I/O is pending
300
- * from a registered client, e.g. irw.
738
+ * This function is called by the LIRC daemon when I/O is pending from a
739
+ * registered client, e.g. irw.
301 740
  *
302
- * return NULL if nothing have been received or a lirc code
741
+ * return NULL if nothing has been received, otherwise a lirc code
303 742
  **************************************************************************/
304
-char *mplay_rec(struct ir_remote *remotes)
743
+char *mplayfamily_rec(struct ir_remote *remotes)
305 744
 {
306 745
 	unsigned char rc_code;
307 746
 	signed int len;
308 747
 	struct timeval current_time;
309
-	LOGPRINTF(1, "Entering mplay_rec()");
748
+	LOGPRINTF(1, "Entering mplayfamily_rec()");
310 749
 	len = read(hw.fd, &rc_code, 1);
311 750
 	gettimeofday(&current_time, NULL);
312 751
 	if (len != 1) {
313
-		/* Something go wrong during the read, we close the device 
314
-		   for prevent endless looping when the device 
315
-		   is disconnected */
316
-		LOGPRINTF(1, "Reading error in mplay_rec()");
317
-		mplay_deinit();
752
+		/* Something go wrong during the read, we close the device for prevent endless
753
+		 * looping when the device is disconnected */
754
+		LOGPRINTF(1, "Reading error in mplayfamily_rec()");
755
+		mplayfamily_deinit();
318 756
 		return NULL;
319 757
 	} else {
320 758
 		/* We have received a code */
321
-		if (rc_code == MPLAY_REPEAT_CODE) {
322
-			if (mplay_local_data.timeout_repetition_flag == 1) {
759
+		if (rc_code == MPLAY_CODE_REPEAT) {
760
+			/* This is a repetition code */
761
+			if (mplayfamily_local_data.timeout_repetition_flag == 1) {
762
+				LOGPRINTF(2, "Ignored received repetition code (timeout)");
323 763
 				/* We ignore the repetition */
324 764
 				return NULL;
765
+			} else if (time_elapsed(&mplayfamily_local_data.last_reception_time, &current_time) <=
766
+				   MAX_TIME_BETWEEN_TWO_REPETITION_CODE) {
767
+				LOGPRINTF(2, "Accepted received repetition code");
768
+				/* This reception is a repeat */
769
+				mplayfamily_local_data.repeat_flag = 1;
770
+				/* We save the reception time */
771
+				mplayfamily_local_data.last_reception_time = current_time;
325 772
 			} else {
326
-				if (time_elapsed(&mplay_local_data.last_reception_time, &current_time) <=
327
-				    MAX_TIME_BETWEEN_TWO_REPETITION_CODE) {
328
-					/* This reception is a repeat */
329
-					mplay_local_data.repeat_flag = 1;
330
-					/* We save the reception time */
331
-					mplay_local_data.last_reception_time = current_time;
332
-				} else {
333
-					/* To much time between repetition, 
334
-					   the receiver have  probably miss 
335
-					   a valide key code. We ignore the 
336
-					   repetition */
337
-					mplay_local_data.timeout_repetition_flag = 1;
338
-					mplay_local_data.repeat_flag = 0;
339
-					return NULL;
340
-				}
773
+				LOGPRINTF(2, "Received invalid repetition code (timeout)");
774
+				/* Too much time between repetitions, the receiver has probably
775
+				 * missed a valide key code. We ignore the repetition. */
776
+				mplayfamily_local_data.timeout_repetition_flag = 1;
777
+				mplayfamily_local_data.repeat_flag = 0;
778
+				return NULL;
341 779
 			}
342 780
 		} else {
343 781
 			/* This is a new code */
344
-			mplay_local_data.rc_code = rc_code;
345
-			mplay_local_data.repeat_flag = 0;
346
-			mplay_local_data.timeout_repetition_flag = 0;
347
-			mplay_local_data.last_reception_time = current_time;
782
+			if (rc_code != MPLAY_CODE_KNOB) {
783
+				/* Any button other than knob */
784
+				LOGPRINTF(2, "Accepted new received code");
785
+				mplayfamily_local_data.repeat_flag = 0;
786
+				mplayfamily_local_data.rc_code = rc_code;
787
+				mplayfamily_local_data.timeout_repetition_flag = 0;
788
+				mplayfamily_local_data.last_reception_time = current_time;
789
+			} else if (mplayfamily_local_data.rc_code != MPLAY_CODE_KNOB || 
790
+				   time_elapsed(&mplayfamily_local_data.last_reception_time, &current_time) > 
791
+				   MIN_TIME_BETWEEN_PRESSES) {
792
+				/* Knob has been pressed after sufficiently large amount of time */
793
+				LOGPRINTF(2, "Accepted new knob code");
794
+				if (mplayfamily_local_data.rc_code == MPLAY_CODE_KNOB && 
795
+				    time_elapsed(&mplayfamily_local_data.last_reception_time, &current_time) < 
796
+				    MAX_TIME_KNOB_PRESS_IS_REPETITION) {
797
+					LOGPRINTF(2, "Interpret knob code as repeated knob code");
798
+					mplayfamily_local_data.repeat_flag = 1;
799
+				} else {
800
+					mplayfamily_local_data.repeat_flag = 0;
801
+				}
802
+				mplayfamily_local_data.rc_code = rc_code;
803
+				mplayfamily_local_data.timeout_repetition_flag = 0;
804
+				mplayfamily_local_data.last_reception_time = current_time;
805
+			} else {
806
+				/* This seems to be a spurious knob press, so ignore it. 
807
+				 * It has been received too shortly 
808
+				 * after the last received knob press. */
809
+				LOGPRINTF(2, "Ignored spurious code 0x%02x at %li sec %li usec",
810
+					  rc_code, current_time.tv_sec, current_time.tv_usec);
811
+				return NULL;
812
+			}
348 813
 		}
349
-		LOGPRINTF(1, "code: %u", (unsigned int)mplay_local_data.rc_code);
350
-		LOGPRINTF(1, "repeat_flag: %d", mplay_local_data.repeat_flag);
814
+		LOGPRINTF(1, "code: 0x%02x", (unsigned int)mplayfamily_local_data.rc_code);
815
+		LOGPRINTF(1, "repeat_flag: %d", mplayfamily_local_data.repeat_flag);
816
+		LOGPRINTF(2, "current_time: %li sec %li usec", current_time.tv_sec, current_time.tv_usec);
351 817
 		return decode_all(remotes);
352 818
 	}
353 819
 }
@@ -366,15 +832,16 @@ char *mplay_rec(struct ir_remote *remotes)
366 832
  *  min_remaining_gapp  Min extimated time gap remaining before next code
367 833
  *  max_remaining_gapp  Max extimated time gap remaining before next code
368 834
  **************************************************************************/
369
-int mplay_decode(struct ir_remote *remote, ir_code * prep, ir_code * codep, ir_code * postp, int *repeat_flagp,
370
-		 lirc_t * min_remaining_gapp, lirc_t * max_remaining_gapp)
835
+int mplayfamily_decode(struct ir_remote *remote, 
836
+			      ir_code * prep, ir_code * codep, ir_code * postp, int *repeat_flagp,
837
+			      lirc_t * min_remaining_gapp, lirc_t * max_remaining_gapp)
371 838
 {
372
-	LOGPRINTF(1, "Entering mplay_decode(), code = %u\n", (unsigned int)mplay_local_data.rc_code);
839
+	LOGPRINTF(1, "Entering mplayfamily_decode(), code=0x%02x\n", (unsigned int)mplayfamily_local_data.rc_code);
373 840
 
374
-	if (!map_code(remote, prep, codep, postp, 0, 0, MPLAY_CODE_LENGTH, mplay_local_data.rc_code, 0, 0)) {
841
+	if (!map_code(remote, prep, codep, postp, 0, 0, MPLAY_CODE_LENGTH, mplayfamily_local_data.rc_code, 0, 0)) {
375 842
 		return (0);
376 843
 	}
377
-	*repeat_flagp = mplay_local_data.repeat_flag;
844
+	*repeat_flagp = mplayfamily_local_data.repeat_flag;
378 845
 	*min_remaining_gapp = 0;
379 846
 	*max_remaining_gapp = 0;
380 847
 	return 1;

+ 4
- 3
e/easyvdr-lirc/daemons/hw_mplay.h View File

@@ -27,12 +27,13 @@
27 27
 
28 28
 #include "drivers/lirc.h"
29 29
 
30
-extern int mplay_decode(struct ir_remote *remote, ir_code * prep, ir_code * codep, ir_code * postp, int *repeat_flagp,
30
+extern int mplayfamily_decode(struct ir_remote *remote, ir_code * prep, 
31
+			ir_code * codep, ir_code * postp, int *repeat_flagp,
31 32
 			lirc_t * min_remaining_gapp, lirc_t * max_remaining_gapp);
32 33
 
33 34
 extern int mplay_init(void);
34 35
 extern int mplay2_init(void);
35
-extern int mplay_deinit(void);
36
-extern char *mplay_rec(struct ir_remote *remotes);
36
+extern int mplayfamily_deinit(void);
37
+extern char *mplayfamily_rec(struct ir_remote *remotes);
37 38
 
38 39
 #endif

+ 7
- 0
e/easyvdr-lirc/debian/changelog View File

@@ -1,3 +1,10 @@
1
+easyvdr-lirc (0.9.0-11easyVDR3~trusty) trusty; urgency=high
2
+
3
+  * added patch by user womiha
4
+  * https://www.easy-vdr.de/thread-17938-post-182206.html#pid182206
5
+
6
+ -- Wolfgang Mangold <vdr@gmx.de>  Sat, 03 Nov 2018 00:24:34 +0100
7
+
1 8
 easyvdr-lirc (0.9.0-11easyVDR2~trusty) trusty; urgency=high
2 9
 
3 10
   * change start

+ 1040
- 0
e/easyvdr-lirc/debian/patches/lirc-0.9.0.patch
File diff suppressed because it is too large
View File


+ 1
- 0
e/easyvdr-lirc/debian/patches/series View File

@@ -0,0 +1 @@
1
+lirc-0.9.0.patch

+ 1
- 0
e/easyvdr-lirc/debian/source/format View File

@@ -0,0 +1 @@
1
+3.0 (native)

+ 3
- 0
e/easyvdr-lirc/debian/source/options View File

@@ -0,0 +1,3 @@
1
+compression = "xz"
2
+unapply-patches
3
+abort-on-upstream-changes

Loading…
Cancel
Save