Currently v4l-utils has no tooling provided for receiving and sending
raw IR using the lirc interface. Some of this can be done using various
tools from the user-space lirc package, but not everything is covered.

We want to be able to do the following:
 - List all the features that a lirc device provides
 - Set all possible receiving and sending parameters
 - Send raw IR, formatted as a text file
 - Record raw IR, with output in the same format as for sending
 - Testbed for lirc drivers. Driver misbehaviour is reported

The need for this is not new. The manufacturer of the IguanaWorks IR
device have a similar tool which is IguanaIR specific:

        http://www.iguanaworks.net/2012/igclient-examples/

Also RedRat3 provide a similar tools but this uses a signal database
for sending IR, and is redrat specific.

        http://www.redrat.co.uk/software/redrat-linux-ir-tools/

Lirc provides a tool for reading raw IR but no method of sending it.

        http://www.lirc.org/html/mode2.html

None of these provides full coverage of the basic raw IR lirc interface,
hence v4l-utils seems like logical place to provide this functionality. It
can be used as a tool for testing features of lirc drivers.

Signed-off-by: Sean Young <s...@mess.org>
---
 configure.ac             |   2 +
 include/media/lirc.h     | 168 +++++++++++
 utils/Makefile.am        |   1 +
 utils/ir-ctl/.gitignore  |   2 +
 utils/ir-ctl/Makefile.am |   6 +
 utils/ir-ctl/ir-ctl.1.in | 192 ++++++++++++
 utils/ir-ctl/ir-ctl.c    | 759 +++++++++++++++++++++++++++++++++++++++++++++++
 v4l-utils.spec.in        |   6 +-
 8 files changed, 1134 insertions(+), 2 deletions(-)
 create mode 100644 include/media/lirc.h
 create mode 100644 utils/ir-ctl/.gitignore
 create mode 100644 utils/ir-ctl/Makefile.am
 create mode 100644 utils/ir-ctl/ir-ctl.1.in
 create mode 100644 utils/ir-ctl/ir-ctl.c

diff --git a/configure.ac b/configure.ac
index 2616f92..41ab97b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@ AC_CONFIG_FILES([Makefile
        utils/decode_tm6000/Makefile
        utils/dvb/Makefile
        utils/keytable/Makefile
+       utils/ir-ctl/Makefile
        utils/cx18-ctl/Makefile
        utils/ivtv-ctl/Makefile
        utils/media-ctl/Makefile
@@ -59,6 +60,7 @@ AC_CONFIG_FILES([Makefile
        utils/v4l2-compliance/v4l2-compliance.1
        utils/v4l2-ctl/v4l2-ctl.1
        utils/keytable/ir-keytable.1
+       utils/ir-ctl/ir-ctl.1
        utils/dvb/dvb-fe-tool.1
        utils/dvb/dvbv5-scan.1
        utils/dvb/dvb-format-convert.1
diff --git a/include/media/lirc.h b/include/media/lirc.h
new file mode 100644
index 0000000..4b3ab29
--- /dev/null
+++ b/include/media/lirc.h
@@ -0,0 +1,168 @@
+/*
+ * lirc.h - linux infrared remote control header file
+ * last modified 2010/07/13 by Jarod Wilson
+ */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define PULSE_BIT       0x01000000
+#define PULSE_MASK      0x00FFFFFF
+
+#define LIRC_MODE2_SPACE     0x00000000
+#define LIRC_MODE2_PULSE     0x01000000
+#define LIRC_MODE2_FREQUENCY 0x02000000
+#define LIRC_MODE2_TIMEOUT   0x03000000
+
+#define LIRC_VALUE_MASK      0x00FFFFFF
+#define LIRC_MODE2_MASK      0xFF000000
+
+#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE)
+#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE)
+#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY)
+#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT)
+
+#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK)
+#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK)
+
+#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE)
+#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE)
+#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY)
+#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT)
+
+/* used heavily by lirc userspace */
+#define lirc_t int
+
+/*** lirc compatible hardware features ***/
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW                  0x00000001
+#define LIRC_MODE_PULSE                0x00000002
+#define LIRC_MODE_MODE2                0x00000004
+#define LIRC_MODE_LIRCCODE             0x00000010
+
+
+#define LIRC_CAN_SEND_RAW              LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE            LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2            LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+
+#define LIRC_CAN_SEND_MASK             0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER      0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE   0x00000200
+#define LIRC_CAN_SET_TRANSMITTER_MASK  0x00000400
+
+#define LIRC_CAN_REC_RAW               LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE             LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2             LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+
+#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER       (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE    (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE    0x80000000
+#define LIRC_CAN_GET_REC_RESOLUTION       0x20000000
+#define LIRC_CAN_SET_REC_TIMEOUT          0x10000000
+#define LIRC_CAN_SET_REC_FILTER           0x08000000
+
+#define LIRC_CAN_MEASURE_CARRIER          0x02000000
+#define LIRC_CAN_USE_WIDEBAND_RECEIVER    0x04000000
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+#define LIRC_CAN_NOTIFY_DECODE            0x01000000
+
+/*** IOCTL commands for lirc driver ***/
+
+#define LIRC_GET_FEATURES              _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE             _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE              _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER          _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER           _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE       _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE        _IOR('i', 0x00000006, __u32)
+#define LIRC_GET_REC_RESOLUTION        _IOR('i', 0x00000007, __u32)
+
+#define LIRC_GET_MIN_TIMEOUT           _IOR('i', 0x00000008, __u32)
+#define LIRC_GET_MAX_TIMEOUT           _IOR('i', 0x00000009, __u32)
+
+#define LIRC_GET_MIN_FILTER_PULSE      _IOR('i', 0x0000000a, __u32)
+#define LIRC_GET_MAX_FILTER_PULSE      _IOR('i', 0x0000000b, __u32)
+#define LIRC_GET_MIN_FILTER_SPACE      _IOR('i', 0x0000000c, __u32)
+#define LIRC_GET_MAX_FILTER_SPACE      _IOR('i', 0x0000000d, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH                _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE             _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE              _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER          _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER           _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE       _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE        _IOW('i', 0x00000016, __u32)
+#define LIRC_SET_TRANSMITTER_MASK      _IOW('i', 0x00000017, __u32)
+
+/*
+ * when a timeout != 0 is set the driver will send a
+ * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is
+ * never sent, timeout is disabled by default
+ */
+#define LIRC_SET_REC_TIMEOUT           _IOW('i', 0x00000018, __u32)
+
+/* 1 enables, 0 disables timeout reports in MODE2 */
+#define LIRC_SET_REC_TIMEOUT_REPORTS   _IOW('i', 0x00000019, __u32)
+
+/*
+ * pulses shorter than this are filtered out by hardware (software
+ * emulation in lirc_dev?)
+ */
+#define LIRC_SET_REC_FILTER_PULSE      _IOW('i', 0x0000001a, __u32)
+/*
+ * spaces shorter than this are filtered out by hardware (software
+ * emulation in lirc_dev?)
+ */
+#define LIRC_SET_REC_FILTER_SPACE      _IOW('i', 0x0000001b, __u32)
+/*
+ * if filter cannot be set independently for pulse/space, this should
+ * be used
+ */
+#define LIRC_SET_REC_FILTER            _IOW('i', 0x0000001c, __u32)
+
+/*
+ * if enabled from the next key press on the driver will send
+ * LIRC_MODE2_FREQUENCY packets
+ */
+#define LIRC_SET_MEASURE_CARRIER_MODE  _IOW('i', 0x0000001d, __u32)
+
+/*
+ * to set a range use
+ * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+ * lower bound first and later
+ * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound
+ */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE  _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE     _IOW('i', 0x0000001f, __u32)
+
+#define LIRC_NOTIFY_DECODE             _IO('i', 0x00000020)
+
+#define LIRC_SETUP_START               _IO('i', 0x00000021)
+#define LIRC_SETUP_END                 _IO('i', 0x00000022)
+
+#define LIRC_SET_WIDEBAND_RECEIVER     _IOW('i', 0x00000023, __u32)
+
+#endif
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 2cb56f0..a19617a 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = \
        libmedia_dev \
        decode_tm6000 \
        ivtv-ctl \
+       ir-ctl \
        cx18-ctl \
        keytable \
        media-ctl \
diff --git a/utils/ir-ctl/.gitignore b/utils/ir-ctl/.gitignore
new file mode 100644
index 0000000..3220d69
--- /dev/null
+++ b/utils/ir-ctl/.gitignore
@@ -0,0 +1,2 @@
+ir-ctl
+ir-ctl.1
diff --git a/utils/ir-ctl/Makefile.am b/utils/ir-ctl/Makefile.am
new file mode 100644
index 0000000..9a1bfed
--- /dev/null
+++ b/utils/ir-ctl/Makefile.am
@@ -0,0 +1,6 @@
+bin_PROGRAMS = ir-ctl
+man_MANS = ir-ctl.1
+
+ir_ctl_SOURCES = ir-ctl.c
+ir_ctl_LDADD = @LIBINTL@
+ir_ctl_LDFLAGS = $(ARGP_LIBS)
diff --git a/utils/ir-ctl/ir-ctl.1.in b/utils/ir-ctl/ir-ctl.1.in
new file mode 100644
index 0000000..4bdf47e
--- /dev/null
+++ b/utils/ir-ctl/ir-ctl.1.in
@@ -0,0 +1,192 @@
+.TH "IR\-CTL" "1" "Tue Jul 5 2016" "v4l-utils @PACKAGE_VERSION@" "User 
Commands"
+.SH NAME
+ir\-ctl \- a swiss\-knife tool to handle raw IR and to set lirc options
+.SH SYNOPSIS
+.B ir\-ctl
+[\fIOPTION\fR]...
+.br
+.B ir\-ctl
+[\fIOPTION\fR]... \fI\-\-features\fR
+.br
+.B ir\-ctl
+[\fIOPTION\fR]... \fI\-\-send\fR [\fIpulse and space file to send\fR]
+.br
+.B ir\-ctl
+[\fIOPTION\fR]... \fI\-\-record\fR [\fIsave to file\fR]
+.SH DESCRIPTION
+ir\-ctl is a tool that allows one to list the features of a lirc device,
+set its options, record raw IR and send raw IR.
+.PP
+Note: You need to have read or write permissions on the /dev/lirc device
+for options to work.
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-device\fR=\fIDEV\fR
+lirc device to control, /dev/lirc0 by default
+.TP
+\fB\-f\fR, \fB\-\-features\fR
+List the features of the lirc device.
+.TP
+\fB\-r\fR, \fB\-\-record\fR=[\fIFILE\fR]
+Record IR and print to standard output if no file is specified, else
+save to the filename.
+.TP
+\fB\-s\fR, \fB\-\-send\fR=\fIFILE\fR
+Send IR in text file. It must be in the format described below. If this
+option is specified multiple times, send all files in order with 125ms delay
+between them.
+.TP
+\fB\-1\fR, \fB\-\-oneshot\fR
+When recording, stop recording after the first message, i.e. after a space or
+timeout of more than 19ms is received.
+.TP
+\fB\-w\fR, \fB\-\-wideband\fR
+Use the wideband receiver if available on the hardware. This is also
+known as learning mode. The measurements should be more precise and any
+carrier frequency should be accepted.
+.TP
+\fB\-n\fR, \fB\-\-no-wideband\fR
+Switches back to the normal, narrowband receiver if the wideband receiver
+was enabled.
+.TP
+\fB\-R\fR, \fB\-\-carrier-range\fR=\fIRANGE\fR
+Set the accepted carrier range for the narrowband receiver. It should be
+specified in the form \fI30000-50000\fR.
+.TP
+\fB\-m\fR, \fB\-\-measure\-carrier\fR
+If the hardware supports it, report what the carrier frequency is on
+recording. You will get the keyword \fIcarrier\fR followed by the frequency.
+This might use the wideband receiver although this is hardware specific.
+.TP
+\fB\-M\fR, \fB\-\-no\-measure\-carrier\fR
+Disable reporting of the carrier frequency. This should make it possible
+to use the narrowband receiver. This is the default.
+.TP
+\fB\-p\fR, \fB\-\-timeout\-reports\fR
+When the IR receiver times out due to inactivity, a timeout message is
+reported. When recording you will get the keyword \fItimeout\fR followed by
+the length of time that no IR was detected for.
+.TP
+\fB\-P\fR, \fB\-\-no\-timeout\-reports\fR
+When the IR receiver times out due to inactivity, do not report this.
+This is the default.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITIMEOUT\fR
+Set the length of a space at which the recorder goes idle, specified in
+microseconds.
+.TP
+\fB\-c\fR, \fB\-\-carrier\fR=\fICARRIER\fR
+Sets the send carrier frequency.
+.TP
+\fB\-D\fR, \fB\-\-duty\-cycle\fR=\fIDUTY\fR
+Set the duty cycle for sending in percent if the hardware support it.
+.TP
+\fB\-e\fR, \fB\-\-emitters\fR=\fIEMITTERS\fR
+Comma separated list of emitters to use for sending. The first emitter is
+number 1. Some devices only support enabling one emitter (the winbond-cir
+driver).
+.TP
+\fB\-?\fR, \fB\-\-help\fR
+Prints the help message
+.TP
+\fB\-\-usage\fR
+Give a short usage message
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the v4l2\-utils version
+.PP
+.SS Format of pulse and space file
+When sending IR, the format of the file should be as follows. A comment
+start with #. The carrier frequency can be specified as:
+.PP
+       carrier 38000
+.PP
+The file should contain alternating lines with pulse and space, followed
+by length in microseconds. The following is a rc-5 encoded message:
+.PP
+       carrier 36000
+.br
+       pulse 920
+.br
+       space 110
+.br
+       pulse 270
+.br
+       space 380
+.br
+       pulse 1800
+.br
+       space 1560
+.br
+       pulse 1730
+.br
+       space 1630
+.br
+       pulse 1730
+.br
+       space 1640
+.br
+       pulse 850
+.br
+       space 830
+.br
+       pulse 1690
+.br
+       space 820
+.br
+       pulse 860
+.br
+       space 1660
+.br
+       pulse 1690
+.br
+       space 830
+.br
+       pulse 850
+.SS Wideband and narrowband receiver
+Most IR receivers have a narrowband and wideband receiver. The narrowband
+receiver can receive over longer distances (usually around 10 metres without
+interference) and is limited to certain carrier frequencies.
+.PP
+The wideband receiver is for higher precision measurements and when the
+carrier frequency is unknown; however it only works over very short
+distances (about 5 centimetres). This is also known as \fBlearning mode\fR.
+.PP
+For most drivers, enabling \fBcarrier reports\fR using \fB\-m\fR also enables
+the wideband receiver.
+.SS Global state
+All the options which can be set for lirc devices are maintained until
+the device is powered down or a new option is set.
+.SH EXIT STATUS
+On success, it returns 0. Otherwise, it will return the error code.
+.SH EXAMPLES
+To list all capabilities of /dev/lirc2:
+.br
+       \fBir\-ctl \-f \-d /dev/lirc2\fR
+.PP
+To show the IR of the first button press on a remote in learning mode:
+.br
+       \fBir\-ctl \-r \-m \-w\fR
+.PP
+Note that \fBir\-ctl \-rmw\fR would record to a file called \fBmw\fR.
+.PP
+To restore the normal (longer distance) receiver:
+.br
+       \fBir\-ctl \-n \-M\fR
+.PP
+To send the pulse and space file \fBplay\fR on emitter 3:
+.br
+       \fBir\-ctl \-e 3 \-\-send=play\fR
+.PP
+To restore the IR receiver on /dev/lirc2 to the default state:
+.br
+       \fBir\-ctl \-PMn \-\-timeout 125000 \-\-device=/dev/lirc2\fR
+.SH BUGS
+Report bugs to \fBLinux Media Mailing List <linux-media@vger.kernel.org>\fR
+.SH COPYRIGHT
+Copyright (c) 2016 by Sean Young.
+.PP
+License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
+.br
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
diff --git a/utils/ir-ctl/ir-ctl.c b/utils/ir-ctl/ir-ctl.c
new file mode 100644
index 0000000..7f2afc0
--- /dev/null
+++ b/utils/ir-ctl/ir-ctl.c
@@ -0,0 +1,759 @@
+/*
+ *  ir-ctl.c - Program to send and record IR using lirc interface
+ *
+ *  Copyright (C) 2016 Sean Young <s...@mess.org>
+ *
+ *  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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <sysexits.h>
+
+#include <config.h>
+
+#include <media/lirc.h>
+
+#ifdef ENABLE_NLS
+# define _(string) gettext(string)
+# include "gettext.h"
+# include <locale.h>
+# include <langinfo.h>
+# include <iconv.h>
+#else
+# define _(string) string
+#endif
+
+# define N_(string) string
+
+
+/* See drivers/media/rc/ir-lirc-codec.c line 23 */
+#define LIRCBUF_SIZE   512
+#define IR_DEFAULT_TIMEOUT 125000
+
+const char *argp_program_version = "IR raw version " V4L_UTILS_VERSION;
+const char *argp_program_bug_address = "Sean Young <s...@mess.org>";
+
+/*
+ * Since this program drives the lirc interface, use the same terminology
+ */
+struct file {
+       struct file *next;
+       const char *fname;
+       unsigned carrier;
+       unsigned len;
+       unsigned buf[LIRCBUF_SIZE];
+};
+
+struct arguments {
+       char *device;
+       bool features;
+       bool record;
+       struct file *send;
+       bool oneshot;
+       char *savetofile;
+       int wideband;
+       unsigned carrier_low, carrier_high;
+       unsigned timeout;
+       int carrier_reports;
+       int timeout_reports;
+       unsigned carrier;
+       unsigned duty;
+       unsigned emitters;
+       bool work_to_do;
+};
+
+static const struct argp_option options[] = {
+       { "device",     'd',    N_("DEV"),      0,      N_("lirc device to 
use") },
+       { "features",   'f',    0,              0,      N_("list lirc device 
features") },
+       { "record",     'r',    N_("FILE"),     OPTION_ARG_OPTIONAL,    
N_("record IR to stdout or file") },
+       { "send",       's',    N_("FILE"),     0,      N_("send IR pulse and 
space file") },
+               { .doc = N_("Recording options:") },
+       { "one-shot",   '1',    0,              0,      N_("end recording after 
first message") },
+       { "wideband",   'w',    0,              0,      N_("use wideband 
receiver aka learning mode") },
+       { "no-wideband",'n',    0,              0,      N_("use normal 
narrowband receiver, disable learning mode") },
+       { "carrier-range", 'R', N_("RANGE"),    0,      N_("set receiver 
carrier range") },
+       { "measure-carrier", 'm', 0,            0,      N_("report carrier 
frequency") },
+       { "no-measure-carrier", 'M', 0,         0,      N_("disable reporting 
carrier frequency") },
+       { "timeout-reports", 'p', 0,            0,      N_("report when a 
timeout occurs") },
+       { "no-timeout-reports", 'P', 0,         0,      N_("disable reporting 
when a timeout occurs") },
+       { "timeout",    't',    N_("TIMEOUT"),  0,      N_("set recording 
timeout") },
+               { .doc = "Sending options:" },
+       { "carrier",    'c',    N_("CARRIER"),  0,      N_("set send carrier") 
},
+       { "duty-cycle", 'D',    N_("DUTY"),     0,      N_("set duty cycle") },
+       { "emitters",   'e',    N_("EMITTERS"), 0,      N_("set send emitters") 
},
+       { }
+};
+
+static const char args_doc[] = N_(
+       "--features\n"
+       "--record [save to file]\n"
+       "--send [file to send]\n"
+       "[to set lirc option]");
+
+static const char doc[] = N_(
+       "\nRecord IR, send IR and list features of lirc device\n"
+       "You will need permission on /dev/lirc for the program to work\n"
+       "\nOn the options below, the arguments are:\n"
+       "  DEV      - the /dev/lirc* device to use\n"
+       "  FILE     - a text file containing pulses and spaces\n"
+       "  CARRIER  - the carrier frequency to use for sending\n"
+       "  DUTY     - the duty cycle to use for sending\n"
+       "  EMITTERS - comma separated list of emitters to use for sending, e.g. 
1,2\n"
+       "  RANGE    - set range of accepted carrier frequencies, e.g. 
20000-40000\n"
+       "  TIMEOUT  - set length of space before recording stops in µs 
(microseonds)\n"
+       "\nNote that most lirc setting have global state, i.e. the device will 
remain\n"
+       "in this state until set otherwise.");
+
+static int strtoint(const char *p, const char *unit)
+{
+       char *end;
+       long arg = strtol(p, &end, 10);
+       if (end == NULL || (end[0] != 0 && strcasecmp(end, unit) != 0))
+               return 0;
+
+       if (arg <= 0 || arg >= 0xffffff)
+               return 0;
+
+       return arg;
+}
+
+static unsigned parse_emitters(char *p)
+{
+       unsigned emit = 0;
+       const char *sep = " ,;:";
+       char *saveptr, *q;
+
+       q = strtok_r(p, sep, &saveptr);
+       while (q) {
+               if (*q) {
+                       char *endptr;
+                       long e = strtol(q, &endptr, 10);
+                       if ((endptr && *endptr) || e <= 0 || e > 32)
+                               return 0;
+
+                       emit |= 1 << (e - 1);
+               }
+               q = strtok_r(NULL, sep, &saveptr);
+       }
+
+       return emit;
+}
+
+static struct file *read_file(const char *fname)
+{
+       bool expect_pulse = true;
+       int lineno = 0, lastspace = 0;
+       char line[1024];
+       int len = 0;
+       const char *whitespace = " \n\r\t";
+       struct file *f;
+
+       FILE *input = fopen(fname, "r");
+
+       if (!input) {
+               fprintf(stderr, _("%s: could not open: %m\n"), fname);
+               return NULL;
+       }
+
+       f = malloc(sizeof(*f));
+       if (f == NULL) {
+               fprintf(stderr, _("Failed to allocate memory\n"));
+               return NULL;
+       }
+       f->carrier = 0;
+       f->fname = fname;
+
+       while (fgets(line, sizeof(line), input)) {
+               char *p, *saveptr;
+               lineno++;
+               char *keyword = strtok_r(line, whitespace, &saveptr);
+
+               if (keyword == NULL || *keyword == 0 || *keyword == '#' ||
+                               (keyword[0] == '/' && keyword[1] == '/'))
+                       continue;
+
+               p = strtok_r(NULL, whitespace, &saveptr);
+               if (p == NULL) {
+                       fprintf(stderr, _("warning: %s:%d: missing 
argument\n"), fname, lineno);
+                       continue;
+               }
+
+               int arg = strtoint(p, "");
+               if (arg == 0) {
+                       fprintf(stderr, _("warning: %s:%d: invalid argument 
'%s'\n"), fname, lineno, p);
+                       continue;
+               }
+
+               p = strtok_r(NULL, whitespace, &saveptr);
+               if (p && p[0] != '#' && !(p[0] == '/' && p[1] == '/')) {
+                       fprintf(stderr, _("warning: %s:%d: '%s' unexpected\n"), 
fname, lineno, p);
+                       continue;
+               }
+
+               if (strcmp(keyword, "space") == 0) {
+                       if (expect_pulse) {
+                               if (len == 0) {
+                                       fprintf(stderr, _("warning: %s:%d: 
leading space ignored\n"),
+                                               fname, lineno);
+                               } else {
+                                       f->buf[len] += arg;
+                               }
+                       } else {
+                               f->buf[len++] = arg;
+                       }
+                       lastspace = lineno;
+                       expect_pulse = true;
+               } else if (strcmp(keyword, "pulse") == 0) {
+                       if (!expect_pulse)
+                               f->buf[len] += arg;
+                       else
+                               f->buf[len++] = arg;
+                       expect_pulse = false;
+               } else if (strcmp(keyword, "carrier") == 0) {
+                       if (f->carrier) {
+                               fprintf(stderr, _("warning: %s:%d: carrier 
already specified\n"), fname, lineno);
+                       } else {
+                               f->carrier = arg;
+                       }
+               } else {
+                       fprintf(stderr, _("warning: %s:%d: unknown keyword '%s' 
ignored\n"), fname, lineno, keyword);
+                       continue;
+               }
+
+               if (len >= LIRCBUF_SIZE) {
+                       fprintf(stderr, _("warning: %s:%d: IR cannot exceed %u 
edges\n"), fname, lineno, LIRCBUF_SIZE);
+                       break;
+               }
+       }
+
+       fclose(input);
+
+       if (len == 0) {
+               fprintf(stderr, _("%s: no pulses or spaces found\n"), fname);
+               free(f);
+               return NULL;
+       }
+
+       if ((len % 2) == 0) {
+               fprintf(stderr, _("warning: %s:%d: trailing space ignored\n"),
+                                                       fname, lastspace);
+               len--;
+       }
+
+       f->len = len;
+
+       return f;
+}
+
+
+static error_t parse_opt(int k, char *arg, struct argp_state *state)
+{
+       struct arguments *arguments = state->input;
+       struct file *s;
+
+       switch (k) {
+       case 'f':
+               if (arguments->record || arguments->send)
+                       argp_error(state, _("features can not be combined with 
record or send option"));
+               arguments->features = true;
+               break;
+       // recording
+       case 'r':
+               if (arguments->features || arguments->send)
+                       argp_error(state, _("record can not be combined with 
features or send option"));
+
+               arguments->record = true;
+               if (arg) {
+                       if (arguments->savetofile)
+                               argp_error(state, _("record filename already 
set"));
+
+                       arguments->savetofile = arg;
+               }
+               break;
+       case '1':
+               arguments->oneshot = true;
+               break;
+       case 'm':
+               if (arguments->carrier_reports == 2)
+                       argp_error(state, _("cannot enable and disable carrier 
reports"));
+
+               arguments->carrier_reports = 1;
+               break;
+       case 'M':
+               if (arguments->carrier_reports == 1)
+                       argp_error(state, _("cannot enable and disable carrier 
reports"));
+
+               arguments->carrier_reports = 2;
+               break;
+       case 'p':
+               if (arguments->timeout_reports == 2)
+                       argp_error(state, _("cannot enable and disable timeout 
reports"));
+
+               arguments->timeout_reports = 1;
+               break;
+       case 'P':
+               if (arguments->timeout_reports == 1)
+                       argp_error(state, _("cannot enable and disable timeout 
reports"));
+
+               arguments->timeout_reports = 2;
+               break;
+       case 'n':
+               if (arguments->wideband)
+                       argp_error(state, _("cannot use narrowband and wideband 
receiver at once"));
+
+               arguments->wideband = 2;
+               break;
+       case 'w':
+               if (arguments->wideband)
+                       argp_error(state, _("cannot use narrowband and wideband 
receiver at once"));
+
+               arguments->wideband = 1;
+               break;
+       case 'R': {
+               long low, high;
+               char *end;
+
+               low = strtol(arg, &end, 10);
+               if (end == NULL || end[0] != '-')
+                       argp_error(state, _("cannot parse carrier range `%s'"), 
arg);
+               high = strtol(end + 1, &end, 10);
+               if (end[0] != 0 || low <= 0 || low >= high || high > 1000000)
+                       argp_error(state, _("cannot parse carrier range `%s'"), 
arg);
+
+               arguments->carrier_low = low;
+               arguments->carrier_high = high;
+               break;
+       }
+       case 't':
+               arguments->timeout = strtoint(arg, "µs");
+               if (arguments->timeout == 0)
+                       argp_error(state, _("cannot parse timeout `%s'"), arg);
+               break;
+
+       // sending
+       case 'd':
+               arguments->device = arg;
+               break;
+       case 'c':
+               arguments->carrier = strtoint(arg, "Hz");
+               if (arguments->carrier == 0)
+                       argp_error(state, _("cannot parse carrier `%s'"), arg);
+               break;
+       case 'e':
+               arguments->emitters = parse_emitters(arg);
+               if (arguments->emitters == 0)
+                       argp_error(state, _("cannot parse emitters `%s'"), arg);
+               break;
+       case 'D':
+               arguments->duty = strtoint(arg, "%");
+               if (arguments->duty == 0 || arguments->duty >= 100)
+                       argp_error(state, _("invalid duty cycle `%s'"), arg);
+               break;
+       case 's':
+               if (arguments->record || arguments->features)
+                       argp_error(state, _("send can not be combined with 
record or features option"));
+               s = read_file(arg);
+               if (s == NULL)
+                       exit(EX_DATAERR);
+
+               s->next = NULL;
+               if (arguments->send == NULL)
+                       arguments->send = s;
+               else {
+                       struct file *p = arguments->send;
+                       while (p->next) p = p->next;
+                       p->next = s;
+               }
+               break;
+       case ARGP_KEY_END:
+               if (!arguments->work_to_do)
+                       argp_usage(state);
+
+               break;
+       default:
+               return ARGP_ERR_UNKNOWN;
+       }
+
+       if (k != '1' && k != 'd')
+               arguments->work_to_do = true;
+
+       return 0;
+}
+
+static const struct argp argp = {
+       .options = options,
+       .parser = parse_opt,
+       .args_doc = args_doc,
+       .doc = doc
+};
+
+static int open_lirc(const char *fname, unsigned *features)
+{
+       int fd;
+
+       fd = TEMP_FAILURE_RETRY(open(fname, O_RDWR | O_CLOEXEC));
+       if (fd == -1) {
+               fprintf(stderr, _("%s: cannot open: %m\n"), fname);
+               return -1;
+       }
+
+       struct stat st;
+       int rc = TEMP_FAILURE_RETRY(fstat(fd, &st));
+       if (rc) {
+               fprintf(stderr, _("%s: cannot stat: %m\n"), fname);
+               close(fd);
+               return -1;
+       }
+
+       if ((st.st_mode & S_IFMT) != S_IFCHR) {
+               fprintf(stderr, _("%s: not character device\n"), fname);
+               close(fd);
+               return -1;
+       }
+
+       rc = ioctl(fd, LIRC_GET_FEATURES, features);
+       if (rc) {
+               fprintf(stderr, _("%s: failed to get lirc features: %m\n"), 
fname);
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static void lirc_set_send_carrier(int fd, const char *devname, unsigned 
features, unsigned carrier)
+{
+       if (features & LIRC_CAN_SET_SEND_CARRIER) {
+               int rc = ioctl(fd, LIRC_SET_SEND_CARRIER, &carrier);
+               if (rc < 0)
+                       fprintf(stderr, _("warning: %s: failed to set carrier: 
%m\n"), devname);
+               if (rc != 0)
+                       fprintf(stderr, _("warning: %s: set send carrier 
returned %d, should return 0\n"), devname, rc);
+       } else
+               fprintf(stderr, _("warning: %s: does not support setting send 
carrier\n"), devname);
+}
+
+static int lirc_options(struct arguments *args, int fd, unsigned features)
+{
+       const char *dev = args->device;
+       int rc;
+
+       if (args->timeout) {
+               if (features & LIRC_CAN_SET_REC_TIMEOUT) {
+                       rc = ioctl(fd, LIRC_SET_REC_TIMEOUT, &args->timeout);
+                       if (rc)
+                               fprintf(stderr, _("%s: failed to set recording 
timeout\n"), dev);
+               } else
+                       fprintf(stderr, _("%s: device does not support setting 
timeout\n"), dev);
+       }
+
+       if (args->wideband) {
+               unsigned on = args->wideband == 1;
+               if (features & LIRC_CAN_USE_WIDEBAND_RECEIVER) {
+                       rc = ioctl(fd, LIRC_SET_WIDEBAND_RECEIVER, &on);
+                       if (rc)
+                               fprintf(stderr, _("%s: failed to set wideband 
receiver %s\n"), dev, on ? _("on") : _("off"));
+               } else
+                       fprintf(stderr, _("%s: device does not have wideband 
receiver\n"), dev);
+       }
+
+       if (args->carrier_reports) {
+               unsigned on = args->carrier_reports == 1;
+               if (features & LIRC_CAN_MEASURE_CARRIER) {
+                       rc = ioctl(fd, LIRC_SET_MEASURE_CARRIER_MODE, &on);
+                       if (rc)
+                               fprintf(stderr, _("%s: failed to set carrier 
reports %s\n"), dev, on ? _("on") : _("off"));
+               } else
+                       fprintf(stderr, _("%s: device cannot measure 
carrier\n"), dev);
+       }
+
+       if (args->timeout_reports) {
+               unsigned on = args->timeout_reports == 1;
+               rc = ioctl(fd, LIRC_SET_REC_TIMEOUT_REPORTS, &on);
+               if (rc)
+                       fprintf(stderr, _("%s: failed to set timeout reports 
%s: %m\n"), dev, on ? _("on") : _("off"));
+       }
+
+       if (args->carrier_low) {
+               if (features & LIRC_CAN_SET_REC_CARRIER_RANGE) {
+                       rc = ioctl(fd, LIRC_SET_REC_CARRIER_RANGE, 
&args->carrier_low);
+                       if (rc)
+                               fprintf(stderr, _("%s: failed to set low 
carrier range: %m\n"), dev);
+                       rc = ioctl(fd, LIRC_SET_REC_CARRIER, 
&args->carrier_high);
+                       if (rc)
+                               fprintf(stderr, _("%s: failed to set high 
carrier range: %m\n"), dev);
+               } else
+                       fprintf(stderr, _("%s: device does not support setting 
receiver carrier range\n"), dev);
+       }
+
+       if (args->carrier)
+               lirc_set_send_carrier(fd, dev, features, args->carrier);
+
+       if (args->duty) {
+               if (features & LIRC_CAN_SET_SEND_DUTY_CYCLE) {
+                       rc = ioctl(fd, LIRC_SET_SEND_DUTY_CYCLE, &args->duty);
+                       if (rc)
+                               fprintf(stderr, _("warning: %s: failed to set 
duty cycle: %m\n"), dev);
+               } else
+                       fprintf(stderr, _("warning: %s: does not support 
setting send duty cycle\n"), dev);
+       }
+
+       if (args->emitters) {
+               if (features & LIRC_CAN_SET_TRANSMITTER_MASK) {
+                       rc = ioctl(fd, LIRC_SET_TRANSMITTER_MASK, 
&args->emitters);
+                       if (rc)
+                               fprintf(stderr, _("warning: %s: failed to set 
send transmitters: %m\n"), dev);
+               } else
+                       fprintf(stderr, _("warning: %s: does not support 
setting send transmitters\n"), dev);
+       }
+
+
+       return 0;
+}
+
+static void lirc_features(struct arguments *args, int fd, unsigned features)
+{
+       const char *dev = args->device;
+       unsigned resolution = 0;
+       int rc;
+
+       if (features & LIRC_CAN_GET_REC_RESOLUTION) {
+               rc = ioctl(fd, LIRC_GET_REC_RESOLUTION, &resolution);
+               if (rc == 0 && resolution == 0)
+                       fprintf(stderr, _("warning: %s: device returned 
resolution of 0\n"), dev);
+               else if (rc)
+                       fprintf(stderr, _("warning: %s: unexpected error while 
retrieving resolution: %m\n"), dev);
+       }
+
+       printf(_("Receive features %s:\n"), dev);
+       if (features & LIRC_CAN_REC_MODE2) {
+               printf(_(" - Device can receive raw IR\n"));
+               if (resolution)
+                       printf(_(" - Resolution %u nanoseconds\n"), resolution);
+               if (features & LIRC_CAN_SET_REC_CARRIER)
+                       printf(_(" - Set receive carrier\n"));
+               if (features & LIRC_CAN_USE_WIDEBAND_RECEIVER)
+                       printf(_(" - Use wideband receiver\n"));
+               if (features & LIRC_CAN_MEASURE_CARRIER)
+                       printf(_(" - Can measure carrier\n"));
+               if (features & LIRC_CAN_SET_REC_TIMEOUT) {
+                       unsigned min_timeout, max_timeout;
+                       int rc = ioctl(fd, LIRC_GET_MIN_TIMEOUT, &min_timeout);
+                       if (rc) {
+                               fprintf(stderr, _("warning: %s: device supports 
setting recording timeout but LIRC_GET_MIN_TIMEOUT returns: %m\n"), dev);
+                               min_timeout = 0;
+                       } else if (min_timeout == 0)
+                               fprintf(stderr, _("warning: %s: device supports 
setting recording timeout but min timeout is 0\n"), dev);
+                       rc = ioctl(fd, LIRC_GET_MAX_TIMEOUT, &max_timeout);
+                       if (rc) {
+                               fprintf(stderr, _("warning: %s: device supports 
setting recording timeout but LIRC_GET_MAX_TIMEOUT returns: %m\n"), dev);
+                               max_timeout = 0;
+                       } else if (max_timeout == 0) {
+                               fprintf(stderr, _("warning: %s: device supports 
setting recording timeout but max timeout is 0\n"), dev);
+                       }
+
+                       if (min_timeout || max_timeout)
+                               printf(_(" - Can set recording timeout min:%uµs 
max:%uµs\n"), min_timeout, max_timeout);
+               }
+       } else {
+               printf(_(" - Device cannot receive\n"));
+       }
+
+       printf(_("Send features %s:\n"), dev);
+       if (features & LIRC_CAN_SEND_PULSE) {
+               printf(_(" - Device can send raw IR\n"));
+               if (features & LIRC_CAN_SET_SEND_CARRIER)
+                       printf(_(" - Set carrier\n"));
+               if (features & LIRC_CAN_SET_SEND_DUTY_CYCLE)
+                       printf(_(" - Set duty cycle\n"));
+               if (features & LIRC_CAN_SET_TRANSMITTER_MASK) {
+                       unsigned mask = ~0;
+                       rc = ioctl(fd, LIRC_SET_TRANSMITTER_MASK, &mask);
+                       if (rc == 0)
+                               fprintf(stderr, _("warning: %s: device supports 
setting transmitter mask but returns 0 as number of transmitters\n"), dev);
+                       else if (rc < 0)
+                               fprintf(stderr, _("warning: %s: device supports 
setting transmitter mask but returns: %m\n"), dev);
+                       else
+                               printf(_(" - Set transmitter (%d 
available)\n"), rc);
+               }
+       } else {
+               printf(_(" - Device cannot send\n"));
+       }
+}
+
+static int lirc_send(struct arguments *args, int fd, unsigned features, struct 
file *f)
+{
+       const char *dev = args->device;
+
+       if (!(features & LIRC_CAN_SEND_PULSE)) {
+               fprintf(stderr, _("%s: device cannot send\n"), dev);
+               return EX_UNAVAILABLE;
+       }
+
+       if (args->carrier && f->carrier)
+               fprintf(stderr, _("warning: %s: carrier specified but 
overwritten on command line\n"), f->fname);
+       else if (f->carrier && args->carrier == 0)
+               lirc_set_send_carrier(fd, dev, features, f->carrier);
+
+       size_t size = f->len * sizeof(unsigned);
+       ssize_t ret = TEMP_FAILURE_RETRY(write(fd, f->buf, size));
+       if (ret < 0) {
+               fprintf(stderr, _("%s: failed to send: %m\n"), dev);
+               return EX_IOERR;
+       }
+
+       if (size < ret) {
+               fprintf(stderr, _("warning: %s: sent %zd out %zd edges\n"),
+                               dev,
+                               ret / sizeof(unsigned),
+                               size / sizeof(unsigned));
+               return EX_IOERR;
+       }
+
+       return 0;
+}
+
+int lirc_record(struct arguments *args, int fd, unsigned features)
+{
+       char *dev = args->device;
+       FILE *out = stdout;
+       int rc = EX_IOERR;
+
+       if (args->savetofile) {
+               out = fopen(args->savetofile, "w");
+               if (!out) {
+                       fprintf(stderr, _("%s: failed to open for writing: 
%m\n"), args->savetofile);
+                       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;
+               }
+
+               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);
+               }
+       }
+
+       rc = 0;
+err:
+       if (args->savetofile)
+               fclose(out);
+
+       return rc;
+}
+
+int main(int argc, char *argv[])
+{
+       struct arguments args = {};
+
+       argp_parse(&argp, argc, argv, 0, 0, &args);
+
+       if (args.device == NULL)
+               args.device = "/dev/lirc0";
+
+       int rc, fd;
+       unsigned features;
+
+       fd = open_lirc(args.device, &features);
+       if (fd < 0)
+               exit(EX_NOINPUT);
+
+       rc = lirc_options(&args, fd, features);
+       if (rc)
+               exit(EX_IOERR);
+
+       struct file *s = args.send;
+       while (s) {
+               struct file *next = s->next;
+               if (s != args.send)
+                       usleep(IR_DEFAULT_TIMEOUT);
+
+               rc = lirc_send(&args, fd, features, s);
+               if (rc) {
+                       close(fd);
+                       exit(rc);
+               }
+
+               free(s);
+               s = next;
+       }
+
+       if (args.record) {
+               rc = lirc_record(&args, fd, features);
+               if (rc) {
+                       close(fd);
+                       exit(rc);
+               }
+       }
+
+       if (args.features)
+               lirc_features(&args, fd, features);
+
+       close(fd);
+
+       return 0;
+}
diff --git a/v4l-utils.spec.in b/v4l-utils.spec.in
index dd8959b..bdbb27b 100644
--- a/v4l-utils.spec.in
+++ b/v4l-utils.spec.in
@@ -17,8 +17,8 @@ Requires:       libv4l = %{version}-%{release}
 
 %description
 v4l-utils is a collection of various video4linux (V4L) and DVB utilities. The
-main v4l-utils package contains cx18-ctl, ir-keytable, ivtv-ctl, v4l2-ctl and
-v4l2-sysfs-path.
+main v4l-utils package contains cx18-ctl, ir-keytable, ir-ctl, ivtv-ctl,
+v4l2-ctl and v4l2-sysfs-path.
 
 
 %package        devel-tools
@@ -129,10 +129,12 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor 
&>/dev/null || :
 %config(noreplace) %{_sysconfdir}/udev/rules.d/70-infrared.rules
 %{_bindir}/cx18-ctl
 %{_bindir}/ir-keytable
+%{_bindir}/ir-ctl
 %{_bindir}/ivtv-ctl
 %{_bindir}/v4l2-ctl
 %{_bindir}/v4l2-sysfs-path
 %{_mandir}/man1/ir-keytable.1*
+%{_mandir}/man1/ir-ctl.1*
 
 %files devel-tools
 %defattr(-,root,root,-)
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to