概述
Linux v4l2架构学习总链接
这个函数比较大,这里一部分一部分分析
v4l2_async_notifier_parse_fwnode_endpoints_by_port
-> __v4l2_async_notifier_parse_fwnode_endpoints
has_port = TRUE
port = 0
static int __v4l2_async_notifier_parse_fwnode_endpoints(
struct device *dev, struct v4l2_async_notifier *notifier,
size_t asd_struct_size, unsigned int port, bool has_port,
int (*parse_endpoint)(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd))
{
struct fwnode_handle *fwnode;
unsigned int max_subdevs = notifier->max_subdevs;
int ret;
if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
return -EINVAL;
/* 首先fwnode_graph_get_next_endpoint 要分析一下 */
for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
dev_fwnode(dev), fwnode)); ) {
...
}
fwnode_graph_get_next_endpoint(dev_fwnode(dev), fwnode)
其中dev_fwnode 就是dev->of_node->fwnode,
of_node和fwnode区别,看这里Linux fwnode和device_node的区别_不想好好取名字的博客-CSDN博客_fwnode
#define fwnode_has_op(fwnode, op)
((fwnode) && (fwnode)->ops && (fwnode)->ops->op)
#define fwnode_call_ptr_op(fwnode, op, ...)
(fwnode_has_op(fwnode, op) ?
(fwnode)->ops->op(fwnode, ## __VA_ARGS__) : NULL)
struct fwnode_handle *
fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
return fwnode_call_ptr_op(fwnode, graph_get_next_endpoint, prev);
}
最终就是调用 fwnode->ops->graph_get_next_endpoint
fwnode_operation的回调函数的设置是在内核初始化的时候设置的
static inline void of_node_init(struct device_node *node)
{
kobject_init(&node->kobj, &of_node_ktype);
node->fwnode.ops = &of_fwnode_ops;
}
graph_get_next_endpoint 对应 of_fwnode_graph_get_next_endpoint
static inline bool is_of_node(const struct fwnode_handle *fwnode)
{
return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &of_fwnode_ops;
}
#define to_of_node(__fwnode)
({
typeof(__fwnode) __to_of_node_fwnode = (__fwnode);
is_of_node(__to_of_node_fwnode) ?
container_of(__to_of_node_fwnode,
struct device_node, fwnode) :
NULL;
})
#define of_fwnode_handle(node)
({
typeof(node) __of_fwnode_handle_node = (node);
__of_fwnode_handle_node ?
&__of_fwnode_handle_node->fwnode : NULL;
})
static struct fwnode_handle *
of_fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
return of_fwnode_handle(of_graph_get_next_endpoint(to_of_node(fwnode),
to_of_node(prev)));
}
找到一个具体的dts分析
csi_dphy0: csi-dphy@ff4b0000 {
compatible = "rockchip,rv1126-csi-dphy";
...
status = "okay";
...
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_ucam0: endpoint@1 {
reg = <1>;
/* imx291 */
remote-endpoint = <&ucam_out2>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_csi2_input>;
};
};
};
};
其中of_graph_get_next_endpoint
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *prev)
{
struct device_node *endpoint;
struct device_node *port;
if (!parent)
return NULL;
/*
* Start by locating the port node. If no previous endpoint is specified
* search for the first port node, otherwise get the previous endpoint
* parent port node.
*/
/*
* prev在首次for循环中是NULL
*/
if (!prev) {
struct device_node *node;
/*
* 当前node指向ports节点
* 这里会将node作为当前的parent,用于分析下面的port节点
*/
node = of_get_child_by_name(parent, "ports");
if (node)
parent = node;
/*
* 主意对于port@0节点其名字是port
* 所以port指向port@0节点
*/
port = of_get_child_by_name(parent, "port");
of_node_put(node);
if (!port) {
pr_err("graph: no port node found in %pOFn", parent);
return NULL;
}
} else {
/*
* prev不为NULL
* 说明当前prev是指向port下面的一个endpoint
* 这里获取port,用于下面获取其他endpoint
*/
port = of_get_parent(prev);
if (WARN_ONCE(!port, "%s(): endpoint %pOF has no parent noden",
__func__, prev))
return NULL;
}
while (1) {
/*
* Now that we have a port node, get the next endpoint by
* getting the next child. If the previous endpoint is NULL this
* will return the first child.
*/
/*
* 这里的endpoint对应 endpoint@1节点
* 主意如果prev是NULL
* 这里获取第一个endpoint
* 如果prev不为NULL,是第一个endpoint
* 那么这里将获取第二个endpoint
*/
endpoint = of_get_next_child(port, prev);
/*
* 找到了一个endpoint就返回
* 不要忘记目的,这个复杂的函数就是为了找到一个endpoint
*/
if (endpoint) {
of_node_put(port);
return endpoint;
}
/*
* 当前port下面已经没有enpoint
* 所以要找其parent也就是ports
* 然后找到ports下面的下一个port
*/
/* No more endpoints under this port, try the next one. */
prev = NULL;
do {
port = of_get_next_child(parent, port);
if (!port)
return NULL;
} while (of_node_cmp(port->name, "port"));
}
}
回到 __v4l2_async_notifier_parse_fwnode_endpoint
static int __v4l2_async_notifier_parse_fwnode_endpoints(
struct device *dev, struct v4l2_async_notifier *notifier,
size_t asd_struct_size, unsigned int port, bool has_port,
int (*parse_endpoint)(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd))
{
struct fwnode_handle *fwnode;
unsigned int max_subdevs = notifier->max_subdevs;
int ret;
if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev)))
return -EINVAL;
/* 首先fwnode_graph_get_next_endpoint 要分析一下 */
for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
dev_fwnode(dev), fwnode)); ) {
struct fwnode_handle *dev_fwnode;
bool is_available;
/*
* 这里已经找到了一个endpoint
* fwnode_graph_get_port_parent
* 这个函数获取到parent并不是想象的endpoint的parent即port
* 而是ports的parent
* csi_dphy0: csi-dphy@ff4b0000 {
* status = "okay";
* ports {
* port@0 {
* mipi_in_ucam0: endpoint@1 {
* };
* };
* port@1 {
* csidphy0_out: endpoint@0 {
* };
* };
* };
* };
* 以这个为例
* dev_fwnode指向的是csi-dphy
*/
dev_fwnode = fwnode_graph_get_port_parent(fwnode);
/*
* is_availabel的判断条件是
* status = "okay" 或者 "ok"
*/
is_available = fwnode_device_is_available(dev_fwnode);
fwnode_handle_put(dev_fwnode);
if (!is_available)
continue;
/*
* 对于这里的has_port有点疑惑
* 只有指定了has_port才会去去解析endpoint的端点编号
* 以下面的为例
* ports {
* #address-cells = <1>;
* #size-cells = <0>;
* port@0 {
* reg = <0>;
* #address-cells = <1>;
* #size-cells = <0>;
*
* mipi_in_ucam0: endpoint@1 {
* reg = <1>;
* /* imx291 */
* remote-endpoint = <&ucam_out2>;
* };
* };
* ep.port = 0
* 对应port0下面的reg的值
* ep.id = 1
* 对应endpoint下面的reg的值
*/
if (has_port) {
struct fwnode_endpoint ep;
ret = fwnode_graph_parse_endpoint(fwnode, &ep);
if (ret) {
fwnode_handle_put(fwnode);
return ret;
}
if (ep.port != port)
continue;
}
/*
* 对于假设的条件
* has_port = 1
* port = 0
* 只能匹配一个endpoint,这里的max_subdev只会加1操作
*/
max_subdevs++;
}
/*
* max_subdevs == notifier->max_subdevs
* 说明没有找到endpoint
* 所以直接返回
*/
/* No subdevs to add? Return here. */
if (max_subdevs == notifier->max_subdevs)
return 0;
/*
* 找到了endpoint
* 1. 更新notifier->subdevs
* 重新申请匹配maxsubdevs个数的内存空间,并将数据拷贝过来,然后释放以前的
* 2. 更新notifier->max_subdevs的个数
*/
ret = v4l2_async_notifier_realloc(notifier, max_subdevs);
if (ret)
return ret;
/*
* 下面的代码和上面的很相似,似乎又操作了一遍
* 上面的代码具体的作用就是查找合适的enpoint
* 并更新notifier的信息,但是并没有记录endpoint的信息
* 有没有什么好的方法?
*/
for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint(
dev_fwnode(dev), fwnode)); ) {
struct fwnode_handle *dev_fwnode;
bool is_available;
dev_fwnode = fwnode_graph_get_port_parent(fwnode);
is_available = fwnode_device_is_available(dev_fwnode);
fwnode_handle_put(dev_fwnode);
if (!is_available)
continue;
if (has_port) {
struct fwnode_endpoint ep;
ret = fwnode_graph_parse_endpoint(fwnode, &ep);
if (ret)
break;
if (ep.port != port)
continue;
}
if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) {
ret = -EINVAL;
break;
}
/*
* 这个循环中上面的代码已经分析过了
* 这里直接分析v4l2_async_notifier_fwnode_parse_endpoint
*/
ret = v4l2_async_notifier_fwnode_parse_endpoint(
dev, notifier, fwnode, asd_struct_size, parse_endpoint);
if (ret < 0)
break;
}
fwnode_handle_put(fwnode);
return ret;
}
v4l2_async_notifier_parse_fwnode_endpoints_by_port
-> __v4l2_async_notifier_parse_fwnode_endpoints
-> v4l2_async_notifier_fwnode_parse_endpoint
static int v4l2_async_notifier_fwnode_parse_endpoint(
struct device *dev, struct v4l2_async_notifier *notifier,
struct fwnode_handle *endpoint, unsigned int asd_struct_size,
int (*parse_endpoint)(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd))
{
struct v4l2_async_subdev *asd;
struct v4l2_fwnode_endpoint *vep;
int ret = 0;
/*
* 上面代码分析过
* notifier->subdevs已经申请了空间,这里会为其填充数据
* 数据对应下面的asd
*/
asd = kzalloc(asd_struct_size, GFP_KERNEL);
if (!asd)
return -ENOMEM;
asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
/*
* fwnode_graph_get_remote_port_parent
* 分2步
* 1. 获取endpoint的数据节点
* 2. 根据节点找到对应的parent
* 同样的这个parent并不是port@x 或者ports
* 实例如下
* 解析下面的remote-endpoint
* remote-endpoint = <&ucam_out2>;
* imx291: imx291@1a {
* compatible = "sony,imx291";
* status = "okay";
* reg = <0x1a>;
*
* ...
*
* port {
* ucam_out2: endpoint {
* remote-endpoint = <&mipi_in_ucam0>;
* data-lanes = <1 2 3 4>;
* };
* };
* };
* 这里得到的就是imx291这个节点的fwnode值
*/
asd->match.fwnode =
fwnode_graph_get_remote_port_parent(endpoint);
if (!asd->match.fwnode) {
dev_warn(dev, "bad remote port parentn");
ret = -EINVAL;
goto out_err;
}
/*
* v4l2_fwnode_endpoint_alloc_parse
* 这个函数的主要作用就是解析endpoint端点找到接口类型
* 比如mipi csi , bt656等等
* csi2 :vep->bus_type = V4L2_MBUS_CSI2
* parallel : vep->bus_type = V4L2_MBUS_BT656
*/
vep = v4l2_fwnode_endpoint_alloc_parse(endpoint);
if (IS_ERR(vep)) {
ret = PTR_ERR(vep);
dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)n",
ret);
goto out_err;
}
/*
* 调用传入的回调函数
* 根据实际情况解析
*/
ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0;
if (ret == -ENOTCONN)
dev_dbg(dev, "ignoring port@%u/endpoint@%un", vep->base.port,
vep->base.id);
else if (ret < 0)
dev_warn(dev,
"driver could not parse port@%u/endpoint@%u (%d)n",
vep->base.port, vep->base.id, ret);
v4l2_fwnode_endpoint_free(vep);
if (ret < 0)
goto out_err;
/*
* 将asd保存
* 更新num_subdevs的值
* 注意还有一个max_subdevs
* num_subdevs <= max_subdevs
* 整理一下asd保存了什么
* 1. asd->match_type = V4L2_ASYNC_MATCH_FWNODE
* 2. asd->match.fwnode的值存储remote-endpoint的parent
* 也就是说asd主要是用于解析remote-endpoint
*/
notifier->subdevs[notifier->num_subdevs] = asd;
notifier->num_subdevs++;
return 0;
out_err:
fwnode_handle_put(asd->match.fwnode);
kfree(asd);
return ret == -ENOTCONN ? 0 : ret;
}
最后
以上就是凶狠果汁为你收集整理的v4l2_async_notifier_parse_fwnode_endpoints_by_port 分析Linux v4l2架构学习总链接的全部内容,希望文章能够帮你解决v4l2_async_notifier_parse_fwnode_endpoints_by_port 分析Linux v4l2架构学习总链接所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复