OpenWrt – Rev 1

Subversion Repositories:
Rev:
From ecc83f926bd3cf3c9e62389494f39a0939949ef3 Mon Sep 17 00:00:00 2001
From: Aron Szabo <aron@aron.ws>
Date: Sat, 16 Jun 2012 12:15:55 +0200
Subject: [PATCH 048/454] 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

lirc: Remove restriction on gpio pins that can be used with lirc

Compute Module, for example could use different pins

lirc_rpi: Add parameter to specify input pin pull

Depending on the connected IR circuitry it might be desirable to change the
gpios internal pull from it pull-down default behaviour. Add a module
parameter to allow the user to set it explicitly.

Signed-off-by: Julian Scheel <julian@jusst.de>

lirc-rpi: Use the higher-level irq control functions

This module used to access the irq_chip methods of the
gpio controller directly, rather than going through the
standard enable_irq/irq_set_irq_type functions. This
caused problems on pinctrl-bcm2835 which only implements
the irq_enable/disable methods and not irq_unmask/mask.

lirc-rpi: Correct the interrupt usage

1) Correct the use of enable_irq (i.e. don't call it so often)
2) Correct the shutdown sequence.
3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier

lirc-rpi: use getnstimeofday instead of read_current_timer

read_current_timer isn't guaranteed to return values in
microseconds, and indeed it doesn't on a Pi2.

Issue: linux#827

lirc-rpi: Add device tree support, and a suitable overlay

The overlay supports DT parameters that match the old module
parameters, except that gpio_in_pull should be set using the
strings "up", "down" or "off".

lirc-rpi: Also support pinctrl-bcm2835 in non-DT mode

fix auto-sense in lirc_rpi driver

On a Raspberry Pi 2, the lirc_rpi driver might receive spurious
interrupts and change it's low-active / high-active setting.
When this happens, the IR remote control stops working.

This patch disables this auto-detection if the 'sense' parameter
was set in the device tree, making the driver robust to such
spurious interrupts.
---
 drivers/staging/media/lirc/Kconfig    |   6 +
 drivers/staging/media/lirc/Makefile   |   1 +
 drivers/staging/media/lirc/lirc_rpi.c | 733 ++++++++++++++++++++++++++
 include/linux/platform_data/bcm2708.h |  23 +
 4 files changed, 763 insertions(+)
 create mode 100644 drivers/staging/media/lirc/lirc_rpi.c
 create mode 100644 include/linux/platform_data/bcm2708.h

--- a/drivers/staging/media/lirc/Kconfig
+++ b/drivers/staging/media/lirc/Kconfig
@@ -12,6 +12,12 @@ menuconfig LIRC_STAGING
 
 if LIRC_STAGING
 
+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_ZILOG
        tristate "Zilog/Hauppauge IR Transmitter"
        depends on LIRC && I2C
--- a/drivers/staging/media/lirc/Makefile
+++ b/drivers/staging/media/lirc/Makefile
@@ -3,4 +3,5 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_LIRC_RPI)         += lirc_rpi.o
 obj-$(CONFIG_LIRC_ZILOG)       += lirc_zilog.o
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_rpi.c
@@ -0,0 +1,733 @@
+/*
+ * 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/timekeeping.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>
+#include <linux/of_platform.h>
+#include <linux/platform_data/bcm2708.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 pull behaviour for input pin */
+static int gpio_in_pull = BCM2708_PULL_DOWN;
+/* 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;
+static int irq_num;
+static int auto_sense = 1;
+
+/* forward declarations */
+static long send_pulse(unsigned long length);
+static void send_space(long length);
+static void lirc_rpi_exit(void);
+
+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 unsigned long read_current_us(void)
+{
+       struct timespec now;
+       getnstimeofday(&now);
+       return (now.tv_sec * 1000000) + (now.tv_nsec/1000);
+}
+
+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;
+       actual_us = read_current_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);
+               actual_us = read_current_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);
+
+       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_DEBUG 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!
+                                */
+                               if (auto_sense) {
+                                       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 inline int read_bool_property(const struct device_node *np,
+                                    const char *propname,
+                                    bool *out_value)
+{
+       u32 value = 0;
+       int err = of_property_read_u32(np, propname, &value);
+       if (err == 0)
+               *out_value = (value != 0);
+       return err;
+}
+
+static void read_pin_settings(struct device_node *node)
+{
+       u32 pin;
+       int index;
+
+       for (index = 0;
+            of_property_read_u32_index(
+                    node,
+                    "brcm,pins",
+                    index,
+                    &pin) == 0;
+            index++) {
+               u32 function;
+               int err;
+               err = of_property_read_u32_index(
+                       node,
+                       "brcm,function",
+                       index,
+                       &function);
+               if (err == 0) {
+                       if (function == 1) /* Output */
+                               gpio_out_pin = pin;
+                       else if (function == 0) /* Input */
+                               gpio_in_pin = pin;
+               }
+       }
+}
+
+static int init_port(void)
+{
+       int i, nlow, nhigh;
+       struct device_node *node;
+
+       node = lirc_rpi_dev->dev.of_node;
+
+       gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);
+
+       /*
+        * Because of the lack of a setpull function, only support
+        * pinctrl-bcm2835 if using device tree.
+       */
+       if (!gpiochip && node)
+               gpiochip = gpiochip_find("pinctrl-bcm2835", is_right_chip);
+
+       if (!gpiochip) {
+               pr_err(LIRC_DRIVER_NAME ": gpio chip not found!\n");
+               return -ENODEV;
+       }
+
+       if (node) {
+               struct device_node *pins_node;
+
+               pins_node = of_parse_phandle(node, "pinctrl-0", 0);
+               if (!pins_node) {
+                       printk(KERN_ERR LIRC_DRIVER_NAME
+                              ": pinctrl settings not found!\n");
+                       return -EINVAL;
+               }
+
+               read_pin_settings(pins_node);
+
+               of_property_read_u32(node, "rpi,sense", &sense);
+
+               read_bool_property(node, "rpi,softcarrier", &softcarrier);
+
+               read_bool_property(node, "rpi,invert", &invert);
+
+               read_bool_property(node, "rpi,debug", &debug);
+
+       } else {
+               return -EINVAL;
+       }
+
+       gpiochip->set(gpiochip, gpio_out_pin, invert);
+
+       irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin);
+       dprintk("to_irq %d\n", irq_num);
+
+       /* 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);
+               auto_sense = 0;
+       }
+
+       return 0;
+}
+
+// called when the character device is opened
+static int set_use_inc(void *data)
+{
+       int result;
+
+       /* initialize timestamp */
+       do_gettimeofday(&lasttv);
+
+       result = request_irq(irq_num,
+                            (irq_handler_t) irq_handler,
+                            IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
+                            LIRC_DRIVER_NAME, (void*) 0);
+
+       switch (result) {
+       case -EBUSY:
+               printk(KERN_ERR LIRC_DRIVER_NAME
+                      ": IRQ %d is busy\n",
+                      irq_num);
+               return -EBUSY;
+       case -EINVAL:
+               printk(KERN_ERR LIRC_DRIVER_NAME
+                      ": Bad irq number or handler\n");
+               return -EINVAL;
+       default:
+               dprintk("Interrupt %d obtained\n",
+                       irq_num);
+               break;
+       };
+
+       /* initialize pulse/space widths */
+       init_timing_params(duty_cycle, freq);
+
+       return 0;
+}
+
+static void set_use_dec(void *data)
+{
+       /* GPIO Pin Falling/Rising Edge Detect Disable */
+       irq_set_irq_type(irq_num, 0);
+       disable_irq(irq_num);
+
+       free_irq(irq_num, (void *) 0);
+
+       dprintk(KERN_INFO LIRC_DRIVER_NAME
+               ": freed IRQ %d\n", irq_num);
+}
+
+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,
+       .data           = NULL,
+       .rbuf           = &rbuf,
+       .fops           = &lirc_fops,
+       .dev            = NULL,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id lirc_rpi_of_match[] = {
+       { .compatible = "rpi,lirc-rpi", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, lirc_rpi_of_match);
+
+static struct platform_driver lirc_rpi_driver = {
+       .driver = {
+               .name   = LIRC_DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(lirc_rpi_of_match),
+       },
+};
+
+static int __init lirc_rpi_init(void)
+{
+       struct device_node *node;
+       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;
+       }
+
+       node = of_find_compatible_node(NULL, NULL,
+                                      lirc_rpi_of_match[0].compatible);
+
+       if (node) {
+               /* DT-enabled */
+               lirc_rpi_dev = of_find_device_by_node(node);
+               WARN_ON(lirc_rpi_dev->dev.of_node != node);
+               of_node_put(node);
+       }
+       else {
+               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)
+{
+       set_use_dec(NULL);
+       if (!lirc_rpi_dev->dev.of_node)
+               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;
+
+       result = lirc_rpi_init();
+       if (result)
+               return result;
+
+       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");
+
+       set_use_inc(NULL);
+
+       return 0;
+
+       exit_rpi:
+       lirc_rpi_exit();
+
+       return result;
+}
+
+static void __exit lirc_rpi_exit_module(void)
+{
+       lirc_unregister_driver(driver.minor);
+
+       gpio_free(gpio_out_pin);
+       gpio_free(gpio_in_pin);
+
+       lirc_rpi_exit();
+
+       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. (default 17");
+
+module_param(gpio_in_pin, int, S_IRUGO);
+MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor."
+                " (default 18");
+
+module_param(gpio_in_pull, int, S_IRUGO);
+MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration."
+                " (0 = off, 1 = up, 2 = down, default down)");
+
+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");
--- /dev/null
+++ b/include/linux/platform_data/bcm2708.h
@@ -0,0 +1,23 @@
+/*
+ * include/linux/platform_data/bcm2708.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * (C) 2014 Julian Scheel <julian@jusst.de>
+ *
+ */
+#ifndef __BCM2708_H_
+#define __BCM2708_H_
+
+typedef enum {
+       BCM2708_PULL_OFF,
+       BCM2708_PULL_UP,
+       BCM2708_PULL_DOWN
+} bcm2708_gpio_pull_t;
+
+extern int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset,
+               bcm2708_gpio_pull_t value);
+
+#endif

Generated by GNU Enscript 1.6.5.90.