rk1808 调试-mipi_dsi_probe

0、背景

在调试LCD驱动时,查看内核打印信息发现 “failed to get mipi dphy”

定位到是 dw_mipi_dsi_probe 函数中出了问题;

进一步添加打印信息可以定位到问题出现在在函数devm_phy_optional_get中:

 

rk1808 调试-mipi_dsi_probe_第1张图片

 

该函数返回了个错误值,在IS_ERR中报错;

1、devm_phy_optional_get

rk1808 调试-mipi_dsi_probe_第2张图片

 

该函数的功能是查找并返回一个phy;

dev: 请求该phy的设备;

string: phy的名称

该函数调用devm_phy_get完成上述功能。

2、devm_phy_get

rk1808 调试-mipi_dsi_probe_第3张图片

 

该函数调用phy_get完成主要功能;

3、phy_get

rk1808 调试-mipi_dsi_probe_第4张图片

 

实际上获取phy的函数为_of_phy_get

通过添加打印信息确定of_property_match_string 返回值正确

4、_of_phy_get

 

rk1808 调试-mipi_dsi_probe_第5张图片

添加打印信息定位到问题所在:of_phy_provider_lookup,该函数的返回值为错误值。

5、of_phy_provider_lookup

 

rk1808 调试-mipi_dsi_probe_第6张图片

该函数功能为查找到phy provider并返回。但是发现该函数似乎没有执行

list_for_each_entry 这个循环结构直接返回了错误值。定位问题出现在该循环结构中;

6、list_for_each_entry

 

rk1808 调试-mipi_dsi_probe_第7张图片

定位该函数,发现其是一个for循环;其功能是对链表进行遍历。对该函数的传参进行分析:

pos:是一个指向宿主结构的指针

head:是需要进行遍历的链表的头

member:是list_head 成员在宿主结构中的名字(通常为list)

 

下面分析它是如何实现对链表的循环:

其中有一个中的函数:list_entry

 

其实现是由container_of完成;

rk1808 调试-mipi_dsi_probe_第8张图片

 

分析该函数前先分析offstof函数:

 

功能:获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量

一层一层分析:

1、((TYPE *)0) 将零转换为TYPE类型指针

2、((TYPE *)0)->MEMBER 访问TYPE结构体中的名为MEMBER的成员

3、&((TYPE *)0)->MEMBER 取出MEMBER成员的地址,TYPE的地址是0,所以这里获得的地址就是MEMBER在TYPE中的偏移地址。

4、((size_t) &((TYPE *)0)->MEMBER) 类型转换,64为系统size_t 为unsigned long类型。

示例:

 

struct studen{

     char gender;

     int id;

     int age;

     char name[20];

}

int gender_offset = offsetof( struct studen,gender);

int id_offset = offsetof( struct studen,id);

int age_offset = offsetof( struct studen,age);

int name_offset = offsetof( struct studen,name);

 

综上,当我们知道一个结构体和该结构体中的一个成员后,可以通过offsetof 计算该成员在结构体中的偏移量。

 

回到container_of 的分析中:

rk1808 调试-mipi_dsi_probe_第9张图片

 

功能:根据结构体(type)中的成员(member)的指针(ptr)来获取指向整个结构体的指针。

1、typeof( ((type *)0)->member ) 获得结构体(type)中成员(member)的类型

2、const typeof( ((type *)0)->member ) *__mptr = (ptr) 定义了一个_mptr指针,并将ptr赋值给了_mptr,这样_mptr就一个member数据类型的指针,其指向ptr所指向的地址。

3、(char *)__mptr 将__mptr 转换成char型指针。

4、( (char *)__mptr - offsetof(type,member) ) 获取结构体(type)的指针的起始地址

5、(type *)( (char *)__mptr - offsetof(type,member) ) 转换为type * 型的指针。

 

综上,当我们知道一个结构体type,以及其中的一个成员member和该成员的地址ptr,我们就能推算出该结构体的地址。

 

回到list_for_each_entry 的分析中:

 

rk1808 调试-mipi_dsi_probe_第10张图片

这个函数的功能是遍历链表,什么是链表?

 

struct studen{

     char gender;

     int id;

      int age;

      struct list_head list;

       char name[20];

}

 

rk1808 调试-mipi_dsi_probe_第11张图片

在结构体中添加一个双向链表的节点list,当很多个list 首尾相连的时候,我们就可以遍历这个链表,然后根据节点的指针获取节点所在结构体的指针,从而获取数据。

 

怎么使用链表?

使用链表首先得有个链表头:

rk1808 调试-mipi_dsi_probe_第12张图片

 

LIST_HEAD 的作用就是定义表头:新建一个双向链表的表头。

有了表头后,就可以将新的节点加入这个链表,通过list_add 实现:

 

rk1808 调试-mipi_dsi_probe_第13张图片

再回到list_for_each_entry 的分析中:

rk1808 调试-mipi_dsi_probe_第14张图片

 

1、pos = list_entry((head)->next, typeof(*pos), member)

链表头

2、&pos->member != (head);

循环终止条件

3、pos = list_entry(pos->member.next, typeof(*pos), member)

链表中的下一个节点

 

回到我们debug中,我们需要完成一个双向链表的遍历,首先需要建立一个双向链表:

 

可知我们需要遍历的双向链表表头为:phy_provider_list;

通过搜索得知:

 

在代码中我们确实定义了这个双向链表的表头。

 

 

这个情况下,我们没有遍历链表的原因只有一个了:

我们只定义了表头(指向自身)并没有使用 list_add 向这个链表中添加后续的节点,由此导致当我们调用list_for_each_entry 时没有遍历链表。

 

综上:我希望能通过遍历链表得到想要的phy,但是代码中没有建立相应的双向链表导致遍历失败,返回了错误值。

终于搞完了这个分享,这csdn不行啊,在网易笔记中先写好了,直接复制过来图片全部都没好,还需要一个个图重新截图,太难了我。。。。

你可能感兴趣的:(debug,数据结构,链表)