1、Linux网络子系统
网络子系统采用分层的结构:
我们这里研究内核空间即可,在内核空间分成5层,分别是:
1、系统调用接口,它面向的客户是应用层序,为应用程序提供访问网络子系统的统一方法,比如说socket,send等函数的系统调用
2、协议无关接口,它提供通用的方法来使用传输层协议,把所有的协议统一起来
3、网络协议,它的作用就是实现具体的网络协议
4、设备无关接口,这个接口是为了屏蔽底层硬件的差异
5、设备驱动,这里实现具体的网卡驱动
2、重要的数据结构
在Linux内核中,每个网卡都由一个net_device结构来描述,其中的一些重要成员有:
char name[IFNAMSIZ]
设备名,如:eth%d
unsigned long base_addr
I/O 基地址
const struct net_device_ops *netdev_ops;
类似于字符设备驱动中的file_operations结构,net_device_ops结构记录了网卡所支持的操作,当用户使用网络传输数据时,内核就会找到这些操作函数来实现具体的硬件操作,比如说DM9000的操作函数是如下的:
static const struct net_device_ops dm9000_netdev_ops =
{
.ndo_open = dm9000_open,
.ndo_stop = dm9000_stop,
.ndo_start_xmit = dm9000_start_xmit,
.ndo_do_ioctl = dm9000_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
这里还有另外一个重要的结构,sk_buff。在应用程序发送数据时,数据包在内核空间中传递,由上到下或者由下到上,这个时候必须使用一个结构来描述这种数据包,sk_buf起到的作用就是这个。
Linux内核中的每个网络数据包都由一个套接字缓冲区结构 struct sk_buff 描述,即一个sk_buff结构就是一个网络包,指向sk_buff的指针通常被称做skb。在这个结构中有这4个成员:
1、head,包头
2、data,数据起始位置
3、tail,数据结尾的位置
4、end,包尾
3、网卡驱动架构分析
我们这里分析CS8900这个网卡驱动,这个分析过程主要分为3步
1、网卡初始化分析
2、数据发送分析
3、数据接收分析
1、网卡初始化分析
初始的第一项工作是分配一个net_device结构:
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
第二项工作就是初始化这个结构:
dev->irq = irq;
dev->base_addr = io;
在这里完成中断号和设备基地址的初始化。
第三项工作就是调用cs89x0_probe1完成其他的初始化:
1、初始化MAC地址
2、初始化netdev_ops操作集函数
3、初始化硬件
4、注册网卡驱动
2、数据发送分析
怎么寻找发送数据的函数呢?联系上面的netdev_ops结构可以知道,我们可以去这里找:
static const struct net_device_ops net_ops = {
.ndo_open = net_open,
.ndo_stop = net_close,
.ndo_tx_timeout = net_timeout,
.ndo_start_xmit = net_send_packet,
.ndo_get_stats = net_get_stats,
.ndo_set_multicast_list = set_multicast_list,
.ndo_set_mac_address = set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = net_poll_controller,
#endif
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
};
ndo_start_xmit对应的函数net_send_packet应该就是网卡发送函数了。
static netdev_tx_t net_send_packet(struct sk_buff *skb,struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
unsigned long flags;
if (net_debug > 3) {
printk("%s: sent %d byte packet of type %x\n",
dev->name, skb->len,
(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
}
/* keep the upload from being interrupted, since we
ask the chip to start transmitting before the
whole packet has been completely uploaded. */
spin_lock_irqsave(&lp->lock, flags);
netif_stop_queue(dev);
/* initiate a transmit sequence */
writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd);
writeword(dev->base_addr, TX_LEN_PORT, skb->len);
/* Test to see if the chip has allocated memory for the packet */
if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
/*
* Gasp! It hasn't. But that shouldn't happen since
* we're waiting for TxOk, so return 1 and requeue this packet.
*/
spin_unlock_irqrestore(&lp->lock, flags);
if (net_debug) printk("cs89x0: Tx buffer not free!\n");
return NETDEV_TX_BUSY;
}
/* Write the contents of the packet */
writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
spin_unlock_irqrestore(&lp->lock, flags);
dev->stats.tx_bytes += skb->len;
dev_kfree_skb (skb);
/*
* We DO NOT call netif_wake_queue() here.
* We also DO NOT call netif_start_queue().
*
* Either of these would cause another bottom half run through
* net_send_packet() before this packet has fully gone out. That causes
* us to hit the "Gasp!" above and the send is rescheduled. it runs like
* a dog. We just return and wait for the Tx completion interrupt handler
* to restart the netdevice layer
*/
return NETDEV_TX_OK;
}
这里首先调用
netif_stop_queue
,这个函数用于告诉上层协议栈,暂停往网卡中发送数据。
然后开始将skb中的数据写入寄存器:
/* initiate a transmit sequence */
writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd);
writeword(dev->base_addr, TX_LEN_PORT, skb->len);
/* Test to see if the chip has allocated memory for the packet */
if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
/*
* Gasp! It hasn't. But that shouldn't happen since
* we're waiting for TxOk, so return 1 and requeue this packet.
*/
spin_unlock_irqrestore(&lp->lock, flags);
if (net_debug) printk("cs89x0: Tx buffer not free!\n");
return NETDEV_TX_BUSY;
}
数据发送完之后,什么时候告诉上层可以继续发送数据呢?其实我们应该想到应该在中断里通知上层协议可以给网卡发送数据了。
3、数据接收分析
接收数据一般在中断中进行的。找到static irqreturn_t net_interrupt(int irq, void *dev_id)这个函数。这里面实现对各种中断的处理,比如说接收数据中断:
case ISQ_RECEIVER_EVENT:
/* Got a packet(s). */
net_rx(dev);
break;
这里调用net_rx,因此我们主要分析这个函数。这里面的实现流程如下:
1、读取接收状态寄存器
2、读取接收的长度
status = readword(ioaddr, RX_FRAME_PORT);
length = readword(ioaddr, RX_FRAME_PORT);
3、构造一个skb
/* Malloc up new buffer. */
skb = dev_alloc_skb(length + 2);
4、然后将受到的数据填入skb包。
skb_reserve(skb, 2);
5、最后把skb包交给上层协议栈
netif_rx(skb);