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