Linux下GMAC网络设备:硬件接口、GMAC/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