blob: 92e0d4eb84b888a38ace9eab14b80d629978cde6 [file] [log] [blame]
Laurent Pinchart176fb0d2009-12-09 08:39:58 -03001/*
2 * Media device
3 *
4 * Copyright (C) 2010 Nokia Corporation
5 *
6 * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 * Sakari Ailus <sakari.ailus@iki.fi>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <linux/types.h>
24#include <linux/ioctl.h>
Laurent Pinchart140d8812010-08-18 11:41:22 -030025#include <linux/media.h>
Laurent Pinchart176fb0d2009-12-09 08:39:58 -030026
27#include <media/media-device.h>
28#include <media/media-devnode.h>
Laurent Pinchart53e269c2009-12-09 08:40:00 -030029#include <media/media-entity.h>
Laurent Pinchart176fb0d2009-12-09 08:39:58 -030030
Laurent Pinchart140d8812010-08-18 11:41:22 -030031/* -----------------------------------------------------------------------------
32 * Userspace API
33 */
34
35static int media_device_open(struct file *filp)
36{
37 return 0;
38}
39
40static int media_device_close(struct file *filp)
41{
42 return 0;
43}
44
45static int media_device_get_info(struct media_device *dev,
46 struct media_device_info __user *__info)
47{
48 struct media_device_info info;
49
50 memset(&info, 0, sizeof(info));
51
52 strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
53 strlcpy(info.model, dev->model, sizeof(info.model));
54 strlcpy(info.serial, dev->serial, sizeof(info.serial));
55 strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));
56
57 info.media_version = MEDIA_API_VERSION;
58 info.hw_revision = dev->hw_revision;
59 info.driver_version = dev->driver_version;
60
61 return copy_to_user(__info, &info, sizeof(*__info));
62}
63
64static long media_device_ioctl(struct file *filp, unsigned int cmd,
65 unsigned long arg)
66{
67 struct media_devnode *devnode = media_devnode_data(filp);
68 struct media_device *dev = to_media_device(devnode);
69 long ret;
70
71 switch (cmd) {
72 case MEDIA_IOC_DEVICE_INFO:
73 ret = media_device_get_info(dev,
74 (struct media_device_info __user *)arg);
75 break;
76
77 default:
78 ret = -ENOIOCTLCMD;
79 }
80
81 return ret;
82}
83
Laurent Pinchart176fb0d2009-12-09 08:39:58 -030084static const struct media_file_operations media_device_fops = {
85 .owner = THIS_MODULE,
Laurent Pinchart140d8812010-08-18 11:41:22 -030086 .open = media_device_open,
87 .ioctl = media_device_ioctl,
88 .release = media_device_close,
Laurent Pinchart176fb0d2009-12-09 08:39:58 -030089};
90
91/* -----------------------------------------------------------------------------
92 * sysfs
93 */
94
95static ssize_t show_model(struct device *cd,
96 struct device_attribute *attr, char *buf)
97{
98 struct media_device *mdev = to_media_device(to_media_devnode(cd));
99
100 return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
101}
102
103static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
104
105/* -----------------------------------------------------------------------------
106 * Registration/unregistration
107 */
108
109static void media_device_release(struct media_devnode *mdev)
110{
111}
112
113/**
114 * media_device_register - register a media device
115 * @mdev: The media device
116 *
117 * The caller is responsible for initializing the media device before
118 * registration. The following fields must be set:
119 *
120 * - dev must point to the parent device
121 * - model must be filled with the device model name
122 */
123int __must_check media_device_register(struct media_device *mdev)
124{
125 int ret;
126
127 if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
128 return -EINVAL;
129
Laurent Pinchart53e269c2009-12-09 08:40:00 -0300130 mdev->entity_id = 1;
131 INIT_LIST_HEAD(&mdev->entities);
132 spin_lock_init(&mdev->lock);
Laurent Pinchart503c3d82010-03-07 15:04:59 -0300133 mutex_init(&mdev->graph_mutex);
Laurent Pinchart53e269c2009-12-09 08:40:00 -0300134
Laurent Pinchart176fb0d2009-12-09 08:39:58 -0300135 /* Register the device node. */
136 mdev->devnode.fops = &media_device_fops;
137 mdev->devnode.parent = mdev->dev;
138 mdev->devnode.release = media_device_release;
139 ret = media_devnode_register(&mdev->devnode);
140 if (ret < 0)
141 return ret;
142
143 ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
144 if (ret < 0) {
145 media_devnode_unregister(&mdev->devnode);
146 return ret;
147 }
148
149 return 0;
150}
151EXPORT_SYMBOL_GPL(media_device_register);
152
153/**
154 * media_device_unregister - unregister a media device
155 * @mdev: The media device
156 *
157 */
158void media_device_unregister(struct media_device *mdev)
159{
Laurent Pinchart53e269c2009-12-09 08:40:00 -0300160 struct media_entity *entity;
161 struct media_entity *next;
162
163 list_for_each_entry_safe(entity, next, &mdev->entities, list)
164 media_device_unregister_entity(entity);
165
Laurent Pinchart176fb0d2009-12-09 08:39:58 -0300166 device_remove_file(&mdev->devnode.dev, &dev_attr_model);
167 media_devnode_unregister(&mdev->devnode);
168}
169EXPORT_SYMBOL_GPL(media_device_unregister);
Laurent Pinchart53e269c2009-12-09 08:40:00 -0300170
171/**
172 * media_device_register_entity - Register an entity with a media device
173 * @mdev: The media device
174 * @entity: The entity
175 */
176int __must_check media_device_register_entity(struct media_device *mdev,
177 struct media_entity *entity)
178{
179 /* Warn if we apparently re-register an entity */
180 WARN_ON(entity->parent != NULL);
181 entity->parent = mdev;
182
183 spin_lock(&mdev->lock);
184 if (entity->id == 0)
185 entity->id = mdev->entity_id++;
186 else
187 mdev->entity_id = max(entity->id + 1, mdev->entity_id);
188 list_add_tail(&entity->list, &mdev->entities);
189 spin_unlock(&mdev->lock);
190
191 return 0;
192}
193EXPORT_SYMBOL_GPL(media_device_register_entity);
194
195/**
196 * media_device_unregister_entity - Unregister an entity
197 * @entity: The entity
198 *
199 * If the entity has never been registered this function will return
200 * immediately.
201 */
202void media_device_unregister_entity(struct media_entity *entity)
203{
204 struct media_device *mdev = entity->parent;
205
206 if (mdev == NULL)
207 return;
208
209 spin_lock(&mdev->lock);
210 list_del(&entity->list);
211 spin_unlock(&mdev->lock);
212 entity->parent = NULL;
213}
214EXPORT_SYMBOL_GPL(media_device_unregister_entity);