您好,登錄后才能下訂單哦!
1.原理概述
1.1 網(wǎng)卡驅(qū)動(dòng)概述
一塊以太網(wǎng)網(wǎng)卡包括OSI模型的兩個(gè)層:物理層和數(shù)據(jù)鏈路層。數(shù)據(jù)鏈路層的芯片簡(jiǎn)稱(chēng)為MAC控制器,物理層的芯片簡(jiǎn)稱(chēng)為PHY。
MAC主要負(fù)責(zé)控制與連接物理層的物理介質(zhì)。在發(fā)送數(shù)據(jù)時(shí),MAC先判斷是否可以發(fā)送數(shù)據(jù),如果可以發(fā)送,給數(shù)據(jù)加上控制信息,最終將數(shù)據(jù)及控制信息按規(guī)定的格式發(fā)送到物理層;在接收數(shù)據(jù)的時(shí)候,MAC先判斷信息是否發(fā)生傳輸錯(cuò)誤,如果沒(méi)有錯(cuò)誤,去掉控制信息發(fā)送至LLC層。該層協(xié)議由IEEE-802.3以太網(wǎng)標(biāo)準(zhǔn)定義。
PHY是物理接口收發(fā)器,它實(shí)現(xiàn)物理層。IEEE-802.3標(biāo)準(zhǔn)定義了以太網(wǎng)PHY。在發(fā)送數(shù)據(jù)時(shí),收到MAC數(shù)據(jù),把并行數(shù)據(jù)轉(zhuǎn)化為串行流數(shù)據(jù),再按照物理層的編碼規(guī)則編碼,再變?yōu)槟M信號(hào)發(fā)送數(shù)據(jù)。收數(shù)據(jù)時(shí)流程反之。PHY還有個(gè)重要的功能是實(shí)現(xiàn)CSMA/CD的部分功能。
PHY和MAC之間的關(guān)系是PCI總線(xiàn)接MAC總線(xiàn),MAC接PHY,PHY接網(wǎng)線(xiàn)。
在以太網(wǎng)網(wǎng)卡驅(qū)動(dòng)中,完全可以將PHY芯片的驅(qū)動(dòng)抽象,做成通用接口。(IEEE802.3定義PHY芯片地址0-15寄存器的功能,地址16-31寄存器留給芯片制造商自由定義)。
2.技術(shù)實(shí)現(xiàn)
2.1 PHY驅(qū)動(dòng)目錄
PHY芯片驅(qū)動(dòng)在SylixOS的Base中已經(jīng)給出,如圖 3.1所示。
圖 3.1 PHY驅(qū)動(dòng)目錄
SylixOS提供的PHY芯片驅(qū)動(dòng)能支持10Mb,100Mb,1000Mb的鏈接能力。
2.2 PHY驅(qū)動(dòng)框架
PHY驅(qū)動(dòng)大致流程為:
初始化組件庫(kù)(信號(hào)量,定時(shí)器)。
選擇自動(dòng)查找/指定查找PHY設(shè)備。
選擇自動(dòng)協(xié)商/指定鏈接模式。
啟用定時(shí)器每隔一段時(shí)間查詢(xún)鏈接狀態(tài)。
如圖 3.2所示。
圖 3.2 PHY驅(qū)動(dòng)框架
在網(wǎng)卡驅(qū)動(dòng)中,先對(duì)程序清單 3.1進(jìn)行傳參,再調(diào)用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; /* 同系列芯片不同信號(hào)識(shí)別 */ pmiidrv->MIID_phydev.PHY_uiTryMax = 100; pmiidrv->MIID_phydev.PHY_uiLinkDelay = 100; /* 延時(shí)100毫秒自動(dòng)協(xié)商過(guò)程 */ pmiidrv->MIID_phydev.PHY_uiPhyFlags = MII_PHY_AUTO | /* 自動(dòng)協(xié)商標(biāo)志 */ MII_PHY_FD | /* 全雙工模式 */ MII_PHY_100 | /* 100Mbit */ MII_PHY_10 | /* 10Mbit */ MII_PHY_HD | /* 半雙工模式 */ MII_PHY_MONITOR; /* 啟用自動(dòng)監(jiān)視功能 */
MAC能對(duì)PHY芯片進(jìn)行讀寫(xiě)操作(miiPhyWrite函數(shù),miiPhyRead函數(shù))。
鏈接狀態(tài)變化函數(shù)(miiLinkStatus函數(shù),打印鏈接信息并進(jìn)行相關(guān)操作)。
控制參數(shù)傳參(PHY驅(qū)動(dòng)會(huì)根據(jù)傳入?yún)?shù)進(jìn)行不同操作,如是否開(kāi)啟自動(dòng)協(xié)商,選擇速度/雙工模式,開(kāi)啟自動(dòng)監(jiān)視功能)。
2.3 具體實(shí)現(xiàn)
2.3.1 API_MiiPhyInit函數(shù)實(shí)現(xiàn)
API_MiiPhyInit函數(shù)會(huì)根據(jù)傳入?yún)?shù)分別執(zhí)行初始化組件庫(kù),查找PHY設(shè)備,設(shè)置鏈接能力。
PHY驅(qū)動(dòng)提供鏈表將有效PHY設(shè)備加入到MII鏈表,能對(duì)多個(gè)PHY芯片進(jìn)行操作。
如程序清單 3.2所示。
程序清單 3.2 API_MiiPhyInit函數(shù)實(shí)現(xiàn)
INTAPI_MiiPhyInit (PHY_DEV *pPhyDev) { …… if (API_MiiLibInit() == MII_ERROR) { return (MII_ERROR); } if (pPhyDev->PHY_ucPhyAddr == 0) { /* Auto scan phydevice */ if (API_MiiPhyScan(pPhyDev) == MII_ERROR) { _DebugHandle(__ERRORMESSAGE_LEVEL, "can notfind phy device.\r\n"); return (MII_ERROR); } } else { /* Test specific phy device */ if (API_MiiPhyProbe(pPhyDev) != MII_OK) { MII_DEBUG_ADDR("can notfind 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 orMII_OK */ }
2.3.2 API_MiiLibInit函數(shù)實(shí)現(xiàn)
API_MiiLibInit函數(shù)初始化組件庫(kù)。大致為創(chuàng)建信號(hào)量,定時(shí)器,并開(kāi)啟定時(shí)器,檢測(cè)鏈接能力。(__miiPhyMonitor函數(shù)主要讀取PHY芯片狀態(tài),更新?tīng)顟B(tài)信息)。
如程序清單 3.3所示。
程序清單 3.3 API_MiiLibInit函數(shù)實(shí)現(xiàn)
INTAPI_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 MIIMutex 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函數(shù)實(shí)現(xiàn)
API_MiiPhyScan函數(shù)自動(dòng)查找PHY設(shè)備。大致為從0-32對(duì)所有PHY設(shè)備地址進(jìn)行查找,若存在設(shè)備,測(cè)試PHY是否有效。
如程序清單 3.4所示。
程序清單 3.4 API_MiiPhyScan函數(shù)實(shí)現(xiàn)
INTAPI_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 aValid PHY */ } return (MII_PHY_NULL); }
2.3.4 API_MiiPhyProbe,API_MiiPhyDiagnostic函數(shù)實(shí)現(xiàn)
API_MiiPhyProbe函數(shù)檢測(cè)PHY設(shè)備是否存在,大致為讀取PHY寄存器ID,進(jìn)行匹配,返回是否成功。如程序清單 3.5所示。
程序清單 3.5 API_MiiPhyProbe函數(shù)實(shí)現(xiàn)
INTAPI_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函數(shù)測(cè)試PHY是否有效,具體為復(fù)位PHY是否成功,檢測(cè)PHY是否處于物理隔離狀態(tài)。如程序清單 3.6所示。
程序清單 3.6 API_MiiPhyDiagnostic函數(shù)實(shí)現(xiàn)
INTAPI_MiiPhyDiagnostic (PHY_DEV *pPhyDev) { …… iRet = MII_WRITE(pPhyDev, ucRegAddr, usData); /* Reset thePHY */ 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函數(shù)實(shí)現(xiàn)
API_MiiPhyLinkSet函數(shù)設(shè)置PHY鏈接模式。如程序清單 3.7所示。
程序清單 3.7 API_MiiPhyLinkSet函數(shù)實(shí)現(xiàn)
INTAPI_MiiPhyLinkSet (PHY_DEV *pPhyDev) { …… if (__miiAbilFlagUpdate(pPhyDev) == MII_ERROR) { return (MII_ERROR); } …… iRet = API_MiiPhyModeSet(pPhyDev); …… return (MII_OK); }
API_MiiPhyModeSet函數(shù)根據(jù)參數(shù)選擇自動(dòng)協(xié)商/手動(dòng)設(shè)置。
如程序清單 3.8所示。
程序清單 3.8 API_MiiPhyModeSet函數(shù)實(shí)現(xiàn)
INTAPI_MiiPhyModeSet (PHY_DEV *pPhyDev) { if (pPhyDev->PHY_uiPhyFlags & MII_PHY_AUTO) { /* AutoNegotiationenabled */ if (__miiAutoNegotiate(pPhyDev) == MII_OK) { return (MII_OK); } } else { /* 未開(kāi)啟自動(dòng)協(xié)商功能 */ if (__miiModeForce(pPhyDev) == MII_OK) { if (__miiFlagsHandle(pPhyDev) == MII_OK) { /* handlesome flags */ return (MII_OK); } } } return (MII_ERROR); }
__miiAutoNegotiate函數(shù)進(jìn)行自動(dòng)協(xié)商并檢查協(xié)商是否成功。__miiBasicCheck用于檢查是否link up/remote fault。如程序清單 3.9所示。
程序清單 3.9 __miiAutoNegotiate函數(shù)實(shí)現(xiàn)
staticINT__miiAutoNegotiate (PHY_DEV *pPhyDev) { …… /* * start theauto-negotiation process: return * only in caseof 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函數(shù)開(kāi)始自動(dòng)協(xié)商。具體為開(kāi)始自動(dòng)協(xié)商并根據(jù)傳入?yún)?shù)選擇等待自動(dòng)協(xié)商結(jié)束/立即返回。如程序清單 3.10所示。
程序清單 3.10 __miiAutoNegStart函數(shù)實(shí)現(xiàn)
staticINT__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 forcompletion */ 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函數(shù)實(shí)現(xiàn)
__miiModeForce函數(shù)根據(jù)傳入?yún)?shù),設(shè)成指定鏈接模式(若多個(gè)參數(shù),指定最高鏈接模式)。如程序清單 3.11所示。
程序清單 3.11 __miiModeForce函數(shù)實(shí)現(xiàn)
staticINT__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函數(shù)設(shè)成指定鏈接模式,并檢查PHY狀態(tài)。__miiBasicCheck函數(shù)用于檢查PHY狀態(tài)是否正確。如程序清單 3.12所示。
程序清單 3.12 __miiForceAttempt函數(shù)實(shí)現(xiàn)
staticINT__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函數(shù)實(shí)現(xiàn)
__miiPhyMonitor函數(shù)持續(xù)監(jiān)測(cè)PHY狀態(tài)。具體為每隔一段時(shí)間檢測(cè)PHY鏈接狀態(tài)并更新?tīng)顟B(tài)信息,當(dāng)檢測(cè)到為失去鏈接時(shí)調(diào)用PHYF_pfuncLinkDown函數(shù)(實(shí)際為之前的miiLinkStatus函數(shù))。如程序清單 3.13所示。
程序清單 3.13 __miiPhyMonitor函數(shù)實(shí)現(xiàn)
staticINT__miiPhyMonitor (VOID) { …… iRet = MII_READ(pPhyDev, MII_STAT_REG, &usPhyStatus); if (iRet == MII_ERROR) { goto __mii_monitor_exit; } /* * is the PHY's status linkchanged? */ 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 實(shí)際運(yùn)用
__phyInit函數(shù)為在實(shí)際網(wǎng)卡驅(qū)動(dòng)中編寫(xiě)的PHY芯片初始化驅(qū)動(dòng)程序。具體為調(diào)用miiDrvInit函數(shù)寫(xiě)入?yún)?shù),調(diào)用API_MiiPhyInit函數(shù)進(jìn)行PHY芯片初始化。
如程序清單 3.14所示。
程序清單 3.14 __phyInit函數(shù)實(shí)現(xiàn)
staticINT__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函數(shù)將API_MiiPhyInit中需要用的參數(shù)進(jìn)行傳參。封裝MAC對(duì)PHY的讀寫(xiě)函數(shù),指定工作模式。如程序清單 3.15所示。
程序清單 3.15 miiDrvInit函數(shù)實(shí)現(xiàn)
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; /* 同系列芯片不同信號(hào)識(shí)別 */ pmiidrv->MIID_phydev.PHY_uiTryMax = 100; pmiidrv->MIID_phydev.PHY_uiLinkDelay = 100; /* 延時(shí)100毫秒自動(dòng)協(xié)商過(guò)程 */ pmiidrv->MIID_phydev.PHY_uiPhyFlags = MII_PHY_AUTO | /* 自動(dòng)協(xié)商標(biāo)志 */ MII_PHY_FD | /* 全雙工模式 */ MII_PHY_100 | /* 100Mbit */ MII_PHY_10 | /* 10Mbit */ MII_PHY_HD | /* 半雙工模式 */ MII_PHY_MONITOR; /* 啟用自動(dòng)監(jiān)視功能 */ return (pmiidrv); }
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。