Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 1 | /* |
| 2 | * LIRC base driver |
| 3 | * |
| 4 | * by Artur Lipowski <alipowski@interia.pl> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 16 | */ |
| 17 | |
Andi Shyti | 3fac031 | 2016-07-06 06:01:16 -0300 | [diff] [blame] | 18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 19 | |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 20 | #include <linux/module.h> |
Ingo Molnar | 174cd4b | 2017-02-02 19:15:33 +0100 | [diff] [blame] | 21 | #include <linux/sched/signal.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 22 | #include <linux/ioctl.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 23 | #include <linux/poll.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 24 | #include <linux/mutex.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 25 | #include <linux/device.h> |
| 26 | #include <linux/cdev.h> |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 27 | #include <linux/idr.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 28 | |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 29 | #include "rc-core-priv.h" |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 30 | #include <media/lirc.h> |
Jarod Wilson | 5690085 | 2010-07-16 14:25:33 -0300 | [diff] [blame] | 31 | #include <media/lirc_dev.h> |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 32 | |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 33 | #define LOGHEAD "lirc_dev (%s[%d]): " |
| 34 | |
| 35 | static dev_t lirc_base_dev; |
| 36 | |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 37 | /* Used to keep track of allocated lirc devices */ |
| 38 | #define LIRC_MAX_DEVICES 256 |
| 39 | static DEFINE_IDA(lirc_ida); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 40 | |
| 41 | /* Only used for sysfs but defined to void otherwise */ |
| 42 | static struct class *lirc_class; |
| 43 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 44 | static void lirc_release_device(struct device *ld) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 45 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 46 | struct lirc_dev *d = container_of(ld, struct lirc_dev, dev); |
Sean Young | a607f51 | 2017-08-04 10:12:03 -0400 | [diff] [blame] | 47 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 48 | put_device(d->dev.parent); |
| 49 | |
| 50 | if (d->buf_internal) { |
| 51 | lirc_buffer_free(d->buf); |
| 52 | kfree(d->buf); |
| 53 | d->buf = NULL; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 54 | } |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 55 | kfree(d); |
| 56 | module_put(THIS_MODULE); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 57 | } |
| 58 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 59 | static int lirc_allocate_buffer(struct lirc_dev *d) |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 60 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 61 | int err; |
Sean Young | 74c839b | 2017-01-30 13:49:58 -0200 | [diff] [blame] | 62 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 63 | if (d->buf) { |
| 64 | d->buf_internal = false; |
| 65 | return 0; |
Andi Shyti | 6fa99e1 | 2016-07-06 06:01:13 -0300 | [diff] [blame] | 66 | } |
Andi Shyti | 6fa99e1 | 2016-07-06 06:01:13 -0300 | [diff] [blame] | 67 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 68 | d->buf = kmalloc(sizeof(*d->buf), GFP_KERNEL); |
| 69 | if (!d->buf) |
| 70 | return -ENOMEM; |
| 71 | |
| 72 | err = lirc_buffer_init(d->buf, d->chunk_size, d->buffer_size); |
| 73 | if (err) { |
| 74 | kfree(d->buf); |
| 75 | d->buf = NULL; |
| 76 | return err; |
| 77 | } |
| 78 | |
| 79 | d->buf_internal = true; |
| 80 | return 0; |
Andi Shyti | 6fa99e1 | 2016-07-06 06:01:13 -0300 | [diff] [blame] | 81 | } |
| 82 | |
David Härdeman | 6ecccc3 | 2017-06-25 09:32:15 -0300 | [diff] [blame] | 83 | struct lirc_dev * |
| 84 | lirc_allocate_device(void) |
| 85 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 86 | struct lirc_dev *d; |
| 87 | |
| 88 | d = kzalloc(sizeof(*d), GFP_KERNEL); |
| 89 | if (d) { |
| 90 | mutex_init(&d->mutex); |
| 91 | device_initialize(&d->dev); |
| 92 | d->dev.class = lirc_class; |
| 93 | d->dev.release = lirc_release_device; |
| 94 | __module_get(THIS_MODULE); |
| 95 | } |
| 96 | |
| 97 | return d; |
David Härdeman | 6ecccc3 | 2017-06-25 09:32:15 -0300 | [diff] [blame] | 98 | } |
| 99 | EXPORT_SYMBOL(lirc_allocate_device); |
| 100 | |
| 101 | void lirc_free_device(struct lirc_dev *d) |
| 102 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 103 | if (!d) |
| 104 | return; |
| 105 | |
| 106 | put_device(&d->dev); |
David Härdeman | 6ecccc3 | 2017-06-25 09:32:15 -0300 | [diff] [blame] | 107 | } |
| 108 | EXPORT_SYMBOL(lirc_free_device); |
| 109 | |
David Härdeman | 5ddc9c0 | 2017-09-21 16:13:34 -0300 | [diff] [blame] | 110 | int lirc_register_device(struct lirc_dev *d) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 111 | { |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 112 | int minor; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 113 | int err; |
| 114 | |
| 115 | if (!d) { |
Andi Shyti | 3fac031 | 2016-07-06 06:01:16 -0300 | [diff] [blame] | 116 | pr_err("driver pointer must be not NULL!\n"); |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 117 | return -EBADRQC; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 118 | } |
| 119 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 120 | if (!d->dev.parent) { |
| 121 | pr_err("dev parent pointer not filled in!\n"); |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 122 | return -EINVAL; |
Jarod Wilson | 715d29a7 | 2010-10-18 12:02:01 -0300 | [diff] [blame] | 123 | } |
| 124 | |
David Härdeman | 712551f | 2017-05-01 13:04:21 -0300 | [diff] [blame] | 125 | if (!d->fops) { |
| 126 | pr_err("fops pointer not filled in!\n"); |
| 127 | return -EINVAL; |
| 128 | } |
| 129 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 130 | if (!d->buf && d->chunk_size < 1) { |
David Härdeman | b145ef9 | 2017-06-25 09:31:45 -0300 | [diff] [blame] | 131 | pr_err("chunk_size must be set!\n"); |
| 132 | return -EINVAL; |
| 133 | } |
| 134 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 135 | if (!d->buf && d->buffer_size < 1) { |
David Härdeman | b145ef9 | 2017-06-25 09:31:45 -0300 | [diff] [blame] | 136 | pr_err("buffer_size must be set!\n"); |
| 137 | return -EINVAL; |
| 138 | } |
| 139 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 140 | if (!d->buf && !(d->fops && d->fops->read && |
| 141 | d->fops->poll && d->fops->unlocked_ioctl)) { |
| 142 | dev_err(&d->dev, "undefined read, poll, ioctl\n"); |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 143 | return -EBADRQC; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 144 | } |
| 145 | |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 146 | /* some safety check 8-) */ |
| 147 | d->name[sizeof(d->name) - 1] = '\0'; |
| 148 | |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 149 | if (LIRC_CAN_REC(d->features)) { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 150 | err = lirc_allocate_buffer(d); |
| 151 | if (err) |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 152 | return err; |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 153 | } |
| 154 | |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 155 | minor = ida_simple_get(&lirc_ida, 0, LIRC_MAX_DEVICES, GFP_KERNEL); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 156 | if (minor < 0) |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 157 | return minor; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 158 | |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 159 | d->minor = minor; |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 160 | d->dev.devt = MKDEV(MAJOR(lirc_base_dev), d->minor); |
| 161 | dev_set_name(&d->dev, "lirc%d", d->minor); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 162 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 163 | cdev_init(&d->cdev, d->fops); |
| 164 | d->cdev.owner = d->owner; |
| 165 | d->attached = true; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 166 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 167 | err = cdev_device_add(&d->cdev, &d->dev); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 168 | if (err) { |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 169 | ida_simple_remove(&lirc_ida, minor); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 170 | return err; |
| 171 | } |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 172 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 173 | get_device(d->dev.parent); |
Sean Young | a607f51 | 2017-08-04 10:12:03 -0400 | [diff] [blame] | 174 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 175 | dev_info(&d->dev, "lirc_dev: driver %s registered at minor = %d\n", |
| 176 | d->name, d->minor); |
David Härdeman | 56481f0 | 2017-05-01 13:04:11 -0300 | [diff] [blame] | 177 | |
David Härdeman | c3c6dd7 | 2017-06-25 09:31:24 -0300 | [diff] [blame] | 178 | return 0; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 179 | } |
David Härdeman | 5ddc9c0 | 2017-09-21 16:13:34 -0300 | [diff] [blame] | 180 | EXPORT_SYMBOL(lirc_register_device); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 181 | |
David Härdeman | 5ddc9c0 | 2017-09-21 16:13:34 -0300 | [diff] [blame] | 182 | void lirc_unregister_device(struct lirc_dev *d) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 183 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 184 | if (!d) |
David Härdeman | c3c6dd7 | 2017-06-25 09:31:24 -0300 | [diff] [blame] | 185 | return; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 186 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 187 | dev_dbg(&d->dev, "lirc_dev: driver %s unregistered from minor = %d\n", |
David Härdeman | c3c6dd7 | 2017-06-25 09:31:24 -0300 | [diff] [blame] | 188 | d->name, d->minor); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 189 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 190 | mutex_lock(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 191 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 192 | d->attached = false; |
| 193 | if (d->open) { |
| 194 | dev_dbg(&d->dev, LOGHEAD "releasing opened driver\n", |
David Härdeman | c3c6dd7 | 2017-06-25 09:31:24 -0300 | [diff] [blame] | 195 | d->name, d->minor); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 196 | wake_up_interruptible(&d->buf->wait_poll); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 197 | } |
| 198 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 199 | mutex_unlock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 200 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 201 | cdev_device_del(&d->cdev, &d->dev); |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 202 | ida_simple_remove(&lirc_ida, d->minor); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 203 | put_device(&d->dev); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 204 | } |
David Härdeman | 5ddc9c0 | 2017-09-21 16:13:34 -0300 | [diff] [blame] | 205 | EXPORT_SYMBOL(lirc_unregister_device); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 206 | |
| 207 | int lirc_dev_fop_open(struct inode *inode, struct file *file) |
| 208 | { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 209 | struct lirc_dev *d = container_of(inode->i_cdev, struct lirc_dev, cdev); |
David Härdeman | de226ec | 2017-06-25 09:31:19 -0300 | [diff] [blame] | 210 | int retval; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 211 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 212 | dev_dbg(&d->dev, LOGHEAD "open called\n", d->name, d->minor); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 213 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 214 | retval = mutex_lock_interruptible(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 215 | if (retval) |
| 216 | return retval; |
| 217 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 218 | if (!d->attached) { |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 219 | retval = -ENODEV; |
| 220 | goto out; |
| 221 | } |
| 222 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 223 | if (d->open) { |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 224 | retval = -EBUSY; |
| 225 | goto out; |
| 226 | } |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 227 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 228 | if (d->rdev) { |
| 229 | retval = rc_open(d->rdev); |
Srinivas Kandagatla | ca7a722 | 2013-07-22 04:23:07 -0300 | [diff] [blame] | 230 | if (retval) |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 231 | goto out; |
Srinivas Kandagatla | ca7a722 | 2013-07-22 04:23:07 -0300 | [diff] [blame] | 232 | } |
| 233 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 234 | if (d->buf) |
| 235 | lirc_buffer_clear(d->buf); |
David Härdeman | 2c5a1f44 | 2017-05-01 13:03:46 -0300 | [diff] [blame] | 236 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 237 | d->open++; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 238 | |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 239 | file->private_data = d->rdev; |
Arnd Bergmann | d9d2e9d | 2010-08-15 18:51:56 +0200 | [diff] [blame] | 240 | nonseekable_open(inode, file); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 241 | mutex_unlock(&d->mutex); |
Arnd Bergmann | d9d2e9d | 2010-08-15 18:51:56 +0200 | [diff] [blame] | 242 | |
David Härdeman | de226ec | 2017-06-25 09:31:19 -0300 | [diff] [blame] | 243 | return 0; |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 244 | |
| 245 | out: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 246 | mutex_unlock(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 247 | return retval; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 248 | } |
| 249 | EXPORT_SYMBOL(lirc_dev_fop_open); |
| 250 | |
| 251 | int lirc_dev_fop_close(struct inode *inode, struct file *file) |
| 252 | { |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 253 | struct rc_dev *rcdev = file->private_data; |
| 254 | struct lirc_dev *d = rcdev->lirc_dev; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 255 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 256 | mutex_lock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 257 | |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 258 | rc_close(rcdev); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 259 | d->open--; |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 260 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 261 | mutex_unlock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 262 | |
| 263 | return 0; |
| 264 | } |
| 265 | EXPORT_SYMBOL(lirc_dev_fop_close); |
| 266 | |
| 267 | unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) |
| 268 | { |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 269 | struct rc_dev *rcdev = file->private_data; |
| 270 | struct lirc_dev *d = rcdev->lirc_dev; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 271 | unsigned int ret; |
| 272 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 273 | if (!d->attached) |
David Härdeman | 29debf3 | 2017-05-01 13:04:37 -0300 | [diff] [blame] | 274 | return POLLHUP | POLLERR; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 275 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 276 | if (d->buf) { |
| 277 | poll_wait(file, &d->buf->wait_poll, wait); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 278 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 279 | if (lirc_buffer_empty(d->buf)) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 280 | ret = 0; |
| 281 | else |
| 282 | ret = POLLIN | POLLRDNORM; |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 283 | } else { |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 284 | ret = POLLERR; |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 285 | } |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 286 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 287 | dev_dbg(&d->dev, LOGHEAD "poll result = %d\n", d->name, d->minor, ret); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 288 | |
| 289 | return ret; |
| 290 | } |
| 291 | EXPORT_SYMBOL(lirc_dev_fop_poll); |
| 292 | |
Arnd Bergmann | 044e587 | 2010-08-02 15:43:35 -0300 | [diff] [blame] | 293 | long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 294 | { |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 295 | struct rc_dev *rcdev = file->private_data; |
| 296 | struct lirc_dev *d = rcdev->lirc_dev; |
Jarod Wilson | be1f985 | 2010-10-08 17:24:21 -0300 | [diff] [blame] | 297 | __u32 mode; |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 298 | int result; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 299 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 300 | dev_dbg(&d->dev, LOGHEAD "ioctl called (0x%x)\n", |
| 301 | d->name, d->minor, cmd); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 302 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 303 | result = mutex_lock_interruptible(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 304 | if (result) |
| 305 | return result; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 306 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 307 | if (!d->attached) { |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 308 | result = -ENODEV; |
| 309 | goto out; |
| 310 | } |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 311 | |
| 312 | switch (cmd) { |
| 313 | case LIRC_GET_FEATURES: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 314 | result = put_user(d->features, (__u32 __user *)arg); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 315 | break; |
| 316 | case LIRC_GET_REC_MODE: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 317 | if (!LIRC_CAN_REC(d->features)) { |
Andi Shyti | b408809 | 2016-07-06 06:01:24 -0300 | [diff] [blame] | 318 | result = -ENOTTY; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 319 | break; |
| 320 | } |
| 321 | |
| 322 | result = put_user(LIRC_REC2MODE |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 323 | (d->features & LIRC_CAN_REC_MASK), |
Hans Verkuil | 60519af | 2014-08-20 19:41:03 -0300 | [diff] [blame] | 324 | (__u32 __user *)arg); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 325 | break; |
| 326 | case LIRC_SET_REC_MODE: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 327 | if (!LIRC_CAN_REC(d->features)) { |
Andi Shyti | b408809 | 2016-07-06 06:01:24 -0300 | [diff] [blame] | 328 | result = -ENOTTY; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 329 | break; |
| 330 | } |
| 331 | |
Hans Verkuil | 60519af | 2014-08-20 19:41:03 -0300 | [diff] [blame] | 332 | result = get_user(mode, (__u32 __user *)arg); |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 333 | if (!result && !(LIRC_MODE2REC(mode) & d->features)) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 334 | result = -EINVAL; |
| 335 | /* |
| 336 | * FIXME: We should actually set the mode somehow but |
| 337 | * for now, lirc_serial doesn't support mode changing either |
| 338 | */ |
| 339 | break; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 340 | default: |
Sean Young | 5c862758 | 2017-01-26 15:19:33 -0200 | [diff] [blame] | 341 | result = -ENOTTY; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 342 | } |
| 343 | |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 344 | out: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 345 | mutex_unlock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 346 | return result; |
| 347 | } |
| 348 | EXPORT_SYMBOL(lirc_dev_fop_ioctl); |
| 349 | |
| 350 | ssize_t lirc_dev_fop_read(struct file *file, |
Dan Carpenter | 0e83508 | 2010-11-17 02:13:39 -0300 | [diff] [blame] | 351 | char __user *buffer, |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 352 | size_t length, |
| 353 | loff_t *ppos) |
| 354 | { |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 355 | struct rc_dev *rcdev = file->private_data; |
| 356 | struct lirc_dev *d = rcdev->lirc_dev; |
Jarod Wilson | 715d29a7 | 2010-10-18 12:02:01 -0300 | [diff] [blame] | 357 | unsigned char *buf; |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 358 | int ret, written = 0; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 359 | DECLARE_WAITQUEUE(wait, current); |
| 360 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 361 | buf = kzalloc(d->buf->chunk_size, GFP_KERNEL); |
Jarod Wilson | 715d29a7 | 2010-10-18 12:02:01 -0300 | [diff] [blame] | 362 | if (!buf) |
| 363 | return -ENOMEM; |
| 364 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 365 | dev_dbg(&d->dev, LOGHEAD "read called\n", d->name, d->minor); |
| 366 | |
| 367 | ret = mutex_lock_interruptible(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 368 | if (ret) { |
| 369 | kfree(buf); |
| 370 | return ret; |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 371 | } |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 372 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 373 | if (!d->attached) { |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 374 | ret = -ENODEV; |
| 375 | goto out_locked; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 376 | } |
| 377 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 378 | if (!LIRC_CAN_REC(d->features)) { |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 379 | ret = -EINVAL; |
| 380 | goto out_locked; |
| 381 | } |
| 382 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 383 | if (length % d->buf->chunk_size) { |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 384 | ret = -EINVAL; |
| 385 | goto out_locked; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 386 | } |
| 387 | |
| 388 | /* |
| 389 | * we add ourselves to the task queue before buffer check |
| 390 | * to avoid losing scan code (in case when queue is awaken somewhere |
| 391 | * between while condition checking and scheduling) |
| 392 | */ |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 393 | add_wait_queue(&d->buf->wait_poll, &wait); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 394 | |
| 395 | /* |
| 396 | * while we didn't provide 'length' bytes, device is opened in blocking |
| 397 | * mode and 'copy_to_user' is happy, wait for data. |
| 398 | */ |
| 399 | while (written < length && ret == 0) { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 400 | if (lirc_buffer_empty(d->buf)) { |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 401 | /* According to the read(2) man page, 'written' can be |
| 402 | * returned as less than 'length', instead of blocking |
| 403 | * again, returning -EWOULDBLOCK, or returning |
Andi Shyti | 62e9268 | 2016-07-06 06:01:25 -0300 | [diff] [blame] | 404 | * -ERESTARTSYS |
| 405 | */ |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 406 | if (written) |
| 407 | break; |
| 408 | if (file->f_flags & O_NONBLOCK) { |
| 409 | ret = -EWOULDBLOCK; |
| 410 | break; |
| 411 | } |
| 412 | if (signal_pending(current)) { |
| 413 | ret = -ERESTARTSYS; |
| 414 | break; |
| 415 | } |
| 416 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 417 | mutex_unlock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 418 | set_current_state(TASK_INTERRUPTIBLE); |
Sean Young | 12accdc | 2016-10-31 15:52:25 -0200 | [diff] [blame] | 419 | schedule(); |
| 420 | set_current_state(TASK_RUNNING); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 421 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 422 | ret = mutex_lock_interruptible(&d->mutex); |
David Härdeman | 3381b77 | 2017-06-30 05:41:57 -0300 | [diff] [blame] | 423 | if (ret) { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 424 | remove_wait_queue(&d->buf->wait_poll, &wait); |
Jarod Wilson | 69c271f | 2010-07-07 11:29:44 -0300 | [diff] [blame] | 425 | goto out_unlocked; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 426 | } |
| 427 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 428 | if (!d->attached) { |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 429 | ret = -ENODEV; |
Sean Young | c77d17c0 | 2016-10-31 15:52:27 -0200 | [diff] [blame] | 430 | goto out_locked; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 431 | } |
| 432 | } else { |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 433 | lirc_buffer_read(d->buf, buf); |
Hans Verkuil | 60519af | 2014-08-20 19:41:03 -0300 | [diff] [blame] | 434 | ret = copy_to_user((void __user *)buffer+written, buf, |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 435 | d->buf->chunk_size); |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 436 | if (!ret) |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 437 | written += d->buf->chunk_size; |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 438 | else |
| 439 | ret = -EFAULT; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 440 | } |
| 441 | } |
| 442 | |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 443 | remove_wait_queue(&d->buf->wait_poll, &wait); |
Dan Carpenter | 250f7a5 | 2010-11-17 02:20:15 -0300 | [diff] [blame] | 444 | |
| 445 | out_locked: |
David Härdeman | b15e393 | 2017-06-25 09:32:36 -0300 | [diff] [blame] | 446 | mutex_unlock(&d->mutex); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 447 | |
Jarod Wilson | 69c271f | 2010-07-07 11:29:44 -0300 | [diff] [blame] | 448 | out_unlocked: |
Jarod Wilson | 715d29a7 | 2010-10-18 12:02:01 -0300 | [diff] [blame] | 449 | kfree(buf); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 450 | |
| 451 | return ret ? ret : written; |
| 452 | } |
| 453 | EXPORT_SYMBOL(lirc_dev_fop_read); |
| 454 | |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 455 | int __init lirc_dev_init(void) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 456 | { |
| 457 | int retval; |
| 458 | |
| 459 | lirc_class = class_create(THIS_MODULE, "lirc"); |
| 460 | if (IS_ERR(lirc_class)) { |
Andi Shyti | 3fac031 | 2016-07-06 06:01:16 -0300 | [diff] [blame] | 461 | pr_err("class_create failed\n"); |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 462 | return PTR_ERR(lirc_class); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 463 | } |
| 464 | |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 465 | retval = alloc_chrdev_region(&lirc_base_dev, 0, LIRC_MAX_DEVICES, |
David Härdeman | 463015d | 2017-05-01 13:04:47 -0300 | [diff] [blame] | 466 | "BaseRemoteCtl"); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 467 | if (retval) { |
| 468 | class_destroy(lirc_class); |
Andi Shyti | 3fac031 | 2016-07-06 06:01:16 -0300 | [diff] [blame] | 469 | pr_err("alloc_chrdev_region failed\n"); |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 470 | return retval; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 471 | } |
| 472 | |
Andi Shyti | 3fac031 | 2016-07-06 06:01:16 -0300 | [diff] [blame] | 473 | pr_info("IR Remote Control driver registered, major %d\n", |
| 474 | MAJOR(lirc_base_dev)); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 475 | |
Andi Shyti | 54fceca | 2016-07-06 06:01:17 -0300 | [diff] [blame] | 476 | return 0; |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 477 | } |
| 478 | |
Sean Young | a60d64b | 2017-09-23 10:41:13 -0400 | [diff] [blame^] | 479 | void __exit lirc_dev_exit(void) |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 480 | { |
| 481 | class_destroy(lirc_class); |
David Härdeman | 46c8f47 | 2017-06-25 09:32:05 -0300 | [diff] [blame] | 482 | unregister_chrdev_region(lirc_base_dev, LIRC_MAX_DEVICES); |
Jarod Wilson | 4a62a5a | 2010-07-03 01:06:57 -0300 | [diff] [blame] | 483 | } |