xref: /openbmc/linux/drivers/media/rc/lirc_dev.c (revision aefb5e34)
132cf86f6SMauro Carvalho Chehab /*
232cf86f6SMauro Carvalho Chehab  * LIRC base driver
332cf86f6SMauro Carvalho Chehab  *
432cf86f6SMauro Carvalho Chehab  * by Artur Lipowski <alipowski@interia.pl>
532cf86f6SMauro Carvalho Chehab  *
632cf86f6SMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
732cf86f6SMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
832cf86f6SMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License, or
932cf86f6SMauro Carvalho Chehab  *  (at your option) any later version.
1032cf86f6SMauro Carvalho Chehab  *
1132cf86f6SMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
1232cf86f6SMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1332cf86f6SMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1432cf86f6SMauro Carvalho Chehab  *  GNU General Public License for more details.
1532cf86f6SMauro Carvalho Chehab  *
1632cf86f6SMauro Carvalho Chehab  */
1732cf86f6SMauro Carvalho Chehab 
183fac0314SAndi Shyti #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
193fac0314SAndi Shyti 
2032cf86f6SMauro Carvalho Chehab #include <linux/module.h>
2132cf86f6SMauro Carvalho Chehab #include <linux/mutex.h>
2232cf86f6SMauro Carvalho Chehab #include <linux/device.h>
2346c8f477SDavid Härdeman #include <linux/idr.h>
24a6ddd4feSSean Young #include <linux/poll.h>
2542e0442fSSean Young #include <linux/sched.h>
2642e0442fSSean Young #include <linux/wait.h>
2732cf86f6SMauro Carvalho Chehab 
28a60d64b1SSean Young #include "rc-core-priv.h"
29*aefb5e34SSean Young #include <uapi/linux/lirc.h>
3032cf86f6SMauro Carvalho Chehab 
3132cf86f6SMauro Carvalho Chehab #define LOGHEAD		"lirc_dev (%s[%d]): "
3242e0442fSSean Young #define LIRCBUF_SIZE	256
3332cf86f6SMauro Carvalho Chehab 
3432cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev;
3532cf86f6SMauro Carvalho Chehab 
3646c8f477SDavid Härdeman /* Used to keep track of allocated lirc devices */
3746c8f477SDavid Härdeman static DEFINE_IDA(lirc_ida);
3832cf86f6SMauro Carvalho Chehab 
3932cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */
4032cf86f6SMauro Carvalho Chehab static struct class *lirc_class;
4132cf86f6SMauro Carvalho Chehab 
4242e0442fSSean Young /**
4342e0442fSSean Young  * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace
4442e0442fSSean Young  *
4542e0442fSSean Young  * @dev:	the struct rc_dev descriptor of the device
4642e0442fSSean Young  * @ev:		the struct ir_raw_event descriptor of the pulse/space
4742e0442fSSean Young  */
4842e0442fSSean Young void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
4942e0442fSSean Young {
5042e0442fSSean Young 	int sample;
5142e0442fSSean Young 
5242e0442fSSean Young 	/* Packet start */
5342e0442fSSean Young 	if (ev.reset) {
5442e0442fSSean Young 		/*
5542e0442fSSean Young 		 * Userspace expects a long space event before the start of
5642e0442fSSean Young 		 * the signal to use as a sync.  This may be done with repeat
5742e0442fSSean Young 		 * packets and normal samples.  But if a reset has been sent
5842e0442fSSean Young 		 * then we assume that a long time has passed, so we send a
5942e0442fSSean Young 		 * space with the maximum time value.
6042e0442fSSean Young 		 */
6142e0442fSSean Young 		sample = LIRC_SPACE(LIRC_VALUE_MASK);
6242e0442fSSean Young 		IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
6342e0442fSSean Young 
6442e0442fSSean Young 	/* Carrier reports */
6542e0442fSSean Young 	} else if (ev.carrier_report) {
6642e0442fSSean Young 		sample = LIRC_FREQUENCY(ev.carrier);
6742e0442fSSean Young 		IR_dprintk(2, "carrier report (freq: %d)\n", sample);
6842e0442fSSean Young 
6942e0442fSSean Young 	/* Packet end */
7042e0442fSSean Young 	} else if (ev.timeout) {
7142e0442fSSean Young 		if (dev->gap)
7242e0442fSSean Young 			return;
7342e0442fSSean Young 
7442e0442fSSean Young 		dev->gap_start = ktime_get();
7542e0442fSSean Young 		dev->gap = true;
7642e0442fSSean Young 		dev->gap_duration = ev.duration;
7742e0442fSSean Young 
7842e0442fSSean Young 		if (!dev->send_timeout_reports)
7942e0442fSSean Young 			return;
8042e0442fSSean Young 
8142e0442fSSean Young 		sample = LIRC_TIMEOUT(ev.duration / 1000);
8242e0442fSSean Young 		IR_dprintk(2, "timeout report (duration: %d)\n", sample);
8342e0442fSSean Young 
8442e0442fSSean Young 	/* Normal sample */
8542e0442fSSean Young 	} else {
8642e0442fSSean Young 		if (dev->gap) {
8742e0442fSSean Young 			dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
8842e0442fSSean Young 							 dev->gap_start));
8942e0442fSSean Young 
9042e0442fSSean Young 			/* Convert to ms and cap by LIRC_VALUE_MASK */
9142e0442fSSean Young 			do_div(dev->gap_duration, 1000);
9242e0442fSSean Young 			dev->gap_duration = min_t(u64, dev->gap_duration,
9342e0442fSSean Young 						  LIRC_VALUE_MASK);
9442e0442fSSean Young 
9542e0442fSSean Young 			kfifo_put(&dev->rawir, LIRC_SPACE(dev->gap_duration));
9642e0442fSSean Young 			dev->gap = false;
9742e0442fSSean Young 		}
9842e0442fSSean Young 
9942e0442fSSean Young 		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
10042e0442fSSean Young 					LIRC_SPACE(ev.duration / 1000);
10142e0442fSSean Young 		IR_dprintk(2, "delivering %uus %s to lirc_dev\n",
10242e0442fSSean Young 			   TO_US(ev.duration), TO_STR(ev.pulse));
10342e0442fSSean Young 	}
10442e0442fSSean Young 
10542e0442fSSean Young 	kfifo_put(&dev->rawir, sample);
10642e0442fSSean Young 	wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
10742e0442fSSean Young }
10842e0442fSSean Young 
10942e0442fSSean Young /**
11042e0442fSSean Young  * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to
11142e0442fSSean Young  *		userspace
11242e0442fSSean Young  * @dev:	the struct rc_dev descriptor of the device
11342e0442fSSean Young  * @lsc:	the struct lirc_scancode describing the decoded scancode
11442e0442fSSean Young  */
11542e0442fSSean Young void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc)
11642e0442fSSean Young {
11742e0442fSSean Young 	lsc->timestamp = ktime_get_ns();
11842e0442fSSean Young 
11942e0442fSSean Young 	if (kfifo_put(&dev->scancodes, *lsc))
12042e0442fSSean Young 		wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
12142e0442fSSean Young }
12242e0442fSSean Young EXPORT_SYMBOL_GPL(ir_lirc_scancode_event);
12342e0442fSSean Young 
12442e0442fSSean Young static int ir_lirc_open(struct inode *inode, struct file *file)
12542e0442fSSean Young {
12642e0442fSSean Young 	struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev,
12742e0442fSSean Young 					  lirc_cdev);
12842e0442fSSean Young 	int retval;
12942e0442fSSean Young 
13042e0442fSSean Young 	retval = rc_open(dev);
13142e0442fSSean Young 	if (retval)
13242e0442fSSean Young 		return retval;
13342e0442fSSean Young 
13442e0442fSSean Young 	retval = mutex_lock_interruptible(&dev->lock);
13542e0442fSSean Young 	if (retval)
13642e0442fSSean Young 		goto out_rc;
13742e0442fSSean Young 
13842e0442fSSean Young 	if (!dev->registered) {
13942e0442fSSean Young 		retval = -ENODEV;
14042e0442fSSean Young 		goto out_unlock;
14142e0442fSSean Young 	}
14242e0442fSSean Young 
14342e0442fSSean Young 	if (dev->lirc_open) {
14442e0442fSSean Young 		retval = -EBUSY;
14542e0442fSSean Young 		goto out_unlock;
14642e0442fSSean Young 	}
14742e0442fSSean Young 
14842e0442fSSean Young 	if (dev->driver_type == RC_DRIVER_IR_RAW)
14942e0442fSSean Young 		kfifo_reset_out(&dev->rawir);
15042e0442fSSean Young 	if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
15142e0442fSSean Young 		kfifo_reset_out(&dev->scancodes);
15242e0442fSSean Young 
15342e0442fSSean Young 	dev->lirc_open++;
15442e0442fSSean Young 	file->private_data = dev;
15542e0442fSSean Young 
15642e0442fSSean Young 	nonseekable_open(inode, file);
15742e0442fSSean Young 	mutex_unlock(&dev->lock);
15842e0442fSSean Young 
15942e0442fSSean Young 	return 0;
16042e0442fSSean Young 
16142e0442fSSean Young out_unlock:
16242e0442fSSean Young 	mutex_unlock(&dev->lock);
16342e0442fSSean Young out_rc:
16442e0442fSSean Young 	rc_close(dev);
16542e0442fSSean Young 	return retval;
16642e0442fSSean Young }
16742e0442fSSean Young 
16842e0442fSSean Young static int ir_lirc_close(struct inode *inode, struct file *file)
16942e0442fSSean Young {
17042e0442fSSean Young 	struct rc_dev *dev = file->private_data;
17142e0442fSSean Young 
17242e0442fSSean Young 	mutex_lock(&dev->lock);
17342e0442fSSean Young 	dev->lirc_open--;
17442e0442fSSean Young 	mutex_unlock(&dev->lock);
17542e0442fSSean Young 
17642e0442fSSean Young 	rc_close(dev);
17742e0442fSSean Young 
17842e0442fSSean Young 	return 0;
17942e0442fSSean Young }
18042e0442fSSean Young 
18142e0442fSSean Young static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
18242e0442fSSean Young 				   size_t n, loff_t *ppos)
18342e0442fSSean Young {
18442e0442fSSean Young 	struct rc_dev *dev = file->private_data;
18542e0442fSSean Young 	unsigned int *txbuf = NULL;
18642e0442fSSean Young 	struct ir_raw_event *raw = NULL;
18742e0442fSSean Young 	ssize_t ret = -EINVAL;
18842e0442fSSean Young 	size_t count;
18942e0442fSSean Young 	ktime_t start;
19042e0442fSSean Young 	s64 towait;
19142e0442fSSean Young 	unsigned int duration = 0; /* signal duration in us */
19242e0442fSSean Young 	int i;
19342e0442fSSean Young 
19442e0442fSSean Young 	if (!dev->registered)
19542e0442fSSean Young 		return -ENODEV;
19642e0442fSSean Young 
19742e0442fSSean Young 	start = ktime_get();
19842e0442fSSean Young 
19942e0442fSSean Young 	if (!dev->tx_ir) {
20042e0442fSSean Young 		ret = -EINVAL;
20142e0442fSSean Young 		goto out;
20242e0442fSSean Young 	}
20342e0442fSSean Young 
20442e0442fSSean Young 	if (dev->send_mode == LIRC_MODE_SCANCODE) {
20542e0442fSSean Young 		struct lirc_scancode scan;
20642e0442fSSean Young 
20742e0442fSSean Young 		if (n != sizeof(scan))
20842e0442fSSean Young 			return -EINVAL;
20942e0442fSSean Young 
21042e0442fSSean Young 		if (copy_from_user(&scan, buf, sizeof(scan)))
21142e0442fSSean Young 			return -EFAULT;
21242e0442fSSean Young 
21342e0442fSSean Young 		if (scan.flags || scan.keycode || scan.timestamp)
21442e0442fSSean Young 			return -EINVAL;
21542e0442fSSean Young 
21642e0442fSSean Young 		/*
21742e0442fSSean Young 		 * The scancode field in lirc_scancode is 64-bit simply
21842e0442fSSean Young 		 * to future-proof it, since there are IR protocols encode
21942e0442fSSean Young 		 * use more than 32 bits. For now only 32-bit protocols
22042e0442fSSean Young 		 * are supported.
22142e0442fSSean Young 		 */
22242e0442fSSean Young 		if (scan.scancode > U32_MAX ||
22342e0442fSSean Young 		    !rc_validate_scancode(scan.rc_proto, scan.scancode))
22442e0442fSSean Young 			return -EINVAL;
22542e0442fSSean Young 
22642e0442fSSean Young 		raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
22742e0442fSSean Young 		if (!raw)
22842e0442fSSean Young 			return -ENOMEM;
22942e0442fSSean Young 
23042e0442fSSean Young 		ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
23142e0442fSSean Young 					     raw, LIRCBUF_SIZE);
23242e0442fSSean Young 		if (ret < 0)
23342e0442fSSean Young 			goto out;
23442e0442fSSean Young 
23542e0442fSSean Young 		count = ret;
23642e0442fSSean Young 
23742e0442fSSean Young 		txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
23842e0442fSSean Young 		if (!txbuf) {
23942e0442fSSean Young 			ret = -ENOMEM;
24042e0442fSSean Young 			goto out;
24142e0442fSSean Young 		}
24242e0442fSSean Young 
24342e0442fSSean Young 		for (i = 0; i < count; i++)
24442e0442fSSean Young 			/* Convert from NS to US */
24542e0442fSSean Young 			txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000);
24642e0442fSSean Young 
24742e0442fSSean Young 		if (dev->s_tx_carrier) {
24842e0442fSSean Young 			int carrier = ir_raw_encode_carrier(scan.rc_proto);
24942e0442fSSean Young 
25042e0442fSSean Young 			if (carrier > 0)
25142e0442fSSean Young 				dev->s_tx_carrier(dev, carrier);
25242e0442fSSean Young 		}
25342e0442fSSean Young 	} else {
25442e0442fSSean Young 		if (n < sizeof(unsigned int) || n % sizeof(unsigned int))
25542e0442fSSean Young 			return -EINVAL;
25642e0442fSSean Young 
25742e0442fSSean Young 		count = n / sizeof(unsigned int);
25842e0442fSSean Young 		if (count > LIRCBUF_SIZE || count % 2 == 0)
25942e0442fSSean Young 			return -EINVAL;
26042e0442fSSean Young 
26142e0442fSSean Young 		txbuf = memdup_user(buf, n);
26242e0442fSSean Young 		if (IS_ERR(txbuf))
26342e0442fSSean Young 			return PTR_ERR(txbuf);
26442e0442fSSean Young 	}
26542e0442fSSean Young 
26642e0442fSSean Young 	for (i = 0; i < count; i++) {
26742e0442fSSean Young 		if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
26842e0442fSSean Young 			ret = -EINVAL;
26942e0442fSSean Young 			goto out;
27042e0442fSSean Young 		}
27142e0442fSSean Young 
27242e0442fSSean Young 		duration += txbuf[i];
27342e0442fSSean Young 	}
27442e0442fSSean Young 
27542e0442fSSean Young 	ret = dev->tx_ir(dev, txbuf, count);
27642e0442fSSean Young 	if (ret < 0)
27742e0442fSSean Young 		goto out;
27842e0442fSSean Young 
27942e0442fSSean Young 	if (dev->send_mode == LIRC_MODE_SCANCODE) {
28042e0442fSSean Young 		ret = n;
28142e0442fSSean Young 	} else {
28242e0442fSSean Young 		for (duration = i = 0; i < ret; i++)
28342e0442fSSean Young 			duration += txbuf[i];
28442e0442fSSean Young 
28542e0442fSSean Young 		ret *= sizeof(unsigned int);
28642e0442fSSean Young 
28742e0442fSSean Young 		/*
28842e0442fSSean Young 		 * The lircd gap calculation expects the write function to
28942e0442fSSean Young 		 * wait for the actual IR signal to be transmitted before
29042e0442fSSean Young 		 * returning.
29142e0442fSSean Young 		 */
29242e0442fSSean Young 		towait = ktime_us_delta(ktime_add_us(start, duration),
29342e0442fSSean Young 					ktime_get());
29442e0442fSSean Young 		if (towait > 0) {
29542e0442fSSean Young 			set_current_state(TASK_INTERRUPTIBLE);
29642e0442fSSean Young 			schedule_timeout(usecs_to_jiffies(towait));
29742e0442fSSean Young 		}
29842e0442fSSean Young 	}
29942e0442fSSean Young 
30042e0442fSSean Young out:
30142e0442fSSean Young 	kfree(txbuf);
30242e0442fSSean Young 	kfree(raw);
30342e0442fSSean Young 	return ret;
30442e0442fSSean Young }
30542e0442fSSean Young 
30642e0442fSSean Young static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
30742e0442fSSean Young 			  unsigned long arg)
30842e0442fSSean Young {
30942e0442fSSean Young 	struct rc_dev *dev = filep->private_data;
31042e0442fSSean Young 	u32 __user *argp = (u32 __user *)(arg);
31142e0442fSSean Young 	int ret = 0;
31242e0442fSSean Young 	__u32 val = 0, tmp;
31342e0442fSSean Young 
31442e0442fSSean Young 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
31542e0442fSSean Young 		ret = get_user(val, argp);
31642e0442fSSean Young 		if (ret)
31742e0442fSSean Young 			return ret;
31842e0442fSSean Young 	}
31942e0442fSSean Young 
32042e0442fSSean Young 	if (!dev->registered)
32142e0442fSSean Young 		return -ENODEV;
32242e0442fSSean Young 
32342e0442fSSean Young 	switch (cmd) {
32442e0442fSSean Young 	case LIRC_GET_FEATURES:
32542e0442fSSean Young 		if (dev->driver_type == RC_DRIVER_SCANCODE)
32642e0442fSSean Young 			val |= LIRC_CAN_REC_SCANCODE;
32742e0442fSSean Young 
32842e0442fSSean Young 		if (dev->driver_type == RC_DRIVER_IR_RAW) {
32942e0442fSSean Young 			val |= LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE;
33042e0442fSSean Young 			if (dev->rx_resolution)
33142e0442fSSean Young 				val |= LIRC_CAN_GET_REC_RESOLUTION;
33242e0442fSSean Young 		}
33342e0442fSSean Young 
33442e0442fSSean Young 		if (dev->tx_ir) {
33542e0442fSSean Young 			val |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
33642e0442fSSean Young 			if (dev->s_tx_mask)
33742e0442fSSean Young 				val |= LIRC_CAN_SET_TRANSMITTER_MASK;
33842e0442fSSean Young 			if (dev->s_tx_carrier)
33942e0442fSSean Young 				val |= LIRC_CAN_SET_SEND_CARRIER;
34042e0442fSSean Young 			if (dev->s_tx_duty_cycle)
34142e0442fSSean Young 				val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
34242e0442fSSean Young 		}
34342e0442fSSean Young 
34442e0442fSSean Young 		if (dev->s_rx_carrier_range)
34542e0442fSSean Young 			val |= LIRC_CAN_SET_REC_CARRIER |
34642e0442fSSean Young 				LIRC_CAN_SET_REC_CARRIER_RANGE;
34742e0442fSSean Young 
34842e0442fSSean Young 		if (dev->s_learning_mode)
34942e0442fSSean Young 			val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
35042e0442fSSean Young 
35142e0442fSSean Young 		if (dev->s_carrier_report)
35242e0442fSSean Young 			val |= LIRC_CAN_MEASURE_CARRIER;
35342e0442fSSean Young 
35442e0442fSSean Young 		if (dev->max_timeout)
35542e0442fSSean Young 			val |= LIRC_CAN_SET_REC_TIMEOUT;
35642e0442fSSean Young 
35742e0442fSSean Young 		break;
35842e0442fSSean Young 
35942e0442fSSean Young 	/* mode support */
36042e0442fSSean Young 	case LIRC_GET_REC_MODE:
36142e0442fSSean Young 		if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
36242e0442fSSean Young 			return -ENOTTY;
36342e0442fSSean Young 
36442e0442fSSean Young 		val = dev->rec_mode;
36542e0442fSSean Young 		break;
36642e0442fSSean Young 
36742e0442fSSean Young 	case LIRC_SET_REC_MODE:
36842e0442fSSean Young 		switch (dev->driver_type) {
36942e0442fSSean Young 		case RC_DRIVER_IR_RAW_TX:
37042e0442fSSean Young 			return -ENOTTY;
37142e0442fSSean Young 		case RC_DRIVER_SCANCODE:
37242e0442fSSean Young 			if (val != LIRC_MODE_SCANCODE)
37342e0442fSSean Young 				return -EINVAL;
37442e0442fSSean Young 			break;
37542e0442fSSean Young 		case RC_DRIVER_IR_RAW:
37642e0442fSSean Young 			if (!(val == LIRC_MODE_MODE2 ||
37742e0442fSSean Young 			      val == LIRC_MODE_SCANCODE))
37842e0442fSSean Young 				return -EINVAL;
37942e0442fSSean Young 			break;
38042e0442fSSean Young 		}
38142e0442fSSean Young 
38242e0442fSSean Young 		dev->rec_mode = val;
38342e0442fSSean Young 		return 0;
38442e0442fSSean Young 
38542e0442fSSean Young 	case LIRC_GET_SEND_MODE:
38642e0442fSSean Young 		if (!dev->tx_ir)
38742e0442fSSean Young 			return -ENOTTY;
38842e0442fSSean Young 
38942e0442fSSean Young 		val = dev->send_mode;
39042e0442fSSean Young 		break;
39142e0442fSSean Young 
39242e0442fSSean Young 	case LIRC_SET_SEND_MODE:
39342e0442fSSean Young 		if (!dev->tx_ir)
39442e0442fSSean Young 			return -ENOTTY;
39542e0442fSSean Young 
39642e0442fSSean Young 		if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
39742e0442fSSean Young 			return -EINVAL;
39842e0442fSSean Young 
39942e0442fSSean Young 		dev->send_mode = val;
40042e0442fSSean Young 		return 0;
40142e0442fSSean Young 
40242e0442fSSean Young 	/* TX settings */
40342e0442fSSean Young 	case LIRC_SET_TRANSMITTER_MASK:
40442e0442fSSean Young 		if (!dev->s_tx_mask)
40542e0442fSSean Young 			return -ENOTTY;
40642e0442fSSean Young 
40742e0442fSSean Young 		return dev->s_tx_mask(dev, val);
40842e0442fSSean Young 
40942e0442fSSean Young 	case LIRC_SET_SEND_CARRIER:
41042e0442fSSean Young 		if (!dev->s_tx_carrier)
41142e0442fSSean Young 			return -ENOTTY;
41242e0442fSSean Young 
41342e0442fSSean Young 		return dev->s_tx_carrier(dev, val);
41442e0442fSSean Young 
41542e0442fSSean Young 	case LIRC_SET_SEND_DUTY_CYCLE:
41642e0442fSSean Young 		if (!dev->s_tx_duty_cycle)
41742e0442fSSean Young 			return -ENOTTY;
41842e0442fSSean Young 
41942e0442fSSean Young 		if (val <= 0 || val >= 100)
42042e0442fSSean Young 			return -EINVAL;
42142e0442fSSean Young 
42242e0442fSSean Young 		return dev->s_tx_duty_cycle(dev, val);
42342e0442fSSean Young 
42442e0442fSSean Young 	/* RX settings */
42542e0442fSSean Young 	case LIRC_SET_REC_CARRIER:
42642e0442fSSean Young 		if (!dev->s_rx_carrier_range)
42742e0442fSSean Young 			return -ENOTTY;
42842e0442fSSean Young 
42942e0442fSSean Young 		if (val <= 0)
43042e0442fSSean Young 			return -EINVAL;
43142e0442fSSean Young 
43242e0442fSSean Young 		return dev->s_rx_carrier_range(dev,
43342e0442fSSean Young 					       dev->carrier_low,
43442e0442fSSean Young 					       val);
43542e0442fSSean Young 
43642e0442fSSean Young 	case LIRC_SET_REC_CARRIER_RANGE:
43742e0442fSSean Young 		if (!dev->s_rx_carrier_range)
43842e0442fSSean Young 			return -ENOTTY;
43942e0442fSSean Young 
44042e0442fSSean Young 		if (val <= 0)
44142e0442fSSean Young 			return -EINVAL;
44242e0442fSSean Young 
44342e0442fSSean Young 		dev->carrier_low = val;
44442e0442fSSean Young 		return 0;
44542e0442fSSean Young 
44642e0442fSSean Young 	case LIRC_GET_REC_RESOLUTION:
44742e0442fSSean Young 		if (!dev->rx_resolution)
44842e0442fSSean Young 			return -ENOTTY;
44942e0442fSSean Young 
45042e0442fSSean Young 		val = dev->rx_resolution / 1000;
45142e0442fSSean Young 		break;
45242e0442fSSean Young 
45342e0442fSSean Young 	case LIRC_SET_WIDEBAND_RECEIVER:
45442e0442fSSean Young 		if (!dev->s_learning_mode)
45542e0442fSSean Young 			return -ENOTTY;
45642e0442fSSean Young 
45742e0442fSSean Young 		return dev->s_learning_mode(dev, !!val);
45842e0442fSSean Young 
45942e0442fSSean Young 	case LIRC_SET_MEASURE_CARRIER_MODE:
46042e0442fSSean Young 		if (!dev->s_carrier_report)
46142e0442fSSean Young 			return -ENOTTY;
46242e0442fSSean Young 
46342e0442fSSean Young 		return dev->s_carrier_report(dev, !!val);
46442e0442fSSean Young 
46542e0442fSSean Young 	/* Generic timeout support */
46642e0442fSSean Young 	case LIRC_GET_MIN_TIMEOUT:
46742e0442fSSean Young 		if (!dev->max_timeout)
46842e0442fSSean Young 			return -ENOTTY;
46942e0442fSSean Young 		val = DIV_ROUND_UP(dev->min_timeout, 1000);
47042e0442fSSean Young 		break;
47142e0442fSSean Young 
47242e0442fSSean Young 	case LIRC_GET_MAX_TIMEOUT:
47342e0442fSSean Young 		if (!dev->max_timeout)
47442e0442fSSean Young 			return -ENOTTY;
47542e0442fSSean Young 		val = dev->max_timeout / 1000;
47642e0442fSSean Young 		break;
47742e0442fSSean Young 
47842e0442fSSean Young 	case LIRC_SET_REC_TIMEOUT:
47942e0442fSSean Young 		if (!dev->max_timeout)
48042e0442fSSean Young 			return -ENOTTY;
48142e0442fSSean Young 
48242e0442fSSean Young 		/* Check for multiply overflow */
48342e0442fSSean Young 		if (val > U32_MAX / 1000)
48442e0442fSSean Young 			return -EINVAL;
48542e0442fSSean Young 
48642e0442fSSean Young 		tmp = val * 1000;
48742e0442fSSean Young 
48842e0442fSSean Young 		if (tmp < dev->min_timeout || tmp > dev->max_timeout)
48942e0442fSSean Young 			return -EINVAL;
49042e0442fSSean Young 
49142e0442fSSean Young 		if (dev->s_timeout)
49242e0442fSSean Young 			ret = dev->s_timeout(dev, tmp);
49342e0442fSSean Young 		if (!ret)
49442e0442fSSean Young 			dev->timeout = tmp;
49542e0442fSSean Young 		break;
49642e0442fSSean Young 
49742e0442fSSean Young 	case LIRC_SET_REC_TIMEOUT_REPORTS:
49842e0442fSSean Young 		if (!dev->timeout)
49942e0442fSSean Young 			return -ENOTTY;
50042e0442fSSean Young 
50142e0442fSSean Young 		dev->send_timeout_reports = !!val;
50242e0442fSSean Young 		break;
50342e0442fSSean Young 
50442e0442fSSean Young 	default:
50542e0442fSSean Young 		return -ENOTTY;
50642e0442fSSean Young 	}
50742e0442fSSean Young 
50842e0442fSSean Young 	if (_IOC_DIR(cmd) & _IOC_READ)
50942e0442fSSean Young 		ret = put_user(val, argp);
51042e0442fSSean Young 
51142e0442fSSean Young 	return ret;
51242e0442fSSean Young }
51342e0442fSSean Young 
51442e0442fSSean Young static unsigned int ir_lirc_poll(struct file *file,
51542e0442fSSean Young 				 struct poll_table_struct *wait)
51642e0442fSSean Young {
51742e0442fSSean Young 	struct rc_dev *rcdev = file->private_data;
51842e0442fSSean Young 	unsigned int events = 0;
51942e0442fSSean Young 
52042e0442fSSean Young 	poll_wait(file, &rcdev->wait_poll, wait);
52142e0442fSSean Young 
52242e0442fSSean Young 	if (!rcdev->registered) {
52342e0442fSSean Young 		events = POLLHUP | POLLERR;
52442e0442fSSean Young 	} else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
52542e0442fSSean Young 		if (rcdev->rec_mode == LIRC_MODE_SCANCODE &&
52642e0442fSSean Young 		    !kfifo_is_empty(&rcdev->scancodes))
52742e0442fSSean Young 			events = POLLIN | POLLRDNORM;
52842e0442fSSean Young 
52942e0442fSSean Young 		if (rcdev->rec_mode == LIRC_MODE_MODE2 &&
53042e0442fSSean Young 		    !kfifo_is_empty(&rcdev->rawir))
53142e0442fSSean Young 			events = POLLIN | POLLRDNORM;
53242e0442fSSean Young 	}
53342e0442fSSean Young 
53442e0442fSSean Young 	return events;
53542e0442fSSean Young }
53642e0442fSSean Young 
53742e0442fSSean Young static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
53842e0442fSSean Young 				  size_t length)
53942e0442fSSean Young {
54042e0442fSSean Young 	struct rc_dev *rcdev = file->private_data;
54142e0442fSSean Young 	unsigned int copied;
54242e0442fSSean Young 	int ret;
54342e0442fSSean Young 
54442e0442fSSean Young 	if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
54542e0442fSSean Young 		return -EINVAL;
54642e0442fSSean Young 
54742e0442fSSean Young 	do {
54842e0442fSSean Young 		if (kfifo_is_empty(&rcdev->rawir)) {
54942e0442fSSean Young 			if (file->f_flags & O_NONBLOCK)
55042e0442fSSean Young 				return -EAGAIN;
55142e0442fSSean Young 
55242e0442fSSean Young 			ret = wait_event_interruptible(rcdev->wait_poll,
55342e0442fSSean Young 					!kfifo_is_empty(&rcdev->rawir) ||
55442e0442fSSean Young 					!rcdev->registered);
55542e0442fSSean Young 			if (ret)
55642e0442fSSean Young 				return ret;
55742e0442fSSean Young 		}
55842e0442fSSean Young 
55942e0442fSSean Young 		if (!rcdev->registered)
56042e0442fSSean Young 			return -ENODEV;
56142e0442fSSean Young 
56242e0442fSSean Young 		ret = mutex_lock_interruptible(&rcdev->lock);
56342e0442fSSean Young 		if (ret)
56442e0442fSSean Young 			return ret;
56542e0442fSSean Young 		ret = kfifo_to_user(&rcdev->rawir, buffer, length, &copied);
56642e0442fSSean Young 		mutex_unlock(&rcdev->lock);
56742e0442fSSean Young 		if (ret)
56842e0442fSSean Young 			return ret;
56942e0442fSSean Young 	} while (copied == 0);
57042e0442fSSean Young 
57142e0442fSSean Young 	return copied;
57242e0442fSSean Young }
57342e0442fSSean Young 
57442e0442fSSean Young static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
57542e0442fSSean Young 				     size_t length)
57642e0442fSSean Young {
57742e0442fSSean Young 	struct rc_dev *rcdev = file->private_data;
57842e0442fSSean Young 	unsigned int copied;
57942e0442fSSean Young 	int ret;
58042e0442fSSean Young 
58142e0442fSSean Young 	if (length < sizeof(struct lirc_scancode) ||
58242e0442fSSean Young 	    length % sizeof(struct lirc_scancode))
58342e0442fSSean Young 		return -EINVAL;
58442e0442fSSean Young 
58542e0442fSSean Young 	do {
58642e0442fSSean Young 		if (kfifo_is_empty(&rcdev->scancodes)) {
58742e0442fSSean Young 			if (file->f_flags & O_NONBLOCK)
58842e0442fSSean Young 				return -EAGAIN;
58942e0442fSSean Young 
59042e0442fSSean Young 			ret = wait_event_interruptible(rcdev->wait_poll,
59142e0442fSSean Young 					!kfifo_is_empty(&rcdev->scancodes) ||
59242e0442fSSean Young 					!rcdev->registered);
59342e0442fSSean Young 			if (ret)
59442e0442fSSean Young 				return ret;
59542e0442fSSean Young 		}
59642e0442fSSean Young 
59742e0442fSSean Young 		if (!rcdev->registered)
59842e0442fSSean Young 			return -ENODEV;
59942e0442fSSean Young 
60042e0442fSSean Young 		ret = mutex_lock_interruptible(&rcdev->lock);
60142e0442fSSean Young 		if (ret)
60242e0442fSSean Young 			return ret;
60342e0442fSSean Young 		ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied);
60442e0442fSSean Young 		mutex_unlock(&rcdev->lock);
60542e0442fSSean Young 		if (ret)
60642e0442fSSean Young 			return ret;
60742e0442fSSean Young 	} while (copied == 0);
60842e0442fSSean Young 
60942e0442fSSean Young 	return copied;
61042e0442fSSean Young }
61142e0442fSSean Young 
61242e0442fSSean Young static ssize_t ir_lirc_read(struct file *file, char __user *buffer,
61342e0442fSSean Young 			    size_t length, loff_t *ppos)
61442e0442fSSean Young {
61542e0442fSSean Young 	struct rc_dev *rcdev = file->private_data;
61642e0442fSSean Young 
61742e0442fSSean Young 	if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
61842e0442fSSean Young 		return -EINVAL;
61942e0442fSSean Young 
62042e0442fSSean Young 	if (!rcdev->registered)
62142e0442fSSean Young 		return -ENODEV;
62242e0442fSSean Young 
62342e0442fSSean Young 	if (rcdev->rec_mode == LIRC_MODE_MODE2)
62442e0442fSSean Young 		return ir_lirc_read_mode2(file, buffer, length);
62542e0442fSSean Young 	else /* LIRC_MODE_SCANCODE */
62642e0442fSSean Young 		return ir_lirc_read_scancode(file, buffer, length);
62742e0442fSSean Young }
62842e0442fSSean Young 
62942e0442fSSean Young static const struct file_operations lirc_fops = {
63042e0442fSSean Young 	.owner		= THIS_MODULE,
63142e0442fSSean Young 	.write		= ir_lirc_transmit_ir,
63242e0442fSSean Young 	.unlocked_ioctl	= ir_lirc_ioctl,
63342e0442fSSean Young #ifdef CONFIG_COMPAT
63442e0442fSSean Young 	.compat_ioctl	= ir_lirc_ioctl,
63542e0442fSSean Young #endif
63642e0442fSSean Young 	.read		= ir_lirc_read,
63742e0442fSSean Young 	.poll		= ir_lirc_poll,
63842e0442fSSean Young 	.open		= ir_lirc_open,
63942e0442fSSean Young 	.release	= ir_lirc_close,
64042e0442fSSean Young 	.llseek		= no_llseek,
64142e0442fSSean Young };
64242e0442fSSean Young 
643b15e3937SDavid Härdeman static void lirc_release_device(struct device *ld)
64432cf86f6SMauro Carvalho Chehab {
645a6ddd4feSSean Young 	struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev);
646a607f51eSSean Young 
64771695affSSean Young 	if (rcdev->driver_type == RC_DRIVER_IR_RAW)
64871695affSSean Young 		kfifo_free(&rcdev->rawir);
649de142c32SSean Young 	if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX)
650de142c32SSean Young 		kfifo_free(&rcdev->scancodes);
651b15e3937SDavid Härdeman 
652a6ddd4feSSean Young 	put_device(&rcdev->dev);
653b15e3937SDavid Härdeman }
654b15e3937SDavid Härdeman 
655a6ddd4feSSean Young int ir_lirc_register(struct rc_dev *dev)
6566ecccc37SDavid Härdeman {
657a6ddd4feSSean Young 	int err, minor;
658b15e3937SDavid Härdeman 
659a6ddd4feSSean Young 	device_initialize(&dev->lirc_dev);
660a6ddd4feSSean Young 	dev->lirc_dev.class = lirc_class;
661a6ddd4feSSean Young 	dev->lirc_dev.release = lirc_release_device;
662a6ddd4feSSean Young 	dev->send_mode = LIRC_MODE_PULSE;
663b15e3937SDavid Härdeman 
66462d6f199SSean Young 	if (dev->driver_type == RC_DRIVER_SCANCODE)
66562d6f199SSean Young 		dev->rec_mode = LIRC_MODE_SCANCODE;
66662d6f199SSean Young 	else
667de142c32SSean Young 		dev->rec_mode = LIRC_MODE_MODE2;
668de142c32SSean Young 
669a6ddd4feSSean Young 	if (dev->driver_type == RC_DRIVER_IR_RAW) {
670a6ddd4feSSean Young 		if (kfifo_alloc(&dev->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL))
67171695affSSean Young 			return -ENOMEM;
6723381b779SDavid Härdeman 	}
6733381b779SDavid Härdeman 
674de142c32SSean Young 	if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
675de142c32SSean Young 		if (kfifo_alloc(&dev->scancodes, 32, GFP_KERNEL)) {
676de142c32SSean Young 			kfifo_free(&dev->rawir);
677de142c32SSean Young 			return -ENOMEM;
678de142c32SSean Young 		}
679de142c32SSean Young 	}
680de142c32SSean Young 
681a6ddd4feSSean Young 	init_waitqueue_head(&dev->wait_poll);
68271695affSSean Young 
683a6ddd4feSSean Young 	minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL);
684a6ddd4feSSean Young 	if (minor < 0) {
685a6ddd4feSSean Young 		err = minor;
686a6ddd4feSSean Young 		goto out_kfifo;
687a6ddd4feSSean Young 	}
68832cf86f6SMauro Carvalho Chehab 
689a6ddd4feSSean Young 	dev->lirc_dev.parent = &dev->dev;
690a6ddd4feSSean Young 	dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor);
691a6ddd4feSSean Young 	dev_set_name(&dev->lirc_dev, "lirc%d", minor);
69232cf86f6SMauro Carvalho Chehab 
693a6ddd4feSSean Young 	cdev_init(&dev->lirc_cdev, &lirc_fops);
69432cf86f6SMauro Carvalho Chehab 
695a6ddd4feSSean Young 	err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev);
696a6ddd4feSSean Young 	if (err)
697a6ddd4feSSean Young 		goto out_ida;
698a6ddd4feSSean Young 
699a6ddd4feSSean Young 	get_device(&dev->dev);
700a6ddd4feSSean Young 
701a6ddd4feSSean Young 	dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d",
702a6ddd4feSSean Young 		 dev->driver_name, minor);
703a6ddd4feSSean Young 
704a6ddd4feSSean Young 	return 0;
705a6ddd4feSSean Young 
706a6ddd4feSSean Young out_ida:
70746c8f477SDavid Härdeman 	ida_simple_remove(&lirc_ida, minor);
708a6ddd4feSSean Young out_kfifo:
709a6ddd4feSSean Young 	if (dev->driver_type == RC_DRIVER_IR_RAW)
710a6ddd4feSSean Young 		kfifo_free(&dev->rawir);
711de142c32SSean Young 	if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
712de142c32SSean Young 		kfifo_free(&dev->scancodes);
7133381b779SDavid Härdeman 	return err;
7143381b779SDavid Härdeman }
71532cf86f6SMauro Carvalho Chehab 
716a6ddd4feSSean Young void ir_lirc_unregister(struct rc_dev *dev)
71732cf86f6SMauro Carvalho Chehab {
718a6ddd4feSSean Young 	dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n",
719a6ddd4feSSean Young 		dev->driver_name, MINOR(dev->lirc_dev.devt));
72071695affSSean Young 
721a6ddd4feSSean Young 	mutex_lock(&dev->lock);
72232cf86f6SMauro Carvalho Chehab 
723a6ddd4feSSean Young 	if (dev->lirc_open) {
724a6ddd4feSSean Young 		dev_dbg(&dev->dev, LOGHEAD "releasing opened driver\n",
725a6ddd4feSSean Young 			dev->driver_name, MINOR(dev->lirc_dev.devt));
726a6ddd4feSSean Young 		wake_up_poll(&dev->wait_poll, POLLHUP);
72774c839b2SSean Young 	}
72874c839b2SSean Young 
729a6ddd4feSSean Young 	mutex_unlock(&dev->lock);
73032cf86f6SMauro Carvalho Chehab 
731a6ddd4feSSean Young 	cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev);
732a6ddd4feSSean Young 	ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt));
733a6ddd4feSSean Young 	put_device(&dev->lirc_dev);
73432cf86f6SMauro Carvalho Chehab }
73532cf86f6SMauro Carvalho Chehab 
736a60d64b1SSean Young int __init lirc_dev_init(void)
73732cf86f6SMauro Carvalho Chehab {
73832cf86f6SMauro Carvalho Chehab 	int retval;
73932cf86f6SMauro Carvalho Chehab 
74032cf86f6SMauro Carvalho Chehab 	lirc_class = class_create(THIS_MODULE, "lirc");
74132cf86f6SMauro Carvalho Chehab 	if (IS_ERR(lirc_class)) {
7423fac0314SAndi Shyti 		pr_err("class_create failed\n");
74354fcecafSAndi Shyti 		return PTR_ERR(lirc_class);
74432cf86f6SMauro Carvalho Chehab 	}
74532cf86f6SMauro Carvalho Chehab 
746a6ddd4feSSean Young 	retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX,
747463015ddSDavid Härdeman 				     "BaseRemoteCtl");
74832cf86f6SMauro Carvalho Chehab 	if (retval) {
74932cf86f6SMauro Carvalho Chehab 		class_destroy(lirc_class);
7503fac0314SAndi Shyti 		pr_err("alloc_chrdev_region failed\n");
75154fcecafSAndi Shyti 		return retval;
75232cf86f6SMauro Carvalho Chehab 	}
75332cf86f6SMauro Carvalho Chehab 
7543fac0314SAndi Shyti 	pr_info("IR Remote Control driver registered, major %d\n",
7553fac0314SAndi Shyti 						MAJOR(lirc_base_dev));
75632cf86f6SMauro Carvalho Chehab 
75754fcecafSAndi Shyti 	return 0;
75832cf86f6SMauro Carvalho Chehab }
75932cf86f6SMauro Carvalho Chehab 
760a60d64b1SSean Young void __exit lirc_dev_exit(void)
76132cf86f6SMauro Carvalho Chehab {
76232cf86f6SMauro Carvalho Chehab 	class_destroy(lirc_class);
763a6ddd4feSSean Young 	unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
76432cf86f6SMauro Carvalho Chehab }
765