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