blob: 92db1e83c192b730b5a406efc637d436e4718d2a [file] [log] [blame]
Jarod Wilson4a62a5a2010-07-03 01:06:57 -03001/*
2 * LIRC base driver
3 *
4 * by Artur Lipowski <alipowski@interia.pl>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030016 */
17
Andi Shyti3fac0312016-07-06 06:01:16 -030018#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030020#include <linux/module.h>
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030021#include <linux/mutex.h>
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030022#include <linux/device.h>
Sean Youngf4364dc2018-05-27 12:24:09 +010023#include <linux/file.h>
David Härdeman46c8f472017-06-25 09:32:05 -030024#include <linux/idr.h>
Sean Younga6ddd4f2017-09-26 09:34:47 -040025#include <linux/poll.h>
Sean Young42e04422017-11-02 16:39:16 -040026#include <linux/sched.h>
27#include <linux/wait.h>
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030028
Sean Younga60d64b2017-09-23 10:41:13 -040029#include "rc-core-priv.h"
Sean Youngaefb5e32017-11-02 16:44:21 -040030#include <uapi/linux/lirc.h>
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030031
Sean Young42e04422017-11-02 16:39:16 -040032#define LIRCBUF_SIZE 256
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030033
34static dev_t lirc_base_dev;
35
David Härdeman46c8f472017-06-25 09:32:05 -030036/* Used to keep track of allocated lirc devices */
David Härdeman46c8f472017-06-25 09:32:05 -030037static DEFINE_IDA(lirc_ida);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030038
39/* Only used for sysfs but defined to void otherwise */
40static struct class *lirc_class;
41
Sean Young42e04422017-11-02 16:39:16 -040042/**
43 * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace
44 *
45 * @dev: the struct rc_dev descriptor of the device
46 * @ev: the struct ir_raw_event descriptor of the pulse/space
47 */
48void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -030049{
Sean Young7e45d662017-11-02 17:21:13 -040050 unsigned long flags;
51 struct lirc_fh *fh;
Sean Young42e04422017-11-02 16:39:16 -040052 int sample;
Sean Younga607f512017-08-04 10:12:03 -040053
Sean Young42e04422017-11-02 16:39:16 -040054 /* Packet start */
55 if (ev.reset) {
56 /*
57 * Userspace expects a long space event before the start of
58 * the signal to use as a sync. This may be done with repeat
59 * packets and normal samples. But if a reset has been sent
60 * then we assume that a long time has passed, so we send a
61 * space with the maximum time value.
62 */
63 sample = LIRC_SPACE(LIRC_VALUE_MASK);
Sean Young1f17f682018-02-12 07:27:50 -050064 dev_dbg(&dev->dev, "delivering reset sync space to lirc_dev\n");
David Härdemanb15e3932017-06-25 09:32:36 -030065
Sean Young42e04422017-11-02 16:39:16 -040066 /* Carrier reports */
67 } else if (ev.carrier_report) {
68 sample = LIRC_FREQUENCY(ev.carrier);
Sean Young1f17f682018-02-12 07:27:50 -050069 dev_dbg(&dev->dev, "carrier report (freq: %d)\n", sample);
Sean Young42e04422017-11-02 16:39:16 -040070
71 /* Packet end */
72 } else if (ev.timeout) {
73 if (dev->gap)
74 return;
75
76 dev->gap_start = ktime_get();
77 dev->gap = true;
78 dev->gap_duration = ev.duration;
79
Sean Young42e04422017-11-02 16:39:16 -040080 sample = LIRC_TIMEOUT(ev.duration / 1000);
Sean Young1f17f682018-02-12 07:27:50 -050081 dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample);
Sean Young42e04422017-11-02 16:39:16 -040082
83 /* Normal sample */
84 } else {
85 if (dev->gap) {
86 dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
87 dev->gap_start));
88
89 /* Convert to ms and cap by LIRC_VALUE_MASK */
90 do_div(dev->gap_duration, 1000);
91 dev->gap_duration = min_t(u64, dev->gap_duration,
92 LIRC_VALUE_MASK);
93
Sean Young7e45d662017-11-02 17:21:13 -040094 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
95 list_for_each_entry(fh, &dev->lirc_fh, list)
96 kfifo_put(&fh->rawir,
97 LIRC_SPACE(dev->gap_duration));
98 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
Sean Young42e04422017-11-02 16:39:16 -040099 dev->gap = false;
100 }
101
102 sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
103 LIRC_SPACE(ev.duration / 1000);
Sean Young1f17f682018-02-12 07:27:50 -0500104 dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n",
105 TO_US(ev.duration), TO_STR(ev.pulse));
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300106 }
Sean Young42e04422017-11-02 16:39:16 -0400107
Sean Youngf4364dc2018-05-27 12:24:09 +0100108 /*
109 * bpf does not care about the gap generated above; that exists
110 * for backwards compatibility
111 */
112 lirc_bpf_run(dev, sample);
113
Sean Young7e45d662017-11-02 17:21:13 -0400114 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
115 list_for_each_entry(fh, &dev->lirc_fh, list) {
116 if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports)
117 continue;
118 if (kfifo_put(&fh->rawir, sample))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800119 wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM);
Sean Young7e45d662017-11-02 17:21:13 -0400120 }
121 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
David Härdeman3381b772017-06-30 05:41:57 -0300122}
123
Sean Young42e04422017-11-02 16:39:16 -0400124/**
125 * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to
Sean Young7e45d662017-11-02 17:21:13 -0400126 * userspace. This can be called in atomic context.
Sean Young42e04422017-11-02 16:39:16 -0400127 * @dev: the struct rc_dev descriptor of the device
128 * @lsc: the struct lirc_scancode describing the decoded scancode
129 */
130void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc)
David Härdeman3381b772017-06-30 05:41:57 -0300131{
Sean Young7e45d662017-11-02 17:21:13 -0400132 unsigned long flags;
133 struct lirc_fh *fh;
Sean Young74c839b2017-01-30 13:49:58 -0200134
Sean Young42e04422017-11-02 16:39:16 -0400135 lsc->timestamp = ktime_get_ns();
136
Sean Young7e45d662017-11-02 17:21:13 -0400137 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
138 list_for_each_entry(fh, &dev->lirc_fh, list) {
139 if (kfifo_put(&fh->scancodes, *lsc))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800140 wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM);
Andi Shyti6fa99e12016-07-06 06:01:13 -0300141 }
Sean Young7e45d662017-11-02 17:21:13 -0400142 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
Andi Shyti6fa99e12016-07-06 06:01:13 -0300143}
Sean Young42e04422017-11-02 16:39:16 -0400144EXPORT_SYMBOL_GPL(ir_lirc_scancode_event);
Andi Shyti6fa99e12016-07-06 06:01:13 -0300145
Sean Young42e04422017-11-02 16:39:16 -0400146static int ir_lirc_open(struct inode *inode, struct file *file)
David Härdeman6ecccc32017-06-25 09:32:15 -0300147{
Sean Young42e04422017-11-02 16:39:16 -0400148 struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev,
149 lirc_cdev);
Sean Young7e45d662017-11-02 17:21:13 -0400150 struct lirc_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
151 unsigned long flags;
David Härdemande226ec2017-06-25 09:31:19 -0300152 int retval;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300153
Sean Young7e45d662017-11-02 17:21:13 -0400154 if (!fh)
155 return -ENOMEM;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300156
Sean Young7e45d662017-11-02 17:21:13 -0400157 get_device(&dev->dev);
David Härdeman3381b772017-06-30 05:41:57 -0300158
Sean Young42e04422017-11-02 16:39:16 -0400159 if (!dev->registered) {
David Härdeman3381b772017-06-30 05:41:57 -0300160 retval = -ENODEV;
Sean Young7e45d662017-11-02 17:21:13 -0400161 goto out_fh;
David Härdeman3381b772017-06-30 05:41:57 -0300162 }
163
Sean Young7e45d662017-11-02 17:21:13 -0400164 if (dev->driver_type == RC_DRIVER_IR_RAW) {
165 if (kfifo_alloc(&fh->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) {
166 retval = -ENOMEM;
167 goto out_fh;
168 }
David Härdeman3381b772017-06-30 05:41:57 -0300169 }
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300170
Sean Young7e45d662017-11-02 17:21:13 -0400171 if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
172 if (kfifo_alloc(&fh->scancodes, 32, GFP_KERNEL)) {
173 retval = -ENOMEM;
174 goto out_rawir;
175 }
Srinivas Kandagatlaca7a7222013-07-22 04:23:07 -0300176 }
177
Sean Young7e45d662017-11-02 17:21:13 -0400178 fh->send_mode = LIRC_MODE_PULSE;
179 fh->rc = dev;
180 fh->send_timeout_reports = true;
David Härdeman2c5a1f442017-05-01 13:03:46 -0300181
Sean Young7e45d662017-11-02 17:21:13 -0400182 if (dev->driver_type == RC_DRIVER_SCANCODE)
183 fh->rec_mode = LIRC_MODE_SCANCODE;
184 else
185 fh->rec_mode = LIRC_MODE_MODE2;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300186
Sean Young7e45d662017-11-02 17:21:13 -0400187 retval = rc_open(dev);
188 if (retval)
189 goto out_kfifo;
190
191 init_waitqueue_head(&fh->wait_poll);
192
193 file->private_data = fh;
194 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
195 list_add(&fh->list, &dev->lirc_fh);
196 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
Sean Young42e04422017-11-02 16:39:16 -0400197
Kirill Smelkovc5bf68f2019-03-26 23:51:19 +0300198 stream_open(inode, file);
Arnd Bergmannd9d2e9d2010-08-15 18:51:56 +0200199
David Härdemande226ec2017-06-25 09:31:19 -0300200 return 0;
Sean Young7e45d662017-11-02 17:21:13 -0400201out_kfifo:
202 if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
203 kfifo_free(&fh->scancodes);
204out_rawir:
205 if (dev->driver_type == RC_DRIVER_IR_RAW)
206 kfifo_free(&fh->rawir);
207out_fh:
208 kfree(fh);
209 put_device(&dev->dev);
David Härdeman3381b772017-06-30 05:41:57 -0300210
David Härdeman3381b772017-06-30 05:41:57 -0300211 return retval;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300212}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300213
Sean Young42e04422017-11-02 16:39:16 -0400214static int ir_lirc_close(struct inode *inode, struct file *file)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300215{
Sean Young7e45d662017-11-02 17:21:13 -0400216 struct lirc_fh *fh = file->private_data;
217 struct rc_dev *dev = fh->rc;
218 unsigned long flags;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300219
Sean Young7e45d662017-11-02 17:21:13 -0400220 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
221 list_del(&fh->list);
222 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300223
Sean Young7e45d662017-11-02 17:21:13 -0400224 if (dev->driver_type == RC_DRIVER_IR_RAW)
225 kfifo_free(&fh->rawir);
226 if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
227 kfifo_free(&fh->scancodes);
228 kfree(fh);
David Härdeman3381b772017-06-30 05:41:57 -0300229
Sean Young42e04422017-11-02 16:39:16 -0400230 rc_close(dev);
Sean Young7e45d662017-11-02 17:21:13 -0400231 put_device(&dev->dev);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300232
233 return 0;
234}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300235
Sean Young42e04422017-11-02 16:39:16 -0400236static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
237 size_t n, loff_t *ppos)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300238{
Sean Young7e45d662017-11-02 17:21:13 -0400239 struct lirc_fh *fh = file->private_data;
240 struct rc_dev *dev = fh->rc;
Sean Younga74b2bf2017-12-13 16:09:21 -0500241 unsigned int *txbuf;
Sean Young42e04422017-11-02 16:39:16 -0400242 struct ir_raw_event *raw = NULL;
Sean Young49571332017-11-04 08:30:45 -0400243 ssize_t ret;
Sean Young42e04422017-11-02 16:39:16 -0400244 size_t count;
245 ktime_t start;
246 s64 towait;
247 unsigned int duration = 0; /* signal duration in us */
248 int i;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300249
Sean Young49571332017-11-04 08:30:45 -0400250 ret = mutex_lock_interruptible(&dev->lock);
251 if (ret)
252 return ret;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300253
Sean Young49571332017-11-04 08:30:45 -0400254 if (!dev->registered) {
255 ret = -ENODEV;
Sean Younga74b2bf2017-12-13 16:09:21 -0500256 goto out_unlock;
David Härdemanb15e3932017-06-25 09:32:36 -0300257 }
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300258
Sean Young42e04422017-11-02 16:39:16 -0400259 if (!dev->tx_ir) {
260 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500261 goto out_unlock;
Sean Young42e04422017-11-02 16:39:16 -0400262 }
263
Sean Young7e45d662017-11-02 17:21:13 -0400264 if (fh->send_mode == LIRC_MODE_SCANCODE) {
Sean Young42e04422017-11-02 16:39:16 -0400265 struct lirc_scancode scan;
266
Sean Young49571332017-11-04 08:30:45 -0400267 if (n != sizeof(scan)) {
268 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500269 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400270 }
Sean Young42e04422017-11-02 16:39:16 -0400271
Sean Young49571332017-11-04 08:30:45 -0400272 if (copy_from_user(&scan, buf, sizeof(scan))) {
273 ret = -EFAULT;
Sean Younga74b2bf2017-12-13 16:09:21 -0500274 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400275 }
Sean Young42e04422017-11-02 16:39:16 -0400276
Sean Young49571332017-11-04 08:30:45 -0400277 if (scan.flags || scan.keycode || scan.timestamp) {
278 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500279 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400280 }
Sean Young42e04422017-11-02 16:39:16 -0400281
282 /*
283 * The scancode field in lirc_scancode is 64-bit simply
284 * to future-proof it, since there are IR protocols encode
285 * use more than 32 bits. For now only 32-bit protocols
286 * are supported.
287 */
288 if (scan.scancode > U32_MAX ||
Sean Young49571332017-11-04 08:30:45 -0400289 !rc_validate_scancode(scan.rc_proto, scan.scancode)) {
290 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500291 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400292 }
Sean Young42e04422017-11-02 16:39:16 -0400293
294 raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
Sean Young49571332017-11-04 08:30:45 -0400295 if (!raw) {
296 ret = -ENOMEM;
Sean Younga74b2bf2017-12-13 16:09:21 -0500297 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400298 }
Sean Young42e04422017-11-02 16:39:16 -0400299
300 ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
301 raw, LIRCBUF_SIZE);
302 if (ret < 0)
Colin Ian King8d25e152017-12-19 11:48:25 -0500303 goto out_kfree_raw;
Sean Young42e04422017-11-02 16:39:16 -0400304
305 count = ret;
306
307 txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
308 if (!txbuf) {
309 ret = -ENOMEM;
Colin Ian King8d25e152017-12-19 11:48:25 -0500310 goto out_kfree_raw;
Sean Young42e04422017-11-02 16:39:16 -0400311 }
312
313 for (i = 0; i < count; i++)
314 /* Convert from NS to US */
315 txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000);
316
317 if (dev->s_tx_carrier) {
318 int carrier = ir_raw_encode_carrier(scan.rc_proto);
319
320 if (carrier > 0)
321 dev->s_tx_carrier(dev, carrier);
322 }
323 } else {
Sean Young49571332017-11-04 08:30:45 -0400324 if (n < sizeof(unsigned int) || n % sizeof(unsigned int)) {
325 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500326 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400327 }
Sean Young42e04422017-11-02 16:39:16 -0400328
329 count = n / sizeof(unsigned int);
Sean Young49571332017-11-04 08:30:45 -0400330 if (count > LIRCBUF_SIZE || count % 2 == 0) {
331 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500332 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400333 }
Sean Young42e04422017-11-02 16:39:16 -0400334
335 txbuf = memdup_user(buf, n);
Sean Young49571332017-11-04 08:30:45 -0400336 if (IS_ERR(txbuf)) {
337 ret = PTR_ERR(txbuf);
Sean Younga74b2bf2017-12-13 16:09:21 -0500338 goto out_unlock;
Sean Young49571332017-11-04 08:30:45 -0400339 }
Sean Young42e04422017-11-02 16:39:16 -0400340 }
341
342 for (i = 0; i < count; i++) {
343 if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
344 ret = -EINVAL;
Sean Younga74b2bf2017-12-13 16:09:21 -0500345 goto out_kfree;
Sean Young42e04422017-11-02 16:39:16 -0400346 }
347
348 duration += txbuf[i];
349 }
350
Sean Young29422732018-02-12 09:00:28 -0500351 start = ktime_get();
352
Sean Young42e04422017-11-02 16:39:16 -0400353 ret = dev->tx_ir(dev, txbuf, count);
354 if (ret < 0)
Sean Younga74b2bf2017-12-13 16:09:21 -0500355 goto out_kfree;
Sean Young42e04422017-11-02 16:39:16 -0400356
Sean Youngf81a8152017-12-13 16:30:22 -0500357 kfree(txbuf);
358 kfree(raw);
359 mutex_unlock(&dev->lock);
360
Sean Youngdde7edf2017-12-11 17:12:09 -0500361 /*
362 * The lircd gap calculation expects the write function to
363 * wait for the actual IR signal to be transmitted before
364 * returning.
365 */
366 towait = ktime_us_delta(ktime_add_us(start, duration),
367 ktime_get());
368 if (towait > 0) {
369 set_current_state(TASK_INTERRUPTIBLE);
370 schedule_timeout(usecs_to_jiffies(towait));
Sean Young42e04422017-11-02 16:39:16 -0400371 }
372
Sean Youngf81a8152017-12-13 16:30:22 -0500373 return n;
Sean Younga74b2bf2017-12-13 16:09:21 -0500374out_kfree:
Sean Young42e04422017-11-02 16:39:16 -0400375 kfree(txbuf);
Colin Ian King8d25e152017-12-19 11:48:25 -0500376out_kfree_raw:
Sean Young42e04422017-11-02 16:39:16 -0400377 kfree(raw);
Sean Younga74b2bf2017-12-13 16:09:21 -0500378out_unlock:
379 mutex_unlock(&dev->lock);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300380 return ret;
381}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300382
Sean Young7e45d662017-11-02 17:21:13 -0400383static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
Sean Young42e04422017-11-02 16:39:16 -0400384 unsigned long arg)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300385{
Sean Young7e45d662017-11-02 17:21:13 -0400386 struct lirc_fh *fh = file->private_data;
387 struct rc_dev *dev = fh->rc;
Sean Young42e04422017-11-02 16:39:16 -0400388 u32 __user *argp = (u32 __user *)(arg);
Sean Young49571332017-11-04 08:30:45 -0400389 u32 val = 0;
390 int ret;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300391
Sean Young42e04422017-11-02 16:39:16 -0400392 if (_IOC_DIR(cmd) & _IOC_WRITE) {
393 ret = get_user(val, argp);
394 if (ret)
395 return ret;
396 }
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300397
Sean Young49571332017-11-04 08:30:45 -0400398 ret = mutex_lock_interruptible(&dev->lock);
399 if (ret)
400 return ret;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300401
Sean Young49571332017-11-04 08:30:45 -0400402 if (!dev->registered) {
403 ret = -ENODEV;
David Härdeman3381b772017-06-30 05:41:57 -0300404 goto out;
405 }
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300406
407 switch (cmd) {
408 case LIRC_GET_FEATURES:
Sean Young42e04422017-11-02 16:39:16 -0400409 if (dev->driver_type == RC_DRIVER_SCANCODE)
410 val |= LIRC_CAN_REC_SCANCODE;
411
412 if (dev->driver_type == RC_DRIVER_IR_RAW) {
Sean Young02d742f2017-12-28 14:58:26 -0500413 val |= LIRC_CAN_REC_MODE2;
Sean Young42e04422017-11-02 16:39:16 -0400414 if (dev->rx_resolution)
415 val |= LIRC_CAN_GET_REC_RESOLUTION;
416 }
417
418 if (dev->tx_ir) {
Sean Young02d742f2017-12-28 14:58:26 -0500419 val |= LIRC_CAN_SEND_PULSE;
Sean Young42e04422017-11-02 16:39:16 -0400420 if (dev->s_tx_mask)
421 val |= LIRC_CAN_SET_TRANSMITTER_MASK;
422 if (dev->s_tx_carrier)
423 val |= LIRC_CAN_SET_SEND_CARRIER;
424 if (dev->s_tx_duty_cycle)
425 val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
426 }
427
428 if (dev->s_rx_carrier_range)
429 val |= LIRC_CAN_SET_REC_CARRIER |
430 LIRC_CAN_SET_REC_CARRIER_RANGE;
431
432 if (dev->s_learning_mode)
433 val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
434
435 if (dev->s_carrier_report)
436 val |= LIRC_CAN_MEASURE_CARRIER;
437
438 if (dev->max_timeout)
439 val |= LIRC_CAN_SET_REC_TIMEOUT;
440
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300441 break;
Sean Young42e04422017-11-02 16:39:16 -0400442
443 /* mode support */
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300444 case LIRC_GET_REC_MODE:
Sean Young42e04422017-11-02 16:39:16 -0400445 if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
Sean Young49571332017-11-04 08:30:45 -0400446 ret = -ENOTTY;
447 else
448 val = fh->rec_mode;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300449 break;
Sean Young42e04422017-11-02 16:39:16 -0400450
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300451 case LIRC_SET_REC_MODE:
Sean Young42e04422017-11-02 16:39:16 -0400452 switch (dev->driver_type) {
453 case RC_DRIVER_IR_RAW_TX:
Sean Young49571332017-11-04 08:30:45 -0400454 ret = -ENOTTY;
455 break;
Sean Young42e04422017-11-02 16:39:16 -0400456 case RC_DRIVER_SCANCODE:
457 if (val != LIRC_MODE_SCANCODE)
Sean Young49571332017-11-04 08:30:45 -0400458 ret = -EINVAL;
Sean Young42e04422017-11-02 16:39:16 -0400459 break;
460 case RC_DRIVER_IR_RAW:
461 if (!(val == LIRC_MODE_MODE2 ||
462 val == LIRC_MODE_SCANCODE))
Sean Young49571332017-11-04 08:30:45 -0400463 ret = -EINVAL;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300464 break;
465 }
466
Sean Young49571332017-11-04 08:30:45 -0400467 if (!ret)
468 fh->rec_mode = val;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300469 break;
Sean Young42e04422017-11-02 16:39:16 -0400470
471 case LIRC_GET_SEND_MODE:
472 if (!dev->tx_ir)
Sean Young49571332017-11-04 08:30:45 -0400473 ret = -ENOTTY;
474 else
475 val = fh->send_mode;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300476 break;
Sean Young42e04422017-11-02 16:39:16 -0400477
478 case LIRC_SET_SEND_MODE:
479 if (!dev->tx_ir)
Sean Young49571332017-11-04 08:30:45 -0400480 ret = -ENOTTY;
481 else if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
482 ret = -EINVAL;
483 else
484 fh->send_mode = val;
485 break;
Sean Young42e04422017-11-02 16:39:16 -0400486
487 /* TX settings */
488 case LIRC_SET_TRANSMITTER_MASK:
489 if (!dev->s_tx_mask)
Sean Young49571332017-11-04 08:30:45 -0400490 ret = -ENOTTY;
491 else
492 ret = dev->s_tx_mask(dev, val);
493 break;
Sean Young42e04422017-11-02 16:39:16 -0400494
495 case LIRC_SET_SEND_CARRIER:
496 if (!dev->s_tx_carrier)
Sean Young49571332017-11-04 08:30:45 -0400497 ret = -ENOTTY;
498 else
499 ret = dev->s_tx_carrier(dev, val);
500 break;
Sean Young42e04422017-11-02 16:39:16 -0400501
502 case LIRC_SET_SEND_DUTY_CYCLE:
503 if (!dev->s_tx_duty_cycle)
Sean Young49571332017-11-04 08:30:45 -0400504 ret = -ENOTTY;
505 else if (val <= 0 || val >= 100)
506 ret = -EINVAL;
507 else
508 ret = dev->s_tx_duty_cycle(dev, val);
509 break;
Sean Young42e04422017-11-02 16:39:16 -0400510
511 /* RX settings */
512 case LIRC_SET_REC_CARRIER:
513 if (!dev->s_rx_carrier_range)
Sean Young49571332017-11-04 08:30:45 -0400514 ret = -ENOTTY;
515 else if (val <= 0)
516 ret = -EINVAL;
517 else
518 ret = dev->s_rx_carrier_range(dev, fh->carrier_low,
519 val);
520 break;
Sean Young42e04422017-11-02 16:39:16 -0400521
522 case LIRC_SET_REC_CARRIER_RANGE:
523 if (!dev->s_rx_carrier_range)
Sean Young49571332017-11-04 08:30:45 -0400524 ret = -ENOTTY;
525 else if (val <= 0)
526 ret = -EINVAL;
527 else
528 fh->carrier_low = val;
529 break;
Sean Young42e04422017-11-02 16:39:16 -0400530
531 case LIRC_GET_REC_RESOLUTION:
532 if (!dev->rx_resolution)
Sean Young49571332017-11-04 08:30:45 -0400533 ret = -ENOTTY;
534 else
535 val = dev->rx_resolution / 1000;
Sean Young42e04422017-11-02 16:39:16 -0400536 break;
537
538 case LIRC_SET_WIDEBAND_RECEIVER:
539 if (!dev->s_learning_mode)
Sean Young49571332017-11-04 08:30:45 -0400540 ret = -ENOTTY;
541 else
542 ret = dev->s_learning_mode(dev, !!val);
543 break;
Sean Young42e04422017-11-02 16:39:16 -0400544
545 case LIRC_SET_MEASURE_CARRIER_MODE:
546 if (!dev->s_carrier_report)
Sean Young49571332017-11-04 08:30:45 -0400547 ret = -ENOTTY;
548 else
549 ret = dev->s_carrier_report(dev, !!val);
550 break;
Sean Young42e04422017-11-02 16:39:16 -0400551
552 /* Generic timeout support */
553 case LIRC_GET_MIN_TIMEOUT:
554 if (!dev->max_timeout)
Sean Young49571332017-11-04 08:30:45 -0400555 ret = -ENOTTY;
556 else
557 val = DIV_ROUND_UP(dev->min_timeout, 1000);
Sean Young42e04422017-11-02 16:39:16 -0400558 break;
559
560 case LIRC_GET_MAX_TIMEOUT:
561 if (!dev->max_timeout)
Sean Young49571332017-11-04 08:30:45 -0400562 ret = -ENOTTY;
563 else
564 val = dev->max_timeout / 1000;
Sean Young42e04422017-11-02 16:39:16 -0400565 break;
566
567 case LIRC_SET_REC_TIMEOUT:
Sean Young49571332017-11-04 08:30:45 -0400568 if (!dev->max_timeout) {
569 ret = -ENOTTY;
570 } else if (val > U32_MAX / 1000) {
571 /* Check for multiply overflow */
572 ret = -EINVAL;
573 } else {
574 u32 tmp = val * 1000;
Sean Young42e04422017-11-02 16:39:16 -0400575
Sean Young49571332017-11-04 08:30:45 -0400576 if (tmp < dev->min_timeout || tmp > dev->max_timeout)
577 ret = -EINVAL;
578 else if (dev->s_timeout)
579 ret = dev->s_timeout(dev, tmp);
Sean Youngc2837ad2018-02-12 08:59:00 -0500580 else
Sean Young49571332017-11-04 08:30:45 -0400581 dev->timeout = tmp;
582 }
Sean Young42e04422017-11-02 16:39:16 -0400583 break;
584
Sean Young95d15442018-03-23 16:59:52 -0400585 case LIRC_GET_REC_TIMEOUT:
Sean Young42e04422017-11-02 16:39:16 -0400586 if (!dev->timeout)
Sean Young49571332017-11-04 08:30:45 -0400587 ret = -ENOTTY;
588 else
Sean Young95d15442018-03-23 16:59:52 -0400589 val = DIV_ROUND_UP(dev->timeout, 1000);
590 break;
591
Sean Young42e04422017-11-02 16:39:16 -0400592 case LIRC_SET_REC_TIMEOUT_REPORTS:
Sean Young28492252018-03-24 08:02:48 -0400593 if (dev->driver_type != RC_DRIVER_IR_RAW)
Sean Young49571332017-11-04 08:30:45 -0400594 ret = -ENOTTY;
595 else
596 fh->send_timeout_reports = !!val;
Sean Young42e04422017-11-02 16:39:16 -0400597 break;
598
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300599 default:
Sean Young49571332017-11-04 08:30:45 -0400600 ret = -ENOTTY;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300601 }
602
Sean Young49571332017-11-04 08:30:45 -0400603 if (!ret && _IOC_DIR(cmd) & _IOC_READ)
Sean Young42e04422017-11-02 16:39:16 -0400604 ret = put_user(val, argp);
605
David Härdeman3381b772017-06-30 05:41:57 -0300606out:
Sean Young49571332017-11-04 08:30:45 -0400607 mutex_unlock(&dev->lock);
Sean Young42e04422017-11-02 16:39:16 -0400608 return ret;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300609}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300610
Linus Torvalds68c57352018-02-06 11:27:48 -0800611static __poll_t ir_lirc_poll(struct file *file, struct poll_table_struct *wait)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300612{
Sean Young7e45d662017-11-02 17:21:13 -0400613 struct lirc_fh *fh = file->private_data;
614 struct rc_dev *rcdev = fh->rc;
Linus Torvalds68c57352018-02-06 11:27:48 -0800615 __poll_t events = 0;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300616
Sean Young7e45d662017-11-02 17:21:13 -0400617 poll_wait(file, &fh->wait_poll, wait);
Jarod Wilson715d29a72010-10-18 12:02:01 -0300618
Sean Young42e04422017-11-02 16:39:16 -0400619 if (!rcdev->registered) {
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800620 events = EPOLLHUP | EPOLLERR;
Sean Young42e04422017-11-02 16:39:16 -0400621 } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
Sean Young7e45d662017-11-02 17:21:13 -0400622 if (fh->rec_mode == LIRC_MODE_SCANCODE &&
623 !kfifo_is_empty(&fh->scancodes))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800624 events = EPOLLIN | EPOLLRDNORM;
David Härdemanb15e3932017-06-25 09:32:36 -0300625
Sean Young7e45d662017-11-02 17:21:13 -0400626 if (fh->rec_mode == LIRC_MODE_MODE2 &&
627 !kfifo_is_empty(&fh->rawir))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800628 events = EPOLLIN | EPOLLRDNORM;
Dan Carpenter250f7a52010-11-17 02:20:15 -0300629 }
David Härdeman3381b772017-06-30 05:41:57 -0300630
Sean Young42e04422017-11-02 16:39:16 -0400631 return events;
632}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300633
Sean Young42e04422017-11-02 16:39:16 -0400634static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
635 size_t length)
636{
Sean Young7e45d662017-11-02 17:21:13 -0400637 struct lirc_fh *fh = file->private_data;
638 struct rc_dev *rcdev = fh->rc;
Sean Young42e04422017-11-02 16:39:16 -0400639 unsigned int copied;
640 int ret;
David Härdeman3381b772017-06-30 05:41:57 -0300641
Sean Young42e04422017-11-02 16:39:16 -0400642 if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
643 return -EINVAL;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300644
Sean Young42e04422017-11-02 16:39:16 -0400645 do {
Sean Young7e45d662017-11-02 17:21:13 -0400646 if (kfifo_is_empty(&fh->rawir)) {
Sean Young42e04422017-11-02 16:39:16 -0400647 if (file->f_flags & O_NONBLOCK)
648 return -EAGAIN;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300649
Sean Young7e45d662017-11-02 17:21:13 -0400650 ret = wait_event_interruptible(fh->wait_poll,
651 !kfifo_is_empty(&fh->rawir) ||
Sean Young42e04422017-11-02 16:39:16 -0400652 !rcdev->registered);
653 if (ret)
654 return ret;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300655 }
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300656
Sean Young42e04422017-11-02 16:39:16 -0400657 if (!rcdev->registered)
658 return -ENODEV;
Dan Carpenter250f7a52010-11-17 02:20:15 -0300659
Sean Young42e04422017-11-02 16:39:16 -0400660 ret = mutex_lock_interruptible(&rcdev->lock);
661 if (ret)
662 return ret;
Sean Young7e45d662017-11-02 17:21:13 -0400663 ret = kfifo_to_user(&fh->rawir, buffer, length, &copied);
Sean Young42e04422017-11-02 16:39:16 -0400664 mutex_unlock(&rcdev->lock);
665 if (ret)
666 return ret;
667 } while (copied == 0);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300668
Sean Young42e04422017-11-02 16:39:16 -0400669 return copied;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300670}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300671
Sean Young42e04422017-11-02 16:39:16 -0400672static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
673 size_t length)
David Härdeman615cd3f2017-06-25 09:31:40 -0300674{
Sean Young7e45d662017-11-02 17:21:13 -0400675 struct lirc_fh *fh = file->private_data;
676 struct rc_dev *rcdev = fh->rc;
Sean Young42e04422017-11-02 16:39:16 -0400677 unsigned int copied;
678 int ret;
David Härdeman615cd3f2017-06-25 09:31:40 -0300679
Sean Young42e04422017-11-02 16:39:16 -0400680 if (length < sizeof(struct lirc_scancode) ||
681 length % sizeof(struct lirc_scancode))
682 return -EINVAL;
683
684 do {
Sean Young7e45d662017-11-02 17:21:13 -0400685 if (kfifo_is_empty(&fh->scancodes)) {
Sean Young42e04422017-11-02 16:39:16 -0400686 if (file->f_flags & O_NONBLOCK)
687 return -EAGAIN;
688
Sean Young7e45d662017-11-02 17:21:13 -0400689 ret = wait_event_interruptible(fh->wait_poll,
690 !kfifo_is_empty(&fh->scancodes) ||
Sean Young42e04422017-11-02 16:39:16 -0400691 !rcdev->registered);
692 if (ret)
693 return ret;
694 }
695
696 if (!rcdev->registered)
697 return -ENODEV;
698
699 ret = mutex_lock_interruptible(&rcdev->lock);
700 if (ret)
701 return ret;
Sean Young7e45d662017-11-02 17:21:13 -0400702 ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied);
Sean Young42e04422017-11-02 16:39:16 -0400703 mutex_unlock(&rcdev->lock);
704 if (ret)
705 return ret;
706 } while (copied == 0);
707
708 return copied;
David Härdeman615cd3f2017-06-25 09:31:40 -0300709}
David Härdeman615cd3f2017-06-25 09:31:40 -0300710
Sean Young42e04422017-11-02 16:39:16 -0400711static ssize_t ir_lirc_read(struct file *file, char __user *buffer,
712 size_t length, loff_t *ppos)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300713{
Sean Young7e45d662017-11-02 17:21:13 -0400714 struct lirc_fh *fh = file->private_data;
715 struct rc_dev *rcdev = fh->rc;
David Härdeman615cd3f2017-06-25 09:31:40 -0300716
Sean Young42e04422017-11-02 16:39:16 -0400717 if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
718 return -EINVAL;
719
720 if (!rcdev->registered)
721 return -ENODEV;
722
Sean Young7e45d662017-11-02 17:21:13 -0400723 if (fh->rec_mode == LIRC_MODE_MODE2)
Sean Young42e04422017-11-02 16:39:16 -0400724 return ir_lirc_read_mode2(file, buffer, length);
725 else /* LIRC_MODE_SCANCODE */
726 return ir_lirc_read_scancode(file, buffer, length);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300727}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300728
Sean Young42e04422017-11-02 16:39:16 -0400729static const struct file_operations lirc_fops = {
730 .owner = THIS_MODULE,
731 .write = ir_lirc_transmit_ir,
732 .unlocked_ioctl = ir_lirc_ioctl,
733#ifdef CONFIG_COMPAT
734 .compat_ioctl = ir_lirc_ioctl,
735#endif
736 .read = ir_lirc_read,
737 .poll = ir_lirc_poll,
738 .open = ir_lirc_open,
739 .release = ir_lirc_close,
740 .llseek = no_llseek,
741};
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300742
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300743static void lirc_release_device(struct device *ld)
744{
Sean Younga6ddd4f2017-09-26 09:34:47 -0400745 struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300746
Sean Younga6ddd4f2017-09-26 09:34:47 -0400747 put_device(&rcdev->dev);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300748}
749
Sean Younga6ddd4f2017-09-26 09:34:47 -0400750int ir_lirc_register(struct rc_dev *dev)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300751{
Sean Younged8c34d2018-03-27 11:24:05 -0400752 const char *rx_type, *tx_type;
Sean Younga6ddd4f2017-09-26 09:34:47 -0400753 int err, minor;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300754
Sean Young7e45d662017-11-02 17:21:13 -0400755 minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL);
756 if (minor < 0)
757 return minor;
758
Sean Younga6ddd4f2017-09-26 09:34:47 -0400759 device_initialize(&dev->lirc_dev);
760 dev->lirc_dev.class = lirc_class;
Sean Younga6ddd4f2017-09-26 09:34:47 -0400761 dev->lirc_dev.parent = &dev->dev;
Sean Young7e45d662017-11-02 17:21:13 -0400762 dev->lirc_dev.release = lirc_release_device;
Sean Younga6ddd4f2017-09-26 09:34:47 -0400763 dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor);
764 dev_set_name(&dev->lirc_dev, "lirc%d", minor);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300765
Sean Young7e45d662017-11-02 17:21:13 -0400766 INIT_LIST_HEAD(&dev->lirc_fh);
767 spin_lock_init(&dev->lirc_fh_lock);
768
Sean Younga6ddd4f2017-09-26 09:34:47 -0400769 cdev_init(&dev->lirc_cdev, &lirc_fops);
770
771 err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev);
772 if (err)
773 goto out_ida;
774
775 get_device(&dev->dev);
776
Sean Younged8c34d2018-03-27 11:24:05 -0400777 switch (dev->driver_type) {
778 case RC_DRIVER_SCANCODE:
779 rx_type = "scancode";
780 break;
781 case RC_DRIVER_IR_RAW:
782 rx_type = "raw IR";
783 break;
784 default:
785 rx_type = "no";
786 break;
787 }
788
789 if (dev->tx_ir)
790 tx_type = "raw IR";
791 else
792 tx_type = "no";
793
794 dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter",
795 dev->driver_name, minor, rx_type, tx_type);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300796
797 return 0;
Sean Younga6ddd4f2017-09-26 09:34:47 -0400798
799out_ida:
800 ida_simple_remove(&lirc_ida, minor);
Sean Younga6ddd4f2017-09-26 09:34:47 -0400801 return err;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300802}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300803
Sean Younga6ddd4f2017-09-26 09:34:47 -0400804void ir_lirc_unregister(struct rc_dev *dev)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300805{
Sean Young7e45d662017-11-02 17:21:13 -0400806 unsigned long flags;
807 struct lirc_fh *fh;
808
Sean Younga6ddd4f2017-09-26 09:34:47 -0400809 dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n",
810 dev->driver_name, MINOR(dev->lirc_dev.devt));
Sean Young71695af2017-09-23 14:44:18 -0400811
Sean Young7e45d662017-11-02 17:21:13 -0400812 spin_lock_irqsave(&dev->lirc_fh_lock, flags);
813 list_for_each_entry(fh, &dev->lirc_fh, list)
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800814 wake_up_poll(&fh->wait_poll, EPOLLHUP | EPOLLERR);
Sean Young7e45d662017-11-02 17:21:13 -0400815 spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300816
Sean Younga6ddd4f2017-09-26 09:34:47 -0400817 cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev);
818 ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt));
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300819}
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300820
Sean Younga60d64b2017-09-23 10:41:13 -0400821int __init lirc_dev_init(void)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300822{
823 int retval;
824
825 lirc_class = class_create(THIS_MODULE, "lirc");
826 if (IS_ERR(lirc_class)) {
Andi Shyti3fac0312016-07-06 06:01:16 -0300827 pr_err("class_create failed\n");
Andi Shyti54fceca2016-07-06 06:01:17 -0300828 return PTR_ERR(lirc_class);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300829 }
830
Sean Younga6ddd4f2017-09-26 09:34:47 -0400831 retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX,
David Härdeman463015d2017-05-01 13:04:47 -0300832 "BaseRemoteCtl");
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300833 if (retval) {
834 class_destroy(lirc_class);
Andi Shyti3fac0312016-07-06 06:01:16 -0300835 pr_err("alloc_chrdev_region failed\n");
Andi Shyti54fceca2016-07-06 06:01:17 -0300836 return retval;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300837 }
838
Sean Young5817b3d2018-02-13 06:11:35 -0500839 pr_debug("IR Remote Control driver registered, major %d\n",
840 MAJOR(lirc_base_dev));
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300841
Andi Shyti54fceca2016-07-06 06:01:17 -0300842 return 0;
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300843}
844
Sean Younga60d64b2017-09-23 10:41:13 -0400845void __exit lirc_dev_exit(void)
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300846{
847 class_destroy(lirc_class);
Sean Younga6ddd4f2017-09-26 09:34:47 -0400848 unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
Jarod Wilson4a62a5a2010-07-03 01:06:57 -0300849}
850
Sean Youngf4364dc2018-05-27 12:24:09 +0100851struct rc_dev *rc_dev_get_from_fd(int fd)
852{
853 struct fd f = fdget(fd);
854 struct lirc_fh *fh;
855 struct rc_dev *dev;
856
857 if (!f.file)
858 return ERR_PTR(-EBADF);
859
860 if (f.file->f_op != &lirc_fops) {
861 fdput(f);
862 return ERR_PTR(-EINVAL);
863 }
864
865 fh = f.file->private_data;
866 dev = fh->rc;
867
868 get_device(&dev->dev);
869 fdput(f);
870
871 return dev;
872}
873
Sean Young04d0e8d2017-12-28 14:45:12 -0500874MODULE_ALIAS("lirc_dev");