aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2009-12-09 06:39:56 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-03-22 03:52:22 -0400
commitcf4b9211b5680cd9ca004232e517fb7ec5bf5316 (patch)
tree5bb830aa4e362d4d9b5774ebfaca1eb79e7db22a /drivers/media
parent02adb1cc765b8c29dc83c6602bda19003cce62f1 (diff)
[media] media: Media device node support
The media_devnode structure provides support for registering and unregistering character devices using a dynamic major number. Reference counting is handled internally, making device drivers easier to write without having to solve the open/disconnect race condition issue over and over again. The code is based on video/v4l2-dev.c. [mchehab@redhat.com: Remove linux/smp_lock.h include to not break compilation on bisect] Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/Kconfig13
-rw-r--r--drivers/media/Makefile6
-rw-r--r--drivers/media/media-devnode.c320
3 files changed, 339 insertions, 0 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 81b3ba83cc65..2466f2b2ecfa 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -14,6 +14,19 @@ if MEDIA_SUPPORT
14comment "Multimedia core support" 14comment "Multimedia core support"
15 15
16# 16#
17# Media controller
18#
19
20config MEDIA_CONTROLLER
21 bool "Media Controller API (EXPERIMENTAL)"
22 depends on EXPERIMENTAL
23 ---help---
24 Enable the media controller API used to query media devices internal
25 topology and configure it dynamically.
26
27 This API is mostly used by camera interfaces in embedded platforms.
28
29#
17# V4L core and enabled API's 30# V4L core and enabled API's
18# 31#
19 32
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index b603ea645ede..d56349f6103f 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -2,6 +2,12 @@
2# Makefile for the kernel multimedia device drivers. 2# Makefile for the kernel multimedia device drivers.
3# 3#
4 4
5media-objs := media-devnode.o
6
7ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
8 obj-$(CONFIG_MEDIA_SUPPORT) += media.o
9endif
10
5obj-y += common/ rc/ video/ 11obj-y += common/ rc/ video/
6 12
7obj-$(CONFIG_VIDEO_DEV) += radio/ 13obj-$(CONFIG_VIDEO_DEV) += radio/
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
new file mode 100644
index 000000000000..af5263c6625a
--- /dev/null
+++ b/drivers/media/media-devnode.c
@@ -0,0 +1,320 @@
1/*
2 * Media device node
3 *
4 * Copyright (C) 2010 Nokia Corporation
5 *
6 * Based on drivers/media/video/v4l2_dev.c code authored by
7 * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
8 * Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
9 *
10 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
11 * Sakari Ailus <sakari.ailus@iki.fi>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 * --
27 *
28 * Generic media device node infrastructure to register and unregister
29 * character devices using a dynamic major number and proper reference
30 * counting.
31 */
32
33#include <linux/errno.h>
34#include <linux/init.h>
35#include <linux/module.h>
36#include <linux/kernel.h>
37#include <linux/kmod.h>
38#include <linux/slab.h>
39#include <linux/mm.h>
40#include <linux/string.h>
41#include <linux/types.h>
42#include <linux/uaccess.h>
43#include <asm/system.h>
44
45#include <media/media-devnode.h>
46
47#define MEDIA_NUM_DEVICES 256
48#define MEDIA_NAME "media"
49
50static dev_t media_dev_t;
51
52/*
53 * Active devices
54 */
55static DEFINE_MUTEX(media_devnode_lock);
56static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
57
58/* Called when the last user of the media device exits. */
59static void media_devnode_release(struct device *cd)
60{
61 struct media_devnode *mdev = to_media_devnode(cd);
62
63 mutex_lock(&media_devnode_lock);
64
65 /* Delete the cdev on this minor as well */
66 cdev_del(&mdev->cdev);
67
68 /* Mark device node number as free */
69 clear_bit(mdev->minor, media_devnode_nums);
70
71 mutex_unlock(&media_devnode_lock);
72
73 /* Release media_devnode and perform other cleanups as needed. */
74 if (mdev->release)
75 mdev->release(mdev);
76}
77
78static struct bus_type media_bus_type = {
79 .name = MEDIA_NAME,
80};
81
82static ssize_t media_read(struct file *filp, char __user *buf,
83 size_t sz, loff_t *off)
84{
85 struct media_devnode *mdev = media_devnode_data(filp);
86
87 if (!mdev->fops->read)
88 return -EINVAL;
89 if (!media_devnode_is_registered(mdev))
90 return -EIO;
91 return mdev->fops->read(filp, buf, sz, off);
92}
93
94static ssize_t media_write(struct file *filp, const char __user *buf,
95 size_t sz, loff_t *off)
96{
97 struct media_devnode *mdev = media_devnode_data(filp);
98
99 if (!mdev->fops->write)
100 return -EINVAL;
101 if (!media_devnode_is_registered(mdev))
102 return -EIO;
103 return mdev->fops->write(filp, buf, sz, off);
104}
105
106static unsigned int media_poll(struct file *filp,
107 struct poll_table_struct *poll)
108{
109 struct media_devnode *mdev = media_devnode_data(filp);
110
111 if (!media_devnode_is_registered(mdev))
112 return POLLERR | POLLHUP;
113 if (!mdev->fops->poll)
114 return DEFAULT_POLLMASK;
115 return mdev->fops->poll(filp, poll);
116}
117
118static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
119{
120 struct media_devnode *mdev = media_devnode_data(filp);
121
122 if (!mdev->fops->ioctl)
123 return -ENOTTY;
124
125 if (!media_devnode_is_registered(mdev))
126 return -EIO;
127
128 return mdev->fops->ioctl(filp, cmd, arg);
129}
130
131/* Override for the open function */
132static int media_open(struct inode *inode, struct file *filp)
133{
134 struct media_devnode *mdev;
135 int ret;
136
137 /* Check if the media device is available. This needs to be done with
138 * the media_devnode_lock held to prevent an open/unregister race:
139 * without the lock, the device could be unregistered and freed between
140 * the media_devnode_is_registered() and get_device() calls, leading to
141 * a crash.
142 */
143 mutex_lock(&media_devnode_lock);
144 mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
145 /* return ENXIO if the media device has been removed
146 already or if it is not registered anymore. */
147 if (!media_devnode_is_registered(mdev)) {
148 mutex_unlock(&media_devnode_lock);
149 return -ENXIO;
150 }
151 /* and increase the device refcount */
152 get_device(&mdev->dev);
153 mutex_unlock(&media_devnode_lock);
154
155 filp->private_data = mdev;
156
157 if (mdev->fops->open) {
158 ret = mdev->fops->open(filp);
159 if (ret) {
160 put_device(&mdev->dev);
161 return ret;
162 }
163 }
164
165 return 0;
166}
167
168/* Override for the release function */
169static int media_release(struct inode *inode, struct file *filp)
170{
171 struct media_devnode *mdev = media_devnode_data(filp);
172 int ret = 0;
173
174 if (mdev->fops->release)
175 mdev->fops->release(filp);
176
177 /* decrease the refcount unconditionally since the release()
178 return value is ignored. */
179 put_device(&mdev->dev);
180 filp->private_data = NULL;
181 return ret;
182}
183
184static const struct file_operations media_devnode_fops = {
185 .owner = THIS_MODULE,
186 .read = media_read,
187 .write = media_write,
188 .open = media_open,
189 .unlocked_ioctl = media_ioctl,
190 .release = media_release,
191 .poll = media_poll,
192 .llseek = no_llseek,
193};
194
195/**
196 * media_devnode_register - register a media device node
197 * @mdev: media device node structure we want to register
198 *
199 * The registration code assigns minor numbers and registers the new device node
200 * with the kernel. An error is returned if no free minor number can be found,
201 * or if the registration of the device node fails.
202 *
203 * Zero is returned on success.
204 *
205 * Note that if the media_devnode_register call fails, the release() callback of
206 * the media_devnode structure is *not* called, so the caller is responsible for
207 * freeing any data.
208 */
209int __must_check media_devnode_register(struct media_devnode *mdev)
210{
211 int minor;
212 int ret;
213
214 /* Part 1: Find a free minor number */
215 mutex_lock(&media_devnode_lock);
216 minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES);
217 if (minor == MEDIA_NUM_DEVICES) {
218 mutex_unlock(&media_devnode_lock);
219 printk(KERN_ERR "could not get a free minor\n");
220 return -ENFILE;
221 }
222
223 set_bit(mdev->minor, media_devnode_nums);
224 mutex_unlock(&media_devnode_lock);
225
226 mdev->minor = minor;
227
228 /* Part 2: Initialize and register the character device */
229 cdev_init(&mdev->cdev, &media_devnode_fops);
230 mdev->cdev.owner = mdev->fops->owner;
231
232 ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
233 if (ret < 0) {
234 printk(KERN_ERR "%s: cdev_add failed\n", __func__);
235 goto error;
236 }
237
238 /* Part 3: Register the media device */
239 mdev->dev.bus = &media_bus_type;
240 mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
241 mdev->dev.release = media_devnode_release;
242 if (mdev->parent)
243 mdev->dev.parent = mdev->parent;
244 dev_set_name(&mdev->dev, "media%d", mdev->minor);
245 ret = device_register(&mdev->dev);
246 if (ret < 0) {
247 printk(KERN_ERR "%s: device_register failed\n", __func__);
248 goto error;
249 }
250
251 /* Part 4: Activate this minor. The char device can now be used. */
252 set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
253
254 return 0;
255
256error:
257 cdev_del(&mdev->cdev);
258 clear_bit(mdev->minor, media_devnode_nums);
259 return ret;
260}
261
262/**
263 * media_devnode_unregister - unregister a media device node
264 * @mdev: the device node to unregister
265 *
266 * This unregisters the passed device. Future open calls will be met with
267 * errors.
268 *
269 * This function can safely be called if the device node has never been
270 * registered or has already been unregistered.
271 */
272void media_devnode_unregister(struct media_devnode *mdev)
273{
274 /* Check if mdev was ever registered at all */
275 if (!media_devnode_is_registered(mdev))
276 return;
277
278 mutex_lock(&media_devnode_lock);
279 clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
280 mutex_unlock(&media_devnode_lock);
281 device_unregister(&mdev->dev);
282}
283
284/*
285 * Initialise media for linux
286 */
287static int __init media_devnode_init(void)
288{
289 int ret;
290
291 printk(KERN_INFO "Linux media interface: v0.10\n");
292 ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
293 MEDIA_NAME);
294 if (ret < 0) {
295 printk(KERN_WARNING "media: unable to allocate major\n");
296 return ret;
297 }
298
299 ret = bus_register(&media_bus_type);
300 if (ret < 0) {
301 unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
302 printk(KERN_WARNING "media: bus_register failed\n");
303 return -EIO;
304 }
305
306 return 0;
307}
308
309static void __exit media_devnode_exit(void)
310{
311 bus_unregister(&media_bus_type);
312 unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
313}
314
315module_init(media_devnode_init)
316module_exit(media_devnode_exit)
317
318MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
319MODULE_DESCRIPTION("Device node registration for media drivers");
320MODULE_LICENSE("GPL");