uboot配置usbhost及代码初步分析--Apple的学习笔记
  2Nv1H5BMjysw 2023年11月02日 32 0

一,前言

之前uboot没配置过usb,但是现在uboot基于DM模型基本和linux driver类似了。那么为了学习linux driver,我可以先学习uboot来做技术储备也是一样的。而且usb在uboot上应该也有用武之地,所以有必要进行刻意练习。

二,分析

1,之前对发现driver用了wraper的方式来打包进行绑定,我理解唯一的好处就是代码看起来更加模块化,在bind函数中对子node进行绑定driver。

U_BOOT_DRIVER(ti_musb_wrapper) = {
	.name	= "ti-musb-wrapper",
	.id	= UCLASS_MISC,
	.of_match = ti_musb_ids,
	.bind = ti_musb_wrapper_bind,
};

这样就算打印出来看起来有主模块和子模块,观察起来不至于分散。

misc          0  [   ]   ti-musb-wrapper       |   |-- usb@47400000
 usb           0  [   ]   ti-musb-host          |   |   `-- usb@47401800

但是我理解不用wrap单独处理也是可以的,所以我准备修改下ti-musb.c,改成不绑定而是独立的一个个,dtsi中已经有compatible参数了,我理解只要在c代码中添加一个个独立usb的of_match即可。然后把wrapper的注册删除掉。

2,结果按我思路usb驱动根本识别不了了,那么我猜测一定是父类没有match那么就不会再扫描子类了。 猜测父类没有匹配成功,就不会去扫描子类了。 所以再看看scan_node依次扫描是否有条件的,我先在此c文件中添加#define _DEBUG 1,看了打印的信息,我才知道原来就是昨天分析的。node去匹配所有的driver,找不到就continue,某个node全部的compatible都没有,继续向下if (entry->of_match)当然也不会有值,所以不对调用device_bind_with_driver_data,也就没有继续的归递下层子类了。

id = NULL;
        for (entry = driver; entry != driver + n_ents; entry++) {
            if (drv) {
                if (drv != entry)
                    continue;
                if (!entry->of_match)
                    break;
            }
            ret = driver_check_compatible(entry->of_match, &id, compat);
            if (!ret)
                break;
        }
        if (entry == driver + n_ents)
            continue;
       if (entry->of_match)
       {
           device_bind_with_driver_data
       }

打印的debug_info信息,No match和match的打印信息是不同的哦

bind node usb@47400000
   - attempt to match compatible string 'ti,am33xx-usb'
No match for node 'usb@47400000'
dm_scan_fdt_node node_name is:ethernet@4a100000
bind node ethernet@4a100000
   - attempt to match compatible string 'ti,am335x-cpsw'
   - found match at 'eth_cpsw': 'ti,cpsw' matches 'ti,am335x-cpsw'
Device 'ethernet@4a100000.bootdev' Driver 'eth_bootdev',drv->bind addr is 0x9ffbd9d1
dm_scan_fdt_node node_name is:sram@40300000
bind node sram@40300000
   - attempt to match compatible string 'mmio-sram'
No match for node 'sram@40300000'

3,知道无法匹配的根本原因,那么就好解决了,只要把设备树中的子节点提升为上层节点即可。可以用dm tree看到绑定成功了。

usb           0  [   ]   ti-musb-host          |   `-- usb@47401800

使用了下,也是正常的,同usb start,probe也成功。

AP-Boot=> usb start
starting USB...
Bus usb@47401800: Device 'usb@47401800' Driver 'ti-musb-host',drv->probe addr is 0x9ff9f11f
scanning bus usb@47401800 for devices... Device 'usb_mass_storage' Driver 'usb_mass_storage',drv->probe addr is 0x9ff7ea15
Device 'usb_mass_storage.lun0.bootdev' Driver 'usb_bootdev',drv->bind addr is 0x9ff9c41b
1 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found

4,问题来了,这个probe需要手工运行usb start的,那么我不调用usb start是否这驱动等于没用了,那么还加它干嘛呢? 于是我想着用qemu调试下vexpress_ca9x4_defconfig中的usb看看效果,接着它默认没有配置,我需要配置下,方法就是通过dts中的匹配信息搜索c代码,然后在makefile中找到这c文件需要的宏开关,添加了几个宏定义即可。

5,编译成功后,开始qemu仿真调试v9,在probe打断点,可以被调用,具体是通过环境变量中的usb命令start来probe的。路径如下

1	board_init_r
2	initcall_run_list
3	run_main_loop
4	main_loop
5	run_command_list
6	parse_string_outer
7	parse_stream_outer
8	run_list
9	run_list_real
10	run_pipe_real
11	cmd_process
12	cmd_call
13	do_run
14	parse_string_outer
15	parse_stream_outer
16	run_list
17	run_list_real
18	run_pipe_real
19	run_list_real
20	run_pipe_real
21	parse_string_outer
22	parse_stream_outer
23	run_list
24	run_list_real
25	run_pipe_real
26	cmd_process
27	cmd_call
28	do_run
29	parse_string_outer
30	parse_stream_outer
31	run_list
32	run_list_real
33	run_pipe_real
34	cmd_process
35	cmd_call
36	do_run
37	parse_string_outer
38	parse_stream_outer
39	run_list
40	run_list_real
41	run_pipe_real
42	cmd_process
43	cmd_call
44	do_usb
45	do_usb_start
46	usb_init
47	device_probe
48	isp1760_plat_probe

6,看了下感觉也是有归递的,cmd_call等都是重复的。最后怎么到了do_usb的命令呢!如图看到线索就是s为usb start,usb start好熟悉,不就是我之前手工输入的cmd命令呀~

uboot配置usbhost及代码初步分析--Apple的学习笔记_设备树

原来传入的参数是bootcmd,那么就能解释了。

const char *bootdelay_process(void)
{
	char *s;
	int bootdelay;
	bootcount_inc();
	s = env_get("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
	。。。。。。
	if (bootcount_error())
		s = env_get("altbootcmd");
	else
		s = env_get("bootcmd");
	return s;
}

7,稍微分析下代码流,cmd_process中主要调用find_cmd,接着调用cmd_call来执行命令 通过find_cmd_tbl在cmd表中找到命令,主要就是比较cmdtp->name

struct cmd_tbl *find_cmd(const char *cmd)
{
	struct cmd_tbl *start = ll_entry_start(struct cmd_tbl, cmd);
	const int len = ll_entry_count(struct cmd_tbl, cmd);
	return find_cmd_tbl(cmd, start, len);
}

另外看到归递parse_string_outer开始有重复,看到如下函数,用了归递调用parse_stream_outer。

int parse_string_outer(const char *s, int flag)
{
	struct in_str input;
	int rcode;
#ifdef __U_BOOT__
	char *p = NULL;
	if (!s)
		return 1;
	if (!*s)
		return 0;
	if (!(p = strchr(s, '\n')) || *++p) {
		p = xmalloc(strlen(s) + 2);
		strcpy(p, s);
		strcat(p, "\n");
		setup_string_in_str(&input, p);
		rcode = parse_stream_outer(&input, flag); //此行归递了。
		free(p);
		return rcode == -2 ? last_return_code : rcode;
	} else {
#endif
	setup_string_in_str(&input, s);  
	rcode = parse_stream_outer(&input, flag);
	return rcode == -2 ? last_return_code : rcode;
#ifdef __U_BOOT__
	}
#endif
}

8,但是这个bootcmd中的start usb是哪里来的呢?printenv先看看信息

=> printenv bootcmd
bootcmd=run distro_bootcmd; run bootflash
=> printenv distro_bootcmd
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
=> printenv boot_targets
boot_targets=mmc1 mmc0 pxe dhcp

我在do_run解析了arg参数后,添加了参数打印,断点依然在probe函数中,接着可以看到如下,参数包括boot_net_usb_start。

Net:   eth0: ethernet@3,02000000
Hit any key to stop autoboot:  0 
do_run:runcmd_arg is distro_bootcmd
do_run:runcmd_arg is bootcmd_mmc1
do_run:runcmd_arg is mmc_boot
MMC Device 1 not found
no mmc device at slot 1
do_run:runcmd_arg is bootcmd_mmc0
do_run:runcmd_arg is mmc_boot
Card did not respond to voltage select! : -110
do_run:runcmd_arg is bootcmd_pxe
do_run:runcmd_arg is boot_net_usb_start
starting USB...
Bus usb@3,03000000: 
......

在相关头3个头文件中找vexpress_ca9x4.h->vexpress_common.h->config_distro_bootcmd.h,在此h文件中能搜索到start usb。 BOOTENV_SHARED_USB什么时候调用的呢?

于是通过BOOTENV找到关键路径,env_default.h中有CFG_EXTRA_ENV_SETTINGS,这是开头,接着一路宏定义展开,就可以找到usb start了。 CFG_EXTRA_ENV_SETTINGS->BOOTENV->BOOTENV_SHARED_USB。

如下"run boot_net_usb_start就是run usb start。

#define BOOTENV_RUN_NET_USB_START "run boot_net_usb_start; "
#define BOOTENV_SHARED_USB \
	"boot_net_usb_start=usb start\0" \
	"usb_boot=" \
		"usb start; " \
		BOOTENV_SHARED_BLKDEV_BODY(usb)

我若不希望去调用usb probe函数从源头上只要BOOTENV_SHARED_USB设置为空,或者BOOTENV中不加入BOOTENV_SHARED_USB即可。其它相关的环境变量含义可以看doc文档doc/develop/distro.rst。

三,小结

没想到uboot中的设备树居然和linux driver中如此相似,那么通过uboot小巧的代码来学习设备树是一个不错的选择。

今天主要学习了关于wrap子类的用法,随意看了其它的usb驱动的c文件很多都用了wrapper的方式来写,wrapper的bind中,就直接把子类device和driver绑定了,主要调用device_bind_driver_to_node函数,例如如下就是判断设备树中的dr_mode模式属性,然后将此dev绑定到name为ti-musb-host的驱动。

case USB_DR_MODE_HOST:
            /* Bind MUSB host */
            ret = device_bind_driver_to_node(parent,"ti-musb-host",
            name,node,&dev);
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
2Nv1H5BMjysw