網絡適配器硬件電路可以檢測出鏈路上是否有載波,載波反映了網絡的連接是否正常。網絡設備驅動可以通過 netif_carrier_on() 和 netif_carrier_off() 函數改變設備的連接狀態,如果驅動檢測到連接狀態發生變化,也應該以 netif_carrier_on() 和 netif_carrier_off() 函數顯式地通知內核。
除了 netif_carrier_on() 和 netif_carrier_off() 函數以外,另一個函數 netif_carrier_ok() 可用於向調用者返回鏈路上的載波信號是否存在。
這幾個函數都接收一個 net_device 設備結構體指針作為參數,原型分別為:
1 /** 2 * netif_carrier_on - set carrier 3 * @dev: network device 4 * 5 * Device has detected that carrier. 6 */ 7 void netif_carrier_on(struct net_device *dev) 8 { 9 if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 10 if (dev->reg_state == NETREG_UNINITIALIZED) 11 return; 12 atomic_inc(&dev->carrier_changes); 13 linkwatch_fire_event(dev); 14 if (netif_running(dev)) 15 __netdev_watchdog_up(dev); 16 } 17 } 18 19 20 /** 21 * netif_carrier_off - clear carrier 22 * @dev: network device 23 * 24 * Device has detected loss of carrier. 25 */ 26 void netif_carrier_off(struct net_device *dev) 27 { 28 if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) { 29 if (dev->reg_state == NETREG_UNINITIALIZED) 30 return; 31 atomic_inc(&dev->carrier_changes); 32 linkwatch_fire_event(dev); 33 } 34 } 35 36 37 /** 38 * netif_carrier_ok - test if carrier present 39 * @dev: network device 40 * 41 * Check if carrier is present on device 42 */ 43 static inline bool netif_carrier_ok(const struct net_device *dev) 44 { 45 return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); 46 }
在網絡設備驅動程序中可采取一定的手段來檢測和報告鏈路狀態,最常見的方法是采用中斷,其次可以設置一個定時器來對鏈路狀態進行周期性的檢查。當定時器到期之后,在定時器處理函數中讀取物理設備的相關寄存器以獲得載波狀態,從而更新設備的連接狀態,如下代碼所示:
1 /* 2 * 網絡設備驅動用定時器周期性檢查鏈路狀態 3 */ 4 5 static void xxx_timer(unsigned long data) 6 { 7 struct net_device *dev = (struct net_device *)data; 8 u16 link; 9 ··· 10 if (!(dev->flags & IFF_UP)) 11 goto set_timer; 12 13 /* 獲得物理上的連接狀態 */ 14 if(link = xxx_chk_link(dev)) { 15 if (!(dev->flags & IFF_RUNNING)) { 16 netif_carrier_on(dev); 17 dev->flags |= IFF_RUNNING; 18 printk(KERN_DEBUG "%s: link up\n", dev->name); 19 } 20 } else { 21 if (dev->flags & IFF_RUNNING) { 22 netif_carrier_off(dev); 23 dev->flags &= ~IFF_RUNNING; 24 printk(KERN_DEBUG "%s: link down\n", dev->name); 25 } 26 } 27 28 set_timer: 29 priv->timer.expires = jiffies + 1 * Hz; 30 priv->timer.data = (unsigned long)dev; 31 priv->timer.function = &xxx_timer; /* timer handler */ 32 add_timer(&priv->timer); 33 }
上述代碼第 14 行調用 xxx_chk_link() 函數來讀取網絡適配器硬件的相關寄存器,以獲得鏈路連接狀態,具體實現由硬件決定。當鏈路連接上時,第 16 行的 netif_carrier_on() 函數顯式地通知內核鏈路正常;反之,第 22 行的 nerif_carrier_off() 同樣顯式地通知內核鏈路失去連接。
此外,從上述源代碼還可以看出,定時器處理函數會不停地利用第 28 ~ 32行代碼啟動新的定時器以實現周期性檢測的目的。最初啟動定時器的地方在哪呢??很顯然,它最適合在設備的打開函數中完成,如下代碼所示:
1 /* 2 * 在網絡設備驅動的打開函數中初始化定時器 3 */ 4 5 static int xxx_open(struct net_device *dev) 6 { 7 struct xxx_priv *priv = netdev_priv(dev); 8 9 10 ··· 11 priv->timer.expires = jiffies + 3 * Hz; 12 priv->timer.data = (unsigned long)dev; 13 priv->timer.function = &xxx_timer; /* 定時器處理函數 */ 14 ··· 15 }