xref: /linux/drivers/media/mc/mc-devnode.c (revision c612e54fca55d9380c1378eaa623d74ed89b62db)
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