33 --> OpenWRT-19.07 的设备树增加gpio-export,name 时内核bug的修复

一、bug现象描述

  • 内核版本: Linux version 4.14.200
  • OpenWRT-19.07
  • 硬件平台:mtk7621a

在设备树中增加 hua_hand 的io设备,设备树内容如下:

		gpio-export {
                compatible = "gpio-export";
                #size-cells = <0>;

                 modem-reset {
                        gpio-export,name = "modem-reset";
                        gpio-export,output = <1>;
                        gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
                };
//增加内容如下
                hua-hand {
                        gpio-export,name = "hua-hand";
                        gpio-export,direction_may_change = <1>; //采用 direction_may_change 方式,sys/class/gpio/下可用看到io的direction。
                        gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>;
                };
        };

二、源码中增加调试信息,日志内容

内核日志如下:

2021-07-23T15:20:58.496 - [    8.036393] io scheduler noop registered
2021-07-23T15:20:58.512 - [    8.044040] io scheduler deadline registered (default)
2021-07-23T15:20:58.527 - [    8.054642] of_phandle_iterator_init, line:1166 , list_name:assigned-clock-parents,list:0. 
// gpio 总数为 2 个
2021-07-23T15:20:58.543 - [    8.071142] of_property_read_string, line:405, dev-name:modem-reset .
2021-07-23T15:20:58.559 - [    8.083902] of_gpio_export_probe, line:553, name:modem-reset, max gpio num:2
// 注册第一个 gpio 设备名 modem-reset
2021-07-23T15:20:58.574 - [    8.097918] __of_parse_phandle_with_args, line:1281, np.name:modem-reset, np.type:<NULL>, list_name:gpios, cells_names:#gpio-cells 
2021-07-23T15:20:58.590 - [    8.121402] of_phandle_iterator_init, line:1166 , list_name:gpios,list:-2128588704. 
2021-07-23T15:20:58.606 - [    8.136778] of_phandle_iterator_next, line:1191 , phandle_end:-2128588704,list_end:-2128588692 
2021-07-23T15:20:58.621 - [    8.154064] __of_parse_phandle_with_args, line:1292 , cur_index:0,index:0 
2021-07-23T15:20:58.637 - [    8.167706] of_get_named_gpiod_flags, line:83 , ret:0 
2021-07-23T15:20:58.637 - [    8.177908] of_get_named_gpiod_flags, line:92 , lable:1e000600.gpio 
2021-07-23T15:20:58.652 - [    8.190512] of_get_named_gpiod_flags, line:100 , desc:-1881307904 
2021-07-23T15:20:58.668 - [    8.202785] of_get_named_gpiod_flags, line:108 , desc:-1881307904 
2021-07-23T15:20:58.684 - [    8.215049] of_get_named_gpio_flags, line:123 , desc:-1881307904 
//注册第二个 gpio 设备名还是 modem-reset, 而不是 hua_hand 设备名,相同设备名称注册失败。
2021-07-23T15:20:58.699 - [    8.227308] __of_parse_phandle_with_args, line:1281, np.name:modem-reset, np.type:<NULL>, list_name:gpios, cells_names:#gpio-cells 
2021-07-23T15:20:58.715 - [    8.250704] of_phandle_iterator_init, line:1166 , list_name:gpios,list:-2128588704. 
2021-07-23T15:20:58.730 - [    8.266074] of_phandle_iterator_next, line:1191 , phandle_end:-2128588704,list_end:-2128588692 
2021-07-23T15:20:58.746 - [    8.283356] __of_parse_phandle_with_args, line:1292 , cur_index:0,index:1 
2021-07-23T15:20:58.762 - [    8.297000] __of_parse_phandle_with_args, line:1319 , cur_index:1 
2021-07-23T15:20:58.777 - [    8.309276] of_phandle_iterator_next, line:1191 , phandle_end:-2128588692,list_end:-2128588692 
2021-07-23T15:20:58.793 - [    8.326549] of_get_named_gpiod_flags, line:83 , ret:-2   //错误码 -2
2021-07-23T15:20:58.793 - [    8.336918] of_get_named_gpio_flags, line:123 , desc:-2 
2021-07-23T15:20:58.809 - [    8.347455] of_gpio_export_probe,line:562, name:modem-reset,gpio number:-2 fail 
2021-07-23T15:20:58.824 - [    8.362279] gpio-export: probe of gpio-export failed with error -2

由此可见在 gpio_export_probe() 函数中,发生注册设备错误。问题的原因是设备重名造成的。

三、源码走读

of_gpio_export_probe 注册程序

static int of_gpio_export_probe(struct platform_device *pdev)
{

	for_each_child_of_node(np, cnp) {  // 遍历设备链表,读取树节点信息
		
		of_property_read_string(cnp, "gpio-export,name", &name);  //检查设备树是否正确
		
		if (!name)
			max_gpio = of_gpio_count(cnp);    //获取gpio数量,从日志信息中可以看到此处设备总数为 2 个gpio

		for (i = 0; i < max_gpio; i++) {  //循环注册 GPIO 设备
			unsigned flags = 0;
			enum of_gpio_flags of_flags;

			gpio = of_get_gpio_flags(cnp, i, &of_flags);
			if (!gpio_is_valid(gpio))
				return gpio;

			if (of_flags == OF_GPIO_ACTIVE_LOW)
				flags |= GPIOF_ACTIVE_LOW;

			if (!of_property_read_u32(cnp, "gpio-export,output", &val))
				flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
			else
				flags |= GPIOF_IN;

			if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
				continue;

			dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
			gpio_export_with_name(gpio, dmc, name);
			nb++;
		}
	}
}

省略部分源码内容,通过日志可以看出,程序在注册时hua_hand时,设备名称为 modem-reset,设备名称重复导致注册失败。为什么会出现这个问题呢?

for_each_child_of_node(np, cnp) 源码走读

获取设备信息

/* 此函数负责gpio 树信息扫描, 和内存中IO信息的初始化 */
for_each_child_of_node(np, cnp)
// 此函数采用宏的方式定义,如下:
#define for_each_child_of_node(parent, child) \
	for (child = of_get_next_child(parent, NULL); child != NULL; \
	     child = of_get_next_child(parent, child))

// 函数调用关系如下
of_get_next_child(parent, child));
    __of_get_next_child(node, prev);
//最终调用的函数如下
static struct device_node *__of_get_next_child(const struct device_node *node,
						struct device_node *prev)
{
	struct device_node *next;
	
	if (!node)
		return NULL;

	next = prev ? prev->sibling : node->child;
	for (; next; next = next->sibling)
		if (of_node_get(next))
			break;
	of_node_put(prev);        // 获取信息送入next 节点
	return next;
}

此函数在遍历设备node节点链表,由此看出 of_gpio_export_probe() 函数,设备的信息是在设备节点遍历时更新,而设备总数为2连续注册,gpio设备节点信息并没有更新。因此设备注册会失败。

四、解决bug方法

源码修改如下

static int of_gpio_export_probe(struct platform_device *pdev)
{
        struct device_node *np = pdev->dev.of_node;
        struct device_node *cnp;
        u32 val;
        int nb = 0;

        for_each_child_of_node(np, cnp) {
                const char *name = NULL;
                int gpio;
                bool dmc;
                int max_gpio = 1;
                int i;

                of_property_read_string(cnp, "gpio-export,name", &name);

                if (!name)
                        max_gpio = of_gpio_count(cnp);
                else  //增加的码内容,起点
                        max_gpio = 1;
                printk(KERN_ERR "%s, line:%d, name:%s, max gpio num:%d\n", __func__, __LINE__, name, max_gpio);
					 //增加的码内容,结束
                for (i = 0; i < max_gpio; i++) {
                        unsigned flags = 0;
                        enum of_gpio_flags of_flags;

                        gpio = of_get_gpio_flags(cnp, i, &of_flags);
                        if (!gpio_is_valid(gpio)){
                                return gpio;
                        }

                        if (of_flags == OF_GPIO_ACTIVE_LOW)
                                flags |= GPIOF_ACTIVE_LOW;

                        if (!of_property_read_u32(cnp, "gpio-export,output", &val))
                                flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
                        else
                                flags |= GPIOF_IN;

                        if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
                                continue;

                        dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
                        gpio_export_with_name(gpio, dmc, name);
                        nb++;
                }
        }

        dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);

        return 0;
}

五、验证结果

注册成功日志内容

2021-07-23T16:03:15.124 - [    8.046305] io scheduler noop registered
2021-07-23T16:03:15.124 - [    8.053953] io scheduler deadline registered (default)
2021-07-23T16:03:15.155 - [    8.064460] of_phandle_iterator_init, line:1166 , list_name:assigned-clock-parents,list:0. 
//注册 modem-reset io设备
2021-07-23T16:03:15.155 - [    8.081056] of_property_read_string, line:405, dev-name:modem-reset .
2021-07-23T16:03:15.171 - [    8.093724] of_gpio_export_probe, line:553, name:modem-reset, max gpio num:1
2021-07-23T16:03:15.202 - [    8.107721] __of_parse_phandle_with_args, line:1281, np.name:modem-reset, np.type:<NULL>, list_name:gpios, cells_names:#gpio-cells 
2021-07-23T16:03:15.218 - [    8.131224] of_phandle_iterator_init, line:1166 , list_name:gpios,list:-2128588704. 
2021-07-23T16:03:15.233 - [    8.146600] of_phandle_iterator_next, line:1191 , phandle_end:-2128588704,list_end:-2128588692 
2021-07-23T16:03:15.249 - [    8.163886] __of_parse_phandle_with_args, line:1292 , cur_index:0,index:0 
2021-07-23T16:03:15.249 - [    8.177532] of_get_named_gpiod_flags, line:83 , ret:0 
//注册 hua_hand io设备
2021-07-23T16:03:15.312 - [    8.237129] of_property_read_string, line:405, dev-name:hua-hand .
2021-07-23T16:03:15.327 - [    8.249290] of_gpio_export_probe, line:553, name:hua-hand, max gpio num:1
2021-07-23T16:03:15.358 - [    8.262756] __of_parse_phandle_with_args, line:1281, np.name:hua-hand, np.type:<NULL>, list_name:gpios, cells_names:#gpio-cells 
2021-07-23T16:03:15.374 - [    8.285748] of_phandle_iterator_init, line:1166 , list_name:gpios,list:-2128588620. 
2021-07-23T16:03:15.390 - [    8.301121] of_phandle_iterator_next, line:1191 , phandle_end:-2128588620,list_end:-2128588608 
2021-07-23T16:03:15.405 - [    8.318408] __of_parse_phandle_with_args, line:1292 , cur_index:0,index:0 
2021-07-23T16:03:15.405 - [    8.332051] of_get_named_gpiod_flags, line:83 , ret:0 
// gpio 注册设备总数 2 
2021-07-23T16:03:15.468 - [    8.391613] gpio-export gpio-export: 2 gpio(s) exported

在目标机上采用如下命令查看注册的设备内容。

$ ls /sys/class/gpio/
//可见设备名称

此部分还需要采用内核补丁的方式,制作补丁文件。请参考《OpenWRT 的 linux内核patch方法》

等忙过这段时间,到内核社区看看这个版本bug修复情况,如果有机会也可以为内核贡献点力量。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值