文章目录
- 一、phy设备概述
- 二、内核驱动配置与设备树添加
- 三、其他补充
一、phy设备概述
我们知道在计算机网络上有一个OSI 7层模型:
应用层
:网络服务与最终用户的一个接口。
协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP
表示层
:数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层)
格式有:JPEG、ASCll、EBCDIC、加密格式等 [2]
会话层
:建立、管理、终止会话。(在五层模型里面已经合并到了应用层)
对应主机进程,指本地主机与远程主机正在进行的会话
传输层
:定义传输数据的协议端口号,以及流控和差错校验。
协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层
网络层
:进行逻辑地址寻址,实现不同网络之间的路径选择。
协议有:ICMP IGMP IP(IPV4 IPV6)
数据链路层
:建立逻辑连接、进行硬件地址寻址、差错校验 [3] 等功能。(由底层网络定义协议)将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。
物理层
:建立、维护、断开物理连接。(由底层网络定义协议)
后来人们觉得7层模型过于复杂又精简成5层或4层模型:
今天我们的主角Phy设备
处于最底层,也就是物理层
。一般与数据链路层的mac芯片
配合使用,在宏观上phy芯片主要是将模拟信号进行解码,通过MII等接口,将数字信号传送出去。
下图为一款DP83TC811 phy芯片的简单原理图,phy芯片与主控一般使用MII/RMII/RGMII接口
进行数据传递。
同时主控一般使用Mdio
总线去配置phy芯片的相关寄存器,有点类似于Codec芯片中PCM/I2S传输音频数据,I2C配置Codec寄存器。下图为该款芯片的一个典型应用图:
另外需要明确的一个点是phy是一个标准设备,必须符合IEEE802.3 中标准模块
的相关规定,其中IEEE802.3 协议定义了地址为0-15 这16个寄存器的功能,地址16-31的寄存器留给芯片制造商自由定义。
也就是说phy芯片不是厂商想怎么设计就怎么设计的,你的符合标准,一旦有标准对于软件来讲就是利好,因为标准化的东西Linux内核已经给我们做好了,因此对于大多数phy芯片使用通用Generic PHY驱动
就可以,对于有厂商自定义或者扩展寄存器的,才需要使用对应厂商的特殊驱动。
二、内核驱动配置与设备树添加
phy设备对应的内核目录为/drivers/net/phy
通过分析各层Makefile文件,我们知道至少需要打开
obj-$(CONFIG_PHYLIB) += phy/
同时如果CONFIG_PHYLIB
打开,对应目录一些文件也会编译进内核,其中包括phy_device.c等
libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o
obj-$(CONFIG_PHYLIB) += libphy.o
前面我们说到phy设备是一个标准设备,所以内核默认已经注册了缺省的默认phy驱动,在phy_device.c中:
... ...
static struct phy_driver genphy_driver[] = {
{
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.soft_reset = genphy_no_soft_reset,
.config_init = genphy_config_init,
.features = PHY_GBIT_FEATURES | SUPPORTED_MII |
SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_BNC,
.config_aneg = genphy_config_aneg,
.aneg_done = genphy_aneg_done,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic 10G PHY",
.soft_reset = gen10g_soft_reset,
.config_init = gen10g_config_init,
.features = 0,
.config_aneg = gen10g_config_aneg,
.read_status = gen10g_read_status,
.suspend = gen10g_suspend,
.resume = gen10g_resume,
} };
static int __init phy_init(void)
{
int rc;
rc = mdio_bus_init();
if (rc)
return rc;
rc = phy_drivers_register(genphy_driver,
ARRAY_SIZE(genphy_driver), THIS_MODULE);
if (rc)
mdio_bus_exit();
return rc;
}
static void __exit phy_exit(void)
{
phy_drivers_unregister(genphy_driver,
ARRAY_SIZE(genphy_driver));
mdio_bus_exit();
}
subsys_initcall(phy_init);
module_exit(phy_exit);
Generic PHY
和Generic 10G PHY
为内核缺省的默认phy驱动,里面有对标准phy驱动的初始化config_init函数
,软复位soft_reset函数
,参数协商配置config_aneg函数
,状态获取read_status函数
,以及休眠唤醒相关的suspend/resume函数
,对于一般的的phy设备就够用了,假设是一些非标或者扩展的,例如Atheros 803x PHY,需要使用其对应的驱动at803x.c
,在宏配置上增加
obj-$(CONFIG_AT803X_PHY) += at803x.o
我们也可以看下at803x.c
里面写的什么,打开可以看到驱动和通用驱动类似,但是有自己独立的实现,支持ATHEROS 8030/8031/8035 3款phy芯片:
static struct phy_driver at803x_driver[] = {
{
/* ATHEROS 8035 */
.phy_id = ATH8035_PHY_ID,
.name = "Atheros 8035 ethernet",
.phy_id_mask = 0xffffffef,
.probe = at803x_probe,
.config_init = at803x_config_init,
.set_wol = at803x_set_wol,
.get_wol = at803x_get_wol,
.suspend = at803x_suspend,
.resume = at803x_resume,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
}, {
/* ATHEROS 8030 */
.phy_id = ATH8030_PHY_ID,
.name = "Atheros 8030 ethernet",
.phy_id_mask = 0xffffffef,
.probe = at803x_probe,
.config_init = at803x_config_init,
.link_change_notify = at803x_link_change_notify,
.set_wol = at803x_set_wol,
.get_wol = at803x_get_wol,
.suspend = at803x_suspend,
.resume = at803x_resume,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
}, {
/* ATHEROS 8031 */
.phy_id = ATH8031_PHY_ID,
.name = "Atheros 8031 ethernet",
.phy_id_mask = 0xffffffef,
.probe = at803x_probe,
.config_init = at803x_config_init,
.set_wol = at803x_set_wol,
.get_wol = at803x_get_wol,
.suspend = at803x_suspend,
.resume = at803x_resume,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = at803x_config_aneg,
.read_status = genphy_read_status,
.aneg_done = at803x_aneg_done,
.ack_interrupt = &at803x_ack_interrupt,
.config_intr = &at803x_config_intr,
} };
module_phy_driver(at803x_driver);
static struct mdio_device_id __maybe_unused atheros_tbl[] = {
{ ATH8030_PHY_ID, 0xffffffef },
{ ATH8031_PHY_ID, 0xffffffef },
{ ATH8035_PHY_ID, 0xffffffef },
{ }
};
MODULE_DEVICE_TABLE(mdio, atheros_tbl);
三、其他补充
其实phy这里还有很多需要讲的,例如phy芯片设备树的配置,例如mdio,rmii总线配置,软件层面phy驱动的注册及匹配,状态机维护及状态变化时相关函数的调用,应用层对网卡的操作及网络配置等,今天有点晚是写不完了,后面慢慢补充吧。