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