SylixOS MII總線層解析

1.原理概述 框架

1.1 網卡驅動概述 函數

一塊以太網網卡包括OSI模型的兩個層:物理層和數據鏈路層。數據鏈路層的芯片簡稱爲MAC控制器,物理層的芯片簡稱爲PHY。測試

MAC主要負責控制與鏈接物理層的物理介質。在發送數據時,MAC先判斷是否能夠發送數據,若是能夠發送,給數據加上控制信息,最終將數據及控制信息按規定的格式發送到物理層;在接收數據的時候,MAC先判斷信息是否發生傳輸錯誤,若是沒有錯誤,去掉控制信息發送至LLC層。該層協議由IEEE-802.3以太網標準定義。ui

PHY是物理接口收發器,它實現物理層。IEEE-802.3標準定義了以太網PHY。在發送數據時,收到MAC數據,把並行數據轉化爲串行流數據,再按照物理層的編碼規則編碼,再變爲模擬信號發送數據。收數據時流程反之。PHY還有個重要的功能是實現CSMA/CD的部分功能。編碼

PHY和MAC之間的關係是PCI總線接MAC總線,MAC接PHY,PHY接網線。rest

在以太網網卡驅動中,徹底能夠將PHY芯片的驅動抽象,作成通用接口。(IEEE802.3定義PHY芯片地址0-15寄存器的功能,地址16-31寄存器留給芯片製造商自由定義)。code

2.技術實現 接口

2.1 PHY驅動目錄 ip

PHY芯片驅動在SylixOS的Base中已經給出,如圖 3.1所示。ci

圖 3.1 PHY驅動目錄

SylixOS提供的PHY芯片驅動能支持10Mb,100Mb,1000Mb的連接能力。

2.2 PHY驅動框架

PHY驅動大體流程爲:

初始化組件庫(信號量,定時器)。

選擇自動查找/指定查找PHY設備。

選擇自動協商/指定連接模式。

啓用定時器每隔一段時間查詢連接狀態。

如圖 3.2所示。

圖 3.2 PHY驅動框架

在網卡驅動中,先對程序清單 3.1進行傳參,再調用API_MiiPhyInit接口就能完成PHY芯片初始化。

程序清單 3.1 傳參

pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncWrite    = (FUNCPTR)__miiPhyWrite;
    pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncRead     = (FUNCPTR)__miiPhyRead;
    pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncLinkDown = (FUNCPTR)__miiLinkStatus;
    pmiidrv->MIID_phydev.PHY_pvMacDrv       = pmiidrv;
    pmiidrv->MIID_phydev.PHY_ucPhyAddr      = ENET_PHYADDR;
    pmiidrv->MIID_phydev.PHY_uiPhyID        = FEC_ENET_PHYID;
    pmiidrv->MIID_phydev.PHY_uiPhyIDMask    = 0x00000000;               /*  同系列芯片不一樣信號識別    */
    pmiidrv->MIID_phydev.PHY_uiTryMax       = 100;
    pmiidrv->MIID_phydev.PHY_uiLinkDelay    = 100;                      /*  延時100毫秒自動協商過程   */
    pmiidrv->MIID_phydev.PHY_uiPhyFlags     = MII_PHY_AUTO |            /*  自動協商標誌              */
                                              MII_PHY_FD   |            /*  全雙工模式                */
                                              MII_PHY_100  |            /*  100Mbit                   */
                                              MII_PHY_10   |            /*  10Mbit                    */
                                              MII_PHY_HD   |            /*  半雙工模式                */
                                              MII_PHY_MONITOR;          /*  啓用自動監視功能          */

MAC能對PHY芯片進行讀寫操做(miiPhyWrite函數,miiPhyRead函數)。

連接狀態變化函數(miiLinkStatus函數,打印連接信息並進行相關操做)。

控制參數傳參(PHY驅動會根據傳入參數進行不一樣操做,如是否開啓自動協商,選擇速度/雙工模式,開啓自動監視功能)。

2.3 具體實現

2.3.1 API_MiiPhyInit函數實現

API_MiiPhyInit函數會根據傳入參數分別執行初始化組件庫,查找PHY設備,設置連接能力。

PHY驅動提供鏈表將有效PHY設備加入到MII鏈表,能對多個PHY芯片進行操做。

如程序清單 3.2所示。

程序清單 3.2 API_MiiPhyInit函數實現

INT API_MiiPhyInit (PHY_DEV *pPhyDev)
{
……
    if (API_MiiLibInit() == MII_ERROR) {
        return  (MII_ERROR);
    }
    if (pPhyDev->PHY_ucPhyAddr == 0) {                                  /*  Auto scan phy device        */
        if (API_MiiPhyScan(pPhyDev) == MII_ERROR) {
            _DebugHandle(__ERRORMESSAGE_LEVEL, "can not find phy device.\r\n");
            return  (MII_ERROR);
        }
    } else {                                                            /*  Test specific phy device    */
        if (API_MiiPhyProbe(pPhyDev) != MII_OK) {
            MII_DEBUG_ADDR("can not find phy device. addr[%02x]\r\n",
                           pPhyDev->PHY_ucPhyAddr);
            return  (MII_ERROR);
        }
        if (API_MiiPhyDiagnostic(pPhyDev) != MII_OK) {
            return  (MII_ERROR);
        }
    }
……
    if (API_MiiPhyLinkSet(pPhyDev) != MII_OK) {
        MII_DEBUG_ADDR("mii: found phy [%02x], but Link-Down.\r\n",
                       pPhyDev->PHY_ucPhyAddr);
    }
	usPhyStatus = pPhyDev->PHY_usPhyStatus;					     /* Remember Link Status         */
												     /* Get The New Status		   */
……
    return  (iRet);                                                     /*  MII_ERROR or MII_OK         */
}

2.3.2 API_MiiLibInit函數實現

API_MiiLibInit函數初始化組件庫。大體爲建立信號量,定時器,並開啓定時器,檢測連接能力。(__miiPhyMonitor函數主要讀取PHY芯片狀態,更新狀態信息)。

如程序清單 3.3所示。

程序清單 3.3 API_MiiLibInit函數實現

INT API_MiiLibInit (VOID)
{
 ……
    _G_hMiiMSem = API_SemaphoreMCreate("mii_lock", LW_PRIO_DEF_CEILING,
                                       LW_OPTION_WAIT_PRIORITY | LW_OPTION_DELETE_SAFE |
                                       LW_OPTION_INHERIT_PRIORITY | LW_OPTION_OBJECT_GLOBAL,
                                       LW_NULL);                        /* Create MII Mutex Semaphore   */
    _G_hMiiTimer = API_TimerCreate("mii_timer", LW_OPTION_ITIMER | LW_OPTION_OBJECT_GLOBAL, LW_NULL); 
    if (_G_hMiiTimer == 0) {                                            /* Create mii timer             */
        return  (MII_ERROR);
    }
……
    if (API_TimerStart(_G_hMiiTimer,                                    /* Start Phy Monitor            */
                       (MII_LINK_CHK_DELAY * LW_TICK_HZ),
                       LW_OPTION_AUTO_RESTART,
                       (PTIMER_CALLBACK_ROUTINE)__miiPhyMonitor,
                       LW_NULL)) {
        API_SemaphoreMDelete(&_G_hMiiMSem);
        return  (MII_ERROR);
    }
……
    return  (MII_OK);
}

2.3.3 API_MiiPhyScan函數實現

API_MiiPhyScan函數自動查找PHY設備。大體爲從0-32對全部PHY設備地址進行查找,若存在設備,測試PHY是否有效。

如程序清單 3.4所示。

程序清單 3.4 API_MiiPhyScan函數實現

INT API_MiiPhyScan (PHY_DEV *pPhyDev)
{
……
    for (i = 0; i < MII_MAX_PHY_NUM; i++, pPhyDev->PHY_ucPhyAddr++) {
        iRet = API_MiiPhyProbe(pPhyDev);
        if (iRet != MII_OK) {
            continue;
        }
        if (API_MiiPhyDiagnostic(pPhyDev) != MII_OK) {
            return  (MII_ERROR);
        }
        return  (MII_OK);                                               /* Found a Valid PHY            */
    }
    return  (MII_PHY_NULL);
}

2.3.4 API_MiiPhyProbe,API_MiiPhyDiagnostic函數實現

API_MiiPhyProbe函數檢測PHY設備是否存在,大體爲讀取PHY寄存器ID,進行匹配,返回是否成功。如程序清單 3.5所示。

程序清單 3.5     API_MiiPhyProbe函數實現

INT API_MiiPhyProbe (PHY_DEV *pPhyDev)
{
……
    if (MII_READ(pPhyDev, MII_PHY_ID1_REG, &usID1) == MII_ERROR) {
        return  (MII_ERROR);
    }
    if (MII_READ(pPhyDev, MII_PHY_ID2_REG, &usID2) == MII_ERROR) {
        return  (MII_ERROR);
    }
    uiPhyID = usID1 | (usID2 << 16);
    if ((pPhyDev->PHY_uiPhyID & pPhyDev->PHY_uiPhyIDMask) !=
                     (uiPhyID & pPhyDev->PHY_uiPhyIDMask)) {
        return  (MII_PHY_NULL);                                         /* phyId不匹配                  */
    }
    return  (MII_OK);                                                   /* phyId匹配                    */
}

API_MiiPhyDiagnostic函數測試PHY是否有效,具體爲復位PHY是否成功,檢測PHY是否處於物理隔離狀態。如程序清單 3.6所示。

程序清單 3.6 API_MiiPhyDiagnostic函數實現

INT API_MiiPhyDiagnostic (PHY_DEV *pPhyDev)
{
……
    iRet = MII_WRITE(pPhyDev, ucRegAddr, usData);                       /* Reset the PHY                */
    if (iRet != MII_OK) {
        return  (MII_ERROR);
    }
    for (i = 0; i < pPhyDev->PHY_uiTryMax; i++) {
        API_TimeMSleep(pPhyDev->PHY_uiLinkDelay);
        if (MII_READ(pPhyDev, ucRegAddr, &usData) == MII_ERROR) {
            return  (MII_ERROR);
        }
}
……
    usData = MII_CTRL_NORM_EN;                                          /* re-enable the chip           */
    if (MII_WRITE(pPhyDev, ucRegAddr, usData) == MII_ERROR) {
        return  (MII_ERROR);
    }
    for (i = 0; i < pPhyDev->PHY_uiTryMax; i++) {
        API_TimeMSleep(pPhyDev->PHY_uiLinkDelay);
        if (MII_READ(pPhyDev, ucRegAddr, &usData) == MII_ERROR) {
            return  (MII_ERROR);
        }
    return  (MII_OK);
}

2.3.5 API_MiiPhyLinkSet函數實現

API_MiiPhyLinkSet函數設置PHY連接模式。如程序清單 3.7所示。

程序清單 3.7 API_MiiPhyLinkSet函數實現

INT API_MiiPhyLinkSet (PHY_DEV *pPhyDev)
{
……
    if (__miiAbilFlagUpdate(pPhyDev) == MII_ERROR) {
        return  (MII_ERROR);
    }
……
    iRet = API_MiiPhyModeSet(pPhyDev);
……
    return  (MII_OK);
}

API_MiiPhyModeSet函數根據參數選擇自動協商/手動設置。

如程序清單 3.8所示。

程序清單 3.8 API_MiiPhyModeSet函數實現

INT API_MiiPhyModeSet (PHY_DEV *pPhyDev)
{
    if (pPhyDev->PHY_uiPhyFlags & MII_PHY_AUTO) {                       /* AutoNegotiation enabled      */
       if (__miiAutoNegotiate(pPhyDev) == MII_OK) {
           return   (MII_OK);
       }
    } else {                                                            /* 未開啓自動協商功能           */
        if (__miiModeForce(pPhyDev) == MII_OK) {
            if (__miiFlagsHandle(pPhyDev) == MII_OK) {                  /* handle some flags            */
                return  (MII_OK);
            }
        }
    }
    return  (MII_ERROR);
}

__miiAutoNegotiate函數進行自動協商並檢查協商是否成功。__miiBasicCheck用於檢查是否link up/remote fault。如程序清單 3.9所示。

程序清單 3.9 __miiAutoNegotiate函數實現

static INT __miiAutoNegotiate (PHY_DEV *pPhyDev)
{
……
    /*
     * start the auto-negotiation process: return
     * only in case of fatal error.
     */
   iRet = __miiAutoNegStart(pPhyDev);
……
    /* check the negotiation was successful */
    if (!(pPhyDev->PHY_uiPhyFlags & MII_PHY_NWAIT_STAT)) {
        if (__miiAnCheck(pPhyDev) == MII_OK) {
            return  (MII_OK);
        }
    }
    
    return  (MII_ERROR);
}

__miiAutoNegStart函數開始自動協商。具體爲開始自動協商並根據傳入參數選擇等待自動協商結束/當即返回。如程序清單 3.10所示。

程序清單 3.10 __miiAutoNegStart函數實現

static INT __miiAutoNegStart (PHY_DEV *pPhyDev)
{
……
    /*
     * restart the auto-negotiation process
     */
    ucRegAddr = MII_CTRL_REG;
    usData    = (MII_CTRL_RESTART | MII_CTRL_AUTO_EN);
    if (MII_WRITE(pPhyDev, ucRegAddr, usData) != MII_OK) {
        return  (MII_ERROR);
    }
    /*
     * let's check the PHY status for completion
     */
    if (!(pPhyDev->PHY_uiPhyFlags & MII_PHY_NWAIT_STAT)) {
        ucRegAddr = MII_STAT_REG;
        do {                                                            /* spin until it is done        */
            API_TimeMSleep(pPhyDev->PHY_uiLinkDelay);
            if (i++ == pPhyDev->PHY_uiTryMax)
                break;
            if (MII_READ(pPhyDev, ucRegAddr, &usPhyStatus) != MII_OK) {
                return  (MII_ERROR);
            }
        } while ((usPhyStatus & MII_SR_AUTO_NEG) != MII_SR_AUTO_NEG);
…… 
    return  (MII_OK);
}

2.3.6 __miiModeForce函數實現

__miiModeForce函數根據傳入參數,設成指定連接模式(若多個參數,指定最高連接模式)。如程序清單 3.11所示。

程序清單 3.11 __miiModeForce函數實現

static INT __miiModeForce (PHY_DEV *pPhyDev)
{
……                                                                    /* 100Mb/s full                 */
    if (MII_PHY_FLAGS_JUDGE(MII_PHY_100) && MII_PHY_FLAGS_JUDGE(MII_PHY_FD)) {
        usData = MII_CTRL_NORM_EN;
        usData |= MII_CTRL_100;
        usData |= MII_CTRL_FDX;
        __miiForceAttempt(pPhyDev, usData);
        MII_PHY_ABILITY_FLAGS_SET(MII_PHY_100 | MII_PHY_FD);
        return  (MII_OK);
    }
……
    return  (MII_ERROR);
}

__miiForceAttempt函數設成指定連接模式,並檢查PHY狀態。__miiBasicCheck函數用於檢查PHY狀態是否正確。如程序清單 3.12所示。

程序清單 3.12 __miiForceAttempt函數實現

static INT __miiForceAttempt (PHY_DEV *pPhyDev, UINT16  usData)
{ 
    if (MII_WRITE(pPhyDev, MII_CTRL_REG, usData) != MII_OK) {
        return  (MII_ERROR);
    }
    if (__miiBasicCheck(pPhyDev) != MII_OK) {
        return  (MII_ERROR);
    }
    return  (MII_OK);
}

2.3.7 __miiPhyMonitor函數實現

__miiPhyMonitor函數持續監測PHY狀態。具體爲每隔一段時間檢測PHY連接狀態並更新狀態信息,當檢測到爲失去連接時調用PHYF_pfuncLinkDown函數(實際爲以前的miiLinkStatus函數)。如程序清單 3.13所示。

程序清單 3.13 __miiPhyMonitor函數實現

static INT __miiPhyMonitor (VOID)
{
……
            iRet = MII_READ(pPhyDev, MII_STAT_REG, &usPhyStatus);
            if (iRet == MII_ERROR) {
                goto    __mii_monitor_exit;
            }
            /*
             * is the PHY's status link changed?
             */
            if ((pPhyDev->PHY_usPhyStatus & MII_SR_LINK_STATUS) !=
                (usPhyStatus & MII_SR_LINK_STATUS)) {
                if (usPhyStatus & MII_SR_LINK_STATUS) {
                    if (pPhyDev->PHY_uiPhyFlags & MII_PHY_AUTO) {
                        __miiAbilFlagUpdate(pPhyDev);
                        __miiPhyUpdate(pPhyDev);
                    } else {
                        __miiFlagsHandle(pPhyDev);
                    }
                }
                if (pPhyDev->PHY_pPhyDrvFunc->PHYF_pfuncLinkDown != LW_NULL) {
                    API_NetJobAdd((VOIDFUNCPTR)(pPhyDev->PHY_pPhyDrvFunc->PHYF_pfuncLinkDown),
                                  (PVOID)(pPhyDev->PHY_pvMacDrv), 0, 0, 0, 0, 0);
                    pPhyDev->PHY_usPhyStatus = usPhyStatus;
                }
……
    return  (iRet);
}

2.4 實際運用

static INT __miiPhyMonitor (VOID)
{
……
            iRet = MII_READ(pPhyDev, MII_STAT_REG, &usPhyStatus);
            if (iRet == MII_ERROR) {
                goto    __mii_monitor_exit;
            }
            /*
             * is the PHY's status link changed?
             */
            if ((pPhyDev->PHY_usPhyStatus & MII_SR_LINK_STATUS) !=
                (usPhyStatus & MII_SR_LINK_STATUS)) {
                if (usPhyStatus & MII_SR_LINK_STATUS) {
                    if (pPhyDev->PHY_uiPhyFlags & MII_PHY_AUTO) {
                        __miiAbilFlagUpdate(pPhyDev);
                        __miiPhyUpdate(pPhyDev);
                    } else {
                        __miiFlagsHandle(pPhyDev);
                    }
                }
                if (pPhyDev->PHY_pPhyDrvFunc->PHYF_pfuncLinkDown != LW_NULL) {
                    API_NetJobAdd((VOIDFUNCPTR)(pPhyDev->PHY_pPhyDrvFunc->PHYF_pfuncLinkDown),
                                  (PVOID)(pPhyDev->PHY_pvMacDrv), 0, 0, 0, 0, 0);
                    pPhyDev->PHY_usPhyStatus = usPhyStatus;
                }
……
    return  (iRet);
}

__phyInit函數爲在實際網卡驅動中編寫的PHY芯片初始化驅動程序。具體爲調用miiDrvInit函數寫入參數,調用API_MiiPhyInit函數進行PHY芯片初始化。

如程序清單 3.14所示。

程序清單 3.14 __phyInit函數實現

static INT __phyInit (struct netdev  *pNetDev)
{
……
    pEnet    = &_G_enetInfo;
    pnetdev = &pEnet->ENET_netdev;
    pmiidrv = miiDrvInit();
    if (!pmiidrv) {
        return  (PX_ERROR);
    }
    pEnet->ENET_miidrv = pmiidrv;
    pmiidrv->MIID_enet = pEnet;
    pnetdev->priv      = (PVOID)pEnet;
    iRet = API_MiiPhyInit(&(pEnet->ENET_miidrv->MIID_phydev));
    if (iRet == MII_OK) {
        pEnet->ENET_iMiiInit = 1;
    }
    return  (ERROR_NONE);
}

miiDrvInit函數將API_MiiPhyInit中須要用的參數進行傳參。封裝MAC對PHY的讀寫函數,指定工做模式。如程序清單 3.15所示。

程序清單 3.15 miiDrvInit函數實現

MII_DRV  *miiDrvInit (VOID)
{
……
    pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncWrite = (FUNCPTR)miiPhyWrite;
    pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncRead = (FUNCPTR)miiPhyRead;
    pmiidrv->MIID_phydev.PHY_pPhyDrvFunc->PHYF_pfuncLinkDown = (FUNCPTR)miiLinkStatus;
    pmiidrv->MIID_phydev.PHY_pvMacDrv       = pmiidrv;
    pmiidrv->MIID_phydev.PHY_ucPhyAddr      = ENET_PHYADDR;
    pmiidrv->MIID_phydev.PHY_uiPhyID        = FEC_ENET_PHYID;
    pmiidrv->MIID_phydev.PHY_uiPhyIDMask    = 0x00000000;               /*  同系列芯片不一樣信號識別    */
    pmiidrv->MIID_phydev.PHY_uiTryMax       = 100;
    pmiidrv->MIID_phydev.PHY_uiLinkDelay    = 100;                      /*  延時100毫秒自動協商過程   */
    pmiidrv->MIID_phydev.PHY_uiPhyFlags     = MII_PHY_AUTO |            /*  自動協商標誌              */
                                              MII_PHY_FD   |            /*  全雙工模式                */
                                              MII_PHY_100  |            /*  100Mbit                   */
                                              MII_PHY_10   |            /*  10Mbit                    */
                                              MII_PHY_HD   |            /*  半雙工模式                */
                                              MII_PHY_MONITOR;          /*  啓用自動監視功能          */
    return  (pmiidrv);
}
相關文章
相關標籤/搜索