Linux下GMAC网络设备:硬件接口、GMAC/PHY、驱动、测试程序
1 嵌入式网络硬件接口 如下是常见的嵌入式网络硬件接口框图: SOC集成MAC。 MAC通过MII系列接口和PHY之间传输数据,通过MDIO接口初始化配置PHY芯...
1 嵌入式网络硬件接口
如下是常见的嵌入式网络硬件接口框图:
SOC集成MAC。
MAC通过MII系列接口和PHY之间传输数据,通过MDIO接口初始化配置PHY芯片。
PHY芯片和RJ45之间通过4组差分模拟信号传输数据,并驱动RJ45的LED信号灯。
RJ45通过网线和外部连接。
1.1 嵌入式网络几种常见架构
嵌入式网络常见的集中架构:
SOC和MAC+PHY芯片连接:
SOC集成MAC IP,通过MII接口和外部PHY芯片连接:
1.2 MII/RMII/GMII/RGMII接口
MII(Media Independent Interface)/RMII(Reduced MII)接口:支持10Mbit/s和100Mbit/s 数据传输模式。
GMII(Gigabit Media Independent Interface)/RGMII(Reduced GMII)接口:支持 10Mbit/s、100Mbit/s 以及1000Mbit/s 数据传输模
1.3 MDIO接口
MDI(Management Data Input/Output)包含数据线MDIO和时钟线MDC。
MDIO 接口支持多达 32 个 PHY。同一时刻内只能对一个 PHY 进行操作。
PHY芯片可以通过地址引脚配置地址。
同一 MDIO接口下的所有PHY芯片,其器件地址不能冲突。
RJ45 座子上一般有两个灯,一个黄色(橙色),一个绿色, 一般绿色亮的话表示网络连接正常,黄色闪烁的话说明当前正在进行网络通信。
1.4 RJ45接口
网络设备是通过网线连接起来的,插入网线的叫做RJ45座。
RJ45 座要与 PHY 芯片连接在一起,但是中间需要一个网络变压器,网络变压器用于隔离以及滤波等,网络变压器也是一个芯片。
2 STM32 GMAC芯片
2.1 GMAC
GMAC集成到SOC,负责将收发网络数据:
AXI Master interface:DMA作为master通过AXI Master interface进行数据传输。
DMA:在系统内存和外设之间搬运数据。
MTL(MAC Transaction Layer):控制DMA和MAC之间数据传输。
MAC:实现Ethernet协议,对接不同MII接口。
MII/RMII/GMII/RGMII:MAC和PHY数据传输接口。
AHB slave interface:提供访问CSR接口。
CSR:控制和状态寄存器,对DMA、MTL、MAC进行控制。
MDIO:MAC对PHY进行配置的控制通道。
2.2 PHY芯片(RTL8211F-CG)
PHY 是 IEEE 802.3 规定的一个标准模块。
PHY芯片寄存器地址空间为5位,地址 0~31共32个寄存器,IEEE 定义了0~15 这16个寄存器的功能,16~31这16个寄存器由厂商自行实现。
以RTL8211F-CG为例:
Phy从MAC收到数据,经过DAC转换从MDI接口传输出去。
Phy从MDI接口收到信号,经过ADC转换从GMII/RGMII传给MAC。
Phy通过MDIO接口接收MAC的控制。
Phy将状态信息通过LED信号对外输出。
2.3 原理图
从SOC输出RGMII、MDIO、中断、复位、时钟等信号到PHY。
PHY通过4组差分信号线输出数据,并通过LED指示状态。
3 Linux网络设备配置
Linux下网络设备配置:
配置网络协议。
配置网络设备驱动,包括用到的总线、PHY等。
Networking support ->Networking options--繁多的网络协议配置选项。 ->Packet socket ->Unix domain sockets ->TCP/IP networking ->CAN bus subsystem support--CAN总线子系统。 ->Wireless--无线协议。Device Drivers
->Network device support ->Network core driver support--网络设备核心层。
->Ethernet driver support--以太网设备驱动。
->STMicroelectronics devices
->STMicroelectronics Multi-Gigabit Ethernet driver--Synopsys以太网IP核心和驱动。
->Support for STMMAC Selftests--STMMAC自测试功能。
->STMMAC Platform bus support
->Generic driver for DWMAC
->Synopsys devices ->MDIO bus device drivers--MDIO驱动。 ->PHY Device support and infrastructure--PHY驱动 ->USB Network Adapters--通过USB连接的网络设备。 ->Wireless LAN--无线局域网设备。
4 Linux网络子系统初始化
4.1 通用网络设备相关组件初始化:net_dev_init
net_dev_init进行网络设备相关组件初始化:
创建每进程网络相关proc节点。
收发软终端注册。
注册会换设备lo。
初始化数据包接收队列等。
net_dev_init dev_proc_init register_pernet_subsys dev_proc_ops--为每个进程创建/proc/xxx/net/dev、softnet_stat、ptype节点。 dev_mc_net_ops--创建/proc/xxx/net/dev_mcast节点。 netdev_kobject_init register_pernet_subsys register_pernet_device open_softirq--注册NET_RX/NET_TX软中断。 cpuhp_setup_state_nocalls
5 STM32MP1 GMAC驱动
5.1 设备树
如下是stm32 dwmac的dts,具体可以参考stm32-dwmac.txt。关于Synopsis部分可以参考snps,dwmac.yaml。网络通用的controller和phy的配置可以参考:ethernet-controller.yaml和ethernet-phy.yaml。
ethernet@5800a000 {
compatible = "st,stm32mp1-dwmac\0snps,dwmac-4.20a";
reg = <0x5800a000 0x2000>;
reg-names = "stmmaceth";
interrupts-extended = <0x06 0x00 0x3d 0x04 0x18 0x46 0x04>;
interrupt-names = "macirq\0eth_wake_irq";--macirq是MAC中断;eth_wake_irq是eth唤醒系统的中断。
clock-names = "stmmaceth\0mac-clk-tx\0mac-clk-rx\0ethstp";--stmmaceth是主时钟;mac-clk-tx是MAC TX时钟;mac-clk-rx是MAC RX时钟;ethstp。
clocks = <0x0e 0x69 0x0e 0x67 0x0e 0x68 0x0e 0x70>;
st,syscon = <0x0d 0x04>;
snps,mixed-burst;--DMA使用mixed burst模式。
snps,pbl = <0x02>;--配置tx/rx burst长度。
snps,en-tx-lpi-clockgating;--允许在TX低功耗模式下将MAX TX时钟关闭。
snps,axi-config = <0x67>;--选择AXI总线模式。
snps,tso;--使能TSO,即TCP Segment Offload。利用网卡的处理能力,降低CPU发送数据包负载的技术。
power-domains = <0x12>;
status = "okay";--驱动使能本设备。
pinctrl-0 = <0x68>;
pinctrl-1 = <0x69>;--定义GMAC引脚复用功能。
pinctrl-names = "default\0sleep";
phy-mode = "rgmii-id";--和PHY接口类型,表示使用的是RGMII接口,有PHY提供RX和TX延迟,MAC不需要添加RX和TX延时内容。
max-speed = <0x3e8>;--以Mbit/s为单位的最高速率,即1000Mbit/s。
phy-handle = <0x6a>;--指向PHY设备句柄。
mdio0 {--描述MDIO总线。
#address-cells = <0x01>;
#size-cells = <0x00>;
compatible = "snps,dwmac-mdio";
ethernet-phy@0 {
reg = <0x00>;--PHY芯片地址。
phandle = <0x6a>;
};
};
};
5.2 网络驱动源码解析
如下是stm32 gmac驱动涉及到的文件,相关的驱动模块有:
stm32_dwmac:
phy_module:注册struct phy_driver,在GMAC驱动里面根据读取到的ID匹配。
drivers/net/
├── ethernet
│ ├── stmicro
│ │ └── stmmac
│ │ ├── chain_mode.c--Chain Mode。│ │ ├── ring_mode.c--Ring Mode。
│ │ ├── dwmac1000_core.c--MAC1000 core函数集。
│ │ ├── dwmac1000_dma.c--MAC1000 DMA函数集。
│ │ ├── dwmac100_core.c
│ │ ├── dwmac100_dma.c
│ │ ├── dwmac4_core.c--处理DWC Ether MAC 4.xx core函数集。
│ │ ├── dwmac4_descs.c--处理DWC Ether MAC 4.xx descriptor函数集。
│ │ ├── dwmac4_dma.c--处理DWC Ether MAC 4.xx DMA函数集。
│ │ ├── dwmac4_lib.c--处理DWC Ether MAC 4.xx DMA函数集。
│ │ ├── dwmac5.c--处理DWC Ether 5.xx QoS Core函数集。
│ │ ├── dwmac-dwc-qos-eth.c
│ │ ├── dwmac-generic.c
│ │ ├── dwmac_lib.c
│ │ ├── dwmac-stm32.c--适配STM32的DWMAC Glue层。
│ │ ├── dwxgmac2_core.c
│ │ ├── dwxgmac2_descs.c
│ │ ├── dwxgmac2_dma.c
│ │ ├── enh_desc.c--处理enhanced descriptors。│ │ ├── norm_desc.c--处理normal descriptors。
│ │ ├── hwif.c--STMMAC的硬件接口初始化,遍历stmmac_hw。
│ │ ├── mmc_core.c--Management Counters。
│ │ ├── stmmac_ethtool.c--实现struct ethtool_ops结构体stmmac_ethtool_ops。
│ │ ├── stmmac_hwtstamp.c--管理HW timerstamp和PTP接口。
│ │ ├── stmmac_main.c--提供STMMAC通用API。
│ │ ├── stmmac_mdio.c--GMAC驱动MDIO注册和去注册接口,以及read/write实现。
│ │ ├── stmmac_platform.c--GMAC驱动platform device相关API。
│ │ ├── stmmac_ptp.c--Precision Time Protocol的注册去注册接口,以及stmmac_ptp_clock_ops函数集。
│ │ └── stmmac_tc.c--Transparent Clock。
├── loopback.c
├── macvlan.c
├── macvtap.c
├── mii.c
├── net_failover.c
├── phy
│ ├── fixed_phy.c
│ ├── mdio-bitbang.c
│ ├── mdio-boardinfo.c
│ ├── mdio_bus.c
│ ├── mdio_device.c
│ ├── motorcomm.c--Motorcomm的PHY驱动。
│ ├── phy-c45.c
│ ├── phy-core.c
│ ├── phy_device.c
│ ├── phylink.c
│ ├── phy.c--查找和配置PHY框架,以及通用PHY驱动。
│ └── swphy.c
├── Space.c
├── tap.c
├── tun.c
├── veth.c
└── virtio_net.c
STM32MP1网络驱动主要包括:
从DTS中获取时钟、中断、寄存器、pinctrl、电源域等配置,以及GMAC特有的属性。
初始化GMAC作为网络设备结构体net_device,以及系统资源初始化。
配置GMAC硬件。
配置MDIO、PHY。
注册GMAC到网络设备子系统中。
创建debufs等调试节点。
stm32_dwmac_probe ->stmmac_get_platform_resources--从设备树获取资源,包括macirq、eth_wake_irq、寄存器地址范围。 ->stmmac_probe_config_dt--获取mac字符串,以及从设备树获取参数填充plat_stmmacenet_data结构体。 ->stmmac_dt_phy-- ->of_device_get_match_data--根据设备树compatible和驱动的stm32_dwmac_match进行匹配。成功则返回data,否则退出。 ->stm32_dwmac_parse_data--获取mac-clk-tx、mac-clk-rx、st,syscon属性值。 ->stm32_dwmac_wake_init--配置GMAC中断唤醒系统。 ->device_set_wakeup_capable--配置设备允许唤醒系统。 ->device_pm_set_wake_irq--将设备中断附着为唤醒中断。 ->stm32_dwmac_init--进行PHY接口模式设置,使能rx/tx时钟。 ->stmmac_dvr_probe--进行网络注册,完成PHY接口初始化。 ->devm_alloc_etherdev_mqs--分配struct net_device结构体,并对其进行初始化。 ->stmmac_set_ethtool_ops--初始化struct ethtool_ops为stmmac_ethtool_ops。 ->stmmac_verify_args--验证驱动module参数,如果有错则使用默认参数。 ->create_singlethread_workqueue--创建stmmac_wq工作队列。 ->stmmac_hw_init--GMAC硬件初始化。 ->stmmac_check_ether_addr--检查MAC地址合法性,失败则分配一个随机MAC地址。 ->stmmac_tc_init ->netif_msg_init ->stmmac_napi_poll_rx/stmmac_napi_poll_tx--NAPI驱动要提供poll函数来轮询处理发送和接收数据。 ->stmmac_mdio_register--向内核注册MDIO总线。 ->mdiobus_alloc--分配struct mii_bus结构体。 ->of_mdiobus_register--解析mdio属性,及其子节点。 ->stmmac_mdio_read/stmmac_mdio_write/stmmac_mdio_reset--通过MDIO对PHY的读/写/复位接口。 ->mdiobus_get_phy--从0开始遍历,最多32个。获取phy对应的phy_device结构。 ->phy_attached_info ->stmmac_phy_setup ->phylink_create--phylink是net_device和phy_device中介,当两者之间连接变化时phylink做出对应配置改变。关于phylink参考《phylink》。 ->register_netdev--在对struct net_device进行初始化后,进行网络设备注册,设备操作函数集为stmmac_netdev_ops。 ->stmmac_init_fs--创建/sys/kernel/debug/stmmaceth/eth0节点,提供DMA rx/tx rings和DMA HW特性信息。
stm32_dwmac_remvoe
5.3 MDIO总线
5.3.1 MDIO总线初始化
MDIO总线初始化:
mdio_bus_init ->class_register--注册mdio_bus类mdio_bus_class。 ->bus_register--注册mdio_bus总线mdio_bus_type。
mdio_bus_type定义了MDIO总线上设备和驱动match函数,以及uevent函数:
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.uevent = mdio_uevent,
};
当设备或者驱动注册到MDIO总线上后,mdio_bus_match进行MDIO总线上设备和驱动匹配:
mdio_bus_match ->of_driver_match_device ->mdio->bus_match ->mdio_device_bus_match--通过mdiobus_create_device()函数创建的设备。判断名称是否一致。 ->phy_bus_match--通过phy_device_create()创建的设备。通过phy_id和phy_id_mask的与运算之后比较设备和driver是否一致。
5.3.2 MDIO总线相关API
mdiobus_alloc()分配一个struct mii_bus结构体,然后of_mdiobus_register根据从DTS中获取的MDIO总线、PHY设备等信息进行初始化:
注册MDIO总线。
遍历MDIO总线下的PHY设备,注册PHY设备。
创建PHY设备和GMAC之间的连接。
->of_mdiobus_register--注册MDIO总线并且根据设备树创建PHY设备。
->mdiobus_register--向MDIO总线注册设备。
->__mdiobus_register
->device_register--注册MDIO设备,类型为mdio_bus_class。
->of_mdiobus_register_phy--从设备树获取PHY进行注册。
->get_phy_device--
->get_phy_id--通过MDIO总线读取MII_PHYSID1和MII_PHYSID2,即PHY设备ID。
->phy_device_create--创建PHY设备,设备总线为mdio_bus_type,设备类型为mdio_bus_phy_type。 ->phy_state_machine
->phy_device_register
->mdiobus_register_device--将PHY设备注册到MDIO总线。
->phy_device_reset--对PHY进行复位。
->phy_scan_fixups--判断设备是否需要Fixup,如需要则调用fixup->run()。
->device_add
mdio_bus_phy_type为Phy设备提供了统一的属性定义、释放函数、电源管理等:
static struct attribute *phy_dev_attrs[] = {
&dev_attr_phy_id.attr,
&dev_attr_phy_interface.attr,
&dev_attr_phy_has_fixups.attr,
NULL,
};
ATTRIBUTE_GROUPS(phy_dev);
static const struct device_type mdio_bus_phy_type = {
.name = "PHY",
.groups = phy_dev_groups,
.release = phy_device_release,
.pm = MDIO_BUS_PHY_PM_OPS,
};
如下为创建的节点:
/sys/class/mdio_bus/stmmac-0--MDIO总线。
|-- device -> ../../../5800a000.ethernet
|-- of_node -> ../../../../../../firmware/devicetree/base/soc/ethernet@5800a000/mdio0
|-- stmmac-0:00--MDIO总线上的PHY设备。
| |-- attached_dev -> ../../../net/eth0--表示此PHY设备时附着到哪一个设备上的。
| |-- driver -> ../../../../../../../bus/mdio_bus/drivers/YT8511\ Gigabit\ Ethernet
| |-- of_node -> ../../../../../../../firmware/devicetree/base/soc/ethernet@5800a000/mdio0/ethernet-phy@0
| |-- phy_has_fixups
| |-- phy_id
| |-- phy_interface--PHY接口类型。
| |-- subsystem -> ../../../../../../../bus/mdio_bus
| `-- uevent
|-- subsystem -> ../../../../../../class/mdio_bus
`-- uevent
5.4 phylink
GMAC在内核中被注册成struct net_device设备,phy芯片被注册成struct phy_device设备。phy_device和net_device之间的关联通过phy_link表达,维护两者之间的连接状态变化。
更多关于phylink说明参考《phylink》,API参考《Linux Networking and Network Devices APIs》。
5.5 PHY子系统和PHY驱动
5.5.1 PHY子系统初始化
关于PHY、phylink、MDIO更多参考《PHY Abstraction Layer》。
PHY子系统注册两个默认的PHY驱动:
phy_init ->mdio_bus_init ->features_init ->phy_driver_register ->genphy_c45_driver ->genphy_driver
genphy_driver是一个通用PHY驱动:
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.soft_reset = genphy_no_soft_reset,
.get_features = genphy_read_abilities,
.aneg_done = genphy_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
};
5.5.2 YT8511驱动
YT8511驱动:
module_phy_driver(ytphy_drvs) ->phy_module_driver ->phy_module_init ->phy_drivers_register ->phy_driver_register--将ytphy_drvs注册到MDIO总线上。
当读取到的ID匹配后,phy_device和phy_driver就关联上了。对phy_device的操作,就可以调用具体phy_driver的驱动函数:
static struct phy_driver ytphy_drvs[] = {
{
.phy_id = PHY_ID_YT8511,
.name = "YT8511 Gigabit Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_GBIT_FEATURES,
//.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
#if GMAC_CLOCK_INPUT_NEEDED
.config_init = yt8511_config_init,
#else
.config_init = genphy_config_init,
#endif
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
6 网络驱动测试
6.1 iperf
启动iperf服务器端:iperf -s。
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[ 4] local 192.168.137.1 port 5001 connected with 192.168.137.111 port 48680
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-30.0 sec 3.29 GBytes 942 Mbits/sec
启动iperf客户端:iperf -c 192.168.137.1 -i 10 -t 30。
------------------------------------------------------------
Client connecting to 192.168.137.1, TCP port 5001
TCP window size: 306 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.137.111 port 48680 connected with 192.168.137.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 1.10 GBytes 942 Mbits/sec
[ 3] 10.0-20.0 sec 1.10 GBytes 946 Mbits/sec
[ 3] 20.0-30.0 sec 1.10 GBytes 942 Mbits/sec
[ 3] 0.0-30.0 sec 3.29 GBytes 943 Mbits/sec
6.2 trace event
Kernel在/sys/kernel/debug/tracing/events提供了网络相关的trace event:
bridge/:
br_fdb_add enable
br_fdb_external_learn_add fdb_delete
br_fdb_update filter
mdio:
enable filter mdio_access
napi:
enable filter napi_poll
net:
enable netif_receive_skb
filter netif_receive_skb_entry
napi_gro_frags_entry netif_receive_skb_exit
napi_gro_frags_exit netif_receive_skb_list_entry
napi_gro_receive_entry netif_receive_skb_list_exit
napi_gro_receive_exit netif_rx
net_dev_queue netif_rx_entry
net_dev_start_xmit netif_rx_exit
net_dev_xmit netif_rx_ni_entry
net_dev_xmit_timeout netif_rx_ni_exit
skb:
consume_skb filter skb_copy_datagram_iovec
enable kfree_skb
sock/:
enable inet_sock_set_state sock_rcvqueue_full
filter sock_exceed_buf_limit
tcp:
enable tcp_probe tcp_retransmit_skb
filter tcp_rcv_space_adjust tcp_retransmit_synack
tcp_destroy_sock tcp_receive_reset tcp_send_reset
udp:
enable filter udp_fail_queue_rcv_skb