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 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | * | |
20 | */ | |
21 | ||
3fac0314 AS |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
23 | ||
4a62a5ab JW |
24 | #include <linux/module.h> |
25 | #include <linux/kernel.h> | |
26 | #include <linux/sched.h> | |
27 | #include <linux/errno.h> | |
28 | #include <linux/ioctl.h> | |
29 | #include <linux/fs.h> | |
30 | #include <linux/poll.h> | |
31 | #include <linux/completion.h> | |
4a62a5ab JW |
32 | #include <linux/mutex.h> |
33 | #include <linux/wait.h> | |
34 | #include <linux/unistd.h> | |
35 | #include <linux/kthread.h> | |
36 | #include <linux/bitops.h> | |
37 | #include <linux/device.h> | |
38 | #include <linux/cdev.h> | |
39 | ||
ca7a722d | 40 | #include <media/rc-core.h> |
4a62a5ab | 41 | #include <media/lirc.h> |
5690085e | 42 | #include <media/lirc_dev.h> |
4a62a5ab | 43 | |
90ab5ee9 | 44 | static bool debug; |
4a62a5ab JW |
45 | |
46 | #define IRCTL_DEV_NAME "BaseRemoteCtl" | |
47 | #define NOPLUG -1 | |
48 | #define LOGHEAD "lirc_dev (%s[%d]): " | |
49 | ||
50 | static dev_t lirc_base_dev; | |
51 | ||
52 | struct irctl { | |
53 | struct lirc_driver d; | |
54 | int attached; | |
55 | int open; | |
56 | ||
57 | struct mutex irctl_lock; | |
58 | struct lirc_buffer *buf; | |
59 | unsigned int chunk_size; | |
60 | ||
8de111e2 JW |
61 | struct cdev *cdev; |
62 | ||
4a62a5ab JW |
63 | struct task_struct *task; |
64 | long jiffies_to_wait; | |
4a62a5ab JW |
65 | }; |
66 | ||
67 | static DEFINE_MUTEX(lirc_dev_lock); | |
68 | ||
69 | static struct irctl *irctls[MAX_IRCTL_DEVICES]; | |
70 | ||
71 | /* Only used for sysfs but defined to void otherwise */ | |
72 | static struct class *lirc_class; | |
73 | ||
74 | /* helper function | |
75 | * initializes the irctl structure | |
76 | */ | |
578fcb8e | 77 | static void lirc_irctl_init(struct irctl *ir) |
4a62a5ab | 78 | { |
4a62a5ab JW |
79 | mutex_init(&ir->irctl_lock); |
80 | ir->d.minor = NOPLUG; | |
81 | } | |
82 | ||
578fcb8e | 83 | static void lirc_irctl_cleanup(struct irctl *ir) |
4a62a5ab | 84 | { |
4a62a5ab JW |
85 | device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); |
86 | ||
87 | if (ir->buf != ir->d.rbuf) { | |
88 | lirc_buffer_free(ir->buf); | |
89 | kfree(ir->buf); | |
90 | } | |
91 | ir->buf = NULL; | |
92 | } | |
93 | ||
94 | /* helper function | |
95 | * reads key codes from driver and puts them into buffer | |
96 | * returns 0 on success | |
97 | */ | |
578fcb8e | 98 | static int lirc_add_to_buf(struct irctl *ir) |
4a62a5ab | 99 | { |
19e56539 AS |
100 | int res; |
101 | int got_data = -1; | |
102 | ||
103 | if (!ir->d.add_to_buf) | |
104 | return 0; | |
105 | ||
106 | /* | |
107 | * service the device as long as it is returning | |
108 | * data and we have space | |
109 | */ | |
110 | do { | |
111 | got_data++; | |
112 | res = ir->d.add_to_buf(ir->d.data, ir->buf); | |
113 | } while (!res); | |
114 | ||
115 | if (res == -ENODEV) | |
116 | kthread_stop(ir->task); | |
4a62a5ab | 117 | |
19e56539 | 118 | return got_data ? 0 : res; |
4a62a5ab JW |
119 | } |
120 | ||
121 | /* main function of the polling thread | |
122 | */ | |
123 | static int lirc_thread(void *irctl) | |
124 | { | |
125 | struct irctl *ir = irctl; | |
126 | ||
4a62a5ab JW |
127 | do { |
128 | if (ir->open) { | |
129 | if (ir->jiffies_to_wait) { | |
130 | set_current_state(TASK_INTERRUPTIBLE); | |
131 | schedule_timeout(ir->jiffies_to_wait); | |
132 | } | |
133 | if (kthread_should_stop()) | |
134 | break; | |
578fcb8e | 135 | if (!lirc_add_to_buf(ir)) |
4a62a5ab JW |
136 | wake_up_interruptible(&ir->buf->wait_poll); |
137 | } else { | |
138 | set_current_state(TASK_INTERRUPTIBLE); | |
139 | schedule(); | |
140 | } | |
141 | } while (!kthread_should_stop()); | |
142 | ||
4a62a5ab JW |
143 | return 0; |
144 | } | |
145 | ||
146 | ||
75ef9de1 | 147 | static const struct file_operations lirc_dev_fops = { |
4a62a5ab JW |
148 | .owner = THIS_MODULE, |
149 | .read = lirc_dev_fop_read, | |
150 | .write = lirc_dev_fop_write, | |
151 | .poll = lirc_dev_fop_poll, | |
044e5878 | 152 | .unlocked_ioctl = lirc_dev_fop_ioctl, |
8be292cc JW |
153 | #ifdef CONFIG_COMPAT |
154 | .compat_ioctl = lirc_dev_fop_ioctl, | |
155 | #endif | |
4a62a5ab JW |
156 | .open = lirc_dev_fop_open, |
157 | .release = lirc_dev_fop_close, | |
6038f373 | 158 | .llseek = noop_llseek, |
4a62a5ab JW |
159 | }; |
160 | ||
161 | static int lirc_cdev_add(struct irctl *ir) | |
162 | { | |
8de111e2 | 163 | int retval = -ENOMEM; |
4a62a5ab | 164 | struct lirc_driver *d = &ir->d; |
8de111e2 JW |
165 | struct cdev *cdev; |
166 | ||
167 | cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); | |
168 | if (!cdev) | |
169 | goto err_out; | |
4a62a5ab JW |
170 | |
171 | if (d->fops) { | |
c1cbb702 JW |
172 | cdev_init(cdev, d->fops); |
173 | cdev->owner = d->owner; | |
4a62a5ab | 174 | } else { |
c1cbb702 JW |
175 | cdev_init(cdev, &lirc_dev_fops); |
176 | cdev->owner = THIS_MODULE; | |
4a62a5ab | 177 | } |
b395cbac VK |
178 | retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); |
179 | if (retval) | |
8de111e2 | 180 | goto err_out; |
4a62a5ab | 181 | |
c1cbb702 | 182 | retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); |
8de111e2 | 183 | if (retval) { |
c1cbb702 | 184 | kobject_put(&cdev->kobj); |
8de111e2 JW |
185 | goto err_out; |
186 | } | |
4a62a5ab | 187 | |
8de111e2 JW |
188 | ir->cdev = cdev; |
189 | ||
190 | return 0; | |
191 | ||
192 | err_out: | |
193 | kfree(cdev); | |
4a62a5ab JW |
194 | return retval; |
195 | } | |
196 | ||
6fa99e1a | 197 | static int lirc_allocate_buffer(struct irctl *ir) |
4a62a5ab | 198 | { |
70143984 | 199 | int err = 0; |
4a62a5ab JW |
200 | int bytes_in_key; |
201 | unsigned int chunk_size; | |
202 | unsigned int buffer_size; | |
6fa99e1a AS |
203 | struct lirc_driver *d = &ir->d; |
204 | ||
70143984 AS |
205 | mutex_lock(&lirc_dev_lock); |
206 | ||
6fa99e1a AS |
207 | bytes_in_key = BITS_TO_LONGS(d->code_length) + |
208 | (d->code_length % 8 ? 1 : 0); | |
209 | buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; | |
210 | chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; | |
211 | ||
212 | if (d->rbuf) { | |
213 | ir->buf = d->rbuf; | |
214 | } else { | |
215 | ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); | |
70143984 AS |
216 | if (!ir->buf) { |
217 | err = -ENOMEM; | |
218 | goto out; | |
219 | } | |
6fa99e1a AS |
220 | |
221 | err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); | |
222 | if (err) { | |
223 | kfree(ir->buf); | |
70143984 | 224 | goto out; |
6fa99e1a AS |
225 | } |
226 | } | |
227 | ir->chunk_size = ir->buf->chunk_size; | |
228 | ||
70143984 AS |
229 | out: |
230 | mutex_unlock(&lirc_dev_lock); | |
231 | ||
232 | return err; | |
6fa99e1a AS |
233 | } |
234 | ||
70143984 | 235 | static int lirc_allocate_driver(struct lirc_driver *d) |
6fa99e1a AS |
236 | { |
237 | struct irctl *ir; | |
238 | int minor; | |
4a62a5ab JW |
239 | int err; |
240 | ||
241 | if (!d) { | |
3fac0314 | 242 | pr_err("driver pointer must be not NULL!\n"); |
54fcecaf | 243 | return -EBADRQC; |
4a62a5ab JW |
244 | } |
245 | ||
715d29a7 | 246 | if (!d->dev) { |
3fac0314 | 247 | pr_err("dev pointer not filled in!\n"); |
54fcecaf | 248 | return -EINVAL; |
715d29a7 JW |
249 | } |
250 | ||
9675ee5a | 251 | if (d->minor >= MAX_IRCTL_DEVICES) { |
3fac0314 AS |
252 | dev_err(d->dev, "minor must be between 0 and %d!\n", |
253 | MAX_IRCTL_DEVICES - 1); | |
54fcecaf | 254 | return -EBADRQC; |
4a62a5ab JW |
255 | } |
256 | ||
9675ee5a | 257 | if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { |
3fac0314 AS |
258 | dev_err(d->dev, "code length must be less than %d bits\n", |
259 | BUFLEN * 8); | |
54fcecaf | 260 | return -EBADRQC; |
4a62a5ab JW |
261 | } |
262 | ||
4a62a5ab JW |
263 | if (d->sample_rate) { |
264 | if (2 > d->sample_rate || HZ < d->sample_rate) { | |
3fac0314 AS |
265 | dev_err(d->dev, "invalid %d sample rate\n", |
266 | d->sample_rate); | |
54fcecaf | 267 | return -EBADRQC; |
4a62a5ab JW |
268 | } |
269 | if (!d->add_to_buf) { | |
3fac0314 | 270 | dev_err(d->dev, "add_to_buf not set\n"); |
54fcecaf | 271 | return -EBADRQC; |
4a62a5ab | 272 | } |
14db9fc2 AS |
273 | } else if (!d->rbuf && !(d->fops && d->fops->read && |
274 | d->fops->poll && d->fops->unlocked_ioctl)) { | |
275 | dev_err(d->dev, "undefined read, poll, ioctl\n"); | |
54fcecaf | 276 | return -EBADRQC; |
4a62a5ab JW |
277 | } |
278 | ||
279 | mutex_lock(&lirc_dev_lock); | |
280 | ||
281 | minor = d->minor; | |
282 | ||
283 | if (minor < 0) { | |
284 | /* find first free slot for driver */ | |
285 | for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) | |
286 | if (!irctls[minor]) | |
287 | break; | |
9675ee5a | 288 | if (minor == MAX_IRCTL_DEVICES) { |
3fac0314 | 289 | dev_err(d->dev, "no free slots for drivers!\n"); |
4a62a5ab JW |
290 | err = -ENOMEM; |
291 | goto out_lock; | |
292 | } | |
293 | } else if (irctls[minor]) { | |
3fac0314 | 294 | dev_err(d->dev, "minor (%d) just registered!\n", minor); |
4a62a5ab JW |
295 | err = -EBUSY; |
296 | goto out_lock; | |
297 | } | |
298 | ||
299 | ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); | |
300 | if (!ir) { | |
301 | err = -ENOMEM; | |
302 | goto out_lock; | |
303 | } | |
578fcb8e | 304 | lirc_irctl_init(ir); |
4a62a5ab JW |
305 | irctls[minor] = ir; |
306 | d->minor = minor; | |
307 | ||
4a62a5ab JW |
308 | /* some safety check 8-) */ |
309 | d->name[sizeof(d->name)-1] = '\0'; | |
310 | ||
4a62a5ab JW |
311 | if (d->features == 0) |
312 | d->features = LIRC_CAN_REC_LIRCCODE; | |
313 | ||
314 | ir->d = *d; | |
4a62a5ab JW |
315 | |
316 | device_create(lirc_class, ir->d.dev, | |
317 | MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, | |
318 | "lirc%u", ir->d.minor); | |
319 | ||
320 | if (d->sample_rate) { | |
6ab86d2a AS |
321 | ir->jiffies_to_wait = HZ / d->sample_rate; |
322 | ||
4a62a5ab JW |
323 | /* try to fire up polling thread */ |
324 | ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); | |
325 | if (IS_ERR(ir->task)) { | |
3fac0314 AS |
326 | dev_err(d->dev, "cannot run thread for minor = %d\n", |
327 | d->minor); | |
4a62a5ab JW |
328 | err = -ECHILD; |
329 | goto out_sysfs; | |
330 | } | |
6ab86d2a AS |
331 | } else { |
332 | /* it means - wait for external event in task queue */ | |
333 | ir->jiffies_to_wait = 0; | |
4a62a5ab JW |
334 | } |
335 | ||
336 | err = lirc_cdev_add(ir); | |
337 | if (err) | |
338 | goto out_sysfs; | |
339 | ||
340 | ir->attached = 1; | |
341 | mutex_unlock(&lirc_dev_lock); | |
342 | ||
343 | dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", | |
344 | ir->d.name, ir->d.minor); | |
345 | return minor; | |
346 | ||
347 | out_sysfs: | |
348 | device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); | |
349 | out_lock: | |
350 | mutex_unlock(&lirc_dev_lock); | |
54fcecaf | 351 | |
4a62a5ab JW |
352 | return err; |
353 | } | |
70143984 AS |
354 | |
355 | int lirc_register_driver(struct lirc_driver *d) | |
356 | { | |
357 | int minor, err = 0; | |
358 | ||
359 | minor = lirc_allocate_driver(d); | |
360 | if (minor < 0) | |
361 | return minor; | |
362 | ||
363 | if (LIRC_CAN_REC(d->features)) { | |
364 | err = lirc_allocate_buffer(irctls[minor]); | |
365 | if (err) | |
366 | lirc_unregister_driver(minor); | |
367 | } | |
368 | ||
369 | return err ? err : minor; | |
370 | } | |
4a62a5ab JW |
371 | EXPORT_SYMBOL(lirc_register_driver); |
372 | ||
373 | int lirc_unregister_driver(int minor) | |
374 | { | |
375 | struct irctl *ir; | |
c1cbb702 | 376 | struct cdev *cdev; |
4a62a5ab JW |
377 | |
378 | if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { | |
3fac0314 AS |
379 | pr_err("minor (%d) must be between 0 and %d!\n", |
380 | minor, MAX_IRCTL_DEVICES - 1); | |
4a62a5ab JW |
381 | return -EBADRQC; |
382 | } | |
383 | ||
384 | ir = irctls[minor]; | |
df1868e4 | 385 | if (!ir) { |
3fac0314 | 386 | pr_err("failed to get irctl\n"); |
df1868e4 JW |
387 | return -ENOENT; |
388 | } | |
4a62a5ab | 389 | |
8de111e2 | 390 | cdev = ir->cdev; |
4a62a5ab JW |
391 | |
392 | mutex_lock(&lirc_dev_lock); | |
393 | ||
394 | if (ir->d.minor != minor) { | |
3fac0314 AS |
395 | dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n", |
396 | minor); | |
4a62a5ab JW |
397 | mutex_unlock(&lirc_dev_lock); |
398 | return -ENOENT; | |
399 | } | |
400 | ||
401 | /* end up polling thread */ | |
402 | if (ir->task) | |
403 | kthread_stop(ir->task); | |
404 | ||
405 | dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", | |
406 | ir->d.name, ir->d.minor); | |
407 | ||
408 | ir->attached = 0; | |
409 | if (ir->open) { | |
410 | dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", | |
411 | ir->d.name, ir->d.minor); | |
412 | wake_up_interruptible(&ir->buf->wait_poll); | |
413 | mutex_lock(&ir->irctl_lock); | |
8e5fa4c6 AS |
414 | |
415 | if (ir->d.set_use_dec) | |
416 | ir->d.set_use_dec(ir->d.data); | |
417 | ||
c1cbb702 | 418 | module_put(cdev->owner); |
4a62a5ab | 419 | mutex_unlock(&ir->irctl_lock); |
4a62a5ab | 420 | } else { |
578fcb8e | 421 | lirc_irctl_cleanup(ir); |
c1cbb702 | 422 | cdev_del(cdev); |
8de111e2 | 423 | kfree(cdev); |
4a62a5ab JW |
424 | kfree(ir); |
425 | irctls[minor] = NULL; | |
426 | } | |
427 | ||
428 | mutex_unlock(&lirc_dev_lock); | |
429 | ||
430 | return 0; | |
431 | } | |
432 | EXPORT_SYMBOL(lirc_unregister_driver); | |
433 | ||
434 | int lirc_dev_fop_open(struct inode *inode, struct file *file) | |
435 | { | |
436 | struct irctl *ir; | |
c1cbb702 | 437 | struct cdev *cdev; |
4a62a5ab JW |
438 | int retval = 0; |
439 | ||
440 | if (iminor(inode) >= MAX_IRCTL_DEVICES) { | |
3fac0314 | 441 | pr_err("open result for %d is -ENODEV\n", iminor(inode)); |
4a62a5ab JW |
442 | return -ENODEV; |
443 | } | |
444 | ||
445 | if (mutex_lock_interruptible(&lirc_dev_lock)) | |
446 | return -ERESTARTSYS; | |
447 | ||
448 | ir = irctls[iminor(inode)]; | |
449 | if (!ir) { | |
450 | retval = -ENODEV; | |
451 | goto error; | |
452 | } | |
453 | ||
454 | dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); | |
455 | ||
456 | if (ir->d.minor == NOPLUG) { | |
457 | retval = -ENODEV; | |
458 | goto error; | |
459 | } | |
460 | ||
461 | if (ir->open) { | |
462 | retval = -EBUSY; | |
463 | goto error; | |
464 | } | |
465 | ||
ca7a722d SK |
466 | if (ir->d.rdev) { |
467 | retval = rc_open(ir->d.rdev); | |
468 | if (retval) | |
469 | goto error; | |
470 | } | |
471 | ||
8de111e2 | 472 | cdev = ir->cdev; |
c1cbb702 JW |
473 | if (try_module_get(cdev->owner)) { |
474 | ir->open++; | |
8e5fa4c6 AS |
475 | if (ir->d.set_use_inc) |
476 | retval = ir->d.set_use_inc(ir->d.data); | |
4a62a5ab JW |
477 | |
478 | if (retval) { | |
c1cbb702 JW |
479 | module_put(cdev->owner); |
480 | ir->open--; | |
4a62a5ab JW |
481 | } else { |
482 | lirc_buffer_clear(ir->buf); | |
483 | } | |
484 | if (ir->task) | |
485 | wake_up_process(ir->task); | |
486 | } | |
487 | ||
488 | error: | |
4a62a5ab JW |
489 | mutex_unlock(&lirc_dev_lock); |
490 | ||
d9d2e9d5 AB |
491 | nonseekable_open(inode, file); |
492 | ||
4a62a5ab JW |
493 | return retval; |
494 | } | |
495 | EXPORT_SYMBOL(lirc_dev_fop_open); | |
496 | ||
497 | int lirc_dev_fop_close(struct inode *inode, struct file *file) | |
498 | { | |
499 | struct irctl *ir = irctls[iminor(inode)]; | |
8de111e2 | 500 | struct cdev *cdev; |
b64e10f3 | 501 | int ret; |
4a62a5ab | 502 | |
715d29a7 | 503 | if (!ir) { |
3fac0314 | 504 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
505 | return -EINVAL; |
506 | } | |
4a62a5ab | 507 | |
8de111e2 JW |
508 | cdev = ir->cdev; |
509 | ||
b64e10f3 MCC |
510 | ret = mutex_lock_killable(&lirc_dev_lock); |
511 | WARN_ON(ret); | |
4a62a5ab | 512 | |
3dd94f00 | 513 | rc_close(ir->d.rdev); |
ca7a722d | 514 | |
715d29a7 | 515 | ir->open--; |
4a62a5ab | 516 | if (ir->attached) { |
8e5fa4c6 AS |
517 | if (ir->d.set_use_dec) |
518 | ir->d.set_use_dec(ir->d.data); | |
c1cbb702 | 519 | module_put(cdev->owner); |
4a62a5ab | 520 | } else { |
578fcb8e | 521 | lirc_irctl_cleanup(ir); |
c1cbb702 | 522 | cdev_del(cdev); |
4a62a5ab | 523 | irctls[ir->d.minor] = NULL; |
8de111e2 | 524 | kfree(cdev); |
4a62a5ab JW |
525 | kfree(ir); |
526 | } | |
527 | ||
b64e10f3 MCC |
528 | if (!ret) |
529 | mutex_unlock(&lirc_dev_lock); | |
4a62a5ab JW |
530 | |
531 | return 0; | |
532 | } | |
533 | EXPORT_SYMBOL(lirc_dev_fop_close); | |
534 | ||
535 | unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) | |
536 | { | |
496ad9aa | 537 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab JW |
538 | unsigned int ret; |
539 | ||
715d29a7 | 540 | if (!ir) { |
3fac0314 | 541 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
542 | return POLLERR; |
543 | } | |
544 | ||
5c769a68 | 545 | if (!ir->attached) |
4a62a5ab | 546 | return POLLERR; |
4a62a5ab | 547 | |
3656cddd AS |
548 | if (ir->buf) { |
549 | poll_wait(file, &ir->buf->wait_poll, wait); | |
4a62a5ab | 550 | |
4a62a5ab JW |
551 | if (lirc_buffer_empty(ir->buf)) |
552 | ret = 0; | |
553 | else | |
554 | ret = POLLIN | POLLRDNORM; | |
3656cddd | 555 | } else |
4a62a5ab JW |
556 | ret = POLLERR; |
557 | ||
558 | dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", | |
559 | ir->d.name, ir->d.minor, ret); | |
560 | ||
561 | return ret; | |
562 | } | |
563 | EXPORT_SYMBOL(lirc_dev_fop_poll); | |
564 | ||
044e5878 | 565 | long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
4a62a5ab | 566 | { |
be1f985f | 567 | __u32 mode; |
4a62a5ab | 568 | int result = 0; |
496ad9aa | 569 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab | 570 | |
8be292cc | 571 | if (!ir) { |
3fac0314 | 572 | pr_err("no irctl found!\n"); |
8be292cc JW |
573 | return -ENODEV; |
574 | } | |
4a62a5ab JW |
575 | |
576 | dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", | |
577 | ir->d.name, ir->d.minor, cmd); | |
578 | ||
579 | if (ir->d.minor == NOPLUG || !ir->attached) { | |
3fac0314 | 580 | dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", |
4a62a5ab JW |
581 | ir->d.name, ir->d.minor); |
582 | return -ENODEV; | |
583 | } | |
584 | ||
585 | mutex_lock(&ir->irctl_lock); | |
586 | ||
587 | switch (cmd) { | |
588 | case LIRC_GET_FEATURES: | |
60519af3 | 589 | result = put_user(ir->d.features, (__u32 __user *)arg); |
4a62a5ab JW |
590 | break; |
591 | case LIRC_GET_REC_MODE: | |
273b902a | 592 | if (LIRC_CAN_REC(ir->d.features)) { |
b4088094 | 593 | result = -ENOTTY; |
4a62a5ab JW |
594 | break; |
595 | } | |
596 | ||
597 | result = put_user(LIRC_REC2MODE | |
598 | (ir->d.features & LIRC_CAN_REC_MASK), | |
60519af3 | 599 | (__u32 __user *)arg); |
4a62a5ab JW |
600 | break; |
601 | case LIRC_SET_REC_MODE: | |
273b902a | 602 | if (LIRC_CAN_REC(ir->d.features)) { |
b4088094 | 603 | result = -ENOTTY; |
4a62a5ab JW |
604 | break; |
605 | } | |
606 | ||
60519af3 | 607 | result = get_user(mode, (__u32 __user *)arg); |
4a62a5ab JW |
608 | if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) |
609 | result = -EINVAL; | |
610 | /* | |
611 | * FIXME: We should actually set the mode somehow but | |
612 | * for now, lirc_serial doesn't support mode changing either | |
613 | */ | |
614 | break; | |
615 | case LIRC_GET_LENGTH: | |
60519af3 | 616 | result = put_user(ir->d.code_length, (__u32 __user *)arg); |
4a62a5ab JW |
617 | break; |
618 | case LIRC_GET_MIN_TIMEOUT: | |
619 | if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || | |
620 | ir->d.min_timeout == 0) { | |
b4088094 | 621 | result = -ENOTTY; |
4a62a5ab JW |
622 | break; |
623 | } | |
624 | ||
60519af3 | 625 | result = put_user(ir->d.min_timeout, (__u32 __user *)arg); |
4a62a5ab JW |
626 | break; |
627 | case LIRC_GET_MAX_TIMEOUT: | |
628 | if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || | |
629 | ir->d.max_timeout == 0) { | |
b4088094 | 630 | result = -ENOTTY; |
4a62a5ab JW |
631 | break; |
632 | } | |
633 | ||
60519af3 | 634 | result = put_user(ir->d.max_timeout, (__u32 __user *)arg); |
4a62a5ab JW |
635 | break; |
636 | default: | |
637 | result = -EINVAL; | |
638 | } | |
639 | ||
4a62a5ab JW |
640 | mutex_unlock(&ir->irctl_lock); |
641 | ||
642 | return result; | |
643 | } | |
644 | EXPORT_SYMBOL(lirc_dev_fop_ioctl); | |
645 | ||
646 | ssize_t lirc_dev_fop_read(struct file *file, | |
0e835087 | 647 | char __user *buffer, |
4a62a5ab JW |
648 | size_t length, |
649 | loff_t *ppos) | |
650 | { | |
496ad9aa | 651 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
715d29a7 | 652 | unsigned char *buf; |
4a62a5ab JW |
653 | int ret = 0, written = 0; |
654 | DECLARE_WAITQUEUE(wait, current); | |
655 | ||
715d29a7 | 656 | if (!ir) { |
3fac0314 | 657 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
658 | return -ENODEV; |
659 | } | |
660 | ||
4a62a5ab JW |
661 | dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); |
662 | ||
715d29a7 JW |
663 | buf = kzalloc(ir->chunk_size, GFP_KERNEL); |
664 | if (!buf) | |
665 | return -ENOMEM; | |
666 | ||
250f7a5f DC |
667 | if (mutex_lock_interruptible(&ir->irctl_lock)) { |
668 | ret = -ERESTARTSYS; | |
669 | goto out_unlocked; | |
670 | } | |
4a62a5ab | 671 | if (!ir->attached) { |
250f7a5f DC |
672 | ret = -ENODEV; |
673 | goto out_locked; | |
4a62a5ab JW |
674 | } |
675 | ||
676 | if (length % ir->chunk_size) { | |
250f7a5f DC |
677 | ret = -EINVAL; |
678 | goto out_locked; | |
4a62a5ab JW |
679 | } |
680 | ||
681 | /* | |
682 | * we add ourselves to the task queue before buffer check | |
683 | * to avoid losing scan code (in case when queue is awaken somewhere | |
684 | * between while condition checking and scheduling) | |
685 | */ | |
686 | add_wait_queue(&ir->buf->wait_poll, &wait); | |
687 | set_current_state(TASK_INTERRUPTIBLE); | |
688 | ||
689 | /* | |
690 | * while we didn't provide 'length' bytes, device is opened in blocking | |
691 | * mode and 'copy_to_user' is happy, wait for data. | |
692 | */ | |
693 | while (written < length && ret == 0) { | |
694 | if (lirc_buffer_empty(ir->buf)) { | |
695 | /* According to the read(2) man page, 'written' can be | |
696 | * returned as less than 'length', instead of blocking | |
697 | * again, returning -EWOULDBLOCK, or returning | |
62e92682 AS |
698 | * -ERESTARTSYS |
699 | */ | |
4a62a5ab JW |
700 | if (written) |
701 | break; | |
702 | if (file->f_flags & O_NONBLOCK) { | |
703 | ret = -EWOULDBLOCK; | |
704 | break; | |
705 | } | |
706 | if (signal_pending(current)) { | |
707 | ret = -ERESTARTSYS; | |
708 | break; | |
709 | } | |
710 | ||
711 | mutex_unlock(&ir->irctl_lock); | |
712 | schedule(); | |
713 | set_current_state(TASK_INTERRUPTIBLE); | |
714 | ||
715 | if (mutex_lock_interruptible(&ir->irctl_lock)) { | |
716 | ret = -ERESTARTSYS; | |
69c271f3 JW |
717 | remove_wait_queue(&ir->buf->wait_poll, &wait); |
718 | set_current_state(TASK_RUNNING); | |
719 | goto out_unlocked; | |
4a62a5ab JW |
720 | } |
721 | ||
722 | if (!ir->attached) { | |
723 | ret = -ENODEV; | |
724 | break; | |
725 | } | |
726 | } else { | |
727 | lirc_buffer_read(ir->buf, buf); | |
60519af3 | 728 | ret = copy_to_user((void __user *)buffer+written, buf, |
4a62a5ab | 729 | ir->buf->chunk_size); |
250f7a5f DC |
730 | if (!ret) |
731 | written += ir->buf->chunk_size; | |
732 | else | |
733 | ret = -EFAULT; | |
4a62a5ab JW |
734 | } |
735 | } | |
736 | ||
737 | remove_wait_queue(&ir->buf->wait_poll, &wait); | |
738 | set_current_state(TASK_RUNNING); | |
250f7a5f DC |
739 | |
740 | out_locked: | |
4a62a5ab JW |
741 | mutex_unlock(&ir->irctl_lock); |
742 | ||
69c271f3 | 743 | out_unlocked: |
715d29a7 | 744 | kfree(buf); |
4a62a5ab JW |
745 | |
746 | return ret ? ret : written; | |
747 | } | |
748 | EXPORT_SYMBOL(lirc_dev_fop_read); | |
749 | ||
750 | void *lirc_get_pdata(struct file *file) | |
751 | { | |
0990a97a | 752 | return irctls[iminor(file_inode(file))]->d.data; |
4a62a5ab JW |
753 | } |
754 | EXPORT_SYMBOL(lirc_get_pdata); | |
755 | ||
756 | ||
0e835087 | 757 | ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, |
4a62a5ab JW |
758 | size_t length, loff_t *ppos) |
759 | { | |
496ad9aa | 760 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab | 761 | |
715d29a7 | 762 | if (!ir) { |
3fac0314 | 763 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
764 | return -ENODEV; |
765 | } | |
766 | ||
4a62a5ab JW |
767 | if (!ir->attached) |
768 | return -ENODEV; | |
769 | ||
770 | return -EINVAL; | |
771 | } | |
772 | EXPORT_SYMBOL(lirc_dev_fop_write); | |
773 | ||
774 | ||
775 | static int __init lirc_dev_init(void) | |
776 | { | |
777 | int retval; | |
778 | ||
779 | lirc_class = class_create(THIS_MODULE, "lirc"); | |
780 | if (IS_ERR(lirc_class)) { | |
3fac0314 | 781 | pr_err("class_create failed\n"); |
54fcecaf | 782 | return PTR_ERR(lirc_class); |
4a62a5ab JW |
783 | } |
784 | ||
785 | retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, | |
786 | IRCTL_DEV_NAME); | |
787 | if (retval) { | |
788 | class_destroy(lirc_class); | |
3fac0314 | 789 | pr_err("alloc_chrdev_region failed\n"); |
54fcecaf | 790 | return retval; |
4a62a5ab JW |
791 | } |
792 | ||
793 | ||
3fac0314 AS |
794 | pr_info("IR Remote Control driver registered, major %d\n", |
795 | MAJOR(lirc_base_dev)); | |
4a62a5ab | 796 | |
54fcecaf | 797 | return 0; |
4a62a5ab JW |
798 | } |
799 | ||
800 | ||
801 | ||
802 | static void __exit lirc_dev_exit(void) | |
803 | { | |
804 | class_destroy(lirc_class); | |
805 | unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); | |
3fac0314 | 806 | pr_info("module unloaded\n"); |
4a62a5ab JW |
807 | } |
808 | ||
809 | module_init(lirc_dev_init); | |
810 | module_exit(lirc_dev_exit); | |
811 | ||
812 | MODULE_DESCRIPTION("LIRC base driver module"); | |
813 | MODULE_AUTHOR("Artur Lipowski"); | |
814 | MODULE_LICENSE("GPL"); | |
815 | ||
816 | module_param(debug, bool, S_IRUGO | S_IWUSR); | |
817 | MODULE_PARM_DESC(debug, "Enable debugging messages"); |