]>
Commit | Line | Data |
---|---|---|
805a8966 JW |
1 | /* |
2 | * lirc_parallel.c | |
3 | * | |
4 | * lirc_parallel - device driver for infra-red signal receiving and | |
5 | * transmitting unit built by the author | |
6 | * | |
7 | * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | * | |
23 | */ | |
24 | ||
25 | /*** Includes ***/ | |
26 | ||
805a8966 JW |
27 | #include <linux/module.h> |
28 | #include <linux/sched.h> | |
29 | #include <linux/errno.h> | |
30 | #include <linux/signal.h> | |
31 | #include <linux/fs.h> | |
32 | #include <linux/kernel.h> | |
33 | #include <linux/ioport.h> | |
34 | #include <linux/time.h> | |
35 | #include <linux/mm.h> | |
36 | #include <linux/delay.h> | |
37 | ||
38 | #include <linux/io.h> | |
805a8966 JW |
39 | #include <linux/irq.h> |
40 | #include <linux/uaccess.h> | |
41 | #include <asm/div64.h> | |
42 | ||
43 | #include <linux/poll.h> | |
44 | #include <linux/parport.h> | |
45 | ||
46 | #include <media/lirc.h> | |
47 | #include <media/lirc_dev.h> | |
48 | ||
49 | #include "lirc_parallel.h" | |
50 | ||
51 | #define LIRC_DRIVER_NAME "lirc_parallel" | |
52 | ||
53 | #ifndef LIRC_IRQ | |
54 | #define LIRC_IRQ 7 | |
55 | #endif | |
56 | #ifndef LIRC_PORT | |
57 | #define LIRC_PORT 0x378 | |
58 | #endif | |
59 | #ifndef LIRC_TIMER | |
60 | #define LIRC_TIMER 65536 | |
61 | #endif | |
62 | ||
63 | /*** Global Variables ***/ | |
64 | ||
65 | static int debug; | |
66 | static int check_pselecd; | |
67 | ||
68 | unsigned int irq = LIRC_IRQ; | |
69 | unsigned int io = LIRC_PORT; | |
70 | #ifdef LIRC_TIMER | |
71 | unsigned int timer; | |
72 | unsigned int default_timer = LIRC_TIMER; | |
73 | #endif | |
74 | ||
75 | #define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */ | |
76 | ||
77 | static int rbuf[RBUF_SIZE]; | |
78 | ||
79 | DECLARE_WAIT_QUEUE_HEAD(lirc_wait); | |
80 | ||
81 | unsigned int rptr; | |
82 | unsigned int wptr; | |
83 | unsigned int lost_irqs; | |
84 | int is_open; | |
85 | ||
86 | struct parport *pport; | |
87 | struct pardevice *ppdevice; | |
88 | int is_claimed; | |
89 | ||
90 | unsigned int tx_mask = 1; | |
91 | ||
92 | /*** Internal Functions ***/ | |
93 | ||
94 | static unsigned int in(int offset) | |
95 | { | |
96 | switch (offset) { | |
97 | case LIRC_LP_BASE: | |
98 | return parport_read_data(pport); | |
99 | case LIRC_LP_STATUS: | |
100 | return parport_read_status(pport); | |
101 | case LIRC_LP_CONTROL: | |
102 | return parport_read_control(pport); | |
103 | } | |
104 | return 0; /* make compiler happy */ | |
105 | } | |
106 | ||
107 | static void out(int offset, int value) | |
108 | { | |
109 | switch (offset) { | |
110 | case LIRC_LP_BASE: | |
111 | parport_write_data(pport, value); | |
112 | break; | |
113 | case LIRC_LP_CONTROL: | |
114 | parport_write_control(pport, value); | |
115 | break; | |
116 | case LIRC_LP_STATUS: | |
117 | printk(KERN_INFO "%s: attempt to write to status register\n", | |
118 | LIRC_DRIVER_NAME); | |
119 | break; | |
120 | } | |
121 | } | |
122 | ||
123 | static unsigned int lirc_get_timer(void) | |
124 | { | |
125 | return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT; | |
126 | } | |
127 | ||
128 | static unsigned int lirc_get_signal(void) | |
129 | { | |
130 | return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT; | |
131 | } | |
132 | ||
133 | static void lirc_on(void) | |
134 | { | |
135 | out(LIRC_PORT_DATA, tx_mask); | |
136 | } | |
137 | ||
138 | static void lirc_off(void) | |
139 | { | |
140 | out(LIRC_PORT_DATA, 0); | |
141 | } | |
142 | ||
143 | static unsigned int init_lirc_timer(void) | |
144 | { | |
145 | struct timeval tv, now; | |
146 | unsigned int level, newlevel, timeelapsed, newtimer; | |
147 | int count = 0; | |
148 | ||
149 | do_gettimeofday(&tv); | |
150 | tv.tv_sec++; /* wait max. 1 sec. */ | |
151 | level = lirc_get_timer(); | |
152 | do { | |
153 | newlevel = lirc_get_timer(); | |
154 | if (level == 0 && newlevel != 0) | |
155 | count++; | |
156 | level = newlevel; | |
157 | do_gettimeofday(&now); | |
158 | } while (count < 1000 && (now.tv_sec < tv.tv_sec | |
159 | || (now.tv_sec == tv.tv_sec | |
160 | && now.tv_usec < tv.tv_usec))); | |
161 | ||
162 | timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000 | |
163 | + (now.tv_usec - tv.tv_usec)); | |
164 | if (count >= 1000 && timeelapsed > 0) { | |
165 | if (default_timer == 0) { | |
166 | /* autodetect timer */ | |
167 | newtimer = (1000000*count)/timeelapsed; | |
168 | printk(KERN_INFO "%s: %u Hz timer detected\n", | |
169 | LIRC_DRIVER_NAME, newtimer); | |
170 | return newtimer; | |
171 | } else { | |
172 | newtimer = (1000000*count)/timeelapsed; | |
173 | if (abs(newtimer - default_timer) > default_timer/10) { | |
174 | /* bad timer */ | |
175 | printk(KERN_NOTICE "%s: bad timer: %u Hz\n", | |
176 | LIRC_DRIVER_NAME, newtimer); | |
177 | printk(KERN_NOTICE "%s: using default timer: " | |
178 | "%u Hz\n", | |
179 | LIRC_DRIVER_NAME, default_timer); | |
180 | return default_timer; | |
181 | } else { | |
182 | printk(KERN_INFO "%s: %u Hz timer detected\n", | |
183 | LIRC_DRIVER_NAME, newtimer); | |
184 | return newtimer; /* use detected value */ | |
185 | } | |
186 | } | |
187 | } else { | |
188 | printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); | |
189 | return 0; | |
190 | } | |
191 | } | |
192 | ||
193 | static int lirc_claim(void) | |
194 | { | |
195 | if (parport_claim(ppdevice) != 0) { | |
196 | printk(KERN_WARNING "%s: could not claim port\n", | |
197 | LIRC_DRIVER_NAME); | |
198 | printk(KERN_WARNING "%s: waiting for port becoming available" | |
199 | "\n", LIRC_DRIVER_NAME); | |
200 | if (parport_claim_or_block(ppdevice) < 0) { | |
201 | printk(KERN_NOTICE "%s: could not claim port, giving" | |
202 | " up\n", LIRC_DRIVER_NAME); | |
203 | return 0; | |
204 | } | |
205 | } | |
206 | out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); | |
207 | is_claimed = 1; | |
208 | return 1; | |
209 | } | |
210 | ||
211 | /*** interrupt handler ***/ | |
212 | ||
213 | static void rbuf_write(int signal) | |
214 | { | |
215 | unsigned int nwptr; | |
216 | ||
217 | nwptr = (wptr + 1) & (RBUF_SIZE - 1); | |
218 | if (nwptr == rptr) { | |
219 | /* no new signals will be accepted */ | |
220 | lost_irqs++; | |
221 | printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); | |
222 | return; | |
223 | } | |
224 | rbuf[wptr] = signal; | |
225 | wptr = nwptr; | |
226 | } | |
227 | ||
228 | static void irq_handler(void *blah) | |
229 | { | |
230 | struct timeval tv; | |
231 | static struct timeval lasttv; | |
232 | static int init; | |
233 | long signal; | |
234 | int data; | |
235 | unsigned int level, newlevel; | |
236 | unsigned int timeout; | |
237 | ||
82ce67bf | 238 | if (!is_open) |
805a8966 JW |
239 | return; |
240 | ||
241 | if (!is_claimed) | |
242 | return; | |
243 | ||
244 | #if 0 | |
245 | /* disable interrupt */ | |
246 | disable_irq(irq); | |
247 | out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN)); | |
248 | #endif | |
249 | if (check_pselecd && (in(1) & LP_PSELECD)) | |
250 | return; | |
251 | ||
252 | #ifdef LIRC_TIMER | |
253 | if (init) { | |
254 | do_gettimeofday(&tv); | |
255 | ||
256 | signal = tv.tv_sec - lasttv.tv_sec; | |
257 | if (signal > 15) | |
258 | /* really long time */ | |
259 | data = PULSE_MASK; | |
260 | else | |
261 | data = (int) (signal*1000000 + | |
262 | tv.tv_usec - lasttv.tv_usec + | |
263 | LIRC_SFH506_DELAY); | |
264 | ||
265 | rbuf_write(data); /* space */ | |
266 | } else { | |
267 | if (timer == 0) { | |
268 | /* | |
269 | * wake up; we'll lose this signal, but it will be | |
270 | * garbage if the device is turned on anyway | |
271 | */ | |
272 | timer = init_lirc_timer(); | |
273 | /* enable_irq(irq); */ | |
274 | return; | |
275 | } | |
276 | init = 1; | |
277 | } | |
278 | ||
279 | timeout = timer/10; /* timeout after 1/10 sec. */ | |
280 | signal = 1; | |
281 | level = lirc_get_timer(); | |
282 | do { | |
283 | newlevel = lirc_get_timer(); | |
284 | if (level == 0 && newlevel != 0) | |
285 | signal++; | |
286 | level = newlevel; | |
287 | ||
288 | /* giving up */ | |
289 | if (signal > timeout | |
290 | || (check_pselecd && (in(1) & LP_PSELECD))) { | |
291 | signal = 0; | |
292 | printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); | |
293 | break; | |
294 | } | |
295 | } while (lirc_get_signal()); | |
296 | ||
297 | if (signal != 0) { | |
298 | /* ajust value to usecs */ | |
a1266818 | 299 | __u64 helper; |
805a8966 | 300 | |
a1266818 | 301 | helper = ((__u64) signal)*1000000; |
805a8966 JW |
302 | do_div(helper, timer); |
303 | signal = (long) helper; | |
304 | ||
305 | if (signal > LIRC_SFH506_DELAY) | |
306 | data = signal - LIRC_SFH506_DELAY; | |
307 | else | |
308 | data = 1; | |
309 | rbuf_write(PULSE_BIT|data); /* pulse */ | |
310 | } | |
311 | do_gettimeofday(&lasttv); | |
312 | #else | |
313 | /* add your code here */ | |
314 | #endif | |
315 | ||
316 | wake_up_interruptible(&lirc_wait); | |
317 | ||
318 | /* enable interrupt */ | |
319 | /* | |
320 | enable_irq(irq); | |
321 | out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN); | |
322 | */ | |
323 | } | |
324 | ||
325 | /*** file operations ***/ | |
326 | ||
327 | static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig) | |
328 | { | |
329 | return -ESPIPE; | |
330 | } | |
331 | ||
332 | static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos) | |
333 | { | |
334 | int result = 0; | |
335 | int count = 0; | |
336 | DECLARE_WAITQUEUE(wait, current); | |
337 | ||
338 | if (n % sizeof(int)) | |
339 | return -EINVAL; | |
340 | ||
341 | add_wait_queue(&lirc_wait, &wait); | |
342 | set_current_state(TASK_INTERRUPTIBLE); | |
343 | while (count < n) { | |
344 | if (rptr != wptr) { | |
345 | if (copy_to_user(buf+count, (char *) &rbuf[rptr], | |
346 | sizeof(int))) { | |
347 | result = -EFAULT; | |
348 | break; | |
349 | } | |
350 | rptr = (rptr + 1) & (RBUF_SIZE - 1); | |
351 | count += sizeof(int); | |
352 | } else { | |
353 | if (filep->f_flags & O_NONBLOCK) { | |
354 | result = -EAGAIN; | |
355 | break; | |
356 | } | |
357 | if (signal_pending(current)) { | |
358 | result = -ERESTARTSYS; | |
359 | break; | |
360 | } | |
361 | schedule(); | |
362 | set_current_state(TASK_INTERRUPTIBLE); | |
363 | } | |
364 | } | |
365 | remove_wait_queue(&lirc_wait, &wait); | |
366 | set_current_state(TASK_RUNNING); | |
367 | return count ? count : result; | |
368 | } | |
369 | ||
370 | static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, | |
371 | loff_t *ppos) | |
372 | { | |
373 | int count; | |
374 | unsigned int i; | |
375 | unsigned int level, newlevel; | |
376 | unsigned long flags; | |
377 | int counttimer; | |
378 | int *wbuf; | |
379 | ||
380 | if (!is_claimed) | |
381 | return -EBUSY; | |
382 | ||
383 | count = n / sizeof(int); | |
384 | ||
385 | if (n % sizeof(int) || count % 2 == 0) | |
386 | return -EINVAL; | |
387 | ||
388 | wbuf = memdup_user(buf, n); | |
389 | if (IS_ERR(wbuf)) | |
390 | return PTR_ERR(wbuf); | |
391 | ||
392 | #ifdef LIRC_TIMER | |
393 | if (timer == 0) { | |
394 | /* try again if device is ready */ | |
395 | timer = init_lirc_timer(); | |
396 | if (timer == 0) | |
397 | return -EIO; | |
398 | } | |
399 | ||
400 | /* adjust values from usecs */ | |
401 | for (i = 0; i < count; i++) { | |
a1266818 | 402 | __u64 helper; |
805a8966 | 403 | |
a1266818 | 404 | helper = ((__u64) wbuf[i])*timer; |
805a8966 JW |
405 | do_div(helper, 1000000); |
406 | wbuf[i] = (int) helper; | |
407 | } | |
408 | ||
409 | local_irq_save(flags); | |
410 | i = 0; | |
411 | while (i < count) { | |
412 | level = lirc_get_timer(); | |
413 | counttimer = 0; | |
414 | lirc_on(); | |
415 | do { | |
416 | newlevel = lirc_get_timer(); | |
417 | if (level == 0 && newlevel != 0) | |
418 | counttimer++; | |
419 | level = newlevel; | |
420 | if (check_pselecd && (in(1) & LP_PSELECD)) { | |
421 | lirc_off(); | |
422 | local_irq_restore(flags); | |
423 | return -EIO; | |
424 | } | |
425 | } while (counttimer < wbuf[i]); | |
426 | i++; | |
427 | ||
428 | lirc_off(); | |
429 | if (i == count) | |
430 | break; | |
431 | counttimer = 0; | |
432 | do { | |
433 | newlevel = lirc_get_timer(); | |
434 | if (level == 0 && newlevel != 0) | |
435 | counttimer++; | |
436 | level = newlevel; | |
437 | if (check_pselecd && (in(1) & LP_PSELECD)) { | |
438 | local_irq_restore(flags); | |
439 | return -EIO; | |
440 | } | |
441 | } while (counttimer < wbuf[i]); | |
442 | i++; | |
443 | } | |
444 | local_irq_restore(flags); | |
445 | #else | |
446 | /* place code that handles write without external timer here */ | |
447 | #endif | |
448 | return n; | |
449 | } | |
450 | ||
451 | static unsigned int lirc_poll(struct file *file, poll_table *wait) | |
452 | { | |
453 | poll_wait(file, &lirc_wait, wait); | |
454 | if (rptr != wptr) | |
455 | return POLLIN | POLLRDNORM; | |
456 | return 0; | |
457 | } | |
458 | ||
459 | static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) | |
460 | { | |
461 | int result; | |
a1266818 JW |
462 | __u32 features = LIRC_CAN_SET_TRANSMITTER_MASK | |
463 | LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; | |
464 | __u32 mode; | |
465 | __u32 value; | |
805a8966 JW |
466 | |
467 | switch (cmd) { | |
468 | case LIRC_GET_FEATURES: | |
a1266818 | 469 | result = put_user(features, (__u32 *) arg); |
805a8966 JW |
470 | if (result) |
471 | return result; | |
472 | break; | |
473 | case LIRC_GET_SEND_MODE: | |
a1266818 | 474 | result = put_user(LIRC_MODE_PULSE, (__u32 *) arg); |
805a8966 JW |
475 | if (result) |
476 | return result; | |
477 | break; | |
478 | case LIRC_GET_REC_MODE: | |
a1266818 | 479 | result = put_user(LIRC_MODE_MODE2, (__u32 *) arg); |
805a8966 JW |
480 | if (result) |
481 | return result; | |
482 | break; | |
483 | case LIRC_SET_SEND_MODE: | |
a1266818 | 484 | result = get_user(mode, (__u32 *) arg); |
805a8966 JW |
485 | if (result) |
486 | return result; | |
487 | if (mode != LIRC_MODE_PULSE) | |
488 | return -EINVAL; | |
489 | break; | |
490 | case LIRC_SET_REC_MODE: | |
a1266818 | 491 | result = get_user(mode, (__u32 *) arg); |
805a8966 JW |
492 | if (result) |
493 | return result; | |
494 | if (mode != LIRC_MODE_MODE2) | |
495 | return -ENOSYS; | |
496 | break; | |
497 | case LIRC_SET_TRANSMITTER_MASK: | |
a1266818 | 498 | result = get_user(value, (__u32 *) arg); |
805a8966 JW |
499 | if (result) |
500 | return result; | |
a1266818 | 501 | if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value) |
805a8966 | 502 | return LIRC_PARALLEL_MAX_TRANSMITTERS; |
a1266818 | 503 | tx_mask = value; |
805a8966 JW |
504 | break; |
505 | default: | |
506 | return -ENOIOCTLCMD; | |
507 | } | |
508 | return 0; | |
509 | } | |
510 | ||
511 | static int lirc_open(struct inode *node, struct file *filep) | |
512 | { | |
82ce67bf | 513 | if (is_open || !lirc_claim()) |
805a8966 JW |
514 | return -EBUSY; |
515 | ||
516 | parport_enable_irq(pport); | |
517 | ||
518 | /* init read ptr */ | |
519 | rptr = 0; | |
520 | wptr = 0; | |
521 | lost_irqs = 0; | |
522 | ||
523 | is_open = 1; | |
524 | return 0; | |
525 | } | |
526 | ||
527 | static int lirc_close(struct inode *node, struct file *filep) | |
528 | { | |
529 | if (is_claimed) { | |
530 | is_claimed = 0; | |
531 | parport_release(ppdevice); | |
532 | } | |
533 | is_open = 0; | |
534 | return 0; | |
535 | } | |
536 | ||
0f9313ad | 537 | static const struct file_operations lirc_fops = { |
805a8966 JW |
538 | .owner = THIS_MODULE, |
539 | .llseek = lirc_lseek, | |
540 | .read = lirc_read, | |
541 | .write = lirc_write, | |
542 | .poll = lirc_poll, | |
543 | .unlocked_ioctl = lirc_ioctl, | |
8be292cc JW |
544 | #ifdef CONFIG_COMPAT |
545 | .compat_ioctl = lirc_ioctl, | |
546 | #endif | |
805a8966 JW |
547 | .open = lirc_open, |
548 | .release = lirc_close | |
549 | }; | |
550 | ||
551 | static int set_use_inc(void *data) | |
552 | { | |
553 | return 0; | |
554 | } | |
555 | ||
556 | static void set_use_dec(void *data) | |
557 | { | |
558 | } | |
559 | ||
560 | static struct lirc_driver driver = { | |
561 | .name = LIRC_DRIVER_NAME, | |
562 | .minor = -1, | |
563 | .code_length = 1, | |
564 | .sample_rate = 0, | |
565 | .data = NULL, | |
566 | .add_to_buf = NULL, | |
567 | .set_use_inc = set_use_inc, | |
568 | .set_use_dec = set_use_dec, | |
569 | .fops = &lirc_fops, | |
570 | .dev = NULL, | |
571 | .owner = THIS_MODULE, | |
572 | }; | |
573 | ||
574 | static int pf(void *handle); | |
575 | static void kf(void *handle); | |
576 | ||
805a8966 JW |
577 | static int pf(void *handle) |
578 | { | |
579 | parport_disable_irq(pport); | |
580 | is_claimed = 0; | |
581 | return 0; | |
582 | } | |
583 | ||
584 | static void kf(void *handle) | |
585 | { | |
586 | if (!is_open) | |
587 | return; | |
588 | if (!lirc_claim()) | |
589 | return; | |
590 | parport_enable_irq(pport); | |
591 | lirc_off(); | |
592 | /* this is a bit annoying when you actually print...*/ | |
593 | /* | |
594 | printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME); | |
595 | */ | |
596 | } | |
597 | ||
598 | /*** module initialization and cleanup ***/ | |
599 | ||
600 | static int __init lirc_parallel_init(void) | |
601 | { | |
602 | pport = parport_find_base(io); | |
603 | if (pport == NULL) { | |
604 | printk(KERN_NOTICE "%s: no port at %x found\n", | |
605 | LIRC_DRIVER_NAME, io); | |
606 | return -ENXIO; | |
607 | } | |
608 | ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, | |
609 | pf, kf, irq_handler, 0, NULL); | |
610 | parport_put_port(pport); | |
611 | if (ppdevice == NULL) { | |
612 | printk(KERN_NOTICE "%s: parport_register_device() failed\n", | |
613 | LIRC_DRIVER_NAME); | |
614 | return -ENXIO; | |
615 | } | |
616 | if (parport_claim(ppdevice) != 0) | |
617 | goto skip_init; | |
618 | is_claimed = 1; | |
619 | out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); | |
620 | ||
621 | #ifdef LIRC_TIMER | |
622 | if (debug) | |
623 | out(LIRC_PORT_DATA, tx_mask); | |
624 | ||
625 | timer = init_lirc_timer(); | |
626 | ||
627 | #if 0 /* continue even if device is offline */ | |
628 | if (timer == 0) { | |
629 | is_claimed = 0; | |
630 | parport_release(pport); | |
631 | parport_unregister_device(ppdevice); | |
632 | return -EIO; | |
633 | } | |
634 | ||
635 | #endif | |
636 | if (debug) | |
637 | out(LIRC_PORT_DATA, 0); | |
638 | #endif | |
639 | ||
640 | is_claimed = 0; | |
641 | parport_release(ppdevice); | |
642 | skip_init: | |
643 | driver.minor = lirc_register_driver(&driver); | |
644 | if (driver.minor < 0) { | |
645 | printk(KERN_NOTICE "%s: register_chrdev() failed\n", | |
646 | LIRC_DRIVER_NAME); | |
647 | parport_unregister_device(ppdevice); | |
648 | return -EIO; | |
649 | } | |
650 | printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", | |
651 | LIRC_DRIVER_NAME, io, irq); | |
652 | return 0; | |
653 | } | |
654 | ||
655 | static void __exit lirc_parallel_exit(void) | |
656 | { | |
657 | parport_unregister_device(ppdevice); | |
658 | lirc_unregister_driver(driver.minor); | |
659 | } | |
660 | ||
661 | module_init(lirc_parallel_init); | |
662 | module_exit(lirc_parallel_exit); | |
663 | ||
664 | MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); | |
665 | MODULE_AUTHOR("Christoph Bartelmus"); | |
666 | MODULE_LICENSE("GPL"); | |
667 | ||
668 | module_param(io, int, S_IRUGO); | |
669 | MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)"); | |
670 | ||
671 | module_param(irq, int, S_IRUGO); | |
672 | MODULE_PARM_DESC(irq, "Interrupt (7 or 5)"); | |
673 | ||
674 | module_param(tx_mask, int, S_IRUGO); | |
675 | MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)"); | |
676 | ||
677 | module_param(debug, bool, S_IRUGO | S_IWUSR); | |
678 | MODULE_PARM_DESC(debug, "Enable debugging messages"); | |
679 | ||
680 | module_param(check_pselecd, bool, S_IRUGO | S_IWUSR); | |
681 | MODULE_PARM_DESC(debug, "Check for printer (default: 0)"); |