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