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种情况:
- source和sink都有的情况:source和sink直接组成一组link。
- source和sink都没有的情况:遍历media device下所有的media entity,选择与source_function一致的entity与sink_function一直的entity组成一组link。
- 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
- 初始化entity实体
- 初始化entity的gobj
- 初始化entity下所有pad的gobj
- 调用
mdev->entity_notify
链表下的notify回调函数
media_device_unregister_entity
- 删除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
- 删除所有的entity
- 删除所有的notify
- 删除所有的intf_link
- 删除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操作