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