ir-lirc-codec.c 8.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* ir-lirc-codec.c - ir-core to classic lirc interface bridge
 *
 * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
 *
 * 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 <linux/sched.h>
#include <linux/wait.h>
#include <media/lirc.h>
18
#include <media/lirc_dev.h>
19
#include <media/rc-core.h>
20
#include "rc-core-priv.h"
21 22 23 24 25 26

#define LIRCBUF_SIZE 256

/**
 * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the
 *		      lircd userspace daemon for decoding.
27
 * @input_dev:	the struct rc_dev descriptor of the device
28 29 30 31
 * @duration:	the struct ir_raw_event descriptor of the pulse/space
 *
 * This function returns -EINVAL if the lirc interfaces aren't wired up.
 */
32
static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
33
{
34
	struct lirc_codec *lirc = &dev->raw->lirc;
M
Maxim Levitsky 已提交
35
	int sample;
36

37
	if (!(dev->raw->enabled_protocols & IR_TYPE_LIRC))
38 39
		return 0;

40
	if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
41 42
		return -EINVAL;

43 44
	/* Packet start */
	if (ev.reset)
M
Maxim Levitsky 已提交
45 46
		return 0;

47 48 49
	/* Carrier reports */
	if (ev.carrier_report) {
		sample = LIRC_FREQUENCY(ev.carrier);
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	/* Packet end */
	} else if (ev.timeout) {

		if (lirc->gap)
			return 0;

		lirc->gap_start = ktime_get();
		lirc->gap = true;
		lirc->gap_duration = ev.duration;

		if (!lirc->send_timeout_reports)
			return 0;

		sample = LIRC_TIMEOUT(ev.duration / 1000);

	/* Normal sample */
	} else {

		if (lirc->gap) {
			int gap_sample;

			lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
				lirc->gap_start));

			/* Convert to ms and cap by LIRC_VALUE_MASK */
			do_div(lirc->gap_duration, 1000);
			lirc->gap_duration = min(lirc->gap_duration,
							(u64)LIRC_VALUE_MASK);

			gap_sample = LIRC_SPACE(lirc->gap_duration);
81
			lirc_buffer_write(dev->raw->lirc.drv->rbuf,
82 83 84 85 86 87 88
						(unsigned char *) &gap_sample);
			lirc->gap = false;
		}

		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
					LIRC_SPACE(ev.duration / 1000);
	}
89

90
	lirc_buffer_write(dev->raw->lirc.drv->rbuf,
M
Maxim Levitsky 已提交
91
			  (unsigned char *) &sample);
92
	wake_up(&dev->raw->lirc.drv->rbuf->wait_poll);
93 94 95 96 97 98 99 100

	return 0;
}

static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
				   size_t n, loff_t *ppos)
{
	struct lirc_codec *lirc;
101
	struct rc_dev *dev;
102 103 104 105 106 107 108 109 110 111 112 113 114 115
	int *txbuf; /* buffer with values to transmit */
	int ret = 0, count;

	lirc = lirc_get_pdata(file);
	if (!lirc)
		return -EFAULT;

	if (n % sizeof(int))
		return -EINVAL;

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

116 117 118
	txbuf = memdup_user(buf, n);
	if (IS_ERR(txbuf))
		return PTR_ERR(txbuf);
119

120 121
	dev = lirc->dev;
	if (!dev) {
122 123 124 125
		ret = -EFAULT;
		goto out;
	}

126 127
	if (dev->tx_ir)
		ret = dev->tx_ir(dev, txbuf, (u32)n);
128 129 130 131 132 133

out:
	kfree(txbuf);
	return ret;
}

134 135
static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
			unsigned long __user arg)
136 137
{
	struct lirc_codec *lirc;
138
	struct rc_dev *dev;
139
	int ret = 0;
140
	__u32 val = 0, tmp;
141 142 143 144 145

	lirc = lirc_get_pdata(filep);
	if (!lirc)
		return -EFAULT;

146 147
	dev = lirc->dev;
	if (!dev)
148 149
		return -EFAULT;

150
	if (_IOC_DIR(cmd) & _IOC_WRITE) {
151
		ret = get_user(val, (__u32 *)arg);
152 153
		if (ret)
			return ret;
154 155 156 157 158 159 160 161 162 163 164 165
	}

	switch (cmd) {

	/* legacy support */
	case LIRC_GET_SEND_MODE:
		val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
		break;

	case LIRC_SET_SEND_MODE:
		if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
			return -EINVAL;
166
		return 0;
167

168 169
	/* TX settings */
	case LIRC_SET_TRANSMITTER_MASK:
170
		if (!dev->s_tx_mask)
171
			return -EINVAL;
172

173
		return dev->s_tx_mask(dev, val);
174 175

	case LIRC_SET_SEND_CARRIER:
176
		if (!dev->s_tx_carrier)
177
			return -EINVAL;
178

179
		return dev->s_tx_carrier(dev, val);
180

181
	case LIRC_SET_SEND_DUTY_CYCLE:
182
		if (!dev->s_tx_duty_cycle)
183 184 185 186 187
			return -ENOSYS;

		if (val <= 0 || val >= 100)
			return -EINVAL;

188
		return dev->s_tx_duty_cycle(dev, val);
189

190 191
	/* RX settings */
	case LIRC_SET_REC_CARRIER:
192
		if (!dev->s_rx_carrier_range)
193
			return -ENOSYS;
194

195 196 197
		if (val <= 0)
			return -EINVAL;

198 199 200
		return dev->s_rx_carrier_range(dev,
					       dev->raw->lirc.carrier_low,
					       val);
201 202

	case LIRC_SET_REC_CARRIER_RANGE:
203 204
		if (val <= 0)
			return -EINVAL;
205

206
		dev->raw->lirc.carrier_low = val;
207
		return 0;
208 209

	case LIRC_GET_REC_RESOLUTION:
210
		val = dev->rx_resolution;
211 212 213
		break;

	case LIRC_SET_WIDEBAND_RECEIVER:
214
		if (!dev->s_learning_mode)
215 216
			return -ENOSYS;

217
		return dev->s_learning_mode(dev, !!val);
218 219

	case LIRC_SET_MEASURE_CARRIER_MODE:
220
		if (!dev->s_carrier_report)
221 222
			return -ENOSYS;

223
		return dev->s_carrier_report(dev, !!val);
224

225 226
	/* Generic timeout support */
	case LIRC_GET_MIN_TIMEOUT:
227
		if (!dev->max_timeout)
228
			return -ENOSYS;
229
		val = dev->min_timeout / 1000;
230 231 232
		break;

	case LIRC_GET_MAX_TIMEOUT:
233
		if (!dev->max_timeout)
234
			return -ENOSYS;
235
		val = dev->max_timeout / 1000;
236 237 238
		break;

	case LIRC_SET_REC_TIMEOUT:
239
		if (!dev->max_timeout)
240 241 242 243
			return -ENOSYS;

		tmp = val * 1000;

244 245
		if (tmp < dev->min_timeout ||
		    tmp > dev->max_timeout)
246 247
				return -EINVAL;

248
		dev->timeout = tmp;
249 250 251 252
		break;

	case LIRC_SET_REC_TIMEOUT_REPORTS:
		lirc->send_timeout_reports = !!val;
253 254 255
		break;

	default:
256
		return lirc_dev_fop_ioctl(filep, cmd, arg);
257 258
	}

259
	if (_IOC_DIR(cmd) & _IOC_READ)
260
		ret = put_user(val, (__u32 *)arg);
261

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	return ret;
}

static int ir_lirc_open(void *data)
{
	return 0;
}

static void ir_lirc_close(void *data)
{
	return;
}

static struct file_operations lirc_fops = {
	.owner		= THIS_MODULE,
	.write		= ir_lirc_transmit_ir,
278
	.unlocked_ioctl	= ir_lirc_ioctl,
279 280 281
#ifdef CONFIG_COMPAT
	.compat_ioctl	= ir_lirc_ioctl,
#endif
282 283 284 285
	.read		= lirc_dev_fop_read,
	.poll		= lirc_dev_fop_poll,
	.open		= lirc_dev_fop_open,
	.release	= lirc_dev_fop_close,
A
Arnd Bergmann 已提交
286
	.llseek		= no_llseek,
287 288
};

289
static int ir_lirc_register(struct rc_dev *dev)
290 291 292 293 294 295 296 297 298 299 300
{
	struct lirc_driver *drv;
	struct lirc_buffer *rbuf;
	int rc = -ENOMEM;
	unsigned long features;

	drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
	if (!drv)
		return rc;

	rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
301
	if (!rbuf)
302 303 304 305 306 307 308
		goto rbuf_alloc_failed;

	rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE);
	if (rc)
		goto rbuf_init_failed;

	features = LIRC_CAN_REC_MODE2;
309
	if (dev->tx_ir) {
310
		features |= LIRC_CAN_SEND_PULSE;
311
		if (dev->s_tx_mask)
312
			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
313
		if (dev->s_tx_carrier)
314
			features |= LIRC_CAN_SET_SEND_CARRIER;
315
		if (dev->s_tx_duty_cycle)
316
			features |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
317 318
	}

319
	if (dev->s_rx_carrier_range)
320 321 322
		features |= LIRC_CAN_SET_REC_CARRIER |
			LIRC_CAN_SET_REC_CARRIER_RANGE;

323
	if (dev->s_learning_mode)
324 325
		features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;

326
	if (dev->s_carrier_report)
327 328
		features |= LIRC_CAN_MEASURE_CARRIER;

329
	if (dev->max_timeout)
330 331
		features |= LIRC_CAN_SET_REC_TIMEOUT;

332
	snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
333
		 dev->driver_name);
334 335
	drv->minor = -1;
	drv->features = features;
336
	drv->data = &dev->raw->lirc;
337 338 339 340 341
	drv->rbuf = rbuf;
	drv->set_use_inc = &ir_lirc_open;
	drv->set_use_dec = &ir_lirc_close;
	drv->code_length = sizeof(struct ir_raw_event) * 8;
	drv->fops = &lirc_fops;
342
	drv->dev = &dev->dev;
343 344 345 346 347 348 349 350
	drv->owner = THIS_MODULE;

	drv->minor = lirc_register_driver(drv);
	if (drv->minor < 0) {
		rc = -ENODEV;
		goto lirc_register_failed;
	}

351 352
	dev->raw->lirc.drv = drv;
	dev->raw->lirc.dev = dev;
353 354 355 356 357 358 359 360 361 362 363
	return 0;

lirc_register_failed:
rbuf_init_failed:
	kfree(rbuf);
rbuf_alloc_failed:
	kfree(drv);

	return rc;
}

364
static int ir_lirc_unregister(struct rc_dev *dev)
365
{
366
	struct lirc_codec *lirc = &dev->raw->lirc;
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401

	lirc_unregister_driver(lirc->drv->minor);
	lirc_buffer_free(lirc->drv->rbuf);
	kfree(lirc->drv);

	return 0;
}

static struct ir_raw_handler lirc_handler = {
	.protocols	= IR_TYPE_LIRC,
	.decode		= ir_lirc_decode,
	.raw_register	= ir_lirc_register,
	.raw_unregister	= ir_lirc_unregister,
};

static int __init ir_lirc_codec_init(void)
{
	ir_raw_handler_register(&lirc_handler);

	printk(KERN_INFO "IR LIRC bridge handler initialized\n");
	return 0;
}

static void __exit ir_lirc_codec_exit(void)
{
	ir_raw_handler_unregister(&lirc_handler);
}

module_init(ir_lirc_codec_init);
module_exit(ir_lirc_codec_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
MODULE_DESCRIPTION("LIRC IR handler bridge");