brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 56444ed Raw
326 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Media device node4 *5 * Copyright (C) 2010 Nokia Corporation6 *7 * Based on drivers/media/video/v4l2_dev.c code authored by8 *	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 unregister17 * character devices using a dynamic major number and proper reference18 * counting.19 */20 21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt22 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	25638#define MEDIA_NAME		"media"39 40static dev_t media_dev_t;41 42/*43 *	Active devices44 */45static DEFINE_MUTEX(media_devnode_lock);46static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);47 48/* Called when the last user of the media device exits. */49static void media_devnode_release(struct device *cd)50{51	struct media_devnode *devnode = to_media_devnode(cd);52 53	mutex_lock(&media_devnode_lock);54	/* Mark device node number as free */55	clear_bit(devnode->minor, media_devnode_nums);56	mutex_unlock(&media_devnode_lock);57 58	/* Release media_devnode and perform other cleanups as needed. */59	if (devnode->release)60		devnode->release(devnode);61 62	kfree(devnode);63	pr_debug("%s: Media Devnode Deallocated\n", __func__);64}65 66static const struct bus_type media_bus_type = {67	.name = MEDIA_NAME,68};69 70static 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	if (!devnode->fops->read)76		return -EINVAL;77	if (!media_devnode_is_registered(devnode))78		return -EIO;79	return devnode->fops->read(filp, buf, sz, off);80}81 82static 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	if (!devnode->fops->write)88		return -EINVAL;89	if (!media_devnode_is_registered(devnode))90		return -EIO;91	return devnode->fops->write(filp, buf, sz, off);92}93 94static __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	if (!media_devnode_is_registered(devnode))100		return EPOLLERR | EPOLLHUP;101	if (!devnode->fops->poll)102		return DEFAULT_POLLMASK;103	return devnode->fops->poll(filp, poll);104}105 106static long107__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	if (!ioctl_func)114		return -ENOTTY;115 116	if (!media_devnode_is_registered(devnode))117		return -EIO;118 119	return ioctl_func(filp, cmd, arg);120}121 122static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)123{124	struct media_devnode *devnode = media_devnode_data(filp);125 126	return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl);127}128 129#ifdef CONFIG_COMPAT130 131static 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 */142static 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 with148	 * the media_devnode_lock held to prevent an open/unregister race:149	 * without the lock, the device could be unregistered and freed between150	 * the media_devnode_is_registered() and get_device() calls, leading to151	 * a crash.152	 */153	mutex_lock(&media_devnode_lock);154	devnode = container_of(inode->i_cdev, struct media_devnode, cdev);155	/* return ENXIO if the media device has been removed156	   already or if it is not registered anymore. */157	if (!media_devnode_is_registered(devnode)) {158		mutex_unlock(&media_devnode_lock);159		return -ENXIO;160	}161	/* and increase the device refcount */162	get_device(&devnode->dev);163	mutex_unlock(&media_devnode_lock);164 165	filp->private_data = devnode;166 167	if (devnode->fops->open) {168		ret = devnode->fops->open(filp);169		if (ret) {170			put_device(&devnode->dev);171			filp->private_data = NULL;172			return ret;173		}174	}175 176	return 0;177}178 179/* Override for the release function */180static int media_release(struct inode *inode, struct file *filp)181{182	struct media_devnode *devnode = media_devnode_data(filp);183 184	if (devnode->fops->release)185		devnode->fops->release(filp);186 187	filp->private_data = NULL;188 189	/* decrease the refcount unconditionally since the release()190	   return value is ignored. */191	put_device(&devnode->dev);192 193	return 0;194}195 196static const struct file_operations media_devnode_fops = {197	.owner = THIS_MODULE,198	.read = media_read,199	.write = media_write,200	.open = media_open,201	.unlocked_ioctl = media_ioctl,202#ifdef CONFIG_COMPAT203	.compat_ioctl = media_compat_ioctl,204#endif /* CONFIG_COMPAT */205	.release = media_release,206	.poll = media_poll,207};208 209int __must_check media_devnode_register(struct media_device *mdev,210					struct media_devnode *devnode,211					struct module *owner)212{213	int minor;214	int ret;215 216	/* Part 1: Find a free minor number */217	mutex_lock(&media_devnode_lock);218	minor = find_first_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES);219	if (minor == MEDIA_NUM_DEVICES) {220		mutex_unlock(&media_devnode_lock);221		pr_err("could not get a free minor\n");222		kfree(devnode);223		return -ENFILE;224	}225 226	set_bit(minor, media_devnode_nums);227	mutex_unlock(&media_devnode_lock);228 229	devnode->minor = minor;230	devnode->media_dev = mdev;231 232	/* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */233	devnode->dev.bus = &media_bus_type;234	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);235	devnode->dev.release = media_devnode_release;236	if (devnode->parent)237		devnode->dev.parent = devnode->parent;238	dev_set_name(&devnode->dev, "media%d", devnode->minor);239	device_initialize(&devnode->dev);240 241	/* Part 2: Initialize the character device */242	cdev_init(&devnode->cdev, &media_devnode_fops);243	devnode->cdev.owner = owner;244	kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);245 246	/* Part 3: Add the media and char device */247	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);248	ret = cdev_device_add(&devnode->cdev, &devnode->dev);249	if (ret < 0) {250		clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);251		pr_err("%s: cdev_device_add failed\n", __func__);252		goto cdev_add_error;253	}254 255	return 0;256 257cdev_add_error:258	mutex_lock(&media_devnode_lock);259	clear_bit(devnode->minor, media_devnode_nums);260	devnode->media_dev = NULL;261	mutex_unlock(&media_devnode_lock);262 263	put_device(&devnode->dev);264	return ret;265}266 267void media_devnode_unregister_prepare(struct media_devnode *devnode)268{269	/* Check if devnode was ever registered at all */270	if (!media_devnode_is_registered(devnode))271		return;272 273	mutex_lock(&media_devnode_lock);274	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);275	mutex_unlock(&media_devnode_lock);276}277 278void media_devnode_unregister(struct media_devnode *devnode)279{280	mutex_lock(&media_devnode_lock);281	/* Delete the cdev on this minor as well */282	cdev_device_del(&devnode->cdev, &devnode->dev);283	devnode->media_dev = NULL;284	mutex_unlock(&media_devnode_lock);285 286	put_device(&devnode->dev);287}288 289/*290 *	Initialise media for linux291 */292static int __init media_devnode_init(void)293{294	int ret;295 296	pr_info("Linux media interface: v0.10\n");297	ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,298				  MEDIA_NAME);299	if (ret < 0) {300		pr_warn("unable to allocate major\n");301		return ret;302	}303 304	ret = bus_register(&media_bus_type);305	if (ret < 0) {306		unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);307		pr_warn("bus_register failed\n");308		return -EIO;309	}310 311	return 0;312}313 314static void __exit media_devnode_exit(void)315{316	bus_unregister(&media_bus_type);317	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);318}319 320subsys_initcall(media_devnode_init);321module_exit(media_devnode_exit)322 323MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");324MODULE_DESCRIPTION("Device node registration for media drivers");325MODULE_LICENSE("GPL");326