xref: /linux/drivers/media/mc/mc-devnode.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * Media device node
4   *
5   * Copyright (C) 2010 Nokia Corporation
6   *
7   * Based on drivers/media/video/v4l2_dev.c code authored by
8   *	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 unregister
17   * character devices using a dynamic major number and proper reference
18   * counting.
19   */
20  
21  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22  
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	256
38  #define MEDIA_NAME		"media"
39  
40  static dev_t media_dev_t;
41  
42  /*
43   *	Active devices
44   */
45  static DEFINE_MUTEX(media_devnode_lock);
46  static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
47  
48  /* Called when the last user of the media device exits. */
media_devnode_release(struct device * cd)49  static 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  
66  static const struct bus_type media_bus_type = {
67  	.name = MEDIA_NAME,
68  };
69  
media_read(struct file * filp,char __user * buf,size_t sz,loff_t * off)70  static 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  
media_write(struct file * filp,const char __user * buf,size_t sz,loff_t * off)82  static 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  
media_poll(struct file * filp,struct poll_table_struct * poll)94  static __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  
106  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  __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  
media_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)122  static 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_COMPAT
130  
media_compat_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)131  static 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 */
media_open(struct inode * inode,struct file * filp)142  static 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 with
148  	 * the media_devnode_lock held to prevent an open/unregister race:
149  	 * without the lock, the device could be unregistered and freed between
150  	 * the media_devnode_is_registered() and get_device() calls, leading to
151  	 * 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 removed
156  	   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 */
media_release(struct inode * inode,struct file * filp)180  static 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  
196  static 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_COMPAT
203  	.compat_ioctl = media_compat_ioctl,
204  #endif /* CONFIG_COMPAT */
205  	.release = media_release,
206  	.poll = media_poll,
207  };
208  
media_devnode_register(struct media_device * mdev,struct media_devnode * devnode,struct module * owner)209  int __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  
257  cdev_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  
media_devnode_unregister_prepare(struct media_devnode * devnode)267  void 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  
media_devnode_unregister(struct media_devnode * devnode)278  void 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 linux
291   */
media_devnode_init(void)292  static 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  
media_devnode_exit(void)314  static 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  
320  subsys_initcall(media_devnode_init);
321  module_exit(media_devnode_exit)
322  
323  MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
324  MODULE_DESCRIPTION("Device node registration for media drivers");
325  MODULE_LICENSE("GPL");
326