123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 |
- From 96fbef3b8f8ab61f7f32d52b54d7993117a5fdbc Mon Sep 17 00:00:00 2001
- From: Aron Szabo <aron@aron.ws>
- Date: Sat, 16 Jun 2012 12:15:55 +0200
- Subject: [PATCH 015/114] 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
- ---
- drivers/staging/media/lirc/Kconfig | 6 +
- drivers/staging/media/lirc/Makefile | 1 +
- drivers/staging/media/lirc/lirc_rpi.c | 659 ++++++++++++++++++++++++++++++++++
- 3 files changed, 666 insertions(+)
- create mode 100644 drivers/staging/media/lirc/lirc_rpi.c
- --- 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
- --- 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
- --- /dev/null
- +++ b/drivers/staging/media/lirc/lirc_rpi.c
- @@ -0,0 +1,659 @@
- +/*
- + * 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 <mach/gpio.h>
- +#include <linux/gpio.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;
- +
- +/* 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 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);
- +
- + 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;
- +
- + 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;
- + }
- +
- + bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull);
- + gpiochip->direction_input(gpiochip, gpio_in_pin);
- + gpiochip->direction_output(gpiochip, gpio_out_pin, 1);
- + 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);
- + }
- +
- + return 0;
- +
- + 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;
- +
- + /* 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,
- + .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)
- +{
- + 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;
- +
- + if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) {
- + 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)
- +{
- + 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");
|