工作中涉及linux以太网驱动,涉及代码:drivers/net/ethernet/stmicro/,drivers/net/phy/,进行一下总结。
概要:
1.以太网硬件
2.软件初始化probe, open
3.数据发送过程
4.数据接收过程
5.设备无关层
6.bring up以太网功能
7.传输过程中传输中断的问题
1.以太网硬件
MAC:通常集成在ARM芯片中,功能类似于一个controller,以太网协议层数据传送给MAC,由MAC通过DMA发送到外部接口,外部接口连接着PHY。或者接收从PHY传过来的信号,DMA搬运到内存中存储。
PHY:通常是一个独立芯片,有数字和模拟两部分,也可以集成在ARM芯片内部。负责把从MAC传送过来的数据转换成可以在网线上传输的信号,或者接收网线上传输过来的信号,转换成数字信号回传给MAC。分为百兆PHY和千兆PHY。
PHY配置:自协商模式开启/关闭; 自协商关闭状态下,配置为强制full duplex, half duplex, 强制1000Mbps/100Mbps/10Mbps;WOL开启/关闭。
MAC和PHY之间的接口分为RMII接口(百兆),RGMII接口(千兆)。
RMII接口:
数据传输信号线TX0, TX1, RX0,RX1,clock
控制口:MDC, MDIO
RGMII接口:
数据传输信号线TX0-3, RXD0-3,TX clock, RX clock
控制口:MDC, MDIO
MDC,MDIO用于控制信号的传输,MAC对PHY的读写数据通过MDIO接口传送。
TX,RX,信号线用于数据信号的传输。
2.软件初始化
我们使用的驱动是stmicro驱动,所以基于该驱动代码来介绍
probe
1.读取dts文件中的ethernet配置,读取MAC基地址,rgmii or rmii, 配置寄存器,读取和保存从uboot传过来的MAC地址。
2.alloc,init net_device struct, 注册ops: stmmac_netdev_ops, register net_device. clock enable.
stmmac_hw_init:注册mac ops, dma ops.
dwmac1000_ops, dwmac1000_dma_ops,
3.寻找phy: stmmac_mdio_register
BUS:bus_register(&mdio_bus_type)
driver: driver_register注册phy driver.
device: 读取phy id, phy_device_create, dev.bus = &mdio_bus_type
mdio_bus_type里面有一个match函数,通过phyid来match phy device和phy driver
stmmac_open
ifconfig eth0 up时会调用到ndo_open回调,实际调用stmmac_open
1.stmmac_init_phy, 如果没有匹配上phy driver, 就赋予通用phy driver, 初始化phy, 启动delayed work: phy_state_machine,
2.alloc&init dma descriptor struct array, sk_buff address array (tx, rx)
3.MAC init
dma mode, descriptor base address, set mac address,
4.request_irq, 注册ISR
5.napi_enable, netif_start_queue.
3.数据发送
每填充一个descriptor, cur_tx++, 每回收一个descriptor, dirty_tx++,
如果dirty_tx != cur_tx, 说明有等待dma传送的数据。
调用stmmac_xmit, 注册为ndo_start_xmit的回调函数。
TX 发送完毕回收内存
stmmac_tx_clean,
一次stmmac_tx_clean调用会通过while循环把队列中所有已经完成发送的descriptor全部回收。
stmmac_tx_clean可通过DMA ISR调用,也可以通过timer ISR调用到。
4.数据接收
关于skb
TX端的skb是从上层传送过来的,我们需要把skb->data地址 dma_map_single到 tx descriptor中,发送完成后,再dma_ummap_single, 调用dev_free_skb, 应该是通知上层,这个skb已经处理完毕。
RX端的skb,是在驱动中alloc的,接收的数据填充到skb中,然后通过napi_gro_receive把skb传送到上层
Q:驱动怎么知道传送给上层的skb中的空间已经可以作为下一次传送的空间来覆盖数据?
A:传给上层的skb不会再使用,会重新获取到新的skb.
调试打印函数:通过修改default_msg_level, 设置打印功能。
5.设备无关层
在协议层和设备驱动程序之间,是skb的传递,类似于其他驱动程序中的core层,与具体设备无关。
skb向上传递:
skb向下传递:
6.bring up以太网功能
检查寄存器基地址,寄存器定义,寄存器配置(百兆,千兆,rmii,rgmii,内部,外部phy), pinmux, 检查phy的配置.
probe失败可能原因:
1.MAC reset失败。检查电源,clock。
2.无法读取phy id。检查phy的电源,clock,pinmux.
能正确显示link状态,但是数据不通:
1.检查寄存器配置,pinmux, 主要是rmii, rgmii相关配置
2.检查phy driver是否正确匹配上,phy driver中的phy id与 phy device 中的phy id如果不一致的话,phy driver就匹配不成功,导致phy config函数没有执行到。
7.传输过程中传输中断的问题
tx方向的传输中断。增加每秒运行一次的work queue, 检查在dma队列中是否有待发送的数据(已经交给dma),如果有,记下队列index, 过5秒之后再检查这个待发送的数据是否已发送完成,如果还未完成,认为TX传输中止,reset ethernet.
rx方向的传输中断。每条检查一次,检查phy的状态,如果状态异常,则reset ethernet. 还可以仿照TX方向增加监控和恢复机制。
在reset ethernet时,需要注意kernel panic。 reset过程是,停止TX,RX,释放之前alloc的所有memory, 重新alloc memory, 初始化mac, 初始化phy。出现panic的原因是,驱动代码还在使用已经被释放了的memory。因为已经停止了TX,RX,按道理不会再运行TX,RX部分的代码了。最后发现是tx timer handler里面会操作memory。当停止TX,RX之后, 释放所有memory, 然后tx timer handler触发,操作被释放的memory, 导致panic. 解决方法是,减少tx timer的expire时间,并且在停止TX之后,增加delay, 使tx timer handler完成之后,再释放memory.