meida 子系统位linux 内核的一个子系统. 可以使用media-ctrl -p 查看meida 下的拓扑结构.
在多媒体的框架中,总是复杂多样的,为了解决多媒体设备的复杂性和数据流动性,创建了 media 子系统。Media 使用一个树状结构,将多媒体数据通路的各个设备连接在一起,方便各个设备的管理和控制。
media-ctl工具的操作是通过/dev/medio0等media 设备,它管理的是Media的拓扑结构中各个节点的format、大小、 链接。
v4l2-ctl工具则是针对/dev/video0,/dev/video1等 video设备,它在video设备上进行set_fmt、reqbuf、qbuf、dqbuf、stream_on、stream_off 等一系列操作。
在开机的时候,将会在 [ media-devnode.c ] 中,通过 media_devnode_init() 函数为 media 设备分配一个主设备号,次设备号从 0-255,同时注册一个名字 “media” 的 bus 总线。这个时候,只是申请分配了一个名为 media 的设备号而已,具体的 media 设备注册,将在相应的设备驱动中进行注册登记。
下面以 camera 为例,介绍 media 框架流程。在了解 media 框架前,先了解 camera 设备的注册流程,在注册 /dev/videoX 节点时,主要操作流程如下:
1、通过 platform probe,初始化各个 v4l2_subdevice;
2、先通过 v4l2_device_register() 函数,注册 v4l2_device;
3、先后通过调用 media_device_init()、media_device_register() 函数,注册 /dev/mediaX 设备,这个时候,将 v4l2_dev->mdev = media_dev;
4、注册各 v4l2_subdevice,同时,在注册各个 subdevice 时,subdevice 将会作为一个 entity,登记到media_dev;
5、通过 video_register_device() 函数注册 /dev/videoX 节点,到目前为止,各个 v4l2_subdevice 已经注册完成,接下来将是根据各个子设备的需求,进行相应的连接,设置数据通路;
实际上 /dev/mediaX 设备是一个字符设备,只不过是通过 media 框架的一层封装,只是注册设备的主设备号与次设备号都是从之前预分配的 media 设备获取。
怎么理解 media deivce 与 entity,可以简单的理解,media device 是一个部门的主管,而 entity 是各个下属,media device 将负责统筹 entity,管理各个数据通路的流向。所以,下面再来看看,entity 又是怎样注册的。
我们是以 camera 为例了解 media 框架的,在 /dev/videoX 节点设备中,通过 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 框架的目标之一是发现设备内部拓扑,实时配置。
1.entity:硬件设备模块抽象(类比电路板上面的各个元器件、芯片)
2.pad:硬件设备端口抽象(类比元器件、芯片上面的管脚)
3.link:硬件设备的连线抽象,link的两端是pad(类比元器件管脚之间的连线)
#------------# #------------#
| __|__ __|__ |
| | | | link | | | |
| | pad |<-------->| pad | |
| |__|__| |__|__| |
| | | |
| entity | | entity |
#------------# #------------#
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 设备。具体函数如下:
meida 下ioctrl 是经过封装, 直接搜索不到.
LOG_IOCTL(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info, "dev_info"); //为例
media_device_ioctl() //kernel/msm-4.19/drivers/media/media-device.c
media_device_get_info()
code :kernel/msm-4.19/drivers/media/media-device.c
#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \
[_IOC_NR(MEDIA_IOC_##__cmd)] = { \
.cmd = MEDIA_IOC_##__cmd, \
.fn = (long (*)(struct media_device *, void *))func, \
.flags = fl, \
.arg_from_user = from_user, \
.arg_to_user = to_user, \
}
#define MEDIA_IOC(__cmd, func, fl) \
MEDIA_IOC_ARG(__cmd, func, fl, copy_arg_from_user, copy_arg_to_user)
struct media_ioctl_info {
unsigned int cmd;
unsigned short flags;
long (*fn)(struct media_device *dev, void *arg);
long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd);
long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd);
};
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),
};
从上面信息可以看出, 支持的ioctrl cmd 指令:
MEDIA_IOC_DEVICE_INFO
MEDIA_IOC_ENUM_ENTITIES
MEDIA_IOC_ENUM_LINKS
MEDIA_IOC_SETUP_LINK
MEDIA_IOC_G_TOPOLOGY
通过这些 ioctl,可以获取操作相应 entity 的句柄,从而控制相应的模块操作。
同时,在应用层打开各个 entity,通过 VIDIOC_SUBSCRIBE_EVENT 设置事件的监听,当在内核中产生相应的事件时,通过 v4l2_event_queue() 函数,将事件传递到应用层。
参考:https://blog.csdn.net/u014674293/article/details/111318314