linux/drivers/media/rc/sir_ir.c
<<
>>
Prefs
   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 * mode of IrDA on many notebooks.
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License as published by
   9 *  the Free Software Foundation; either version 2 of the License, or
  10 *  (at your option) any later version.
  11 */
  12
  13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14
  15#include <linux/module.h>
  16#include <linux/interrupt.h>
  17#include <linux/kernel.h>
  18#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/* SECTION: Definitions */
  26#define PULSE '['
  27
  28/* 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 */
  35static int io = 0x3e8;
  36static int irq = 4;
  37static int threshold = 3;
  38
  39static DEFINE_SPINLOCK(timer_lock);
  40static struct timer_list timerlist;
  41/* time of last signal change detected */
  42static ktime_t last;
  43/* time of last UART data ready interrupt */
  44static ktime_t last_intr_time;
  45static int last_value;
  46static struct rc_dev *rcdev;
  47
  48static struct platform_device *sir_ir_dev;
  49
  50static DEFINE_SPINLOCK(hardware_lock);
  51
  52/* SECTION: Prototypes */
  53
  54/* Communication with user-space */
  55static void add_read_queue(int flag, unsigned long val);
  56static int init_chrdev(void);
  57/* Hardware */
  58static irqreturn_t sir_interrupt(int irq, void *dev_id);
  59static void send_space(unsigned long len);
  60static void send_pulse(unsigned long len);
  61static int init_hardware(void);
  62static void drop_hardware(void);
  63/* Initialisation */
  64static int init_port(void);
  65static void drop_port(void);
  66
  67static inline unsigned int sinp(int offset)
  68{
  69        return inb(io + offset);
  70}
  71
  72static inline void soutp(int offset, int value)
  73{
  74        outb(value, io + offset);
  75}
  76
  77/* SECTION: Communication with user-space */
  78static int sir_tx_ir(struct rc_dev *dev, unsigned int *tx_buf,
  79                     unsigned int count)
  80{
  81        unsigned long flags;
  82        int i;
  83
  84        local_irq_save(flags);
  85        for (i = 0; i < count;) {
  86                if (tx_buf[i])
  87                        send_pulse(tx_buf[i]);
  88                i++;
  89                if (i >= count)
  90                        break;
  91                if (tx_buf[i])
  92                        send_space(tx_buf[i]);
  93                i++;
  94        }
  95        local_irq_restore(flags);
  96
  97        return count;
  98}
  99
 100static void add_read_queue(int flag, unsigned long val)
 101{
 102        DEFINE_IR_RAW_EVENT(ev);
 103
 104        pr_debug("add flag %d with val %lu\n", flag, val);
 105
 106        /*
 107         * statistically, pulses are ~TIME_CONST/2 too long. we could
 108         * maybe make this more exact, but this is good enough
 109         */
 110        if (flag) {
 111                /* pulse */
 112                if (val > TIME_CONST / 2)
 113                        val -= TIME_CONST / 2;
 114                else /* should not ever happen */
 115                        val = 1;
 116                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
 125static int init_chrdev(void)
 126{
 127        rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW);
 128        if (!rcdev)
 129                return -ENOMEM;
 130
 131        rcdev->input_name = "SIR IrDA port";
 132        rcdev->input_phys = KBUILD_MODNAME "/input0";
 133        rcdev->input_id.bustype = BUS_HOST;
 134        rcdev->input_id.vendor = 0x0001;
 135        rcdev->input_id.product = 0x0001;
 136        rcdev->input_id.version = 0x0100;
 137        rcdev->tx_ir = sir_tx_ir;
 138        rcdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
 139        rcdev->driver_name = KBUILD_MODNAME;
 140        rcdev->map_name = RC_MAP_RC6_MCE;
 141        rcdev->timeout = IR_DEFAULT_TIMEOUT;
 142        rcdev->dev.parent = &sir_ir_dev->dev;
 143
 144        return devm_rc_register_device(&sir_ir_dev->dev, rcdev);
 145}
 146
 147/* SECTION: Hardware */
 148static void sir_timeout(unsigned long data)
 149{
 150        /*
 151         * if last received signal was a pulse, but receiving stopped
 152         * within the 9 bit frame, we need to finish this pulse and
 153         * simulate a signal change to from pulse to space. Otherwise
 154         * upper layers will receive two sequences next time.
 155         */
 156
 157        unsigned long flags;
 158        unsigned long pulse_end;
 159
 160        /* avoid interference with interrupt */
 161        spin_lock_irqsave(&timer_lock, flags);
 162        if (last_value) {
 163                /* clear unread bits in UART and restart */
 164                outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
 165                /* determine 'virtual' pulse end: */
 166                pulse_end = min_t(unsigned long,
 167                                  ktime_us_delta(last, last_intr_time),
 168                                  IR_MAX_DURATION);
 169                dev_dbg(&sir_ir_dev->dev, "timeout add %d for %lu usec\n",
 170                        last_value, pulse_end);
 171                add_read_queue(last_value, pulse_end);
 172                last_value = 0;
 173                last = last_intr_time;
 174        }
 175        spin_unlock_irqrestore(&timer_lock, flags);
 176        ir_raw_event_handle(rcdev);
 177}
 178
 179static irqreturn_t sir_interrupt(int irq, void *dev_id)
 180{
 181        unsigned char data;
 182        ktime_t curr_time;
 183        static unsigned long delt;
 184        unsigned long deltintr;
 185        unsigned long flags;
 186        int counter = 0;
 187        int iir, lsr;
 188
 189        while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
 190                if (++counter > 256) {
 191                        dev_err(&sir_ir_dev->dev, "Trapped in interrupt");
 192                        break;
 193                }
 194
 195                switch (iir & UART_IIR_ID) { /* FIXME toto treba preriedit */
 196                case UART_IIR_MSI:
 197                        (void)inb(io + UART_MSR);
 198                        break;
 199                case UART_IIR_RLSI:
 200                case UART_IIR_THRI:
 201                        (void)inb(io + UART_LSR);
 202                        break;
 203                case UART_IIR_RDI:
 204                        /* avoid interference with timer */
 205                        spin_lock_irqsave(&timer_lock, flags);
 206                        do {
 207                                del_timer(&timerlist);
 208                                data = inb(io + UART_RX);
 209                                curr_time = ktime_get();
 210                                delt = min_t(unsigned long,
 211                                             ktime_us_delta(last, curr_time),
 212                                             IR_MAX_DURATION);
 213                                deltintr = min_t(unsigned long,
 214                                                 ktime_us_delta(last_intr_time,
 215                                                                curr_time),
 216                                                 IR_MAX_DURATION);
 217                                dev_dbg(&sir_ir_dev->dev, "t %lu, d %d\n",
 218                                        deltintr, (int)data);
 219                                /*
 220                                 * if nothing came in last X cycles,
 221                                 * it was gap
 222                                 */
 223                                if (deltintr > TIME_CONST * threshold) {
 224                                        if (last_value) {
 225                                                dev_dbg(&sir_ir_dev->dev, "GAP\n");
 226                                                /* simulate signal change */
 227                                                add_read_queue(last_value,
 228                                                               delt -
 229                                                               deltintr);
 230                                                last_value = 0;
 231                                                last = last_intr_time;
 232                                                delt = deltintr;
 233                                        }
 234                                }
 235                                data = 1;
 236                                if (data ^ last_value) {
 237                                        /*
 238                                         * deltintr > 2*TIME_CONST, remember?
 239                                         * the other case is timeout
 240                                         */
 241                                        add_read_queue(last_value,
 242                                                       delt - TIME_CONST);
 243                                        last_value = data;
 244                                        last = curr_time;
 245                                        last = ktime_sub_us(last,
 246                                                            TIME_CONST);
 247                                }
 248                                last_intr_time = curr_time;
 249                                if (data) {
 250                                        /*
 251                                         * start timer for end of
 252                                         * sequence detection
 253                                         */
 254                                        timerlist.expires = jiffies +
 255                                                                SIR_TIMEOUT;
 256                                        add_timer(&timerlist);
 257                                }
 258
 259                                lsr = inb(io + UART_LSR);
 260                        } while (lsr & UART_LSR_DR); /* data ready */
 261                        spin_unlock_irqrestore(&timer_lock, flags);
 262                        break;
 263                default:
 264                        break;
 265                }
 266        }
 267        ir_raw_event_handle(rcdev);
 268        return IRQ_RETVAL(IRQ_HANDLED);
 269}
 270
 271static void send_space(unsigned long len)
 272{
 273        usleep_range(len, len + 25);
 274}
 275
 276static void send_pulse(unsigned long len)
 277{
 278        long bytes_out = len / TIME_CONST;
 279
 280        if (bytes_out == 0)
 281                bytes_out++;
 282
 283        while (bytes_out--) {
 284                outb(PULSE, io + UART_TX);
 285                /* FIXME treba seriozne cakanie z char/serial.c */
 286                while (!(inb(io + UART_LSR) & UART_LSR_THRE))
 287                        ;
 288        }
 289}
 290
 291static int init_hardware(void)
 292{
 293        unsigned long flags;
 294
 295        spin_lock_irqsave(&hardware_lock, flags);
 296        /* reset UART */
 297        outb(0, io + UART_MCR);
 298        outb(0, io + UART_IER);
 299        /* init UART */
 300        /* set DLAB, speed = 115200 */
 301        outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
 302        outb(1, io + UART_DLL); outb(0, io + UART_DLM);
 303        /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
 304        outb(UART_LCR_WLEN7, io + UART_LCR);
 305        /* FIFO operation */
 306        outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
 307        /* interrupts */
 308        /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
 309        outb(UART_IER_RDI, io + UART_IER);
 310        /* turn on UART */
 311        outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR);
 312        spin_unlock_irqrestore(&hardware_lock, flags);
 313        return 0;
 314}
 315
 316static void drop_hardware(void)
 317{
 318        unsigned long flags;
 319
 320        spin_lock_irqsave(&hardware_lock, flags);
 321
 322        /* turn off interrupts */
 323        outb(0, io + UART_IER);
 324
 325        spin_unlock_irqrestore(&hardware_lock, flags);
 326}
 327
 328/* SECTION: Initialisation */
 329
 330static int init_port(void)
 331{
 332        int retval;
 333
 334        setup_timer(&timerlist, sir_timeout, 0);
 335
 336        /* get I/O port access and IRQ line */
 337        if (!request_region(io, 8, KBUILD_MODNAME)) {
 338                pr_err("i/o port 0x%.4x already in use.\n", io);
 339                return -EBUSY;
 340        }
 341        retval = request_irq(irq, sir_interrupt, 0,
 342                             KBUILD_MODNAME, NULL);
 343        if (retval < 0) {
 344                release_region(io, 8);
 345                pr_err("IRQ %d already in use.\n", irq);
 346                return retval;
 347        }
 348        pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq);
 349
 350        return 0;
 351}
 352
 353static void drop_port(void)
 354{
 355        free_irq(irq, NULL);
 356        del_timer_sync(&timerlist);
 357        release_region(io, 8);
 358}
 359
 360static int init_sir_ir(void)
 361{
 362        int retval;
 363
 364        retval = init_port();
 365        if (retval < 0)
 366                return retval;
 367        init_hardware();
 368        return 0;
 369}
 370
 371static int sir_ir_probe(struct platform_device *dev)
 372{
 373        int retval;
 374
 375        retval = init_chrdev();
 376        if (retval < 0)
 377                return retval;
 378
 379        return init_sir_ir();
 380}
 381
 382static int sir_ir_remove(struct platform_device *dev)
 383{
 384        return 0;
 385}
 386
 387static struct platform_driver sir_ir_driver = {
 388        .probe          = sir_ir_probe,
 389        .remove         = sir_ir_remove,
 390        .driver         = {
 391                .name   = "sir_ir",
 392        },
 393};
 394
 395static int __init sir_ir_init(void)
 396{
 397        int retval;
 398
 399        retval = platform_driver_register(&sir_ir_driver);
 400        if (retval)
 401                return retval;
 402
 403        sir_ir_dev = platform_device_alloc("sir_ir", 0);
 404        if (!sir_ir_dev) {
 405                retval = -ENOMEM;
 406                goto pdev_alloc_fail;
 407        }
 408
 409        retval = platform_device_add(sir_ir_dev);
 410        if (retval)
 411                goto pdev_add_fail;
 412
 413        return 0;
 414
 415pdev_add_fail:
 416        platform_device_put(sir_ir_dev);
 417pdev_alloc_fail:
 418        platform_driver_unregister(&sir_ir_driver);
 419        return retval;
 420}
 421
 422static void __exit sir_ir_exit(void)
 423{
 424        drop_hardware();
 425        drop_port();
 426        platform_device_unregister(sir_ir_dev);
 427        platform_driver_unregister(&sir_ir_driver);
 428}
 429
 430module_init(sir_ir_init);
 431module_exit(sir_ir_exit);
 432
 433MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
 434MODULE_AUTHOR("Milan Pikula");
 435MODULE_LICENSE("GPL");
 436
 437module_param(io, int, 0444);
 438MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
 439
 440module_param(irq, int, 0444);
 441MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
 442
 443module_param(threshold, int, 0444);
 444MODULE_PARM_DESC(threshold, "space detection threshold (3)");
 445