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