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