Linux以太網卡架構解析-MAC層和PHY層

引子

最近,在調試基於Freescale IMX6UL板子的以太網口時,遇到了一個奇怪的問題:網口插拔時,系統檢測不到Link Down、Link UP事件。並且,在使用ifconfig eth0 up,然後再ifconfig eth0 down時,會提示:

	$ sudo ifconfig eth0 up
	$ sudo ifconfig eth0 down
    ifconfig: SIOCSIFFLAGS: No such device

首先,可以確定的是,以太網PHY芯片驅動可以正確加載,這說明芯片的DTS配置應該沒有問題。之後,又嘗試其他的檢測網口插拔事件的實現方式,比如通過socket的SIOCGIFFLAGS獲取網口的狀態,以及通過/sys/class/net/eth0/iplink查看網口的狀態等等,事實證明,這些方法都不能正確獲取到網口的狀態變化。

最後,通過下載IMX6UL的最新固件,發現可以正確的監測到網線的插拔狀態,那問題應該內核配置或者PHY芯片的DTS配置有有問題。通過對比PHY芯片相關的內核配置以及DTS配置,最後確定是DTS的ethernet的PHY_ID配置錯了。這個PHY_ID爲PHY芯片進行MDIO通信時的設備地址,這裏所說的MDIO爲MAC控制器控制PHY芯片時所採用的的通信方式,其原理與I2C通信類似,PHY_ID類似於I2C中的設備地址。到這裏,一切都真相大白了,難怪無法檢測到網線的插拔插拔狀態,因爲CPU的MAC控制器根本沒有辦法和PHY芯片進行通信,也就沒有辦法獲取到PHY芯片的狀態了。之後,也知道了其實ifconfig最終也是通過MDIO與PHY通信來實現的,所以,纔會有上面的錯誤提示。

說了這麼多,其實,裏面涉及到的知識量很大,比如,一個網卡硬件的組成部分包括哪些?PHY芯片和MAC控制器擔當的是什麼?它們之間是如何連接、通信的?ifconfig、ip、ethtool這些應用程序工具是如何控制真實的硬件的?下面分小節一一簡單說明一下,算是對以上知識的一個科普。

測試環境

CPU:Freescale i.MX6UltraLite  
開發板:飛凌OKMX6UL-C2開發板
內核:3.6.18
以太網PHY:KSZ8081RNB

以太網控制器

網卡工作在 OSI 網絡體系的最後兩層,物理層和數據鏈路層,物理層定義了數據傳送與接收所需要的電與光信號、線路狀態、時鐘基準、數據編碼和電路等,並向數據鏈路層設備提供標準接口。物理層的芯片稱之爲 PHY。數據鏈路層則提供尋址機構、數據幀的構建、數據差錯檢查、傳送控制、向網絡層提供標準的數據接口等功能。以太網卡中數據鏈路層的芯片稱之爲 MAC 控制器。很多網卡的這兩個部分是做到一起的。他們之間的關係是 PCI 總線接 MAC 總線,MAC 接 PHY,PHY 接網線(當然也不是直接接上的,還有一個變壓裝置)。
在這裏插入圖片描述
圖 1. 一個典型的符合 IEEE802.3 標準的的以太網控制器結構圖
這裏簡單解析一下:

  • MAC:Media Access Control,即媒體訪問控制子層協議。該協議位於 OSI 七層協議中數據鏈路層的下半部分,主要負責控制與連接物理層的物理介質。在發送數據的時候,MAC 協議可以事先判斷是否可以發送數據,如果可以發送將給數據加上一些控制信息,最終將數據以及控制信息以規定的格式發送到物理層;在接收數據的時候,MAC 協議首先判斷輸入的信息並是否發生傳輸錯誤,如果沒有錯誤,則去掉控制信息發送至 LLC 層。以太網 MAC 由 IEEE-802.3 以太網標準定義。

  • PHY:工作在OSI七層協議中的物理層, 嵌入式系統中“網卡”芯片一般都是PHY。它實現物理層。包括 MII/GMII(介質獨立接口)子層、PCS(物理編碼子層)、PMA(物理介質附加)子層、 PMD(物理介質相關)子層、MDI 子層。

  • MDC/MDIO爲MII的管理通信接口,工作方式爲兩線,雙工,MDC爲時鐘,MDIO爲雙向數據通信,原理上很類似於I2C總線,上圖也可以看出MAC可以通過MDC/MDIO掛接多個PHY

RMII、MII

MII

MII Media Independant Interface,即媒體獨立接口 ,其對MAC和PHY之間的通信方式進行了抽象,MAC和PHY各自實現MII接口,就可以相關通信。包括分別用於發送器和接收器的兩條獨立信道。每條信道都有自己的數據、時鐘和控制信號。MII 數據接口總共需要 16 個信號,包括 TX_ER,TXD<3:0>,TX_EN,TX_CLK,COL,RXD<3:0>,RX_EX,RX_CLK,CRS,RX_DV 等。MII的時鐘爲25MHz,傳輸速率爲10/100Mbps

MAC與PHY通過MII連接的示意圖如下:
在這裏插入圖片描述

  • MII_TX_CLK:發送數據使用的時鐘信號,對於10M位/s的數據傳輸,此時鐘爲2.5MHz,對於100M位/s的數據傳輸,此時鐘爲25MHz。
  • MII_RX_CLK:接收數據使用的時鐘信號,對於10M位/s的數據傳輸,此時鐘爲2.5MHz,對於100M位/s的數據傳輸,此時鐘爲25MHz。
  • MII_TX_EN:傳輸使能信號,此信號必需與數據前導符的起始位同步出現,並在傳輸完畢前一直保持。
  • MII_TXD[3:0]:發送數據線,每次傳輸4位數據,數據在MII_TX_EN信號有效時有效。MII_TXD[0]是數據的最低位,MII_TXD[3]是最高位。當MII_TX_EN信號無效時,PHY忽略傳輸的數據。
  • MII_CRS:載波偵聽信號,僅工作在半雙工模式下,由PHY控制,當發送或接收的介質非空閒時,使能此信號。 PHY必需保證MII_CRS信號在發生衝突的整個時間段內都保持有效,不需要此信號與發送/接收的時鐘同步。
  • MII_COL:衝突檢測信號,僅工作在半雙工模式下,由PHY控制,當檢測到介質發生衝突時,使能此信號,並且在整個衝突的持續時間內,保持此信號有效。此信號不需要和發送/接收的時鐘同步。
  • MII_RXD[3:0]:接收數據線,每次接收4位數據,數據在MII_RX_DV信號有效時有效。MII_RXD[0]是數據的最低位,MII_RXD[3]是最高位。當MII_RX_EN無效,而MII_RX_ER有效時,MII_RXD[3:0]數據值代表特定的信息(請參考表194)。
  • MII_RX_DV:接收數據使能信號,由PHY控制,當PHY準備好數據供MAC接收時,使能該信號。此信號必需和幀數據的首位同步出現,並保持有效直到數據傳輸完成。在傳送最後4位數據後的第一個時鐘之前,此信號必需變爲無效狀態。爲了正確的接收一個幀,有效電平不能滯後於數據線上的SFD位出現。
  • MII_RX_ER:接收出錯信號,保持一個或多個時鐘週期(MII_RX_CLK)的有效狀態,表明MAC在接收過程中檢測到錯誤。具體錯誤原因需配合MII_RX_DV的狀態及MII_RXD[3:0]的數據值。

RMMI

RMII (Reduced Media Independant Interface ) 是簡化的 MII 接口,在數據的收發上它比 MII 接口少了一倍的信號線,所以它一般要求是 50 M的總線時鐘,MII的時鐘總線爲25M。RMII 一般用在多端口的交換機,它不是每個端口安排收、發兩個時鐘,而是所有的數據端口公用一個時鐘用於所有端口的收發 ,這裏就節省了不少的端口數目。RMII 的一個端口要求 7 個數據線 ,比 MII 少了一倍,所以交換機能夠接入多一倍數據的端口。和 MII 一樣,RMII 支持 10 兆和 100 兆的總線接口速度 。RMII的時鐘爲50MHz,傳輸速率爲10/100Mbps。
MAC與PHY通過RMII連接的示意圖如下:
在這裏插入圖片描述

GMII

GMII(Gigabit MII) 是千兆網的 MII 接口,這個也有相應的 RGMII 接口,表示簡化了的 GMII 接口。GMII 採用 8 位接口數據,工作時鐘 125MHz,因此傳輸速率可達 1000Mbps 。同時兼容 MII 所規定的 10/100 Mbps 工作方式。GMII的時鐘頻率爲:2.5/25/125MHz),傳輸速率爲:10/100/1000Mbps

下面MII、RMII、GMII三種接口的對比:

接口類型 信號數量 時鐘速率 時鐘源 傳輸速率
MII 16 25MHz 外部晶振或者MAC提供,不需要與MAC時鐘同步 10/100Mbps
RMII 8 50MHz 一般是MAC提供,需要與MAC時鐘同步 0/1000Mbps
GRMII 8 125MHz 一般是MAC提供,需要與MAC時鐘同步 10/100/1000Mbps

MDC/MDIO

基本原理

MDC/MDIO爲MII的管理通信接口,工作方式爲兩線,雙工,MDC爲時鐘,MDIO爲雙向數據通信,原理上很類似於I2C總線,上圖也可以看出MAC可以通過MDC/MDIO掛接多個PHY。該總線由 IEEE 通過以太網標準 IEEE 802.3 的若干條款加以定義。MDIO 是一種簡單的雙線串行接口,將管理器件 ( 如 MAC 控制器、微處理器 ) 與具備管理功能的收發器 ( 如多端口吉比特以太網收發器或 10GbE XAUI 收發器 ) 相連接,從而控制收發器並從收發器收集狀態信息。可收集的信息包括鏈接狀態、傳輸速度與選擇、斷電、低功率休眠狀態、TX/RX 模式選擇、自動協商控制、環回模式控制等。除了擁有 IEEE 要求的功能之外,收發器廠商還可添加更多的信息收集功能,例如流控的打開關閉,自協商模式還是強制模式等,這也是 ethtool 的工作原理。MDC 則是管理數據的時鐘輸入,最高速率可達 8.3MHz。MDIO 是管理數據的輸入輸出雙向接口,數據是與 MDC 時鐘同步的。

特性

MDC/MDIO基本特性:

  1. 兩線制:MDC(時鐘線)和MDIO(數據線)。
  2. 時鐘頻率:2.5MHz
  3. 通信方式:總線制,可同時接入的PHY數量爲32個。

工作流程

MDIO 的工作流程爲:

  1. MDIO 接口在沒有傳輸數據的空閒狀態(IDLE)數據線 MDIO 處於高阻態。
  2. MDIO 出現一個 2bit 的開始標識碼 (01) 一個讀 / 寫操作開始。
  3. MDIO 出現一個 2bit 數據來標識是讀操作 (10) 還是寫操作 (01)。
  4. MDIO 出現一個 5bit 數據標識 PHY 的地址。
  5. MDIO 出現一個 5bitPHY 寄存器地址。
  6. MDIO 需要 2 個時鐘的訪問時間。
  7. MDIO 串行讀出 / 寫入 16bit 的寄存器數據。
  8. MDIO 恢復成 IDLE 狀態,同時 MDIO 進入高阻狀態。

IEEE 802.3 規定的 MII 寄存器

關於 MII/GMII 接口 PHY 寄存器的定義在 802.3 的 22.2.4 Management functions. 章節中。如該章節中的 Table 22 – 6 和 Table 22 – 7(即本文的圖 3 和圖 4,均出自 http://standards.ieee.org/getieee802/download/802.3-2008_section2.pdf)所示。

圖 2. IEEE802.3 定義的 MII 管理寄存器集

在這裏插入圖片描述
可以看到寄存器分爲基本集和擴展集,基本集的定義因 GMII 和 MII 而不同,對於 MII, 基本集包括寄存器 0 控制寄存器和 1 狀態寄存器,而對於 GMII;基本集包括寄存器 0、1 和 15。控制寄存器 0 和狀態寄存器 1 的定義如圖 2所示。

圖3 IEEE802.3 定義的寄存器 0 控制寄存器和 1 狀態寄存器
在這裏插入圖片描述
在這裏插入圖片描述
對寄存器 0 和寄存器 1 的讀寫可以實現對網卡的管理,清單 1 列出了部分 PHY 管理寄存器以及控制寄存器和狀態寄存器的各個 bit 的定義。

Linux內核中的/kernel/drivers/net/Mii.h, 定義 PHY 管理寄存器。

#define MII_BMCR            0x00        /* Basic mode control register */ 
#define MII_BMSR            0x01        /* Basic mode status register  */ 
#define MII_PHYSID1         0x02        /* PHYS ID 1                   */ 
#define MII_PHYSID2         0x03        /* PHYS ID 2                   */ 
#define MII_ADVERTISE       0x04        /* Advertisement control reg   */ 
#define MII_LPA             0x05        /* Link partner ability reg    */ 
#define MII_EXPANSION       0x06        /* Expansion register          */ 
#define MII_CTRL1000        0x09        /* 1000BASE-T control          */ 
... 
 
/* Basic mode control register. */ 
#define BMCR_RESV               0x003f  /* Unused...                   */ 
#define BMCR_SPEED1000          0x0040  /* MSB of Speed (1000)         */ 
#define BMCR_CTST               0x0080  /* Collision test              */ 
#define BMCR_FULLDPLX           0x0100  /* Full duplex                 */ 
#define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */ 
#define BMCR_ISOLATE            0x0400  /* Disconnect DP83840 from MII */ 
#define BMCR_PDOWN              0x0800  /* Powerdown the DP83840       */ 
#define BMCR_ANENABLE           0x1000  /* Enable auto negotiation     */ 
#define BMCR_SPEED100           0x2000  /* Select 100Mbps              */ 
#define BMCR_LOOPBACK           0x4000  /* TXD loopback bits           */ 
#define BMCR_RESET              0x8000  /* Reset the DP83840           */ 
 
/* Basic mode status register. */ 
#define BMSR_ERCAP              0x0001  /* Ext-reg capability          */ 
#define BMSR_JCD                0x0002  /* Jabber detected             */ 
#define BMSR_LSTATUS            0x0004  /* Link status                 */ 
#define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */ 
#define BMSR_RFAULT             0x0010  /* Remote fault detected       */ 
#define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */ 
#define BMSR_RESV               0x00c0  /* Unused...                   */ 
#define BMSR_ESTATEN        0x0100      /* Extended Status in R15 */ 
#define BMSR_100FULL2       0x0200      /* Can do 100BASE-T2 HDX */ 
#define BMSR_100HALF2       0x0400      /* Can do 100BASE-T2 FDX */ 
#define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */ 
#define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */ 
#define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */ 
#define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */ 
#define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */

通過 MDC/MDIO 讀寫 MII 寄存器的具體實現

在本文的前面部分介紹過 MDC/MDIO 的工作流程,網卡驅動程序中的 MDIO 讀寫函數 mdio_read 和 mdio_write,這些函數的具體實現是在各個網卡的驅動程序文件中完成的,都遵從 IEEE802.3 MDIO 的幀格式。典型的幀格式是第 22 條款中定義的格式:
在這裏插入圖片描述

長度(bit) 說明
ST 2bits 01b
OP 2bits 操作碼,
PHYADR 5bits PHY ID
REGADR 5bits 寄存器地址
TA 2 bits 狀態轉換域,讀操作爲 X0b, 寫操作爲 10b
DATA 16 bits 數據

Linux驅動解析

上文說了,網卡硬件一般包括MAC控制器和PHY,一個處理數據鏈路層的數據,一個處理物理層的數據。有的網卡會將MAC和PHY集成到一起,然後通過PCI總線與CPU連接;另外的網卡,MAC控制器集成到SoC芯片,PHY作爲單獨芯片,然後,兩者通過MII或者RMII接口進行連接。

嵌入式Linux開發模式下, 網卡的硬件架構一般都是第二種方式,即MAC與PHY是獨立的。至於網卡的驅動也分爲兩部分實現:MAC控制器驅動由SoC廠商開發,PHY芯片驅動由PHY廠商開發,當然,驅動之間要完成通信,必須嚴格按照IEEE802.3制定的協議。

涉及到具體的芯片驅動程序,其實現一般比較複雜,實現方式一般都是按照網絡架構實現具體的接口,本文不會去具體解析驅動的實現代碼,只是通過幾個實例來描述一下,如何實現網卡的控制。

ifconfig

ifconfig一般用來簡單的管理網卡,例如,查看狀態,配置ip、掩碼、網關,up/down網卡等。那麼,對於一塊具體的PHY或者MAC,ifconfig是如何實現管理的呢?下面通過一個接口調用圖說明一下。
在這裏插入圖片描述

  • ifconfig與網卡交互通過ioctl系統調用實現。
  • 內核網絡子系統預定義了很多命令,比如,SIOCGIFFLAGS用於配置網卡狀態,比如up/down。
  • PHY和MAC驅動通過實現具體的接口來爲上層的ifconfig提供服務。
  • MAC驅動實現最終與PHY通信的MII接口,通過mdio和mdio_read可以實現PHY的管理,比如,設置傳輸速率、獲取link status等等。

內核的/include/uapi/linux/sockios.h定義了socket配置命令:

/* Socket configuration controls. */
#define SIOCGIFNAME	0x8910		/* get iface name		*/
#define SIOCSIFLINK	0x8911		/* set iface channel		*/
#define SIOCGIFCONF	0x8912		/* get iface list		*/
#define SIOCGIFFLAGS	0x8913		/* get flags			*/
#define SIOCSIFFLAGS	0x8914		/* set flags			*/
#define SIOCGIFADDR	0x8915		/* get PA address		*/
#define SIOCSIFADDR	0x8916		/* set PA address		*/
#define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
#define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
#define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
#define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
#define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
#define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/
#define SIOCGIFMETRIC	0x891d		/* get metric			*/
#define SIOCSIFMETRIC	0x891e		/* set metric			*/
#define SIOCGIFMEM	0x891f		/* get memory address (BSD)	*/
#define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
#define SIOCGIFMTU	0x8921		/* get MTU size			*/
#define SIOCSIFMTU	0x8922		/* set MTU size			*/
#define SIOCSIFNAME	0x8923		/* set interface name */
#define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
#define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
#define SIOCSIFENCAP	0x8926		
#define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
#define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
#define SIOCSIFSLAVE	0x8930
#define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
#define SIOCDELMULTI	0x8932
#define SIOCGIFINDEX	0x8933		/* name -> if_index mapping	*/
#define SIOGIFINDEX	SIOCGIFINDEX	/* misprint compatibility :-)	*/
#define SIOCSIFPFLAGS	0x8934		/* set/get extended flags set	*/
#define SIOCGIFPFLAGS	0x8935
#define SIOCDIFADDR	0x8936		/* delete PA address		*/
#define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
#define SIOCGIFCOUNT	0x8938		/* get number of devices */

#define SIOCGIFBR	0x8940		/* Bridging support		*/
#define SIOCSIFBR	0x8941		/* Set bridging options 	*/

#define SIOCGIFTXQLEN	0x8942		/* Get the tx queue length	*/
#define SIOCSIFTXQLEN	0x8943		/* Set the tx queue length 	*/

/* SIOCGIFDIVERT was:	0x8944		Frame diversion support */
/* SIOCSIFDIVERT was:	0x8945		Set frame diversion options */

#define SIOCETHTOOL	0x8946		/* Ethtool interface		*/

#define SIOCGMIIPHY	0x8947		/* Get address of MII PHY in use. */
#define SIOCGMIIREG	0x8948		/* Read MII PHY register.	*/
#define SIOCSMIIREG	0x8949		/* Write MII PHY register.	*/

問題解決

回到文章開始時問題,1)系統無法檢測到網線的插拔事件;2)通過ifconfig配置網絡設備提示:ifconfig: SIOCSIFFLAGS: No such device。

通過上面的講解,可以猜測,可能是MAC驅動程序不能與PHY硬件進行正常的通信,從而導致上述的問題。那麼,下一步就是要檢查DTS中關於MAC的配置,對於本文中板子,IMX6UL,其配置信息如下:

fec1: ethernet@02188000 {                                                                                                                                                                               
                compatible = "fsl,imx6ul-fec", "fsl,imx6q-fec";
                reg = <0x02188000 0x4000>;
                interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
                         <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&clks IMX6UL_CLK_ENET>,
                     <&clks IMX6UL_CLK_ENET_AHB>,
                     <&clks IMX6UL_CLK_ENET_PTP>,
                     <&clks IMX6UL_CLK_ENET_REF>,
                     <&clks IMX6UL_CLK_ENET_REF>;
                clock-names = "ipg", "ahb", "ptp",
                          "enet_clk_ref", "enet_out";
                stop-mode = <&gpr 0x10 3>;
                fsl,num-tx-queues=<1>;
                fsl,num-rx-queues=<1>;
                fsl,magic-packet;
                fsl,wakeup_irq = <0>; 
                status = "disabled";
                pinctrl-names = "default";
			    pinctrl-0 = <&pinctrl_enet1>;
			    phy-mode = "rmii";
			    phy-handle = <&ethphy0>;                                                                                                                                                                                        
			    status = "okay";
			                    
			    mdio {             
			         #address-cells = <1>;
			         #size-cells = <0>;
			                    
			         ethphy0: ethernet-phy@1 {
			             compatible = "ethernet-phy-ieee802.3-c22";
			             reg = <0>;
			         };         
			   };   
    };   

這裏需要關注的是:

  1. phy-mode:指定MAC和PHY之間的接口模式,這裏用的是rmii模式;
  2. phy-handle:指定MAC與PHY之間通信的配置信息;
  3. mdio:關於MDC/MDIO通信相關配置信息;

可以參照PHY的datasheet和具體的原理圖,查看上面幾項配置信息,是否正確。最終發現,mdio中的reg項配置錯誤,該項指定了PHY的地址,用於MAC和PHY之間的通信。本文用到的KSZ8081RNB這款PHY芯片,默認PHY地址爲1,而mdio中的reg將其配置成了0,所以導致MAC和PHY之間無法通信,從而導致上述兩個問題。修改reg爲1之後,問題解決。

總結

本文通過一個具體的問題作爲引子,主要講解了關於網卡的一般架構,其都是遵循IEEE 802.3協議的,進而又分析了MAC和PHY之間的通信接口MII/RMII,之後,通過linux系統下ifconfig的實現框架,講解了應用程序如何與具體網卡芯片進行通信,最後,分析如何解決開始遇到的問題。這裏得出幾點結論:

  1. 遇到問題時,要學會由現象到本質的分析方法。即使一時解決了問題,也需要保持一顆好奇心,想方設法去探究問題的根本。只有這樣,才能做到“知其然,知其所以然”,再遇到類似問題,才能從容應對。
  2. 由MAC和PHY之間通信接口MII,可以看到,良好的接口可以極大的降低系統的複雜度,降低系統之間耦合。無論軟件和硬件,這條規律都是適用的。
  3. 由ifconfig的實現架構,可以推而廣之去分析類似Linux應用工具,比如,ethtool。
  4. 最後,stay hungry stay foolish!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章