[PATCH 4/5] ir-ctl: implement scancode reading

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Sean Young <sean@xxxxxxxx>
---
 utils/ir-ctl/ir-ctl.c    | 188 +++++++++++++++++++++++++++++++++--------------
 utils/ir-ctl/ir-encode.c |   4 +
 2 files changed, 135 insertions(+), 57 deletions(-)

diff --git a/utils/ir-ctl/ir-ctl.c b/utils/ir-ctl/ir-ctl.c
index f0dcd2a3..f9cf30a3 100644
--- a/utils/ir-ctl/ir-ctl.c
+++ b/utils/ir-ctl/ir-ctl.c
@@ -24,6 +24,7 @@
 #include <fcntl.h>
 #include <argp.h>
 #include <sysexits.h>
+#include <poll.h>
 
 #include <config.h>
 
@@ -840,24 +841,122 @@ static int lirc_send(struct arguments *args, int fd, unsigned features, struct f
 	return 0;
 }
 
-int lirc_record(struct arguments *args, int fd, unsigned features)
+static bool keep_reading = true;
+static bool leading_space = true;
+
+int lirc_record_mode2(FILE *out, int fd, struct arguments *args)
 {
-	char *dev = args->device;
-	FILE *out = stdout;
-	int rc = EX_IOERR;
 	int mode = LIRC_MODE_MODE2;
+	unsigned buf[LIRCBUF_SIZE];
+	char *dev = args->device;
+	ssize_t ret;
 
-	if (!(features & LIRC_CAN_REC_MODE2)) {
-		fprintf(stderr, _("%s: device cannot record raw ir\n"), dev);
-		return EX_UNAVAILABLE;
+	// some kernel versions return errors, ignore them
+	ioctl(fd, LIRC_SET_REC_MODE, &mode);
+
+	ret = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
+	if (ret < 0) {
+		fprintf(stderr, _("%s: failed read: %m\n"), dev);
+		return EX_IOERR;
 	}
 
-	// kernel v4.8 and v4.9 return ENOTTY
-	if (ioctl(fd, LIRC_SET_REC_MODE, &mode) && errno != ENOTTY) {
-		fprintf(stderr, _("%s: failed to set record mode: %m\n"), dev);
+	if (ret == 0 || ret % sizeof(unsigned)) {
+		fprintf(stderr, _("%s: read returned %zd bytes\n"), dev, ret);
+		return EX_IOERR;
+	}
+
+	for (int i=0; i<ret / sizeof(unsigned); i++) {
+		unsigned val = buf[i] & LIRC_VALUE_MASK;
+		unsigned msg = buf[i] & LIRC_MODE2_MASK;
+
+		// FIXME: the kernel often send us a space after
+		// the IR receiver comes out of idle mode. This
+		// is meaningless, maybe fix the kernel?
+		if (leading_space && msg == LIRC_MODE2_SPACE)
+			continue;
+		else
+			leading_space = false;
+
+		if (args->oneshot &&
+			(msg == LIRC_MODE2_TIMEOUT ||
+			(msg == LIRC_MODE2_SPACE && val > 19000))) {
+			keep_reading = false;
+			break;
+		}
+
+		switch (msg) {
+		case LIRC_MODE2_TIMEOUT:
+			fprintf(out, "timeout %u\n", val);
+			leading_space = true;
+			break;
+		case LIRC_MODE2_PULSE:
+			fprintf(out, "pulse %u\n", val);
+			break;
+		case LIRC_MODE2_SPACE:
+			fprintf(out, "space %u\n", val);
+			break;
+		case LIRC_MODE2_FREQUENCY:
+			fprintf(out, "carrier %u\n", val);
+			break;
+		}
+
+		fflush(out);
+	}
+
+	return 0;
+}
+
+int lirc_record_scancode(FILE *out, int fd, const char *dev)
+{
+	struct lirc_scancode sc[32];
+	int mode = LIRC_MODE_SCANCODE;
+	ssize_t ret;
+
+	ret = ioctl(fd, LIRC_SET_REC_MODE, &mode);
+	if (ret < 0) {
+		fprintf(stderr, _("%s: failed set recording mode: %m\n"), dev);
+		return EX_IOERR;
+	}
+
+	ret = TEMP_FAILURE_RETRY(read(fd, sc, sizeof sc));
+	if (ret < 0) {
+		fprintf(stderr, _("%s: failed read: %m\n"), dev);
 		return EX_IOERR;
 	}
 
+	if (ret == 0 || ret % sizeof sc[0]) {
+		fprintf(stderr, _("%s: read returned %zd bytes\n"), dev, ret);
+		return EX_IOERR;
+	}
+
+	for (int i=0; i<ret / sizeof sc[0]; i++) {
+		const char *proto = protocol_name(sc[i].rc_proto);
+
+		if (proto)
+			fprintf(out, "scancode %s:0x%llx\n",
+				protocol_name(sc[i].rc_proto), sc[i].scancode);
+		else
+			fprintf(out, "scancode protocol-%u:0x%llx\n", i,
+				sc[i].scancode);
+	}
+
+	fflush(out);
+
+	return 0;
+}
+
+int lirc_record(struct arguments *args, int fd, unsigned features)
+{
+	char *dev = args->device;
+	FILE *out = stdout;
+	int rc = 0;
+	ssize_t ret;
+
+	if (!(features & (LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE))) {
+		fprintf(stderr, _("%s: device cannot receive\n"), dev);
+		return EX_UNAVAILABLE;
+	}
+
 	if (args->savetofile) {
 		out = fopen(args->savetofile, "w");
 		if (!out) {
@@ -865,65 +964,40 @@ int lirc_record(struct arguments *args, int fd, unsigned features)
 			return EX_CANTCREAT;
 		}
 	}
-	unsigned buf[LIRCBUF_SIZE];
-
-	bool keep_reading = true;
-	bool leading_space = true;
 
 	while (keep_reading) {
-		ssize_t ret = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
-		if (ret < 0) {
-			fprintf(stderr, _("%s: failed read: %m\n"), dev);
-			goto err;
-		}
-
-		if (ret == 0 || ret % sizeof(unsigned)) {
-			fprintf(stderr, _("%s: read returned %zd bytes\n"),
-								dev, ret);
-			goto err;
+		if (features & LIRC_CAN_REC_SCANCODE) {
+			unsigned mode = LIRC_MODE_MODE2 | LIRC_MODE_SCANCODE;
+			if (ioctl(fd, LIRC_SET_POLL_MODE, &mode)) {
+				fprintf(stderr, _("%s: set poll mode failed: %m\n"), dev);
+				rc = EX_IOERR;
+				break;
+			}
 		}
 
-		for (int i=0; i<ret / sizeof(unsigned); i++) {
-			unsigned val = buf[i] & LIRC_VALUE_MASK;
-			unsigned msg = buf[i] & LIRC_MODE2_MASK;
-
-			// FIXME: the kernel often send us a space after
-			// the IR receiver comes out of idle mode. This
-			// is meaningless, maybe fix the kernel?
-			if (leading_space && msg == LIRC_MODE2_SPACE)
+		struct pollfd p = { .fd = fd, .events = POLLIN };
+		ret = poll(&p, 1, -1);
+		if (ret == -1) {
+			if (errno == -EINTR)
 				continue;
-			else
-				leading_space = false;
+			fprintf(stderr, _("%s: poll failed: %m\n"), dev);
+			rc = EX_IOERR;
+			break;
+		}
 
-			if (args->oneshot &&
-				(msg == LIRC_MODE2_TIMEOUT ||
-				(msg == LIRC_MODE2_SPACE && val > 19000))) {
-				keep_reading = false;
+		if (features & LIRC_CAN_REC_SCANCODE) {
+			rc = lirc_record_scancode(out, fd, dev);
+			if (rc)
 				break;
-			}
+		}
 
-			switch (msg) {
-			case LIRC_MODE2_TIMEOUT:
-				fprintf(out, "timeout %u\n", val);
-				leading_space = true;
-				break;
-			case LIRC_MODE2_PULSE:
-				fprintf(out, "pulse %u\n", val);
-				break;
-			case LIRC_MODE2_SPACE:
-				fprintf(out, "space %u\n", val);
-				break;
-			case LIRC_MODE2_FREQUENCY:
-				fprintf(out, "carrier %u\n", val);
+		if (features & LIRC_CAN_REC_MODE2) {
+			rc = lirc_record_mode2(out, fd, args);
+			if (rc)
 				break;
-			}
-
-			fflush(out);
 		}
 	}
 
-	rc = 0;
-err:
 	if (args->savetofile)
 		fclose(out);
 
diff --git a/utils/ir-ctl/ir-encode.c b/utils/ir-ctl/ir-encode.c
index 8a125628..702c1bb0 100644
--- a/utils/ir-ctl/ir-encode.c
+++ b/utils/ir-ctl/ir-encode.c
@@ -18,6 +18,7 @@
  */
 
 #include <stdbool.h>
+#include <stdlib.h>
 #include <stdint.h>
 #include <ctype.h>
 
@@ -445,5 +446,8 @@ unsigned protocol_encode(enum rc_proto proto, unsigned scancode, unsigned *buf)
 
 const char* protocol_name(enum rc_proto proto)
 {
+	if (proto >= ARRAY_SIZE(encoders))
+		return NULL;
+
 	return encoders[proto].name;
 }
-- 
2.13.5




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux