在多媒体的框架中,总是复杂多样的,为了解决多媒体设备的复杂性和数据流动性,创建了media子系统。Media使用一个树状结构,将多媒体数据通路的各个设备连接在一起,方便各个设备的管理和控制。
在开机的时候,将会在[media-devnode.c]中,通过media_devnode_init()
函数为media设备分配一个主设备号,次设备号从0~255,同时注册一个名字为“media”的bus总线。这个时候,只是申请分配了一个名为media的设备号而已,具体的media设备注册,将在相应的设备驱动中进行注册登记。
下面以camera为例,介绍media框架流程。在了解media框架前,先了解camera设备的注册流程,在注册 /dev/videoX 节点时,主要操作流程如下:
实际上 /dev/mediaX 设备是一个字符设备,只不过是通过 media 框架的一层封装,只是注册设备的主设备号与此设备号都是从之前预分配的 media 设备获取。
怎么理解 media device 与 entity ,可以简单的理解,media device是一个部门的主管,而 entity 是各个下属,media device 将负责统筹 entity,管理各个数据通路的流向。所以,下面再来看看,entity又是怎样注册的。
我们是以camera为例了解media框架的,在 /dev/videox jieidan 节点设备中,通过struct video_device
结构体管理节点,而在该结构体中,又将会包含 v4l2 device 的结构体struct v4l2_device
,而在 v4l2 device 的结构体中,又将会包含media device的结构体struct media_device
,就是这样,层层包含。
在通过 v4l2_device_register_subdev() 函数注册 v4l2 subdevice 时,将会调用 media_device_register_entity() 函数,将该 v4l2 subdevice 当成一个 entity 注册到 media device,于此同时,还将会有 entity 的 pads 初始化等操作。
media框架的目标之一是发现设备内部拓扑,实时配置。为了实现这个目标,硬件设备被建模为一些图形积木,称为用垫子(pads)连接的实体(entity)。一个entity是一个基本的媒体硬件积木。它可以和大量的逻辑模块–譬如物理硬件设备(CMOS传感器), 逻辑硬件设备(一个SOC图形处理流水线积木),DMA通道或者物理连接器通信。pad是用于entity和其他entity互通的连接端点。entity产生的数据(不仅是video)从该entity的输出流向一个或多个entity的输入。pads不能与芯片的物理pin脚弄混了。一个link是两个pads之间的点对点连接,可以是同一个entity或者不同的entity之间。数据从pad源流向pad终点。
各个entity都已经注册到 media ,那么,接下来看看 entity 间的 link 又是怎样操作的。
media entity link 是通过media_create_pad_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags)
函数完成的。如该函数的声明,需要输入source entity以及source pad、sink entity以及sink pad。各个 entity pad数目是预先已知的,所以在连接的时候,自然知道需要使用哪个pad。
link 时,其实就是分别针对 source、sink 创建一个 media link,都添加到 media device中,并将赋值 link 到 source 与 sink 。
到这里,简单的理解了 media device、entity、pad、link,那么,我们究竟使用这一套框架干嘛呢,继续往下看。
我们知道,在使用camera的过程中,有各个模块需要互相协助,简单的数据流如下:
sensor ---> CPHY ---> csi ---> isp ---> stream
那么,我们在设置camera的图像输出格式时,将会需要确认,整个 pipeline 都是支持该格式的,这个时候,我们就可以通过 media 框架提供的函数 media_entity_graph_walk_init()
、 media_entity_graph_walk_start()
、 media_entity_graph_walk_next()
等函数开始遍历 pipeline 的各个entity,确认每个entity都是支持该格式的,完成一个数据的协商过程。
同时,当平台支持 scaler 缩放时,又可以从同一个源,经过 scaler 之后,输出不同的分辨率大小等,这样的一个entity,多个输出,这样也是很方便通过 media 框架进行数据的管理。
大部分soc厂商,对自己的ISP算法是不开源的,但是linux内核是开源的,这个时候,soc厂商会将自己的ISP算法放在应用层,这个时候,就需要通过应用层操作到内核,而 media 框架管理着数据的整条通路,所以,在这里,通过 media 框架操作ISP等硬件是非常便捷的。
在注册 media 设备时,在 __media_device_register() 函数,赋值 media devnode fops 为 media_device_fops。而 media_device_fops 主要是通过 ioctl 操作 media 设备。支持以下操作:
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),
};
通过这些ioctl,可以获取操作相应 entity 的句柄,进而控制相应的模块操作。
同时,在应用层打开各个 entity,通过 VIDIOC_SUBSCRIBE_EVENT 设置事件的监听,当在内核中产生相应的事件时,通过 v4l2_event_queue() 函数,将事件传递到应用层。