它主要完成:对网应对应的net net_device赋初值。并向内核调用register_netdev完成网络设备的注册,网络设备注册我们在上一节中已经说过,这里不再赘述。
看一下net_device中几个关键的函数:
//在设备将打开的时候,调用此函数
netdev->open = e100_open;
//在设备停用的时候调用此函数
netdev->stop = e100_close;
//设备发送数据的时候调用此函数
netdev->hard_start_xmit = e100_xmit_frame;
到此时,网卡的初始化工作已经完成了。之后就可以操作网卡了。
那网卡应该怎么使用呢?必须首先唤起网卡,即使之UP,例如 ifconfig eth0 up
此时,内核会根据接口名字“eth0”找到对应的net_device.然后调用 net_device-> open.即:e100_open。
分析如下:
static int e100_open(struct net_device *netdev)
{
struct nic *nic = netdev_priv(netdev);
int err = 0;
//网卡正在UP,关闭载波信号
netif_carrier_off(netdev);
if((err = e100_up(nic)))
DPRINTK(IFUP, ERR, "Cannot open interface, aborting.\n");
return err;
}
我们关心的是e100_up。跟踪如下:
static int e100_up(struct nic *nic)
{
int err;
//分配收包队列
if((err = e100_rx_alloc_list(nic)))
return err;
//分配控制队列
if((err = e100_alloc_cbs(nic)))
goto err_rx_clean_list;
//硬件初始化
if((err = e100_hw_init(nic)))
goto err_clean_cbs;
//多播
e100_set_multicast_list(nic->netdev);
//开始接收数据
e100_start_receiver(nic);
mod_timer(&nic->watchdog, jiffies);
//注册中断例程
if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ,
nic->netdev->name, nic->netdev)))
goto err_no_irq;
//启用中断
e100_enable_irq(nic);
netif_wake_queue(nic->netdev);
return 0;
err_no_irq:
del_timer_sync(&nic->watchdog);
err_clean_cbs:
e100_clean_cbs(nic);
err_rx_clean_list:
e100_rx_clean_list(nic);
return err;
}
在此函数中,我们可以看到,它主要完成了:接立接收环形DMA缓冲区。注册了中断处理函数
关于环形DMA缓冲区接立是由e100_rx_alloc_list(nic)完成的
static int e100_rx_alloc_list(struct nic *nic)
{
struct rx *rx;
// nic->params.rfds.count,接收缓存的总个数
unsigned int i, count = nic->params.rfds.count;
//rx_to_use:正在存在数据的位置
//rx_to_clean:数据的初始为止。所以。数据的有限位置是从rx_to_use到rx_to_use
nic->rx_to_use = nic->rx_to_clean = NULL;
if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC)))
return -ENOMEM;
memset(nic->rxs, 0, sizeof(struct rx) * count);
//遍历并建立循环链表
for(rx = nic->rxs, i = 0; i < count; rx++, i++) {
rx->next = (i + 1 < count) ? rx + 1 : nic->rxs;
rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1;
if(e100_rx_alloc_skb(nic, rx)) {
e100_rx_clean_list(nic);
return -ENOMEM;
}
}
//初始化起如位置为nic->rxs
nic->rx_to_use = nic->rx_to_clean = nic->rxs;
return 0;
}
为设备建立DMA映射的主函数为e100_rx_alloc_skb().分析如下:
static inline int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
{
unsigned int rx_offset = 2; /* u32 align protocol headers */
if(!(rx->skb = dev_alloc_skb(RFD_BUF_LEN + rx_offset)))
return -ENOMEM;
/* Align, init, and map the RFD. */
rx->skb->dev = nic->netdev;
//在数据存储区之前空出offset空间
skb_reserve(rx->skb, rx_offset);
//skb->data前部置RFD
memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd));
//DMA内存映射,映射至skb->data
rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
/* Link the RFD to end of RFA by linking previous RFD to
l this one, and clearing EL bit of previous. */
//初始化前一个skb中的控制信息
if(rx->prev->skb) {
struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data;
put_unaligned(cpu_to_le32(rx->dma_addr),
(u32 *)&prev_rfd->link);
wmb();
prev_rfd->command &= ~cpu_to_le16(cb_el);
pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
sizeof(struct rfd), PCI_DMA_TODEVICE);
}
return 0;
}
在这个函数里,主要完成了:DMA环形链表的建立。在这里涉及到了一个重要的数据结构sk_buff.稍后再给出它的结构分析。在这里我们只要知道在skb->data里储存的是接收数据就OK了。值得一提的是,Intel 100M 网卡对接收数据的处理,跟平时遇到的网卡不一样,接收数据时会由接收控制RU写入接收信息,由此判断接收是否完全等信息。也就是我们在代码里面看到的rfd.所以,在skb->data对应的就是rfd+网络传过来的数据.
到这里,接收准备工作已经完成了。