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