Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b619258 authored by Sean Young's avatar Sean Young Committed by Mauro Carvalho Chehab
Browse files

media: lirc: implement scancode sending



This introduces a new lirc mode: scancode. Any device which can send raw IR
can now also send scancodes.

int main()
{
	int mode, fd = open("/dev/lirc0", O_RDWR);

        mode = LIRC_MODE_SCANCODE;
	if (ioctl(fd, LIRC_SET_SEND_MODE, &mode)) {
		// kernel too old or lirc does not support transmit
	}
	struct lirc_scancode scancode = {
		.scancode = 0x1e3d,
		.rc_proto = RC_PROTO_RC5,
	};
	write(fd, &scancode, sizeof(scancode));
	close(fd);
}

The other fields of lirc_scancode must be set to 0.

Note that toggle (rc5, rc6) and repeats (nec) are not implemented. Nor is
there a method for holding down a key for a period.

Signed-off-by: default avatarSean Young <sean@mess.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 4e3cd001
Loading
Loading
Loading
Loading
+72 −27
Original line number Diff line number Diff line
@@ -107,7 +107,8 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
{
	struct lirc_codec *lirc;
	struct rc_dev *dev;
	unsigned int *txbuf; /* buffer with values to transmit */
	unsigned int *txbuf = NULL;
	struct ir_raw_event *raw = NULL;
	ssize_t ret = -EINVAL;
	size_t count;
	ktime_t start;
@@ -121,16 +122,50 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
	if (!lirc)
		return -EFAULT;

	if (n < sizeof(unsigned) || n % sizeof(unsigned))
	if (lirc->send_mode == LIRC_MODE_SCANCODE) {
		struct lirc_scancode scan;

		if (n != sizeof(scan))
			return -EINVAL;

		if (copy_from_user(&scan, buf, sizeof(scan)))
			return -EFAULT;

		if (scan.flags || scan.keycode || scan.timestamp)
			return -EINVAL;

	count = n / sizeof(unsigned);
		raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
		if (!raw)
			return -ENOMEM;

		ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
					     raw, LIRCBUF_SIZE);
		if (ret < 0)
			goto out;

		count = ret;

		txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
		if (!txbuf) {
			ret = -ENOMEM;
			goto out;
		}

		for (i = 0; i < count; i++)
			/* Convert from NS to US */
			txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000);
	} else {
		if (n < sizeof(unsigned int) || n % sizeof(unsigned int))
			return -EINVAL;

		count = n / sizeof(unsigned int);
		if (count > LIRCBUF_SIZE || count % 2 == 0)
			return -EINVAL;

		txbuf = memdup_user(buf, n);
		if (IS_ERR(txbuf))
			return PTR_ERR(txbuf);
	}

	dev = lirc->dev;
	if (!dev) {
@@ -156,6 +191,9 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
	if (ret < 0)
		goto out;

	if (lirc->send_mode == LIRC_MODE_SCANCODE) {
		ret = n;
	} else {
		for (duration = i = 0; i < ret; i++)
			duration += txbuf[i];

@@ -166,14 +204,17 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
		 * wait for the actual IR signal to be transmitted before
		 * returning.
		 */
	towait = ktime_us_delta(ktime_add_us(start, duration), ktime_get());
		towait = ktime_us_delta(ktime_add_us(start, duration),
					ktime_get());
		if (towait > 0) {
			set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout(usecs_to_jiffies(towait));
		}
	}

out:
	kfree(txbuf);
	kfree(raw);
	return ret;
}

@@ -202,20 +243,22 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,

	switch (cmd) {

	/* legacy support */
	/* mode support */
	case LIRC_GET_SEND_MODE:
		if (!dev->tx_ir)
			return -ENOTTY;

		val = LIRC_MODE_PULSE;
		val = lirc->send_mode;
		break;

	case LIRC_SET_SEND_MODE:
		if (!dev->tx_ir)
			return -ENOTTY;

		if (val != LIRC_MODE_PULSE)
		if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
			return -EINVAL;

		lirc->send_mode = val;
		return 0;

	/* TX settings */
@@ -361,7 +404,7 @@ static int ir_lirc_register(struct rc_dev *dev)
	}

	if (dev->tx_ir) {
		features |= LIRC_CAN_SEND_PULSE;
		features |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
		if (dev->s_tx_mask)
			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
		if (dev->s_tx_carrier)
@@ -399,6 +442,8 @@ static int ir_lirc_register(struct rc_dev *dev)
	if (rc < 0)
		goto out;

	dev->raw->lirc.send_mode = LIRC_MODE_PULSE;

	dev->raw->lirc.ldev = ldev;
	dev->raw->lirc.dev = dev;
	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ struct ir_raw_event_ctrl {
		u64 gap_duration;
		bool gap;
		bool send_timeout_reports;

		u8 send_mode;
	} lirc;
	struct xmp_dec {
		int state;
+1 −53
Original line number Diff line number Diff line
@@ -10,59 +10,7 @@
 */

#include <linux/input.h>

/**
 * enum rc_proto - the Remote Controller protocol
 *
 * @RC_PROTO_UNKNOWN: Protocol not known
 * @RC_PROTO_OTHER: Protocol known but proprietary
 * @RC_PROTO_RC5: Philips RC5 protocol
 * @RC_PROTO_RC5X_20: Philips RC5x 20 bit protocol
 * @RC_PROTO_RC5_SZ: StreamZap variant of RC5
 * @RC_PROTO_JVC: JVC protocol
 * @RC_PROTO_SONY12: Sony 12 bit protocol
 * @RC_PROTO_SONY15: Sony 15 bit protocol
 * @RC_PROTO_SONY20: Sony 20 bit protocol
 * @RC_PROTO_NEC: NEC protocol
 * @RC_PROTO_NECX: Extended NEC protocol
 * @RC_PROTO_NEC32: NEC 32 bit protocol
 * @RC_PROTO_SANYO: Sanyo protocol
 * @RC_PROTO_MCIR2_KBD: RC6-ish MCE keyboard
 * @RC_PROTO_MCIR2_MSE: RC6-ish MCE mouse
 * @RC_PROTO_RC6_0: Philips RC6-0-16 protocol
 * @RC_PROTO_RC6_6A_20: Philips RC6-6A-20 protocol
 * @RC_PROTO_RC6_6A_24: Philips RC6-6A-24 protocol
 * @RC_PROTO_RC6_6A_32: Philips RC6-6A-32 protocol
 * @RC_PROTO_RC6_MCE: MCE (Philips RC6-6A-32 subtype) protocol
 * @RC_PROTO_SHARP: Sharp protocol
 * @RC_PROTO_XMP: XMP protocol
 * @RC_PROTO_CEC: CEC protocol
 */
enum rc_proto {
	RC_PROTO_UNKNOWN	= 0,
	RC_PROTO_OTHER		= 1,
	RC_PROTO_RC5		= 2,
	RC_PROTO_RC5X_20	= 3,
	RC_PROTO_RC5_SZ		= 4,
	RC_PROTO_JVC		= 5,
	RC_PROTO_SONY12		= 6,
	RC_PROTO_SONY15		= 7,
	RC_PROTO_SONY20		= 8,
	RC_PROTO_NEC		= 9,
	RC_PROTO_NECX		= 10,
	RC_PROTO_NEC32		= 11,
	RC_PROTO_SANYO		= 12,
	RC_PROTO_MCIR2_KBD	= 13,
	RC_PROTO_MCIR2_MSE	= 14,
	RC_PROTO_RC6_0		= 15,
	RC_PROTO_RC6_6A_20	= 16,
	RC_PROTO_RC6_6A_24	= 17,
	RC_PROTO_RC6_6A_32	= 18,
	RC_PROTO_RC6_MCE	= 19,
	RC_PROTO_SHARP		= 20,
	RC_PROTO_XMP		= 21,
	RC_PROTO_CEC		= 22,
};
#include <uapi/linux/lirc.h>

#define RC_PROTO_BIT_NONE		0ULL
#define RC_PROTO_BIT_UNKNOWN		BIT_ULL(RC_PROTO_UNKNOWN)
+82 −0
Original line number Diff line number Diff line
@@ -47,12 +47,14 @@
#define LIRC_MODE_RAW                  0x00000001
#define LIRC_MODE_PULSE                0x00000002
#define LIRC_MODE_MODE2                0x00000004
#define LIRC_MODE_SCANCODE             0x00000008
#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_SCANCODE         LIRC_MODE2SEND(LIRC_MODE_SCANCODE)
#define LIRC_CAN_SEND_LIRCCODE         LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)

#define LIRC_CAN_SEND_MASK             0x0000003f
@@ -64,6 +66,7 @@
#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_SCANCODE          LIRC_MODE2REC(LIRC_MODE_SCANCODE)
#define LIRC_CAN_REC_LIRCCODE          LIRC_MODE2REC(LIRC_MODE_LIRCCODE)

#define LIRC_CAN_REC_MASK              LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
@@ -131,4 +134,83 @@

#define LIRC_SET_WIDEBAND_RECEIVER     _IOW('i', 0x00000023, __u32)

/*
 * struct lirc_scancode - decoded scancode with protocol for use with
 *	LIRC_MODE_SCANCODE
 *
 * @timestamp: Timestamp in nanoseconds using CLOCK_MONOTONIC when IR
 *	was decoded.
 * @flags: should be 0 for transmit. When receiving scancodes,
 *	LIRC_SCANCODE_FLAG_TOGGLE or LIRC_SCANCODE_FLAG_REPEAT can be set
 *	depending on the protocol
 * @rc_proto: see enum rc_proto
 * @keycode: the translated keycode. Set to 0 for transmit.
 * @scancode: the scancode received or to be sent
 */
struct lirc_scancode {
	__u64	timestamp;
	__u16	flags;
	__u16	rc_proto;
	__u32	keycode;
	__u64	scancode;
};

/* Set if the toggle bit of rc-5 or rc-6 is enabled */
#define LIRC_SCANCODE_FLAG_TOGGLE	1
/* Set if this is a nec or sanyo repeat */
#define LIRC_SCANCODE_FLAG_REPEAT	2

/**
 * enum rc_proto - the Remote Controller protocol
 *
 * @RC_PROTO_UNKNOWN: Protocol not known
 * @RC_PROTO_OTHER: Protocol known but proprietary
 * @RC_PROTO_RC5: Philips RC5 protocol
 * @RC_PROTO_RC5X_20: Philips RC5x 20 bit protocol
 * @RC_PROTO_RC5_SZ: StreamZap variant of RC5
 * @RC_PROTO_JVC: JVC protocol
 * @RC_PROTO_SONY12: Sony 12 bit protocol
 * @RC_PROTO_SONY15: Sony 15 bit protocol
 * @RC_PROTO_SONY20: Sony 20 bit protocol
 * @RC_PROTO_NEC: NEC protocol
 * @RC_PROTO_NECX: Extended NEC protocol
 * @RC_PROTO_NEC32: NEC 32 bit protocol
 * @RC_PROTO_SANYO: Sanyo protocol
 * @RC_PROTO_MCIR2_KBD: RC6-ish MCE keyboard
 * @RC_PROTO_MCIR2_MSE: RC6-ish MCE mouse
 * @RC_PROTO_RC6_0: Philips RC6-0-16 protocol
 * @RC_PROTO_RC6_6A_20: Philips RC6-6A-20 protocol
 * @RC_PROTO_RC6_6A_24: Philips RC6-6A-24 protocol
 * @RC_PROTO_RC6_6A_32: Philips RC6-6A-32 protocol
 * @RC_PROTO_RC6_MCE: MCE (Philips RC6-6A-32 subtype) protocol
 * @RC_PROTO_SHARP: Sharp protocol
 * @RC_PROTO_XMP: XMP protocol
 * @RC_PROTO_CEC: CEC protocol
 */
enum rc_proto {
	RC_PROTO_UNKNOWN	= 0,
	RC_PROTO_OTHER		= 1,
	RC_PROTO_RC5		= 2,
	RC_PROTO_RC5X_20	= 3,
	RC_PROTO_RC5_SZ		= 4,
	RC_PROTO_JVC		= 5,
	RC_PROTO_SONY12		= 6,
	RC_PROTO_SONY15		= 7,
	RC_PROTO_SONY20		= 8,
	RC_PROTO_NEC		= 9,
	RC_PROTO_NECX		= 10,
	RC_PROTO_NEC32		= 11,
	RC_PROTO_SANYO		= 12,
	RC_PROTO_MCIR2_KBD	= 13,
	RC_PROTO_MCIR2_MSE	= 14,
	RC_PROTO_RC6_0		= 15,
	RC_PROTO_RC6_6A_20	= 16,
	RC_PROTO_RC6_6A_24	= 17,
	RC_PROTO_RC6_6A_32	= 18,
	RC_PROTO_RC6_MCE	= 19,
	RC_PROTO_SHARP		= 20,
	RC_PROTO_XMP		= 21,
	RC_PROTO_CEC		= 22,
};

#endif