lirc_parallel.c revision 0f9313ad068af4156109661fb8e94ee7fcb79001
1805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*
2805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson * lirc_parallel.c
3805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
4805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson * lirc_parallel - device driver for infra-red signal receiving and
5805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *                 transmitting unit built by the author
6805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
7805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
8805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
9805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  This program is free software; you can redistribute it and/or modify
10805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  it under the terms of the GNU General Public License as published by
11805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  the Free Software Foundation; either version 2 of the License, or
12805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  (at your option) any later version.
13805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
14805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  This program is distributed in the hope that it will be useful,
15805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  GNU General Public License for more details.
18805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
19805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  You should have received a copy of the GNU General Public License
20805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  along with this program; if not, write to the Free Software
21805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson *
23805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson */
24805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
25805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** Includes ***/
26805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
27805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifdef CONFIG_SMP
28805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#error "--- Sorry, this driver is not SMP safe. ---"
29805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
30805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
31805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/module.h>
32805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/sched.h>
33805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/errno.h>
34805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/signal.h>
35805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/fs.h>
36805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/kernel.h>
37805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/ioport.h>
38805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/time.h>
39805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/mm.h>
40805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/delay.h>
41805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
42805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/io.h>
43805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/signal.h>
44805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/irq.h>
45805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/uaccess.h>
46805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <asm/div64.h>
47805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
48805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/poll.h>
49805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <linux/parport.h>
50805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
51805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <media/lirc.h>
52805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include <media/lirc_dev.h>
53805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
54805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#include "lirc_parallel.h"
55805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
56805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#define LIRC_DRIVER_NAME "lirc_parallel"
57805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
58805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifndef LIRC_IRQ
59805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#define LIRC_IRQ 7
60805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
61805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifndef LIRC_PORT
62805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#define LIRC_PORT 0x378
63805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
64805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifndef LIRC_TIMER
65805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#define LIRC_TIMER 65536
66805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
67805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
68805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** Global Variables ***/
69805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
70805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int debug;
71805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int check_pselecd;
72805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
73805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int irq = LIRC_IRQ;
74805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int io = LIRC_PORT;
75805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifdef LIRC_TIMER
76805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int timer;
77805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int default_timer = LIRC_TIMER;
78805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
79805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
80805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
81805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
82805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int rbuf[RBUF_SIZE];
83805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
84805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonDECLARE_WAIT_QUEUE_HEAD(lirc_wait);
85805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
86805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int rptr;
87805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int wptr;
88805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int lost_irqs;
89805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonint is_open;
90805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
91805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstruct parport *pport;
92805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstruct pardevice *ppdevice;
93805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonint is_claimed;
94805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
95805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonunsigned int tx_mask = 1;
96805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
97805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** Internal Functions ***/
98805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
99805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic unsigned int in(int offset)
100805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
101805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	switch (offset) {
102805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_BASE:
103805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return parport_read_data(pport);
104805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_STATUS:
105805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return parport_read_status(pport);
106805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_CONTROL:
107805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return parport_read_control(pport);
108805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
109805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0; /* make compiler happy */
110805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
111805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
112805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void out(int offset, int value)
113805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
114805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	switch (offset) {
115805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_BASE:
116805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_write_data(pport, value);
117805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
118805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_CONTROL:
119805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_write_control(pport, value);
120805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
121805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_LP_STATUS:
122805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_INFO "%s: attempt to write to status register\n",
123805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME);
124805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
125805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
126805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
127805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
128805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic unsigned int lirc_get_timer(void)
129805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
130805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
131805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
132805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
133805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic unsigned int lirc_get_signal(void)
134805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
135805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
136805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
137805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
138805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void lirc_on(void)
139805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
140805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	out(LIRC_PORT_DATA, tx_mask);
141805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
142805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
143805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void lirc_off(void)
144805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
145805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	out(LIRC_PORT_DATA, 0);
146805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
147805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
148805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic unsigned int init_lirc_timer(void)
149805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
150805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	struct timeval tv, now;
151805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int level, newlevel, timeelapsed, newtimer;
152805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int count = 0;
153805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
154805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	do_gettimeofday(&tv);
155805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	tv.tv_sec++;                     /* wait max. 1 sec. */
156805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	level = lirc_get_timer();
157805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	do {
158805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		newlevel = lirc_get_timer();
159805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (level == 0 && newlevel != 0)
160805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			count++;
161805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		level = newlevel;
162805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do_gettimeofday(&now);
163805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	} while (count < 1000 && (now.tv_sec < tv.tv_sec
164805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			     || (now.tv_sec == tv.tv_sec
165805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				 && now.tv_usec < tv.tv_usec)));
166805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
167805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
168805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		     + (now.tv_usec - tv.tv_usec));
169805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (count >= 1000 && timeelapsed > 0) {
170805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (default_timer == 0) {
171805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			/* autodetect timer */
172805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			newtimer = (1000000*count)/timeelapsed;
173805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			printk(KERN_INFO "%s: %u Hz timer detected\n",
174805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			       LIRC_DRIVER_NAME, newtimer);
175805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return newtimer;
176805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}  else {
177805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			newtimer = (1000000*count)/timeelapsed;
178805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (abs(newtimer - default_timer) > default_timer/10) {
179805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				/* bad timer */
180805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
181805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				       LIRC_DRIVER_NAME, newtimer);
182805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				printk(KERN_NOTICE "%s: using default timer: "
183805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				       "%u Hz\n",
184805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				       LIRC_DRIVER_NAME, default_timer);
185805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				return default_timer;
186805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			} else {
187805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				printk(KERN_INFO "%s: %u Hz timer detected\n",
188805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				       LIRC_DRIVER_NAME, newtimer);
189805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				return newtimer; /* use detected value */
190805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
191805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}
192805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	} else {
193805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
194805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return 0;
195805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
196805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
197805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
198805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int lirc_claim(void)
199805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
200805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (parport_claim(ppdevice) != 0) {
201805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_WARNING "%s: could not claim port\n",
202805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME);
203805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_WARNING "%s: waiting for port becoming available"
204805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       "\n", LIRC_DRIVER_NAME);
205805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (parport_claim_or_block(ppdevice) < 0) {
206805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			printk(KERN_NOTICE "%s: could not claim port, giving"
207805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			       " up\n", LIRC_DRIVER_NAME);
208805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return 0;
209805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}
210805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
211805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
212805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_claimed = 1;
213805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 1;
214805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
215805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
216805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** interrupt handler ***/
217805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
218805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void rbuf_write(int signal)
219805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
220805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int nwptr;
221805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
222805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	nwptr = (wptr + 1) & (RBUF_SIZE - 1);
223805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (nwptr == rptr) {
224805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		/* no new signals will be accepted */
225805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		lost_irqs++;
226805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
227805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
228805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
229805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	rbuf[wptr] = signal;
230805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	wptr = nwptr;
231805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
232805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
233805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void irq_handler(void *blah)
234805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
235805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	struct timeval tv;
236805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	static struct timeval lasttv;
237805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	static int init;
238805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	long signal;
239805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int data;
240805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int level, newlevel;
241805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int timeout;
242805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
243805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!module_refcount(THIS_MODULE))
244805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
245805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
246805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!is_claimed)
247805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
248805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
249805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#if 0
250805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* disable interrupt */
251805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	  disable_irq(irq);
252805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
253805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
254805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (check_pselecd && (in(1) & LP_PSELECD))
255805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
256805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
257805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifdef LIRC_TIMER
258805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (init) {
259805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do_gettimeofday(&tv);
260805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
261805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		signal = tv.tv_sec - lasttv.tv_sec;
262805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (signal > 15)
263805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			/* really long time */
264805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			data = PULSE_MASK;
265805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		else
266805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			data = (int) (signal*1000000 +
267805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson					 tv.tv_usec - lasttv.tv_usec +
268805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson					 LIRC_SFH506_DELAY);
269805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
270805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		rbuf_write(data); /* space */
271805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	} else {
272805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (timer == 0) {
273805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			/*
274805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			 * wake up; we'll lose this signal, but it will be
275805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			 * garbage if the device is turned on anyway
276805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			 */
277805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			timer = init_lirc_timer();
278805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			/* enable_irq(irq); */
279805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return;
280805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}
281805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		init = 1;
282805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
283805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
284805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	timeout = timer/10;	/* timeout after 1/10 sec. */
285805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	signal = 1;
286805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	level = lirc_get_timer();
287805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	do {
288805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		newlevel = lirc_get_timer();
289805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (level == 0 && newlevel != 0)
290805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			signal++;
291805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		level = newlevel;
292805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
293805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		/* giving up */
294805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (signal > timeout
295805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		    || (check_pselecd && (in(1) & LP_PSELECD))) {
296805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			signal = 0;
297805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
298805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			break;
299805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}
300805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	} while (lirc_get_signal());
301805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
302805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (signal != 0) {
303805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		/* ajust value to usecs */
304805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		unsigned long long helper;
305805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
306805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		helper = ((unsigned long long) signal)*1000000;
307805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do_div(helper, timer);
308805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		signal = (long) helper;
309805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
310805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (signal > LIRC_SFH506_DELAY)
311805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			data = signal - LIRC_SFH506_DELAY;
312805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		else
313805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			data = 1;
314805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		rbuf_write(PULSE_BIT|data); /* pulse */
315805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
316805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	do_gettimeofday(&lasttv);
317805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#else
318805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* add your code here */
319805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
320805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
321805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	wake_up_interruptible(&lirc_wait);
322805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
323805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* enable interrupt */
324805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/*
325805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	  enable_irq(irq);
326805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	  out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
327805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	*/
328805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
329805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
330805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** file operations ***/
331805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
332805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
333805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
334805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return -ESPIPE;
335805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
336805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
337805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
338805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
339805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int result = 0;
340805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int count = 0;
341805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	DECLARE_WAITQUEUE(wait, current);
342805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
343805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (n % sizeof(int))
344805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EINVAL;
345805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
346805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	add_wait_queue(&lirc_wait, &wait);
347805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	set_current_state(TASK_INTERRUPTIBLE);
348805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	while (count < n) {
349805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (rptr != wptr) {
350805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (copy_to_user(buf+count, (char *) &rbuf[rptr],
351805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson					 sizeof(int))) {
352805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				result = -EFAULT;
353805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				break;
354805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
355805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			rptr = (rptr + 1) & (RBUF_SIZE - 1);
356805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			count += sizeof(int);
357805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		} else {
358805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (filep->f_flags & O_NONBLOCK) {
359805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				result = -EAGAIN;
360805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				break;
361805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
362805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (signal_pending(current)) {
363805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				result = -ERESTARTSYS;
364805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				break;
365805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
366805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			schedule();
367805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			set_current_state(TASK_INTERRUPTIBLE);
368805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		}
369805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
370805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	remove_wait_queue(&lirc_wait, &wait);
371805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	set_current_state(TASK_RUNNING);
372805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return count ? count : result;
373805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
374805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
375805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
376805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			  loff_t *ppos)
377805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
378805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int count;
379805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int i;
380805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int level, newlevel;
381805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned long flags;
382805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int counttimer;
383805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int *wbuf;
384805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
385805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!is_claimed)
386805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EBUSY;
387805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
388805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	count = n / sizeof(int);
389805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
390805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (n % sizeof(int) || count % 2 == 0)
391805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EINVAL;
392805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
393805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	wbuf = memdup_user(buf, n);
394805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (IS_ERR(wbuf))
395805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return PTR_ERR(wbuf);
396805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
397805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifdef LIRC_TIMER
398805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (timer == 0) {
399805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		/* try again if device is ready */
400805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		timer = init_lirc_timer();
401805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (timer == 0)
402805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return -EIO;
403805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
404805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
405805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* adjust values from usecs */
406805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	for (i = 0; i < count; i++) {
407805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		unsigned long long helper;
408805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
409805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		helper = ((unsigned long long) wbuf[i])*timer;
410805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do_div(helper, 1000000);
411805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		wbuf[i] = (int) helper;
412805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
413805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
414805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	local_irq_save(flags);
415805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	i = 0;
416805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	while (i < count) {
417805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		level = lirc_get_timer();
418805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		counttimer = 0;
419805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		lirc_on();
420805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do {
421805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			newlevel = lirc_get_timer();
422805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (level == 0 && newlevel != 0)
423805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				counttimer++;
424805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			level = newlevel;
425805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (check_pselecd && (in(1) & LP_PSELECD)) {
426805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				lirc_off();
427805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				local_irq_restore(flags);
428805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				return -EIO;
429805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
430805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		} while (counttimer < wbuf[i]);
431805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		i++;
432805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
433805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		lirc_off();
434805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (i == count)
435805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			break;
436805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		counttimer = 0;
437805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		do {
438805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			newlevel = lirc_get_timer();
439805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (level == 0 && newlevel != 0)
440805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				counttimer++;
441805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			level = newlevel;
442805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			if (check_pselecd && (in(1) & LP_PSELECD)) {
443805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				local_irq_restore(flags);
444805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				return -EIO;
445805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			}
446805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		} while (counttimer < wbuf[i]);
447805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		i++;
448805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
449805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	local_irq_restore(flags);
450805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#else
451805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* place code that handles write without external timer here */
452805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
453805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return n;
454805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
455805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
456805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic unsigned int lirc_poll(struct file *file, poll_table *wait)
457805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
458805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	poll_wait(file, &lirc_wait, wait);
459805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (rptr != wptr)
460805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return POLLIN | POLLRDNORM;
461805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
462805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
463805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
464805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
465805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
466805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	int result;
467805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
468805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
469805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned long mode;
470805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	unsigned int ivalue;
471805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
472805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	switch (cmd) {
473805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_GET_FEATURES:
474805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = put_user(features, (unsigned long *) arg);
475805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
476805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
477805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
478805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_GET_SEND_MODE:
479805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
480805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
481805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
482805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
483805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_GET_REC_MODE:
484805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
485805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
486805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
487805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
488805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_SET_SEND_MODE:
489805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = get_user(mode, (unsigned long *) arg);
490805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
491805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
492805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (mode != LIRC_MODE_PULSE)
493805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return -EINVAL;
494805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
495805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_SET_REC_MODE:
496805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = get_user(mode, (unsigned long *) arg);
497805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
498805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
499805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (mode != LIRC_MODE_MODE2)
500805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return -ENOSYS;
501805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
502805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	case LIRC_SET_TRANSMITTER_MASK:
503805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		result = get_user(ivalue, (unsigned int *) arg);
504805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if (result)
505805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return result;
506805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
507805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson			return LIRC_PARALLEL_MAX_TRANSMITTERS;
508805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		tx_mask = ivalue;
509805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		break;
510805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	default:
511805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -ENOIOCTLCMD;
512805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
513805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
514805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
515805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
516805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int lirc_open(struct inode *node, struct file *filep)
517805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
518805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (module_refcount(THIS_MODULE) || !lirc_claim())
519805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EBUSY;
520805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
521805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_enable_irq(pport);
522805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
523805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* init read ptr */
524805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	rptr = 0;
525805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	wptr = 0;
526805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	lost_irqs = 0;
527805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
528805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_open = 1;
529805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
530805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
531805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
532805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int lirc_close(struct inode *node, struct file *filep)
533805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
534805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (is_claimed) {
535805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		is_claimed = 0;
536805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_release(ppdevice);
537805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
538805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_open = 0;
539805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
540805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
541805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
5420f9313ad068af4156109661fb8e94ee7fcb79001Mauro Carvalho Chehabstatic const struct file_operations lirc_fops = {
543805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.owner		= THIS_MODULE,
544805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.llseek		= lirc_lseek,
545805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.read		= lirc_read,
546805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.write		= lirc_write,
547805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.poll		= lirc_poll,
548805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.unlocked_ioctl	= lirc_ioctl,
549805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.open		= lirc_open,
550805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	.release	= lirc_close
551805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson};
552805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
553805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int set_use_inc(void *data)
554805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
555805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
556805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
557805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
558805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void set_use_dec(void *data)
559805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
560805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
561805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
562805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic struct lirc_driver driver = {
563805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .name		= LIRC_DRIVER_NAME,
564805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .minor		= -1,
565805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .code_length	= 1,
566805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .sample_rate	= 0,
567805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .data		= NULL,
568805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .add_to_buf	= NULL,
569805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .set_use_inc	= set_use_inc,
570805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .set_use_dec	= set_use_dec,
571805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .fops		= &lirc_fops,
572805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .dev		= NULL,
573805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson       .owner		= THIS_MODULE,
574805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson};
575805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
576805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int pf(void *handle);
577805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void kf(void *handle);
578805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
579805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic struct timer_list poll_timer;
580805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void poll_state(unsigned long ignored);
581805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
582805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void poll_state(unsigned long ignored)
583805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
584805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	printk(KERN_NOTICE "%s: time\n",
585805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	       LIRC_DRIVER_NAME);
586805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	del_timer(&poll_timer);
587805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (is_claimed)
588805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
589805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	kf(NULL);
590805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!is_claimed) {
591805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: could not claim port, giving up\n",
592805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME);
593805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		init_timer(&poll_timer);
594805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		poll_timer.expires = jiffies + HZ;
595805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		poll_timer.data = (unsigned long)current;
596805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		poll_timer.function = poll_state;
597805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		add_timer(&poll_timer);
598805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
599805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
600805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
601805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int pf(void *handle)
602805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
603805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_disable_irq(pport);
604805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_claimed = 0;
605805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
606805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
607805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
608805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void kf(void *handle)
609805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
610805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!is_open)
611805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
612805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (!lirc_claim())
613805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return;
614805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_enable_irq(pport);
615805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	lirc_off();
616805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/* this is a bit annoying when you actually print...*/
617805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	/*
618805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
619805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	*/
620805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
621805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
622805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson/*** module initialization and cleanup ***/
623805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
624805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic int __init lirc_parallel_init(void)
625805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
626805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	pport = parport_find_base(io);
627805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (pport == NULL) {
628805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: no port at %x found\n",
629805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME, io);
630805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -ENXIO;
631805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
632805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
633805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson					   pf, kf, irq_handler, 0, NULL);
634805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_put_port(pport);
635805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (ppdevice == NULL) {
636805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: parport_register_device() failed\n",
637805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME);
638805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -ENXIO;
639805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
640805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (parport_claim(ppdevice) != 0)
641805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		goto skip_init;
642805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_claimed = 1;
643805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
644805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
645805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#ifdef LIRC_TIMER
646805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (debug)
647805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		out(LIRC_PORT_DATA, tx_mask);
648805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
649805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	timer = init_lirc_timer();
650805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
651805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#if 0	/* continue even if device is offline */
652805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (timer == 0) {
653805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		is_claimed = 0;
654805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_release(pport);
655805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_unregister_device(ppdevice);
656805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EIO;
657805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
658805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
659805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
660805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (debug)
661805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		out(LIRC_PORT_DATA, 0);
662805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson#endif
663805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
664805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	is_claimed = 0;
665805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_release(ppdevice);
666805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson skip_init:
667805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	driver.minor = lirc_register_driver(&driver);
668805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	if (driver.minor < 0) {
669805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		printk(KERN_NOTICE "%s: register_chrdev() failed\n",
670805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		       LIRC_DRIVER_NAME);
671805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		parport_unregister_device(ppdevice);
672805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson		return -EIO;
673805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	}
674805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
675805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	       LIRC_DRIVER_NAME, io, irq);
676805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	return 0;
677805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
678805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
679805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonstatic void __exit lirc_parallel_exit(void)
680805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson{
681805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	parport_unregister_device(ppdevice);
682805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson	lirc_unregister_driver(driver.minor);
683805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson}
684805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
685805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_init(lirc_parallel_init);
686805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_exit(lirc_parallel_exit);
687805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
688805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
689805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_AUTHOR("Christoph Bartelmus");
690805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_LICENSE("GPL");
691805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
692805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_param(io, int, S_IRUGO);
693805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
694805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
695805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_param(irq, int, S_IRUGO);
696805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
697805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
698805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_param(tx_mask, int, S_IRUGO);
699805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
700805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
701805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_param(debug, bool, S_IRUGO | S_IWUSR);
702805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_PARM_DESC(debug, "Enable debugging messages");
703805a8966659563df68ea7bbd94241dafd645c725Jarod Wilson
704805a8966659563df68ea7bbd94241dafd645c725Jarod Wilsonmodule_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
705805a8966659563df68ea7bbd94241dafd645c725Jarod WilsonMODULE_PARM_DESC(debug, "Check for printer (default: 0)");
706