Signed-off-by: Sean Young <s...@mess.org>
---
 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

Reply via email to