Commit de142c32 authored by Sean Young's avatar Sean Young Committed by Mauro Carvalho Chehab

media: lirc: implement reading scancode

This implements LIRC_MODE_SCANCODE reading from the lirc device. The
scancode can be read from the input device too, but with this interface
you get the rc protocol, keycode, toggle and repeat status in addition
to just the scancode.

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

	mode = LIRC_MODE_SCANCODE;
	if (ioctl(fd, LIRC_SET_REC_MODE, &mode)) {
		// kernel too old or lirc does not support transmit
	}
	struct lirc_scancode scancode;
	while (read(fd, &scancode, sizeof(scancode)) == sizeof(scancode)) {
		printf("protocol:%d scancode:0x%x toggle:%d repeat:%d\n",
			scancode.rc_proto, scancode.scancode,
			!!(scancode.flags & LIRC_SCANCODE_FLAG_TOGGLE),
			!!(scancode.flags & LIRC_SCANCODE_FLAG_REPEAT));
	}
	close(fd);
}
Signed-off-by: default avatarSean Young <sean@mess.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent a6ddd4fe
...@@ -88,6 +88,21 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) ...@@ -88,6 +88,21 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM); wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
} }
/**
* ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to
* userspace
* @dev: the struct rc_dev descriptor of the device
* @lsc: the struct lirc_scancode describing the decoded scancode
*/
void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc)
{
lsc->timestamp = ktime_get_ns();
if (kfifo_put(&dev->scancodes, *lsc))
wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
}
EXPORT_SYMBOL_GPL(ir_lirc_scancode_event);
static int ir_lirc_open(struct inode *inode, struct file *file) static int ir_lirc_open(struct inode *inode, struct file *file)
{ {
struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev, struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev,
...@@ -114,6 +129,8 @@ static int ir_lirc_open(struct inode *inode, struct file *file) ...@@ -114,6 +129,8 @@ static int ir_lirc_open(struct inode *inode, struct file *file)
if (dev->driver_type == RC_DRIVER_IR_RAW) if (dev->driver_type == RC_DRIVER_IR_RAW)
kfifo_reset_out(&dev->rawir); kfifo_reset_out(&dev->rawir);
if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
kfifo_reset_out(&dev->scancodes);
dev->lirc_open++; dev->lirc_open++;
file->private_data = dev; file->private_data = dev;
...@@ -288,7 +305,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, ...@@ -288,7 +305,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
switch (cmd) { switch (cmd) {
case LIRC_GET_FEATURES: case LIRC_GET_FEATURES:
if (dev->driver_type == RC_DRIVER_IR_RAW) { if (dev->driver_type == RC_DRIVER_IR_RAW) {
val |= LIRC_CAN_REC_MODE2; val |= LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE;
if (dev->rx_resolution) if (dev->rx_resolution)
val |= LIRC_CAN_GET_REC_RESOLUTION; val |= LIRC_CAN_GET_REC_RESOLUTION;
} }
...@@ -323,15 +340,17 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, ...@@ -323,15 +340,17 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
if (dev->driver_type == RC_DRIVER_IR_RAW_TX) if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
return -ENOTTY; return -ENOTTY;
val = LIRC_MODE_MODE2; val = dev->rec_mode;
break; break;
case LIRC_SET_REC_MODE: case LIRC_SET_REC_MODE:
if (dev->driver_type == RC_DRIVER_IR_RAW_TX) if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
return -ENOTTY; return -ENOTTY;
if (val != LIRC_MODE_MODE2) if (!(val == LIRC_MODE_MODE2 || val == LIRC_MODE_SCANCODE))
return -EINVAL; return -EINVAL;
dev->rec_mode = val;
return 0; return 0;
case LIRC_GET_SEND_MODE: case LIRC_GET_SEND_MODE:
...@@ -471,31 +490,31 @@ static unsigned int ir_lirc_poll(struct file *file, ...@@ -471,31 +490,31 @@ static unsigned int ir_lirc_poll(struct file *file,
poll_wait(file, &rcdev->wait_poll, wait); poll_wait(file, &rcdev->wait_poll, wait);
if (!rcdev->registered) if (!rcdev->registered) {
events = POLLHUP | POLLERR; events = POLLHUP | POLLERR;
else if (rcdev->driver_type == RC_DRIVER_IR_RAW && } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
!kfifo_is_empty(&rcdev->rawir)) if (rcdev->rec_mode == LIRC_MODE_SCANCODE &&
events = POLLIN | POLLRDNORM; !kfifo_is_empty(&rcdev->scancodes))
events = POLLIN | POLLRDNORM;
if (rcdev->rec_mode == LIRC_MODE_MODE2 &&
!kfifo_is_empty(&rcdev->rawir))
events = POLLIN | POLLRDNORM;
}
return events; return events;
} }
static ssize_t ir_lirc_read(struct file *file, char __user *buffer, static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
size_t length, loff_t *ppos) size_t length)
{ {
struct rc_dev *rcdev = file->private_data; struct rc_dev *rcdev = file->private_data;
unsigned int copied; unsigned int copied;
int ret; int ret;
if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
return -EINVAL;
if (length < sizeof(unsigned int) || length % sizeof(unsigned int)) if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
return -EINVAL; return -EINVAL;
if (!rcdev->registered)
return -ENODEV;
do { do {
if (kfifo_is_empty(&rcdev->rawir)) { if (kfifo_is_empty(&rcdev->rawir)) {
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK)
...@@ -523,6 +542,61 @@ static ssize_t ir_lirc_read(struct file *file, char __user *buffer, ...@@ -523,6 +542,61 @@ static ssize_t ir_lirc_read(struct file *file, char __user *buffer,
return copied; return copied;
} }
static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
size_t length)
{
struct rc_dev *rcdev = file->private_data;
unsigned int copied;
int ret;
if (length < sizeof(struct lirc_scancode) ||
length % sizeof(struct lirc_scancode))
return -EINVAL;
do {
if (kfifo_is_empty(&rcdev->scancodes)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible(rcdev->wait_poll,
!kfifo_is_empty(&rcdev->scancodes) ||
!rcdev->registered);
if (ret)
return ret;
}
if (!rcdev->registered)
return -ENODEV;
ret = mutex_lock_interruptible(&rcdev->lock);
if (ret)
return ret;
ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied);
mutex_unlock(&rcdev->lock);
if (ret)
return ret;
} while (copied == 0);
return copied;
}
static ssize_t ir_lirc_read(struct file *file, char __user *buffer,
size_t length, loff_t *ppos)
{
struct rc_dev *rcdev = file->private_data;
if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
return -EINVAL;
if (!rcdev->registered)
return -ENODEV;
if (rcdev->rec_mode == LIRC_MODE_MODE2)
return ir_lirc_read_mode2(file, buffer, length);
else /* LIRC_MODE_SCANCODE */
return ir_lirc_read_scancode(file, buffer, length);
}
const struct file_operations lirc_fops = { const struct file_operations lirc_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.write = ir_lirc_transmit_ir, .write = ir_lirc_transmit_ir,
......
...@@ -215,6 +215,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) ...@@ -215,6 +215,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct mce_kbd_dec *data = &dev->raw->mce_kbd; struct mce_kbd_dec *data = &dev->raw->mce_kbd;
u32 scancode; u32 scancode;
unsigned long delay; unsigned long delay;
struct lirc_scancode lsc = {};
if (!is_timing_event(ev)) { if (!is_timing_event(ev)) {
if (ev.reset) if (ev.reset)
...@@ -326,18 +327,22 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) ...@@ -326,18 +327,22 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
mod_timer(&data->rx_timeout, jiffies + delay); mod_timer(&data->rx_timeout, jiffies + delay);
/* Pass data to keyboard buffer parser */ /* Pass data to keyboard buffer parser */
ir_mce_kbd_process_keyboard_data(data->idev, scancode); ir_mce_kbd_process_keyboard_data(data->idev, scancode);
lsc.rc_proto = RC_PROTO_MCIR2_KBD;
break; break;
case MCIR2_MOUSE_NBITS: case MCIR2_MOUSE_NBITS:
scancode = data->body & 0x1fffff; scancode = data->body & 0x1fffff;
IR_dprintk(1, "mouse data 0x%06x\n", scancode); IR_dprintk(1, "mouse data 0x%06x\n", scancode);
/* Pass data to mouse buffer parser */ /* Pass data to mouse buffer parser */
ir_mce_kbd_process_mouse_data(data->idev, scancode); ir_mce_kbd_process_mouse_data(data->idev, scancode);
lsc.rc_proto = RC_PROTO_MCIR2_MSE;
break; break;
default: default:
IR_dprintk(1, "not keyboard or mouse data\n"); IR_dprintk(1, "not keyboard or mouse data\n");
goto out; goto out;
} }
lsc.scancode = scancode;
ir_lirc_scancode_event(dev, &lsc);
data->state = STATE_INACTIVE; data->state = STATE_INACTIVE;
input_event(data->idev, EV_MSC, MSC_SCAN, scancode); input_event(data->idev, EV_MSC, MSC_SCAN, scancode);
input_sync(data->idev); input_sync(data->idev);
......
...@@ -42,6 +42,8 @@ static void lirc_release_device(struct device *ld) ...@@ -42,6 +42,8 @@ static void lirc_release_device(struct device *ld)
if (rcdev->driver_type == RC_DRIVER_IR_RAW) if (rcdev->driver_type == RC_DRIVER_IR_RAW)
kfifo_free(&rcdev->rawir); kfifo_free(&rcdev->rawir);
if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX)
kfifo_free(&rcdev->scancodes);
put_device(&rcdev->dev); put_device(&rcdev->dev);
} }
...@@ -55,11 +57,20 @@ int ir_lirc_register(struct rc_dev *dev) ...@@ -55,11 +57,20 @@ int ir_lirc_register(struct rc_dev *dev)
dev->lirc_dev.release = lirc_release_device; dev->lirc_dev.release = lirc_release_device;
dev->send_mode = LIRC_MODE_PULSE; dev->send_mode = LIRC_MODE_PULSE;
dev->rec_mode = LIRC_MODE_MODE2;
if (dev->driver_type == RC_DRIVER_IR_RAW) { if (dev->driver_type == RC_DRIVER_IR_RAW) {
if (kfifo_alloc(&dev->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) if (kfifo_alloc(&dev->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
} }
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
if (kfifo_alloc(&dev->scancodes, 32, GFP_KERNEL)) {
kfifo_free(&dev->rawir);
return -ENOMEM;
}
}
init_waitqueue_head(&dev->wait_poll); init_waitqueue_head(&dev->wait_poll);
minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL);
...@@ -90,6 +101,8 @@ int ir_lirc_register(struct rc_dev *dev) ...@@ -90,6 +101,8 @@ int ir_lirc_register(struct rc_dev *dev)
out_kfifo: out_kfifo:
if (dev->driver_type == RC_DRIVER_IR_RAW) if (dev->driver_type == RC_DRIVER_IR_RAW)
kfifo_free(&dev->rawir); kfifo_free(&dev->rawir);
if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
kfifo_free(&dev->scancodes);
return err; return err;
} }
......
...@@ -276,6 +276,7 @@ void ir_raw_init(void); ...@@ -276,6 +276,7 @@ void ir_raw_init(void);
int lirc_dev_init(void); int lirc_dev_init(void);
void lirc_dev_exit(void); void lirc_dev_exit(void);
void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev); void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
int ir_lirc_register(struct rc_dev *dev); int ir_lirc_register(struct rc_dev *dev);
void ir_lirc_unregister(struct rc_dev *dev); void ir_lirc_unregister(struct rc_dev *dev);
...@@ -285,6 +286,8 @@ static inline int lirc_dev_init(void) { return 0; } ...@@ -285,6 +286,8 @@ static inline int lirc_dev_init(void) { return 0; }
static inline void lirc_dev_exit(void) {} static inline void lirc_dev_exit(void) {}
static inline void ir_lirc_raw_event(struct rc_dev *dev, static inline void ir_lirc_raw_event(struct rc_dev *dev,
struct ir_raw_event ev) { } struct ir_raw_event ev) { }
static inline void ir_lirc_scancode_event(struct rc_dev *dev,
struct lirc_scancode *lsc) { }
static inline int ir_lirc_register(struct rc_dev *dev) { return 0; } static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
static inline void ir_lirc_unregister(struct rc_dev *dev) { } static inline void ir_lirc_unregister(struct rc_dev *dev) { }
#endif #endif
......
...@@ -697,6 +697,13 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, ...@@ -697,6 +697,13 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol,
dev->last_protocol != protocol || dev->last_protocol != protocol ||
dev->last_scancode != scancode || dev->last_scancode != scancode ||
dev->last_toggle != toggle); dev->last_toggle != toggle);
struct lirc_scancode sc = {
.scancode = scancode, .rc_proto = protocol,
.flags = toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0,
.keycode = keycode
};
ir_lirc_scancode_event(dev, &sc);
if (new_event && dev->keypressed) if (new_event && dev->keypressed)
ir_do_keyup(dev, false); ir_do_keyup(dev, false);
......
...@@ -126,9 +126,12 @@ enum rc_filter_type { ...@@ -126,9 +126,12 @@ enum rc_filter_type {
* @gap: true if we're in a gap * @gap: true if we're in a gap
* @send_timeout_reports: report timeouts in lirc raw IR. * @send_timeout_reports: report timeouts in lirc raw IR.
* @rawir: queue for incoming raw IR * @rawir: queue for incoming raw IR
* @scancodes: queue for incoming decoded scancodes
* @wait_poll: poll struct for lirc device * @wait_poll: poll struct for lirc device
* @send_mode: lirc mode for sending, either LIRC_MODE_SCANCODE or * @send_mode: lirc mode for sending, either LIRC_MODE_SCANCODE or
* LIRC_MODE_PULSE * LIRC_MODE_PULSE
* @rec_mode: lirc mode for receiving, either LIRC_MODE_SCANCODE or
* LIRC_MODE_MODE2
* @registered: set to true by rc_register_device(), false by * @registered: set to true by rc_register_device(), false by
* rc_unregister_device * rc_unregister_device
* @change_protocol: allow changing the protocol used on hardware decoders * @change_protocol: allow changing the protocol used on hardware decoders
...@@ -200,8 +203,10 @@ struct rc_dev { ...@@ -200,8 +203,10 @@ struct rc_dev {
bool gap; bool gap;
bool send_timeout_reports; bool send_timeout_reports;
DECLARE_KFIFO_PTR(rawir, unsigned int); DECLARE_KFIFO_PTR(rawir, unsigned int);
DECLARE_KFIFO_PTR(scancodes, struct lirc_scancode);
wait_queue_head_t wait_poll; wait_queue_head_t wait_poll;
u8 send_mode; u8 send_mode;
u8 rec_mode;
#endif #endif
bool registered; bool registered;
int (*change_protocol)(struct rc_dev *dev, u64 *rc_proto); int (*change_protocol)(struct rc_dev *dev, u64 *rc_proto);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment