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); | |
414 | ir->d.set_use_dec(ir->d.data); | |
c1cbb702 | 415 | module_put(cdev->owner); |
4a62a5ab | 416 | mutex_unlock(&ir->irctl_lock); |
4a62a5ab | 417 | } else { |
578fcb8e | 418 | lirc_irctl_cleanup(ir); |
c1cbb702 | 419 | cdev_del(cdev); |
8de111e2 | 420 | kfree(cdev); |
4a62a5ab JW |
421 | kfree(ir); |
422 | irctls[minor] = NULL; | |
423 | } | |
424 | ||
425 | mutex_unlock(&lirc_dev_lock); | |
426 | ||
427 | return 0; | |
428 | } | |
429 | EXPORT_SYMBOL(lirc_unregister_driver); | |
430 | ||
431 | int lirc_dev_fop_open(struct inode *inode, struct file *file) | |
432 | { | |
433 | struct irctl *ir; | |
c1cbb702 | 434 | struct cdev *cdev; |
4a62a5ab JW |
435 | int retval = 0; |
436 | ||
437 | if (iminor(inode) >= MAX_IRCTL_DEVICES) { | |
3fac0314 | 438 | pr_err("open result for %d is -ENODEV\n", iminor(inode)); |
4a62a5ab JW |
439 | return -ENODEV; |
440 | } | |
441 | ||
442 | if (mutex_lock_interruptible(&lirc_dev_lock)) | |
443 | return -ERESTARTSYS; | |
444 | ||
445 | ir = irctls[iminor(inode)]; | |
446 | if (!ir) { | |
447 | retval = -ENODEV; | |
448 | goto error; | |
449 | } | |
450 | ||
451 | dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); | |
452 | ||
453 | if (ir->d.minor == NOPLUG) { | |
454 | retval = -ENODEV; | |
455 | goto error; | |
456 | } | |
457 | ||
458 | if (ir->open) { | |
459 | retval = -EBUSY; | |
460 | goto error; | |
461 | } | |
462 | ||
ca7a722d SK |
463 | if (ir->d.rdev) { |
464 | retval = rc_open(ir->d.rdev); | |
465 | if (retval) | |
466 | goto error; | |
467 | } | |
468 | ||
8de111e2 | 469 | cdev = ir->cdev; |
c1cbb702 JW |
470 | if (try_module_get(cdev->owner)) { |
471 | ir->open++; | |
4a62a5ab JW |
472 | retval = ir->d.set_use_inc(ir->d.data); |
473 | ||
474 | if (retval) { | |
c1cbb702 JW |
475 | module_put(cdev->owner); |
476 | ir->open--; | |
4a62a5ab JW |
477 | } else { |
478 | lirc_buffer_clear(ir->buf); | |
479 | } | |
480 | if (ir->task) | |
481 | wake_up_process(ir->task); | |
482 | } | |
483 | ||
484 | error: | |
4a62a5ab JW |
485 | mutex_unlock(&lirc_dev_lock); |
486 | ||
d9d2e9d5 AB |
487 | nonseekable_open(inode, file); |
488 | ||
4a62a5ab JW |
489 | return retval; |
490 | } | |
491 | EXPORT_SYMBOL(lirc_dev_fop_open); | |
492 | ||
493 | int lirc_dev_fop_close(struct inode *inode, struct file *file) | |
494 | { | |
495 | struct irctl *ir = irctls[iminor(inode)]; | |
8de111e2 | 496 | struct cdev *cdev; |
b64e10f3 | 497 | int ret; |
4a62a5ab | 498 | |
715d29a7 | 499 | if (!ir) { |
3fac0314 | 500 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
501 | return -EINVAL; |
502 | } | |
4a62a5ab | 503 | |
8de111e2 JW |
504 | cdev = ir->cdev; |
505 | ||
b64e10f3 MCC |
506 | ret = mutex_lock_killable(&lirc_dev_lock); |
507 | WARN_ON(ret); | |
4a62a5ab | 508 | |
3dd94f00 | 509 | rc_close(ir->d.rdev); |
ca7a722d | 510 | |
715d29a7 | 511 | ir->open--; |
4a62a5ab JW |
512 | if (ir->attached) { |
513 | ir->d.set_use_dec(ir->d.data); | |
c1cbb702 | 514 | module_put(cdev->owner); |
4a62a5ab | 515 | } else { |
578fcb8e | 516 | lirc_irctl_cleanup(ir); |
c1cbb702 | 517 | cdev_del(cdev); |
4a62a5ab | 518 | irctls[ir->d.minor] = NULL; |
8de111e2 | 519 | kfree(cdev); |
4a62a5ab JW |
520 | kfree(ir); |
521 | } | |
522 | ||
b64e10f3 MCC |
523 | if (!ret) |
524 | mutex_unlock(&lirc_dev_lock); | |
4a62a5ab JW |
525 | |
526 | return 0; | |
527 | } | |
528 | EXPORT_SYMBOL(lirc_dev_fop_close); | |
529 | ||
530 | unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) | |
531 | { | |
496ad9aa | 532 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab JW |
533 | unsigned int ret; |
534 | ||
715d29a7 | 535 | if (!ir) { |
3fac0314 | 536 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
537 | return POLLERR; |
538 | } | |
539 | ||
5c769a68 | 540 | if (!ir->attached) |
4a62a5ab | 541 | return POLLERR; |
4a62a5ab | 542 | |
3656cddd AS |
543 | if (ir->buf) { |
544 | poll_wait(file, &ir->buf->wait_poll, wait); | |
4a62a5ab | 545 | |
4a62a5ab JW |
546 | if (lirc_buffer_empty(ir->buf)) |
547 | ret = 0; | |
548 | else | |
549 | ret = POLLIN | POLLRDNORM; | |
3656cddd | 550 | } else |
4a62a5ab JW |
551 | ret = POLLERR; |
552 | ||
553 | dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", | |
554 | ir->d.name, ir->d.minor, ret); | |
555 | ||
556 | return ret; | |
557 | } | |
558 | EXPORT_SYMBOL(lirc_dev_fop_poll); | |
559 | ||
044e5878 | 560 | long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
4a62a5ab | 561 | { |
be1f985f | 562 | __u32 mode; |
4a62a5ab | 563 | int result = 0; |
496ad9aa | 564 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab | 565 | |
8be292cc | 566 | if (!ir) { |
3fac0314 | 567 | pr_err("no irctl found!\n"); |
8be292cc JW |
568 | return -ENODEV; |
569 | } | |
4a62a5ab JW |
570 | |
571 | dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", | |
572 | ir->d.name, ir->d.minor, cmd); | |
573 | ||
574 | if (ir->d.minor == NOPLUG || !ir->attached) { | |
3fac0314 | 575 | dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", |
4a62a5ab JW |
576 | ir->d.name, ir->d.minor); |
577 | return -ENODEV; | |
578 | } | |
579 | ||
580 | mutex_lock(&ir->irctl_lock); | |
581 | ||
582 | switch (cmd) { | |
583 | case LIRC_GET_FEATURES: | |
60519af3 | 584 | result = put_user(ir->d.features, (__u32 __user *)arg); |
4a62a5ab JW |
585 | break; |
586 | case LIRC_GET_REC_MODE: | |
587 | if (!(ir->d.features & LIRC_CAN_REC_MASK)) { | |
b4088094 | 588 | result = -ENOTTY; |
4a62a5ab JW |
589 | break; |
590 | } | |
591 | ||
592 | result = put_user(LIRC_REC2MODE | |
593 | (ir->d.features & LIRC_CAN_REC_MASK), | |
60519af3 | 594 | (__u32 __user *)arg); |
4a62a5ab JW |
595 | break; |
596 | case LIRC_SET_REC_MODE: | |
597 | if (!(ir->d.features & LIRC_CAN_REC_MASK)) { | |
b4088094 | 598 | result = -ENOTTY; |
4a62a5ab JW |
599 | break; |
600 | } | |
601 | ||
60519af3 | 602 | result = get_user(mode, (__u32 __user *)arg); |
4a62a5ab JW |
603 | if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) |
604 | result = -EINVAL; | |
605 | /* | |
606 | * FIXME: We should actually set the mode somehow but | |
607 | * for now, lirc_serial doesn't support mode changing either | |
608 | */ | |
609 | break; | |
610 | case LIRC_GET_LENGTH: | |
60519af3 | 611 | result = put_user(ir->d.code_length, (__u32 __user *)arg); |
4a62a5ab JW |
612 | break; |
613 | case LIRC_GET_MIN_TIMEOUT: | |
614 | if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || | |
615 | ir->d.min_timeout == 0) { | |
b4088094 | 616 | result = -ENOTTY; |
4a62a5ab JW |
617 | break; |
618 | } | |
619 | ||
60519af3 | 620 | result = put_user(ir->d.min_timeout, (__u32 __user *)arg); |
4a62a5ab JW |
621 | break; |
622 | case LIRC_GET_MAX_TIMEOUT: | |
623 | if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || | |
624 | ir->d.max_timeout == 0) { | |
b4088094 | 625 | result = -ENOTTY; |
4a62a5ab JW |
626 | break; |
627 | } | |
628 | ||
60519af3 | 629 | result = put_user(ir->d.max_timeout, (__u32 __user *)arg); |
4a62a5ab JW |
630 | break; |
631 | default: | |
632 | result = -EINVAL; | |
633 | } | |
634 | ||
4a62a5ab JW |
635 | mutex_unlock(&ir->irctl_lock); |
636 | ||
637 | return result; | |
638 | } | |
639 | EXPORT_SYMBOL(lirc_dev_fop_ioctl); | |
640 | ||
641 | ssize_t lirc_dev_fop_read(struct file *file, | |
0e835087 | 642 | char __user *buffer, |
4a62a5ab JW |
643 | size_t length, |
644 | loff_t *ppos) | |
645 | { | |
496ad9aa | 646 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
715d29a7 | 647 | unsigned char *buf; |
4a62a5ab JW |
648 | int ret = 0, written = 0; |
649 | DECLARE_WAITQUEUE(wait, current); | |
650 | ||
715d29a7 | 651 | if (!ir) { |
3fac0314 | 652 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
653 | return -ENODEV; |
654 | } | |
655 | ||
4a62a5ab JW |
656 | dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); |
657 | ||
715d29a7 JW |
658 | buf = kzalloc(ir->chunk_size, GFP_KERNEL); |
659 | if (!buf) | |
660 | return -ENOMEM; | |
661 | ||
250f7a5f DC |
662 | if (mutex_lock_interruptible(&ir->irctl_lock)) { |
663 | ret = -ERESTARTSYS; | |
664 | goto out_unlocked; | |
665 | } | |
4a62a5ab | 666 | if (!ir->attached) { |
250f7a5f DC |
667 | ret = -ENODEV; |
668 | goto out_locked; | |
4a62a5ab JW |
669 | } |
670 | ||
671 | if (length % ir->chunk_size) { | |
250f7a5f DC |
672 | ret = -EINVAL; |
673 | goto out_locked; | |
4a62a5ab JW |
674 | } |
675 | ||
676 | /* | |
677 | * we add ourselves to the task queue before buffer check | |
678 | * to avoid losing scan code (in case when queue is awaken somewhere | |
679 | * between while condition checking and scheduling) | |
680 | */ | |
681 | add_wait_queue(&ir->buf->wait_poll, &wait); | |
682 | set_current_state(TASK_INTERRUPTIBLE); | |
683 | ||
684 | /* | |
685 | * while we didn't provide 'length' bytes, device is opened in blocking | |
686 | * mode and 'copy_to_user' is happy, wait for data. | |
687 | */ | |
688 | while (written < length && ret == 0) { | |
689 | if (lirc_buffer_empty(ir->buf)) { | |
690 | /* According to the read(2) man page, 'written' can be | |
691 | * returned as less than 'length', instead of blocking | |
692 | * again, returning -EWOULDBLOCK, or returning | |
62e92682 AS |
693 | * -ERESTARTSYS |
694 | */ | |
4a62a5ab JW |
695 | if (written) |
696 | break; | |
697 | if (file->f_flags & O_NONBLOCK) { | |
698 | ret = -EWOULDBLOCK; | |
699 | break; | |
700 | } | |
701 | if (signal_pending(current)) { | |
702 | ret = -ERESTARTSYS; | |
703 | break; | |
704 | } | |
705 | ||
706 | mutex_unlock(&ir->irctl_lock); | |
707 | schedule(); | |
708 | set_current_state(TASK_INTERRUPTIBLE); | |
709 | ||
710 | if (mutex_lock_interruptible(&ir->irctl_lock)) { | |
711 | ret = -ERESTARTSYS; | |
69c271f3 JW |
712 | remove_wait_queue(&ir->buf->wait_poll, &wait); |
713 | set_current_state(TASK_RUNNING); | |
714 | goto out_unlocked; | |
4a62a5ab JW |
715 | } |
716 | ||
717 | if (!ir->attached) { | |
718 | ret = -ENODEV; | |
719 | break; | |
720 | } | |
721 | } else { | |
722 | lirc_buffer_read(ir->buf, buf); | |
60519af3 | 723 | ret = copy_to_user((void __user *)buffer+written, buf, |
4a62a5ab | 724 | ir->buf->chunk_size); |
250f7a5f DC |
725 | if (!ret) |
726 | written += ir->buf->chunk_size; | |
727 | else | |
728 | ret = -EFAULT; | |
4a62a5ab JW |
729 | } |
730 | } | |
731 | ||
732 | remove_wait_queue(&ir->buf->wait_poll, &wait); | |
733 | set_current_state(TASK_RUNNING); | |
250f7a5f DC |
734 | |
735 | out_locked: | |
4a62a5ab JW |
736 | mutex_unlock(&ir->irctl_lock); |
737 | ||
69c271f3 | 738 | out_unlocked: |
715d29a7 | 739 | kfree(buf); |
4a62a5ab JW |
740 | |
741 | return ret ? ret : written; | |
742 | } | |
743 | EXPORT_SYMBOL(lirc_dev_fop_read); | |
744 | ||
745 | void *lirc_get_pdata(struct file *file) | |
746 | { | |
0990a97a | 747 | return irctls[iminor(file_inode(file))]->d.data; |
4a62a5ab JW |
748 | } |
749 | EXPORT_SYMBOL(lirc_get_pdata); | |
750 | ||
751 | ||
0e835087 | 752 | ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, |
4a62a5ab JW |
753 | size_t length, loff_t *ppos) |
754 | { | |
496ad9aa | 755 | struct irctl *ir = irctls[iminor(file_inode(file))]; |
4a62a5ab | 756 | |
715d29a7 | 757 | if (!ir) { |
3fac0314 | 758 | pr_err("called with invalid irctl\n"); |
715d29a7 JW |
759 | return -ENODEV; |
760 | } | |
761 | ||
4a62a5ab JW |
762 | if (!ir->attached) |
763 | return -ENODEV; | |
764 | ||
765 | return -EINVAL; | |
766 | } | |
767 | EXPORT_SYMBOL(lirc_dev_fop_write); | |
768 | ||
769 | ||
770 | static int __init lirc_dev_init(void) | |
771 | { | |
772 | int retval; | |
773 | ||
774 | lirc_class = class_create(THIS_MODULE, "lirc"); | |
775 | if (IS_ERR(lirc_class)) { | |
3fac0314 | 776 | pr_err("class_create failed\n"); |
54fcecaf | 777 | return PTR_ERR(lirc_class); |
4a62a5ab JW |
778 | } |
779 | ||
780 | retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, | |
781 | IRCTL_DEV_NAME); | |
782 | if (retval) { | |
783 | class_destroy(lirc_class); | |
3fac0314 | 784 | pr_err("alloc_chrdev_region failed\n"); |
54fcecaf | 785 | return retval; |
4a62a5ab JW |
786 | } |
787 | ||
788 | ||
3fac0314 AS |
789 | pr_info("IR Remote Control driver registered, major %d\n", |
790 | MAJOR(lirc_base_dev)); | |
4a62a5ab | 791 | |
54fcecaf | 792 | return 0; |
4a62a5ab JW |
793 | } |
794 | ||
795 | ||
796 | ||
797 | static void __exit lirc_dev_exit(void) | |
798 | { | |
799 | class_destroy(lirc_class); | |
800 | unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); | |
3fac0314 | 801 | pr_info("module unloaded\n"); |
4a62a5ab JW |
802 | } |
803 | ||
804 | module_init(lirc_dev_init); | |
805 | module_exit(lirc_dev_exit); | |
806 | ||
807 | MODULE_DESCRIPTION("LIRC base driver module"); | |
808 | MODULE_AUTHOR("Artur Lipowski"); | |
809 | MODULE_LICENSE("GPL"); | |
810 | ||
811 | module_param(debug, bool, S_IRUGO | S_IWUSR); | |
812 | MODULE_PARM_DESC(debug, "Enable debugging messages"); |