Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.17-rc3 466 lines 12 kB view raw
1/* 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/compat.h> 24#include <linux/export.h> 25#include <linux/ioctl.h> 26#include <linux/media.h> 27#include <linux/types.h> 28 29#include <media/media-device.h> 30#include <media/media-devnode.h> 31#include <media/media-entity.h> 32 33/* ----------------------------------------------------------------------------- 34 * Userspace API 35 */ 36 37static int media_device_open(struct file *filp) 38{ 39 return 0; 40} 41 42static int media_device_close(struct file *filp) 43{ 44 return 0; 45} 46 47static int media_device_get_info(struct media_device *dev, 48 struct media_device_info __user *__info) 49{ 50 struct media_device_info info; 51 52 memset(&info, 0, sizeof(info)); 53 54 strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver)); 55 strlcpy(info.model, dev->model, sizeof(info.model)); 56 strlcpy(info.serial, dev->serial, sizeof(info.serial)); 57 strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info)); 58 59 info.media_version = MEDIA_API_VERSION; 60 info.hw_revision = dev->hw_revision; 61 info.driver_version = dev->driver_version; 62 63 if (copy_to_user(__info, &info, sizeof(*__info))) 64 return -EFAULT; 65 return 0; 66} 67 68static struct media_entity *find_entity(struct media_device *mdev, u32 id) 69{ 70 struct media_entity *entity; 71 int next = id & MEDIA_ENT_ID_FLAG_NEXT; 72 73 id &= ~MEDIA_ENT_ID_FLAG_NEXT; 74 75 spin_lock(&mdev->lock); 76 77 media_device_for_each_entity(entity, mdev) { 78 if ((entity->id == id && !next) || 79 (entity->id > id && next)) { 80 spin_unlock(&mdev->lock); 81 return entity; 82 } 83 } 84 85 spin_unlock(&mdev->lock); 86 87 return NULL; 88} 89 90static long media_device_enum_entities(struct media_device *mdev, 91 struct media_entity_desc __user *uent) 92{ 93 struct media_entity *ent; 94 struct media_entity_desc u_ent; 95 96 memset(&u_ent, 0, sizeof(u_ent)); 97 if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id))) 98 return -EFAULT; 99 100 ent = find_entity(mdev, u_ent.id); 101 102 if (ent == NULL) 103 return -EINVAL; 104 105 u_ent.id = ent->id; 106 if (ent->name) { 107 strncpy(u_ent.name, ent->name, sizeof(u_ent.name)); 108 u_ent.name[sizeof(u_ent.name) - 1] = '\0'; 109 } 110 u_ent.type = ent->type; 111 u_ent.revision = ent->revision; 112 u_ent.flags = ent->flags; 113 u_ent.group_id = ent->group_id; 114 u_ent.pads = ent->num_pads; 115 u_ent.links = ent->num_links - ent->num_backlinks; 116 memcpy(&u_ent.raw, &ent->info, sizeof(ent->info)); 117 if (copy_to_user(uent, &u_ent, sizeof(u_ent))) 118 return -EFAULT; 119 return 0; 120} 121 122static void media_device_kpad_to_upad(const struct media_pad *kpad, 123 struct media_pad_desc *upad) 124{ 125 upad->entity = kpad->entity->id; 126 upad->index = kpad->index; 127 upad->flags = kpad->flags; 128} 129 130static long __media_device_enum_links(struct media_device *mdev, 131 struct media_links_enum *links) 132{ 133 struct media_entity *entity; 134 135 entity = find_entity(mdev, links->entity); 136 if (entity == NULL) 137 return -EINVAL; 138 139 if (links->pads) { 140 unsigned int p; 141 142 for (p = 0; p < entity->num_pads; p++) { 143 struct media_pad_desc pad; 144 145 memset(&pad, 0, sizeof(pad)); 146 media_device_kpad_to_upad(&entity->pads[p], &pad); 147 if (copy_to_user(&links->pads[p], &pad, sizeof(pad))) 148 return -EFAULT; 149 } 150 } 151 152 if (links->links) { 153 struct media_link_desc __user *ulink; 154 unsigned int l; 155 156 for (l = 0, ulink = links->links; l < entity->num_links; l++) { 157 struct media_link_desc link; 158 159 /* Ignore backlinks. */ 160 if (entity->links[l].source->entity != entity) 161 continue; 162 163 memset(&link, 0, sizeof(link)); 164 media_device_kpad_to_upad(entity->links[l].source, 165 &link.source); 166 media_device_kpad_to_upad(entity->links[l].sink, 167 &link.sink); 168 link.flags = entity->links[l].flags; 169 if (copy_to_user(ulink, &link, sizeof(*ulink))) 170 return -EFAULT; 171 ulink++; 172 } 173 } 174 175 return 0; 176} 177 178static long media_device_enum_links(struct media_device *mdev, 179 struct media_links_enum __user *ulinks) 180{ 181 struct media_links_enum links; 182 int rval; 183 184 if (copy_from_user(&links, ulinks, sizeof(links))) 185 return -EFAULT; 186 187 rval = __media_device_enum_links(mdev, &links); 188 if (rval < 0) 189 return rval; 190 191 if (copy_to_user(ulinks, &links, sizeof(*ulinks))) 192 return -EFAULT; 193 194 return 0; 195} 196 197static long media_device_setup_link(struct media_device *mdev, 198 struct media_link_desc __user *_ulink) 199{ 200 struct media_link *link = NULL; 201 struct media_link_desc ulink; 202 struct media_entity *source; 203 struct media_entity *sink; 204 int ret; 205 206 if (copy_from_user(&ulink, _ulink, sizeof(ulink))) 207 return -EFAULT; 208 209 /* Find the source and sink entities and link. 210 */ 211 source = find_entity(mdev, ulink.source.entity); 212 sink = find_entity(mdev, ulink.sink.entity); 213 214 if (source == NULL || sink == NULL) 215 return -EINVAL; 216 217 if (ulink.source.index >= source->num_pads || 218 ulink.sink.index >= sink->num_pads) 219 return -EINVAL; 220 221 link = media_entity_find_link(&source->pads[ulink.source.index], 222 &sink->pads[ulink.sink.index]); 223 if (link == NULL) 224 return -EINVAL; 225 226 /* Setup the link on both entities. */ 227 ret = __media_entity_setup_link(link, ulink.flags); 228 229 if (copy_to_user(_ulink, &ulink, sizeof(ulink))) 230 return -EFAULT; 231 232 return ret; 233} 234 235static long media_device_ioctl(struct file *filp, unsigned int cmd, 236 unsigned long arg) 237{ 238 struct media_devnode *devnode = media_devnode_data(filp); 239 struct media_device *dev = to_media_device(devnode); 240 long ret; 241 242 switch (cmd) { 243 case MEDIA_IOC_DEVICE_INFO: 244 ret = media_device_get_info(dev, 245 (struct media_device_info __user *)arg); 246 break; 247 248 case MEDIA_IOC_ENUM_ENTITIES: 249 ret = media_device_enum_entities(dev, 250 (struct media_entity_desc __user *)arg); 251 break; 252 253 case MEDIA_IOC_ENUM_LINKS: 254 mutex_lock(&dev->graph_mutex); 255 ret = media_device_enum_links(dev, 256 (struct media_links_enum __user *)arg); 257 mutex_unlock(&dev->graph_mutex); 258 break; 259 260 case MEDIA_IOC_SETUP_LINK: 261 mutex_lock(&dev->graph_mutex); 262 ret = media_device_setup_link(dev, 263 (struct media_link_desc __user *)arg); 264 mutex_unlock(&dev->graph_mutex); 265 break; 266 267 default: 268 ret = -ENOIOCTLCMD; 269 } 270 271 return ret; 272} 273 274#ifdef CONFIG_COMPAT 275 276struct media_links_enum32 { 277 __u32 entity; 278 compat_uptr_t pads; /* struct media_pad_desc * */ 279 compat_uptr_t links; /* struct media_link_desc * */ 280 __u32 reserved[4]; 281}; 282 283static long media_device_enum_links32(struct media_device *mdev, 284 struct media_links_enum32 __user *ulinks) 285{ 286 struct media_links_enum links; 287 compat_uptr_t pads_ptr, links_ptr; 288 289 memset(&links, 0, sizeof(links)); 290 291 if (get_user(links.entity, &ulinks->entity) 292 || get_user(pads_ptr, &ulinks->pads) 293 || get_user(links_ptr, &ulinks->links)) 294 return -EFAULT; 295 296 links.pads = compat_ptr(pads_ptr); 297 links.links = compat_ptr(links_ptr); 298 299 return __media_device_enum_links(mdev, &links); 300} 301 302#define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) 303 304static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, 305 unsigned long arg) 306{ 307 struct media_devnode *devnode = media_devnode_data(filp); 308 struct media_device *dev = to_media_device(devnode); 309 long ret; 310 311 switch (cmd) { 312 case MEDIA_IOC_DEVICE_INFO: 313 case MEDIA_IOC_ENUM_ENTITIES: 314 case MEDIA_IOC_SETUP_LINK: 315 return media_device_ioctl(filp, cmd, arg); 316 317 case MEDIA_IOC_ENUM_LINKS32: 318 mutex_lock(&dev->graph_mutex); 319 ret = media_device_enum_links32(dev, 320 (struct media_links_enum32 __user *)arg); 321 mutex_unlock(&dev->graph_mutex); 322 break; 323 324 default: 325 ret = -ENOIOCTLCMD; 326 } 327 328 return ret; 329} 330#endif /* CONFIG_COMPAT */ 331 332static const struct media_file_operations media_device_fops = { 333 .owner = THIS_MODULE, 334 .open = media_device_open, 335 .ioctl = media_device_ioctl, 336#ifdef CONFIG_COMPAT 337 .compat_ioctl = media_device_compat_ioctl, 338#endif /* CONFIG_COMPAT */ 339 .release = media_device_close, 340}; 341 342/* ----------------------------------------------------------------------------- 343 * sysfs 344 */ 345 346static ssize_t show_model(struct device *cd, 347 struct device_attribute *attr, char *buf) 348{ 349 struct media_device *mdev = to_media_device(to_media_devnode(cd)); 350 351 return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); 352} 353 354static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); 355 356/* ----------------------------------------------------------------------------- 357 * Registration/unregistration 358 */ 359 360static void media_device_release(struct media_devnode *mdev) 361{ 362} 363 364/** 365 * media_device_register - register a media device 366 * @mdev: The media device 367 * 368 * The caller is responsible for initializing the media device before 369 * registration. The following fields must be set: 370 * 371 * - dev must point to the parent device 372 * - model must be filled with the device model name 373 */ 374int __must_check __media_device_register(struct media_device *mdev, 375 struct module *owner) 376{ 377 int ret; 378 379 if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) 380 return -EINVAL; 381 382 mdev->entity_id = 1; 383 INIT_LIST_HEAD(&mdev->entities); 384 spin_lock_init(&mdev->lock); 385 mutex_init(&mdev->graph_mutex); 386 387 /* Register the device node. */ 388 mdev->devnode.fops = &media_device_fops; 389 mdev->devnode.parent = mdev->dev; 390 mdev->devnode.release = media_device_release; 391 ret = media_devnode_register(&mdev->devnode, owner); 392 if (ret < 0) 393 return ret; 394 395 ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); 396 if (ret < 0) { 397 media_devnode_unregister(&mdev->devnode); 398 return ret; 399 } 400 401 return 0; 402} 403EXPORT_SYMBOL_GPL(__media_device_register); 404 405/** 406 * media_device_unregister - unregister a media device 407 * @mdev: The media device 408 * 409 */ 410void media_device_unregister(struct media_device *mdev) 411{ 412 struct media_entity *entity; 413 struct media_entity *next; 414 415 list_for_each_entry_safe(entity, next, &mdev->entities, list) 416 media_device_unregister_entity(entity); 417 418 device_remove_file(&mdev->devnode.dev, &dev_attr_model); 419 media_devnode_unregister(&mdev->devnode); 420} 421EXPORT_SYMBOL_GPL(media_device_unregister); 422 423/** 424 * media_device_register_entity - Register an entity with a media device 425 * @mdev: The media device 426 * @entity: The entity 427 */ 428int __must_check media_device_register_entity(struct media_device *mdev, 429 struct media_entity *entity) 430{ 431 /* Warn if we apparently re-register an entity */ 432 WARN_ON(entity->parent != NULL); 433 entity->parent = mdev; 434 435 spin_lock(&mdev->lock); 436 if (entity->id == 0) 437 entity->id = mdev->entity_id++; 438 else 439 mdev->entity_id = max(entity->id + 1, mdev->entity_id); 440 list_add_tail(&entity->list, &mdev->entities); 441 spin_unlock(&mdev->lock); 442 443 return 0; 444} 445EXPORT_SYMBOL_GPL(media_device_register_entity); 446 447/** 448 * media_device_unregister_entity - Unregister an entity 449 * @entity: The entity 450 * 451 * If the entity has never been registered this function will return 452 * immediately. 453 */ 454void media_device_unregister_entity(struct media_entity *entity) 455{ 456 struct media_device *mdev = entity->parent; 457 458 if (mdev == NULL) 459 return; 460 461 spin_lock(&mdev->lock); 462 list_del(&entity->list); 463 spin_unlock(&mdev->lock); 464 entity->parent = NULL; 465} 466EXPORT_SYMBOL_GPL(media_device_unregister_entity);