网络设备的驱动程序提供一些供系统对设备的参数进行设置 或 读取设备相关信息的方法。
当用户调用 ioctl() 函数,并指定 SIOCSIFHWADDR 命令时,意味着要设置这个设备的 MAC 地址。设置网络设备的 MAC 地址可用如下代码所示的模板:
1 /* 2 * 设置网络设备的 MAC 地址 3 */ 4 5 static int set_mac_address(struct net_device *dev, void *addr) 6 { 7 if (netif_running(dev)) 8 return -EBUSY; 9 10 /* 设置以太网的 MAC 地址 */ 11 xxx_set_mac(dev, addr); 12 13 return 0; 14 }
以上程序首先用 netif_running() 宏判断设备是否正在运行,如果是,则意味着设备忙,此时不允许设置 MAC 地址;否则,调用 xxx_set_mac() 函数在网络适配器硬件内写入新的 MAC 地址。这要求设备在硬件上支持 MAC 地址的修改,而实际上,许多设备并不提供修改 MAC 地址的接口。
netif_running() 宏的定义为:
1 /** 2 * netif_running - test if up 3 * @dev: network device 4 * 5 * Test if the device has been brought up. 6 */ 7 static inline bool netif_running(const struct net_device *dev) 8 { 9 return test_bit(__LINK_STATE_START, &dev->state); 10 }
当用户调用 ioctl() 函数时,若命令为 SIOCSIFMAP (如在控制台中运行网络配置命令 ifconfig 就会引发这一调用),系统会调用驱动程序的 set_config() 函数。
系统会向 set_config() 函数传递一个 ifmap 结构体,该结构体主要包含用户欲设置的设备要使用的 I/O 地址、中断等信息。注意,并不是 ifmap 结构体中给出的所有修改都是可以接受的。实际上,大多数设备并不适合包含 set_config() 函数。set_config() 函数的例子如以下代码所示:
1 /** 2 * 网络设备驱动的 set_config 函数模板 3 */ 4 5 static int xxx_config(struct net_device *dev, struct ifmap *map) 6 { 7 if (netif_running(dev)) /* 不能设置一个正在运行状态的设备哦 */ 8 return -EBUSY; 9 10 /* 假设不允许改变 I/O 地址 */ 11 if (map->base_addr != dev->base_addr) { 12 printk(KERN_WARNING "xxx: Can't change I/O address\n"); 13 return -EOPNOTSUPP; 14 } 15 16 /* 假设允许改变 IRQ */ 17 if(map->irq != dev->irq) 18 dev->irq = map->irq; 19 20 return 0; 21 }
上述代码中的 set_config() 函数接受 IRQ 的修改,拒绝设备 I/O 地址的修改。具体的设备是否接受这些信息的修改,要视硬件的设计而定。
如果用户调用 ioctl() 时,命令类型在 SIOCDEVPRIVATE 和 SIOCDEVPRIVATE+15 之间。系统会调用驱动程序的 do_ioctl() 函数,以进行设备专用数据的设置。这个设置在大多数情况下也不需要。
驱动程序还应提供 get_stats() 函数以向用户反馈设备状态和统计信息,该函数返回的是一个 net_device_stats 结构体,代码如下所示:
1 /** 2 * 网络设备驱动的 get_stats() 函数模板 3 */ 4 5 struct net_device_stats * xxx_stats(struct net_device *dev) 6 { 7 ··· 8 return &dev->stats; 9 }
有的网卡硬件比较强大,可以从硬件的寄存器中读出一些统计信息,如 rx_missed_errors、tx_aborted_errors、rx_dropped、rx_length_errors等。这个时候,我们应该从硬件寄存器读取统计信息,填充到 net_device 的 stats 字段中,并返回。具体例子参见 drivers/net/ethernet/adaptec/starfire.c 中的 get_stats() 函数。
net_device_stats 结构体定义在内核的 include/linux/netdevice.h 文件中,它包含了比较完整的统计信息,如以下代码所示:
1 /* 2 * Old network device statistics. Fields are native words 3 * (unsigned long) so they can be read and written atomically. 4 */ 5 6 struct net_device_stats { 7 unsigned long rx_packets; /* 收到的数据包数 */ 8 unsigned long tx_packets; /* 发送的数据包数 */ 9 unsigned long rx_bytes; /* 收到的字节数 */ 10 unsigned long tx_bytes; /* 发送的字节数 */ 11 unsigned long rx_errors; /* 收到的错误数据包数 */ 12 unsigned long tx_errors; /* 发生发送错误的数据包数 */ 13 unsigned long rx_dropped; 14 unsigned long tx_dropped; 15 unsigned long multicast; 16 unsigned long collisions; 17 unsigned long rx_length_errors; 18 unsigned long rx_over_errors; 19 unsigned long rx_crc_errors; 20 unsigned long rx_frame_errors; 21 unsigned long rx_fifo_errors; 22 unsigned long rx_missed_errors; 23 unsigned long tx_aborted_errors; 24 unsigned long tx_carrier_errors; 25 unsigned long tx_fifo_errors; 26 unsigned long tx_heartbeat_errors; 27 unsigned long tx_window_errors; 28 unsigned long rx_compressed; 29 unsigned long tx_compressed; 30 };
上述代码列出了 net_device_stats 包含的所有统计信息,包含主项目统计信息以及子项目统计信息。
net_device_stats 结构体已经内嵌在与网络设备对应的 net_device 结构体中,而其中统计信息的修改则应该在设备驱动的与发送和接收相关的具体函数中完成,这些函数包括中断处理程序、数据包发送函数、数据包发送超时函数和数据包接收相关函数等。我们应该在这些函数中添加相应的代码,如下所示:
1 /** 2 * net_device_stats 结构体中统计信息的维护 3 */ 4 5 /* 发送超时函数 */ 6 void xxx_tx_timeout(struct net_device *dev) 7 { 8 ··· 9 dev->stats.tx_errors++; /* 发送错误包数加1 */ 10 ··· 11 } 12 13 /* 中断处理函数 */ 14 static void xxx_interrupt(int irq, void *dev_id) 15 { 16 struct net_device *dev = dev_id; 17 switch (status & ISQ_EVENT_MASK) { 18 ··· 19 case ISQ_TRANSMITTER_EVENT: 20 dev->stats.tx_packets++; /* 数据包发送成功,tx_packets 信息加 1 */ 21 netif_wake_queue(dev); /* 通知上层协议 */ 22 if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | 23 TX_LATE_COL | TX_16_COL)) != TX_OK) { /* 读取硬件上的出错标志 */ 24 /* 根据错误的不同情况,对 net_device_stats 的不同成员加 1 */ 25 if ((status & TX_OK) == 0) 26 dev->stats.tx_errors++; 27 if (status & TX_LOST_CRS) 28 dev->stats.tx_carrier_errors++; 29 if (status & TX_SQE_ERROR) 30 dev->stats.tx_heartbeat_errors++; 31 if (status & TX_LATE_COL) 32 dev->stats.tx_window_errors++; 33 if (status & TX_16_COL) 34 dev->stats.tx_aborted_errors++; 35 } 36 break; 37 38 case ISQ_RX_MISS_EVENT: 39 dev->stats.rx_missed_errors += (status >> 6); 40 break; 41 42 case ISQ_TX_COL_EVENT: 43 dev->stats.collisions += (status >> 6); 44 break; 45 } 46 }
上述代码第 9 行意味着在发送数据包超时时,将发生发送错误的数据包数加 1。第17 ~ 44 行则意味着当网络设备中断产生时,中断处理程序读取硬件的相关信息以决定修改 net_device_stats 统计信息中的哪些项目和子项目,并将相应的项目加 1。