12-网络连接状态
16.7 网络连接状态
网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常。网络设备驱动可以通过netif_carrier_on()和netif_carrier_off()函数改变设备的连接状态,如果驱动检测到连接状态发生变化,也应该以netif_carrier_on()和netif_carrier_off()函数显式地通知内核。
除了netif_carrier_on()和netif_carrier_off()函数以外,另一个函数netif_carrier_ok()可用于向调用者返回链路上的载波信号是否存在。
这几个函数都接收一个net_device设备结构体指针为参数,原型分别为:
void netif_carrier_on(struct net_device *dev);
void netif_carrier_off(struct net_device *dev);
int netif_carrier_ok(struct net_device *dev);
网络设备驱动程序中可采取一定的手段来检测和报告链路状态,例如设置一个定时器来对链路状态进行周期性地检查。当定时器到期之后,在定时器处理函数中读取物理设备的相关寄存器获得载波状态,从而更新设备的连接状态,如代码清单16.12所示。
代码清单16.12 网络设备驱动用定时器周期检查链路状态
1 static void xxx_timer(unsigned long data)
2 {
3 struct net_device dev = (struct net_device)data;
4 u16 link;
5 …
6 if (!(dev->flags &IFF_UP))
7 goto set_timer;
8
9 / 获得物理上的连接状态 /
10 if (link = xxx_chk_link(dev)) {
11 if (!(dev->flags &IFF_RUNNING)) {
12 netif_carrier_on(dev);
13 dev->flags |= IFF_RUNNING;
14 printk(KERN_DEBUG "%s: link up\n", dev->name);
15 }
16 } else {
17 if (dev->flags &IFF_RUNNING) {
18 netif_carrier_off(dev);
19 dev->flags &= ~IFF_RUNNING;
20 printk(KERN_DEBUG "%s: link down\n", dev->name);
21 }
22 }
23
24 set_timer:
25 priv->timer.expires = jiffies + 1 * Hz;
26 priv->timer.data = (unsigned long)dev;
27 priv->timer.function = &xxx_timer; / timer handler /
28 add_timer(&priv->timer);
29 }
上述代码第10行调用的xxx_chk_link()函数用于读取网络适配器硬件的相关寄存器以获得链路连接状态,具体实现由硬件决定。当链路连接上时,第12行的netif_carrier_on()函数显式地通知内核链路正常;反之,第18行的netif_carrier_off()同样显式地通知内核链路失去连接。
此外,从上述源代码还可以看出,定时器处理函数会不停地利用第24~28行代码启动新的定时器以实现周期检测的目的。那么最初启动定时器的地方在哪里呢?很显然,它最适合在设备的打开函数中完成,如代码清单16.13所示。
代码清单16.13 在网络设备驱动的打开函数中初始化定时器
1 static int xxx_open(struct net_device *dev)
2 {
3 struct xxx_priv priv = (struct xxx_priv)dev->priv;
4
5 ...
6 priv->timer.expires = jiffies + 3 * Hz;
7 priv->timer.data = (unsigned long)dev;
8 priv->timer.function = &xxx_timer; / timer handler /
9 add_timer(&priv->timer);
10 ...
11 }