一,前言
之前uboot偶尔第一次上电会ping通一次,之后就ping不通,我以为电源问题,好像也不是,然后我又认为是交叉网线问题,我用的可能是直连网线,之前用的交叉网线头子坏了我就丢了。于是网上买了新的交叉网线,到货后依然ping不通,而且一次都不行了。难道代码有问题,于是用了我之前的uboot代码,结果还是ping不通,难道硬件出问题了,虽然大概率怀疑硬件有问题,但是我想找到实锤,同时通过分析bug的过程来进行快速学习。
二,分析
1,从最早的错误信息开始分析代码
U-Boot 2021.01 (Oct 08 2023 - 06:19:31 -0700)
CPU : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM: 512 MiB
WDT: Started with servicing (60s timeout)
NAND: 0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... *** Warning - bad CRC, using default environment
<ethaddr> not set. Validating first E-fuse MAC
Net: Could not get PHY for ethernet@4a100000: addr 0
eth2: ethernet@4a100000, eth3: usb_ether
Hit any key to stop autoboot: 0
=> setenv ipaddr 192.168.0.111
=> setenv gatewayip 192.168.0.1
=> setenv netmask 255.255.255.0
=> setenv serverip 192.168.0.110
=> saveenv
Saving Environment to FAT... OK
=> ping 192.168.0.110
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
ping failed; host 192.168.0.110 is not alive
2,能看到明显的代码流 net_loop ->ping_start ->eth_get_name ->eth_get_dev ->eth_get_uclass_priv ->uclass_get
关于结构体uclass里包括uclass_driver,uclass_driver里包括uclass_id,uclass_id是枚举,eth找的就是UCLASS_ETH。
int uclass_get(enum uclass_id id, struct uclass **ucp)
{
struct uclass *uc;
*ucp = NULL;
uc = uclass_find(id);
if (!uc)
return uclass_add(id, ucp);
*ucp = uc;
return 0;
}
关键的结构体,英文有描述第二个是driver,第三个是device的链表头,第四个是uclasss本身的链表的下一个node。
/*
* @priv: Private data for this uclass
* @uc_drv: The driver for the uclass itself, not to be confused with a
* 'struct driver'
* @dev_head: List of devices in this uclass (devices are attached to their
* uclass when their bind method is called)
* @sibling_node: Next uclass in the linked list of uclasses
*/
struct uclass {
void *priv;
struct uclass_driver *uc_drv;
struct list_head dev_head;
struct list_head sibling_node;
};
关于eth uclass的定义
UCLASS_DRIVER(eth) = {
.name = "eth",
.id = UCLASS_ETH,
.post_bind = eth_post_bind,
.pre_unbind = eth_pre_unbind,
.post_probe = eth_post_probe,
.pre_remove = eth_pre_remove,
.priv_auto_alloc_size = sizeof(struct eth_uclass_priv),
.per_device_auto_alloc_size = sizeof(struct eth_device_priv),
.flags = DM_UC_FLAG_SEQ_ALIAS,
};
在map文件中搜索".u_boot_list_"就可以找到已经被编译入的uclass。
.u_boot_list_2_cmd_2_fdt
.u_boot_list_2_cmd_2_go
.u_boot_list_2_cmd_2_gpio
.u_boot_list_2_cmd_2_mdio
.u_boot_list_2_cmd_2_mii
.u_boot_list_2_cmd_2_nboot
.u_boot_list_2_cmd_2_nfs
.u_boot_list_2_cmd_2_tftpboot
.u_boot_list_2_driver_2_eth_cpsw
.u_boot_list_2_driver_2_eth_usb
.u_boot_list_2_driver_2_ti_musb_host
.u_boot_list_2_driver_2_ti_musb_peripheral
.u_boot_list_2_driver_2_ti_musb_wrapper
.u_boot_list_2_driver_2_usb_dev_generic_drv
.u_boot_list_2_driver_2_usb_generic_hub
.u_boot_list_2_driver_2_usb_mass_storage
.u_boot_list_2_driver_2_usb_storage_blk
.u_boot_list_2_env_clbk_2_nvlan
.u_boot_list_2_env_clbk_2_serialno
.u_boot_list_2_env_clbk_2_serverip
.u_boot_list_2_env_clbk_2_vlan
.u_boot_list_2_uclass_2_eth
.u_boot_list_2_uclass_2_spi
.u_boot_list_2_uclass_2_usb
.u_boot_list_2_uclass_2_usb_dev_generic
.u_boot_list_2_uclass_2_usb_gadget_generic
.u_boot_list_2_uclass_2_usb_hub
.u_boot_list_2_uclass_2_usb_mass_storage
.u_boot_list_2_uclass_2_wdt
.u_boot_list_2_uclass_3
关于driver和device也都有注册的api,在map文件中搜索方法也都是".u_boot_list_" U_BOOT_DRIVER(eth_cpsw)为driver注册的api。 U_BOOT_DEVICE(am335x_eth)是不带设备树的。带设备树的initr_dm为初始化中dm_init_and_scan就完成了uclass,driver,device的绑定,此函数最后调用device_probe进行设备初始化。
3,看map文件的时候看到了mmi等一系列命令,原来还有mdio命令,之前mmi只能看一部分,不能看全部的,现在用mdio就可以看全部的寄存器值了,功能如下
=> mdio
mdio - MDIO utility commands
Usage:
mdio list - List MDIO buses
mdio read <phydev> [<devad>.]<reg> - read PHY's register at <devad>.<reg>
mdio write <phydev> [<devad>.]<reg> <data> - write PHY's register at <devad>.<reg>
mdio rx <phydev> [<devad>.]<reg> - read PHY's extended register at <devad>.<reg>
mdio wx <phydev> [<devad>.]<reg> <data> - write PHY's extended register at <devad>.<reg>
<phydev> may be:
<busname> <addr>
<addr>
<eth name>
<addr> <devad>, and <reg> may be ranges, e.g. 1-5.4-0x1f.
读取的信息如下
=> mdio read ethernet@4a100000 2 0x11
Reading from bus ethernet@4a100000
PHY at address 2:
17 - 0x2
=> mdio read ethernet@4a100000 2 11
Reading from bus ethernet@4a100000
PHY at address 2:
17 - 0x2
=> mdio read ethernet@4a100000 2 12
Reading from bus ethernet@4a100000
PHY at address 2:
18 - 0x6022
=> mdio read ethernet@4a100000 2 1a
Reading from bus ethernet@4a100000
PHY at address 2:
26 - 0x0
=> mdio read ethernet@4a100000 2 1b
Reading from bus ethernet@4a100000
PHY at address 2:
27 - 0xb
=> mdio read ethernet@4a100000 2 1d
Reading from bus ethernet@4a100000
PHY at address 2:
29 - 0x80
=> mdio read ethernet@4a100000 2 1f
Reading from bus ethernet@4a100000
PHY at address 2:
31 - 0x54
=> mdio read ethernet@4a100000 2 1e
Reading from bus ethernet@4a100000
PHY at address 2:
30 - 0x0
通过reg 31可以知道MAC的模式必须配置为MII,速度是10M. "board\ti\am335x\board.c"中ft_board_setup里面就是获取DTS设备树信息。因为IMAGE_OF_BOARD_SETUP配置为1才能进入。
if (IMAGE_OF_BOARD_SETUP) {
fdt_ret = ft_board_setup(blob, gd->bd);
if (fdt_ret) {
printf("ERROR: board-specific fdt fixup failed: %s\n",
fdt_strerror(fdt_ret));
goto err;
}
}
4,从debug信息来看吧,主要是获取device信息
static int do_ping(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
if (argc < 2)
return CMD_RET_USAGE;
net_ping_ip = string_to_ip(argv[1]);
if (net_ping_ip.s_addr == 0)
return CMD_RET_USAGE;
if (net_loop(PING) < 0) {
printf("ping failed; host %s is not alive\n", argv[1]);
return CMD_RET_FAILURE;
}
printf("host %s is alive\n", argv[1]);
return CMD_RET_SUCCESS;
}
然后进入net_loop函数
int net_loop(enum proto_t protocol)
{
int ret = -EINVAL;
enum net_loop_state prev_net_state = net_state;
#if defined(CONFIG_CMD_PING)
if (protocol != PING)
net_ping_ip.s_addr = 0;
#endif
net_restarted = 0;
net_dev_exists = 0;
net_try_count = 1;
debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");
bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
net_init();
if (eth_is_on_demand_init() || protocol != NETCONS) {
eth_halt();
eth_set_current();
ret = eth_init();
if (ret < 0) {
eth_halt();
return ret;
}
}
里面会调用eth_halt函数
void eth_halt(void)
{
struct udevice *current;
struct eth_device_priv *priv;
current = eth_get_dev();
if (!current || !eth_is_active(current))
return;
eth_get_ops(current)->stop(current);
priv = current->uclass_priv;
if (priv)
priv->state = ETH_STATE_PASSIVE;
}
上面的stop到底是哪个udevice的我也不清楚,但是通过报错的打印信息,可以反搜索到用了如下函数,而这些函数明显是usb虚拟网卡,不是用我连接的网线。 usb_gadget_probe_driver->udc_bind_to_driver->eth_bind 然后才会有如下信息 using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in MAC de:ad:be:ef:00:01
5,ping会调用哪个网络设备是传入的呢?先猜测了下应该是环境变量设置了,找了代码确实有线索。net_loop里面就是eth_set_current拿到eth设备了。 添加了些printf查看信息,发现Trying usb_ether,说明实锤了,设备用的不是bdinfo中的current device,变成了usb_ether。
=> ping 192.168.0.110
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
Trying ethernet@4a100000
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=▒▒▒▒▒@4a10000
Trying usb_ether
using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
musb-hdrc: peripheral reset irq lost!
high speed config #2: 2 mA, Ethernet Gadget, using RNDIS
eth_current_changed start act=usb_ether
eth_current_changed end act=▒▒▒▒r
ping failed; host 192.168.0.110 is not alive
问题就出在本来Trying 的设备是对的,后来Trying的设备变了,为什么会变化设备呢?
=> ping 192.168.0.110
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
eth_get_dev ethernet@4a100000
Trying ethernet@4a100000
FAIL
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=usb_ether
Trying usb_ether
using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
musb-hdrc: peripheral reset irq lost!
FAIL
eth_current_changed start act=usb_ether
eth_current_changed end act=▒▒▒▒r
ping failed; host 192.168.0.110 is not alive
再分析源码,我不能打开debug编译超过sram的大小了,否则还要裁剪麻烦,所以只能先简单加些printf来验证。
old_current = current;
do {
if (current) {
debug("Trying %s\n", current->name);
printf("Trying %s\n", current->name); // by apple
if (device_active(current)) {
ret = eth_get_ops(current)->start(current);
if (ret >= 0) {
。。。。。。
return 0;
}
else
{
printf("device_active failed\n");// by apple
}
}
printf("FAIL\n");// by apple
debug("FAIL\n");
}
/*
* If ethrotate is enabled, this will change "current",
* otherwise we will drop out of this while loop immediately
*/
eth_try_another(0);
/* This will ensure the new "current" attempted to probe */
current = eth_get_dev();
} while (old_current != current);
从如上代码是一个do while循环,ret = eth_get_ops(current)->start(current);返回值一定小于0,所以导致没有在第一次循环的时候直接return 0出去。于是try了下一个设备也就是usb_ether设备。
6, 现在的问题通过这个函数到底访问的是哪个设备,是lan8710吗? 通过搜索driver注册的关键字U_BOOT_DRIVER(eth_cpsw),能找到start函数名字,于是map文件中就可以找到地址,然后uboot会进行地址搬运,所以需要通过bdinfo查看偏移量。
if (device_active(current)) {
ret = eth_get_ops(current)->start(current);
start的函数应该就是cpsw.c的cpsw_eth_start。
static const struct eth_ops cpsw_eth_ops = {
.start = cpsw_eth_start,
.send = cpsw_eth_send,
.recv = cpsw_eth_recv,
.free_pkt = cpsw_eth_free_pkt,
.stop = cpsw_eth_stop,
};
static int cpsw_eth_start(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct cpsw_priv *priv = dev_get_priv(dev);
return _cpsw_init(priv, pdata->enetaddr);
}
我把2个设备的ops addr都打印出来了。
=> ping 192.168.0.111
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=ethernet@4a100000
eth_get_dev ethernet@4a100000
Trying ethernet@4a100000
ops addr -1610870700 // eth1的opt地址信息
FAIL
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=usb_ether
Trying usb_ether
ops addr -1610864732 // eth2的opt地址信息
using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
musb-hdrc: peripheral reset irq lost!
FAIL
eth_current_changed start act=usb_ether
eth_current_changed end act=ethernet@4a100000
ping failed; host 192.168.0.111 is not alive
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
flashstart = 0x00000000
flashsize = 0x00000000
flashoffset = 0x00000000
baudrate = 115200 bps
relocaddr = 0x9ff6d000
reloc off = 0x1f76d000
我忘记用hex来打印了,负数也不要紧计算机自己转下即可,偏移量也能看到reloc off = 0x1f76d000,所以9FFC27A4-1f76d000 =80854054,在map文件中搜索到了函数名称,与预期一致。
.rodata.cpsw_eth_ops
0x0000000080854054 0x20 drivers/net/built-in.o
然后关注点转移到cpsw.c中的函数了,继续通过打印来排查。
eth_get_dev ethernet@4a100000
Trying ethernet@4a100000
ops addr -1610870660
../drivers/net/ti/cpsw.c:_cpsw_init[687]:
../drivers/net/ti/cpsw.c:cpsw_update_link[521]:
../drivers/net/ti/cpsw.c:cpsw_slave_update_link[475]:
FAIL
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=usb_ether
跟踪后,发现返回ret错误的原因是priv->phydev为空。
static int cpsw_slave_update_link(struct cpsw_slave *slave,
struct cpsw_priv *priv, int *link)
{
struct phy_device *phy;
u32 mac_control = 0;
int ret = -ENODEV;
printf("%s:%s[%d]:", __FILE__,__func__, __LINE__); // by apple
phy = priv->phydev;
if (!phy)
goto out;
为什么phydev为空?看看应该在哪里赋值的?找到如下初始化的时候,说明phy_connect没通过。
static int cpsw_phy_init(struct cpsw_priv *priv, struct cpsw_slave *slave)
{
struct phy_device *phydev;
u32 supported = PHY_GBIT_FEATURES;
int ret;
phydev = phy_connect(priv->bus,
slave->data->phy_addr,
priv->dev,
slave->data->phy_if);
if (!phydev)
return -1;
......
}
找到phy_connect没通过的原因,怪不得boot启动有Could not get PHY for ethernet@4a100000: addr 0的提示。
struct phy_device *phy_connect(struct mii_dev *bus, int addr,
struct udevice *dev,
phy_interface_t interface)
{
struct phy_device *phydev = NULL;
uint mask = (addr >= 0) ? (1 << addr) : 0xffffffff;
if (!phydev)
phydev = phy_find_by_mask(bus, mask, interface);
if (phydev)
phy_connect_dev(phydev, dev);
else
printf("Could not get PHY for %s: addr %d\n", bus->name, addr);
return phydev;
}
7,现在想下phy地址设置的是0,是否地址不匹配导致的,然后uboot mii和mdio调试的时候看到的phy addr就是2,所以我在dts中检查下应该就可以解决了。 先通过搜索代码了解phy_addr应该是哪个属性,主要是在of解析函数中搜索关键字找即可。
ret = ofnode_parse_phandle_with_args(subnode, "phy-handle",
NULL, 0, 0, &out_args);
if (!ret) {
slave_data->phy_of_handle = out_args.node;
ret = ofnode_read_s32(slave_data->phy_of_handle, "reg",
&slave_data->phy_addr);
if (ret)
printf("error: phy addr not found in dt\n");
很明显是phy-handler中的reg值代表phy_addr
&cpsw_emac0 {
phy-handle = <ðphy0>;
phy-mode = "mii";
};
&davinci_mdio {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&davinci_mdio_default>;
pinctrl-1 = <&davinci_mdio_sleep>;
status = "okay";
ethphy0: ethernet-phy@0 {
reg = <2>; //原来为<0>
};
};
修改为<2>后果然能够识别到phy,且挂载到eth1上了。但是ping还是报错ethernet@4a100000 Waiting for PHY auto negotiation to complete......... TIMEOUT !
U-Boot SPL 2021.01 (Oct 15 2023 - 14:11:46 +0800)
Trying to boot from MMC1
U-Boot 2021.01 (Oct 15 2023 - 14:11:46 +0800)
CPU : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM: 512 MiB
WDT: Started with servicing (60s timeout)
NAND: 0 MiB
MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... OK
Net: ../drivers/net/ti/cpsw.c:cpsw_eth_probe[1182]
../drivers/net/ti/cpsw.c:cpsw_phy_init[845]
eth2: ethernet@4a100000, eth3: usb_ether
Hit any key to stop autoboot: 0
=> ping 192.168.0.110
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=ethernet@4a100000
eth_get_dev ethernet@4a100000
Trying ethernet@4a100000
ops addr -1610870604
../drivers/net/ti/cpsw.c:_cpsw_init[687]
../drivers/net/ti/cpsw.c:cpsw_update_link[521]
../drivers/net/ti/cpsw.c:cpsw_slave_update_link[475]
../drivers/net/ti/cpsw.c:cpsw_slave_update_link[479]
ethernet@4a100000 Waiting for PHY auto negotiation to complete......... TIMEOUT !
FAIL
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=usb_ether
Trying usb_ether
ops addr -1610864636
using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
musb-hdrc: peripheral reset irq lost!
FAIL
eth_current_changed start act=usb_ether
eth_current_changed end act=ethernet@4a100000
ping failed; host 192.168.0.110 is not alive
=>
然后我关闭了防火墙,依然如此,我发现win10上的网络连接图标是断开的。link down状态,情况好像比之前糟糕。于是我看了下mii值多了自动协商。mdio list看上去多了一个,其实我理解应该是描述具体含义的,因为dev下成功挂载了phy
=> mdio list
ethernet@4a100000:
2 - SMSC LAN8710/LAN8720 <--> ethernet@4a100000
但是现象不同说明reg设置不同,我把lan8710的reg都打印出来,发现自协商开启了,我关闭后就可以link up成功,但是自协商永远不成功。
=> mdio read ethernet@4a100000 2 0x0
Reading from bus ethernet@4a100000
PHY at address 2:
0 - 0x1100
=> mdio read ethernet@4a100000 2 0x1
Reading from bus ethernet@4a100000
PHY at address 2:
1 - 0x7809
=> mdio read ethernet@4a100000 2 0x2
Reading from bus ethernet@4a100000
PHY at address 2:
2 - 0x7
=> mdio read ethernet@4a100000 2 0x3
Reading from bus ethernet@4a100000
PHY at address 2:
3 - 0xc0f1
=> mdio read ethernet@4a100000 2 0x4
Reading from bus ethernet@4a100000
PHY at address 2:
4 - 0x1e1
=> mdio read ethernet@4a100000 2 0x5
Reading from bus ethernet@4a100000
PHY at address 2:
5 - 0x1
=> mdio read ethernet@4a100000 2 0x6
Reading from bus ethernet@4a100000
PHY at address 2:
6 - 0x0
=> mdio read ethernet@4a100000 2 0x11
Reading from bus ethernet@4a100000
PHY at address 2:
17 - 0x2
=> mdio read ethernet@4a100000 2 0x12
Reading from bus ethernet@4a100000
PHY at address 2:
18 - 0x6022
=> mdio read ethernet@4a100000 2 0x1a
Reading from bus ethernet@4a100000
PHY at address 2:
26 - 0x0
=> mdio read ethernet@4a100000 2 0x1b
Reading from bus ethernet@4a100000
PHY at address 2:
27 - 0x1
=> mdio read ethernet@4a100000 2 0x1d
Reading from bus ethernet@4a100000
PHY at address 2:
29 - 0x90
=> mdio read ethernet@4a100000 2 0x1e
Reading from bus ethernet@4a100000
PHY at address 2:
30 - 0x0
=> mdio read ethernet@4a100000 2 0x1f
Reading from bus ethernet@4a100000
PHY at address 2:
31 - 0x40
8,接着应该就是修改phy的reg值,然后看看能否先link up,在协商成功。 我在代码中genphy_update_link函数找下强制设置phydev->autoneg=disable,出现了arp相关的信息,但是ping依然不成功,我就用wireshark抓包都看不到报文,此时再设置回环自己ping自己依然也不成功。
9,我怀疑是否时钟设置有问题,但是看了25M和125M无问题,看来我不得不承认是硬件问题了,但是硬件到底是哪里坏了呢?正准备用示波器去测试,不过脚有点多,比较麻烦。于是我突然想到我之前为了当单片机用,自己焊接过20pin的很小的jtag口,而且一开始能用,之后又不能用了,所以我uboot也无法用调试器来调试。
10,我先猜测是否我焊接的jtag破坏了其它元器件,或者短路了,或者断路了。那么我真心没法修了。就死马当活马医,发现jtag插座有一部分翘起来了,索性我用剪刀把它拔下来了。于是再上电,提示Could not get PHY for ethernet@4a100000: addr 2。mii info显示的phy-addr变成了0。我好像突然看到希望了,也就是说sdk原来就是addr为0的,3年前板子是好用的,而且我一起开始就确认了就是硬件问题,因为3年前的boot代码我找不来也不好用的。于是我再用早上没修改过reg=2编译的bin文件,上电后读取mii的状态,都是正确了,然后也能pin通了。
=> mii dump 0 0
0. (3100) -- PHY control register --
(8000:0000) 0.15 = 0 reset
(4000:0000) 0.14 = 0 loopback
(2040:2000) 0. 6,13 = b01 speed selection = 100 Mbps
(1000:1000) 0.12 = 1 A/N enable
(0800:0000) 0.11 = 0 power-down
(0400:0000) 0.10 = 0 isolate
(0200:0000) 0. 9 = 0 restart A/N
(0100:0100) 0. 8 = 1 duplex = full
(0080:0000) 0. 7 = 0 collision test enable
(003f:0000) 0. 5- 0 = 0 (reserved)
=> mii dump 0 1
1. (782d) -- PHY status register --
(8000:0000) 1.15 = 0 100BASE-T4 able
(4000:4000) 1.14 = 1 100BASE-X full duplex able
(2000:2000) 1.13 = 1 100BASE-X half duplex able
(1000:1000) 1.12 = 1 10 Mbps full duplex able
(0800:0800) 1.11 = 1 10 Mbps half duplex able
(0400:0000) 1.10 = 0 100BASE-T2 full duplex able
(0200:0000) 1. 9 = 0 100BASE-T2 half duplex able
(0100:0000) 1. 8 = 0 extended status
(0080:0000) 1. 7 = 0 (reserved)
(0040:0000) 1. 6 = 0 MF preamble suppression
(0020:0020) 1. 5 = 1 A/N complete
(0010:0000) 1. 4 = 0 remote fault
(0008:0008) 1. 3 = 1 A/N able
(0004:0004) 1. 2 = 1 link status
(0002:0000) 1. 1 = 0 jabber detect
(0001:0001) 1. 0 = 1 extended capabilities
=> ping 192.168.0.110
--- net_loop Entry
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
eth_current_changed start act=ethernet@4a100000
eth_current_changed end act=ethernet@4a100000
eth_get_dev ethernet@4a100000
Trying ethernet@4a100000
link up on port 0, speed 10, full duplex
--- NetState set to 0
--- net_loop Init
Using ethernet@4a100000 device
--- net_loop timeout handler set (9ffbf5b9)
sending ARP for 192.168.0.110
ARP broadcast 1
packet received
Receive from protocol 0x806
Got ARP
Got ARP REPLY
packet received
Receive from protocol 0x800
Got IP
len=28, v=45
--- NetState set to 2
--- net_loop UDP handler set (00000000)
--- net_loop ARP handler set (00000000)
--- net_loop timeout handler cancelled
--- net_loop Success!
--- NetState set to 0
host 192.168.0.110 is alive
所以导致Lan8710无法使用的原因应该就是短路,导致连地址识别出来都不对了,3个和mode/addr复用的脚状态应该存在短路问题。
三,小结
我这次主要就是想带着问题来学习,这样学习动力更强,问题解决了,也很有成就感。而且过程中不但学习了调试技巧也对排查问题的方法或者套路更加熟练了。过程中主要学习了
1)芯片手册看了MAC的3端口功能,了解了vlan和switch的功能,看了PHY手册了解了lan8710的功能。MDIO主从接口可以一拖多个,addr有5个bit。
2)学习了mii和mdio调试和设置phy寄存器的命令。
3)ping命令的源码路径分析。
4)网上搜索关于ping不同排查硬件的方向注意就是clock,mii或rmii接口是否匹配,phy地址是否正确,pin脚是否焊接错误。
5)以太网通信的回环测试方法。
6)学习了uboot基于设备树的修改方法和uclass及driver和device(基本都改成设备树了)的api。这块看起来很容易,因为之前qemu基于QObject进行的初始化注册,然后使用的时候再match都是一样的设计,qemu的结构体比uboot要复杂多了,但是原理类似,一通百通。
7)学习了快速排查关于哪些文件有被编译,主要看u-boot.map文件是否由此api,也可以看相关的u-boot.cfg中是否有配置,这些配置主要在代码或Makefile中用到。然后map文件中搜索关键字.u_boot_list_2_就可以找到哪些uclass或driver被注册了。
8)学习了如何排查钩子函数,只要打印它的地址,然后减去bdinfo的offset就可以知道原始地址,在map文件中就可以找到钩子函数的api。