sir_ir.c 9.9 KB
Newer Older
1
/*
2
 * IR SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
3
 *
4
 * sir_ir - Device driver for use with SIR (serial infra red)
5 6 7 8 9 10 11 12
 * mode of IrDA on many notebooks.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 */

13 14
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

15 16 17 18
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/serial_reg.h>
19
#include <linux/ktime.h>
20
#include <linux/delay.h>
21
#include <linux/platform_device.h>
22

23
#include <media/rc-core.h>
24 25 26 27 28

/* SECTION: Definitions */
#define PULSE '['

/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
29
#define TIME_CONST (9000000ul / 115200ul)
30 31

/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
32
#define SIR_TIMEOUT	(HZ * 5 / 100)
33 34

/* onboard sir ports are typically com3 */
35 36
static int io = 0x3e8;
static int irq = 4;
37 38 39 40 41
static int threshold = 3;

static DEFINE_SPINLOCK(timer_lock);
static struct timer_list timerlist;
/* time of last signal change detected */
42
static ktime_t last;
43
/* time of last UART data ready interrupt */
44
static ktime_t last_intr_time;
45
static int last_value;
46
static struct rc_dev *rcdev;
47

48
static struct platform_device *sir_ir_dev;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

static DEFINE_SPINLOCK(hardware_lock);

/* SECTION: Prototypes */

/* Communication with user-space */
static void add_read_queue(int flag, unsigned long val);
static int init_chrdev(void);
/* Hardware */
static irqreturn_t sir_interrupt(int irq, void *dev_id);
static void send_space(unsigned long len);
static void send_pulse(unsigned long len);
static int init_hardware(void);
static void drop_hardware(void);
/* Initialisation */
static int init_port(void);
static void drop_port(void);

static inline unsigned int sinp(int offset)
{
	return inb(io + offset);
}

static inline void soutp(int offset, int value)
{
	outb(value, io + offset);
}

/* SECTION: Communication with user-space */
78 79
static int sir_tx_ir(struct rc_dev *dev, unsigned int *tx_buf,
		     unsigned int count)
80 81
{
	unsigned long flags;
82 83
	int i;

84
	local_irq_save(flags);
85
	for (i = 0; i < count;) {
86 87 88 89 90 91 92 93 94 95 96
		if (tx_buf[i])
			send_pulse(tx_buf[i]);
		i++;
		if (i >= count)
			break;
		if (tx_buf[i])
			send_space(tx_buf[i]);
		i++;
	}
	local_irq_restore(flags);

97
	return count;
98 99 100 101
}

static void add_read_queue(int flag, unsigned long val)
{
102
	DEFINE_IR_RAW_EVENT(ev);
103

104
	pr_debug("add flag %d with val %lu\n", flag, val);
105 106 107 108 109 110 111

	/*
	 * statistically, pulses are ~TIME_CONST/2 too long. we could
	 * maybe make this more exact, but this is good enough
	 */
	if (flag) {
		/* pulse */
112 113
		if (val > TIME_CONST / 2)
			val -= TIME_CONST / 2;
114
		else /* should not ever happen */
115 116
			val = 1;
		ev.pulse = true;
117
	} else {
118
		val += TIME_CONST / 2;
119
	}
120
	ev.duration = US_TO_NS(val);
121

122
	ir_raw_event_store_with_filter(rcdev, &ev);
123 124 125 126
}

static int init_chrdev(void)
{
127 128 129 130
	rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW);
	if (!rcdev)
		return -ENOMEM;

131
	rcdev->input_name = "SIR IrDA port";
132 133 134 135 136 137 138
	rcdev->input_phys = KBUILD_MODNAME "/input0";
	rcdev->input_id.bustype = BUS_HOST;
	rcdev->input_id.vendor = 0x0001;
	rcdev->input_id.product = 0x0001;
	rcdev->input_id.version = 0x0100;
	rcdev->tx_ir = sir_tx_ir;
	rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
139
	rcdev->driver_name = KBUILD_MODNAME;
140 141 142 143 144
	rcdev->map_name = RC_MAP_RC6_MCE;
	rcdev->timeout = IR_DEFAULT_TIMEOUT;
	rcdev->dev.parent = &sir_ir_dev->dev;

	return devm_rc_register_device(&sir_ir_dev->dev, rcdev);
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
}

/* SECTION: Hardware */
static void sir_timeout(unsigned long data)
{
	/*
	 * if last received signal was a pulse, but receiving stopped
	 * within the 9 bit frame, we need to finish this pulse and
	 * simulate a signal change to from pulse to space. Otherwise
	 * upper layers will receive two sequences next time.
	 */

	unsigned long flags;
	unsigned long pulse_end;

	/* avoid interference with interrupt */
	spin_lock_irqsave(&timer_lock, flags);
	if (last_value) {
		/* clear unread bits in UART and restart */
		outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
		/* determine 'virtual' pulse end: */
166 167
		pulse_end = min_t(unsigned long,
				  ktime_us_delta(last, last_intr_time),
168 169 170
				  IR_MAX_DURATION);
		dev_dbg(&sir_ir_dev->dev, "timeout add %d for %lu usec\n",
			last_value, pulse_end);
171 172
		add_read_queue(last_value, pulse_end);
		last_value = 0;
173
		last = last_intr_time;
174 175
	}
	spin_unlock_irqrestore(&timer_lock, flags);
176
	ir_raw_event_handle(rcdev);
177 178 179 180 181
}

static irqreturn_t sir_interrupt(int irq, void *dev_id)
{
	unsigned char data;
182 183 184
	ktime_t curr_time;
	static unsigned long delt;
	unsigned long deltintr;
185 186 187 188
	unsigned long flags;
	int iir, lsr;

	while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
189
		switch (iir & UART_IIR_ID) { /* FIXME toto treba preriedit */
190
		case UART_IIR_MSI:
191
			(void)inb(io + UART_MSR);
192 193 194
			break;
		case UART_IIR_RLSI:
		case UART_IIR_THRI:
195
			(void)inb(io + UART_LSR);
196 197 198 199 200 201 202
			break;
		case UART_IIR_RDI:
			/* avoid interference with timer */
			spin_lock_irqsave(&timer_lock, flags);
			do {
				del_timer(&timerlist);
				data = inb(io + UART_RX);
203 204 205
				curr_time = ktime_get();
				delt = min_t(unsigned long,
					     ktime_us_delta(last, curr_time),
206
					     IR_MAX_DURATION);
207 208 209
				deltintr = min_t(unsigned long,
						 ktime_us_delta(last_intr_time,
								curr_time),
210 211 212
						 IR_MAX_DURATION);
				dev_dbg(&sir_ir_dev->dev, "t %lu, d %d\n",
					deltintr, (int)data);
213 214 215 216
				/*
				 * if nothing came in last X cycles,
				 * it was gap
				 */
217
				if (deltintr > TIME_CONST * threshold) {
218
					if (last_value) {
219
						dev_dbg(&sir_ir_dev->dev, "GAP\n");
220 221
						/* simulate signal change */
						add_read_queue(last_value,
222 223
							       delt -
							       deltintr);
224
						last_value = 0;
225 226
						last = last_intr_time;
						delt = deltintr;
227 228 229 230 231
					}
				}
				data = 1;
				if (data ^ last_value) {
					/*
232
					 * deltintr > 2*TIME_CONST, remember?
233 234 235
					 * the other case is timeout
					 */
					add_read_queue(last_value,
236
						       delt - TIME_CONST);
237
					last_value = data;
238 239 240
					last = curr_time;
					last = ktime_sub_us(last,
							    TIME_CONST);
241
				}
242
				last_intr_time = curr_time;
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
				if (data) {
					/*
					 * start timer for end of
					 * sequence detection
					 */
					timerlist.expires = jiffies +
								SIR_TIMEOUT;
					add_timer(&timerlist);
				}

				lsr = inb(io + UART_LSR);
			} while (lsr & UART_LSR_DR); /* data ready */
			spin_unlock_irqrestore(&timer_lock, flags);
			break;
		default:
			break;
		}
	}
261
	ir_raw_event_handle(rcdev);
262 263 264 265 266
	return IRQ_RETVAL(IRQ_HANDLED);
}

static void send_space(unsigned long len)
{
267
	usleep_range(len, len + 25);
268 269 270 271 272 273
}

static void send_pulse(unsigned long len)
{
	long bytes_out = len / TIME_CONST;

274
	if (bytes_out == 0)
275
		bytes_out++;
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
	while (bytes_out--) {
		outb(PULSE, io + UART_TX);
		/* FIXME treba seriozne cakanie z char/serial.c */
		while (!(inb(io + UART_LSR) & UART_LSR_THRE))
			;
	}
}

static int init_hardware(void)
{
	unsigned long flags;

	spin_lock_irqsave(&hardware_lock, flags);
	/* reset UART */
	outb(0, io + UART_MCR);
	outb(0, io + UART_IER);
	/* init UART */
	/* set DLAB, speed = 115200 */
	outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
	outb(1, io + UART_DLL); outb(0, io + UART_DLM);
	/* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
	outb(UART_LCR_WLEN7, io + UART_LCR);
	/* FIFO operation */
	outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
	/* interrupts */
	/* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
	outb(UART_IER_RDI, io + UART_IER);
	/* turn on UART */
305
	outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR);
306 307 308 309 310 311 312 313 314 315 316 317
	spin_unlock_irqrestore(&hardware_lock, flags);
	return 0;
}

static void drop_hardware(void)
{
	unsigned long flags;

	spin_lock_irqsave(&hardware_lock, flags);

	/* turn off interrupts */
	outb(0, io + UART_IER);
318

319 320 321 322 323 324 325 326 327
	spin_unlock_irqrestore(&hardware_lock, flags);
}

/* SECTION: Initialisation */

static int init_port(void)
{
	int retval;

328 329
	setup_timer(&timerlist, sir_timeout, 0);

330
	/* get I/O port access and IRQ line */
331
	if (!request_region(io, 8, KBUILD_MODNAME)) {
332
		pr_err("i/o port 0x%.4x already in use.\n", io);
333 334
		return -EBUSY;
	}
Y
Yong Zhang 已提交
335
	retval = request_irq(irq, sir_interrupt, 0,
336
			     KBUILD_MODNAME, NULL);
337 338
	if (retval < 0) {
		release_region(io, 8);
339
		pr_err("IRQ %d already in use.\n", irq);
340 341
		return retval;
	}
342
	pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq);
343 344 345 346 347 348 349 350 351 352 353

	return 0;
}

static void drop_port(void)
{
	free_irq(irq, NULL);
	del_timer_sync(&timerlist);
	release_region(io, 8);
}

354
static int init_sir_ir(void)
355 356 357 358 359 360 361 362 363 364
{
	int retval;

	retval = init_port();
	if (retval < 0)
		return retval;
	init_hardware();
	return 0;
}

365
static int sir_ir_probe(struct platform_device *dev)
366
{
367 368 369 370 371 372 373
	int retval;

	retval = init_chrdev();
	if (retval < 0)
		return retval;

	return init_sir_ir();
374 375
}

376
static int sir_ir_remove(struct platform_device *dev)
377 378 379 380
{
	return 0;
}

381 382 383
static struct platform_driver sir_ir_driver = {
	.probe		= sir_ir_probe,
	.remove		= sir_ir_remove,
384
	.driver		= {
385
		.name	= "sir_ir",
386 387
	},
};
388

389
static int __init sir_ir_init(void)
390 391 392
{
	int retval;

393
	retval = platform_driver_register(&sir_ir_driver);
394 395
	if (retval)
		return retval;
396

397 398
	sir_ir_dev = platform_device_alloc("sir_ir", 0);
	if (!sir_ir_dev) {
399 400 401 402
		retval = -ENOMEM;
		goto pdev_alloc_fail;
	}

403
	retval = platform_device_add(sir_ir_dev);
404
	if (retval)
405 406
		goto pdev_add_fail;

407
	return 0;
408 409

pdev_add_fail:
410
	platform_device_put(sir_ir_dev);
411
pdev_alloc_fail:
412
	platform_driver_unregister(&sir_ir_driver);
413
	return retval;
414 415
}

416
static void __exit sir_ir_exit(void)
417 418 419
{
	drop_hardware();
	drop_port();
420 421
	platform_device_unregister(sir_ir_dev);
	platform_driver_unregister(&sir_ir_driver);
422 423
}

424 425
module_init(sir_ir_init);
module_exit(sir_ir_exit);
426 427 428 429 430

MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
MODULE_AUTHOR("Milan Pikula");
MODULE_LICENSE("GPL");

431
module_param(io, int, 0444);
432 433
MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");

434
module_param(irq, int, 0444);
435 436
MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");

437
module_param(threshold, int, 0444);
438
MODULE_PARM_DESC(threshold, "space detection threshold (3)");