media controller framework

mc-devnode.c

结构体解析

struct media_devnode {
	struct media_device *media_dev;

	/* device ops */
	const struct media_file_operations *fops;

	/* sysfs */
	struct device dev;		/* media device */
	struct cdev cdev;		/* character device */
	struct device *parent;		/* device parent */

	/* device info */
	int minor;
	unsigned long flags;		/* Use bitops to access flags */

	/* callbacks */
	void (*release)(struct media_devnode *devnode);
};

成员描述

fops: 外部register devnode的时候传进来的回调函数,给media_devnode_fops里面的那些fops函数调用。
dev/cdev:在register devnode的时候初始化这两个实例。

函数解析

media_devnode_init
media devnode驱动的入口函数,调用了alloc_chrdev_region和bus_register创建media总线。

media_devnode_unregister
删除cdev节点

media_devnode_register
注册media devnode。这个文件唯一给外部提供的函数。
由调用者持有media device和media devnode,这个函数初始化一个device(实例在devnode里),并将创建的cdev node绑定到这个device上,生成用户态node节点。
node节点fops:

static const struct file_operations media_devnode_fops = {
	.owner = THIS_MODULE,
	.read = media_read,
	.write = media_write,
	.open = media_open,
	.unlocked_ioctl = media_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = media_compat_ioctl,
#endif /* CONFIG_COMPAT */
	.release = media_release,
	.poll = media_poll,
	.llseek = no_llseek,
};

media_open
在media devnode注册之后(调用media_devnode_register成功之后),open才会有效。
会将devnode结构体指针赋值给file->private_data。

media_release
调用media devnode的release函数,并将device的引用计数减1。

media_ioctl
调用media devnode的ioctl回调。

media_poll
调用media devnode的poll回调。

media_read
调用media devnode的read回调。

media_write
调用media devnode的write回调。

mc-entity.c

几种类型

media gobj类型

static inline const char *gobj_type(enum media_gobj_type type)
{
	switch (type) {
	case MEDIA_GRAPH_ENTITY:
		return "entity";
	case MEDIA_GRAPH_PAD:
		return "pad";
	case MEDIA_GRAPH_LINK:
		return "link";
	case MEDIA_GRAPH_INTF_DEVNODE:
		return "intf-devnode";
	default:
		return "unknown";
	}
}

media intf类型

static inline const char *intf_type(struct media_interface *intf)
{
	switch (intf->type) {
	case MEDIA_INTF_T_DVB_FE:
		return "dvb-frontend";
	case MEDIA_INTF_T_DVB_DEMUX:
		return "dvb-demux";
	case MEDIA_INTF_T_DVB_DVR:
		return "dvb-dvr";
	case MEDIA_INTF_T_DVB_CA:
		return  "dvb-ca";
	case MEDIA_INTF_T_DVB_NET:
		return "dvb-net";
	case MEDIA_INTF_T_V4L_VIDEO:
		return "v4l-video";
	case MEDIA_INTF_T_V4L_VBI:
		return "v4l-vbi";
	case MEDIA_INTF_T_V4L_RADIO:
		return "v4l-radio";
	case MEDIA_INTF_T_V4L_SUBDEV:
		return "v4l-subdev";
	case MEDIA_INTF_T_V4L_SWRADIO:
		return "v4l-swradio";
	case MEDIA_INTF_T_V4L_TOUCH:
		return "v4l-touch";
	default:
		return "unknown-intf";
	}
};

结构体解析

struct media_gobj

描述

mdev:指向struct media_device结构体
id:高8位是enum media_gobj_type类型,低8位是ID序号。
list:同类型的media_gobj挂在同一条链表下。

使用场景

如果是MEDIA_GRAPH_ENTITY类型,那么它是挂在struct media_entity下的;
如果是 MEDIA_GRAPH_PAD类型,那么它挂在struct media_pad下;
如果是MEDIA_GRAPH_LINK类型,那么它挂在struct media_link下;
如果是MEDIA_GRAPH_INTF_DEVNODE类型,那么它挂在struct media_intf_devnode下的struct media_interface中。

struct media_gobj {
	struct media_device	*mdev;
	u32			id;
	struct list_head	list;
};

struct media_entity

描述

pad:由外部提供实体,调用函数media_entity_pads_init来初始化,并指定个数num_pads。
internal_idx:entity的序号,在register的时候被指定
function:MEDIA_ENT_F_xxx

struct media_entity {
	struct media_gobj graph_obj;	/* must be first field in struct */
	const char *name;
	enum media_entity_type obj_type;
	u32 function;
	unsigned long flags;

	u16 num_pads;
	u16 num_links;
	u16 num_backlinks;
	int internal_idx;

	struct media_pad *pads;
	struct list_head links;

	const struct media_entity_operations *ops;

	int stream_count;
	int use_count;

	struct media_pipeline *pipe;

	union {
		struct {
			u32 major;
			u32 minor;
		} dev;
	} info;
};

struct media_link

用于连接两个不同的media_gobj / media_gobj / media_interface

成员描述
struct media_link {
	struct media_gobj graph_obj;
	struct list_head list;
	union {
		struct media_gobj *gobj0;
		struct media_gobj *source;
		struct media_interface *intf;
	};
	union {
		struct media_gobj *gobj1;
		struct media_pad *sink;
		struct media_entity *entity;
	};
	struct media_link *reverse;
	unsigned long flags;
	bool is_backlink;
};

struct media_pad

成员描述

flags:MEDIA_PAD_FL_xxx

struct media_pad {
	struct media_gobj graph_obj;	/* must be first field in struct */
	struct media_entity *entity;
	u16 index;
	enum media_pad_signal_type sig_type;
	unsigned long flags;
};

struct media_intf_devnode

成员描述

intf:media interface定义

struct media_interface {
	struct media_gobj		graph_obj;
	struct list_head		links;
	u32				type;
	u32				flags;
};

struct media_intf_devnode {
	struct media_interface		intf;

	/* Should match the fields at media_v2_intf_devnode */
	u32				major;
	u32				minor;
};

函数解析

__media_entity_enum_init
分配并初始化struct media_entity_enum的bmap。

media_entity_enum_cleanup
释放struct media_entity_enum的bmap。

media_gobj_create
将gobj按分类添加到struct media_device的列表中

media_gobj_destroy
删除gobj节点

media_entity_pads_init
初始化struct media_entity中的pad成员。

media_graph_walk_init
单纯的调用__media_entity_enum_init初始化一个graph结构体

media_graph_walk_cleanup
调用media_entity_enum_cleanup清除graph的bmap

media_graph_walk_start
初始化graph,并将入参entity作为第一个节点压栈

media_graph_walk_iter
作为media_graph_walk_next的迭代器,寻找当前link下的另一个entity并压栈进graph

media_graph_walk_next
执行media_graph_walk_iter将link下所有的entity压栈,然后取头上第一个entity

media_entity_get_fwnode_pad
获取fwnode的pad号

__media_pipeline_start
首先遍历pipe->graph里所有的entity,然后遍历entity下所有的link,对所有的link执行函数entity->ops->link_validate(link)

__media_pipeline_stop
对pipe下所有的entity,stream_count成员进行递减操作(__media_pipeline_start会递增)。

media_get_pad_index
获取pad的序号

media_create_pad_link
传入source entity和sink entity,对应的创建两个media link,并调用media_gobj_create初始化两个media link的gobj

media_create_pad_links
分为以下3种情况:

  1. source和sink都有的情况:source和sink直接组成一组link。
  2. source和sink都没有的情况:遍历media device下所有的media entity,选择与source_function一致的entity与sink_function一直的entity组成一组link。
  3. source或者sink有其一的情况:遍历media device下所有的media entity,选择与source_function或者sink_function一致的entity组成一组link,所有匹配的entity都会新建一组link。

media_entity_remove_links
移除entity下所有的link

media_entity_setup_link
setup link,会调用mdev->ops->link_notify()link->ops->link_setup()

media_entity_find_link
根据source pad和sink pad寻找media link。

media_entity_remote_pad
获取此pad的remote pad

media_devnode_create
分配并初始化一个struct media_intf_devnode结构体

media_create_intf_link
创建一个intf link,在创建video device或者subdev等时被调用,创建一个intf_link实体。

media_remove_intf_link
移除一个intf link

mc-request.c

结构体

struct media_request

成员描述
struct media_request {
	struct media_device *mdev;
	struct kref kref;
	char debug_str[TASK_COMM_LEN + 11];
	enum media_request_state state;
	unsigned int updating_count;
	unsigned int access_count;
	struct list_head objects;
	unsigned int num_incomplete_objects;
	wait_queue_head_t poll_wait;
	spinlock_t lock;
};

struct media_request_object

成员描述
struct media_request_object {
	const struct media_request_object_ops *ops;
	void *priv;
	struct media_request *req;
	struct list_head list;
	struct kref kref;
	bool completed;
};

函数解析

media_request_get_by_fd
通过fd号获取media_request结构体指针,从file->private_data中获取。

media_request_alloc
分配一个struct media_request结构体并初始化里面的成员,然后分配一个fd与之匹配起来

media_request_object_find
根据入参寻找对应的media_request_object

media_request_object_bind
media_request_object添加到media_request

media_request_object_complete
唤醒media_request_object对应的等待队列(poll_wait)

request fd对应的ops

static const struct file_operations request_fops = {
	.owner = THIS_MODULE,
	.poll = media_request_poll,
	.unlocked_ioctl = media_request_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = media_request_ioctl,
#endif /* CONFIG_COMPAT */
	.release = media_request_close,
};

media_request_poll
调用poll_wait挂起当前进程

media_request_ioctl

static long media_request_ioctl(struct file *filp, unsigned int cmd,
				unsigned long arg)
{
	struct media_request *req = filp->private_data;

	switch (cmd) {
	case MEDIA_REQUEST_IOC_QUEUE:
		return media_request_ioctl_queue(req);
	case MEDIA_REQUEST_IOC_REINIT:
		return media_request_ioctl_reinit(req);
	default:
		return -ENOIOCTLCMD;
	}
}

media_request_ioctl_queue
先调用mdev->ops->req_validate,然后调用mdev->ops->req_queue

media_request_ioctl_reinit
把request置成初始状态

mc-device.c

结构体解析

struct media_device

成员描述
struct media_device {
	/* dev->driver_data points to this struct. */
	struct device *dev;
	struct media_devnode *devnode;

	char model[32];
	char driver_name[32];
	char serial[40];
	char bus_info[32];
	u32 hw_revision;

	u64 topology_version;

	u32 id;
	struct ida entity_internal_idx;
	int entity_internal_idx_max;

	struct list_head entities;
	struct list_head interfaces;
	struct list_head pads;
	struct list_head links;

	/* notify callback list invoked when a new entity is registered */
	struct list_head entity_notify;

	/* Serializes graph operations. */
	struct mutex graph_mutex;
	struct media_graph pm_count_walk;

	void *source_priv;
	int (*enable_source)(struct media_entity *entity,
			     struct media_pipeline *pipe);
	void (*disable_source)(struct media_entity *entity);

	const struct media_device_ops *ops;

	struct mutex req_queue_mutex;
	atomic_t request_id;
};

struct media_entity_notify

驱动可以通过这个结构体向mdev注册回调,在注册entity的时候该回调被调用。

struct media_entity_notify {
	struct list_head list;
	void *notify_data;
	void (*notify)(struct media_entity *entity, void *notify_data);
};
成员描述

notify_data:调用notify回调时的入参
notify:在注册entity的时候被调用,通常用来创建links来关联entities而不能创建和注册entities。

函数解析

media_device_register_entity

  1. 初始化entity实体
  2. 初始化entity的gobj
  3. 初始化entity下所有pad的gobj
  4. 调用mdev->entity_notify链表下的notify回调函数

media_device_unregister_entity

  1. 删除mdev下所有的link,销毁所有的gobj

media_device_init
初始化mdev,在注册之前要先初始化

media_device_cleanup
清理mdev

__media_device_register
分配一个devnode,并生成用户态节点,并挂接回调函数:media_device_fops

static const struct media_file_operations media_device_fops = {
	.owner = THIS_MODULE,
	.open = media_device_open,
	.ioctl = media_device_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = media_device_compat_ioctl,
#endif /* CONFIG_COMPAT */
	.release = media_device_close,
};

这个fops主要实现了ioctl,ioctl主要有下面几个回调:

static const struct media_ioctl_info ioctl_info[] = {
	MEDIA_IOC(DEVICE_INFO, media_device_get_info, MEDIA_IOC_FL_GRAPH_MUTEX),
	MEDIA_IOC(ENUM_ENTITIES, media_device_enum_entities, MEDIA_IOC_FL_GRAPH_MUTEX),
	MEDIA_IOC(ENUM_LINKS, media_device_enum_links, MEDIA_IOC_FL_GRAPH_MUTEX),
	MEDIA_IOC(SETUP_LINK, media_device_setup_link, MEDIA_IOC_FL_GRAPH_MUTEX),
	MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
	MEDIA_IOC(REQUEST_ALLOC, media_device_request_alloc, 0),
};

media_device_get_info
获取media device信息,包括驱动名字、model名字、串口信息、bus信息、media版本信息等

media_device_enum_entities
根据上层传入的id寻找entity,并将entity信息传回

media_device_enum_links

media_device_setup_link
setup link。需要指定link的sink和source

media_device_get_topology
获取media device下所有的entity、intf link、pad

media_device_request_alloc
分配一个media request

media_device_register_entity_notify
注册一个notify回调

media_device_unregister_entity_notify
删除一个notify回调

media_device_unregister

  1. 删除所有的entity
  2. 删除所有的notify
  3. 删除所有的intf_link
  4. 删除devnode,用户态节点

media_device_pci_init
就是调用了media_device_init(),只不过mdev->dev指向的pci->dev。

__media_device_usb_init
除了将udev->dev赋值给mdev->dev之外,还使用了udev的一些name参数。

v4l2-mc.c

结构体解析

函数解析

v4l2_mc_create_media_graph
给所有的entity创建links

v4l_enable_media_source
执行mdev的enable_source回调,入参是vdev->entity和vdev->pipe。
通常在执行vidioc_s_xxx()之前调用。

v4l_vb2q_enable_media_source
以vb2_queue为入参的enable_media_source

pipeline_pm_use_count

pipeline_pm_power_one
调用subdev的s_power进行电源操作(对象只能是subdev)

v4l2_pipeline_pm_use
对graph下所有的entity进行power操作

v4l2_pipeline_link_notify
对link所在的pipeline上所有entity进行power操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值