diff options
Diffstat (limited to 'extras/recipes-kernel/linux/linux-omap/media/0008-media-Media-device-node-support.patch')
-rw-r--r-- | extras/recipes-kernel/linux/linux-omap/media/0008-media-Media-device-node-support.patch | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/extras/recipes-kernel/linux/linux-omap/media/0008-media-Media-device-node-support.patch b/extras/recipes-kernel/linux/linux-omap/media/0008-media-Media-device-node-support.patch new file mode 100644 index 00000000..fd8b4952 --- /dev/null +++ b/extras/recipes-kernel/linux/linux-omap/media/0008-media-Media-device-node-support.patch | |||
@@ -0,0 +1,500 @@ | |||
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 | ||
5 | |||
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. | ||
11 | |||
12 | The code is based on video/v4l2-dev.c. | ||
13 | |||
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 | ||
23 | |||
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 | + */ | ||
105 | + | ||
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> | ||
118 | + | ||
119 | +#include <media/media-devnode.h> | ||
120 | + | ||
121 | +#define MEDIA_NUM_DEVICES 256 | ||
122 | +#define MEDIA_NAME "media" | ||
123 | + | ||
124 | +static dev_t media_dev_t; | ||
125 | + | ||
126 | +/* | ||
127 | + * Active devices | ||
128 | + */ | ||
129 | +static DEFINE_MUTEX(media_devnode_lock); | ||
130 | +static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); | ||
131 | + | ||
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); | ||
136 | + | ||
137 | + mutex_lock(&media_devnode_lock); | ||
138 | + | ||
139 | + /* Delete the cdev on this minor as well */ | ||
140 | + cdev_del(&mdev->cdev); | ||
141 | + | ||
142 | + /* Mark device node number as free */ | ||
143 | + clear_bit(mdev->minor, media_devnode_nums); | ||
144 | + | ||
145 | + mutex_unlock(&media_devnode_lock); | ||
146 | + | ||
147 | + /* Release media_devnode and perform other cleanups as needed. */ | ||
148 | + if (mdev->release) | ||
149 | + mdev->release(mdev); | ||
150 | +} | ||
151 | + | ||
152 | +static struct bus_type media_bus_type = { | ||
153 | + .name = MEDIA_NAME, | ||
154 | +}; | ||
155 | + | ||
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); | ||
160 | + | ||
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 | +} | ||
167 | + | ||
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); | ||
172 | + | ||
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 | +} | ||
179 | + | ||
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); | ||
184 | + | ||
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 | +} | ||
191 | + | ||
192 | +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
193 | +{ | ||
194 | + struct media_devnode *mdev = media_devnode_data(filp); | ||
195 | + | ||
196 | + if (!mdev->fops->ioctl) | ||
197 | + return -ENOTTY; | ||
198 | + | ||
199 | + if (!media_devnode_is_registered(mdev)) | ||
200 | + return -EIO; | ||
201 | + | ||
202 | + return mdev->fops->ioctl(filp, cmd, arg); | ||
203 | +} | ||
204 | + | ||
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; | ||
210 | + | ||
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); | ||
228 | + | ||
229 | + filp->private_data = mdev; | ||
230 | + | ||
231 | + if (mdev->fops->open) { | ||
232 | + ret = mdev->fops->open(filp); | ||
233 | + if (ret) { | ||
234 | + put_device(&mdev->dev); | ||
235 | + return ret; | ||
236 | + } | ||
237 | + } | ||
238 | + | ||
239 | + return 0; | ||
240 | +} | ||
241 | + | ||
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; | ||
247 | + | ||
248 | + if (mdev->fops->release) | ||
249 | + mdev->fops->release(filp); | ||
250 | + | ||
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 | +} | ||
257 | + | ||
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 | +}; | ||
268 | + | ||
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; | ||
287 | + | ||
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 | + } | ||
296 | + | ||
297 | + set_bit(mdev->minor, media_devnode_nums); | ||
298 | + mutex_unlock(&media_devnode_lock); | ||
299 | + | ||
300 | + mdev->minor = minor; | ||
301 | + | ||
302 | + /* Part 2: Initialize and register the character device */ | ||
303 | + cdev_init(&mdev->cdev, &media_devnode_fops); | ||
304 | + mdev->cdev.owner = mdev->fops->owner; | ||
305 | + | ||
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 | + } | ||
311 | + | ||
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 | + } | ||
324 | + | ||
325 | + /* Part 4: Activate this minor. The char device can now be used. */ | ||
326 | + set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); | ||
327 | + | ||
328 | + return 0; | ||
329 | + | ||
330 | +error: | ||
331 | + cdev_del(&mdev->cdev); | ||
332 | + clear_bit(mdev->minor, media_devnode_nums); | ||
333 | + return ret; | ||
334 | +} | ||
335 | + | ||
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; | ||
351 | + | ||
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 | +} | ||
357 | + | ||
358 | +/* | ||
359 | + * Initialise media for linux | ||
360 | + */ | ||
361 | +static int __init media_devnode_init(void) | ||
362 | +{ | ||
363 | + int ret; | ||
364 | + | ||
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 | + } | ||
372 | + | ||
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 | + } | ||
379 | + | ||
380 | + return 0; | ||
381 | +} | ||
382 | + | ||
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 | +} | ||
388 | + | ||
389 | +module_init(media_devnode_init) | ||
390 | +module_exit(media_devnode_exit) | ||
391 | + | ||
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 | + */ | ||
427 | + | ||
428 | +#ifndef _MEDIA_DEVNODE_H | ||
429 | +#define _MEDIA_DEVNODE_H | ||
430 | + | ||
431 | +#include <linux/poll.h> | ||
432 | +#include <linux/fs.h> | ||
433 | +#include <linux/device.h> | ||
434 | +#include <linux/cdev.h> | ||
435 | + | ||
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 | ||
442 | + | ||
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 | +}; | ||
452 | + | ||
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; | ||
467 | + | ||
468 | + /* sysfs */ | ||
469 | + struct device dev; /* media device */ | ||
470 | + struct cdev cdev; /* character device */ | ||
471 | + struct device *parent; /* device parent */ | ||
472 | + | ||
473 | + /* device info */ | ||
474 | + int minor; | ||
475 | + unsigned long flags; /* Use bitops to access flags */ | ||
476 | + | ||
477 | + /* callbacks */ | ||
478 | + void (*release)(struct media_devnode *mdev); | ||
479 | +}; | ||
480 | + | ||
481 | +/* dev to media_devnode */ | ||
482 | +#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) | ||
483 | + | ||
484 | +int __must_check media_devnode_register(struct media_devnode *mdev); | ||
485 | +void media_devnode_unregister(struct media_devnode *mdev); | ||
486 | + | ||
487 | +static inline struct media_devnode *media_devnode_data(struct file *filp) | ||
488 | +{ | ||
489 | + return filp->private_data; | ||
490 | +} | ||
491 | + | ||
492 | +static inline int media_devnode_is_registered(struct media_devnode *mdev) | ||
493 | +{ | ||
494 | + return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); | ||
495 | +} | ||
496 | + | ||
497 | +#endif /* _MEDIA_DEVNODE_H */ | ||
498 | -- | ||
499 | 1.6.6.1 | ||
500 | |||