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