Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * Media device node 4 : : * 5 : : * Copyright (C) 2010 Nokia Corporation 6 : : * 7 : : * Based on drivers/media/video/v4l2_dev.c code authored by 8 : : * Mauro Carvalho Chehab <mchehab@kernel.org> (version 2) 9 : : * Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) 10 : : * 11 : : * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 12 : : * Sakari Ailus <sakari.ailus@iki.fi> 13 : : * 14 : : * -- 15 : : * 16 : : * Generic media device node infrastructure to register and unregister 17 : : * character devices using a dynamic major number and proper reference 18 : : * counting. 19 : : */ 20 : : 21 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 : : 23 : : #include <linux/errno.h> 24 : : #include <linux/init.h> 25 : : #include <linux/module.h> 26 : : #include <linux/kernel.h> 27 : : #include <linux/kmod.h> 28 : : #include <linux/slab.h> 29 : : #include <linux/mm.h> 30 : : #include <linux/string.h> 31 : : #include <linux/types.h> 32 : : #include <linux/uaccess.h> 33 : : 34 : : #include <media/media-devnode.h> 35 : : #include <media/media-device.h> 36 : : 37 : : #define MEDIA_NUM_DEVICES 256 38 : : #define MEDIA_NAME "media" 39 : : 40 : : static dev_t media_dev_t; 41 : : 42 : : /* 43 : : * Active devices 44 : : */ 45 : : static DEFINE_MUTEX(media_devnode_lock); 46 : : static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); 47 : : 48 : : /* Called when the last user of the media device exits. */ 49 : 0 : static void media_devnode_release(struct device *cd) 50 : : { 51 : 0 : struct media_devnode *devnode = to_media_devnode(cd); 52 : : 53 : 0 : mutex_lock(&media_devnode_lock); 54 : : /* Mark device node number as free */ 55 : 0 : clear_bit(devnode->minor, media_devnode_nums); 56 : 0 : mutex_unlock(&media_devnode_lock); 57 : : 58 : : /* Release media_devnode and perform other cleanups as needed. */ 59 : 0 : if (devnode->release) 60 : 0 : devnode->release(devnode); 61 : : 62 : 0 : kfree(devnode); 63 : : pr_debug("%s: Media Devnode Deallocated\n", __func__); 64 : 0 : } 65 : : 66 : : static struct bus_type media_bus_type = { 67 : : .name = MEDIA_NAME, 68 : : }; 69 : : 70 : 0 : static ssize_t media_read(struct file *filp, char __user *buf, 71 : : size_t sz, loff_t *off) 72 : : { 73 : : struct media_devnode *devnode = media_devnode_data(filp); 74 : : 75 : 0 : if (!devnode->fops->read) 76 : : return -EINVAL; 77 : 0 : if (!media_devnode_is_registered(devnode)) 78 : : return -EIO; 79 : 0 : return devnode->fops->read(filp, buf, sz, off); 80 : : } 81 : : 82 : 0 : static ssize_t media_write(struct file *filp, const char __user *buf, 83 : : size_t sz, loff_t *off) 84 : : { 85 : : struct media_devnode *devnode = media_devnode_data(filp); 86 : : 87 : 0 : if (!devnode->fops->write) 88 : : return -EINVAL; 89 : 0 : if (!media_devnode_is_registered(devnode)) 90 : : return -EIO; 91 : 0 : return devnode->fops->write(filp, buf, sz, off); 92 : : } 93 : : 94 : 0 : static __poll_t media_poll(struct file *filp, 95 : : struct poll_table_struct *poll) 96 : : { 97 : : struct media_devnode *devnode = media_devnode_data(filp); 98 : : 99 : 0 : if (!media_devnode_is_registered(devnode)) 100 : : return EPOLLERR | EPOLLHUP; 101 : 0 : if (!devnode->fops->poll) 102 : : return DEFAULT_POLLMASK; 103 : 0 : return devnode->fops->poll(filp, poll); 104 : : } 105 : : 106 : : static long 107 : : __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, 108 : : long (*ioctl_func)(struct file *filp, unsigned int cmd, 109 : : unsigned long arg)) 110 : : { 111 : : struct media_devnode *devnode = media_devnode_data(filp); 112 : : 113 : 0 : if (!ioctl_func) 114 : : return -ENOTTY; 115 : : 116 : 0 : if (!media_devnode_is_registered(devnode)) 117 : : return -EIO; 118 : : 119 : 0 : return ioctl_func(filp, cmd, arg); 120 : : } 121 : : 122 : 0 : static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 123 : : { 124 : : struct media_devnode *devnode = media_devnode_data(filp); 125 : : 126 : 0 : return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl); 127 : : } 128 : : 129 : : #ifdef CONFIG_COMPAT 130 : : 131 : : static long media_compat_ioctl(struct file *filp, unsigned int cmd, 132 : : unsigned long arg) 133 : : { 134 : : struct media_devnode *devnode = media_devnode_data(filp); 135 : : 136 : : return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl); 137 : : } 138 : : 139 : : #endif /* CONFIG_COMPAT */ 140 : : 141 : : /* Override for the open function */ 142 : 0 : static int media_open(struct inode *inode, struct file *filp) 143 : : { 144 : : struct media_devnode *devnode; 145 : : int ret; 146 : : 147 : : /* Check if the media device is available. This needs to be done with 148 : : * the media_devnode_lock held to prevent an open/unregister race: 149 : : * without the lock, the device could be unregistered and freed between 150 : : * the media_devnode_is_registered() and get_device() calls, leading to 151 : : * a crash. 152 : : */ 153 : 0 : mutex_lock(&media_devnode_lock); 154 : 0 : devnode = container_of(inode->i_cdev, struct media_devnode, cdev); 155 : : /* return ENXIO if the media device has been removed 156 : : already or if it is not registered anymore. */ 157 : 0 : if (!media_devnode_is_registered(devnode)) { 158 : 0 : mutex_unlock(&media_devnode_lock); 159 : 0 : return -ENXIO; 160 : : } 161 : : /* and increase the device refcount */ 162 : 0 : get_device(&devnode->dev); 163 : 0 : mutex_unlock(&media_devnode_lock); 164 : : 165 : 0 : filp->private_data = devnode; 166 : : 167 : 0 : if (devnode->fops->open) { 168 : 0 : ret = devnode->fops->open(filp); 169 : 0 : if (ret) { 170 : 0 : put_device(&devnode->dev); 171 : 0 : filp->private_data = NULL; 172 : 0 : return ret; 173 : : } 174 : : } 175 : : 176 : : return 0; 177 : : } 178 : : 179 : : /* Override for the release function */ 180 : 0 : static int media_release(struct inode *inode, struct file *filp) 181 : : { 182 : : struct media_devnode *devnode = media_devnode_data(filp); 183 : : 184 : 0 : if (devnode->fops->release) 185 : 0 : devnode->fops->release(filp); 186 : : 187 : 0 : filp->private_data = NULL; 188 : : 189 : : /* decrease the refcount unconditionally since the release() 190 : : return value is ignored. */ 191 : 0 : put_device(&devnode->dev); 192 : : 193 : : pr_debug("%s: Media Release\n", __func__); 194 : 0 : return 0; 195 : : } 196 : : 197 : : static const struct file_operations media_devnode_fops = { 198 : : .owner = THIS_MODULE, 199 : : .read = media_read, 200 : : .write = media_write, 201 : : .open = media_open, 202 : : .unlocked_ioctl = media_ioctl, 203 : : #ifdef CONFIG_COMPAT 204 : : .compat_ioctl = media_compat_ioctl, 205 : : #endif /* CONFIG_COMPAT */ 206 : : .release = media_release, 207 : : .poll = media_poll, 208 : : .llseek = no_llseek, 209 : : }; 210 : : 211 : 3 : int __must_check media_devnode_register(struct media_device *mdev, 212 : : struct media_devnode *devnode, 213 : : struct module *owner) 214 : : { 215 : : int minor; 216 : : int ret; 217 : : 218 : : /* Part 1: Find a free minor number */ 219 : 3 : mutex_lock(&media_devnode_lock); 220 : 3 : minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0); 221 : 3 : if (minor == MEDIA_NUM_DEVICES) { 222 : 0 : mutex_unlock(&media_devnode_lock); 223 : 0 : pr_err("could not get a free minor\n"); 224 : 0 : kfree(devnode); 225 : 0 : return -ENFILE; 226 : : } 227 : : 228 : 3 : set_bit(minor, media_devnode_nums); 229 : 3 : mutex_unlock(&media_devnode_lock); 230 : : 231 : 3 : devnode->minor = minor; 232 : 3 : devnode->media_dev = mdev; 233 : : 234 : : /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */ 235 : 3 : devnode->dev.bus = &media_bus_type; 236 : 3 : devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor); 237 : 3 : devnode->dev.release = media_devnode_release; 238 : 3 : if (devnode->parent) 239 : 3 : devnode->dev.parent = devnode->parent; 240 : 3 : dev_set_name(&devnode->dev, "media%d", devnode->minor); 241 : 3 : device_initialize(&devnode->dev); 242 : : 243 : : /* Part 2: Initialize the character device */ 244 : 3 : cdev_init(&devnode->cdev, &media_devnode_fops); 245 : 3 : devnode->cdev.owner = owner; 246 : 3 : kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor); 247 : : 248 : : /* Part 3: Add the media and char device */ 249 : 3 : ret = cdev_device_add(&devnode->cdev, &devnode->dev); 250 : 3 : if (ret < 0) { 251 : 0 : pr_err("%s: cdev_device_add failed\n", __func__); 252 : : goto cdev_add_error; 253 : : } 254 : : 255 : : /* Part 4: Activate this minor. The char device can now be used. */ 256 : 3 : set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); 257 : : 258 : 3 : return 0; 259 : : 260 : : cdev_add_error: 261 : 0 : mutex_lock(&media_devnode_lock); 262 : 0 : clear_bit(devnode->minor, media_devnode_nums); 263 : 0 : devnode->media_dev = NULL; 264 : 0 : mutex_unlock(&media_devnode_lock); 265 : : 266 : 0 : put_device(&devnode->dev); 267 : 0 : return ret; 268 : : } 269 : : 270 : 0 : void media_devnode_unregister_prepare(struct media_devnode *devnode) 271 : : { 272 : : /* Check if devnode was ever registered at all */ 273 : 0 : if (!media_devnode_is_registered(devnode)) 274 : 0 : return; 275 : : 276 : 0 : mutex_lock(&media_devnode_lock); 277 : 0 : clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); 278 : 0 : mutex_unlock(&media_devnode_lock); 279 : : } 280 : : 281 : 0 : void media_devnode_unregister(struct media_devnode *devnode) 282 : : { 283 : 0 : mutex_lock(&media_devnode_lock); 284 : : /* Delete the cdev on this minor as well */ 285 : 0 : cdev_device_del(&devnode->cdev, &devnode->dev); 286 : 0 : devnode->media_dev = NULL; 287 : 0 : mutex_unlock(&media_devnode_lock); 288 : : 289 : 0 : put_device(&devnode->dev); 290 : 0 : } 291 : : 292 : : /* 293 : : * Initialise media for linux 294 : : */ 295 : 3 : static int __init media_devnode_init(void) 296 : : { 297 : : int ret; 298 : : 299 : 3 : pr_info("Linux media interface: v0.10\n"); 300 : 3 : ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES, 301 : : MEDIA_NAME); 302 : 3 : if (ret < 0) { 303 : 0 : pr_warn("unable to allocate major\n"); 304 : 0 : return ret; 305 : : } 306 : : 307 : 3 : ret = bus_register(&media_bus_type); 308 : 3 : if (ret < 0) { 309 : 0 : unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); 310 : 0 : pr_warn("bus_register failed\n"); 311 : 0 : return -EIO; 312 : : } 313 : : 314 : : return 0; 315 : : } 316 : : 317 : 0 : static void __exit media_devnode_exit(void) 318 : : { 319 : 0 : bus_unregister(&media_bus_type); 320 : 0 : unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); 321 : 0 : } 322 : : 323 : : subsys_initcall(media_devnode_init); 324 : : module_exit(media_devnode_exit) 325 : : 326 : : MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 327 : : MODULE_DESCRIPTION("Device node registration for media drivers"); 328 : : MODULE_LICENSE("GPL");