summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2017-02-25 06:51:29 -0500
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-12-14 10:35:15 -0500
commit9b6192589be788dec73a0e99fe49b8f8ddaf825e (patch)
tree0f2482fc765d53e274c58c2d0000168eaa493559 /drivers
parent4e3cd001fde13dfd4a91888f908b2a07fd0a4047 (diff)
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: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/rc/ir-lirc-codec.c99
-rw-r--r--drivers/media/rc/rc-core-priv.h2
2 files changed, 73 insertions, 28 deletions
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 9954ad4b8e59..0a3ec693d290 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -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))
- return -EINVAL;
+ if (lirc->send_mode == LIRC_MODE_SCANCODE) {
+ struct lirc_scancode scan;
- count = n / sizeof(unsigned);
- if (count > LIRCBUF_SIZE || count % 2 == 0)
- return -EINVAL;
+ if (n != sizeof(scan))
+ return -EINVAL;
- txbuf = memdup_user(buf, n);
- if (IS_ERR(txbuf))
- return PTR_ERR(txbuf);
+ if (copy_from_user(&scan, buf, sizeof(scan)))
+ return -EFAULT;
+
+ if (scan.flags || scan.keycode || scan.timestamp)
+ return -EINVAL;
+
+ 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,24 +191,30 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
if (ret < 0)
goto out;
- for (duration = i = 0; i < ret; i++)
- duration += txbuf[i];
-
- ret *= sizeof(unsigned int);
-
- /*
- * The lircd gap calculation expects the write function to
- * wait for the actual IR signal to be transmitted before
- * returning.
- */
- 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));
+ if (lirc->send_mode == LIRC_MODE_SCANCODE) {
+ ret = n;
+ } else {
+ for (duration = i = 0; i < ret; i++)
+ duration += txbuf[i];
+
+ ret *= sizeof(unsigned int);
+
+ /*
+ * The lircd gap calculation expects the write function to
+ * wait for the actual IR signal to be transmitted before
+ * returning.
+ */
+ 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;
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 564d6e13585e..d10fc998e1db 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -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;