我是靠谱客的博主 凶狠果汁,最近开发中收集的这篇文章主要介绍v4l2_async_notifier_parse_fwnode_endpoints_by_port 分析Linux v4l2架构学习总链接,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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架构学习总链接所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部