一,前言
之前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 '[email protected]' 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命令呀~
原来传入的参数是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);
标签:node,usbhost,run,Apple,cmd,start,ti,uboot,usb
From: https://blog.51cto.com/AppleCai/7994775