我们知道数据包的接收是通过中断来实现的,下面再分析一下数据的发送,当协议层已经封装好上层协议数据的skb_buffer后,将调用前面提到的dm9000_start_xmit函数把数据发送出去,下面我们看看这个dm9000_start_xmit函数的实现
static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; board_info_t *db = netdev_priv(dev); //获取私有数据 dm9000_dbg(db, 3, "%s:\n", __func__); if (db->tx_pkt_cnt > 1) //网卡忙 return NETDEV_TX_BUSY; spin_lock_irqsave(&db->lock, flags); //关闭中断,获取锁 writeb(DM9000_MWCMD, db->io_addr); //读数据命令 (db->outblk)(db->io_data, skb->data, skb->len); //把数据发送出去 dev->stats.tx_bytes += skb->len; //统计发送字节数 db->tx_pkt_cnt++; //正在发送中的数据计数加1 if (db->tx_pkt_cnt == 1) { dm9000_send_packet(dev, skb->ip_summed, skb->len); } else { db->queue_pkt_len = skb->len; db->queue_ip_summed = skb->ip_summed; netif_stop_queue(dev); //网卡停止接收数据 } spin_unlock_irqrestore(&db->lock, flags); //释放锁 dev_kfree_skb(skb); //释放内存空间 return NETDEV_TX_OK; } |
首先需要说明的是上述函数实现中调用spin_lock_irqsave(&db->lock, flags)来获取一个自旋锁,当重发后释放该自旋锁,以避免与协议栈调用发送函数产生竞态。
另外,当软件完成指示硬件发送数据包后该函数返回,但此时硬件传送可能还没完成,因此驱动需要告知协议栈不要再起的发送,即调用netif_stop_queue函数,直至硬件准备好接收新的数据。当数据发送完,会触发中断,仍然调用前面的中断处理函数,然后执行dm9000_tx_done(dev, db),跟踪下dm9000_tx_done的实现吧
static void dm9000_tx_done(struct net_device *dev, board_info_t *db) { int tx_status = ior(db, DM9000_NSR); if (tx_status & (NSR_TX2END | NSR_TX1END)) { db->tx_pkt_cnt--; dev->stats.tx_packets++; if (netif_msg_tx_done(db)) dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); if (db->tx_pkt_cnt > 0) dm9000_send_packet(dev, db->queue_ip_summed, db->queue_pkt_len); netif_wake_queue(dev); //告知协议栈可以再次发送数据 } } |
需要补充的是如果在其他地方需要停止数据包的传送,可以使用netif_tx_disable函数。
四、网络设备驱动测试
网络设备没有字符设备里的open、close等函数,而是靠IP地址选择路由,Linux网络系统的路由选择会自动查找匹配合适的驱动,这是网络驱动和其他两种驱动的主要区别。
下面给出三种测试网络驱动是否正常的方法
其一,使用ifconfig进行IP地址的设置。
其二,为了让用户获取网络统计的数据,驱动一般有一个net_device_stats结构体,并提供get_stats函数接收它,比如要从eth0接口得到接收的数据包数,可以查看/sys/class/net/eth0/statistics,其中rx_packet表示受到的报文数目,rx_byte表示收到的字节总数。
其三,应用程序使用标准的socket、bind、send等操作。
相关链接:
Linux下的网络设备驱动(一)