Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
4a62a5ab JW |
2 | /* |
3 | * LIRC base driver | |
4 | * | |
5 | * by Artur Lipowski <alipowski@interia.pl> | |
4a62a5ab JW |
6 | */ |
7 | ||
3fac0314 AS |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | ||
4a62a5ab | 10 | #include <linux/module.h> |
4a62a5ab | 11 | #include <linux/mutex.h> |
4a62a5ab | 12 | #include <linux/device.h> |
f4364dcf | 13 | #include <linux/file.h> |
46c8f477 | 14 | #include <linux/idr.h> |
a6ddd4fe | 15 | #include <linux/poll.h> |
42e0442f SY |
16 | #include <linux/sched.h> |
17 | #include <linux/wait.h> | |
4a62a5ab | 18 | |
a60d64b1 | 19 | #include "rc-core-priv.h" |
aefb5e34 | 20 | #include <uapi/linux/lirc.h> |
4a62a5ab | 21 | |
5c4c8b4a | 22 | #define LIRCBUF_SIZE 1024 |
4a62a5ab JW |
23 | |
24 | static dev_t lirc_base_dev; | |
25 | ||
46c8f477 | 26 | /* Used to keep track of allocated lirc devices */ |
46c8f477 | 27 | static DEFINE_IDA(lirc_ida); |
4a62a5ab JW |
28 | |
29 | /* Only used for sysfs but defined to void otherwise */ | |
30 | static struct class *lirc_class; | |
31 | ||
42e0442f SY |
32 | /** |
33 | * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace | |
34 | * | |
35 | * @dev: the struct rc_dev descriptor of the device | |
36 | * @ev: the struct ir_raw_event descriptor of the pulse/space | |
37 | */ | |
38 | void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) | |
4a62a5ab | 39 | { |
7e45d660 SY |
40 | unsigned long flags; |
41 | struct lirc_fh *fh; | |
42e0442f | 42 | int sample; |
74c839b2 | 43 | |
42e0442f SY |
44 | /* Packet start */ |
45 | if (ev.reset) { | |
46 | /* | |
47 | * Userspace expects a long space event before the start of | |
48 | * the signal to use as a sync. This may be done with repeat | |
49 | * packets and normal samples. But if a reset has been sent | |
50 | * then we assume that a long time has passed, so we send a | |
51 | * space with the maximum time value. | |
52 | */ | |
53 | sample = LIRC_SPACE(LIRC_VALUE_MASK); | |
1f17f684 | 54 | dev_dbg(&dev->dev, "delivering reset sync space to lirc_dev\n"); |
4a62a5ab | 55 | |
42e0442f SY |
56 | /* Carrier reports */ |
57 | } else if (ev.carrier_report) { | |
58 | sample = LIRC_FREQUENCY(ev.carrier); | |
1f17f684 | 59 | dev_dbg(&dev->dev, "carrier report (freq: %d)\n", sample); |
6fa99e1a | 60 | |
42e0442f SY |
61 | /* Packet end */ |
62 | } else if (ev.timeout) { | |
63 | if (dev->gap) | |
64 | return; | |
6fa99e1a | 65 | |
42e0442f SY |
66 | dev->gap_start = ktime_get(); |
67 | dev->gap = true; | |
68 | dev->gap_duration = ev.duration; | |
0f7c4063 | 69 | |
42e0442f | 70 | sample = LIRC_TIMEOUT(ev.duration / 1000); |
1f17f684 | 71 | dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample); |
6fa99e1a | 72 | |
42e0442f SY |
73 | /* Normal sample */ |
74 | } else { | |
75 | if (dev->gap) { | |
76 | dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(), | |
77 | dev->gap_start)); | |
78 | ||
79 | /* Convert to ms and cap by LIRC_VALUE_MASK */ | |
80 | do_div(dev->gap_duration, 1000); | |
81 | dev->gap_duration = min_t(u64, dev->gap_duration, | |
82 | LIRC_VALUE_MASK); | |
83 | ||
7e45d660 SY |
84 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
85 | list_for_each_entry(fh, &dev->lirc_fh, list) | |
86 | kfifo_put(&fh->rawir, | |
87 | LIRC_SPACE(dev->gap_duration)); | |
88 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); | |
42e0442f SY |
89 | dev->gap = false; |
90 | } | |
6fa99e1a | 91 | |
42e0442f SY |
92 | sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) : |
93 | LIRC_SPACE(ev.duration / 1000); | |
1f17f684 SY |
94 | dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n", |
95 | TO_US(ev.duration), TO_STR(ev.pulse)); | |
b15e3937 DH |
96 | } |
97 | ||
f4364dcf SY |
98 | /* |
99 | * bpf does not care about the gap generated above; that exists | |
100 | * for backwards compatibility | |
101 | */ | |
102 | lirc_bpf_run(dev, sample); | |
103 | ||
7e45d660 SY |
104 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
105 | list_for_each_entry(fh, &dev->lirc_fh, list) { | |
106 | if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports) | |
107 | continue; | |
108 | if (kfifo_put(&fh->rawir, sample)) | |
a9a08845 | 109 | wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM); |
7e45d660 SY |
110 | } |
111 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); | |
6ecccc37 | 112 | } |
6ecccc37 | 113 | |
42e0442f SY |
114 | /** |
115 | * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to | |
7e45d660 | 116 | * userspace. This can be called in atomic context. |
42e0442f SY |
117 | * @dev: the struct rc_dev descriptor of the device |
118 | * @lsc: the struct lirc_scancode describing the decoded scancode | |
119 | */ | |
120 | void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc) | |
6fa99e1a | 121 | { |
7e45d660 SY |
122 | unsigned long flags; |
123 | struct lirc_fh *fh; | |
4a62a5ab | 124 | |
42e0442f | 125 | lsc->timestamp = ktime_get_ns(); |
4a62a5ab | 126 | |
7e45d660 SY |
127 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
128 | list_for_each_entry(fh, &dev->lirc_fh, list) { | |
129 | if (kfifo_put(&fh->scancodes, *lsc)) | |
a9a08845 | 130 | wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM); |
715d29a7 | 131 | } |
7e45d660 | 132 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); |
42e0442f SY |
133 | } |
134 | EXPORT_SYMBOL_GPL(ir_lirc_scancode_event); | |
715d29a7 | 135 | |
42e0442f SY |
136 | static int ir_lirc_open(struct inode *inode, struct file *file) |
137 | { | |
138 | struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev, | |
139 | lirc_cdev); | |
7e45d660 SY |
140 | struct lirc_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); |
141 | unsigned long flags; | |
42e0442f | 142 | int retval; |
712551f0 | 143 | |
7e45d660 SY |
144 | if (!fh) |
145 | return -ENOMEM; | |
b145ef94 | 146 | |
7e45d660 | 147 | get_device(&dev->dev); |
b145ef94 | 148 | |
42e0442f SY |
149 | if (!dev->registered) { |
150 | retval = -ENODEV; | |
7e45d660 | 151 | goto out_fh; |
4a62a5ab JW |
152 | } |
153 | ||
7e45d660 SY |
154 | if (dev->driver_type == RC_DRIVER_IR_RAW) { |
155 | if (kfifo_alloc(&fh->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) { | |
156 | retval = -ENOMEM; | |
157 | goto out_fh; | |
158 | } | |
4a62a5ab JW |
159 | } |
160 | ||
7e45d660 SY |
161 | if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { |
162 | if (kfifo_alloc(&fh->scancodes, 32, GFP_KERNEL)) { | |
163 | retval = -ENOMEM; | |
164 | goto out_rawir; | |
165 | } | |
3381b779 DH |
166 | } |
167 | ||
7e45d660 SY |
168 | fh->send_mode = LIRC_MODE_PULSE; |
169 | fh->rc = dev; | |
170 | fh->send_timeout_reports = true; | |
712551f0 | 171 | |
7e45d660 SY |
172 | if (dev->driver_type == RC_DRIVER_SCANCODE) |
173 | fh->rec_mode = LIRC_MODE_SCANCODE; | |
174 | else | |
175 | fh->rec_mode = LIRC_MODE_MODE2; | |
4a62a5ab | 176 | |
7e45d660 SY |
177 | retval = rc_open(dev); |
178 | if (retval) | |
179 | goto out_kfifo; | |
74c839b2 | 180 | |
7e45d660 | 181 | init_waitqueue_head(&fh->wait_poll); |
4a62a5ab | 182 | |
7e45d660 SY |
183 | file->private_data = fh; |
184 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); | |
185 | list_add(&fh->list, &dev->lirc_fh); | |
186 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); | |
a607f51e | 187 | |
c5bf68fe | 188 | stream_open(inode, file); |
56481f00 | 189 | |
c3c6dd75 | 190 | return 0; |
7e45d660 SY |
191 | out_kfifo: |
192 | if (dev->driver_type != RC_DRIVER_IR_RAW_TX) | |
193 | kfifo_free(&fh->scancodes); | |
194 | out_rawir: | |
195 | if (dev->driver_type == RC_DRIVER_IR_RAW) | |
196 | kfifo_free(&fh->rawir); | |
197 | out_fh: | |
198 | kfree(fh); | |
199 | put_device(&dev->dev); | |
42e0442f | 200 | |
42e0442f | 201 | return retval; |
4a62a5ab | 202 | } |
4a62a5ab | 203 | |
42e0442f | 204 | static int ir_lirc_close(struct inode *inode, struct file *file) |
4a62a5ab | 205 | { |
7e45d660 SY |
206 | struct lirc_fh *fh = file->private_data; |
207 | struct rc_dev *dev = fh->rc; | |
208 | unsigned long flags; | |
4a62a5ab | 209 | |
7e45d660 SY |
210 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
211 | list_del(&fh->list); | |
212 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); | |
3381b779 | 213 | |
7e45d660 SY |
214 | if (dev->driver_type == RC_DRIVER_IR_RAW) |
215 | kfifo_free(&fh->rawir); | |
216 | if (dev->driver_type != RC_DRIVER_IR_RAW_TX) | |
217 | kfifo_free(&fh->scancodes); | |
218 | kfree(fh); | |
8e5fa4c6 | 219 | |
42e0442f | 220 | rc_close(dev); |
7e45d660 | 221 | put_device(&dev->dev); |
4a62a5ab | 222 | |
42e0442f | 223 | return 0; |
4a62a5ab | 224 | } |
4a62a5ab | 225 | |
42e0442f SY |
226 | static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, |
227 | size_t n, loff_t *ppos) | |
4a62a5ab | 228 | { |
7e45d660 SY |
229 | struct lirc_fh *fh = file->private_data; |
230 | struct rc_dev *dev = fh->rc; | |
a74b2bff | 231 | unsigned int *txbuf; |
42e0442f | 232 | struct ir_raw_event *raw = NULL; |
4957133f | 233 | ssize_t ret; |
42e0442f SY |
234 | size_t count; |
235 | ktime_t start; | |
236 | s64 towait; | |
237 | unsigned int duration = 0; /* signal duration in us */ | |
238 | int i; | |
239 | ||
4957133f SY |
240 | ret = mutex_lock_interruptible(&dev->lock); |
241 | if (ret) | |
242 | return ret; | |
4a62a5ab | 243 | |
4957133f SY |
244 | if (!dev->registered) { |
245 | ret = -ENODEV; | |
a74b2bff | 246 | goto out_unlock; |
4957133f | 247 | } |
4a62a5ab | 248 | |
42e0442f SY |
249 | if (!dev->tx_ir) { |
250 | ret = -EINVAL; | |
a74b2bff | 251 | goto out_unlock; |
3381b779 DH |
252 | } |
253 | ||
7e45d660 | 254 | if (fh->send_mode == LIRC_MODE_SCANCODE) { |
42e0442f | 255 | struct lirc_scancode scan; |
4a62a5ab | 256 | |
4957133f SY |
257 | if (n != sizeof(scan)) { |
258 | ret = -EINVAL; | |
a74b2bff | 259 | goto out_unlock; |
4957133f | 260 | } |
ca7a722d | 261 | |
4957133f SY |
262 | if (copy_from_user(&scan, buf, sizeof(scan))) { |
263 | ret = -EFAULT; | |
a74b2bff | 264 | goto out_unlock; |
4957133f | 265 | } |
2c5a1f44 | 266 | |
4957133f SY |
267 | if (scan.flags || scan.keycode || scan.timestamp) { |
268 | ret = -EINVAL; | |
a74b2bff | 269 | goto out_unlock; |
4957133f | 270 | } |
4a62a5ab | 271 | |
e6c6d7d4 | 272 | /* We only have encoders for 32-bit protocols. */ |
42e0442f | 273 | if (scan.scancode > U32_MAX || |
4957133f SY |
274 | !rc_validate_scancode(scan.rc_proto, scan.scancode)) { |
275 | ret = -EINVAL; | |
a74b2bff | 276 | goto out_unlock; |
4957133f | 277 | } |
d9d2e9d5 | 278 | |
42e0442f | 279 | raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL); |
4957133f SY |
280 | if (!raw) { |
281 | ret = -ENOMEM; | |
a74b2bff | 282 | goto out_unlock; |
4957133f | 283 | } |
3381b779 | 284 | |
42e0442f SY |
285 | ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode, |
286 | raw, LIRCBUF_SIZE); | |
287 | if (ret < 0) | |
8d25e15d | 288 | goto out_kfree_raw; |
4a62a5ab | 289 | |
42e0442f | 290 | count = ret; |
4a62a5ab | 291 | |
42e0442f SY |
292 | txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL); |
293 | if (!txbuf) { | |
294 | ret = -ENOMEM; | |
8d25e15d | 295 | goto out_kfree_raw; |
42e0442f | 296 | } |
4a62a5ab | 297 | |
42e0442f SY |
298 | for (i = 0; i < count; i++) |
299 | /* Convert from NS to US */ | |
300 | txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000); | |
3381b779 | 301 | |
42e0442f SY |
302 | if (dev->s_tx_carrier) { |
303 | int carrier = ir_raw_encode_carrier(scan.rc_proto); | |
4a62a5ab | 304 | |
42e0442f SY |
305 | if (carrier > 0) |
306 | dev->s_tx_carrier(dev, carrier); | |
307 | } | |
308 | } else { | |
4957133f SY |
309 | if (n < sizeof(unsigned int) || n % sizeof(unsigned int)) { |
310 | ret = -EINVAL; | |
a74b2bff | 311 | goto out_unlock; |
4957133f | 312 | } |
4a62a5ab | 313 | |
42e0442f | 314 | count = n / sizeof(unsigned int); |
4957133f SY |
315 | if (count > LIRCBUF_SIZE || count % 2 == 0) { |
316 | ret = -EINVAL; | |
a74b2bff | 317 | goto out_unlock; |
4957133f | 318 | } |
4a62a5ab | 319 | |
42e0442f | 320 | txbuf = memdup_user(buf, n); |
4957133f SY |
321 | if (IS_ERR(txbuf)) { |
322 | ret = PTR_ERR(txbuf); | |
a74b2bff | 323 | goto out_unlock; |
4957133f | 324 | } |
42e0442f | 325 | } |
4a62a5ab | 326 | |
42e0442f SY |
327 | for (i = 0; i < count; i++) { |
328 | if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) { | |
329 | ret = -EINVAL; | |
a74b2bff | 330 | goto out_kfree; |
42e0442f | 331 | } |
4a62a5ab | 332 | |
42e0442f | 333 | duration += txbuf[i]; |
b15e3937 | 334 | } |
4a62a5ab | 335 | |
29422737 SY |
336 | start = ktime_get(); |
337 | ||
42e0442f SY |
338 | ret = dev->tx_ir(dev, txbuf, count); |
339 | if (ret < 0) | |
a74b2bff | 340 | goto out_kfree; |
42e0442f | 341 | |
f81a8158 SY |
342 | kfree(txbuf); |
343 | kfree(raw); | |
344 | mutex_unlock(&dev->lock); | |
345 | ||
dde7edff SY |
346 | /* |
347 | * The lircd gap calculation expects the write function to | |
348 | * wait for the actual IR signal to be transmitted before | |
349 | * returning. | |
350 | */ | |
351 | towait = ktime_us_delta(ktime_add_us(start, duration), | |
352 | ktime_get()); | |
353 | if (towait > 0) { | |
354 | set_current_state(TASK_INTERRUPTIBLE); | |
355 | schedule_timeout(usecs_to_jiffies(towait)); | |
42e0442f | 356 | } |
4a62a5ab | 357 | |
f81a8158 | 358 | return n; |
a74b2bff | 359 | out_kfree: |
42e0442f | 360 | kfree(txbuf); |
8d25e15d | 361 | out_kfree_raw: |
42e0442f | 362 | kfree(raw); |
a74b2bff SY |
363 | out_unlock: |
364 | mutex_unlock(&dev->lock); | |
4a62a5ab JW |
365 | return ret; |
366 | } | |
4a62a5ab | 367 | |
7e45d660 | 368 | static long ir_lirc_ioctl(struct file *file, unsigned int cmd, |
42e0442f | 369 | unsigned long arg) |
4a62a5ab | 370 | { |
7e45d660 SY |
371 | struct lirc_fh *fh = file->private_data; |
372 | struct rc_dev *dev = fh->rc; | |
42e0442f | 373 | u32 __user *argp = (u32 __user *)(arg); |
4957133f SY |
374 | u32 val = 0; |
375 | int ret; | |
42e0442f SY |
376 | |
377 | if (_IOC_DIR(cmd) & _IOC_WRITE) { | |
378 | ret = get_user(val, argp); | |
379 | if (ret) | |
380 | return ret; | |
381 | } | |
4a62a5ab | 382 | |
4957133f SY |
383 | ret = mutex_lock_interruptible(&dev->lock); |
384 | if (ret) | |
385 | return ret; | |
3381b779 | 386 | |
4957133f SY |
387 | if (!dev->registered) { |
388 | ret = -ENODEV; | |
3381b779 | 389 | goto out; |
4a62a5ab JW |
390 | } |
391 | ||
4a62a5ab JW |
392 | switch (cmd) { |
393 | case LIRC_GET_FEATURES: | |
42e0442f SY |
394 | if (dev->driver_type == RC_DRIVER_SCANCODE) |
395 | val |= LIRC_CAN_REC_SCANCODE; | |
396 | ||
397 | if (dev->driver_type == RC_DRIVER_IR_RAW) { | |
02d742f4 | 398 | val |= LIRC_CAN_REC_MODE2; |
42e0442f SY |
399 | if (dev->rx_resolution) |
400 | val |= LIRC_CAN_GET_REC_RESOLUTION; | |
401 | } | |
402 | ||
403 | if (dev->tx_ir) { | |
02d742f4 | 404 | val |= LIRC_CAN_SEND_PULSE; |
42e0442f SY |
405 | if (dev->s_tx_mask) |
406 | val |= LIRC_CAN_SET_TRANSMITTER_MASK; | |
407 | if (dev->s_tx_carrier) | |
408 | val |= LIRC_CAN_SET_SEND_CARRIER; | |
409 | if (dev->s_tx_duty_cycle) | |
410 | val |= LIRC_CAN_SET_SEND_DUTY_CYCLE; | |
4a62a5ab JW |
411 | } |
412 | ||
42e0442f SY |
413 | if (dev->s_rx_carrier_range) |
414 | val |= LIRC_CAN_SET_REC_CARRIER | | |
415 | LIRC_CAN_SET_REC_CARRIER_RANGE; | |
416 | ||
417 | if (dev->s_learning_mode) | |
418 | val |= LIRC_CAN_USE_WIDEBAND_RECEIVER; | |
419 | ||
420 | if (dev->s_carrier_report) | |
421 | val |= LIRC_CAN_MEASURE_CARRIER; | |
422 | ||
423 | if (dev->max_timeout) | |
424 | val |= LIRC_CAN_SET_REC_TIMEOUT; | |
425 | ||
4a62a5ab | 426 | break; |
42e0442f SY |
427 | |
428 | /* mode support */ | |
429 | case LIRC_GET_REC_MODE: | |
430 | if (dev->driver_type == RC_DRIVER_IR_RAW_TX) | |
4957133f SY |
431 | ret = -ENOTTY; |
432 | else | |
433 | val = fh->rec_mode; | |
42e0442f SY |
434 | break; |
435 | ||
4a62a5ab | 436 | case LIRC_SET_REC_MODE: |
42e0442f SY |
437 | switch (dev->driver_type) { |
438 | case RC_DRIVER_IR_RAW_TX: | |
4957133f SY |
439 | ret = -ENOTTY; |
440 | break; | |
42e0442f SY |
441 | case RC_DRIVER_SCANCODE: |
442 | if (val != LIRC_MODE_SCANCODE) | |
4957133f | 443 | ret = -EINVAL; |
42e0442f SY |
444 | break; |
445 | case RC_DRIVER_IR_RAW: | |
446 | if (!(val == LIRC_MODE_MODE2 || | |
447 | val == LIRC_MODE_SCANCODE)) | |
4957133f | 448 | ret = -EINVAL; |
4a62a5ab JW |
449 | break; |
450 | } | |
451 | ||
4957133f SY |
452 | if (!ret) |
453 | fh->rec_mode = val; | |
454 | break; | |
42e0442f SY |
455 | |
456 | case LIRC_GET_SEND_MODE: | |
457 | if (!dev->tx_ir) | |
4957133f SY |
458 | ret = -ENOTTY; |
459 | else | |
460 | val = fh->send_mode; | |
42e0442f SY |
461 | break; |
462 | ||
463 | case LIRC_SET_SEND_MODE: | |
464 | if (!dev->tx_ir) | |
4957133f SY |
465 | ret = -ENOTTY; |
466 | else if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE)) | |
467 | ret = -EINVAL; | |
468 | else | |
469 | fh->send_mode = val; | |
470 | break; | |
42e0442f SY |
471 | |
472 | /* TX settings */ | |
473 | case LIRC_SET_TRANSMITTER_MASK: | |
474 | if (!dev->s_tx_mask) | |
4957133f SY |
475 | ret = -ENOTTY; |
476 | else | |
477 | ret = dev->s_tx_mask(dev, val); | |
478 | break; | |
42e0442f SY |
479 | |
480 | case LIRC_SET_SEND_CARRIER: | |
481 | if (!dev->s_tx_carrier) | |
4957133f SY |
482 | ret = -ENOTTY; |
483 | else | |
484 | ret = dev->s_tx_carrier(dev, val); | |
485 | break; | |
42e0442f SY |
486 | |
487 | case LIRC_SET_SEND_DUTY_CYCLE: | |
488 | if (!dev->s_tx_duty_cycle) | |
4957133f SY |
489 | ret = -ENOTTY; |
490 | else if (val <= 0 || val >= 100) | |
491 | ret = -EINVAL; | |
492 | else | |
493 | ret = dev->s_tx_duty_cycle(dev, val); | |
494 | break; | |
42e0442f SY |
495 | |
496 | /* RX settings */ | |
497 | case LIRC_SET_REC_CARRIER: | |
498 | if (!dev->s_rx_carrier_range) | |
4957133f SY |
499 | ret = -ENOTTY; |
500 | else if (val <= 0) | |
501 | ret = -EINVAL; | |
502 | else | |
503 | ret = dev->s_rx_carrier_range(dev, fh->carrier_low, | |
504 | val); | |
505 | break; | |
42e0442f SY |
506 | |
507 | case LIRC_SET_REC_CARRIER_RANGE: | |
508 | if (!dev->s_rx_carrier_range) | |
4957133f SY |
509 | ret = -ENOTTY; |
510 | else if (val <= 0) | |
511 | ret = -EINVAL; | |
512 | else | |
513 | fh->carrier_low = val; | |
514 | break; | |
42e0442f SY |
515 | |
516 | case LIRC_GET_REC_RESOLUTION: | |
517 | if (!dev->rx_resolution) | |
4957133f SY |
518 | ret = -ENOTTY; |
519 | else | |
520 | val = dev->rx_resolution / 1000; | |
4a62a5ab | 521 | break; |
42e0442f SY |
522 | |
523 | case LIRC_SET_WIDEBAND_RECEIVER: | |
524 | if (!dev->s_learning_mode) | |
4957133f SY |
525 | ret = -ENOTTY; |
526 | else | |
527 | ret = dev->s_learning_mode(dev, !!val); | |
4a62a5ab | 528 | break; |
42e0442f SY |
529 | |
530 | case LIRC_SET_MEASURE_CARRIER_MODE: | |
531 | if (!dev->s_carrier_report) | |
4957133f SY |
532 | ret = -ENOTTY; |
533 | else | |
534 | ret = dev->s_carrier_report(dev, !!val); | |
535 | break; | |
42e0442f SY |
536 | |
537 | /* Generic timeout support */ | |
538 | case LIRC_GET_MIN_TIMEOUT: | |
539 | if (!dev->max_timeout) | |
4957133f SY |
540 | ret = -ENOTTY; |
541 | else | |
542 | val = DIV_ROUND_UP(dev->min_timeout, 1000); | |
42e0442f SY |
543 | break; |
544 | ||
545 | case LIRC_GET_MAX_TIMEOUT: | |
546 | if (!dev->max_timeout) | |
4957133f SY |
547 | ret = -ENOTTY; |
548 | else | |
549 | val = dev->max_timeout / 1000; | |
42e0442f SY |
550 | break; |
551 | ||
552 | case LIRC_SET_REC_TIMEOUT: | |
4957133f SY |
553 | if (!dev->max_timeout) { |
554 | ret = -ENOTTY; | |
555 | } else if (val > U32_MAX / 1000) { | |
556 | /* Check for multiply overflow */ | |
557 | ret = -EINVAL; | |
558 | } else { | |
559 | u32 tmp = val * 1000; | |
560 | ||
561 | if (tmp < dev->min_timeout || tmp > dev->max_timeout) | |
562 | ret = -EINVAL; | |
563 | else if (dev->s_timeout) | |
564 | ret = dev->s_timeout(dev, tmp); | |
c2837ad0 | 565 | else |
4957133f SY |
566 | dev->timeout = tmp; |
567 | } | |
42e0442f SY |
568 | break; |
569 | ||
95d1544e | 570 | case LIRC_GET_REC_TIMEOUT: |
42e0442f | 571 | if (!dev->timeout) |
4957133f | 572 | ret = -ENOTTY; |
95d1544e SY |
573 | else |
574 | val = DIV_ROUND_UP(dev->timeout, 1000); | |
575 | break; | |
576 | ||
42e0442f | 577 | case LIRC_SET_REC_TIMEOUT_REPORTS: |
28492256 | 578 | if (dev->driver_type != RC_DRIVER_IR_RAW) |
4957133f SY |
579 | ret = -ENOTTY; |
580 | else | |
581 | fh->send_timeout_reports = !!val; | |
42e0442f SY |
582 | break; |
583 | ||
4a62a5ab | 584 | default: |
4957133f | 585 | ret = -ENOTTY; |
4a62a5ab JW |
586 | } |
587 | ||
4957133f | 588 | if (!ret && _IOC_DIR(cmd) & _IOC_READ) |
42e0442f SY |
589 | ret = put_user(val, argp); |
590 | ||
3381b779 | 591 | out: |
4957133f | 592 | mutex_unlock(&dev->lock); |
42e0442f | 593 | return ret; |
4a62a5ab | 594 | } |
4a62a5ab | 595 | |
68c5735e | 596 | static __poll_t ir_lirc_poll(struct file *file, struct poll_table_struct *wait) |
4a62a5ab | 597 | { |
7e45d660 SY |
598 | struct lirc_fh *fh = file->private_data; |
599 | struct rc_dev *rcdev = fh->rc; | |
68c5735e | 600 | __poll_t events = 0; |
42e0442f | 601 | |
7e45d660 | 602 | poll_wait(file, &fh->wait_poll, wait); |
42e0442f SY |
603 | |
604 | if (!rcdev->registered) { | |
a9a08845 | 605 | events = EPOLLHUP | EPOLLERR; |
42e0442f | 606 | } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) { |
7e45d660 SY |
607 | if (fh->rec_mode == LIRC_MODE_SCANCODE && |
608 | !kfifo_is_empty(&fh->scancodes)) | |
a9a08845 | 609 | events = EPOLLIN | EPOLLRDNORM; |
42e0442f | 610 | |
7e45d660 SY |
611 | if (fh->rec_mode == LIRC_MODE_MODE2 && |
612 | !kfifo_is_empty(&fh->rawir)) | |
a9a08845 | 613 | events = EPOLLIN | EPOLLRDNORM; |
42e0442f | 614 | } |
4a62a5ab | 615 | |
42e0442f SY |
616 | return events; |
617 | } | |
715d29a7 | 618 | |
42e0442f SY |
619 | static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, |
620 | size_t length) | |
621 | { | |
7e45d660 SY |
622 | struct lirc_fh *fh = file->private_data; |
623 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
624 | unsigned int copied; |
625 | int ret; | |
b15e3937 | 626 | |
42e0442f SY |
627 | if (length < sizeof(unsigned int) || length % sizeof(unsigned int)) |
628 | return -EINVAL; | |
3381b779 | 629 | |
42e0442f | 630 | do { |
7e45d660 | 631 | if (kfifo_is_empty(&fh->rawir)) { |
42e0442f SY |
632 | if (file->f_flags & O_NONBLOCK) |
633 | return -EAGAIN; | |
4a62a5ab | 634 | |
7e45d660 SY |
635 | ret = wait_event_interruptible(fh->wait_poll, |
636 | !kfifo_is_empty(&fh->rawir) || | |
42e0442f SY |
637 | !rcdev->registered); |
638 | if (ret) | |
639 | return ret; | |
640 | } | |
3381b779 | 641 | |
42e0442f SY |
642 | if (!rcdev->registered) |
643 | return -ENODEV; | |
4a62a5ab | 644 | |
42e0442f SY |
645 | ret = mutex_lock_interruptible(&rcdev->lock); |
646 | if (ret) | |
647 | return ret; | |
7e45d660 | 648 | ret = kfifo_to_user(&fh->rawir, buffer, length, &copied); |
42e0442f SY |
649 | mutex_unlock(&rcdev->lock); |
650 | if (ret) | |
651 | return ret; | |
652 | } while (copied == 0); | |
4a62a5ab | 653 | |
42e0442f SY |
654 | return copied; |
655 | } | |
656 | ||
657 | static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, | |
658 | size_t length) | |
659 | { | |
7e45d660 SY |
660 | struct lirc_fh *fh = file->private_data; |
661 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
662 | unsigned int copied; |
663 | int ret; | |
664 | ||
665 | if (length < sizeof(struct lirc_scancode) || | |
666 | length % sizeof(struct lirc_scancode)) | |
667 | return -EINVAL; | |
668 | ||
669 | do { | |
7e45d660 | 670 | if (kfifo_is_empty(&fh->scancodes)) { |
42e0442f SY |
671 | if (file->f_flags & O_NONBLOCK) |
672 | return -EAGAIN; | |
673 | ||
7e45d660 SY |
674 | ret = wait_event_interruptible(fh->wait_poll, |
675 | !kfifo_is_empty(&fh->scancodes) || | |
42e0442f SY |
676 | !rcdev->registered); |
677 | if (ret) | |
678 | return ret; | |
4a62a5ab | 679 | } |
4a62a5ab | 680 | |
42e0442f SY |
681 | if (!rcdev->registered) |
682 | return -ENODEV; | |
683 | ||
684 | ret = mutex_lock_interruptible(&rcdev->lock); | |
685 | if (ret) | |
686 | return ret; | |
7e45d660 | 687 | ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied); |
42e0442f SY |
688 | mutex_unlock(&rcdev->lock); |
689 | if (ret) | |
690 | return ret; | |
691 | } while (copied == 0); | |
692 | ||
693 | return copied; | |
694 | } | |
250f7a5f | 695 | |
42e0442f SY |
696 | static ssize_t ir_lirc_read(struct file *file, char __user *buffer, |
697 | size_t length, loff_t *ppos) | |
698 | { | |
7e45d660 SY |
699 | struct lirc_fh *fh = file->private_data; |
700 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
701 | |
702 | if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX) | |
703 | return -EINVAL; | |
4a62a5ab | 704 | |
42e0442f SY |
705 | if (!rcdev->registered) |
706 | return -ENODEV; | |
4a62a5ab | 707 | |
7e45d660 | 708 | if (fh->rec_mode == LIRC_MODE_MODE2) |
42e0442f SY |
709 | return ir_lirc_read_mode2(file, buffer, length); |
710 | else /* LIRC_MODE_SCANCODE */ | |
711 | return ir_lirc_read_scancode(file, buffer, length); | |
4a62a5ab | 712 | } |
4a62a5ab | 713 | |
42e0442f SY |
714 | static const struct file_operations lirc_fops = { |
715 | .owner = THIS_MODULE, | |
716 | .write = ir_lirc_transmit_ir, | |
717 | .unlocked_ioctl = ir_lirc_ioctl, | |
1832f2d8 | 718 | .compat_ioctl = compat_ptr_ioctl, |
42e0442f SY |
719 | .read = ir_lirc_read, |
720 | .poll = ir_lirc_poll, | |
721 | .open = ir_lirc_open, | |
722 | .release = ir_lirc_close, | |
723 | .llseek = no_llseek, | |
724 | }; | |
725 | ||
b15e3937 | 726 | static void lirc_release_device(struct device *ld) |
615cd3fe | 727 | { |
a6ddd4fe | 728 | struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev); |
615cd3fe | 729 | |
a6ddd4fe | 730 | put_device(&rcdev->dev); |
615cd3fe | 731 | } |
615cd3fe | 732 | |
a6ddd4fe | 733 | int ir_lirc_register(struct rc_dev *dev) |
4a62a5ab | 734 | { |
ed8c34d7 | 735 | const char *rx_type, *tx_type; |
a6ddd4fe | 736 | int err, minor; |
4a62a5ab | 737 | |
a6ddd4fe | 738 | minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); |
7e45d660 SY |
739 | if (minor < 0) |
740 | return minor; | |
3381b779 | 741 | |
7e45d660 SY |
742 | device_initialize(&dev->lirc_dev); |
743 | dev->lirc_dev.class = lirc_class; | |
a6ddd4fe | 744 | dev->lirc_dev.parent = &dev->dev; |
7e45d660 | 745 | dev->lirc_dev.release = lirc_release_device; |
a6ddd4fe SY |
746 | dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor); |
747 | dev_set_name(&dev->lirc_dev, "lirc%d", minor); | |
615cd3fe | 748 | |
7e45d660 SY |
749 | INIT_LIST_HEAD(&dev->lirc_fh); |
750 | spin_lock_init(&dev->lirc_fh_lock); | |
751 | ||
a6ddd4fe | 752 | cdev_init(&dev->lirc_cdev, &lirc_fops); |
4a62a5ab | 753 | |
a6ddd4fe SY |
754 | err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev); |
755 | if (err) | |
756 | goto out_ida; | |
74c839b2 | 757 | |
a6ddd4fe | 758 | get_device(&dev->dev); |
4a62a5ab | 759 | |
ed8c34d7 SY |
760 | switch (dev->driver_type) { |
761 | case RC_DRIVER_SCANCODE: | |
762 | rx_type = "scancode"; | |
763 | break; | |
764 | case RC_DRIVER_IR_RAW: | |
765 | rx_type = "raw IR"; | |
766 | break; | |
767 | default: | |
768 | rx_type = "no"; | |
769 | break; | |
770 | } | |
771 | ||
772 | if (dev->tx_ir) | |
773 | tx_type = "raw IR"; | |
774 | else | |
775 | tx_type = "no"; | |
776 | ||
777 | dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter", | |
778 | dev->driver_name, minor, rx_type, tx_type); | |
56481f00 | 779 | |
c3c6dd75 | 780 | return 0; |
a6ddd4fe SY |
781 | |
782 | out_ida: | |
783 | ida_simple_remove(&lirc_ida, minor); | |
a6ddd4fe | 784 | return err; |
4a62a5ab | 785 | } |
4a62a5ab | 786 | |
a6ddd4fe | 787 | void ir_lirc_unregister(struct rc_dev *dev) |
4a62a5ab | 788 | { |
7e45d660 SY |
789 | unsigned long flags; |
790 | struct lirc_fh *fh; | |
791 | ||
a6ddd4fe SY |
792 | dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n", |
793 | dev->driver_name, MINOR(dev->lirc_dev.devt)); | |
4a62a5ab | 794 | |
7e45d660 SY |
795 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
796 | list_for_each_entry(fh, &dev->lirc_fh, list) | |
a9a08845 | 797 | wake_up_poll(&fh->wait_poll, EPOLLHUP | EPOLLERR); |
7e45d660 | 798 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); |
4a62a5ab | 799 | |
a6ddd4fe SY |
800 | cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev); |
801 | ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt)); | |
4a62a5ab | 802 | } |
4a62a5ab | 803 | |
a60d64b1 | 804 | int __init lirc_dev_init(void) |
4a62a5ab JW |
805 | { |
806 | int retval; | |
807 | ||
808 | lirc_class = class_create(THIS_MODULE, "lirc"); | |
809 | if (IS_ERR(lirc_class)) { | |
3fac0314 | 810 | pr_err("class_create failed\n"); |
54fcecaf | 811 | return PTR_ERR(lirc_class); |
4a62a5ab JW |
812 | } |
813 | ||
a6ddd4fe | 814 | retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, |
463015dd | 815 | "BaseRemoteCtl"); |
4a62a5ab JW |
816 | if (retval) { |
817 | class_destroy(lirc_class); | |
3fac0314 | 818 | pr_err("alloc_chrdev_region failed\n"); |
54fcecaf | 819 | return retval; |
4a62a5ab JW |
820 | } |
821 | ||
5817b3d1 SY |
822 | pr_debug("IR Remote Control driver registered, major %d\n", |
823 | MAJOR(lirc_base_dev)); | |
4a62a5ab | 824 | |
54fcecaf | 825 | return 0; |
4a62a5ab JW |
826 | } |
827 | ||
a60d64b1 | 828 | void __exit lirc_dev_exit(void) |
4a62a5ab JW |
829 | { |
830 | class_destroy(lirc_class); | |
a6ddd4fe | 831 | unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX); |
4a62a5ab JW |
832 | } |
833 | ||
f4364dcf SY |
834 | struct rc_dev *rc_dev_get_from_fd(int fd) |
835 | { | |
836 | struct fd f = fdget(fd); | |
837 | struct lirc_fh *fh; | |
838 | struct rc_dev *dev; | |
839 | ||
840 | if (!f.file) | |
841 | return ERR_PTR(-EBADF); | |
842 | ||
843 | if (f.file->f_op != &lirc_fops) { | |
844 | fdput(f); | |
845 | return ERR_PTR(-EINVAL); | |
846 | } | |
847 | ||
848 | fh = f.file->private_data; | |
849 | dev = fh->rc; | |
850 | ||
851 | get_device(&dev->dev); | |
852 | fdput(f); | |
853 | ||
854 | return dev; | |
855 | } | |
856 | ||
04d0e8de | 857 | MODULE_ALIAS("lirc_dev"); |