前面文章零零星星地分析了PHY,本来想完整地,系统地做分析,发现工程量太大了,而自己又一知半解,所以只好各个击破,一点一点来分析。本文主要分析了设备上电、拨出网线、插上网线、自动协商等过程的PHY状态。
MAC驱动和PHY驱动
PHY一般和具体的MAC控制驱动联系一起,这里以TI的MAC驱动为例,由它切入到PHY驱动。Linux内核通过mdio总线访问、控制PHY,源码实现在driver/net/phy/mdio_bus.c中。下面是mdio扫描、找到并注册phy的过程:
1
2
3
4
5
6
7
8
9davinci_mdio_probe
->mdiobus_register
-> device_register
-> mdiobus_scan
-> get_phy_device
-> get_phy_id // 读寄存器
-> phy_device_create
-> INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!!!!初始化状态机函数
-> phy_device_register
在phy_device_create中做了大量的初始化工作,比如默认就是使能自动协商,另外调用INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine)创建phy的状态机,——实际上它是一个延时工作队列。
cpsw驱动在net_device_ops的ndo_open函数,亦即cpsw_ndo_open中调用cpsw_slave_open,通过phy_connect与phy连接,同时将cpsw_adjust_link赋值给phy的状态调整函数指针adjust_link。在些过程将将PHY状态机开启。
这个过程主要的函数如下:
1
2
3
4
5
6
7cpsw_ndo_open
-> cpsw_slave_open
-> phy_connect (传递cpsw_adjust_link)
-> phy_connect_direct (PHY_READY)
-> phy_prepare_link (赋值cpsw_adjust_link为adjust_link)
-> phy_start_machine
-> phy_start (PHY_READY变成PHY_UP)
当系统启动时,经过上述的步骤,一切已经准备妥当。就等着迎接PHY的状态变更了。在这里,需要提及的函数是cpsw_adjust_link,它调用了_cpsw_adjust_link,之后通知内核其它网络模块当前的状态。这个函数将在phy状态机函数中时时被调用,所以要关注一下。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static void cpsw_adjust_link(struct net_device *ndev)
{
struct cpsw_priv *priv = netdev_priv(ndev);
bool link = false;
for_each_slave(priv, _cpsw_adjust_link, priv, &link);
if (link) {
netif_carrier_on(ndev); // 通知内核子系统网络,当前链接是OK的
if (netif_running(ndev))
netif_wake_queue(ndev);
} else {
netif_carrier_off(ndev); // 通知内核子系统网络,当前链接断开了
netif_stop_queue(ndev);
}
}
真正干活(设置)的是这个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45static void _cpsw_adjust_link(struct cpsw_slave *slave,
struct cpsw_priv *priv, bool *link)
{
struct phy_device *phy = slave->phy;
u32 mac_control = 0;
u32 slave_port;
if (!phy)
retur