summaryrefslogtreecommitdiff
path: root/target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch')
-rw-r--r--target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch747
1 files changed, 747 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch b/target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch
new file mode 100644
index 0000000..717faa3
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.14/0012-lirc-added-support-for-RaspberryPi-GPIO.patch
@@ -0,0 +1,747 @@
+From 04439327ad7c2afd72b6fe8c43c922b892dff7c3 Mon Sep 17 00:00:00 2001
+From: Aron Szabo <aron@aron.ws>
+Date: Sat, 16 Jun 2012 12:15:55 +0200
+Subject: [PATCH 12/54] lirc: added support for RaspberryPi GPIO
+
+lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others
+See: https://github.com/raspberrypi/linux/issues/525
+---
+ drivers/staging/media/lirc/Kconfig | 6 +
+ drivers/staging/media/lirc/Makefile | 1 +
+ drivers/staging/media/lirc/lirc_rpi.c | 695 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 702 insertions(+)
+ create mode 100644 drivers/staging/media/lirc/lirc_rpi.c
+
+diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
+index e60a59f..6b7ff70 100644
+--- a/drivers/staging/media/lirc/Kconfig
++++ b/drivers/staging/media/lirc/Kconfig
+@@ -38,6 +38,12 @@ config LIRC_PARALLEL
+ help
+ Driver for Homebrew Parallel Port Receivers
+
++config LIRC_RPI
++ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi"
++ depends on LIRC
++ help
++ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi
++
+ config LIRC_SASEM
+ tristate "Sasem USB IR Remote"
+ depends on LIRC && USB
+diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile
+index b90fcab..2b227fd 100644
+--- a/drivers/staging/media/lirc/Makefile
++++ b/drivers/staging/media/lirc/Makefile
+@@ -7,6 +7,7 @@ obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
+ obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
+ obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
+ obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
++obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o
+ obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
+ obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
+ obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
+diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c
+new file mode 100644
+index 0000000..57ffacf
+--- /dev/null
++++ b/drivers/staging/media/lirc/lirc_rpi.c
+@@ -0,0 +1,695 @@
++/*
++ * lirc_rpi.c
++ *
++ * lirc_rpi - Device driver that records pulse- and pause-lengths
++ * (space-lengths) (just like the lirc_serial driver does)
++ * between GPIO interrupt events on the Raspberry Pi.
++ * Lots of code has been taken from the lirc_serial module,
++ * so I would like say thanks to the authors.
++ *
++ * Copyright (C) 2012 Aron Robert Szabo <aron@reon.hu>,
++ * Michael Bishop <cleverca22@gmail.com>
++ * 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
++ */
++
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/time.h>
++#include <linux/timex.h>
++#include <linux/string.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++#include <linux/spinlock.h>
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++#include <linux/gpio.h>
++
++#define LIRC_DRIVER_NAME "lirc_rpi"
++#define RBUF_LEN 256
++#define LIRC_TRANSMITTER_LATENCY 50
++
++#ifndef MAX_UDELAY_MS
++#define MAX_UDELAY_US 5000
++#else
++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
++#endif
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++/* module parameters */
++
++/* set the default GPIO input pin */
++static int gpio_in_pin = 18;
++/* set the default GPIO output pin */
++static int gpio_out_pin = 17;
++/* enable debugging messages */
++static bool debug;
++/* -1 = auto, 0 = active high, 1 = active low */
++static int sense = -1;
++/* use softcarrier by default */
++static bool softcarrier = 1;
++/* 0 = do not invert output, 1 = invert output */
++static bool invert = 0;
++
++struct gpio_chip *gpiochip;
++struct irq_chip *irqchip;
++struct irq_data *irqdata;
++
++/* forward declarations */
++static long send_pulse(unsigned long length);
++static void send_space(long length);
++static void lirc_rpi_exit(void);
++
++int valid_gpio_pins[] = { 0, 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 21,
++ 22, 23, 24, 25 ,27, 28, 29, 30, 31 };
++
++static struct platform_device *lirc_rpi_dev;
++static struct timeval lasttv = { 0, 0 };
++static struct lirc_buffer rbuf;
++static spinlock_t lock;
++
++/* initialized/set in init_timing_params() */
++static unsigned int freq = 38000;
++static unsigned int duty_cycle = 50;
++static unsigned long period;
++static unsigned long pulse_width;
++static unsigned long space_width;
++
++static void safe_udelay(unsigned long usecs)
++{
++ while (usecs > MAX_UDELAY_US) {
++ udelay(MAX_UDELAY_US);
++ usecs -= MAX_UDELAY_US;
++ }
++ udelay(usecs);
++}
++
++static int init_timing_params(unsigned int new_duty_cycle,
++ unsigned int new_freq)
++{
++ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <=
++ LIRC_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
++ LIRC_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ duty_cycle = new_duty_cycle;
++ freq = new_freq;
++ period = 1000 * 1000000L / freq;
++ pulse_width = period * duty_cycle / 100;
++ space_width = period - pulse_width;
++ dprintk("in init_timing_params, freq=%d pulse=%ld, "
++ "space=%ld\n", freq, pulse_width, space_width);
++ return 0;
++}
++
++static long send_pulse_softcarrier(unsigned long length)
++{
++ int flag;
++ unsigned long actual, target;
++ unsigned long actual_us, initial_us, target_us;
++
++ length *= 1000;
++
++ actual = 0; target = 0; flag = 0;
++ read_current_timer(&actual_us);
++
++ while (actual < length) {
++ if (flag) {
++ gpiochip->set(gpiochip, gpio_out_pin, invert);
++ target += space_width;
++ } else {
++ gpiochip->set(gpiochip, gpio_out_pin, !invert);
++ target += pulse_width;
++ }
++ initial_us = actual_us;
++ target_us = actual_us + (target - actual) / 1000;
++ /*
++ * Note - we've checked in ioctl that the pulse/space
++ * widths are big enough so that d is > 0
++ */
++ if ((int)(target_us - actual_us) > 0)
++ udelay(target_us - actual_us);
++ read_current_timer(&actual_us);
++ actual += (actual_us - initial_us) * 1000;
++ flag = !flag;
++ }
++ return (actual-length) / 1000;
++}
++
++static long send_pulse(unsigned long length)
++{
++ if (length <= 0)
++ return 0;
++
++ if (softcarrier) {
++ return send_pulse_softcarrier(length);
++ } else {
++ gpiochip->set(gpiochip, gpio_out_pin, !invert);
++ safe_udelay(length);
++ return 0;
++ }
++}
++
++static void send_space(long length)
++{
++ gpiochip->set(gpiochip, gpio_out_pin, invert);
++ if (length <= 0)
++ return;
++ safe_udelay(length);
++}
++
++static void rbwrite(int l)
++{
++ if (lirc_buffer_full(&rbuf)) {
++ /* no new signals will be accepted */
++ dprintk("Buffer overrun\n");
++ return;
++ }
++ lirc_buffer_write(&rbuf, (void *)&l);
++}
++
++static void frbwrite(int l)
++{
++ /* simple noise filter */
++ static int pulse, space;
++ static unsigned int ptr;
++
++ if (ptr > 0 && (l & PULSE_BIT)) {
++ pulse += l & PULSE_MASK;
++ if (pulse > 250) {
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ return;
++ }
++ if (!(l & PULSE_BIT)) {
++ if (ptr == 0) {
++ if (l > 20000) {
++ space = l;
++ ptr++;
++ return;
++ }
++ } else {
++ if (l > 20000) {
++ space += pulse;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ space += l;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ pulse = 0;
++ return;
++ }
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ }
++ rbwrite(l);
++}
++
++static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs)
++{
++ struct timeval tv;
++ long deltv;
++ int data;
++ int signal;
++
++ /* use the GPIO signal level */
++ signal = gpiochip->get(gpiochip, gpio_in_pin);
++
++ /* unmask the irq */
++ irqchip->irq_unmask(irqdata);
++
++ if (sense != -1) {
++ /* get current time */
++ do_gettimeofday(&tv);
++
++ /* calc time since last interrupt in microseconds */
++ deltv = tv.tv_sec-lasttv.tv_sec;
++ if (tv.tv_sec < lasttv.tv_sec ||
++ (tv.tv_sec == lasttv.tv_sec &&
++ tv.tv_usec < lasttv.tv_usec)) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: your clock just jumped backwards\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": %d %d %lx %lx %lx %lx\n", signal, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ data = PULSE_MASK;
++ } else if (deltv > 15) {
++ data = PULSE_MASK; /* really long time */
++ if (!(signal^sense)) {
++ /* sanity check */
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: %d %d %lx %lx %lx %lx\n",
++ signal, sense, tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ /*
++ * detecting pulse while this
++ * MUST be a space!
++ */
++ sense = sense ? 0 : 1;
++ }
++ } else {
++ data = (int) (deltv*1000000 +
++ (tv.tv_usec - lasttv.tv_usec));
++ }
++ frbwrite(signal^sense ? data : (data|PULSE_BIT));
++ lasttv = tv;
++ wake_up_interruptible(&rbuf.wait_poll);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int is_right_chip(struct gpio_chip *chip, void *data)
++{
++ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label));
++
++ if (strcmp(data, chip->label) == 0)
++ return 1;
++ return 0;
++}
++
++static int init_port(void)
++{
++ int i, nlow, nhigh, ret, irq;
++
++ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
++
++ if (!gpiochip)
++ return -ENODEV;
++
++ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) {
++ printk(KERN_ALERT LIRC_DRIVER_NAME
++ ": cant claim gpio pin %d\n", gpio_out_pin);
++ ret = -ENODEV;
++ goto exit_init_port;
++ }
++
++ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) {
++ printk(KERN_ALERT LIRC_DRIVER_NAME
++ ": cant claim gpio pin %d\n", gpio_in_pin);
++ ret = -ENODEV;
++ goto exit_gpio_free_out_pin;
++ }
++
++ gpiochip->direction_input(gpiochip, gpio_in_pin);
++ gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
++ gpiochip->set(gpiochip, gpio_out_pin, invert);
++
++ irq = gpiochip->to_irq(gpiochip, gpio_in_pin);
++ dprintk("to_irq %d\n", irq);
++ irqdata = irq_get_irq_data(irq);
++
++ if (irqdata && irqdata->chip) {
++ irqchip = irqdata->chip;
++ } else {
++ ret = -ENODEV;
++ goto exit_gpio_free_in_pin;
++ }
++
++ /* if pin is high, then this must be an active low receiver. */
++ if (sense == -1) {
++ /* wait 1/2 sec for the power supply */
++ msleep(500);
++
++ /*
++ * probe 9 times every 0.04s, collect "votes" for
++ * active high/low
++ */
++ nlow = 0;
++ nhigh = 0;
++ for (i = 0; i < 9; i++) {
++ if (gpiochip->get(gpiochip, gpio_in_pin))
++ nlow++;
++ else
++ nhigh++;
++ msleep(40);
++ }
++ sense = (nlow >= nhigh ? 1 : 0);
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": auto-detected active %s receiver on GPIO pin %d\n",
++ sense ? "low" : "high", gpio_in_pin);
++ } else {
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": manually using active %s receiver on GPIO pin %d\n",
++ sense ? "low" : "high", gpio_in_pin);
++ }
++
++ return 0;
++
++ exit_gpio_free_in_pin:
++ gpio_free(gpio_in_pin);
++
++ exit_gpio_free_out_pin:
++ gpio_free(gpio_out_pin);
++
++ exit_init_port:
++ return ret;
++}
++
++// called when the character device is opened
++static int set_use_inc(void *data)
++{
++ int result;
++ unsigned long flags;
++
++ /* initialize timestamp */
++ do_gettimeofday(&lasttv);
++
++ result = request_irq(gpiochip->to_irq(gpiochip, gpio_in_pin),
++ (irq_handler_t) irq_handler, 0,
++ LIRC_DRIVER_NAME, (void*) 0);
++
++ switch (result) {
++ case -EBUSY:
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": IRQ %d is busy\n",
++ gpiochip->to_irq(gpiochip, gpio_in_pin));
++ return -EBUSY;
++ case -EINVAL:
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": Bad irq number or handler\n");
++ return -EINVAL;
++ default:
++ dprintk("Interrupt %d obtained\n",
++ gpiochip->to_irq(gpiochip, gpio_in_pin));
++ break;
++ };
++
++ /* initialize pulse/space widths */
++ init_timing_params(duty_cycle, freq);
++
++ spin_lock_irqsave(&lock, flags);
++
++ /* GPIO Pin Falling/Rising Edge Detect Enable */
++ irqchip->irq_set_type(irqdata,
++ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING);
++
++ /* unmask the irq */
++ irqchip->irq_unmask(irqdata);
++
++ spin_unlock_irqrestore(&lock, flags);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&lock, flags);
++
++ /* GPIO Pin Falling/Rising Edge Detect Disable */
++ irqchip->irq_set_type(irqdata, 0);
++ irqchip->irq_mask(irqdata);
++
++ spin_unlock_irqrestore(&lock, flags);
++
++ free_irq(gpiochip->to_irq(gpiochip, gpio_in_pin), (void *) 0);
++
++ dprintk(KERN_INFO LIRC_DRIVER_NAME
++ ": freed IRQ %d\n", gpiochip->to_irq(gpiochip, gpio_in_pin));
++}
++
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *ppos)
++{
++ int i, count;
++ unsigned long flags;
++ long delta = 0;
++ int *wbuf;
++
++ 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);
++ spin_lock_irqsave(&lock, flags);
++
++ for (i = 0; i < count; i++) {
++ if (i%2)
++ send_space(wbuf[i] - delta);
++ else
++ delta = send_pulse(wbuf[i]);
++ }
++ gpiochip->set(gpiochip, gpio_out_pin, invert);
++
++ spin_unlock_irqrestore(&lock, flags);
++ kfree(wbuf);
++ return n;
++}
++
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ int result;
++ __u32 value;
++
++ switch (cmd) {
++ case LIRC_GET_SEND_MODE:
++ return -ENOIOCTLCMD;
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ result = get_user(value, (__u32 *) arg);
++ if (result)
++ return result;
++ /* only LIRC_MODE_PULSE supported */
++ if (value != LIRC_MODE_PULSE)
++ return -ENOSYS;
++ break;
++
++ case LIRC_GET_LENGTH:
++ return -ENOSYS;
++ break;
++
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ dprintk("SET_SEND_DUTY_CYCLE\n");
++ result = get_user(value, (__u32 *) arg);
++ if (result)
++ return result;
++ if (value <= 0 || value > 100)
++ return -EINVAL;
++ return init_timing_params(value, freq);
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ dprintk("SET_SEND_CARRIER\n");
++ result = get_user(value, (__u32 *) arg);
++ if (result)
++ return result;
++ if (value > 500000 || value < 20000)
++ return -EINVAL;
++ return init_timing_params(duty_cycle, value);
++ break;
++
++ default:
++ return lirc_dev_fop_ioctl(filep, cmd, arg);
++ }
++ return 0;
++}
++
++static const struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .write = lirc_write,
++ .unlocked_ioctl = lirc_ioctl,
++ .read = lirc_dev_fop_read,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++ .llseek = no_llseek,
++};
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .rbuf = &rbuf,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static struct platform_driver lirc_rpi_driver = {
++ .driver = {
++ .name = LIRC_DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init lirc_rpi_init(void)
++{
++ int result;
++
++ /* Init read buffer. */
++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
++ if (result < 0)
++ return -ENOMEM;
++
++ result = platform_driver_register(&lirc_rpi_driver);
++ if (result) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": lirc register returned %d\n", result);
++ goto exit_buffer_free;
++ }
++
++ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
++ if (!lirc_rpi_dev) {
++ result = -ENOMEM;
++ goto exit_driver_unregister;
++ }
++
++ result = platform_device_add(lirc_rpi_dev);
++ if (result)
++ goto exit_device_put;
++
++ return 0;
++
++ exit_device_put:
++ platform_device_put(lirc_rpi_dev);
++
++ exit_driver_unregister:
++ platform_driver_unregister(&lirc_rpi_driver);
++
++ exit_buffer_free:
++ lirc_buffer_free(&rbuf);
++
++ return result;
++}
++
++static void lirc_rpi_exit(void)
++{
++ platform_device_unregister(lirc_rpi_dev);
++ platform_driver_unregister(&lirc_rpi_driver);
++ lirc_buffer_free(&rbuf);
++}
++
++static int __init lirc_rpi_init_module(void)
++{
++ int result, i;
++
++ result = lirc_rpi_init();
++ if (result)
++ return result;
++
++ /* check if the module received valid gpio pin numbers */
++ result = 0;
++ if (gpio_in_pin != gpio_out_pin) {
++ for(i = 0; (i < ARRAY_SIZE(valid_gpio_pins)) && (result != 2); i++) {
++ if (gpio_in_pin == valid_gpio_pins[i] ||
++ gpio_out_pin == valid_gpio_pins[i]) {
++ result++;
++ }
++ }
++ }
++
++ if (result != 2) {
++ result = -EINVAL;
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": invalid GPIO pin(s) specified!\n");
++ goto exit_rpi;
++ }
++
++ result = init_port();
++ if (result < 0)
++ goto exit_rpi;
++
++ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_REC_MODE2;
++
++ driver.dev = &lirc_rpi_dev->dev;
++ driver.minor = lirc_register_driver(&driver);
++
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": device registration failed with %d\n", result);
++ result = -EIO;
++ goto exit_rpi;
++ }
++
++ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n");
++
++ return 0;
++
++ exit_rpi:
++ lirc_rpi_exit();
++
++ return result;
++}
++
++static void __exit lirc_rpi_exit_module(void)
++{
++ gpio_free(gpio_out_pin);
++ gpio_free(gpio_in_pin);
++
++ lirc_rpi_exit();
++
++ lirc_unregister_driver(driver.minor);
++ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n");
++}
++
++module_init(lirc_rpi_init_module);
++module_exit(lirc_rpi_exit_module);
++
++MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO.");
++MODULE_AUTHOR("Aron Robert Szabo <aron@reon.hu>");
++MODULE_AUTHOR("Michael Bishop <cleverca22@gmail.com>");
++MODULE_LICENSE("GPL");
++
++module_param(gpio_out_pin, int, S_IRUGO);
++MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM"
++ " processor. Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11,"
++ " 14, 15, 17, 18, 21, 22, 23, 24, 25, default 17");
++
++module_param(gpio_in_pin, int, S_IRUGO);
++MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor."
++ " Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11, 14, 15,"
++ " 17, 18, 21, 22, 23, 24, 25, default 18");
++
++module_param(sense, int, S_IRUGO);
++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
++ " (0 = active high, 1 = active low )");
++
++module_param(softcarrier, bool, S_IRUGO);
++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
++
++module_param(invert, bool, S_IRUGO);
++MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+--
+1.9.1
+