Linux网络设备驱动之网络连接状态(七)
网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常。网络设备驱动可以通过 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 }
本文来自博客园,作者:闹闹爸爸,转载请注明原文链接:https://www.cnblogs.com/wanglouxiaozi/p/13383089.html