F:\Khadas_Edge_Android_Q\kernel\include\media\v4l2-dev.h /* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to * the common handler */
/** * struct video_device - Structure used to create and manage the V4L2 device * nodes. * * @entity: &struct media_entity * @intf_devnode: pointer to &struct media_intf_devnode * @pipe: &struct media_pipeline * @fops: pointer to &struct v4l2_file_operations for the video device * @device_caps: device capabilities as used in v4l2_capabilities * @dev: &struct device for the video device * @cdev: character device * @v4l2_dev: pointer to &struct v4l2_device parent * @dev_parent: pointer to &struct device parent * @ctrl_handler: Control handler associated with this device node. * May be NULL. * @queue: &struct vb2_queue associated with this device node. May be NULL. * @prio: pointer to &struct v4l2_prio_state with device's Priority state. * If NULL, then v4l2_dev->prio will be used. * @name: video device name * @vfl_type: V4L device type, as defined by &enum vfl_devnode_type * @vfl_dir: V4L receiver, transmitter or m2m * @minor: device node 'minor'. It is set to -1 if the registration failed * @num: number of the video device node * @flags: video device flags. Use bitops to set/clear/test flags. * Contains a set of &enum v4l2_video_device_flags. * @index: attribute to differentiate multiple indices on one physical device * @fh_lock: Lock for all v4l2_fhs * @fh_list: List of &struct v4l2_fh * @dev_debug: Internal device debug flags, not for use by drivers * @tvnorms: Supported tv norms * * @release: video device release() callback * @ioctl_ops: pointer to &struct v4l2_ioctl_ops with ioctl callbacks * * @valid_ioctls: bitmap with the valid ioctls for this device * @lock: pointer to &struct mutex serialization lock * * .. note:: * Only set @dev_parent if that can't be deduced from @v4l2_dev. */
structvideo_device { #if defined(CONFIG_MEDIA_CONTROLLER) structmedia_entityentity; structmedia_intf_devnode *intf_devnode; structmedia_pipelinepipe; #endif //pointer to &struct v4l2_file_operations for the video device conststructv4l2_file_operations *fops; //evice capabilities as used in v4l2_capabilities u32 device_caps;
/* sysfs */ structdevicedev; structcdev *cdev;
/* Set either parent or v4l2_dev if your driver uses v4l2_device */ structv4l2_device *v4l2_dev;/* v4l2_device parent */ structdevice *dev_parent;/* device parent */
//Control handler associated with this device node. May be NULL. structv4l2_ctrl_handler *ctrl_handler;
//&struct vb2_queue associated with this device node. May be NULL. structvb2_queue *queue;
///* Priority state. If NULL, then v4l2_dev->prio will be used. */ structv4l2_prio_state *prio;
/* device info */ char name[32]; //video device name enum vfl_devnode_type vfl_type; //V4L device type, as defined by &enum vfl_devnode_type enum vfl_devnode_direction vfl_dir; int minor; u16 num; unsignedlong flags; int index;
F:\Khadas_Edge_Android_Q\kernel\include\media\v4l2-device.h /** * struct v4l2_device - main struct to for V4L2 device drivers * * @dev: pointer to struct device. * @mdev: pointer to struct media_device, may be NULL. * @subdevs: used to keep track of the registered subdevs * @lock: lock this struct; can be used by the driver as well * if this struct is embedded into a larger struct. * @name: unique device name, by default the driver name + bus ID * @notify: notify operation called by some sub-devices. * @ctrl_handler: The control handler. May be %NULL. * @prio: Device's priority state * @ref: Keep track of the references to this struct. * @release: Release function that is called when the ref count * goes to 0. * * Each instance of a V4L2 device should create the v4l2_device struct, * either stand-alone or embedded in a larger struct. * * It allows easy access to sub-devices (see v4l2-subdev.h) and provides * basic V4L2 device-level support. * * .. note:: * * #) @dev->driver_data points to this struct. * #) @dev might be %NULL if there is no parent device */ structv4l2_device { structdevice *dev; structmedia_device *mdev; structlist_headsubdevs; spinlock_t lock; char name[V4L2_DEVICE_NAME_SIZE]; void (*notify)(struct v4l2_subdev *sd, unsignedint notification, void *arg); structv4l2_ctrl_handler *ctrl_handler; structv4l2_prio_stateprio; structkrefref; void (*release)(struct v4l2_device *v4l2_dev); };
F:\Khadas_Edge_Android_Q\kernel\include\media\v4l2-subdev.h /** * struct v4l2_subdev - describes a V4L2 sub-device * * @entity: pointer to &struct media_entity * @list: List of sub-devices * @owner: The owner is the same as the driver's &struct device owner. * @owner_v4l2_dev: true if the &sd->owner matches the owner of @v4l2_dev->dev * owner. Initialized by v4l2_device_register_subdev(). * @flags: subdev flags. Can be: * %V4L2_SUBDEV_FL_IS_I2C - Set this flag if this subdev is a i2c device; * %V4L2_SUBDEV_FL_IS_SPI - Set this flag if this subdev is a spi device; * %V4L2_SUBDEV_FL_HAS_DEVNODE - Set this flag if this subdev needs a * device node; * %V4L2_SUBDEV_FL_HAS_EVENTS - Set this flag if this subdev generates * events. * * @v4l2_dev: pointer to struct &v4l2_device * @ops: pointer to struct &v4l2_subdev_ops * @internal_ops: pointer to struct &v4l2_subdev_internal_ops. * Never call these internal ops from within a driver! * @ctrl_handler: The control handler of this subdev. May be NULL. * @name: Name of the sub-device. Please notice that the name must be unique. * @grp_id: can be used to group similar subdevs. Value is driver-specific * @dev_priv: pointer to private data * @host_priv: pointer to private data used by the device where the subdev * is attached. * @devnode: subdev device node * @dev: pointer to the physical device, if any * @fwnode: The fwnode_handle of the subdev, usually the same as * either dev->of_node->fwnode or dev->fwnode (whichever is non-NULL). * @async_list: Links this subdev to a global subdev_list or @notifier->done * list. * @asd: Pointer to respective &struct v4l2_async_subdev. * @notifier: Pointer to the managing notifier. * @subdev_notifier: A sub-device notifier implicitly registered for the sub- * device using v4l2_device_register_sensor_subdev(). * @pdata: common part of subdevice platform data * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. * * This structure should be initialized by v4l2_subdev_init() or one of * its variants: v4l2_spi_subdev_init(), v4l2_i2c_subdev_init(). */ structv4l2_subdev { #if defined(CONFIG_MEDIA_CONTROLLER) structmedia_entityentity; #endif structlist_headlist; structmodule *owner; bool owner_v4l2_dev; u32 flags; structv4l2_device *v4l2_dev; conststructv4l2_subdev_ops *ops; conststructv4l2_subdev_internal_ops *internal_ops; structv4l2_ctrl_handler *ctrl_handler; char name[V4L2_SUBDEV_NAME_SIZE]; u32 grp_id; void *dev_priv; void *host_priv; structvideo_device *devnode; structdevice *dev; structfwnode_handle *fwnode; structlist_headasync_list; structv4l2_async_subdev *asd; structv4l2_async_notifier *notifier; structv4l2_async_notifier *subdev_notifier; structv4l2_subdev_platform_data *pdata; };
INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); mutex_init(&v4l2_dev->ioctl_lock); v4l2_prio_init(&v4l2_dev->prio); kref_init(&v4l2_dev->ref); //上面都是做一些初始化工作, get_device(dev); //下面将当前设备的对象,赋值给v4l2_dev->dev中,这样的话当前的设备就成了v4l2设备,由于当前设备已经注册过了设备文件,所以后续不需要在重新注册设备文件。 v4l2_dev->dev = dev; if (dev == NULL) { /* If dev == NULL, then name must be filled in by the caller */ if (WARN_ON(!v4l2_dev->name[0])) return -EINVAL; return0; }
/* Set name to driver name + device name if it is empty. */ /* 如果v4l2设备的名字为空,则会将当前设备的名字拷贝为v4l2设备中。*/ if (!v4l2_dev->name[0]) snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", dev->driver->name, dev_name(dev)); //下面是非常重要的一步,即将v4l2设备对象,保存到dev设备中,这个dev可以是platform、char、block等设,我们可以使用dev_get_drvdata(dev)获取到v4l2对象。 if (!dev_get_drvdata(dev)) dev_set_drvdata(dev, v4l2_dev); return0; }
F:\Khadas_Edge_Android_Q\kernel\include\media\v4l2-dev.h /** * video_register_device - register video4linux devices * * @vdev: struct video_device to register * @type: type of device to register, as defined by &enum vfl_devnode_type * @nr: which device node number is desired: * (0 == /dev/video0, 1 == /dev/video1, ..., -1 == first free) * * Internally, it calls __video_register_device(). Please see its * documentation for more details. * * .. note:: * if video_register_device fails, the release() callback of * &struct video_device structure is *not* called, so the caller * is responsible for freeing any data. Usually that means that * you video_device_release() should be called on failure. */ staticinlineint __must_check video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr) { return __video_register_device(vdev, type, nr, 1, vdev->fops->owner); }
F:\Khadas_Edge_Android_Q\kernel\drivers\media\v4l2-core\v4l2-dev.c int __video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr, int warn_if_nr_in_use, struct module *owner) { int i = 0; int ret; int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; constchar *name_base;
/* A minor value of -1 marks this video device as never having been registered */ vdev->minor = -1;
/* the release callback MUST be present */ if (WARN_ON(!vdev->release)) return -EINVAL; /* the v4l2_dev pointer MUST be present */ if (WARN_ON(!vdev->v4l2_dev)) return -EINVAL;
/* v4l2_fh support */ spin_lock_init(&vdev->fh_lock); INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: name_base = "video"; break; case VFL_TYPE_VBI: name_base = "vbi"; break; case VFL_TYPE_RADIO: name_base = "radio"; break; case VFL_TYPE_SUBDEV: name_base = "v4l-subdev"; break; case VFL_TYPE_SDR: /* Use device name 'swradio' because 'sdr' was already taken. */ name_base = "swradio"; break; case VFL_TYPE_TOUCH: name_base = "v4l-touch"; break; default: pr_err("%s called with unknown type: %d\n", __func__, type); return -EINVAL; }
vdev->vfl_type = type; vdev->cdev = NULL; if (vdev->dev_parent == NULL) vdev->dev_parent = vdev->v4l2_dev->dev; if (vdev->ctrl_handler == NULL) vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; /* If the prio state pointer is NULL, then use the v4l2_device prio state. */ if (vdev->prio == NULL) vdev->prio = &vdev->v4l2_dev->prio;
/* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. * Newer devices (not yet in place) should use the range * of 128-191 and just pick the first free minor there * (new style). */ switch (type) { case VFL_TYPE_GRABBER: minor_offset = 0; minor_cnt = 64; break; case VFL_TYPE_RADIO: minor_offset = 64; minor_cnt = 64; break; case VFL_TYPE_VBI: minor_offset = 224; minor_cnt = 32; break; default: minor_offset = 128; minor_cnt = 64; break; } #endif
/* Pick a device node number */ mutex_lock(&videodev_lock); nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); if (nr == minor_cnt) nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) { pr_err("could not get a free device node number\n"); mutex_unlock(&videodev_lock); return -ENFILE; } #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* 1-on-1 mapping of device node number to minor number */ i = nr;//这里保存下空闲的次设备号 #else /* The device node number and minor numbers are independent, so we just find the first free minor number. */ /* 查找到第一个空闲的minor号,可以理解成是次设备号*/ for (i = 0; i < VIDEO_NUM_DEVICES; i++) if (video_devices[i] == NULL)//所有注册的video_device都会保存到这个数组中 break; if (i == VIDEO_NUM_DEVICES) { mutex_unlock(&videodev_lock); pr_err("could not get a free minor\n"); return -ENFILE; } #endif //minor_offset一般是0,i就是查找到的空闲次设备号,这里总的次设备支持到256个 vdev->minor = i + minor_offset; //这里num会保存到session_id中,唯一表示一个任务。一般情况下nr == minor vdev->num = nr;//将标准位置为已用。
/* Should not happen since we thought this minor was free */ if (WARN_ON(video_devices[vdev->minor])) { mutex_unlock(&videodev_lock); pr_err("video_device not empty!\n"); return -ENFILE; } devnode_set(vdev); /* 下面这个方法字面意思看起来是获取一个index,但是查看源代码会发现 “这里会去查找不是直系的设备空闲号”,就是说只有video_device不为空,而且v4l2 parent 对象相同都会认为是同类,直接跳过相应的index号*/ vdev->index = get_index(vdev); /* 下面要重点了,这里根据次设备号将当前video_device保存到video_device[]数组中*/ video_devices[vdev->minor] = vdev; mutex_unlock(&videodev_lock);
if (vdev->ioctl_ops) determine_valid_ioctls(vdev); /* 分配字符设备文件*/ /* Part 3: Initialize the character device */ vdev->cdev = cdev_alloc(); if (vdev->cdev == NULL) { ret = -ENOMEM; goto cleanup; }//务必留意这个ioctl,后面子设备中的ioctl都是通过这里查找的。 vdev->cdev->ops = &v4l2_fops; vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { pr_err("%s: cdev_add failed\n", __func__); kfree(vdev->cdev); vdev->cdev = NULL; goto cleanup; }
/* Part 4: register the device with sysfs */ vdev->dev.class = &video_class;//根目录sys中有对应的属性可操作。 vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);//主设备号为81 vdev->dev.parent = vdev->dev_parent;//这里一般是v4l2_device对象 dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev);//注册该video_device到kernel中。 if (ret < 0) { pr_err("%s: device_register failed\n", __func__); goto cleanup; } /* Register the release callback that will be called when the last reference to the device goes away. */ vdev->dev.release = v4l2_device_release;
/* Part 5: Register the entity. */ ret = video_register_media_controller(vdev);
/* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags);
return0;
cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); video_devices[vdev->minor] = NULL; devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ vdev->minor = -1; return ret; }
F:\Khadas_Edge_Android_Q\kernel\drivers\media\v4l2-core\v4l2-device.c intv4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd) { #if defined(CONFIG_MEDIA_CONTROLLER) //获取到子设备的入口对象,方便后面注册到media_device上面。 structmedia_entity *entity = &sd->entity; #endif int err; //此处省略一些错误检查代码 /* * The reason to acquire the module here is to avoid unloading * a module of sub-device which is registered to a media * device. To make it possible to unload modules for media * devices that also register sub-devices, do not * try_module_get() such sub-device owners. */ sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver && sd->owner == v4l2_dev->dev->driver->owner;
if (!sd->owner_v4l2_dev && !try_module_get(sd->owner)) return -ENODEV; //下面v4l2_dev一般为系统根v4l2_device设备。 sd->v4l2_dev = v4l2_dev; //这里对应具体的子设备,可以发现调用了registered()回调,如果有需要可以在对应的设备驱动中实现。 if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); if (err) goto error_module; } //印证了之前说的,子设备的ctrl_handler都会挂载到根设备v4l2_device的ctrl_handler上面。 /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); if (err) goto error_unregister;
#if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { //上面将 err = media_device_register_entity(v4l2_dev->mdev, entity); if (err < 0) goto error_unregister; } #endif
/* Register a device node for every subdev marked with the * V4L2_SUBDEV_FL_HAS_DEVNODE flag. */ list_for_each_entry(sd, &v4l2_dev->subdevs, list) { if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) continue;
F:\Khadas_Edge_Android_Q\kernel\drivers\media\media-device.c /** * media_device_register - register a media device * @mdev: The media device * * The caller is responsible for initializing the media device before * registration. The following fields must be set: * * - dev must point to the parent device * - model must be filled with the device model name */ int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { int ret; if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) return -EINVAL;
F:\Khadas_Edge_Android_Q\kernel\drivers\media\media-devnode.c /** * media_devnode_register - register a media device node * @mdev: media device node structure we want to register * * The registration code assigns minor numbers and registers the new device node * with the kernel. An error is returned if no free minor number can be found, * or if the registration of the device node fails. * * Zero is returned on success. * * Note that if the media_devnode_register call fails, the release() callback of * the media_devnode structure is *not* called, so the caller is responsible for * freeing any data. */ int __must_check media_devnode_register(struct media_devnode *mdev, struct module *owner) { int minor; int ret;
/* Part 1: Find a free minor number */ mutex_lock(&media_devnode_lock); //获取可用的次设备编号。 minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0); if (minor == MEDIA_NUM_DEVICES) { mutex_unlock(&media_devnode_lock); pr_err("could not get a free minor\n"); return -ENFILE; }
F:\Khadas_Edge_Android_Q\kernel\include\media\v4l2-fh.h /** * struct v4l2_fh - Describes a V4L2 file handler * * @list: list of file handlers * @vdev: pointer to &struct video_device * @ctrl_handler: pointer to &struct v4l2_ctrl_handler * @prio: priority of the file handler, as defined by &enum v4l2_priority * * @wait: event' s wait queue * @subscribe_lock: serialise changes to the subscribed list; guarantee that * the add and del event callbacks are orderly called * @subscribed: list of subscribed events * @available: list of events waiting to be dequeued * @navailable: number of available events at @available list * @sequence: event sequence number * * @m2m_ctx: pointer to &struct v4l2_m2m_ctx */ structv4l2_fh { structlist_headlist; structvideo_device *vdev; structv4l2_ctrl_handler *ctrl_handler; enum v4l2_priority prio;
F:\Khadas_Edge_Android_Q\kernel\include\uapi\linux\videodev2.h /** * struct v4l2_subscribed_event - Internal struct representing a subscribed * event. * * @list: List node for the v4l2_fh->subscribed list. * @type: Event type. * @id: Associated object ID (e.g. control ID). 0 if there isn't any. * @flags: Copy of v4l2_event_subscription->flags. * @fh: Filehandle that subscribed to this event. * @node: List node that hooks into the object's event list * (if there is one). * @ops: v4l2_subscribed_event_ops * @elems: The number of elements in the events array. * @first: The index of the events containing the oldest available event. * @in_use: The number of queued events. * @events: An array of @elems events. */ structv4l2_subscribed_event { structlist_headlist; u32 type; u32 id; u32 flags; structv4l2_fh *fh; structlist_headnode; conststructv4l2_subscribed_event_ops *ops; unsignedint elems; unsignedint first; unsignedint in_use; structv4l2_keventevents[]; };
/* Are we subscribed? */ //这里特别要注意了,前面说过消息必须先订阅,才能queue进来,这了可以看到 //当检测到订阅消息列表中,没有当前消息,则直接return. sev = v4l2_event_subscribed(fh, ev->type, ev->id); if (sev == NULL) return;
/* Increase event sequence number on fh. */ //消息序列号自动+1. fh->sequence++;
/* Do we have any free events? */ //显然当in_use和elems相等时,此时消息数组已经满员了,此时需要将最旧的 //没有处理的消息移除订阅消息队列。 if (sev->in_use == sev->elems) { /* no, remove the oldest one */ //队列中已经慢了,将第一个消息移除队列,腾出空间。 kev = sev->events + sev_pos(sev, 0); list_del(&kev->list); sev->in_use--; //如队列消息数目-1. sev->first = sev_pos(sev, 1); //将可用指针指向第二个消息实体 fh->navailable--;//消息队列消息数量-1. if (sev->elems == 1) { //如果子消息数量只有1个,则替换该消息,注意由于都是同类消息,只需要改变状态即可。 if (sev->replace) { sev->replace(&kev->event, ev); copy_payload = false; } } elseif (sev->merge) { struct v4l2_kevent *second_oldest = sev->events + sev_pos(sev, 0); sev->merge(&kev->event, &second_oldest->event); } }
/* Take one and fill it. */ //这了取出in_use所对应的消息实体,来存放具体的消息内容。 kev = sev->events + sev_pos(sev, sev->in_use); kev->event.type = ev->type; if (copy_payload)//拷贝数据内容 kev->event.u = ev->u; kev->event.id = ev->id; kev->event.timestamp = *ts; kev->event.sequence = fh->sequence; sev->in_use++;//可用消息索引往后移动,相当于记录指针。 //上面已经把消息添加到订阅消息子消息列表中了,种类把消息再次插入具体设备的消息队列中。 list_add_tail(&kev->list, &fh->available);
/** * struct media_device - Media device * @dev: Parent device * @devnode: Media device node * @driver_name: Optional device driver name. If not set, calls to * %MEDIA_IOC_DEVICE_INFO will return ``dev->driver->name``. * This is needed for USB drivers for example, as otherwise * they'll all appear as if the driver name was "usb". * @model: Device model name * @serial: Device serial number (optional) * @bus_info: Unique and stable device location identifier * @hw_revision: Hardware device revision * @topology_version: Monotonic counter for storing the version of the graph * topology. Should be incremented each time the topology changes. * @id: Unique ID used on the last registered graph object * @entity_internal_idx: Unique internal entity ID used by the graph traversal * algorithms * @entity_internal_idx_max: Allocated internal entity indices * @entities: List of registered entities * @interfaces: List of registered interfaces * @pads: List of registered pads * @links: List of registered links * @entity_notify: List of registered entity_notify callbacks * @graph_mutex: Protects access to struct media_device data * @pm_count_walk: Graph walk for power state walk. Access serialised using * graph_mutex. * * @source_priv: Driver Private data for enable/disable source handlers * @enable_source: Enable Source Handler function pointer * @disable_source: Disable Source Handler function pointer * * @ops: Operation handler callbacks * * This structure represents an abstract high-level media device. It allows easy * access to entities and provides basic media device-level support. The * structure can be allocated directly or embedded in a larger structure. * * The parent @dev is a physical device. It must be set before registering the * media device. * * @model is a descriptive model name exported through sysfs. It doesn't have to * be unique. * * @enable_source is a handler to find source entity for the * sink entity and activate the link between them if source * entity is free. Drivers should call this handler before * accessing the source. * * @disable_source is a handler to find source entity for the * sink entity and deactivate the link between them. Drivers * should call this handler to release the source. * * Use-case: find tuner entity connected to the decoder * entity and check if it is available, and activate the * the link between them from @enable_source and deactivate * from @disable_source. * * .. note:: * * Bridge driver is expected to implement and set the * handler when &media_device is registered or when * bridge driver finds the media_device during probe. * Bridge driver sets source_priv with information * necessary to run @enable_source and @disable_source handlers. * Callers should hold graph_mutex to access and call @enable_source * and @disable_source handlers. */ structmedia_device { /* dev->driver_data points to this struct. */ structdevice *dev; structmedia_devnode *devnode;