]>
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 | |
42e0442f | 22 | #define LIRCBUF_SIZE 256 |
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 | |
42e0442f SY |
272 | /* |
273 | * The scancode field in lirc_scancode is 64-bit simply | |
274 | * to future-proof it, since there are IR protocols encode | |
275 | * use more than 32 bits. For now only 32-bit protocols | |
276 | * are supported. | |
277 | */ | |
278 | if (scan.scancode > U32_MAX || | |
4957133f SY |
279 | !rc_validate_scancode(scan.rc_proto, scan.scancode)) { |
280 | ret = -EINVAL; | |
a74b2bff | 281 | goto out_unlock; |
4957133f | 282 | } |
d9d2e9d5 | 283 | |
42e0442f | 284 | raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL); |
4957133f SY |
285 | if (!raw) { |
286 | ret = -ENOMEM; | |
a74b2bff | 287 | goto out_unlock; |
4957133f | 288 | } |
3381b779 | 289 | |
42e0442f SY |
290 | ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode, |
291 | raw, LIRCBUF_SIZE); | |
292 | if (ret < 0) | |
8d25e15d | 293 | goto out_kfree_raw; |
4a62a5ab | 294 | |
42e0442f | 295 | count = ret; |
4a62a5ab | 296 | |
42e0442f SY |
297 | txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL); |
298 | if (!txbuf) { | |
299 | ret = -ENOMEM; | |
8d25e15d | 300 | goto out_kfree_raw; |
42e0442f | 301 | } |
4a62a5ab | 302 | |
42e0442f SY |
303 | for (i = 0; i < count; i++) |
304 | /* Convert from NS to US */ | |
305 | txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000); | |
3381b779 | 306 | |
42e0442f SY |
307 | if (dev->s_tx_carrier) { |
308 | int carrier = ir_raw_encode_carrier(scan.rc_proto); | |
4a62a5ab | 309 | |
42e0442f SY |
310 | if (carrier > 0) |
311 | dev->s_tx_carrier(dev, carrier); | |
312 | } | |
313 | } else { | |
4957133f SY |
314 | if (n < sizeof(unsigned int) || n % sizeof(unsigned int)) { |
315 | ret = -EINVAL; | |
a74b2bff | 316 | goto out_unlock; |
4957133f | 317 | } |
4a62a5ab | 318 | |
42e0442f | 319 | count = n / sizeof(unsigned int); |
4957133f SY |
320 | if (count > LIRCBUF_SIZE || count % 2 == 0) { |
321 | ret = -EINVAL; | |
a74b2bff | 322 | goto out_unlock; |
4957133f | 323 | } |
4a62a5ab | 324 | |
42e0442f | 325 | txbuf = memdup_user(buf, n); |
4957133f SY |
326 | if (IS_ERR(txbuf)) { |
327 | ret = PTR_ERR(txbuf); | |
a74b2bff | 328 | goto out_unlock; |
4957133f | 329 | } |
42e0442f | 330 | } |
4a62a5ab | 331 | |
42e0442f SY |
332 | for (i = 0; i < count; i++) { |
333 | if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) { | |
334 | ret = -EINVAL; | |
a74b2bff | 335 | goto out_kfree; |
42e0442f | 336 | } |
4a62a5ab | 337 | |
42e0442f | 338 | duration += txbuf[i]; |
b15e3937 | 339 | } |
4a62a5ab | 340 | |
29422737 SY |
341 | start = ktime_get(); |
342 | ||
42e0442f SY |
343 | ret = dev->tx_ir(dev, txbuf, count); |
344 | if (ret < 0) | |
a74b2bff | 345 | goto out_kfree; |
42e0442f | 346 | |
f81a8158 SY |
347 | kfree(txbuf); |
348 | kfree(raw); | |
349 | mutex_unlock(&dev->lock); | |
350 | ||
dde7edff SY |
351 | /* |
352 | * The lircd gap calculation expects the write function to | |
353 | * wait for the actual IR signal to be transmitted before | |
354 | * returning. | |
355 | */ | |
356 | towait = ktime_us_delta(ktime_add_us(start, duration), | |
357 | ktime_get()); | |
358 | if (towait > 0) { | |
359 | set_current_state(TASK_INTERRUPTIBLE); | |
360 | schedule_timeout(usecs_to_jiffies(towait)); | |
42e0442f | 361 | } |
4a62a5ab | 362 | |
f81a8158 | 363 | return n; |
a74b2bff | 364 | out_kfree: |
42e0442f | 365 | kfree(txbuf); |
8d25e15d | 366 | out_kfree_raw: |
42e0442f | 367 | kfree(raw); |
a74b2bff SY |
368 | out_unlock: |
369 | mutex_unlock(&dev->lock); | |
4a62a5ab JW |
370 | return ret; |
371 | } | |
4a62a5ab | 372 | |
7e45d660 | 373 | static long ir_lirc_ioctl(struct file *file, unsigned int cmd, |
42e0442f | 374 | unsigned long arg) |
4a62a5ab | 375 | { |
7e45d660 SY |
376 | struct lirc_fh *fh = file->private_data; |
377 | struct rc_dev *dev = fh->rc; | |
42e0442f | 378 | u32 __user *argp = (u32 __user *)(arg); |
4957133f SY |
379 | u32 val = 0; |
380 | int ret; | |
42e0442f SY |
381 | |
382 | if (_IOC_DIR(cmd) & _IOC_WRITE) { | |
383 | ret = get_user(val, argp); | |
384 | if (ret) | |
385 | return ret; | |
386 | } | |
4a62a5ab | 387 | |
4957133f SY |
388 | ret = mutex_lock_interruptible(&dev->lock); |
389 | if (ret) | |
390 | return ret; | |
3381b779 | 391 | |
4957133f SY |
392 | if (!dev->registered) { |
393 | ret = -ENODEV; | |
3381b779 | 394 | goto out; |
4a62a5ab JW |
395 | } |
396 | ||
4a62a5ab JW |
397 | switch (cmd) { |
398 | case LIRC_GET_FEATURES: | |
42e0442f SY |
399 | if (dev->driver_type == RC_DRIVER_SCANCODE) |
400 | val |= LIRC_CAN_REC_SCANCODE; | |
401 | ||
402 | if (dev->driver_type == RC_DRIVER_IR_RAW) { | |
02d742f4 | 403 | val |= LIRC_CAN_REC_MODE2; |
42e0442f SY |
404 | if (dev->rx_resolution) |
405 | val |= LIRC_CAN_GET_REC_RESOLUTION; | |
406 | } | |
407 | ||
408 | if (dev->tx_ir) { | |
02d742f4 | 409 | val |= LIRC_CAN_SEND_PULSE; |
42e0442f SY |
410 | if (dev->s_tx_mask) |
411 | val |= LIRC_CAN_SET_TRANSMITTER_MASK; | |
412 | if (dev->s_tx_carrier) | |
413 | val |= LIRC_CAN_SET_SEND_CARRIER; | |
414 | if (dev->s_tx_duty_cycle) | |
415 | val |= LIRC_CAN_SET_SEND_DUTY_CYCLE; | |
4a62a5ab JW |
416 | } |
417 | ||
42e0442f SY |
418 | if (dev->s_rx_carrier_range) |
419 | val |= LIRC_CAN_SET_REC_CARRIER | | |
420 | LIRC_CAN_SET_REC_CARRIER_RANGE; | |
421 | ||
422 | if (dev->s_learning_mode) | |
423 | val |= LIRC_CAN_USE_WIDEBAND_RECEIVER; | |
424 | ||
425 | if (dev->s_carrier_report) | |
426 | val |= LIRC_CAN_MEASURE_CARRIER; | |
427 | ||
428 | if (dev->max_timeout) | |
429 | val |= LIRC_CAN_SET_REC_TIMEOUT; | |
430 | ||
4a62a5ab | 431 | break; |
42e0442f SY |
432 | |
433 | /* mode support */ | |
434 | case LIRC_GET_REC_MODE: | |
435 | if (dev->driver_type == RC_DRIVER_IR_RAW_TX) | |
4957133f SY |
436 | ret = -ENOTTY; |
437 | else | |
438 | val = fh->rec_mode; | |
42e0442f SY |
439 | break; |
440 | ||
4a62a5ab | 441 | case LIRC_SET_REC_MODE: |
42e0442f SY |
442 | switch (dev->driver_type) { |
443 | case RC_DRIVER_IR_RAW_TX: | |
4957133f SY |
444 | ret = -ENOTTY; |
445 | break; | |
42e0442f SY |
446 | case RC_DRIVER_SCANCODE: |
447 | if (val != LIRC_MODE_SCANCODE) | |
4957133f | 448 | ret = -EINVAL; |
42e0442f SY |
449 | break; |
450 | case RC_DRIVER_IR_RAW: | |
451 | if (!(val == LIRC_MODE_MODE2 || | |
452 | val == LIRC_MODE_SCANCODE)) | |
4957133f | 453 | ret = -EINVAL; |
4a62a5ab JW |
454 | break; |
455 | } | |
456 | ||
4957133f SY |
457 | if (!ret) |
458 | fh->rec_mode = val; | |
459 | break; | |
42e0442f SY |
460 | |
461 | case LIRC_GET_SEND_MODE: | |
462 | if (!dev->tx_ir) | |
4957133f SY |
463 | ret = -ENOTTY; |
464 | else | |
465 | val = fh->send_mode; | |
42e0442f SY |
466 | break; |
467 | ||
468 | case LIRC_SET_SEND_MODE: | |
469 | if (!dev->tx_ir) | |
4957133f SY |
470 | ret = -ENOTTY; |
471 | else if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE)) | |
472 | ret = -EINVAL; | |
473 | else | |
474 | fh->send_mode = val; | |
475 | break; | |
42e0442f SY |
476 | |
477 | /* TX settings */ | |
478 | case LIRC_SET_TRANSMITTER_MASK: | |
479 | if (!dev->s_tx_mask) | |
4957133f SY |
480 | ret = -ENOTTY; |
481 | else | |
482 | ret = dev->s_tx_mask(dev, val); | |
483 | break; | |
42e0442f SY |
484 | |
485 | case LIRC_SET_SEND_CARRIER: | |
486 | if (!dev->s_tx_carrier) | |
4957133f SY |
487 | ret = -ENOTTY; |
488 | else | |
489 | ret = dev->s_tx_carrier(dev, val); | |
490 | break; | |
42e0442f SY |
491 | |
492 | case LIRC_SET_SEND_DUTY_CYCLE: | |
493 | if (!dev->s_tx_duty_cycle) | |
4957133f SY |
494 | ret = -ENOTTY; |
495 | else if (val <= 0 || val >= 100) | |
496 | ret = -EINVAL; | |
497 | else | |
498 | ret = dev->s_tx_duty_cycle(dev, val); | |
499 | break; | |
42e0442f SY |
500 | |
501 | /* RX settings */ | |
502 | case LIRC_SET_REC_CARRIER: | |
503 | if (!dev->s_rx_carrier_range) | |
4957133f SY |
504 | ret = -ENOTTY; |
505 | else if (val <= 0) | |
506 | ret = -EINVAL; | |
507 | else | |
508 | ret = dev->s_rx_carrier_range(dev, fh->carrier_low, | |
509 | val); | |
510 | break; | |
42e0442f SY |
511 | |
512 | case LIRC_SET_REC_CARRIER_RANGE: | |
513 | if (!dev->s_rx_carrier_range) | |
4957133f SY |
514 | ret = -ENOTTY; |
515 | else if (val <= 0) | |
516 | ret = -EINVAL; | |
517 | else | |
518 | fh->carrier_low = val; | |
519 | break; | |
42e0442f SY |
520 | |
521 | case LIRC_GET_REC_RESOLUTION: | |
522 | if (!dev->rx_resolution) | |
4957133f SY |
523 | ret = -ENOTTY; |
524 | else | |
525 | val = dev->rx_resolution / 1000; | |
4a62a5ab | 526 | break; |
42e0442f SY |
527 | |
528 | case LIRC_SET_WIDEBAND_RECEIVER: | |
529 | if (!dev->s_learning_mode) | |
4957133f SY |
530 | ret = -ENOTTY; |
531 | else | |
532 | ret = dev->s_learning_mode(dev, !!val); | |
4a62a5ab | 533 | break; |
42e0442f SY |
534 | |
535 | case LIRC_SET_MEASURE_CARRIER_MODE: | |
536 | if (!dev->s_carrier_report) | |
4957133f SY |
537 | ret = -ENOTTY; |
538 | else | |
539 | ret = dev->s_carrier_report(dev, !!val); | |
540 | break; | |
42e0442f SY |
541 | |
542 | /* Generic timeout support */ | |
543 | case LIRC_GET_MIN_TIMEOUT: | |
544 | if (!dev->max_timeout) | |
4957133f SY |
545 | ret = -ENOTTY; |
546 | else | |
547 | val = DIV_ROUND_UP(dev->min_timeout, 1000); | |
42e0442f SY |
548 | break; |
549 | ||
550 | case LIRC_GET_MAX_TIMEOUT: | |
551 | if (!dev->max_timeout) | |
4957133f SY |
552 | ret = -ENOTTY; |
553 | else | |
554 | val = dev->max_timeout / 1000; | |
42e0442f SY |
555 | break; |
556 | ||
557 | case LIRC_SET_REC_TIMEOUT: | |
4957133f SY |
558 | if (!dev->max_timeout) { |
559 | ret = -ENOTTY; | |
560 | } else if (val > U32_MAX / 1000) { | |
561 | /* Check for multiply overflow */ | |
562 | ret = -EINVAL; | |
563 | } else { | |
564 | u32 tmp = val * 1000; | |
565 | ||
566 | if (tmp < dev->min_timeout || tmp > dev->max_timeout) | |
567 | ret = -EINVAL; | |
568 | else if (dev->s_timeout) | |
569 | ret = dev->s_timeout(dev, tmp); | |
c2837ad0 | 570 | else |
4957133f SY |
571 | dev->timeout = tmp; |
572 | } | |
42e0442f SY |
573 | break; |
574 | ||
95d1544e | 575 | case LIRC_GET_REC_TIMEOUT: |
42e0442f | 576 | if (!dev->timeout) |
4957133f | 577 | ret = -ENOTTY; |
95d1544e SY |
578 | else |
579 | val = DIV_ROUND_UP(dev->timeout, 1000); | |
580 | break; | |
581 | ||
42e0442f | 582 | case LIRC_SET_REC_TIMEOUT_REPORTS: |
28492256 | 583 | if (dev->driver_type != RC_DRIVER_IR_RAW) |
4957133f SY |
584 | ret = -ENOTTY; |
585 | else | |
586 | fh->send_timeout_reports = !!val; | |
42e0442f SY |
587 | break; |
588 | ||
4a62a5ab | 589 | default: |
4957133f | 590 | ret = -ENOTTY; |
4a62a5ab JW |
591 | } |
592 | ||
4957133f | 593 | if (!ret && _IOC_DIR(cmd) & _IOC_READ) |
42e0442f SY |
594 | ret = put_user(val, argp); |
595 | ||
3381b779 | 596 | out: |
4957133f | 597 | mutex_unlock(&dev->lock); |
42e0442f | 598 | return ret; |
4a62a5ab | 599 | } |
4a62a5ab | 600 | |
68c5735e | 601 | static __poll_t ir_lirc_poll(struct file *file, struct poll_table_struct *wait) |
4a62a5ab | 602 | { |
7e45d660 SY |
603 | struct lirc_fh *fh = file->private_data; |
604 | struct rc_dev *rcdev = fh->rc; | |
68c5735e | 605 | __poll_t events = 0; |
42e0442f | 606 | |
7e45d660 | 607 | poll_wait(file, &fh->wait_poll, wait); |
42e0442f SY |
608 | |
609 | if (!rcdev->registered) { | |
a9a08845 | 610 | events = EPOLLHUP | EPOLLERR; |
42e0442f | 611 | } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) { |
7e45d660 SY |
612 | if (fh->rec_mode == LIRC_MODE_SCANCODE && |
613 | !kfifo_is_empty(&fh->scancodes)) | |
a9a08845 | 614 | events = EPOLLIN | EPOLLRDNORM; |
42e0442f | 615 | |
7e45d660 SY |
616 | if (fh->rec_mode == LIRC_MODE_MODE2 && |
617 | !kfifo_is_empty(&fh->rawir)) | |
a9a08845 | 618 | events = EPOLLIN | EPOLLRDNORM; |
42e0442f | 619 | } |
4a62a5ab | 620 | |
42e0442f SY |
621 | return events; |
622 | } | |
715d29a7 | 623 | |
42e0442f SY |
624 | static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, |
625 | size_t length) | |
626 | { | |
7e45d660 SY |
627 | struct lirc_fh *fh = file->private_data; |
628 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
629 | unsigned int copied; |
630 | int ret; | |
b15e3937 | 631 | |
42e0442f SY |
632 | if (length < sizeof(unsigned int) || length % sizeof(unsigned int)) |
633 | return -EINVAL; | |
3381b779 | 634 | |
42e0442f | 635 | do { |
7e45d660 | 636 | if (kfifo_is_empty(&fh->rawir)) { |
42e0442f SY |
637 | if (file->f_flags & O_NONBLOCK) |
638 | return -EAGAIN; | |
4a62a5ab | 639 | |
7e45d660 SY |
640 | ret = wait_event_interruptible(fh->wait_poll, |
641 | !kfifo_is_empty(&fh->rawir) || | |
42e0442f SY |
642 | !rcdev->registered); |
643 | if (ret) | |
644 | return ret; | |
645 | } | |
3381b779 | 646 | |
42e0442f SY |
647 | if (!rcdev->registered) |
648 | return -ENODEV; | |
4a62a5ab | 649 | |
42e0442f SY |
650 | ret = mutex_lock_interruptible(&rcdev->lock); |
651 | if (ret) | |
652 | return ret; | |
7e45d660 | 653 | ret = kfifo_to_user(&fh->rawir, buffer, length, &copied); |
42e0442f SY |
654 | mutex_unlock(&rcdev->lock); |
655 | if (ret) | |
656 | return ret; | |
657 | } while (copied == 0); | |
4a62a5ab | 658 | |
42e0442f SY |
659 | return copied; |
660 | } | |
661 | ||
662 | static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, | |
663 | size_t length) | |
664 | { | |
7e45d660 SY |
665 | struct lirc_fh *fh = file->private_data; |
666 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
667 | unsigned int copied; |
668 | int ret; | |
669 | ||
670 | if (length < sizeof(struct lirc_scancode) || | |
671 | length % sizeof(struct lirc_scancode)) | |
672 | return -EINVAL; | |
673 | ||
674 | do { | |
7e45d660 | 675 | if (kfifo_is_empty(&fh->scancodes)) { |
42e0442f SY |
676 | if (file->f_flags & O_NONBLOCK) |
677 | return -EAGAIN; | |
678 | ||
7e45d660 SY |
679 | ret = wait_event_interruptible(fh->wait_poll, |
680 | !kfifo_is_empty(&fh->scancodes) || | |
42e0442f SY |
681 | !rcdev->registered); |
682 | if (ret) | |
683 | return ret; | |
4a62a5ab | 684 | } |
4a62a5ab | 685 | |
42e0442f SY |
686 | if (!rcdev->registered) |
687 | return -ENODEV; | |
688 | ||
689 | ret = mutex_lock_interruptible(&rcdev->lock); | |
690 | if (ret) | |
691 | return ret; | |
7e45d660 | 692 | ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied); |
42e0442f SY |
693 | mutex_unlock(&rcdev->lock); |
694 | if (ret) | |
695 | return ret; | |
696 | } while (copied == 0); | |
697 | ||
698 | return copied; | |
699 | } | |
250f7a5f | 700 | |
42e0442f SY |
701 | static ssize_t ir_lirc_read(struct file *file, char __user *buffer, |
702 | size_t length, loff_t *ppos) | |
703 | { | |
7e45d660 SY |
704 | struct lirc_fh *fh = file->private_data; |
705 | struct rc_dev *rcdev = fh->rc; | |
42e0442f SY |
706 | |
707 | if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX) | |
708 | return -EINVAL; | |
4a62a5ab | 709 | |
42e0442f SY |
710 | if (!rcdev->registered) |
711 | return -ENODEV; | |
4a62a5ab | 712 | |
7e45d660 | 713 | if (fh->rec_mode == LIRC_MODE_MODE2) |
42e0442f SY |
714 | return ir_lirc_read_mode2(file, buffer, length); |
715 | else /* LIRC_MODE_SCANCODE */ | |
716 | return ir_lirc_read_scancode(file, buffer, length); | |
4a62a5ab | 717 | } |
4a62a5ab | 718 | |
42e0442f SY |
719 | static const struct file_operations lirc_fops = { |
720 | .owner = THIS_MODULE, | |
721 | .write = ir_lirc_transmit_ir, | |
722 | .unlocked_ioctl = ir_lirc_ioctl, | |
723 | #ifdef CONFIG_COMPAT | |
724 | .compat_ioctl = ir_lirc_ioctl, | |
725 | #endif | |
726 | .read = ir_lirc_read, | |
727 | .poll = ir_lirc_poll, | |
728 | .open = ir_lirc_open, | |
729 | .release = ir_lirc_close, | |
730 | .llseek = no_llseek, | |
731 | }; | |
732 | ||
b15e3937 | 733 | static void lirc_release_device(struct device *ld) |
615cd3fe | 734 | { |
a6ddd4fe | 735 | struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev); |
615cd3fe | 736 | |
a6ddd4fe | 737 | put_device(&rcdev->dev); |
615cd3fe | 738 | } |
615cd3fe | 739 | |
a6ddd4fe | 740 | int ir_lirc_register(struct rc_dev *dev) |
4a62a5ab | 741 | { |
ed8c34d7 | 742 | const char *rx_type, *tx_type; |
a6ddd4fe | 743 | int err, minor; |
4a62a5ab | 744 | |
a6ddd4fe | 745 | minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); |
7e45d660 SY |
746 | if (minor < 0) |
747 | return minor; | |
3381b779 | 748 | |
7e45d660 SY |
749 | device_initialize(&dev->lirc_dev); |
750 | dev->lirc_dev.class = lirc_class; | |
a6ddd4fe | 751 | dev->lirc_dev.parent = &dev->dev; |
7e45d660 | 752 | dev->lirc_dev.release = lirc_release_device; |
a6ddd4fe SY |
753 | dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor); |
754 | dev_set_name(&dev->lirc_dev, "lirc%d", minor); | |
615cd3fe | 755 | |
7e45d660 SY |
756 | INIT_LIST_HEAD(&dev->lirc_fh); |
757 | spin_lock_init(&dev->lirc_fh_lock); | |
758 | ||
a6ddd4fe | 759 | cdev_init(&dev->lirc_cdev, &lirc_fops); |
4a62a5ab | 760 | |
a6ddd4fe SY |
761 | err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev); |
762 | if (err) | |
763 | goto out_ida; | |
74c839b2 | 764 | |
a6ddd4fe | 765 | get_device(&dev->dev); |
4a62a5ab | 766 | |
ed8c34d7 SY |
767 | switch (dev->driver_type) { |
768 | case RC_DRIVER_SCANCODE: | |
769 | rx_type = "scancode"; | |
770 | break; | |
771 | case RC_DRIVER_IR_RAW: | |
772 | rx_type = "raw IR"; | |
773 | break; | |
774 | default: | |
775 | rx_type = "no"; | |
776 | break; | |
777 | } | |
778 | ||
779 | if (dev->tx_ir) | |
780 | tx_type = "raw IR"; | |
781 | else | |
782 | tx_type = "no"; | |
783 | ||
784 | dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter", | |
785 | dev->driver_name, minor, rx_type, tx_type); | |
56481f00 | 786 | |
c3c6dd75 | 787 | return 0; |
a6ddd4fe SY |
788 | |
789 | out_ida: | |
790 | ida_simple_remove(&lirc_ida, minor); | |
a6ddd4fe | 791 | return err; |
4a62a5ab | 792 | } |
4a62a5ab | 793 | |
a6ddd4fe | 794 | void ir_lirc_unregister(struct rc_dev *dev) |
4a62a5ab | 795 | { |
7e45d660 SY |
796 | unsigned long flags; |
797 | struct lirc_fh *fh; | |
798 | ||
a6ddd4fe SY |
799 | dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n", |
800 | dev->driver_name, MINOR(dev->lirc_dev.devt)); | |
4a62a5ab | 801 | |
7e45d660 SY |
802 | spin_lock_irqsave(&dev->lirc_fh_lock, flags); |
803 | list_for_each_entry(fh, &dev->lirc_fh, list) | |
a9a08845 | 804 | wake_up_poll(&fh->wait_poll, EPOLLHUP | EPOLLERR); |
7e45d660 | 805 | spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); |
4a62a5ab | 806 | |
a6ddd4fe SY |
807 | cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev); |
808 | ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt)); | |
4a62a5ab | 809 | } |
4a62a5ab | 810 | |
a60d64b1 | 811 | int __init lirc_dev_init(void) |
4a62a5ab JW |
812 | { |
813 | int retval; | |
814 | ||
815 | lirc_class = class_create(THIS_MODULE, "lirc"); | |
816 | if (IS_ERR(lirc_class)) { | |
3fac0314 | 817 | pr_err("class_create failed\n"); |
54fcecaf | 818 | return PTR_ERR(lirc_class); |
4a62a5ab JW |
819 | } |
820 | ||
a6ddd4fe | 821 | retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, |
463015dd | 822 | "BaseRemoteCtl"); |
4a62a5ab JW |
823 | if (retval) { |
824 | class_destroy(lirc_class); | |
3fac0314 | 825 | pr_err("alloc_chrdev_region failed\n"); |
54fcecaf | 826 | return retval; |
4a62a5ab JW |
827 | } |
828 | ||
5817b3d1 SY |
829 | pr_debug("IR Remote Control driver registered, major %d\n", |
830 | MAJOR(lirc_base_dev)); | |
4a62a5ab | 831 | |
54fcecaf | 832 | return 0; |
4a62a5ab JW |
833 | } |
834 | ||
a60d64b1 | 835 | void __exit lirc_dev_exit(void) |
4a62a5ab JW |
836 | { |
837 | class_destroy(lirc_class); | |
a6ddd4fe | 838 | unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX); |
4a62a5ab JW |
839 | } |
840 | ||
f4364dcf SY |
841 | struct rc_dev *rc_dev_get_from_fd(int fd) |
842 | { | |
843 | struct fd f = fdget(fd); | |
844 | struct lirc_fh *fh; | |
845 | struct rc_dev *dev; | |
846 | ||
847 | if (!f.file) | |
848 | return ERR_PTR(-EBADF); | |
849 | ||
850 | if (f.file->f_op != &lirc_fops) { | |
851 | fdput(f); | |
852 | return ERR_PTR(-EINVAL); | |
853 | } | |
854 | ||
855 | fh = f.file->private_data; | |
856 | dev = fh->rc; | |
857 | ||
858 | get_device(&dev->dev); | |
859 | fdput(f); | |
860 | ||
861 | return dev; | |
862 | } | |
863 | ||
04d0e8de | 864 | MODULE_ALIAS("lirc_dev"); |