cregit-Linux how code gets into the kernel

Release 4.7 drivers/staging/media/lirc/lirc_parallel.c

/*
 * lirc_parallel.c
 *
 * lirc_parallel - device driver for infra-red signal receiving and
 *                 transmitting unit built by the author
 *
 * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

/*** Includes ***/

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/ktime.h>
#include <linux/mm.h>
#include <linux/delay.h>

#include <linux/io.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <asm/div64.h>

#include <linux/poll.h>
#include <linux/parport.h>
#include <linux/platform_device.h>

#include <media/lirc.h>
#include <media/lirc_dev.h>

#include "lirc_parallel.h"


#define LIRC_DRIVER_NAME "lirc_parallel"

#ifndef LIRC_IRQ

#define LIRC_IRQ 7
#endif
#ifndef LIRC_PORT

#define LIRC_PORT 0x378
#endif
#ifndef LIRC_TIMER

#define LIRC_TIMER 65536
#endif

/*** Global Variables ***/


static bool debug;

static bool check_pselecd;


static unsigned int irq = LIRC_IRQ;

static unsigned int io = LIRC_PORT;
#ifdef LIRC_TIMER

static unsigned int timer;

static unsigned int default_timer = LIRC_TIMER;
#endif


#define RBUF_SIZE (256) 
/* this must be a power of 2 larger than 1 */


static int rbuf[RBUF_SIZE];

static DECLARE_WAIT_QUEUE_HEAD(lirc_wait);


static unsigned int rptr;

static unsigned int wptr;

static unsigned int lost_irqs;

static int is_open;


static struct parport *pport;

static struct pardevice *ppdevice;

static int is_claimed;


static unsigned int tx_mask = 1;

/*** Internal Functions ***/


static unsigned int in(int offset) { switch (offset) { case LIRC_LP_BASE: return parport_read_data(pport); case LIRC_LP_STATUS: return parport_read_status(pport); case LIRC_LP_CONTROL: return parport_read_control(pport); } return 0; /* make compiler happy */ }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson47100.00%1100.00%
Total47100.00%1100.00%


static void out(int offset, int value) { switch (offset) { case LIRC_LP_BASE: parport_write_data(pport, value); break; case LIRC_LP_CONTROL: parport_write_control(pport, value); break; case LIRC_LP_STATUS: pr_info("attempt to write to status register\n"); break; } }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson4795.92%150.00%
toshiaki yamanetoshiaki yamane24.08%150.00%
Total49100.00%2100.00%


static unsigned int lirc_get_timer(void) { return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson17100.00%1100.00%
Total17100.00%1100.00%


static unsigned int lirc_get_signal(void) { return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson17100.00%1100.00%
Total17100.00%1100.00%


static void lirc_on(void) { out(LIRC_PORT_DATA, tx_mask); }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson15100.00%1100.00%
Total15100.00%1100.00%


static void lirc_off(void) { out(LIRC_PORT_DATA, 0); }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson15100.00%1100.00%
Total15100.00%1100.00%


static unsigned int init_lirc_timer(void) { ktime_t kt, now, timeout; unsigned int level, newlevel, timeelapsed, newtimer; int count = 0; kt = ktime_get(); /* wait max. 1 sec. */ timeout = ktime_add_ns(kt, NSEC_PER_SEC); level = lirc_get_timer(); do { newlevel = lirc_get_timer(); if (level == 0 && newlevel != 0) count++; level = newlevel; now = ktime_get(); } while (count < 1000 && (ktime_before(now, timeout))); timeelapsed = ktime_us_delta(now, kt); if (count >= 1000 && timeelapsed > 0) { if (default_timer == 0) { /* autodetect timer */ newtimer = (1000000*count)/timeelapsed; pr_info("%u Hz timer detected\n", newtimer); return newtimer; } newtimer = (1000000*count)/timeelapsed; if (abs(newtimer - default_timer) > default_timer/10) { /* bad timer */ pr_notice("bad timer: %u Hz\n", newtimer); pr_notice("using default timer: %u Hz\n", default_timer); return default_timer; } pr_info("%u Hz timer detected\n", newtimer); return newtimer; /* use detected value */ } pr_notice("no timer detected\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson17081.34%133.33%
arnd bergmannarnd bergmann2913.88%133.33%
toshiaki yamanetoshiaki yamane104.78%133.33%
Total209100.00%3100.00%


static int lirc_claim(void) { if (parport_claim(ppdevice) != 0) { pr_warn("could not claim port\n"); pr_warn("waiting for port becoming available\n"); if (parport_claim_or_block(ppdevice) < 0) { pr_notice("could not claim port, giving up\n"); return 0; } } out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP); is_claimed = 1; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson5890.62%150.00%
toshiaki yamanetoshiaki yamane69.38%150.00%
Total64100.00%2100.00%

/*** interrupt handler ***/
static void rbuf_write(int signal) { unsigned int nwptr; nwptr = (wptr + 1) & (RBUF_SIZE - 1); if (nwptr == rptr) { /* no new signals will be accepted */ lost_irqs++; pr_notice("buffer overrun\n"); return; } rbuf[wptr] = signal; wptr = nwptr; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson5496.43%150.00%
toshiaki yamanetoshiaki yamane23.57%150.00%
Total56100.00%2100.00%


static void lirc_lirc_irq_handler(void *blah) { ktime_t kt, delkt; static ktime_t lastkt; static int init; long signal; int data; unsigned int level, newlevel; unsigned int timeout; if (!is_open) return; if (!is_claimed) return; #if 0 /* disable interrupt */ disable_irq(irq); out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN)); #endif if (check_pselecd && (in(1) & LP_PSELECD)) return; #ifdef LIRC_TIMER if (init) { kt = ktime_get(); delkt = ktime_sub(kt, lastkt); if (ktime_compare(delkt, ktime_set(15, 0)) > 0) /* really long time */ data = PULSE_MASK; else data = (int)(ktime_to_us(delkt) + LIRC_SFH506_DELAY); rbuf_write(data); /* space */ } else { if (timer == 0) { /* * wake up; we'll lose this signal, but it will be * garbage if the device is turned on anyway */ timer = init_lirc_timer(); /* enable_irq(irq); */ return; } init = 1; } timeout = timer / 10; /* timeout after 1/10 sec. */ signal = 1; level = lirc_get_timer(); do { newlevel = lirc_get_timer(); if (level == 0 && newlevel != 0) signal++; level = newlevel; /* giving up */ if (signal > timeout || (check_pselecd && (in(1) & LP_PSELECD))) { signal = 0; pr_notice("timeout\n"); break; } } while (lirc_get_signal()); if (signal != 0) { /* adjust value to usecs */ __u64 helper; helper = ((__u64)signal) * 1000000; do_div(helper, timer); signal = (long)helper; if (signal > LIRC_SFH506_DELAY) data = signal - LIRC_SFH506_DELAY; else data = 1; rbuf_write(PULSE_BIT | data); /* pulse */ } lastkt = ktime_get(); #else /* add your code here */ #endif wake_up_interruptible(&lirc_wait); /* enable interrupt */ /* enable_irq(irq); out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN); */ }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson27887.15%342.86%
arnd bergmannarnd bergmann3711.60%114.29%
toshiaki yamanetoshiaki yamane20.63%114.29%
uwe kleine-koeniguwe kleine-koenig10.31%114.29%
mauro carvalho chehabmauro carvalho chehab10.31%114.29%
Total319100.00%7100.00%

/*** file operations ***/
static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig) { return -ESPIPE; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson21100.00%1100.00%
Total21100.00%1100.00%


static ssize_t lirc_read(struct file *filep, char __user *buf, size_t n, loff_t *ppos) { int result = 0; int count = 0; DECLARE_WAITQUEUE(wait, current); if (n % sizeof(int)) return -EINVAL; add_wait_queue(&lirc_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); while (count < n) { if (rptr != wptr) { if (copy_to_user(buf + count, &rbuf[rptr], sizeof(int))) { result = -EFAULT; break; } rptr = (rptr + 1) & (RBUF_SIZE - 1); count += sizeof(int); } else { if (filep->f_flags & O_NONBLOCK) { result = -EAGAIN; break; } if (signal_pending(current)) { result = -ERESTARTSYS; break; } schedule(); set_current_state(TASK_INTERRUPTIBLE); } } remove_wait_queue(&lirc_wait, &wait); set_current_state(TASK_RUNNING); return count ? count : result; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson19499.49%150.00%
tuomas tynkkynentuomas tynkkynen10.51%150.00%
Total195100.00%2100.00%


static ssize_t lirc_write(struct file *filep, const char __user *buf, size_t n, loff_t *ppos) { int count; unsigned int i; unsigned int level, newlevel; unsigned long flags; int counttimer; int *wbuf; ssize_t ret; if (!is_claimed) return -EBUSY; count = n / sizeof(int); if (n % sizeof(int) || count % 2 == 0) return -EINVAL; wbuf = memdup_user(buf, n); if (IS_ERR(wbuf)) return PTR_ERR(wbuf); #ifdef LIRC_TIMER if (timer == 0) { /* try again if device is ready */ timer = init_lirc_timer(); if (timer == 0) { ret = -EIO; goto out; } } /* adjust values from usecs */ for (i = 0; i < count; i++) { __u64 helper; helper = ((__u64)wbuf[i]) * timer; do_div(helper, 1000000); wbuf[i] = (int)helper; } local_irq_save(flags); i = 0; while (i < count) { level = lirc_get_timer(); counttimer = 0; lirc_on(); do { newlevel = lirc_get_timer(); if (level == 0 && newlevel != 0) counttimer++; level = newlevel; if (check_pselecd && (in(1) & LP_PSELECD)) { lirc_off(); local_irq_restore(flags); ret = -EIO; goto out; } } while (counttimer < wbuf[i]); i++; lirc_off(); if (i == count) break; counttimer = 0; do { newlevel = lirc_get_timer(); if (level == 0 && newlevel != 0) counttimer++; level = newlevel; if (check_pselecd && (in(1) & LP_PSELECD)) { local_irq_restore(flags); ret = -EIO; goto out; } } while (counttimer < wbuf[i]); i++; } local_irq_restore(flags); #else /* place code that handles write without external timer here */ #endif ret = n; out: kfree(wbuf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson39499.75%375.00%
tuomas tynkkynentuomas tynkkynen10.25%125.00%
Total395100.00%4100.00%


static unsigned int lirc_poll(struct file *file, poll_table *wait) { poll_wait(file, &lirc_wait, wait); if (rptr != wptr) return POLLIN | POLLRDNORM; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson40100.00%1100.00%
Total40100.00%1100.00%


static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { int result; u32 __user *uptr = (u32 __user *)arg; u32 features = LIRC_CAN_SET_TRANSMITTER_MASK | LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; u32 mode; u32 value; switch (cmd) { case LIRC_GET_FEATURES: result = put_user(features, uptr); if (result) return result; break; case LIRC_GET_SEND_MODE: result = put_user(LIRC_MODE_PULSE, uptr); if (result) return result; break; case LIRC_GET_REC_MODE: result = put_user(LIRC_MODE_MODE2, uptr); if (result) return result; break; case LIRC_SET_SEND_MODE: result = get_user(mode, uptr); if (result) return result; if (mode != LIRC_MODE_PULSE) return -EINVAL; break; case LIRC_SET_REC_MODE: result = get_user(mode, uptr); if (result) return result; if (mode != LIRC_MODE_MODE2) return -ENOSYS; break; case LIRC_SET_TRANSMITTER_MASK: result = get_user(value, uptr); if (result) return result; if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value) return LIRC_PARALLEL_MAX_TRANSMITTERS; tx_mask = value; break; default: return -ENOIOCTLCMD; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson19990.45%266.67%
tuomas tynkkynentuomas tynkkynen219.55%133.33%
Total220100.00%3100.00%


static int lirc_open(struct inode *node, struct file *filep) { if (is_open || !lirc_claim()) return -EBUSY; parport_enable_irq(pport); /* init read ptr */ rptr = 0; wptr = 0; lost_irqs = 0; is_open = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson53100.00%2100.00%
Total53100.00%2100.00%


static int lirc_close(struct inode *node, struct file *filep) { if (is_claimed) { is_claimed = 0; parport_release(ppdevice); } is_open = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson38100.00%1100.00%
Total38100.00%1100.00%

static const struct file_operations lirc_fops = { .owner = THIS_MODULE, .llseek = lirc_lseek, .read = lirc_read, .write = lirc_write, .poll = lirc_poll, .unlocked_ioctl = lirc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = lirc_ioctl, #endif .open = lirc_open, .release = lirc_close };
static int set_use_inc(void *data) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson13100.00%1100.00%
Total13100.00%1100.00%


static void set_use_dec(void *data) { }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson9100.00%1100.00%
Total9100.00%1100.00%

static struct lirc_driver driver = { .name = LIRC_DRIVER_NAME, .minor = -1, .code_length = 1, .sample_rate = 0, .data = NULL, .add_to_buf = NULL, .set_use_inc = set_use_inc, .set_use_dec = set_use_dec, .fops = &lirc_fops, .dev = NULL, .owner = THIS_MODULE, }; static struct platform_device *lirc_parallel_dev;
static int lirc_parallel_probe(struct platform_device *dev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas viehwegerthomas viehweger14100.00%1100.00%
Total14100.00%1100.00%


static int lirc_parallel_remove(struct platform_device *dev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas viehwegerthomas viehweger14100.00%1100.00%
Total14100.00%1100.00%


static int lirc_parallel_suspend(struct platform_device *dev, pm_message_t state) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas viehwegerthomas viehweger17100.00%1100.00%
Total17100.00%1100.00%


static int lirc_parallel_resume(struct platform_device *dev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas viehwegerthomas viehweger14100.00%1100.00%
Total14100.00%1100.00%

static struct platform_driver lirc_parallel_driver = { .probe = lirc_parallel_probe, .remove = lirc_parallel_remove, .suspend = lirc_parallel_suspend, .resume = lirc_parallel_resume, .driver = { .name = LIRC_DRIVER_NAME, }, };
static int pf(void *handle) { parport_disable_irq(pport); is_claimed = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson22100.00%1100.00%
Total22100.00%1100.00%


static void kf(void *handle) { if (!is_open) return; if (!lirc_claim()) return; parport_enable_irq(pport); lirc_off(); /* this is a bit annoying when you actually print...*/ /* printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME); */ }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson33100.00%1100.00%
Total33100.00%1100.00%

/*** module initialization and cleanup ***/
static int __init lirc_parallel_init(void) { int result; result = platform_driver_register(&lirc_parallel_driver); if (result) { pr_notice("platform_driver_register returned %d\n", result); return result; } lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); if (!lirc_parallel_dev) { result = -ENOMEM; goto exit_driver_unregister; } result = platform_device_add(lirc_parallel_dev); if (result) goto exit_device_put; pport = parport_find_base(io); if (!pport) { pr_notice("no port at %x found\n", io); result = -ENXIO; goto exit_device_put; } ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, pf, kf, lirc_lirc_irq_handler, 0, NULL); parport_put_port(pport); if (!ppdevice) { pr_notice("parport_register_device() failed\n"); result = -ENXIO; goto exit_device_put; } if (parport_claim(ppdevice) != 0) goto skip_init; is_claimed = 1; out(LIRC_LP_CONTROL, LP_PSELECP | LP_PINITP); #ifdef LIRC_TIMER if (debug) out(LIRC_PORT_DATA, tx_mask); timer = init_lirc_timer(); #if 0 /* continue even if device is offline */ if (timer == 0) { is_claimed = 0; parport_release(pport); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } #endif if (debug) out(LIRC_PORT_DATA, 0); #endif is_claimed = 0; parport_release(ppdevice); skip_init: driver.dev = &lirc_parallel_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; exit_device_put: platform_device_put(lirc_parallel_dev); exit_driver_unregister: platform_driver_unregister(&lirc_parallel_driver); return result; }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson17759.20%120.00%
thomas viehwegerthomas viehweger10936.45%120.00%
toshiaki yamanetoshiaki yamane103.34%120.00%
sudip mukherjeesudip mukherjee20.67%120.00%
mauro carvalho chehabmauro carvalho chehab10.33%120.00%
Total299100.00%5100.00%


static void __exit lirc_parallel_exit(void) { parport_unregister_device(ppdevice); lirc_unregister_driver(driver.minor); platform_device_unregister(lirc_parallel_dev); platform_driver_unregister(&lirc_parallel_driver); }

Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson2165.62%150.00%
dave jonesdave jones1134.38%150.00%
Total32100.00%2100.00%

module_init(lirc_parallel_init); module_exit(lirc_parallel_exit); MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); MODULE_AUTHOR("Christoph Bartelmus"); MODULE_LICENSE("GPL"); module_param(io, int, S_IRUGO); MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)"); module_param(irq, int, S_IRUGO); MODULE_PARM_DESC(irq, "Interrupt (7 or 5)"); module_param(tx_mask, int, S_IRUGO); MODULE_PARM_DESC(tx_mask, "Transmitter mask (default: 0x01)"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging messages"); module_param(check_pselecd, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(check_pselecd, "Check for printer (default: 0)");

Overall Contributors

PersonTokensPropCommitsCommitProp
jarod wilsonjarod wilson233886.11%527.78%
thomas viehwegerthomas viehweger2147.88%15.56%
arnd bergmannarnd bergmann672.47%15.56%
toshiaki yamanetoshiaki yamane391.44%15.56%
tuomas tynkkynentuomas tynkkynen230.85%15.56%
ebru akagunduzebru akagunduz130.48%15.56%
dave jonesdave jones110.41%15.56%
rusty russellrusty russell30.11%211.11%
mauro carvalho chehabmauro carvalho chehab30.11%211.11%
sudip mukherjeesudip mukherjee20.07%15.56%
dan carpenterdan carpenter10.04%15.56%
uwe kleine-koeniguwe kleine-koenig10.04%15.56%
Total2715100.00%18100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}