summaryrefslogtreecommitdiff
path: root/drivers/media/rc/lirc_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/rc/lirc_dev.c')
-rw-r--r--drivers/media/rc/lirc_dev.c1304
1 files changed, 660 insertions, 644 deletions
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 8dc057b273f2..7d4942925993 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -1,824 +1,840 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LIRC base driver
*
* by Artur Lipowski <alipowski@interia.pl>
- *
- * 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
+
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/completion.h>
#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/unistd.h>
-#include <linux/kthread.h>
-#include <linux/bitops.h>
#include <linux/device.h>
-#include <linux/cdev.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
+#include <linux/file.h>
+#include <linux/idr.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
-static bool debug;
+#include "rc-core-priv.h"
+#include <uapi/linux/lirc.h>
-#define IRCTL_DEV_NAME "BaseRemoteCtl"
-#define NOPLUG -1
-#define LOGHEAD "lirc_dev (%s[%d]): "
+#define LIRCBUF_SIZE 1024
static dev_t lirc_base_dev;
-struct irctl {
- struct lirc_driver d;
- int attached;
- int open;
+/* Used to keep track of allocated lirc devices */
+static DEFINE_IDA(lirc_ida);
- struct mutex irctl_lock;
- struct lirc_buffer *buf;
- unsigned int chunk_size;
+/* Only used for sysfs but defined to void otherwise */
+static const struct class lirc_class = {
+ .name = "lirc",
+};
- struct cdev *cdev;
+/**
+ * lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace
+ *
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ */
+void lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ unsigned long flags;
+ struct lirc_fh *fh;
+ int sample;
- struct task_struct *task;
- long jiffies_to_wait;
-};
+ /* Receiver overflow, data missing */
+ if (ev.overflow) {
+ /*
+ * Send lirc overflow message. This message is unknown to
+ * lircd, but it will interpret this as a long space as
+ * long as the value is set to high value. This resets its
+ * decoder state.
+ */
+ sample = LIRC_OVERFLOW(LIRC_VALUE_MASK);
+ dev_dbg(&dev->dev, "delivering overflow to lirc_dev\n");
-static DEFINE_MUTEX(lirc_dev_lock);
+ /* Carrier reports */
+ } else if (ev.carrier_report) {
+ sample = LIRC_FREQUENCY(ev.carrier);
+ dev_dbg(&dev->dev, "carrier report (freq: %d)\n", sample);
-static struct irctl *irctls[MAX_IRCTL_DEVICES];
+ /* Packet end */
+ } else if (ev.timeout) {
+ dev->gap_start = ktime_get();
-/* Only used for sysfs but defined to void otherwise */
-static struct class *lirc_class;
+ sample = LIRC_TIMEOUT(ev.duration);
+ dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample);
-/* helper function
- * initializes the irctl structure
- */
-static void lirc_irctl_init(struct irctl *ir)
-{
- mutex_init(&ir->irctl_lock);
- ir->d.minor = NOPLUG;
-}
+ /* Normal sample */
+ } else {
+ if (dev->gap_start) {
+ u64 duration = ktime_us_delta(ktime_get(),
+ dev->gap_start);
+
+ /* Cap by LIRC_VALUE_MASK */
+ duration = min_t(u64, duration, LIRC_VALUE_MASK);
+
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_for_each_entry(fh, &dev->lirc_fh, list)
+ kfifo_put(&fh->rawir, LIRC_SPACE(duration));
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
+ dev->gap_start = 0;
+ }
-static void lirc_irctl_cleanup(struct irctl *ir)
-{
- dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
+ sample = ev.pulse ? LIRC_PULSE(ev.duration) :
+ LIRC_SPACE(ev.duration);
+ dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n",
+ ev.duration, TO_STR(ev.pulse));
+ }
- device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
+ /*
+ * bpf does not care about the gap generated above; that exists
+ * for backwards compatibility
+ */
+ lirc_bpf_run(dev, sample);
- if (ir->buf != ir->d.rbuf) {
- lirc_buffer_free(ir->buf);
- kfree(ir->buf);
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_for_each_entry(fh, &dev->lirc_fh, list) {
+ if (kfifo_put(&fh->rawir, sample))
+ wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM);
}
- ir->buf = NULL;
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
}
-/* helper function
- * reads key codes from driver and puts them into buffer
- * returns 0 on success
+/**
+ * lirc_scancode_event() - Send scancode data to lirc to be relayed to
+ * userspace. This can be called in atomic context.
+ * @dev: the struct rc_dev descriptor of the device
+ * @lsc: the struct lirc_scancode describing the decoded scancode
*/
-static int lirc_add_to_buf(struct irctl *ir)
+void lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc)
{
- if (ir->d.add_to_buf) {
- int res = -ENODATA;
- int got_data = 0;
-
- /*
- * service the device as long as it is returning
- * data and we have space
- */
-get_data:
- res = ir->d.add_to_buf(ir->d.data, ir->buf);
- if (res == 0) {
- got_data++;
- goto get_data;
- }
+ unsigned long flags;
+ struct lirc_fh *fh;
- if (res == -ENODEV)
- kthread_stop(ir->task);
+ lsc->timestamp = ktime_get_ns();
- return got_data ? 0 : res;
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_for_each_entry(fh, &dev->lirc_fh, list) {
+ if (kfifo_put(&fh->scancodes, *lsc))
+ wake_up_poll(&fh->wait_poll, EPOLLIN | EPOLLRDNORM);
}
-
- return 0;
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
}
+EXPORT_SYMBOL_GPL(lirc_scancode_event);
-/* main function of the polling thread
- */
-static int lirc_thread(void *irctl)
+static int lirc_open(struct inode *inode, struct file *file)
{
- struct irctl *ir = irctl;
-
- dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
- ir->d.name, ir->d.minor);
+ struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev,
+ lirc_cdev);
+ struct lirc_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ unsigned long flags;
+ int retval;
- do {
- if (ir->open) {
- if (ir->jiffies_to_wait) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(ir->jiffies_to_wait);
- }
- if (kthread_should_stop())
- break;
- if (!lirc_add_to_buf(ir))
- wake_up_interruptible(&ir->buf->wait_poll);
- } else {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- } while (!kthread_should_stop());
+ if (!fh)
+ return -ENOMEM;
- dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
- ir->d.name, ir->d.minor);
+ get_device(&dev->dev);
- return 0;
-}
+ if (!dev->registered) {
+ retval = -ENODEV;
+ goto out_fh;
+ }
+ if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ if (kfifo_alloc(&fh->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) {
+ retval = -ENOMEM;
+ goto out_fh;
+ }
+ }
-static const struct file_operations lirc_dev_fops = {
- .owner = THIS_MODULE,
- .read = lirc_dev_fop_read,
- .write = lirc_dev_fop_write,
- .poll = lirc_dev_fop_poll,
- .unlocked_ioctl = lirc_dev_fop_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = lirc_dev_fop_ioctl,
-#endif
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
- .llseek = noop_llseek,
-};
+ if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
+ if (kfifo_alloc(&fh->scancodes, 32, GFP_KERNEL)) {
+ retval = -ENOMEM;
+ goto out_rawir;
+ }
+ }
-static int lirc_cdev_add(struct irctl *ir)
-{
- int retval = -ENOMEM;
- struct lirc_driver *d = &ir->d;
- struct cdev *cdev;
+ fh->send_mode = LIRC_MODE_PULSE;
+ fh->rc = dev;
- cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
- if (!cdev)
- goto err_out;
+ if (dev->driver_type == RC_DRIVER_SCANCODE)
+ fh->rec_mode = LIRC_MODE_SCANCODE;
+ else
+ fh->rec_mode = LIRC_MODE_MODE2;
- if (d->fops) {
- cdev_init(cdev, d->fops);
- cdev->owner = d->owner;
- } else {
- cdev_init(cdev, &lirc_dev_fops);
- cdev->owner = THIS_MODULE;
- }
- retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
+ retval = rc_open(dev);
if (retval)
- goto err_out;
+ goto out_kfifo;
- retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
- if (retval) {
- kobject_put(&cdev->kobj);
- goto err_out;
- }
+ init_waitqueue_head(&fh->wait_poll);
+
+ file->private_data = fh;
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_add(&fh->list, &dev->lirc_fh);
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
- ir->cdev = cdev;
+ stream_open(inode, file);
return 0;
+out_kfifo:
+ if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
+ kfifo_free(&fh->scancodes);
+out_rawir:
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ kfifo_free(&fh->rawir);
+out_fh:
+ kfree(fh);
+ put_device(&dev->dev);
-err_out:
- kfree(cdev);
return retval;
}
-int lirc_register_driver(struct lirc_driver *d)
+static int lirc_close(struct inode *inode, struct file *file)
{
- struct irctl *ir;
- int minor;
- int bytes_in_key;
- unsigned int chunk_size;
- unsigned int buffer_size;
- int err;
-
- if (!d) {
- printk(KERN_ERR "lirc_dev: lirc_register_driver: "
- "driver pointer must be not NULL!\n");
- err = -EBADRQC;
- goto out;
- }
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *dev = fh->rc;
+ unsigned long flags;
- if (!d->dev) {
- printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
- err = -EINVAL;
- goto out;
- }
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_del(&fh->list);
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
- if (MAX_IRCTL_DEVICES <= d->minor) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "\"minor\" must be between 0 and %d (%d)!\n",
- MAX_IRCTL_DEVICES - 1, d->minor);
- err = -EBADRQC;
- goto out;
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ kfifo_free(&fh->rawir);
+ if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
+ kfifo_free(&fh->scancodes);
+ kfree(fh);
+
+ rc_close(dev);
+ put_device(&dev->dev);
+
+ return 0;
+}
+
+static ssize_t lirc_transmit(struct file *file, const char __user *buf,
+ size_t n, loff_t *ppos)
+{
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *dev = fh->rc;
+ unsigned int *txbuf;
+ struct ir_raw_event *raw = NULL;
+ ssize_t ret;
+ size_t count;
+ ktime_t start;
+ s64 towait;
+ unsigned int duration = 0; /* signal duration in us */
+ int i;
+
+ ret = mutex_lock_interruptible(&dev->lock);
+ if (ret)
+ return ret;
+
+ if (!dev->registered) {
+ ret = -ENODEV;
+ goto out_unlock;
}
- if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "code length in bits for minor (%d) "
- "must be less than %d!\n",
- d->minor, BUFLEN * 8);
- err = -EBADRQC;
- goto out;
+ if (!dev->tx_ir) {
+ ret = -EINVAL;
+ goto out_unlock;
}
- dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
- d->sample_rate);
- if (d->sample_rate) {
- if (2 > d->sample_rate || HZ < d->sample_rate) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "sample_rate must be between 2 and %d!\n", HZ);
- err = -EBADRQC;
- goto out;
- }
- if (!d->add_to_buf) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "add_to_buf cannot be NULL when "
- "sample_rate is set\n");
- err = -EBADRQC;
- goto out;
+ if (fh->send_mode == LIRC_MODE_SCANCODE) {
+ struct lirc_scancode scan;
+
+ if (n != sizeof(scan)) {
+ ret = -EINVAL;
+ goto out_unlock;
}
- } else if (!(d->fops && d->fops->read) && !d->rbuf) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "fops->read and rbuf cannot all be NULL!\n");
- err = -EBADRQC;
- goto out;
- } else if (!d->rbuf) {
- if (!(d->fops && d->fops->read && d->fops->poll &&
- d->fops->unlocked_ioctl)) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "neither read, poll nor unlocked_ioctl can be NULL!\n");
- err = -EBADRQC;
- goto out;
+
+ if (copy_from_user(&scan, buf, sizeof(scan))) {
+ ret = -EFAULT;
+ goto out_unlock;
}
- }
- mutex_lock(&lirc_dev_lock);
+ if (scan.flags || scan.keycode || scan.timestamp ||
+ scan.rc_proto > RC_PROTO_MAX) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
- minor = d->minor;
+ /* We only have encoders for 32-bit protocols. */
+ if (scan.scancode > U32_MAX ||
+ !rc_validate_scancode(scan.rc_proto, scan.scancode)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
- if (minor < 0) {
- /* find first free slot for driver */
- for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
- if (!irctls[minor])
- break;
- if (MAX_IRCTL_DEVICES == minor) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "no free slots for drivers!\n");
- err = -ENOMEM;
- goto out_lock;
+ raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
+ if (!raw) {
+ ret = -ENOMEM;
+ goto out_unlock;
}
- } else if (irctls[minor]) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "minor (%d) just registered!\n", minor);
- err = -EBUSY;
- goto out_lock;
- }
- ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
- if (!ir) {
- err = -ENOMEM;
- goto out_lock;
- }
- lirc_irctl_init(ir);
- irctls[minor] = ir;
- d->minor = minor;
+ ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
+ raw, LIRCBUF_SIZE);
+ if (ret < 0)
+ goto out_kfree_raw;
- if (d->sample_rate) {
- ir->jiffies_to_wait = HZ / d->sample_rate;
- } else {
- /* it means - wait for external event in task queue */
- ir->jiffies_to_wait = 0;
- }
+ /* drop trailing space */
+ if (!(ret % 2))
+ count = ret - 1;
+ else
+ count = ret;
+
+ txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
+ if (!txbuf) {
+ ret = -ENOMEM;
+ goto out_kfree_raw;
+ }
- /* some safety check 8-) */
- d->name[sizeof(d->name)-1] = '\0';
+ for (i = 0; i < count; i++)
+ txbuf[i] = raw[i].duration;
- bytes_in_key = BITS_TO_LONGS(d->code_length) +
- (d->code_length % 8 ? 1 : 0);
- buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
- chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key;
+ if (dev->s_tx_carrier) {
+ int carrier = ir_raw_encode_carrier(scan.rc_proto);
- if (d->rbuf) {
- ir->buf = d->rbuf;
+ if (carrier > 0)
+ dev->s_tx_carrier(dev, carrier);
+ }
} else {
- ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!ir->buf) {
- err = -ENOMEM;
- goto out_lock;
+ if (n < sizeof(unsigned int) || n % sizeof(unsigned int)) {
+ ret = -EINVAL;
+ goto out_unlock;
}
- err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
- if (err) {
- kfree(ir->buf);
- goto out_lock;
+
+ count = n / sizeof(unsigned int);
+ if (count > LIRCBUF_SIZE || count % 2 == 0) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ txbuf = memdup_user(buf, n);
+ if (IS_ERR(txbuf)) {
+ ret = PTR_ERR(txbuf);
+ goto out_unlock;
}
}
- ir->chunk_size = ir->buf->chunk_size;
-
- if (d->features == 0)
- d->features = LIRC_CAN_REC_LIRCCODE;
-
- ir->d = *d;
-
- device_create(lirc_class, ir->d.dev,
- MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
- "lirc%u", ir->d.minor);
-
- if (d->sample_rate) {
- /* try to fire up polling thread */
- ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
- if (IS_ERR(ir->task)) {
- dev_err(d->dev, "lirc_dev: lirc_register_driver: "
- "cannot run poll thread for minor = %d\n",
- d->minor);
- err = -ECHILD;
- goto out_sysfs;
+
+ for (i = 0; i < count; i++) {
+ if (txbuf[i] > IR_MAX_DURATION - duration || !txbuf[i]) {
+ ret = -EINVAL;
+ goto out_kfree;
}
+
+ duration += txbuf[i];
}
- err = lirc_cdev_add(ir);
- if (err)
- goto out_sysfs;
+ start = ktime_get();
- ir->attached = 1;
- mutex_unlock(&lirc_dev_lock);
+ ret = dev->tx_ir(dev, txbuf, count);
+ if (ret < 0)
+ goto out_kfree;
- dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
- ir->d.name, ir->d.minor);
- return minor;
+ kfree(txbuf);
+ kfree(raw);
+ mutex_unlock(&dev->lock);
-out_sysfs:
- device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
-out_lock:
- mutex_unlock(&lirc_dev_lock);
-out:
- return err;
+ /*
+ * The lircd gap calculation expects the write function to
+ * wait for the actual IR signal to be transmitted before
+ * returning.
+ */
+ towait = ktime_us_delta(ktime_add_us(start, duration),
+ ktime_get());
+ if (towait > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(towait));
+ }
+
+ return n;
+out_kfree:
+ kfree(txbuf);
+out_kfree_raw:
+ kfree(raw);
+out_unlock:
+ mutex_unlock(&dev->lock);
+ return ret;
}
-EXPORT_SYMBOL(lirc_register_driver);
-int lirc_unregister_driver(int minor)
+static long lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct irctl *ir;
- struct cdev *cdev;
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *dev = fh->rc;
+ u32 __user *argp = (u32 __user *)(arg);
+ u32 val = 0;
+ int ret;
- if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
- printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
- "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);
- return -EBADRQC;
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = get_user(val, argp);
+ if (ret)
+ return ret;
}
- ir = irctls[minor];
- if (!ir) {
- printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
- "for minor %d!\n", __func__, minor);
- return -ENOENT;
+ ret = mutex_lock_interruptible(&dev->lock);
+ if (ret)
+ return ret;
+
+ if (!dev->registered) {
+ ret = -ENODEV;
+ goto out;
}
- cdev = ir->cdev;
+ switch (cmd) {
+ case LIRC_GET_FEATURES:
+ if (dev->driver_type == RC_DRIVER_SCANCODE)
+ val |= LIRC_CAN_REC_SCANCODE;
- mutex_lock(&lirc_dev_lock);
+ if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ val |= LIRC_CAN_REC_MODE2;
+ if (dev->rx_resolution)
+ val |= LIRC_CAN_GET_REC_RESOLUTION;
+ }
- if (ir->d.minor != minor) {
- printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
- "registered!\n", __func__, minor);
- mutex_unlock(&lirc_dev_lock);
- return -ENOENT;
- }
+ if (dev->tx_ir) {
+ val |= LIRC_CAN_SEND_PULSE;
+ if (dev->s_tx_mask)
+ val |= LIRC_CAN_SET_TRANSMITTER_MASK;
+ if (dev->s_tx_carrier)
+ val |= LIRC_CAN_SET_SEND_CARRIER;
+ if (dev->s_tx_duty_cycle)
+ val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
+ }
- /* end up polling thread */
- if (ir->task)
- kthread_stop(ir->task);
-
- dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
- ir->d.name, ir->d.minor);
-
- ir->attached = 0;
- if (ir->open) {
- dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
- ir->d.name, ir->d.minor);
- wake_up_interruptible(&ir->buf->wait_poll);
- mutex_lock(&ir->irctl_lock);
- ir->d.set_use_dec(ir->d.data);
- module_put(cdev->owner);
- mutex_unlock(&ir->irctl_lock);
- } else {
- lirc_irctl_cleanup(ir);
- cdev_del(cdev);
- kfree(cdev);
- kfree(ir);
- irctls[minor] = NULL;
- }
+ if (dev->s_rx_carrier_range)
+ val |= LIRC_CAN_SET_REC_CARRIER |
+ LIRC_CAN_SET_REC_CARRIER_RANGE;
- mutex_unlock(&lirc_dev_lock);
+ if (dev->s_wideband_receiver)
+ val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
- return 0;
-}
-EXPORT_SYMBOL(lirc_unregister_driver);
+ if (dev->s_carrier_report)
+ val |= LIRC_CAN_MEASURE_CARRIER;
-int lirc_dev_fop_open(struct inode *inode, struct file *file)
-{
- struct irctl *ir;
- struct cdev *cdev;
- int retval = 0;
+ if (dev->max_timeout)
+ val |= LIRC_CAN_SET_REC_TIMEOUT;
- if (iminor(inode) >= MAX_IRCTL_DEVICES) {
- printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
- iminor(inode));
- return -ENODEV;
- }
+ break;
+
+ /* mode support */
+ case LIRC_GET_REC_MODE:
+ if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
+ ret = -ENOTTY;
+ else
+ val = fh->rec_mode;
+ break;
- if (mutex_lock_interruptible(&lirc_dev_lock))
- return -ERESTARTSYS;
+ case LIRC_SET_REC_MODE:
+ switch (dev->driver_type) {
+ case RC_DRIVER_IR_RAW_TX:
+ ret = -ENOTTY;
+ break;
+ case RC_DRIVER_SCANCODE:
+ if (val != LIRC_MODE_SCANCODE)
+ ret = -EINVAL;
+ break;
+ case RC_DRIVER_IR_RAW:
+ if (!(val == LIRC_MODE_MODE2 ||
+ val == LIRC_MODE_SCANCODE))
+ ret = -EINVAL;
+ break;
+ }
- ir = irctls[iminor(inode)];
- if (!ir) {
- retval = -ENODEV;
- goto error;
- }
+ if (!ret)
+ fh->rec_mode = val;
+ break;
- dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
+ case LIRC_GET_SEND_MODE:
+ if (!dev->tx_ir)
+ ret = -ENOTTY;
+ else
+ val = fh->send_mode;
+ break;
- if (ir->d.minor == NOPLUG) {
- retval = -ENODEV;
- goto error;
- }
+ case LIRC_SET_SEND_MODE:
+ if (!dev->tx_ir)
+ ret = -ENOTTY;
+ else if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
+ ret = -EINVAL;
+ else
+ fh->send_mode = val;
+ break;
- if (ir->open) {
- retval = -EBUSY;
- goto error;
- }
+ /* TX settings */
+ case LIRC_SET_TRANSMITTER_MASK:
+ if (!dev->s_tx_mask)
+ ret = -ENOTTY;
+ else
+ ret = dev->s_tx_mask(dev, val);
+ break;
- cdev = ir->cdev;
- if (try_module_get(cdev->owner)) {
- ir->open++;
- retval = ir->d.set_use_inc(ir->d.data);
+ case LIRC_SET_SEND_CARRIER:
+ if (!dev->s_tx_carrier)
+ ret = -ENOTTY;
+ else
+ ret = dev->s_tx_carrier(dev, val);
+ break;
- if (retval) {
- module_put(cdev->owner);
- ir->open--;
- } else {
- lirc_buffer_clear(ir->buf);
- }
- if (ir->task)
- wake_up_process(ir->task);
- }
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ if (!dev->s_tx_duty_cycle)
+ ret = -ENOTTY;
+ else if (val <= 0 || val >= 100)
+ ret = -EINVAL;
+ else
+ ret = dev->s_tx_duty_cycle(dev, val);
+ break;
-error:
- if (ir)
- dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
- ir->d.name, ir->d.minor, retval);
+ /* RX settings */
+ case LIRC_SET_REC_CARRIER:
+ if (!dev->s_rx_carrier_range)
+ ret = -ENOTTY;
+ else if (val <= 0)
+ ret = -EINVAL;
+ else
+ ret = dev->s_rx_carrier_range(dev, fh->carrier_low,
+ val);
+ break;
- mutex_unlock(&lirc_dev_lock);
+ case LIRC_SET_REC_CARRIER_RANGE:
+ if (!dev->s_rx_carrier_range)
+ ret = -ENOTTY;
+ else if (val <= 0)
+ ret = -EINVAL;
+ else
+ fh->carrier_low = val;
+ break;
- nonseekable_open(inode, file);
+ case LIRC_GET_REC_RESOLUTION:
+ if (!dev->rx_resolution)
+ ret = -ENOTTY;
+ else
+ val = dev->rx_resolution;
+ break;
- return retval;
-}
-EXPORT_SYMBOL(lirc_dev_fop_open);
+ case LIRC_SET_WIDEBAND_RECEIVER:
+ if (!dev->s_wideband_receiver)
+ ret = -ENOTTY;
+ else
+ ret = dev->s_wideband_receiver(dev, !!val);
+ break;
-int lirc_dev_fop_close(struct inode *inode, struct file *file)
-{
- struct irctl *ir = irctls[iminor(inode)];
- struct cdev *cdev;
+ case LIRC_SET_MEASURE_CARRIER_MODE:
+ if (!dev->s_carrier_report)
+ ret = -ENOTTY;
+ else
+ ret = dev->s_carrier_report(dev, !!val);
+ break;
- if (!ir) {
- printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
- return -EINVAL;
- }
+ /* Generic timeout support */
+ case LIRC_GET_MIN_TIMEOUT:
+ if (!dev->max_timeout)
+ ret = -ENOTTY;
+ else
+ val = dev->min_timeout;
+ break;
- cdev = ir->cdev;
+ case LIRC_GET_MAX_TIMEOUT:
+ if (!dev->max_timeout)
+ ret = -ENOTTY;
+ else
+ val = dev->max_timeout;
+ break;
+
+ case LIRC_SET_REC_TIMEOUT:
+ if (!dev->max_timeout) {
+ ret = -ENOTTY;
+ } else {
+ if (val < dev->min_timeout || val > dev->max_timeout)
+ ret = -EINVAL;
+ else if (dev->s_timeout)
+ ret = dev->s_timeout(dev, val);
+ else
+ dev->timeout = val;
+ }
+ break;
- dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
+ case LIRC_GET_REC_TIMEOUT:
+ if (!dev->timeout)
+ ret = -ENOTTY;
+ else
+ val = dev->timeout;
+ break;
- WARN_ON(mutex_lock_killable(&lirc_dev_lock));
+ case LIRC_SET_REC_TIMEOUT_REPORTS:
+ if (dev->driver_type != RC_DRIVER_IR_RAW)
+ ret = -ENOTTY;
+ break;
- ir->open--;
- if (ir->attached) {
- ir->d.set_use_dec(ir->d.data);
- module_put(cdev->owner);
- } else {
- lirc_irctl_cleanup(ir);
- cdev_del(cdev);
- irctls[ir->d.minor] = NULL;
- kfree(cdev);
- kfree(ir);
+ default:
+ ret = -ENOTTY;
}
- mutex_unlock(&lirc_dev_lock);
+ if (!ret && _IOC_DIR(cmd) & _IOC_READ)
+ ret = put_user(val, argp);
- return 0;
+out:
+ mutex_unlock(&dev->lock);
+ return ret;
}
-EXPORT_SYMBOL(lirc_dev_fop_close);
-unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
+static __poll_t lirc_poll(struct file *file, struct poll_table_struct *wait)
{
- struct irctl *ir = irctls[iminor(file_inode(file))];
- unsigned int ret;
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *rcdev = fh->rc;
+ __poll_t events = 0;
+
+ poll_wait(file, &fh->wait_poll, wait);
- if (!ir) {
- printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
- return POLLERR;
+ if (!rcdev->registered) {
+ events = EPOLLHUP | EPOLLERR;
+ } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
+ if (fh->rec_mode == LIRC_MODE_SCANCODE &&
+ !kfifo_is_empty(&fh->scancodes))
+ events = EPOLLIN | EPOLLRDNORM;
+
+ if (fh->rec_mode == LIRC_MODE_MODE2 &&
+ !kfifo_is_empty(&fh->rawir))
+ events = EPOLLIN | EPOLLRDNORM;
}
- dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
+ return events;
+}
- if (!ir->attached)
- return POLLERR;
+static ssize_t lirc_read_mode2(struct file *file, char __user *buffer,
+ size_t length)
+{
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *rcdev = fh->rc;
+ unsigned int copied;
+ int ret;
- poll_wait(file, &ir->buf->wait_poll, wait);
+ if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
+ return -EINVAL;
- if (ir->buf)
- if (lirc_buffer_empty(ir->buf))
- ret = 0;
- else
- ret = POLLIN | POLLRDNORM;
- else
- ret = POLLERR;
+ do {
+ if (kfifo_is_empty(&fh->rawir)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(fh->wait_poll,
+ !kfifo_is_empty(&fh->rawir) ||
+ !rcdev->registered);
+ if (ret)
+ return ret;
+ }
- dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
- ir->d.name, ir->d.minor, ret);
+ if (!rcdev->registered)
+ return -ENODEV;
- return ret;
+ ret = mutex_lock_interruptible(&rcdev->lock);
+ if (ret)
+ return ret;
+ ret = kfifo_to_user(&fh->rawir, buffer, length, &copied);
+ mutex_unlock(&rcdev->lock);
+ if (ret)
+ return ret;
+ } while (copied == 0);
+
+ return copied;
}
-EXPORT_SYMBOL(lirc_dev_fop_poll);
-long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static ssize_t lirc_read_scancode(struct file *file, char __user *buffer,
+ size_t length)
{
- __u32 mode;
- int result = 0;
- struct irctl *ir = irctls[iminor(file_inode(file))];
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *rcdev = fh->rc;
+ unsigned int copied;
+ int ret;
- if (!ir) {
- printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
- return -ENODEV;
- }
+ if (length < sizeof(struct lirc_scancode) ||
+ length % sizeof(struct lirc_scancode))
+ return -EINVAL;
- dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
- ir->d.name, ir->d.minor, cmd);
+ do {
+ if (kfifo_is_empty(&fh->scancodes)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(fh->wait_poll,
+ !kfifo_is_empty(&fh->scancodes) ||
+ !rcdev->registered);
+ if (ret)
+ return ret;
+ }
- if (ir->d.minor == NOPLUG || !ir->attached) {
- dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
- ir->d.name, ir->d.minor);
- return -ENODEV;
- }
+ if (!rcdev->registered)
+ return -ENODEV;
- mutex_lock(&ir->irctl_lock);
+ ret = mutex_lock_interruptible(&rcdev->lock);
+ if (ret)
+ return ret;
+ ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied);
+ mutex_unlock(&rcdev->lock);
+ if (ret)
+ return ret;
+ } while (copied == 0);
- switch (cmd) {
- case LIRC_GET_FEATURES:
- result = put_user(ir->d.features, (__u32 *)arg);
- break;
- case LIRC_GET_REC_MODE:
- if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
- result = -ENOSYS;
- break;
- }
+ return copied;
+}
- result = put_user(LIRC_REC2MODE
- (ir->d.features & LIRC_CAN_REC_MASK),
- (__u32 *)arg);
- break;
- case LIRC_SET_REC_MODE:
- if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
- result = -ENOSYS;
- break;
- }
+static ssize_t lirc_read(struct file *file, char __user *buffer, size_t length,
+ loff_t *ppos)
+{
+ struct lirc_fh *fh = file->private_data;
+ struct rc_dev *rcdev = fh->rc;
- result = get_user(mode, (__u32 *)arg);
- if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
- result = -EINVAL;
- /*
- * FIXME: We should actually set the mode somehow but
- * for now, lirc_serial doesn't support mode changing either
- */
- break;
- case LIRC_GET_LENGTH:
- result = put_user(ir->d.code_length, (__u32 *)arg);
- break;
- case LIRC_GET_MIN_TIMEOUT:
- if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
- ir->d.min_timeout == 0) {
- result = -ENOSYS;
- break;
- }
+ if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
+ return -EINVAL;
- result = put_user(ir->d.min_timeout, (__u32 *)arg);
- break;
- case LIRC_GET_MAX_TIMEOUT:
- if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
- ir->d.max_timeout == 0) {
- result = -ENOSYS;
- break;
- }
+ if (!rcdev->registered)
+ return -ENODEV;
- result = put_user(ir->d.max_timeout, (__u32 *)arg);
- break;
- default:
- result = -EINVAL;
- }
+ if (fh->rec_mode == LIRC_MODE_MODE2)
+ return lirc_read_mode2(file, buffer, length);
+ else /* LIRC_MODE_SCANCODE */
+ return lirc_read_scancode(file, buffer, length);
+}
- dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
- ir->d.name, ir->d.minor, result);
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_transmit,
+ .unlocked_ioctl = lirc_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .read = lirc_read,
+ .poll = lirc_poll,
+ .open = lirc_open,
+ .release = lirc_close,
+};
- mutex_unlock(&ir->irctl_lock);
+static void lirc_release_device(struct device *ld)
+{
+ struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev);
- return result;
+ put_device(&rcdev->dev);
}
-EXPORT_SYMBOL(lirc_dev_fop_ioctl);
-ssize_t lirc_dev_fop_read(struct file *file,
- char __user *buffer,
- size_t length,
- loff_t *ppos)
+int lirc_register(struct rc_dev *dev)
{
- struct irctl *ir = irctls[iminor(file_inode(file))];
- unsigned char *buf;
- int ret = 0, written = 0;
- DECLARE_WAITQUEUE(wait, current);
+ const char *rx_type, *tx_type;
+ int err, minor;
- if (!ir) {
- printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
- return -ENODEV;
- }
+ minor = ida_alloc_max(&lirc_ida, RC_DEV_MAX - 1, GFP_KERNEL);
+ if (minor < 0)
+ return minor;
- dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
+ device_initialize(&dev->lirc_dev);
+ dev->lirc_dev.class = &lirc_class;
+ dev->lirc_dev.parent = &dev->dev;
+ dev->lirc_dev.release = lirc_release_device;
+ dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor);
+ dev_set_name(&dev->lirc_dev, "lirc%d", minor);
- buf = kzalloc(ir->chunk_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ INIT_LIST_HEAD(&dev->lirc_fh);
+ spin_lock_init(&dev->lirc_fh_lock);
- if (mutex_lock_interruptible(&ir->irctl_lock)) {
- ret = -ERESTARTSYS;
- goto out_unlocked;
- }
- if (!ir->attached) {
- ret = -ENODEV;
- goto out_locked;
- }
+ cdev_init(&dev->lirc_cdev, &lirc_fops);
- if (length % ir->chunk_size) {
- ret = -EINVAL;
- goto out_locked;
- }
+ get_device(&dev->dev);
- /*
- * we add ourselves to the task queue before buffer check
- * to avoid losing scan code (in case when queue is awaken somewhere
- * between while condition checking and scheduling)
- */
- add_wait_queue(&ir->buf->wait_poll, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
+ err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev);
+ if (err)
+ goto out_put_device;
- /*
- * while we didn't provide 'length' bytes, device is opened in blocking
- * mode and 'copy_to_user' is happy, wait for data.
- */
- while (written < length && ret == 0) {
- if (lirc_buffer_empty(ir->buf)) {
- /* According to the read(2) man page, 'written' can be
- * returned as less than 'length', instead of blocking
- * again, returning -EWOULDBLOCK, or returning
- * -ERESTARTSYS */
- if (written)
- break;
- if (file->f_flags & O_NONBLOCK) {
- ret = -EWOULDBLOCK;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
-
- mutex_unlock(&ir->irctl_lock);
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (mutex_lock_interruptible(&ir->irctl_lock)) {
- ret = -ERESTARTSYS;
- remove_wait_queue(&ir->buf->wait_poll, &wait);
- set_current_state(TASK_RUNNING);
- goto out_unlocked;
- }
-
- if (!ir->attached) {
- ret = -ENODEV;
- break;
- }
- } else {
- lirc_buffer_read(ir->buf, buf);
- ret = copy_to_user((void *)buffer+written, buf,
- ir->buf->chunk_size);
- if (!ret)
- written += ir->buf->chunk_size;
- else
- ret = -EFAULT;
- }
+ switch (dev->driver_type) {
+ case RC_DRIVER_SCANCODE:
+ rx_type = "scancode";
+ break;
+ case RC_DRIVER_IR_RAW:
+ rx_type = "raw IR";
+ break;
+ default:
+ rx_type = "no";
+ break;
}
- remove_wait_queue(&ir->buf->wait_poll, &wait);
- set_current_state(TASK_RUNNING);
-
-out_locked:
- mutex_unlock(&ir->irctl_lock);
+ if (dev->tx_ir)
+ tx_type = "raw IR";
+ else
+ tx_type = "no";
-out_unlocked:
- kfree(buf);
- dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
- ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret);
+ dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter",
+ dev->driver_name, minor, rx_type, tx_type);
- return ret ? ret : written;
-}
-EXPORT_SYMBOL(lirc_dev_fop_read);
+ return 0;
-void *lirc_get_pdata(struct file *file)
-{
- return irctls[iminor(file_inode(file))]->d.data;
+out_put_device:
+ put_device(&dev->lirc_dev);
+ ida_free(&lirc_ida, minor);
+ return err;
}
-EXPORT_SYMBOL(lirc_get_pdata);
-
-ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
- size_t length, loff_t *ppos)
+void lirc_unregister(struct rc_dev *dev)
{
- struct irctl *ir = irctls[iminor(file_inode(file))];
+ unsigned long flags;
+ struct lirc_fh *fh;
- if (!ir) {
- printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
- return -ENODEV;
- }
+ dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n",
+ dev->driver_name, MINOR(dev->lirc_dev.devt));
- dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
+ spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+ list_for_each_entry(fh, &dev->lirc_fh, list)
+ wake_up_poll(&fh->wait_poll, EPOLLHUP | EPOLLERR);
+ spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
- if (!ir->attached)
- return -ENODEV;
-
- return -EINVAL;
+ cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev);
+ ida_free(&lirc_ida, MINOR(dev->lirc_dev.devt));
}
-EXPORT_SYMBOL(lirc_dev_fop_write);
-
-static int __init lirc_dev_init(void)
+int __init lirc_dev_init(void)
{
int retval;
- lirc_class = class_create(THIS_MODULE, "lirc");
- if (IS_ERR(lirc_class)) {
- retval = PTR_ERR(lirc_class);
- printk(KERN_ERR "lirc_dev: class_create failed\n");
- goto error;
- }
+ retval = class_register(&lirc_class);
+ if (retval)
+ return retval;
- retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
- IRCTL_DEV_NAME);
+ retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, "lirc");
if (retval) {
- class_destroy(lirc_class);
- printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
- goto error;
+ class_unregister(&lirc_class);
+ pr_err("alloc_chrdev_region failed\n");
+ return retval;
}
+ pr_debug("IR Remote Control driver registered, major %d\n",
+ MAJOR(lirc_base_dev));
- printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
- "major %d \n", MAJOR(lirc_base_dev));
+ return 0;
+}
-error:
- return retval;
+void __exit lirc_dev_exit(void)
+{
+ class_unregister(&lirc_class);
+ unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
}
+struct rc_dev *rc_dev_get_from_fd(int fd, bool write)
+{
+ CLASS(fd, f)(fd);
+ struct lirc_fh *fh;
+ struct rc_dev *dev;
+ if (fd_empty(f))
+ return ERR_PTR(-EBADF);
-static void __exit lirc_dev_exit(void)
-{
- class_destroy(lirc_class);
- unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
- printk(KERN_INFO "lirc_dev: module unloaded\n");
-}
+ if (fd_file(f)->f_op != &lirc_fops)
+ return ERR_PTR(-EINVAL);
+
+ if (write && !(fd_file(f)->f_mode & FMODE_WRITE))
+ return ERR_PTR(-EPERM);
-module_init(lirc_dev_init);
-module_exit(lirc_dev_exit);
+ fh = fd_file(f)->private_data;
+ dev = fh->rc;
-MODULE_DESCRIPTION("LIRC base driver module");
-MODULE_AUTHOR("Artur Lipowski");
-MODULE_LICENSE("GPL");
+ get_device(&dev->dev);
+
+ return dev;
+}
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
+MODULE_ALIAS("lirc_dev");