linux-omap 2.6.37: sync with OE .dev
[glsdk/meta-ti-glsdk.git] / recipes-bsp / linux / linux-omap / media / 0008-media-Media-device-node-support.patch
1 From 27c789f3ae1d24212355d10857efb2d406d0fedd Mon Sep 17 00:00:00 2001
2 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
3 Date: Wed, 9 Dec 2009 12:39:56 +0100
4 Subject: [PATCH 08/43] media: Media device node support
6 The media_devnode structure provides support for registering and
7 unregistering character devices using a dynamic major number. Reference
8 counting is handled internally, making device drivers easier to write
9 without having to solve the open/disconnect race condition issue over
10 and over again.
12 The code is based on video/v4l2-dev.c.
14 Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
15 ---
16  drivers/media/Kconfig         |   13 ++
17  drivers/media/Makefile        |   10 +-
18  drivers/media/media-devnode.c |  321 +++++++++++++++++++++++++++++++++++++++++
19  include/media/media-devnode.h |   97 +++++++++++++
20  4 files changed, 439 insertions(+), 2 deletions(-)
21  create mode 100644 drivers/media/media-devnode.c
22  create mode 100644 include/media/media-devnode.h
24 diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
25 index a28541b..6b946e6 100644
26 --- a/drivers/media/Kconfig
27 +++ b/drivers/media/Kconfig
28 @@ -14,6 +14,19 @@ if MEDIA_SUPPORT
29  comment "Multimedia core support"
30  
31  #
32 +# Media controller
33 +#
34 +
35 +config MEDIA_CONTROLLER
36 +       bool "Media Controller API (EXPERIMENTAL)"
37 +       depends on EXPERIMENTAL
38 +       ---help---
39 +         Enable the media controller API used to query media devices internal
40 +         topology and configure it dynamically.
41 +
42 +         This API is mostly used by camera interfaces in embedded platforms.
43 +
44 +#
45  # V4L core and enabled API's
46  #
47  
48 diff --git a/drivers/media/Makefile b/drivers/media/Makefile
49 index 499b081..3a08991 100644
50 --- a/drivers/media/Makefile
51 +++ b/drivers/media/Makefile
52 @@ -2,7 +2,13 @@
53  # Makefile for the kernel multimedia device drivers.
54  #
55  
56 +media-objs     := media-devnode.o
57 +
58 +ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
59 +  obj-$(CONFIG_MEDIA_SUPPORT) += media.o
60 +endif
61 +
62  obj-y += common/ IR/ video/
63  
64 -obj-$(CONFIG_VIDEO_DEV) += radio/
65 -obj-$(CONFIG_DVB_CORE)  += dvb/
66 +obj-$(CONFIG_VIDEO_DEV)                += radio/
67 +obj-$(CONFIG_DVB_CORE)         += dvb/
68 diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
69 new file mode 100644
70 index 0000000..7804b70
71 --- /dev/null
72 +++ b/drivers/media/media-devnode.c
73 @@ -0,0 +1,321 @@
74 +/*
75 + * Media device node
76 + *
77 + * Copyright (C) 2010 Nokia Corporation
78 + *
79 + * Based on drivers/media/video/v4l2_dev.c code authored by
80 + *     Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
81 + *     Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
82 + *
83 + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
84 + *          Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
85 + *
86 + * This program is free software; you can redistribute it and/or modify
87 + * it under the terms of the GNU General Public License version 2 as
88 + * published by the Free Software Foundation.
89 + *
90 + * This program is distributed in the hope that it will be useful,
91 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
92 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
93 + * GNU General Public License for more details.
94 + *
95 + * You should have received a copy of the GNU General Public License
96 + * along with this program; if not, write to the Free Software
97 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
98 + *
99 + * --
100 + *
101 + * Generic media device node infrastructure to register and unregister
102 + * character devices using a dynamic major number and proper reference
103 + * counting.
104 + */
106 +#include <linux/errno.h>
107 +#include <linux/init.h>
108 +#include <linux/module.h>
109 +#include <linux/kernel.h>
110 +#include <linux/kmod.h>
111 +#include <linux/slab.h>
112 +#include <linux/mm.h>
113 +#include <linux/smp_lock.h>
114 +#include <linux/string.h>
115 +#include <linux/types.h>
116 +#include <linux/uaccess.h>
117 +#include <asm/system.h>
119 +#include <media/media-devnode.h>
121 +#define MEDIA_NUM_DEVICES      256
122 +#define MEDIA_NAME             "media"
124 +static dev_t media_dev_t;
126 +/*
127 + *     Active devices
128 + */
129 +static DEFINE_MUTEX(media_devnode_lock);
130 +static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
132 +/* Called when the last user of the media device exits. */
133 +static void media_devnode_release(struct device *cd)
134 +{
135 +       struct media_devnode *mdev = to_media_devnode(cd);
137 +       mutex_lock(&media_devnode_lock);
139 +       /* Delete the cdev on this minor as well */
140 +       cdev_del(&mdev->cdev);
142 +       /* Mark device node number as free */
143 +       clear_bit(mdev->minor, media_devnode_nums);
145 +       mutex_unlock(&media_devnode_lock);
147 +       /* Release media_devnode and perform other cleanups as needed. */
148 +       if (mdev->release)
149 +               mdev->release(mdev);
150 +}
152 +static struct bus_type media_bus_type = {
153 +       .name = MEDIA_NAME,
154 +};
156 +static ssize_t media_read(struct file *filp, char __user *buf,
157 +               size_t sz, loff_t *off)
158 +{
159 +       struct media_devnode *mdev = media_devnode_data(filp);
161 +       if (!mdev->fops->read)
162 +               return -EINVAL;
163 +       if (!media_devnode_is_registered(mdev))
164 +               return -EIO;
165 +       return mdev->fops->read(filp, buf, sz, off);
166 +}
168 +static ssize_t media_write(struct file *filp, const char __user *buf,
169 +               size_t sz, loff_t *off)
170 +{
171 +       struct media_devnode *mdev = media_devnode_data(filp);
173 +       if (!mdev->fops->write)
174 +               return -EINVAL;
175 +       if (!media_devnode_is_registered(mdev))
176 +               return -EIO;
177 +       return mdev->fops->write(filp, buf, sz, off);
178 +}
180 +static unsigned int media_poll(struct file *filp,
181 +                              struct poll_table_struct *poll)
182 +{
183 +       struct media_devnode *mdev = media_devnode_data(filp);
185 +       if (!media_devnode_is_registered(mdev))
186 +               return POLLERR | POLLHUP;
187 +       if (!mdev->fops->poll)
188 +               return DEFAULT_POLLMASK;
189 +       return mdev->fops->poll(filp, poll);
190 +}
192 +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
193 +{
194 +       struct media_devnode *mdev = media_devnode_data(filp);
196 +       if (!mdev->fops->ioctl)
197 +               return -ENOTTY;
199 +       if (!media_devnode_is_registered(mdev))
200 +               return -EIO;
202 +       return mdev->fops->ioctl(filp, cmd, arg);
203 +}
205 +/* Override for the open function */
206 +static int media_open(struct inode *inode, struct file *filp)
207 +{
208 +       struct media_devnode *mdev;
209 +       int ret;
211 +       /* Check if the media device is available. This needs to be done with
212 +        * the media_devnode_lock held to prevent an open/unregister race:
213 +        * without the lock, the device could be unregistered and freed between
214 +        * the media_devnode_is_registered() and get_device() calls, leading to
215 +        * a crash.
216 +        */
217 +       mutex_lock(&media_devnode_lock);
218 +       mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
219 +       /* return ENXIO if the media device has been removed
220 +          already or if it is not registered anymore. */
221 +       if (!media_devnode_is_registered(mdev)) {
222 +               mutex_unlock(&media_devnode_lock);
223 +               return -ENXIO;
224 +       }
225 +       /* and increase the device refcount */
226 +       get_device(&mdev->dev);
227 +       mutex_unlock(&media_devnode_lock);
229 +       filp->private_data = mdev;
231 +       if (mdev->fops->open) {
232 +               ret = mdev->fops->open(filp);
233 +               if (ret) {
234 +                       put_device(&mdev->dev);
235 +                       return ret;
236 +               }
237 +       }
239 +       return 0;
240 +}
242 +/* Override for the release function */
243 +static int media_release(struct inode *inode, struct file *filp)
244 +{
245 +       struct media_devnode *mdev = media_devnode_data(filp);
246 +       int ret = 0;
248 +       if (mdev->fops->release)
249 +               mdev->fops->release(filp);
251 +       /* decrease the refcount unconditionally since the release()
252 +          return value is ignored. */
253 +       put_device(&mdev->dev);
254 +       filp->private_data = NULL;
255 +       return ret;
256 +}
258 +static const struct file_operations media_devnode_fops = {
259 +       .owner = THIS_MODULE,
260 +       .read = media_read,
261 +       .write = media_write,
262 +       .open = media_open,
263 +       .unlocked_ioctl = media_ioctl,
264 +       .release = media_release,
265 +       .poll = media_poll,
266 +       .llseek = no_llseek,
267 +};
269 +/**
270 + * media_devnode_register - register a media device node
271 + * @mdev: media device node structure we want to register
272 + *
273 + * The registration code assigns minor numbers and registers the new device node
274 + * with the kernel. An error is returned if no free minor number can be found,
275 + * or if the registration of the device node fails.
276 + *
277 + * Zero is returned on success.
278 + *
279 + * Note that if the media_devnode_register call fails, the release() callback of
280 + * the media_devnode structure is *not* called, so the caller is responsible for
281 + * freeing any data.
282 + */
283 +int __must_check media_devnode_register(struct media_devnode *mdev)
284 +{
285 +       int minor;
286 +       int ret;
288 +       /* Part 1: Find a free minor number */
289 +       mutex_lock(&media_devnode_lock);
290 +       minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES);
291 +       if (minor == MEDIA_NUM_DEVICES) {
292 +               mutex_unlock(&media_devnode_lock);
293 +               printk(KERN_ERR "could not get a free minor\n");
294 +               return -ENFILE;
295 +       }
297 +       set_bit(mdev->minor, media_devnode_nums);
298 +       mutex_unlock(&media_devnode_lock);
300 +       mdev->minor = minor;
302 +       /* Part 2: Initialize and register the character device */
303 +       cdev_init(&mdev->cdev, &media_devnode_fops);
304 +       mdev->cdev.owner = mdev->fops->owner;
306 +       ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
307 +       if (ret < 0) {
308 +               printk(KERN_ERR "%s: cdev_add failed\n", __func__);
309 +               goto error;
310 +       }
312 +       /* Part 3: Register the media device */
313 +       mdev->dev.bus = &media_bus_type;
314 +       mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
315 +       mdev->dev.release = media_devnode_release;
316 +       if (mdev->parent)
317 +               mdev->dev.parent = mdev->parent;
318 +       dev_set_name(&mdev->dev, "media%d", mdev->minor);
319 +       ret = device_register(&mdev->dev);
320 +       if (ret < 0) {
321 +               printk(KERN_ERR "%s: device_register failed\n", __func__);
322 +               goto error;
323 +       }
325 +       /* Part 4: Activate this minor. The char device can now be used. */
326 +       set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
328 +       return 0;
330 +error:
331 +       cdev_del(&mdev->cdev);
332 +       clear_bit(mdev->minor, media_devnode_nums);
333 +       return ret;
334 +}
336 +/**
337 + * media_devnode_unregister - unregister a media device node
338 + * @mdev: the device node to unregister
339 + *
340 + * This unregisters the passed device. Future open calls will be met with
341 + * errors.
342 + *
343 + * This function can safely be called if the device node has never been
344 + * registered or has already been unregistered.
345 + */
346 +void media_devnode_unregister(struct media_devnode *mdev)
347 +{
348 +       /* Check if mdev was ever registered at all */
349 +       if (!media_devnode_is_registered(mdev))
350 +               return;
352 +       mutex_lock(&media_devnode_lock);
353 +       clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
354 +       mutex_unlock(&media_devnode_lock);
355 +       device_unregister(&mdev->dev);
356 +}
358 +/*
359 + *     Initialise media for linux
360 + */
361 +static int __init media_devnode_init(void)
362 +{
363 +       int ret;
365 +       printk(KERN_INFO "Linux media interface: v0.10\n");
366 +       ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
367 +                                 MEDIA_NAME);
368 +       if (ret < 0) {
369 +               printk(KERN_WARNING "media: unable to allocate major\n");
370 +               return ret;
371 +       }
373 +       ret = bus_register(&media_bus_type);
374 +       if (ret < 0) {
375 +               unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
376 +               printk(KERN_WARNING "media: bus_register failed\n");
377 +               return -EIO;
378 +       }
380 +       return 0;
381 +}
383 +static void __exit media_devnode_exit(void)
384 +{
385 +       bus_unregister(&media_bus_type);
386 +       unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
387 +}
389 +module_init(media_devnode_init)
390 +module_exit(media_devnode_exit)
392 +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
393 +MODULE_DESCRIPTION("Device node registration for media drivers");
394 +MODULE_LICENSE("GPL");
395 diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
396 new file mode 100644
397 index 0000000..01cd034
398 --- /dev/null
399 +++ b/include/media/media-devnode.h
400 @@ -0,0 +1,97 @@
401 +/*
402 + * Media device node
403 + *
404 + * Copyright (C) 2010 Nokia Corporation
405 + *
406 + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
407 + *          Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
408 + *
409 + * This program is free software; you can redistribute it and/or modify
410 + * it under the terms of the GNU General Public License version 2 as
411 + * published by the Free Software Foundation.
412 + *
413 + * This program is distributed in the hope that it will be useful,
414 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
415 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
416 + * GNU General Public License for more details.
417 + *
418 + * You should have received a copy of the GNU General Public License
419 + * along with this program; if not, write to the Free Software
420 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
421 + *
422 + * --
423 + *
424 + * Common functions for media-related drivers to register and unregister media
425 + * device nodes.
426 + */
428 +#ifndef _MEDIA_DEVNODE_H
429 +#define _MEDIA_DEVNODE_H
431 +#include <linux/poll.h>
432 +#include <linux/fs.h>
433 +#include <linux/device.h>
434 +#include <linux/cdev.h>
436 +/*
437 + * Flag to mark the media_devnode struct as registered. Drivers must not touch
438 + * this flag directly, it will be set and cleared by media_devnode_register and
439 + * media_devnode_unregister.
440 + */
441 +#define MEDIA_FLAG_REGISTERED  0
443 +struct media_file_operations {
444 +       struct module *owner;
445 +       ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
446 +       ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
447 +       unsigned int (*poll) (struct file *, struct poll_table_struct *);
448 +       long (*ioctl) (struct file *, unsigned int, unsigned long);
449 +       int (*open) (struct file *);
450 +       int (*release) (struct file *);
451 +};
453 +/**
454 + * struct media_devnode - Media device node
455 + * @parent:    parent device
456 + * @minor:     device node minor number
457 + * @flags:     flags, combination of the MEDIA_FLAG_* constants
458 + *
459 + * This structure represents a media-related device node.
460 + *
461 + * The @parent is a physical device. It must be set by core or device drivers
462 + * before registering the node.
463 + */
464 +struct media_devnode {
465 +       /* device ops */
466 +       const struct media_file_operations *fops;
468 +       /* sysfs */
469 +       struct device dev;              /* media device */
470 +       struct cdev cdev;               /* character device */
471 +       struct device *parent;          /* device parent */
473 +       /* device info */
474 +       int minor;
475 +       unsigned long flags;            /* Use bitops to access flags */
477 +       /* callbacks */
478 +       void (*release)(struct media_devnode *mdev);
479 +};
481 +/* dev to media_devnode */
482 +#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
484 +int __must_check media_devnode_register(struct media_devnode *mdev);
485 +void media_devnode_unregister(struct media_devnode *mdev);
487 +static inline struct media_devnode *media_devnode_data(struct file *filp)
488 +{
489 +       return filp->private_data;
490 +}
492 +static inline int media_devnode_is_registered(struct media_devnode *mdev)
493 +{
494 +       return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
495 +}
497 +#endif /* _MEDIA_DEVNODE_H */
498 -- 
499 1.6.6.1