1.Linux网络子系统
- 我们这里研究内核空间即可,在内核空间分成5层,分别是:
- 1、系统调用接口,它面向的客户是应用层序,为应用程序提供访问网络子系统的统一方法,比如说socket,send等函数的系统调用
- 2、协议无关接口,它提供通用的方法来使用传输层协议,把所有的协议统一起来
- 3、网络协议,它的作用就是实现具体的网络协议
- 4、设备无关接口,这个接口是为了屏蔽底层硬件的差异
- 5、设备驱动,这里实现具体的网卡驱动
2.重要的数据结构
2.1 网卡描述结构
- 在Linux内核中,每个网卡都由一个net_device结构来描述,其中的一些重要成员有:
- char name[IFNAMSIZ]:设备名,如:eth%d
- unsigned long base_addr :I/O基地址
- const struct net_device_ops *netdev_ops
2.2 网卡操作集合
- 类似于字符设备驱动中的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,
};
2.3 网络数据包
- 这里还有另外一个重要的结构,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、数据接收分析
3.1 网卡初始化分析
- 网卡驱动初始化主要在函数init_module中完成,部分代码如下:
int __init init_module(void)
{struct net_device *dev = alloc_etherdev(sizeof(struct net_local));struct net_local *lp;int ret = 0;...dev->irq = irq;dev->base_addr = io;...ret = cs89x0_probe1(dev, io, 1);...
}
- cs89x0_probe1函数部分代码如下:
static int __init cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
{struct net_local *lp = netdev_priv(dev);static unsigned version_printed;int i;int tmp;unsigned rev_type = 0;int eeprom_buff[CHKSUM_LEN];int retval;...writeword(ioaddr, ADD_PORT, PP_ChipID);tmp = readword(ioaddr, DATA_PORT); //对硬件的初始化...for (i = 0; i < ETH_ALEN/2; i++) //初始化MAC地址{dev->dev_addr[i*2] = eeprom_buff[i];dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;}...dev->netdev_ops = &net_ops; //初始化netdev_ops...retval = register_netdev(dev); //注册网卡驱动
}
- 初始的第一项工作是分配一个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、注册网卡驱动
3.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 weask the chip to start transmitting before thewhole 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;
}
- 1、首先调用netif_stop_queue,这个函数用于告诉上层协议栈,暂停往网卡中发送数据。
- 2、然后skb中的数据写入寄存器中并发送走
- 3、释放skb空间
/* 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);
- 但是到这里并不算完,如果这就完了上层协议还是无法向网卡发送数据,网卡不能正常工作,显然这是不正常的。那么在什么地方重新允许上层协议向网卡发送数据包呢?
- 其实,当网卡发送走一个数据包后,会进入网卡中断程序中,查找request_irq的知中断处理程序名称为net_interrupt.
static irqreturn_t net_interrupt(int irq, void *dev_id)
{struct net_device *dev = dev_id;struct net_local *lp;int ioaddr, status;int handled = 0;ioaddr = dev->base_addr;lp = netdev_priv(dev);while ((status = readword(dev->base_addr, ISQ_PORT))){switch(status & ISQ_EVENT_MASK) {...case ISQ_TRANSMITTER_EVENT:dev->stats.tx_packets++;netif_wake_queue(dev); /* Inform upper layers. */if ((status & ( TX_OK |TX_LOST_CRS |TX_SQE_ERROR |TX_LATE_COL |TX_16_COL)) != TX_OK) {if ((status & TX_OK) == 0)dev->stats.tx_errors++;if (status & TX_LOST_CRS)dev->stats.tx_carrier_errors++;if (status & TX_SQE_ERROR)dev->stats.tx_heartbeat_errors++;if (status & TX_LATE_COL)dev->stats.tx_window_errors++;if (status & TX_16_COL)dev->stats.tx_aborted_errors++;}break;...} }
}
- 4、通知上层协议,可以向网卡发送数据包。使用函数netif_wake_queue(dev)。
3.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,因此我们主要分析这个函数。这里面的实现流程如下:
static void
net_rx(struct net_device *dev)
{struct sk_buff *skb;int status, length;int ioaddr = dev->base_addr;status = readword(ioaddr, RX_FRAME_PORT);length = readword(ioaddr, RX_FRAME_PORT);if ((status & RX_OK) == 0) {count_rx_errors(status, dev);return;}/* Malloc up new buffer. */skb = dev_alloc_skb(length + 2);if (skb == NULL) {
#if 0 /* Again, this seems a cruel thing to do */printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
#endifdev->stats.rx_dropped++;return;}skb_reserve(skb, 2); /* longword align L3 header */readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1);if (length & 1)skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT);if (net_debug > 3) {printk( "%s: received %d byte packet of type %x\n",dev->name, length,(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);}skb->protocol=eth_type_trans(skb,dev);netif_rx(skb);dev->stats.rx_packets++;dev->stats.rx_bytes += length;
}
- 1、读取接收状态寄存器:status = readword(ioaddr, RX_FRAME_PORT);
- 2、读取接收的长度:length = readword(ioaddr, RX_FRAME_PORT);
- 3、构造一个skb:skb = dev_alloc_skb(length + 2);
- 4、然后将受到的数据填入skb包:skb_reserve(skb, 2);
- 5、最后把skb包交给上层协议栈:netif_rx(skb);