1c612e54fSHans Verkuil /* 2c612e54fSHans Verkuil * Media device 3c612e54fSHans Verkuil * 4c612e54fSHans Verkuil * Copyright (C) 2010 Nokia Corporation 5c612e54fSHans Verkuil * 6c612e54fSHans Verkuil * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 7c612e54fSHans Verkuil * Sakari Ailus <sakari.ailus@iki.fi> 8c612e54fSHans Verkuil * 9c612e54fSHans Verkuil * This program is free software; you can redistribute it and/or modify 10c612e54fSHans Verkuil * it under the terms of the GNU General Public License version 2 as 11c612e54fSHans Verkuil * published by the Free Software Foundation. 12c612e54fSHans Verkuil * 13c612e54fSHans Verkuil * This program is distributed in the hope that it will be useful, 14c612e54fSHans Verkuil * but WITHOUT ANY WARRANTY; without even the implied warranty of 15c612e54fSHans Verkuil * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16c612e54fSHans Verkuil * GNU General Public License for more details. 17c612e54fSHans Verkuil */ 18c612e54fSHans Verkuil 19c612e54fSHans Verkuil #include <linux/compat.h> 20c612e54fSHans Verkuil #include <linux/export.h> 21c612e54fSHans Verkuil #include <linux/idr.h> 22c612e54fSHans Verkuil #include <linux/ioctl.h> 23c612e54fSHans Verkuil #include <linux/media.h> 24c612e54fSHans Verkuil #include <linux/slab.h> 25c612e54fSHans Verkuil #include <linux/types.h> 26c612e54fSHans Verkuil #include <linux/pci.h> 27c612e54fSHans Verkuil #include <linux/usb.h> 28c612e54fSHans Verkuil #include <linux/version.h> 29c612e54fSHans Verkuil 30c612e54fSHans Verkuil #include <media/media-device.h> 31c612e54fSHans Verkuil #include <media/media-devnode.h> 32c612e54fSHans Verkuil #include <media/media-entity.h> 33c612e54fSHans Verkuil #include <media/media-request.h> 34c612e54fSHans Verkuil 35c612e54fSHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER 36c612e54fSHans Verkuil 37c612e54fSHans Verkuil /* 38c612e54fSHans Verkuil * Legacy defines from linux/media.h. This is the only place we need this 39c612e54fSHans Verkuil * so we just define it here. The media.h header doesn't expose it to the 40c612e54fSHans Verkuil * kernel to prevent it from being used by drivers, but here (and only here!) 41c612e54fSHans Verkuil * we need it to handle the legacy behavior. 42c612e54fSHans Verkuil */ 43c612e54fSHans Verkuil #define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff 44c612e54fSHans Verkuil #define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \ 45c612e54fSHans Verkuil MEDIA_ENT_SUBTYPE_MASK) 46c612e54fSHans Verkuil 47c612e54fSHans Verkuil /* ----------------------------------------------------------------------------- 48c612e54fSHans Verkuil * Userspace API 49c612e54fSHans Verkuil */ 50c612e54fSHans Verkuil 51c612e54fSHans Verkuil static inline void __user *media_get_uptr(__u64 arg) 52c612e54fSHans Verkuil { 53c612e54fSHans Verkuil return (void __user *)(uintptr_t)arg; 54c612e54fSHans Verkuil } 55c612e54fSHans Verkuil 56c612e54fSHans Verkuil static int media_device_open(struct file *filp) 57c612e54fSHans Verkuil { 58c612e54fSHans Verkuil return 0; 59c612e54fSHans Verkuil } 60c612e54fSHans Verkuil 61c612e54fSHans Verkuil static int media_device_close(struct file *filp) 62c612e54fSHans Verkuil { 63c612e54fSHans Verkuil return 0; 64c612e54fSHans Verkuil } 65c612e54fSHans Verkuil 66c612e54fSHans Verkuil static long media_device_get_info(struct media_device *dev, void *arg) 67c612e54fSHans Verkuil { 68c612e54fSHans Verkuil struct media_device_info *info = arg; 69c612e54fSHans Verkuil 70c612e54fSHans Verkuil memset(info, 0, sizeof(*info)); 71c612e54fSHans Verkuil 72c612e54fSHans Verkuil if (dev->driver_name[0]) 73c612e54fSHans Verkuil strscpy(info->driver, dev->driver_name, sizeof(info->driver)); 74c612e54fSHans Verkuil else 75c612e54fSHans Verkuil strscpy(info->driver, dev->dev->driver->name, 76c612e54fSHans Verkuil sizeof(info->driver)); 77c612e54fSHans Verkuil 78c612e54fSHans Verkuil strscpy(info->model, dev->model, sizeof(info->model)); 79c612e54fSHans Verkuil strscpy(info->serial, dev->serial, sizeof(info->serial)); 80c612e54fSHans Verkuil strscpy(info->bus_info, dev->bus_info, sizeof(info->bus_info)); 81c612e54fSHans Verkuil 82c612e54fSHans Verkuil info->media_version = LINUX_VERSION_CODE; 83c612e54fSHans Verkuil info->driver_version = info->media_version; 84c612e54fSHans Verkuil info->hw_revision = dev->hw_revision; 85c612e54fSHans Verkuil 86c612e54fSHans Verkuil return 0; 87c612e54fSHans Verkuil } 88c612e54fSHans Verkuil 89c612e54fSHans Verkuil static struct media_entity *find_entity(struct media_device *mdev, u32 id) 90c612e54fSHans Verkuil { 91c612e54fSHans Verkuil struct media_entity *entity; 92c612e54fSHans Verkuil int next = id & MEDIA_ENT_ID_FLAG_NEXT; 93c612e54fSHans Verkuil 94c612e54fSHans Verkuil id &= ~MEDIA_ENT_ID_FLAG_NEXT; 95c612e54fSHans Verkuil 96c612e54fSHans Verkuil media_device_for_each_entity(entity, mdev) { 97c612e54fSHans Verkuil if (((media_entity_id(entity) == id) && !next) || 98c612e54fSHans Verkuil ((media_entity_id(entity) > id) && next)) { 99c612e54fSHans Verkuil return entity; 100c612e54fSHans Verkuil } 101c612e54fSHans Verkuil } 102c612e54fSHans Verkuil 103c612e54fSHans Verkuil return NULL; 104c612e54fSHans Verkuil } 105c612e54fSHans Verkuil 106c612e54fSHans Verkuil static long media_device_enum_entities(struct media_device *mdev, void *arg) 107c612e54fSHans Verkuil { 108c612e54fSHans Verkuil struct media_entity_desc *entd = arg; 109c612e54fSHans Verkuil struct media_entity *ent; 110c612e54fSHans Verkuil 111c612e54fSHans Verkuil ent = find_entity(mdev, entd->id); 112c612e54fSHans Verkuil if (ent == NULL) 113c612e54fSHans Verkuil return -EINVAL; 114c612e54fSHans Verkuil 115c612e54fSHans Verkuil memset(entd, 0, sizeof(*entd)); 116c612e54fSHans Verkuil 117c612e54fSHans Verkuil entd->id = media_entity_id(ent); 118c612e54fSHans Verkuil if (ent->name) 119c612e54fSHans Verkuil strscpy(entd->name, ent->name, sizeof(entd->name)); 120c612e54fSHans Verkuil entd->type = ent->function; 121c612e54fSHans Verkuil entd->revision = 0; /* Unused */ 122c612e54fSHans Verkuil entd->flags = ent->flags; 123c612e54fSHans Verkuil entd->group_id = 0; /* Unused */ 124c612e54fSHans Verkuil entd->pads = ent->num_pads; 125c612e54fSHans Verkuil entd->links = ent->num_links - ent->num_backlinks; 126c612e54fSHans Verkuil 127c612e54fSHans Verkuil /* 128c612e54fSHans Verkuil * Workaround for a bug at media-ctl <= v1.10 that makes it to 129c612e54fSHans Verkuil * do the wrong thing if the entity function doesn't belong to 130c612e54fSHans Verkuil * either MEDIA_ENT_F_OLD_BASE or MEDIA_ENT_F_OLD_SUBDEV_BASE 131c612e54fSHans Verkuil * Ranges. 132c612e54fSHans Verkuil * 133c612e54fSHans Verkuil * Non-subdevices are expected to be at the MEDIA_ENT_F_OLD_BASE, 134c612e54fSHans Verkuil * or, otherwise, will be silently ignored by media-ctl when 135c612e54fSHans Verkuil * printing the graphviz diagram. So, map them into the devnode 136c612e54fSHans Verkuil * old range. 137c612e54fSHans Verkuil */ 138c612e54fSHans Verkuil if (ent->function < MEDIA_ENT_F_OLD_BASE || 139c612e54fSHans Verkuil ent->function > MEDIA_ENT_F_TUNER) { 140c612e54fSHans Verkuil if (is_media_entity_v4l2_subdev(ent)) 141c612e54fSHans Verkuil entd->type = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; 142c612e54fSHans Verkuil else if (ent->function != MEDIA_ENT_F_IO_V4L) 143c612e54fSHans Verkuil entd->type = MEDIA_ENT_T_DEVNODE_UNKNOWN; 144c612e54fSHans Verkuil } 145c612e54fSHans Verkuil 146c612e54fSHans Verkuil memcpy(&entd->raw, &ent->info, sizeof(ent->info)); 147c612e54fSHans Verkuil 148c612e54fSHans Verkuil return 0; 149c612e54fSHans Verkuil } 150c612e54fSHans Verkuil 151c612e54fSHans Verkuil static void media_device_kpad_to_upad(const struct media_pad *kpad, 152c612e54fSHans Verkuil struct media_pad_desc *upad) 153c612e54fSHans Verkuil { 154c612e54fSHans Verkuil upad->entity = media_entity_id(kpad->entity); 155c612e54fSHans Verkuil upad->index = kpad->index; 156c612e54fSHans Verkuil upad->flags = kpad->flags; 157c612e54fSHans Verkuil } 158c612e54fSHans Verkuil 159c612e54fSHans Verkuil static long media_device_enum_links(struct media_device *mdev, void *arg) 160c612e54fSHans Verkuil { 161c612e54fSHans Verkuil struct media_links_enum *links = arg; 162c612e54fSHans Verkuil struct media_entity *entity; 163c612e54fSHans Verkuil 164c612e54fSHans Verkuil entity = find_entity(mdev, links->entity); 165c612e54fSHans Verkuil if (entity == NULL) 166c612e54fSHans Verkuil return -EINVAL; 167c612e54fSHans Verkuil 168c612e54fSHans Verkuil if (links->pads) { 169c612e54fSHans Verkuil unsigned int p; 170c612e54fSHans Verkuil 171c612e54fSHans Verkuil for (p = 0; p < entity->num_pads; p++) { 172c612e54fSHans Verkuil struct media_pad_desc pad; 173c612e54fSHans Verkuil 174c612e54fSHans Verkuil memset(&pad, 0, sizeof(pad)); 175c612e54fSHans Verkuil media_device_kpad_to_upad(&entity->pads[p], &pad); 176c612e54fSHans Verkuil if (copy_to_user(&links->pads[p], &pad, sizeof(pad))) 177c612e54fSHans Verkuil return -EFAULT; 178c612e54fSHans Verkuil } 179c612e54fSHans Verkuil } 180c612e54fSHans Verkuil 181c612e54fSHans Verkuil if (links->links) { 182c612e54fSHans Verkuil struct media_link *link; 183c612e54fSHans Verkuil struct media_link_desc __user *ulink_desc = links->links; 184c612e54fSHans Verkuil 185c612e54fSHans Verkuil list_for_each_entry(link, &entity->links, list) { 186c612e54fSHans Verkuil struct media_link_desc klink_desc; 187c612e54fSHans Verkuil 188c612e54fSHans Verkuil /* Ignore backlinks. */ 189c612e54fSHans Verkuil if (link->source->entity != entity) 190c612e54fSHans Verkuil continue; 191c612e54fSHans Verkuil memset(&klink_desc, 0, sizeof(klink_desc)); 192c612e54fSHans Verkuil media_device_kpad_to_upad(link->source, 193c612e54fSHans Verkuil &klink_desc.source); 194c612e54fSHans Verkuil media_device_kpad_to_upad(link->sink, 195c612e54fSHans Verkuil &klink_desc.sink); 196c612e54fSHans Verkuil klink_desc.flags = link->flags; 197c612e54fSHans Verkuil if (copy_to_user(ulink_desc, &klink_desc, 198c612e54fSHans Verkuil sizeof(*ulink_desc))) 199c612e54fSHans Verkuil return -EFAULT; 200c612e54fSHans Verkuil ulink_desc++; 201c612e54fSHans Verkuil } 202c612e54fSHans Verkuil } 203c612e54fSHans Verkuil memset(links->reserved, 0, sizeof(links->reserved)); 204c612e54fSHans Verkuil 205c612e54fSHans Verkuil return 0; 206c612e54fSHans Verkuil } 207c612e54fSHans Verkuil 208c612e54fSHans Verkuil static long media_device_setup_link(struct media_device *mdev, void *arg) 209c612e54fSHans Verkuil { 210c612e54fSHans Verkuil struct media_link_desc *linkd = arg; 211c612e54fSHans Verkuil struct media_link *link = NULL; 212c612e54fSHans Verkuil struct media_entity *source; 213c612e54fSHans Verkuil struct media_entity *sink; 214c612e54fSHans Verkuil 215c612e54fSHans Verkuil /* Find the source and sink entities and link. 216c612e54fSHans Verkuil */ 217c612e54fSHans Verkuil source = find_entity(mdev, linkd->source.entity); 218c612e54fSHans Verkuil sink = find_entity(mdev, linkd->sink.entity); 219c612e54fSHans Verkuil 220c612e54fSHans Verkuil if (source == NULL || sink == NULL) 221c612e54fSHans Verkuil return -EINVAL; 222c612e54fSHans Verkuil 223c612e54fSHans Verkuil if (linkd->source.index >= source->num_pads || 224c612e54fSHans Verkuil linkd->sink.index >= sink->num_pads) 225c612e54fSHans Verkuil return -EINVAL; 226c612e54fSHans Verkuil 227c612e54fSHans Verkuil link = media_entity_find_link(&source->pads[linkd->source.index], 228c612e54fSHans Verkuil &sink->pads[linkd->sink.index]); 229c612e54fSHans Verkuil if (link == NULL) 230c612e54fSHans Verkuil return -EINVAL; 231c612e54fSHans Verkuil 232c612e54fSHans Verkuil memset(linkd->reserved, 0, sizeof(linkd->reserved)); 233c612e54fSHans Verkuil 234c612e54fSHans Verkuil /* Setup the link on both entities. */ 235c612e54fSHans Verkuil return __media_entity_setup_link(link, linkd->flags); 236c612e54fSHans Verkuil } 237c612e54fSHans Verkuil 238c612e54fSHans Verkuil static long media_device_get_topology(struct media_device *mdev, void *arg) 239c612e54fSHans Verkuil { 240c612e54fSHans Verkuil struct media_v2_topology *topo = arg; 241c612e54fSHans Verkuil struct media_entity *entity; 242c612e54fSHans Verkuil struct media_interface *intf; 243c612e54fSHans Verkuil struct media_pad *pad; 244c612e54fSHans Verkuil struct media_link *link; 245c612e54fSHans Verkuil struct media_v2_entity kentity, __user *uentity; 246c612e54fSHans Verkuil struct media_v2_interface kintf, __user *uintf; 247c612e54fSHans Verkuil struct media_v2_pad kpad, __user *upad; 248c612e54fSHans Verkuil struct media_v2_link klink, __user *ulink; 249c612e54fSHans Verkuil unsigned int i; 250c612e54fSHans Verkuil int ret = 0; 251c612e54fSHans Verkuil 252c612e54fSHans Verkuil topo->topology_version = mdev->topology_version; 253c612e54fSHans Verkuil 254c612e54fSHans Verkuil /* Get entities and number of entities */ 255c612e54fSHans Verkuil i = 0; 256c612e54fSHans Verkuil uentity = media_get_uptr(topo->ptr_entities); 257c612e54fSHans Verkuil media_device_for_each_entity(entity, mdev) { 258c612e54fSHans Verkuil i++; 259c612e54fSHans Verkuil if (ret || !uentity) 260c612e54fSHans Verkuil continue; 261c612e54fSHans Verkuil 262c612e54fSHans Verkuil if (i > topo->num_entities) { 263c612e54fSHans Verkuil ret = -ENOSPC; 264c612e54fSHans Verkuil continue; 265c612e54fSHans Verkuil } 266c612e54fSHans Verkuil 267c612e54fSHans Verkuil /* Copy fields to userspace struct if not error */ 268c612e54fSHans Verkuil memset(&kentity, 0, sizeof(kentity)); 269c612e54fSHans Verkuil kentity.id = entity->graph_obj.id; 270c612e54fSHans Verkuil kentity.function = entity->function; 271c612e54fSHans Verkuil kentity.flags = entity->flags; 272c612e54fSHans Verkuil strscpy(kentity.name, entity->name, 273c612e54fSHans Verkuil sizeof(kentity.name)); 274c612e54fSHans Verkuil 275c612e54fSHans Verkuil if (copy_to_user(uentity, &kentity, sizeof(kentity))) 276c612e54fSHans Verkuil ret = -EFAULT; 277c612e54fSHans Verkuil uentity++; 278c612e54fSHans Verkuil } 279c612e54fSHans Verkuil topo->num_entities = i; 280c612e54fSHans Verkuil topo->reserved1 = 0; 281c612e54fSHans Verkuil 282c612e54fSHans Verkuil /* Get interfaces and number of interfaces */ 283c612e54fSHans Verkuil i = 0; 284c612e54fSHans Verkuil uintf = media_get_uptr(topo->ptr_interfaces); 285c612e54fSHans Verkuil media_device_for_each_intf(intf, mdev) { 286c612e54fSHans Verkuil i++; 287c612e54fSHans Verkuil if (ret || !uintf) 288c612e54fSHans Verkuil continue; 289c612e54fSHans Verkuil 290c612e54fSHans Verkuil if (i > topo->num_interfaces) { 291c612e54fSHans Verkuil ret = -ENOSPC; 292c612e54fSHans Verkuil continue; 293c612e54fSHans Verkuil } 294c612e54fSHans Verkuil 295c612e54fSHans Verkuil memset(&kintf, 0, sizeof(kintf)); 296c612e54fSHans Verkuil 297c612e54fSHans Verkuil /* Copy intf fields to userspace struct */ 298c612e54fSHans Verkuil kintf.id = intf->graph_obj.id; 299c612e54fSHans Verkuil kintf.intf_type = intf->type; 300c612e54fSHans Verkuil kintf.flags = intf->flags; 301c612e54fSHans Verkuil 302c612e54fSHans Verkuil if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) { 303c612e54fSHans Verkuil struct media_intf_devnode *devnode; 304c612e54fSHans Verkuil 305c612e54fSHans Verkuil devnode = intf_to_devnode(intf); 306c612e54fSHans Verkuil 307c612e54fSHans Verkuil kintf.devnode.major = devnode->major; 308c612e54fSHans Verkuil kintf.devnode.minor = devnode->minor; 309c612e54fSHans Verkuil } 310c612e54fSHans Verkuil 311c612e54fSHans Verkuil if (copy_to_user(uintf, &kintf, sizeof(kintf))) 312c612e54fSHans Verkuil ret = -EFAULT; 313c612e54fSHans Verkuil uintf++; 314c612e54fSHans Verkuil } 315c612e54fSHans Verkuil topo->num_interfaces = i; 316c612e54fSHans Verkuil topo->reserved2 = 0; 317c612e54fSHans Verkuil 318c612e54fSHans Verkuil /* Get pads and number of pads */ 319c612e54fSHans Verkuil i = 0; 320c612e54fSHans Verkuil upad = media_get_uptr(topo->ptr_pads); 321c612e54fSHans Verkuil media_device_for_each_pad(pad, mdev) { 322c612e54fSHans Verkuil i++; 323c612e54fSHans Verkuil if (ret || !upad) 324c612e54fSHans Verkuil continue; 325c612e54fSHans Verkuil 326c612e54fSHans Verkuil if (i > topo->num_pads) { 327c612e54fSHans Verkuil ret = -ENOSPC; 328c612e54fSHans Verkuil continue; 329c612e54fSHans Verkuil } 330c612e54fSHans Verkuil 331c612e54fSHans Verkuil memset(&kpad, 0, sizeof(kpad)); 332c612e54fSHans Verkuil 333c612e54fSHans Verkuil /* Copy pad fields to userspace struct */ 334c612e54fSHans Verkuil kpad.id = pad->graph_obj.id; 335c612e54fSHans Verkuil kpad.entity_id = pad->entity->graph_obj.id; 336c612e54fSHans Verkuil kpad.flags = pad->flags; 337c612e54fSHans Verkuil kpad.index = pad->index; 338c612e54fSHans Verkuil 339c612e54fSHans Verkuil if (copy_to_user(upad, &kpad, sizeof(kpad))) 340c612e54fSHans Verkuil ret = -EFAULT; 341c612e54fSHans Verkuil upad++; 342c612e54fSHans Verkuil } 343c612e54fSHans Verkuil topo->num_pads = i; 344c612e54fSHans Verkuil topo->reserved3 = 0; 345c612e54fSHans Verkuil 346c612e54fSHans Verkuil /* Get links and number of links */ 347c612e54fSHans Verkuil i = 0; 348c612e54fSHans Verkuil ulink = media_get_uptr(topo->ptr_links); 349c612e54fSHans Verkuil media_device_for_each_link(link, mdev) { 350c612e54fSHans Verkuil if (link->is_backlink) 351c612e54fSHans Verkuil continue; 352c612e54fSHans Verkuil 353c612e54fSHans Verkuil i++; 354c612e54fSHans Verkuil 355c612e54fSHans Verkuil if (ret || !ulink) 356c612e54fSHans Verkuil continue; 357c612e54fSHans Verkuil 358c612e54fSHans Verkuil if (i > topo->num_links) { 359c612e54fSHans Verkuil ret = -ENOSPC; 360c612e54fSHans Verkuil continue; 361c612e54fSHans Verkuil } 362c612e54fSHans Verkuil 363c612e54fSHans Verkuil memset(&klink, 0, sizeof(klink)); 364c612e54fSHans Verkuil 365c612e54fSHans Verkuil /* Copy link fields to userspace struct */ 366c612e54fSHans Verkuil klink.id = link->graph_obj.id; 367c612e54fSHans Verkuil klink.source_id = link->gobj0->id; 368c612e54fSHans Verkuil klink.sink_id = link->gobj1->id; 369c612e54fSHans Verkuil klink.flags = link->flags; 370c612e54fSHans Verkuil 371c612e54fSHans Verkuil if (copy_to_user(ulink, &klink, sizeof(klink))) 372c612e54fSHans Verkuil ret = -EFAULT; 373c612e54fSHans Verkuil ulink++; 374c612e54fSHans Verkuil } 375c612e54fSHans Verkuil topo->num_links = i; 376c612e54fSHans Verkuil topo->reserved4 = 0; 377c612e54fSHans Verkuil 378c612e54fSHans Verkuil return ret; 379c612e54fSHans Verkuil } 380c612e54fSHans Verkuil 381c612e54fSHans Verkuil static long media_device_request_alloc(struct media_device *mdev, 382c612e54fSHans Verkuil int *alloc_fd) 383c612e54fSHans Verkuil { 384c612e54fSHans Verkuil #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API 385c612e54fSHans Verkuil if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue) 386c612e54fSHans Verkuil return -ENOTTY; 387c612e54fSHans Verkuil 388c612e54fSHans Verkuil return media_request_alloc(mdev, alloc_fd); 389c612e54fSHans Verkuil #else 390c612e54fSHans Verkuil return -ENOTTY; 391c612e54fSHans Verkuil #endif 392c612e54fSHans Verkuil } 393c612e54fSHans Verkuil 394c612e54fSHans Verkuil static long copy_arg_from_user(void *karg, void __user *uarg, unsigned int cmd) 395c612e54fSHans Verkuil { 396c612e54fSHans Verkuil if ((_IOC_DIR(cmd) & _IOC_WRITE) && 397c612e54fSHans Verkuil copy_from_user(karg, uarg, _IOC_SIZE(cmd))) 398c612e54fSHans Verkuil return -EFAULT; 399c612e54fSHans Verkuil 400c612e54fSHans Verkuil return 0; 401c612e54fSHans Verkuil } 402c612e54fSHans Verkuil 403c612e54fSHans Verkuil static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) 404c612e54fSHans Verkuil { 405c612e54fSHans Verkuil if ((_IOC_DIR(cmd) & _IOC_READ) && 406c612e54fSHans Verkuil copy_to_user(uarg, karg, _IOC_SIZE(cmd))) 407c612e54fSHans Verkuil return -EFAULT; 408c612e54fSHans Verkuil 409c612e54fSHans Verkuil return 0; 410c612e54fSHans Verkuil } 411c612e54fSHans Verkuil 412c612e54fSHans Verkuil /* Do acquire the graph mutex */ 413c612e54fSHans Verkuil #define MEDIA_IOC_FL_GRAPH_MUTEX BIT(0) 414c612e54fSHans Verkuil 415c612e54fSHans Verkuil #define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \ 416c612e54fSHans Verkuil [_IOC_NR(MEDIA_IOC_##__cmd)] = { \ 417c612e54fSHans Verkuil .cmd = MEDIA_IOC_##__cmd, \ 418c612e54fSHans Verkuil .fn = (long (*)(struct media_device *, void *))func, \ 419c612e54fSHans Verkuil .flags = fl, \ 420c612e54fSHans Verkuil .arg_from_user = from_user, \ 421c612e54fSHans Verkuil .arg_to_user = to_user, \ 422c612e54fSHans Verkuil } 423c612e54fSHans Verkuil 424c612e54fSHans Verkuil #define MEDIA_IOC(__cmd, func, fl) \ 425c612e54fSHans Verkuil MEDIA_IOC_ARG(__cmd, func, fl, copy_arg_from_user, copy_arg_to_user) 426c612e54fSHans Verkuil 427c612e54fSHans Verkuil /* the table is indexed by _IOC_NR(cmd) */ 428c612e54fSHans Verkuil struct media_ioctl_info { 429c612e54fSHans Verkuil unsigned int cmd; 430c612e54fSHans Verkuil unsigned short flags; 431c612e54fSHans Verkuil long (*fn)(struct media_device *dev, void *arg); 432c612e54fSHans Verkuil long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd); 433c612e54fSHans Verkuil long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd); 434c612e54fSHans Verkuil }; 435c612e54fSHans Verkuil 436c612e54fSHans Verkuil static const struct media_ioctl_info ioctl_info[] = { 437c612e54fSHans Verkuil MEDIA_IOC(DEVICE_INFO, media_device_get_info, MEDIA_IOC_FL_GRAPH_MUTEX), 438c612e54fSHans Verkuil MEDIA_IOC(ENUM_ENTITIES, media_device_enum_entities, MEDIA_IOC_FL_GRAPH_MUTEX), 439c612e54fSHans Verkuil MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX), 440c612e54fSHans Verkuil MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX), 441c612e54fSHans Verkuil MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX), 442c612e54fSHans Verkuil MEDIA_IOC(REQUEST_ALLOC, media_device_request_alloc, 0), 443c612e54fSHans Verkuil }; 444c612e54fSHans Verkuil 445c612e54fSHans Verkuil static long media_device_ioctl(struct file *filp, unsigned int cmd, 446c612e54fSHans Verkuil unsigned long __arg) 447c612e54fSHans Verkuil { 448c612e54fSHans Verkuil struct media_devnode *devnode = media_devnode_data(filp); 449c612e54fSHans Verkuil struct media_device *dev = devnode->media_dev; 450c612e54fSHans Verkuil const struct media_ioctl_info *info; 451c612e54fSHans Verkuil void __user *arg = (void __user *)__arg; 452c612e54fSHans Verkuil char __karg[256], *karg = __karg; 453c612e54fSHans Verkuil long ret; 454c612e54fSHans Verkuil 455c612e54fSHans Verkuil if (_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_info) 456c612e54fSHans Verkuil || ioctl_info[_IOC_NR(cmd)].cmd != cmd) 457c612e54fSHans Verkuil return -ENOIOCTLCMD; 458c612e54fSHans Verkuil 459c612e54fSHans Verkuil info = &ioctl_info[_IOC_NR(cmd)]; 460c612e54fSHans Verkuil 461c612e54fSHans Verkuil if (_IOC_SIZE(info->cmd) > sizeof(__karg)) { 462c612e54fSHans Verkuil karg = kmalloc(_IOC_SIZE(info->cmd), GFP_KERNEL); 463c612e54fSHans Verkuil if (!karg) 464c612e54fSHans Verkuil return -ENOMEM; 465c612e54fSHans Verkuil } 466c612e54fSHans Verkuil 467c612e54fSHans Verkuil if (info->arg_from_user) { 468c612e54fSHans Verkuil ret = info->arg_from_user(karg, arg, cmd); 469c612e54fSHans Verkuil if (ret) 470c612e54fSHans Verkuil goto out_free; 471c612e54fSHans Verkuil } 472c612e54fSHans Verkuil 473c612e54fSHans Verkuil if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) 474c612e54fSHans Verkuil mutex_lock(&dev->graph_mutex); 475c612e54fSHans Verkuil 476c612e54fSHans Verkuil ret = info->fn(dev, karg); 477c612e54fSHans Verkuil 478c612e54fSHans Verkuil if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX) 479c612e54fSHans Verkuil mutex_unlock(&dev->graph_mutex); 480c612e54fSHans Verkuil 481c612e54fSHans Verkuil if (!ret && info->arg_to_user) 482c612e54fSHans Verkuil ret = info->arg_to_user(arg, karg, cmd); 483c612e54fSHans Verkuil 484c612e54fSHans Verkuil out_free: 485c612e54fSHans Verkuil if (karg != __karg) 486c612e54fSHans Verkuil kfree(karg); 487c612e54fSHans Verkuil 488c612e54fSHans Verkuil return ret; 489c612e54fSHans Verkuil } 490c612e54fSHans Verkuil 491c612e54fSHans Verkuil #ifdef CONFIG_COMPAT 492c612e54fSHans Verkuil 493c612e54fSHans Verkuil struct media_links_enum32 { 494c612e54fSHans Verkuil __u32 entity; 495c612e54fSHans Verkuil compat_uptr_t pads; /* struct media_pad_desc * */ 496c612e54fSHans Verkuil compat_uptr_t links; /* struct media_link_desc * */ 497c612e54fSHans Verkuil __u32 reserved[4]; 498c612e54fSHans Verkuil }; 499c612e54fSHans Verkuil 500c612e54fSHans Verkuil static long media_device_enum_links32(struct media_device *mdev, 501c612e54fSHans Verkuil struct media_links_enum32 __user *ulinks) 502c612e54fSHans Verkuil { 503c612e54fSHans Verkuil struct media_links_enum links; 504c612e54fSHans Verkuil compat_uptr_t pads_ptr, links_ptr; 505c612e54fSHans Verkuil int ret; 506c612e54fSHans Verkuil 507c612e54fSHans Verkuil memset(&links, 0, sizeof(links)); 508c612e54fSHans Verkuil 509c612e54fSHans Verkuil if (get_user(links.entity, &ulinks->entity) 510c612e54fSHans Verkuil || get_user(pads_ptr, &ulinks->pads) 511c612e54fSHans Verkuil || get_user(links_ptr, &ulinks->links)) 512c612e54fSHans Verkuil return -EFAULT; 513c612e54fSHans Verkuil 514c612e54fSHans Verkuil links.pads = compat_ptr(pads_ptr); 515c612e54fSHans Verkuil links.links = compat_ptr(links_ptr); 516c612e54fSHans Verkuil 517c612e54fSHans Verkuil ret = media_device_enum_links(mdev, &links); 518c612e54fSHans Verkuil if (ret) 519c612e54fSHans Verkuil return ret; 520c612e54fSHans Verkuil 521*518fa4e0SHans Verkuil if (copy_to_user(ulinks->reserved, links.reserved, 522*518fa4e0SHans Verkuil sizeof(ulinks->reserved))) 523*518fa4e0SHans Verkuil return -EFAULT; 524c612e54fSHans Verkuil return 0; 525c612e54fSHans Verkuil } 526c612e54fSHans Verkuil 527c612e54fSHans Verkuil #define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) 528c612e54fSHans Verkuil 529c612e54fSHans Verkuil static long media_device_compat_ioctl(struct file *filp, unsigned int cmd, 530c612e54fSHans Verkuil unsigned long arg) 531c612e54fSHans Verkuil { 532c612e54fSHans Verkuil struct media_devnode *devnode = media_devnode_data(filp); 533c612e54fSHans Verkuil struct media_device *dev = devnode->media_dev; 534c612e54fSHans Verkuil long ret; 535c612e54fSHans Verkuil 536c612e54fSHans Verkuil switch (cmd) { 537c612e54fSHans Verkuil case MEDIA_IOC_ENUM_LINKS32: 538c612e54fSHans Verkuil mutex_lock(&dev->graph_mutex); 539c612e54fSHans Verkuil ret = media_device_enum_links32(dev, 540c612e54fSHans Verkuil (struct media_links_enum32 __user *)arg); 541c612e54fSHans Verkuil mutex_unlock(&dev->graph_mutex); 542c612e54fSHans Verkuil break; 543c612e54fSHans Verkuil 544c612e54fSHans Verkuil default: 545c612e54fSHans Verkuil return media_device_ioctl(filp, cmd, arg); 546c612e54fSHans Verkuil } 547c612e54fSHans Verkuil 548c612e54fSHans Verkuil return ret; 549c612e54fSHans Verkuil } 550c612e54fSHans Verkuil #endif /* CONFIG_COMPAT */ 551c612e54fSHans Verkuil 552c612e54fSHans Verkuil static const struct media_file_operations media_device_fops = { 553c612e54fSHans Verkuil .owner = THIS_MODULE, 554c612e54fSHans Verkuil .open = media_device_open, 555c612e54fSHans Verkuil .ioctl = media_device_ioctl, 556c612e54fSHans Verkuil #ifdef CONFIG_COMPAT 557c612e54fSHans Verkuil .compat_ioctl = media_device_compat_ioctl, 558c612e54fSHans Verkuil #endif /* CONFIG_COMPAT */ 559c612e54fSHans Verkuil .release = media_device_close, 560c612e54fSHans Verkuil }; 561c612e54fSHans Verkuil 562c612e54fSHans Verkuil /* ----------------------------------------------------------------------------- 563c612e54fSHans Verkuil * sysfs 564c612e54fSHans Verkuil */ 565c612e54fSHans Verkuil 566c612e54fSHans Verkuil static ssize_t show_model(struct device *cd, 567c612e54fSHans Verkuil struct device_attribute *attr, char *buf) 568c612e54fSHans Verkuil { 569c612e54fSHans Verkuil struct media_devnode *devnode = to_media_devnode(cd); 570c612e54fSHans Verkuil struct media_device *mdev = devnode->media_dev; 571c612e54fSHans Verkuil 572c612e54fSHans Verkuil return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); 573c612e54fSHans Verkuil } 574c612e54fSHans Verkuil 575c612e54fSHans Verkuil static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); 576c612e54fSHans Verkuil 577c612e54fSHans Verkuil /* ----------------------------------------------------------------------------- 578c612e54fSHans Verkuil * Registration/unregistration 579c612e54fSHans Verkuil */ 580c612e54fSHans Verkuil 581c612e54fSHans Verkuil static void media_device_release(struct media_devnode *devnode) 582c612e54fSHans Verkuil { 583c612e54fSHans Verkuil dev_dbg(devnode->parent, "Media device released\n"); 584c612e54fSHans Verkuil } 585c612e54fSHans Verkuil 586c612e54fSHans Verkuil /** 587c612e54fSHans Verkuil * media_device_register_entity - Register an entity with a media device 588c612e54fSHans Verkuil * @mdev: The media device 589c612e54fSHans Verkuil * @entity: The entity 590c612e54fSHans Verkuil */ 591c612e54fSHans Verkuil int __must_check media_device_register_entity(struct media_device *mdev, 592c612e54fSHans Verkuil struct media_entity *entity) 593c612e54fSHans Verkuil { 594c612e54fSHans Verkuil struct media_entity_notify *notify, *next; 595c612e54fSHans Verkuil unsigned int i; 596c612e54fSHans Verkuil int ret; 597c612e54fSHans Verkuil 598c612e54fSHans Verkuil if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN || 599c612e54fSHans Verkuil entity->function == MEDIA_ENT_F_UNKNOWN) 600c612e54fSHans Verkuil dev_warn(mdev->dev, 601c612e54fSHans Verkuil "Entity type for entity %s was not initialized!\n", 602c612e54fSHans Verkuil entity->name); 603c612e54fSHans Verkuil 604c612e54fSHans Verkuil /* Warn if we apparently re-register an entity */ 605c612e54fSHans Verkuil WARN_ON(entity->graph_obj.mdev != NULL); 606c612e54fSHans Verkuil entity->graph_obj.mdev = mdev; 607c612e54fSHans Verkuil INIT_LIST_HEAD(&entity->links); 608c612e54fSHans Verkuil entity->num_links = 0; 609c612e54fSHans Verkuil entity->num_backlinks = 0; 610c612e54fSHans Verkuil 611c612e54fSHans Verkuil ret = ida_alloc_min(&mdev->entity_internal_idx, 1, GFP_KERNEL); 612c612e54fSHans Verkuil if (ret < 0) 613c612e54fSHans Verkuil return ret; 614c612e54fSHans Verkuil entity->internal_idx = ret; 615c612e54fSHans Verkuil 616c612e54fSHans Verkuil mutex_lock(&mdev->graph_mutex); 617c612e54fSHans Verkuil mdev->entity_internal_idx_max = 618c612e54fSHans Verkuil max(mdev->entity_internal_idx_max, entity->internal_idx); 619c612e54fSHans Verkuil 620c612e54fSHans Verkuil /* Initialize media_gobj embedded at the entity */ 621c612e54fSHans Verkuil media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj); 622c612e54fSHans Verkuil 623c612e54fSHans Verkuil /* Initialize objects at the pads */ 624c612e54fSHans Verkuil for (i = 0; i < entity->num_pads; i++) 625c612e54fSHans Verkuil media_gobj_create(mdev, MEDIA_GRAPH_PAD, 626c612e54fSHans Verkuil &entity->pads[i].graph_obj); 627c612e54fSHans Verkuil 628c612e54fSHans Verkuil /* invoke entity_notify callbacks */ 629c612e54fSHans Verkuil list_for_each_entry_safe(notify, next, &mdev->entity_notify, list) 630c612e54fSHans Verkuil notify->notify(entity, notify->notify_data); 631c612e54fSHans Verkuil 632c612e54fSHans Verkuil if (mdev->entity_internal_idx_max 633c612e54fSHans Verkuil >= mdev->pm_count_walk.ent_enum.idx_max) { 634c612e54fSHans Verkuil struct media_graph new = { .top = 0 }; 635c612e54fSHans Verkuil 636c612e54fSHans Verkuil /* 637c612e54fSHans Verkuil * Initialise the new graph walk before cleaning up 638c612e54fSHans Verkuil * the old one in order not to spoil the graph walk 639c612e54fSHans Verkuil * object of the media device if graph walk init fails. 640c612e54fSHans Verkuil */ 641c612e54fSHans Verkuil ret = media_graph_walk_init(&new, mdev); 642c612e54fSHans Verkuil if (ret) { 643c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 644c612e54fSHans Verkuil return ret; 645c612e54fSHans Verkuil } 646c612e54fSHans Verkuil media_graph_walk_cleanup(&mdev->pm_count_walk); 647c612e54fSHans Verkuil mdev->pm_count_walk = new; 648c612e54fSHans Verkuil } 649c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 650c612e54fSHans Verkuil 651c612e54fSHans Verkuil return 0; 652c612e54fSHans Verkuil } 653c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_register_entity); 654c612e54fSHans Verkuil 655c612e54fSHans Verkuil static void __media_device_unregister_entity(struct media_entity *entity) 656c612e54fSHans Verkuil { 657c612e54fSHans Verkuil struct media_device *mdev = entity->graph_obj.mdev; 658c612e54fSHans Verkuil struct media_link *link, *tmp; 659c612e54fSHans Verkuil struct media_interface *intf; 660c612e54fSHans Verkuil unsigned int i; 661c612e54fSHans Verkuil 662c612e54fSHans Verkuil ida_free(&mdev->entity_internal_idx, entity->internal_idx); 663c612e54fSHans Verkuil 664c612e54fSHans Verkuil /* Remove all interface links pointing to this entity */ 665c612e54fSHans Verkuil list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) { 666c612e54fSHans Verkuil list_for_each_entry_safe(link, tmp, &intf->links, list) { 667c612e54fSHans Verkuil if (link->entity == entity) 668c612e54fSHans Verkuil __media_remove_intf_link(link); 669c612e54fSHans Verkuil } 670c612e54fSHans Verkuil } 671c612e54fSHans Verkuil 672c612e54fSHans Verkuil /* Remove all data links that belong to this entity */ 673c612e54fSHans Verkuil __media_entity_remove_links(entity); 674c612e54fSHans Verkuil 675c612e54fSHans Verkuil /* Remove all pads that belong to this entity */ 676c612e54fSHans Verkuil for (i = 0; i < entity->num_pads; i++) 677c612e54fSHans Verkuil media_gobj_destroy(&entity->pads[i].graph_obj); 678c612e54fSHans Verkuil 679c612e54fSHans Verkuil /* Remove the entity */ 680c612e54fSHans Verkuil media_gobj_destroy(&entity->graph_obj); 681c612e54fSHans Verkuil 682c612e54fSHans Verkuil /* invoke entity_notify callbacks to handle entity removal?? */ 683c612e54fSHans Verkuil 684c612e54fSHans Verkuil entity->graph_obj.mdev = NULL; 685c612e54fSHans Verkuil } 686c612e54fSHans Verkuil 687c612e54fSHans Verkuil void media_device_unregister_entity(struct media_entity *entity) 688c612e54fSHans Verkuil { 689c612e54fSHans Verkuil struct media_device *mdev = entity->graph_obj.mdev; 690c612e54fSHans Verkuil 691c612e54fSHans Verkuil if (mdev == NULL) 692c612e54fSHans Verkuil return; 693c612e54fSHans Verkuil 694c612e54fSHans Verkuil mutex_lock(&mdev->graph_mutex); 695c612e54fSHans Verkuil __media_device_unregister_entity(entity); 696c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 697c612e54fSHans Verkuil } 698c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_unregister_entity); 699c612e54fSHans Verkuil 700c612e54fSHans Verkuil /** 701c612e54fSHans Verkuil * media_device_init() - initialize a media device 702c612e54fSHans Verkuil * @mdev: The media device 703c612e54fSHans Verkuil * 704c612e54fSHans Verkuil * The caller is responsible for initializing the media device before 705c612e54fSHans Verkuil * registration. The following fields must be set: 706c612e54fSHans Verkuil * 707c612e54fSHans Verkuil * - dev must point to the parent device 708c612e54fSHans Verkuil * - model must be filled with the device model name 709c612e54fSHans Verkuil */ 710c612e54fSHans Verkuil void media_device_init(struct media_device *mdev) 711c612e54fSHans Verkuil { 712c612e54fSHans Verkuil INIT_LIST_HEAD(&mdev->entities); 713c612e54fSHans Verkuil INIT_LIST_HEAD(&mdev->interfaces); 714c612e54fSHans Verkuil INIT_LIST_HEAD(&mdev->pads); 715c612e54fSHans Verkuil INIT_LIST_HEAD(&mdev->links); 716c612e54fSHans Verkuil INIT_LIST_HEAD(&mdev->entity_notify); 717c612e54fSHans Verkuil 718c612e54fSHans Verkuil mutex_init(&mdev->req_queue_mutex); 719c612e54fSHans Verkuil mutex_init(&mdev->graph_mutex); 720c612e54fSHans Verkuil ida_init(&mdev->entity_internal_idx); 721c612e54fSHans Verkuil 722c612e54fSHans Verkuil atomic_set(&mdev->request_id, 0); 723c612e54fSHans Verkuil 724c612e54fSHans Verkuil dev_dbg(mdev->dev, "Media device initialized\n"); 725c612e54fSHans Verkuil } 726c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_init); 727c612e54fSHans Verkuil 728c612e54fSHans Verkuil void media_device_cleanup(struct media_device *mdev) 729c612e54fSHans Verkuil { 730c612e54fSHans Verkuil ida_destroy(&mdev->entity_internal_idx); 731c612e54fSHans Verkuil mdev->entity_internal_idx_max = 0; 732c612e54fSHans Verkuil media_graph_walk_cleanup(&mdev->pm_count_walk); 733c612e54fSHans Verkuil mutex_destroy(&mdev->graph_mutex); 734c612e54fSHans Verkuil mutex_destroy(&mdev->req_queue_mutex); 735c612e54fSHans Verkuil } 736c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_cleanup); 737c612e54fSHans Verkuil 738c612e54fSHans Verkuil int __must_check __media_device_register(struct media_device *mdev, 739c612e54fSHans Verkuil struct module *owner) 740c612e54fSHans Verkuil { 741c612e54fSHans Verkuil struct media_devnode *devnode; 742c612e54fSHans Verkuil int ret; 743c612e54fSHans Verkuil 744c612e54fSHans Verkuil devnode = kzalloc(sizeof(*devnode), GFP_KERNEL); 745c612e54fSHans Verkuil if (!devnode) 746c612e54fSHans Verkuil return -ENOMEM; 747c612e54fSHans Verkuil 748c612e54fSHans Verkuil /* Register the device node. */ 749c612e54fSHans Verkuil mdev->devnode = devnode; 750c612e54fSHans Verkuil devnode->fops = &media_device_fops; 751c612e54fSHans Verkuil devnode->parent = mdev->dev; 752c612e54fSHans Verkuil devnode->release = media_device_release; 753c612e54fSHans Verkuil 754c612e54fSHans Verkuil /* Set version 0 to indicate user-space that the graph is static */ 755c612e54fSHans Verkuil mdev->topology_version = 0; 756c612e54fSHans Verkuil 757c612e54fSHans Verkuil ret = media_devnode_register(mdev, devnode, owner); 758c612e54fSHans Verkuil if (ret < 0) { 759c612e54fSHans Verkuil /* devnode free is handled in media_devnode_*() */ 760c612e54fSHans Verkuil mdev->devnode = NULL; 761c612e54fSHans Verkuil return ret; 762c612e54fSHans Verkuil } 763c612e54fSHans Verkuil 764c612e54fSHans Verkuil ret = device_create_file(&devnode->dev, &dev_attr_model); 765c612e54fSHans Verkuil if (ret < 0) { 766c612e54fSHans Verkuil /* devnode free is handled in media_devnode_*() */ 767c612e54fSHans Verkuil mdev->devnode = NULL; 768c612e54fSHans Verkuil media_devnode_unregister_prepare(devnode); 769c612e54fSHans Verkuil media_devnode_unregister(devnode); 770c612e54fSHans Verkuil return ret; 771c612e54fSHans Verkuil } 772c612e54fSHans Verkuil 773c612e54fSHans Verkuil dev_dbg(mdev->dev, "Media device registered\n"); 774c612e54fSHans Verkuil 775c612e54fSHans Verkuil return 0; 776c612e54fSHans Verkuil } 777c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(__media_device_register); 778c612e54fSHans Verkuil 779c612e54fSHans Verkuil int __must_check media_device_register_entity_notify(struct media_device *mdev, 780c612e54fSHans Verkuil struct media_entity_notify *nptr) 781c612e54fSHans Verkuil { 782c612e54fSHans Verkuil mutex_lock(&mdev->graph_mutex); 783c612e54fSHans Verkuil list_add_tail(&nptr->list, &mdev->entity_notify); 784c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 785c612e54fSHans Verkuil return 0; 786c612e54fSHans Verkuil } 787c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_register_entity_notify); 788c612e54fSHans Verkuil 789c612e54fSHans Verkuil /* 790c612e54fSHans Verkuil * Note: Should be called with mdev->lock held. 791c612e54fSHans Verkuil */ 792c612e54fSHans Verkuil static void __media_device_unregister_entity_notify(struct media_device *mdev, 793c612e54fSHans Verkuil struct media_entity_notify *nptr) 794c612e54fSHans Verkuil { 795c612e54fSHans Verkuil list_del(&nptr->list); 796c612e54fSHans Verkuil } 797c612e54fSHans Verkuil 798c612e54fSHans Verkuil void media_device_unregister_entity_notify(struct media_device *mdev, 799c612e54fSHans Verkuil struct media_entity_notify *nptr) 800c612e54fSHans Verkuil { 801c612e54fSHans Verkuil mutex_lock(&mdev->graph_mutex); 802c612e54fSHans Verkuil __media_device_unregister_entity_notify(mdev, nptr); 803c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 804c612e54fSHans Verkuil } 805c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_unregister_entity_notify); 806c612e54fSHans Verkuil 807c612e54fSHans Verkuil void media_device_unregister(struct media_device *mdev) 808c612e54fSHans Verkuil { 809c612e54fSHans Verkuil struct media_entity *entity; 810c612e54fSHans Verkuil struct media_entity *next; 811c612e54fSHans Verkuil struct media_interface *intf, *tmp_intf; 812c612e54fSHans Verkuil struct media_entity_notify *notify, *nextp; 813c612e54fSHans Verkuil 814c612e54fSHans Verkuil if (mdev == NULL) 815c612e54fSHans Verkuil return; 816c612e54fSHans Verkuil 817c612e54fSHans Verkuil mutex_lock(&mdev->graph_mutex); 818c612e54fSHans Verkuil 819c612e54fSHans Verkuil /* Check if mdev was ever registered at all */ 820c612e54fSHans Verkuil if (!media_devnode_is_registered(mdev->devnode)) { 821c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 822c612e54fSHans Verkuil return; 823c612e54fSHans Verkuil } 824c612e54fSHans Verkuil 825c612e54fSHans Verkuil /* Clear the devnode register bit to avoid races with media dev open */ 826c612e54fSHans Verkuil media_devnode_unregister_prepare(mdev->devnode); 827c612e54fSHans Verkuil 828c612e54fSHans Verkuil /* Remove all entities from the media device */ 829c612e54fSHans Verkuil list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list) 830c612e54fSHans Verkuil __media_device_unregister_entity(entity); 831c612e54fSHans Verkuil 832c612e54fSHans Verkuil /* Remove all entity_notify callbacks from the media device */ 833c612e54fSHans Verkuil list_for_each_entry_safe(notify, nextp, &mdev->entity_notify, list) 834c612e54fSHans Verkuil __media_device_unregister_entity_notify(mdev, notify); 835c612e54fSHans Verkuil 836c612e54fSHans Verkuil /* Remove all interfaces from the media device */ 837c612e54fSHans Verkuil list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces, 838c612e54fSHans Verkuil graph_obj.list) { 839c612e54fSHans Verkuil /* 840c612e54fSHans Verkuil * Unlink the interface, but don't free it here; the 841c612e54fSHans Verkuil * module which created it is responsible for freeing 842c612e54fSHans Verkuil * it 843c612e54fSHans Verkuil */ 844c612e54fSHans Verkuil __media_remove_intf_links(intf); 845c612e54fSHans Verkuil media_gobj_destroy(&intf->graph_obj); 846c612e54fSHans Verkuil } 847c612e54fSHans Verkuil 848c612e54fSHans Verkuil mutex_unlock(&mdev->graph_mutex); 849c612e54fSHans Verkuil 850c612e54fSHans Verkuil dev_dbg(mdev->dev, "Media device unregistered\n"); 851c612e54fSHans Verkuil 852c612e54fSHans Verkuil device_remove_file(&mdev->devnode->dev, &dev_attr_model); 853c612e54fSHans Verkuil media_devnode_unregister(mdev->devnode); 854c612e54fSHans Verkuil /* devnode free is handled in media_devnode_*() */ 855c612e54fSHans Verkuil mdev->devnode = NULL; 856c612e54fSHans Verkuil } 857c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_unregister); 858c612e54fSHans Verkuil 859c612e54fSHans Verkuil #if IS_ENABLED(CONFIG_PCI) 860c612e54fSHans Verkuil void media_device_pci_init(struct media_device *mdev, 861c612e54fSHans Verkuil struct pci_dev *pci_dev, 862c612e54fSHans Verkuil const char *name) 863c612e54fSHans Verkuil { 864c612e54fSHans Verkuil mdev->dev = &pci_dev->dev; 865c612e54fSHans Verkuil 866c612e54fSHans Verkuil if (name) 867c612e54fSHans Verkuil strscpy(mdev->model, name, sizeof(mdev->model)); 868c612e54fSHans Verkuil else 869c612e54fSHans Verkuil strscpy(mdev->model, pci_name(pci_dev), sizeof(mdev->model)); 870c612e54fSHans Verkuil 871c612e54fSHans Verkuil sprintf(mdev->bus_info, "PCI:%s", pci_name(pci_dev)); 872c612e54fSHans Verkuil 873c612e54fSHans Verkuil mdev->hw_revision = (pci_dev->subsystem_vendor << 16) 874c612e54fSHans Verkuil | pci_dev->subsystem_device; 875c612e54fSHans Verkuil 876c612e54fSHans Verkuil media_device_init(mdev); 877c612e54fSHans Verkuil } 878c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(media_device_pci_init); 879c612e54fSHans Verkuil #endif 880c612e54fSHans Verkuil 881c612e54fSHans Verkuil #if IS_ENABLED(CONFIG_USB) 882c612e54fSHans Verkuil void __media_device_usb_init(struct media_device *mdev, 883c612e54fSHans Verkuil struct usb_device *udev, 884c612e54fSHans Verkuil const char *board_name, 885c612e54fSHans Verkuil const char *driver_name) 886c612e54fSHans Verkuil { 887c612e54fSHans Verkuil mdev->dev = &udev->dev; 888c612e54fSHans Verkuil 889c612e54fSHans Verkuil if (driver_name) 890c612e54fSHans Verkuil strscpy(mdev->driver_name, driver_name, 891c612e54fSHans Verkuil sizeof(mdev->driver_name)); 892c612e54fSHans Verkuil 893c612e54fSHans Verkuil if (board_name) 894c612e54fSHans Verkuil strscpy(mdev->model, board_name, sizeof(mdev->model)); 895c612e54fSHans Verkuil else if (udev->product) 896c612e54fSHans Verkuil strscpy(mdev->model, udev->product, sizeof(mdev->model)); 897c612e54fSHans Verkuil else 898c612e54fSHans Verkuil strscpy(mdev->model, "unknown model", sizeof(mdev->model)); 899c612e54fSHans Verkuil if (udev->serial) 900c612e54fSHans Verkuil strscpy(mdev->serial, udev->serial, sizeof(mdev->serial)); 901c612e54fSHans Verkuil usb_make_path(udev, mdev->bus_info, sizeof(mdev->bus_info)); 902c612e54fSHans Verkuil mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 903c612e54fSHans Verkuil 904c612e54fSHans Verkuil media_device_init(mdev); 905c612e54fSHans Verkuil } 906c612e54fSHans Verkuil EXPORT_SYMBOL_GPL(__media_device_usb_init); 907c612e54fSHans Verkuil #endif 908c612e54fSHans Verkuil 909c612e54fSHans Verkuil 910c612e54fSHans Verkuil #endif /* CONFIG_MEDIA_CONTROLLER */ 911