at v4.11 344 lines 9.1 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 and register the character device */ 252 cdev_init(&devnode->cdev, &media_devnode_fops); 253 devnode->cdev.owner = owner; 254 devnode->cdev.kobj.parent = &devnode->dev.kobj; 255 256 ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1); 257 if (ret < 0) { 258 pr_err("%s: cdev_add failed\n", __func__); 259 goto cdev_add_error; 260 } 261 262 /* Part 3: Add the media device */ 263 ret = device_add(&devnode->dev); 264 if (ret < 0) { 265 pr_err("%s: device_add failed\n", __func__); 266 goto device_add_error; 267 } 268 269 /* Part 4: Activate this minor. The char device can now be used. */ 270 set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); 271 272 return 0; 273 274device_add_error: 275 cdev_del(&devnode->cdev); 276cdev_add_error: 277 mutex_lock(&media_devnode_lock); 278 clear_bit(devnode->minor, media_devnode_nums); 279 devnode->media_dev = NULL; 280 mutex_unlock(&media_devnode_lock); 281 282 put_device(&devnode->dev); 283 return ret; 284} 285 286void media_devnode_unregister_prepare(struct media_devnode *devnode) 287{ 288 /* Check if devnode was ever registered at all */ 289 if (!media_devnode_is_registered(devnode)) 290 return; 291 292 mutex_lock(&media_devnode_lock); 293 clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags); 294 mutex_unlock(&media_devnode_lock); 295} 296 297void media_devnode_unregister(struct media_devnode *devnode) 298{ 299 mutex_lock(&media_devnode_lock); 300 /* Delete the cdev on this minor as well */ 301 cdev_del(&devnode->cdev); 302 mutex_unlock(&media_devnode_lock); 303 device_del(&devnode->dev); 304 devnode->media_dev = NULL; 305 put_device(&devnode->dev); 306} 307 308/* 309 * Initialise media for linux 310 */ 311static int __init media_devnode_init(void) 312{ 313 int ret; 314 315 pr_info("Linux media interface: v0.10\n"); 316 ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES, 317 MEDIA_NAME); 318 if (ret < 0) { 319 pr_warn("unable to allocate major\n"); 320 return ret; 321 } 322 323 ret = bus_register(&media_bus_type); 324 if (ret < 0) { 325 unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); 326 pr_warn("bus_register failed\n"); 327 return -EIO; 328 } 329 330 return 0; 331} 332 333static void __exit media_devnode_exit(void) 334{ 335 bus_unregister(&media_bus_type); 336 unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); 337} 338 339subsys_initcall(media_devnode_init); 340module_exit(media_devnode_exit) 341 342MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 343MODULE_DESCRIPTION("Device node registration for media drivers"); 344MODULE_LICENSE("GPL");