123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- /*
- * lirc_parallel.c
- *
- * lirc_parallel - device driver for infra-red signal receiving and
- * transmitting unit built by the author
- *
- * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- /*** Includes ***/
- #include <linux/module.h>
- #include <linux/sched.h>
- #include <linux/errno.h>
- #include <linux/signal.h>
- #include <linux/fs.h>
- #include <linux/kernel.h>
- #include <linux/ioport.h>
- #include <linux/time.h>
- #include <linux/mm.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <linux/irq.h>
- #include <linux/uaccess.h>
- #include <asm/div64.h>
- #include <linux/poll.h>
- #include <linux/parport.h>
- #include <linux/platform_device.h>
- #include <media/lirc.h>
- #include <media/lirc_dev.h>
- #include "lirc_parallel.h"
- #define LIRC_DRIVER_NAME "lirc_parallel"
- #ifndef LIRC_IRQ
- #define LIRC_IRQ 7
- #endif
- #ifndef LIRC_PORT
- #define LIRC_PORT 0x378
- #endif
- #ifndef LIRC_TIMER
- #define LIRC_TIMER 65536
- #endif
- /*** Global Variables ***/
- static bool debug;
- static bool check_pselecd;
- static unsigned int irq = LIRC_IRQ;
- static unsigned int io = LIRC_PORT;
- #ifdef LIRC_TIMER
- static unsigned int timer;
- static unsigned int default_timer = LIRC_TIMER;
- #endif
- #define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
- static int rbuf[RBUF_SIZE];
- static DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
- static unsigned int rptr;
- static unsigned int wptr;
- static unsigned int lost_irqs;
- static int is_open;
- static struct parport *pport;
- static struct pardevice *ppdevice;
- static int is_claimed;
- static unsigned int tx_mask = 1;
- /*** Internal Functions ***/
- static unsigned int in(int offset)
- {
- switch (offset) {
- case LIRC_LP_BASE:
- return parport_read_data(pport);
- case LIRC_LP_STATUS:
- return parport_read_status(pport);
- case LIRC_LP_CONTROL:
- return parport_read_control(pport);
- }
- return 0; /* make compiler happy */
- }
- static void out(int offset, int value)
- {
- switch (offset) {
- case LIRC_LP_BASE:
- parport_write_data(pport, value);
- break;
- case LIRC_LP_CONTROL:
- parport_write_control(pport, value);
- break;
- case LIRC_LP_STATUS:
- pr_info("attempt to write to status register\n");
- break;
- }
- }
- static unsigned int lirc_get_timer(void)
- {
- return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
- }
- static unsigned int lirc_get_signal(void)
- {
- return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
- }
- static void lirc_on(void)
- {
- out(LIRC_PORT_DATA, tx_mask);
- }
- static void lirc_off(void)
- {
- out(LIRC_PORT_DATA, 0);
- }
- static unsigned int init_lirc_timer(void)
- {
- struct timeval tv, now;
- unsigned int level, newlevel, timeelapsed, newtimer;
- int count = 0;
- do_gettimeofday(&tv);
- tv.tv_sec++; /* wait max. 1 sec. */
- level = lirc_get_timer();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- count++;
- level = newlevel;
- do_gettimeofday(&now);
- } while (count < 1000 && (now.tv_sec < tv.tv_sec
- || (now.tv_sec == tv.tv_sec
- && now.tv_usec < tv.tv_usec)));
- timeelapsed = (now.tv_sec + 1 - tv.tv_sec)*1000000
- + (now.tv_usec - tv.tv_usec);
- if (count >= 1000 && timeelapsed > 0) {
- if (default_timer == 0) {
- /* autodetect timer */
- newtimer = (1000000*count)/timeelapsed;
- pr_info("%u Hz timer detected\n", newtimer);
- return newtimer;
- }
- newtimer = (1000000*count)/timeelapsed;
- if (abs(newtimer - default_timer) > default_timer/10) {
- /* bad timer */
- pr_notice("bad timer: %u Hz\n", newtimer);
- pr_notice("using default timer: %u Hz\n",
- default_timer);
- return default_timer;
- }
- pr_info("%u Hz timer detected\n", newtimer);
- return newtimer; /* use detected value */
- }
- pr_notice("no timer detected\n");
- return 0;
- }
- static int lirc_claim(void)
- {
- if (parport_claim(ppdevice) != 0) {
- pr_warn("could not claim port\n");
- pr_warn("waiting for port becoming available\n");
- if (parport_claim_or_block(ppdevice) < 0) {
- pr_notice("could not claim port, giving up\n");
- return 0;
- }
- }
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
- is_claimed = 1;
- return 1;
- }
- /*** interrupt handler ***/
- static void rbuf_write(int signal)
- {
- unsigned int nwptr;
- nwptr = (wptr + 1) & (RBUF_SIZE - 1);
- if (nwptr == rptr) {
- /* no new signals will be accepted */
- lost_irqs++;
- pr_notice("buffer overrun\n");
- return;
- }
- rbuf[wptr] = signal;
- wptr = nwptr;
- }
- static void lirc_lirc_irq_handler(void *blah)
- {
- struct timeval tv;
- static struct timeval lasttv;
- static int init;
- long signal;
- int data;
- unsigned int level, newlevel;
- unsigned int timeout;
- if (!is_open)
- return;
- if (!is_claimed)
- return;
- #if 0
- /* disable interrupt */
- disable_irq(irq);
- out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
- #endif
- if (check_pselecd && (in(1) & LP_PSELECD))
- return;
- #ifdef LIRC_TIMER
- if (init) {
- do_gettimeofday(&tv);
- signal = tv.tv_sec - lasttv.tv_sec;
- if (signal > 15)
- /* really long time */
- data = PULSE_MASK;
- else
- data = (int) (signal*1000000 +
- tv.tv_usec - lasttv.tv_usec +
- LIRC_SFH506_DELAY);
- rbuf_write(data); /* space */
- } else {
- if (timer == 0) {
- /*
- * wake up; we'll lose this signal, but it will be
- * garbage if the device is turned on anyway
- */
- timer = init_lirc_timer();
- /* enable_irq(irq); */
- return;
- }
- init = 1;
- }
- timeout = timer/10; /* timeout after 1/10 sec. */
- signal = 1;
- level = lirc_get_timer();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- signal++;
- level = newlevel;
- /* giving up */
- if (signal > timeout
- || (check_pselecd && (in(1) & LP_PSELECD))) {
- signal = 0;
- pr_notice("timeout\n");
- break;
- }
- } while (lirc_get_signal());
- if (signal != 0) {
- /* adjust value to usecs */
- __u64 helper;
- helper = ((__u64) signal)*1000000;
- do_div(helper, timer);
- signal = (long) helper;
- if (signal > LIRC_SFH506_DELAY)
- data = signal - LIRC_SFH506_DELAY;
- else
- data = 1;
- rbuf_write(PULSE_BIT|data); /* pulse */
- }
- do_gettimeofday(&lasttv);
- #else
- /* add your code here */
- #endif
- wake_up_interruptible(&lirc_wait);
- /* enable interrupt */
- /*
- enable_irq(irq);
- out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
- */
- }
- /*** file operations ***/
- static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
- {
- return -ESPIPE;
- }
- static ssize_t lirc_read(struct file *filep, char __user *buf, size_t n,
- loff_t *ppos)
- {
- int result = 0;
- int count = 0;
- DECLARE_WAITQUEUE(wait, current);
- if (n % sizeof(int))
- return -EINVAL;
- add_wait_queue(&lirc_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (count < n) {
- if (rptr != wptr) {
- if (copy_to_user(buf+count, &rbuf[rptr],
- sizeof(int))) {
- result = -EFAULT;
- break;
- }
- rptr = (rptr + 1) & (RBUF_SIZE - 1);
- count += sizeof(int);
- } else {
- if (filep->f_flags & O_NONBLOCK) {
- result = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- result = -ERESTARTSYS;
- break;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- }
- remove_wait_queue(&lirc_wait, &wait);
- set_current_state(TASK_RUNNING);
- return count ? count : result;
- }
- static ssize_t lirc_write(struct file *filep, const char __user *buf, size_t n,
- loff_t *ppos)
- {
- int count;
- unsigned int i;
- unsigned int level, newlevel;
- unsigned long flags;
- int counttimer;
- int *wbuf;
- ssize_t ret;
- if (!is_claimed)
- return -EBUSY;
- count = n / sizeof(int);
- if (n % sizeof(int) || count % 2 == 0)
- return -EINVAL;
- wbuf = memdup_user(buf, n);
- if (IS_ERR(wbuf))
- return PTR_ERR(wbuf);
- #ifdef LIRC_TIMER
- if (timer == 0) {
- /* try again if device is ready */
- timer = init_lirc_timer();
- if (timer == 0) {
- ret = -EIO;
- goto out;
- }
- }
- /* adjust values from usecs */
- for (i = 0; i < count; i++) {
- __u64 helper;
- helper = ((__u64) wbuf[i])*timer;
- do_div(helper, 1000000);
- wbuf[i] = (int) helper;
- }
- local_irq_save(flags);
- i = 0;
- while (i < count) {
- level = lirc_get_timer();
- counttimer = 0;
- lirc_on();
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- counttimer++;
- level = newlevel;
- if (check_pselecd && (in(1) & LP_PSELECD)) {
- lirc_off();
- local_irq_restore(flags);
- ret = -EIO;
- goto out;
- }
- } while (counttimer < wbuf[i]);
- i++;
- lirc_off();
- if (i == count)
- break;
- counttimer = 0;
- do {
- newlevel = lirc_get_timer();
- if (level == 0 && newlevel != 0)
- counttimer++;
- level = newlevel;
- if (check_pselecd && (in(1) & LP_PSELECD)) {
- local_irq_restore(flags);
- ret = -EIO;
- goto out;
- }
- } while (counttimer < wbuf[i]);
- i++;
- }
- local_irq_restore(flags);
- #else
- /* place code that handles write without external timer here */
- #endif
- ret = n;
- out:
- kfree(wbuf);
- return ret;
- }
- static unsigned int lirc_poll(struct file *file, poll_table *wait)
- {
- poll_wait(file, &lirc_wait, wait);
- if (rptr != wptr)
- return POLLIN | POLLRDNORM;
- return 0;
- }
- static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
- {
- int result;
- u32 __user *uptr = (u32 __user *)arg;
- u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
- LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
- u32 mode;
- u32 value;
- switch (cmd) {
- case LIRC_GET_FEATURES:
- result = put_user(features, uptr);
- if (result)
- return result;
- break;
- case LIRC_GET_SEND_MODE:
- result = put_user(LIRC_MODE_PULSE, uptr);
- if (result)
- return result;
- break;
- case LIRC_GET_REC_MODE:
- result = put_user(LIRC_MODE_MODE2, uptr);
- if (result)
- return result;
- break;
- case LIRC_SET_SEND_MODE:
- result = get_user(mode, uptr);
- if (result)
- return result;
- if (mode != LIRC_MODE_PULSE)
- return -EINVAL;
- break;
- case LIRC_SET_REC_MODE:
- result = get_user(mode, uptr);
- if (result)
- return result;
- if (mode != LIRC_MODE_MODE2)
- return -ENOSYS;
- break;
- case LIRC_SET_TRANSMITTER_MASK:
- result = get_user(value, uptr);
- if (result)
- return result;
- if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
- return LIRC_PARALLEL_MAX_TRANSMITTERS;
- tx_mask = value;
- break;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
- }
- static int lirc_open(struct inode *node, struct file *filep)
- {
- if (is_open || !lirc_claim())
- return -EBUSY;
- parport_enable_irq(pport);
- /* init read ptr */
- rptr = 0;
- wptr = 0;
- lost_irqs = 0;
- is_open = 1;
- return 0;
- }
- static int lirc_close(struct inode *node, struct file *filep)
- {
- if (is_claimed) {
- is_claimed = 0;
- parport_release(ppdevice);
- }
- is_open = 0;
- return 0;
- }
- static const struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .llseek = lirc_lseek,
- .read = lirc_read,
- .write = lirc_write,
- .poll = lirc_poll,
- .unlocked_ioctl = lirc_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = lirc_ioctl,
- #endif
- .open = lirc_open,
- .release = lirc_close
- };
- static int set_use_inc(void *data)
- {
- return 0;
- }
- static void set_use_dec(void *data)
- {
- }
- static struct lirc_driver driver = {
- .name = LIRC_DRIVER_NAME,
- .minor = -1,
- .code_length = 1,
- .sample_rate = 0,
- .data = NULL,
- .add_to_buf = NULL,
- .set_use_inc = set_use_inc,
- .set_use_dec = set_use_dec,
- .fops = &lirc_fops,
- .dev = NULL,
- .owner = THIS_MODULE,
- };
- static struct platform_device *lirc_parallel_dev;
- static int lirc_parallel_probe(struct platform_device *dev)
- {
- return 0;
- }
- static int lirc_parallel_remove(struct platform_device *dev)
- {
- return 0;
- }
- static int lirc_parallel_suspend(struct platform_device *dev,
- pm_message_t state)
- {
- return 0;
- }
- static int lirc_parallel_resume(struct platform_device *dev)
- {
- return 0;
- }
- static struct platform_driver lirc_parallel_driver = {
- .probe = lirc_parallel_probe,
- .remove = lirc_parallel_remove,
- .suspend = lirc_parallel_suspend,
- .resume = lirc_parallel_resume,
- .driver = {
- .name = LIRC_DRIVER_NAME,
- },
- };
- static int pf(void *handle)
- {
- parport_disable_irq(pport);
- is_claimed = 0;
- return 0;
- }
- static void kf(void *handle)
- {
- if (!is_open)
- return;
- if (!lirc_claim())
- return;
- parport_enable_irq(pport);
- lirc_off();
- /* this is a bit annoying when you actually print...*/
- /*
- printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
- */
- }
- /*** module initialization and cleanup ***/
- static int __init lirc_parallel_init(void)
- {
- int result;
- result = platform_driver_register(&lirc_parallel_driver);
- if (result) {
- pr_notice("platform_driver_register returned %d\n", result);
- return result;
- }
- lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
- if (!lirc_parallel_dev) {
- result = -ENOMEM;
- goto exit_driver_unregister;
- }
- result = platform_device_add(lirc_parallel_dev);
- if (result)
- goto exit_device_put;
- pport = parport_find_base(io);
- if (pport == NULL) {
- pr_notice("no port at %x found\n", io);
- result = -ENXIO;
- goto exit_device_put;
- }
- ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
- pf, kf, lirc_lirc_irq_handler, 0,
- NULL);
- parport_put_port(pport);
- if (ppdevice == NULL) {
- pr_notice("parport_register_device() failed\n");
- result = -ENXIO;
- goto exit_device_put;
- }
- if (parport_claim(ppdevice) != 0)
- goto skip_init;
- is_claimed = 1;
- out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
- #ifdef LIRC_TIMER
- if (debug)
- out(LIRC_PORT_DATA, tx_mask);
- timer = init_lirc_timer();
- #if 0 /* continue even if device is offline */
- if (timer == 0) {
- is_claimed = 0;
- parport_release(pport);
- parport_unregister_device(ppdevice);
- result = -EIO;
- goto exit_device_put;
- }
- #endif
- if (debug)
- out(LIRC_PORT_DATA, 0);
- #endif
- is_claimed = 0;
- parport_release(ppdevice);
- skip_init:
- driver.dev = &lirc_parallel_dev->dev;
- driver.minor = lirc_register_driver(&driver);
- if (driver.minor < 0) {
- pr_notice("register_chrdev() failed\n");
- parport_unregister_device(ppdevice);
- result = -EIO;
- goto exit_device_put;
- }
- pr_info("installed using port 0x%04x irq %d\n", io, irq);
- return 0;
- exit_device_put:
- platform_device_put(lirc_parallel_dev);
- exit_driver_unregister:
- platform_driver_unregister(&lirc_parallel_driver);
- return result;
- }
- static void __exit lirc_parallel_exit(void)
- {
- parport_unregister_device(ppdevice);
- lirc_unregister_driver(driver.minor);
- platform_device_unregister(lirc_parallel_dev);
- platform_driver_unregister(&lirc_parallel_driver);
- }
- module_init(lirc_parallel_init);
- module_exit(lirc_parallel_exit);
- MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
- MODULE_AUTHOR("Christoph Bartelmus");
- MODULE_LICENSE("GPL");
- module_param(io, int, S_IRUGO);
- MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
- module_param(irq, int, S_IRUGO);
- MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
- module_param(tx_mask, int, S_IRUGO);
- MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
- module_param(debug, bool, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(debug, "Enable debugging messages");
- module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(check_pselecd, "Check for printer (default: 0)");
|