1. 程式人生 > >Linux網路子系統之---- PHY 配置

Linux網路子系統之---- PHY 配置

 MII即媒體獨立介面,也叫介質無關介面。

它包括一個數據介面,以及一個MAC和PHY之間的管理介面(圖1)。

資料介面包括分別用於傳送器和接收器的兩條獨立通道。每條通道都有自己的資料、時鐘和控制訊號。MII資料介面總共需16個訊號。

管理介面是個雙訊號介面:一個是時鐘訊號,另一個是資料訊號。通過管理介面,上層能監視和控制PHY。

RMII口是用兩根線來傳輸資料的,

MII口是用4根線來傳輸資料的,

GMII是用8根線來傳輸資料的。

GMII (Gigabit MII)

GMII是8bit並行同步收發介面,採用8位介面資料,工作時鐘125MHz,因此傳輸速率可達1000Mbps。同時相容MII所規定的10/100 Mbps工作方式。

GMII介面資料結構符合IEEE乙太網標準。該介面定義見IEEE802.3-2000。

傳送器:

◇ GTXCLK——吉位元TX..訊號的時鐘訊號(125MHz)

◇ TXCLK——10/100M訊號時鐘

◇ TXD[7..0]——被髮送資料  -------  mii  為4,所以一個通道是8,兩個是16

◇ TXEN——傳送器使能訊號

◇ TXER——傳送器錯誤(用於破壞一個數據包)

注:在千兆速率下,向PHY提供GTXCLK訊號,TXD、TXEN、TXER訊號與此時鐘訊號同步。否則,在10/100M速率下,PHY提供 TXCLK時鐘訊號,其它訊號與此訊號同步。其工作頻率為25MHz(100M網路)或2.5MHz(10M網路)。

接收器:

◇ RXCLK——接收時鐘訊號(從收到的資料中提取,因此與GTXCLK無關聯)

◇ RXD[7..0]——接收資料

◇ RXDV——接收資料有效指示

◇ RXER——接收資料出錯指示

◇ COL——衝突檢測(僅用於半雙工狀態)

管理配置

◇ MDC——配置介面時鐘

◇ MDIO——配置介面I/O

管理配置介面控制PHY的特性。該介面有32個暫存器地址,每個地址16位。其中前16個已經在“IEEE 802.3,2000-22.2.4Management Functions”中規定了用途,其餘的則由各器件自己指定。

MII/RMII只是一種介面,對於10M線速,MII的速率是2.5M,RMII則是5M;對於100M線速,MII的速率是25M,RMII則是50M。

SGMII--Serial Gigabit Media IndependentInterface

SGMII是PHY與MAC之間的介面,類似與GMII和RGMII,只不過GMII和RGMII都是並行的,而且需要隨路時鐘,PCB佈線相對麻煩,而且不適應背板應用。

而SGMII是序列的,不需要提供另外的時鐘,MAC和PHY都需要CDR去恢復時鐘。另外SGMII是有8B/10b編碼的,速率是1.25G

在 linux 配置PHY

drivers/net/phy

配置的引數  自適應, 1000M, 全雙工。

phydev-> autonet, speed, duplex.

1. MDIO簡介

  The MDIO interface is a simple, two-wire, serial interface to connect a management entity and a managed PHY for the purposes of controlling the PHY and gathering status from the PHY.
   The two lines include the MDC line [Management Data Clock], and the MDIO line [Management Data Input/Output]. The clock is point-to-point, while the data line is a bi-directional multi-drop interface.
   The data line is Tri-state able and can drive 32 devices.

   MDIO介面,MAC與PHY間的管理介面(MII是資料介面),有2根線:時鐘線MDC,資料線MDIO(雙向)



   MDIO工作流程:
    * Preamle(PRE)       在沒有傳輸資料的空閒狀態時,資料線MDIO處於高阻態(一直為1)。
    * Start of Frame(ST) MAC驅動MDIO線,出現一個2bit的開始標識碼(01)。
    * Operation Code(OP) MAC驅動MDIO線,出現一個2bit資料來標識是讀操作(10)還是寫操作(01)。
    * PHY Address(PHYAD) MAC驅動MDIO線,出現一個5bit資料標識PHY的地址。
    * Reg Address(REGAD) MAC驅動MDIO線,出現一個5bitPHY暫存器地址。
    * Turnaround(TA)     寫操作的話,MAC驅動MDIO線,出現10
                         讀操作的話,MDIO pin of MAC must be put in high-impedance state
                                     在第二個週期,PHY驅動MDIO線,出現0

    * Data               MDIO序列讀出/寫入16bit的暫存器資料。

    * MDIO恢復成空閒狀態,同時MDIO進入高阻狀態。


    下面是PHY晶片 BCM5461 的一個例子:


2. PowerPC對MDIO的支援

PowerPC操作MDIO時,涉及以下暫存器:
MIIMCFG  配置暫存器
MIIMCOM  命令暫存器
MIIMADD  地址暫存器
MIIMCON  控制暫存器
MIIMSTAT 狀態暫存器
MIIMIND  指示暫存器

以MPC8560舉例,這些暫存器在CCSR中的位置如下:






2.1 MIIMCFG:配置暫存器


ResetMgmt:   用於重置MDIO模組
MgmtClockSet:時鐘設定,是CCB的 2的n次方之一



2.2 MIIMCOM  命令暫存器


ReadCycle: 0->1 觸發MDIO讀時序


2.3 MIIMADD  地址暫存器


PHYaddr:PHY地址,共5bit,系統最多聯31個PHY(地址0為保留)
REGaddr:暫存器地址,共5bit,一個PHY上最多32個暫存器地址(可以使用shadow value技術,訪問更多的暫存器)


2.4 MIIMCON  控制暫存器



PHYcontrol:在寫流程時,這裡存放要寫入暫存器的值


2.5 MIIMSTAT 狀態暫存器



PHYstatus:讀流程時,PHY reg的內容會放到此

2.6 MIIMIND  指示暫存器


NotVal:若置1,表示讀流程結束,可以去讀MIIMSTAT
Scan:  若置1,表示掃描流程進行中
Busy:  只有置0時,才能進行新的讀寫流程



3. linux中MDIO的實現

讀寫PHY暫存器時通過2個函式 

phy_read()和phy_write(),

最終呼叫
int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value)

引數regs就是MDIO相關暫存器:
  1. struct gfar_mii {
  2.     u32 miimcfg; /* 0x.520 - MII Management Config Register */
  3.     u32 miimcom; /* 0x.524 - MII Management Command Register */
  4.     u32 miimadd; /* 0x.528 - MII Management Address Register */
  5.     u32 miimcon; /* 0x.52c - MII Management Control Register */
  6.     u32 miimstat; /* 0x.530 - MII Management Status Register */
  7.     u32 miimind; /* 0x.534 - MII Management Indicator Register */
  8. };
引數mii_id,就是PHY的id
引數regnum,就是暫存器地址


上程式碼,簡單不解釋
  1. int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
  2. {
  3.     u16 value;
  4.     /* Set the PHY address and the register address we want to read */
  5.     gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
  6.     /* Clear miimcom, and then initiate a read */
  7.     gfar_write(&regs->miimcom, 0);
  8.     gfar_write(&regs->miimcom, MII_READ_COMMAND);
  9.     /* Wait for the transaction to finish */
  10.     while (gfar_read(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
  11.         cpu_relax();
  12.     /* Grab the value of the register from miimstat */
  13.     value = gfar_read(&regs->miimstat);
  14.     return value;
  15. }


  1. int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id,
  2.               int regnum, u16 value)
  3. {
  4.     /* Set the PHY address and the register address we want to write */
  5.     gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
  6.     /* Write out the value we want */
  7.     gfar_write(&regs->miimcon, value);
  8.     /* Wait for the transaction to finish */
  9.     while (gfar_read(&regs->miimind) & MIIMIND_BUSY)
  10.         cpu_relax();
  11.     return 0;
  12. }


核心啟動時的準備工作

4.1 初始化網路相關的全域性資料結構,並掛載處理網路相關軟中斷的鉤子函式
start_kernel()
    --> rest_init()
        --> do_basic_setup()
            --> do_initcall
               -->net_dev_init

__init net_dev_init()
{
    //每個CPU都有一個CPU私有變數 _get_cpu_var(softnet_data)
    //_get_cpu_var(softnet_data).poll_list很重要,軟中斷中需要遍歷它的

    for_each_possible_cpu(i) {
        struct softnet_data *queue;
        queue = &per_cpu(softnet_data, i);
        skb_queue_head_init(&queue->input_pkt_queue);
        queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
        queue->backlog.poll = process_backlog;
        queue->backlog.weight = weight_p;
    }
    open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在軟中斷上掛網路傳送handler
    open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在軟中斷上掛網路接收handler
}
   4.2 載入網路裝置的驅動
NOTE:這裡的網路裝置是指MAC層的網路裝置,即TSEC和PCI網絡卡(bcm5461是phy)
在網路裝置驅動中建立net_device資料結構,並初始化其鉤子函式 open(),close() 等
掛載TSEC的驅動的入口函式是 gfar_probe

// 平臺裝置 TSEC 的資料結構
static struct platform_driver gfar_driver = {
    .probe = gfar_probe,
    .remove = gfar_remove,
    .driver = {
        .name = "fsl-gianfar",
    },
};

int gfar_probe(struct platform_device *pdev)
{
    dev = alloc_etherdev(sizeof (*priv)); // 建立net_device資料結構

    dev->open = gfar_enet_open;
    dev->hard_start_xmit = gfar_start_xmit;
    dev->tx_timeout = gfar_timeout;
    dev->watchdog_timeo = TX_TIMEOUT;
#ifdef CONFIG_GFAR_NAPI
    netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT); //軟中斷裡會呼叫poll鉤子函式
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
    dev->poll_controller = gfar_netpoll;
#endif
    dev->stop = gfar_close;
    dev->change_mtu = gfar_change_mtu;
    dev->mtu = 1500;
    dev->set_multicast_list = gfar_set_multi;
    dev->set_mac_address = gfar_set_mac_address;
    dev->ethtool_ops = &gfar_ethtool_ops;
}


五、啟用網路裝置
5.1 使用者呼叫ifconfig等程式,然後通過ioctl系統呼叫進入核心
socket的ioctl()系統呼叫
    --> sock_ioctl()
        --> dev_ioctl()                              //判斷SIOCSIFFLAGS
          --> __dev_get_by_name(net, ifr->ifr_name)  //根據名字選net_device
             --> dev_change_flags()                  //判斷IFF_UP
                --> dev_open(net_device)             //呼叫open鉤子函式 

對於TSEC來說,掛的鉤子函式是 gfar_enet_open(net_device)

5.2 在網路裝置的open鉤子函式裡,分配接收bd,掛中斷ISR(包括rx、tx、err),對於TSEC來說

gfar_enet_open
    --> 給Rx Tx Bd 分配一致性DMA記憶體 
    --> 把Rx Bd的“EA地址”賦給資料結構,實體地址賦給TSEC暫存器
    --> 把Tx Bd的“EA地址”賦給資料結構,實體地址賦給TSEC暫存器
    --> 給 tx_skbuff 指標陣列 分配記憶體,並初始化為NULL
    --> 給 rx_skbuff 指標陣列 分配記憶體,並初始化為NULL

    --> 初始化Tx Bd
    --> 初始化Rx Bd,提前分配儲存乙太網包的skb,這裡使用的是一次性dma對映
       (注意:#define DEFAULT_RX_BUFFER_SIZE  1536保證了skb能存一個乙太網包)
        rxbdp = priv->rx_bd_base;
        for (i = 0; i < priv->rx_ring_size; i++) {
            struct sk_buff *skb = NULL;
            rxbdp->status = 0;
          //這裡真正分配skb,並且初始化rxbpd->bufPtr, rxbdpd->length
skb = gfar_new_skb(dev, rxbdp);    
            priv->rx_skbuff[i] = skb;

            rxbdp++;
        }
        rxbdp--;
        rxbdp->status |= RXBD_WRAP; // 給最後一個bd設定標記WRAP標記

    --> 註冊TSEC相關的中斷handler: 錯誤,接收,傳送
        request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
        request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包傳送完
        request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev)  //包接收完

    -->gfar_start(net_device)
        // 使能Rx、Tx
        // 開啟TSEC的 DMA 暫存器
        // Mask 掉我們不關心的中斷event


最終,TSEC相關的Bd等資料結構應該是下面這個樣子的

六、中斷裡接收乙太網包

 TSEC的RX已經使能了,網路資料包進入記憶體的流程為:
    網線 --> Rj45網口 --> MDI 差分線
         --> bcm5461(PHY晶片進行數模轉換) --> MII匯流排 
         --> TSEC的DMA Engine 會自動檢查下一個可用的Rx bd 
         --> 把網路資料包 DMA 到 Rx bd 所指向的記憶體,即skb->data

接收到一個完整的乙太網資料包後,TSEC會根據event mask觸發一個 Rx 外部中斷。
cpu儲存現場,根據中斷向量,開始執行外部中斷處理函式do_IRQ()

do_IRQ 虛擬碼
{
   上半部處理硬中斷
       檢視中斷源暫存器,得知是網路外設產生了外部中斷
       執行網路裝置的rx中斷handler(裝置不同,函式不同,但流程類似,TSEC是gfar_receive
          1. mask 掉 rx event,再來資料包就不會產生rx中斷
          2. 給napi_struct.state加上 NAPI_STATE_SCHED 狀態
          3. 掛網路裝置自己的napi_struct結構到cpu私有變數_get_cpu_var(softnet_data).poll_list
          4. 觸發網路接收軟中斷
    下半部處理軟中斷
        依次執行所有軟中斷handler,包括timer,tasklet等等
        執行網路接收的軟中斷handler net_rx_action
          1. 遍歷cpu私有變數_get_cpu_var(softnet_data).poll_list 
          2. 取出poll_list上面掛的napi_struct 結構,執行鉤子函式napi_struct.poll()
(裝置不同,鉤子函式不同,流程類似,TSEC是gfar_poll)
          3. 若poll鉤子函式處理完所有包,則開啟rx event mask,再來資料包的話會產生rx中斷
          4. 呼叫napi_complete(napi_struct *n)
             把napi_struct 結構從_get_cpu_var(softnet_data).poll_list 上移走
             同時去掉 napi_struct.state 的 NAPI_STATE_SCHED 狀態
}

6.1 TSEC的接收中斷處理函式
gfar_receive
{
#ifdef CONFIG_GFAR_NAPI
    // test_and_set當前net_device的napi_struct.state 為 NAPI_STATE_SCHED
    // 在軟中斷裡呼叫 net_rx_action 會檢查狀態 napi_struct.state

    if (netif_rx_schedule_prep(dev, &priv->napi)) {  
        tempval = gfar_read(&priv->regs->imask);            
        tempval &= IMASK_RX_DISABLED; //mask掉rx,不再產生rx中斷
        gfar_write(&priv->regs->imask, tempval);    
        // 將當前net_device的 napi_struct.poll_list 掛到
        // CPU私有變數__get_cpu_var(softnet_data).poll_list 上,並觸發軟中斷
        // 所以,在軟中斷中呼叫 net_rx_action 的時候,就會執行當前net_device的
        // napi_struct.poll()鉤子函式,即 gfar_poll()

__netif_rx_schedule(dev, &priv->napi);   
    } 
#else

相關推薦

Linux網路子系統---- PHY 配置

 MII即媒體獨立介面,也叫介質無關介面。 它包括一個數據介面,以及一個MAC和PHY之間的管理介面(圖1)。 資料介面包括分別用於傳送器和接收器的兩條獨立通道。每條通道都有自己的資料、時鐘和控制訊號。MII資料介面總共需16個訊號。 管理介面是個雙訊號介面:一個是時鐘訊

Linux網絡子系統---- PHY 配置

向量 driver otv wan probe clu all 分配 ++ MII即媒體獨立接口,也叫介質無關接口。 它包括一個數據接口,以及一個MAC和PHY之間的管理接口(圖1)。 數據接口包括分別用於發送器和接收器的兩條獨立信道。每條信道都有自己的數據、時鐘和控制信

Linux時間子系統八:動態時鐘框架(CONFIG_NO_HZ、tickless)

sleep file rup linux時間 load 曾經 大致 獲取 conf 在前面章節的討論中,我們一直基於一個假設:Linux中的時鐘事件都是由一個周期時鐘提供,不管系統中的clock_event_device是工作於周期觸發模式,還是工作於單觸發模式,也不管定時

Linux時間子系統四:定時器的引擎:clock_event_device

到來 開始 register 工作模式 統一 10個 net 說過 序列 早期的內核版本中,進程的調度基於一個稱之為tick的時鐘滴答,通常使用時鐘中斷來定時地產生tick信號,每次tick定時中斷都會進行進程的統計和調度,並對tick進行計數,記錄在一個jiffies變量

Linux時間子系統六:高精度定時器(HRTIMER)的原理和實現

3.4 size 屬於 running return repr 而是 復雜度 ctu 上一篇文章,我介紹了傳統的低分辨率定時器的實現原理。而隨著內核的不斷演進,大牛們已經對這種低分辨率定時器的精度不再滿足,而且,硬件也在不斷地發展,系統中的定時器硬件的精度也越來越高,這也給

Linux時間子系統七:定時器的應用--msleep(),hrtimer_nanosleep()

get 關系 警告 mov signed num wakeup sch switch 我們已經在前面幾章介紹了低分辨率定時器和高精度定時器的實現原理,內核為了方便其它子系統,在時間子系統中提供了一些用於延時或調度的API,例如msleep,hrtimer_nanosleep

Linux時間子系統二:Alarm Timer

數據 類型 oid mtime orm 分別是 type mon 超時 一、前言 嚴格來講Alarm Timer也算POSIX Timer一部分,包含兩種類型CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM。分別是在CLOCK_REALTI

Android驅動 Linux Input子系統TP——A/B(Slot)協議

翻譯 莫名其妙 屬性 設備 事件 ng- 點數據 希望 top utm_source=tuicool&utm_medium=referral">點擊打開鏈接將A/B協議這部分單獨拿出來說一方面是由於這部分內容是比較easy忽視的。周圍大多數用到input子系統的

linux音訊子系統alsa asoc層

ALSA SoC層概述 ALSA片上系統(ASoC)層的總體專案目標是為嵌入式片上系統處理器(例如pxa2xx,au1x00,iMX等)和行動式音訊編解碼器提供更好的ALSA支援。在ASoC子系統之前,核心對SoC音訊有一些支援,但它有一些限制: - 編解碼器驅動程式通常與底層SoC CPU緊密

linux 網路程式設計廣播

linux 網路程式設計之廣播 轉載:https://blog.csdn.net/qdlovecsj/article/details/8805483 廣播方式主要是指使用UDP套介面傳送資料,傳送資料的目標地址不是普通的地址,而是所指定網路的廣播地址。 什麼是廣播地址?是指IP地

Linux網路程式設計TCP程式設計

直接上程式碼如下所示: 1、標頭檔案定義 #ifndef __HEAD_NET_H__ #define __HEAD_NET_H__ #include <stdio.h> #include <string.h> #include <stdlib.h>

Linux網路程式設計IO模型

本文基於IO訪問中存在的兩個階段詳細介紹了Linux產生的五種IO模型。 上篇文章回顧: 小米開源監控Open-Falcon收錄汽車之家貢獻的Win版Agent 同步與非同步 同步是指一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成後

linux 輸入子系統電阻式觸控式螢幕驅動

一、輸入子系統情景回憶ING...... 在Linux中,輸入子系統是由輸入子系統裝置驅動層、輸入子系統核心層(Input Core)和輸入子系統事件處理層(Event Handler)組成。其中裝置驅動層提供對硬體各暫存器的讀寫訪問和將底層硬體對使用者輸入訪問的響應轉換為標準的輸入事件,再

linux網路程式設計多程序併發伺服器

1)使用多程序併發伺服器考慮的因素:       (1)父程序描述最大檔案描述符的個數(父程序需要關閉accept返回的新檔案描述符)       (2)系統內可建立程序的個數(與記憶體大小相關)       (3)程序建立過多是否降低整體服務效能 2)多程序建立併發

linux網路程式設計TCP狀態轉換及埠複用

(1)TCP狀態轉換圖               其中圖中分為三種狀態:實線代表的主動發起連線,虛線代表的被動發起連線,細實線代表的可以雙向發起連線的狀態。 主動發起連線方狀態變化:1)主動發起連線的一方傳送SYN標誌位,進入SYN_SENT狀態,等待接收被髮起連線方

linux 網路涉及的所有配置檔案詳解

linux 網路涉及的所有配置檔案詳解 Linux 為 配 置 網 絡 提 供 了 許 多 工 具 , 其 中 有 圖 形 界 面 的 ( 如NetworkManager1)、也有偽圖形介面(如 system-config-network 2)的。雖然使用這些工具來配置網路會很方便,但是由於各個發行版本的

Linux網路程式設計高階併發伺服器(轉)

1. 介紹 在上一節,我們介紹了Linux簡單的併發伺服器,通過在伺服器端建立多個子程序,來接收客戶端的請求,實現併發處理,但這種方式明顯有缺陷,伺服器並不知道客戶端請求的數量,所以事先建立的程序數不好確定。所以,這裡介紹三種高階併發伺服器模式。第一種是伺服器端統一

linux網路程式設計TCP介面詳解

socket int socket(int domain, int type, intprotocol);     監聽套接字描述符由socket建立,隨後用作bind和listen的第一個引數。一個伺服器通常僅建立一個監聽套接字,他在該伺服器的生命週期內一直存在。 c

linux網路程式設計用socket實現簡單客戶端和服務端的通訊(基於TCP)

一、介紹基於TCP協議通過socket實現網路程式設計常用API 1、讀者如果不是很熟悉,可以先看我之前寫的幾篇部落格,有socket,地址結構的理解,更加方便讀者理解 地址分別是: 2、socket(TCP)程式設計API簡介 1)、socket int s

linux網路程式設計RTP協議

以下內容取自: 本機通訊:https://www.cnblogs.com/lidabo/p/4160138.html(RTP協議傳輸)https://www.cnblogs.com/lidabo/p/4160145.html(RTP協議傳輸) 非本機:http://velep.com/arc