【推荐阅读】
此处主要讲的是从数据来到,中断到最终数据包被处理的过程。
0:首先来介绍一下IO端口访问问题,内核提供了这样一组函数处理: // kernel/io.c中
>: inb( )、inw( )、inl( )函数
分别从I/O端口读取1、2或4个连续字节。
后缀“b”、“w”、“l”分别代表一个字节(8位)、一个字(16位)以及一个长整型(32 位)。
>: inb_p( )、inw_p( )、inl_p( )
分别从I/O端口读取1、2或4个连续字节,然后执行一条 “空指令” 使CPU暂停。 p 可以理解成pause
>: outb( )、outw( )、outl( )
分别向一个I/O端口写入1、2或4个连续字节。
>: outb_p( )、outw_p( )、outl_p( )
分别向一个I/O端口写入1、2或4个连续字节,然后执行一条“空指令”指令使CPU暂停。
>: insb( )、insw( )、insl( )
分别从I/O端口读入以1、2或4个字节为一组的连续字节序列。字节序列的长度由该函数的参数给出。
>: outsb( )、outsw( )、outsl( )
分别向I/O端口写入以1、2或4个字节为一组的连续字节序列。
1:当一个中断来到,首先响应 net_interrupt 函数
/*
* The typical workload of the driver:
* Handle the network interface interrupts.
*/
static irqreturn_t net_interrupt(int irq, void *dev_id) // 注意参数是:中断号和设备id
{
struct net_device *dev = dev_id;
struct net_local *np;
int ioaddr, status;
int handled = 0;
ioaddr = dev->base_addr; // 设备的IO地址
np = netdev_priv(dev); // 得到dev私有数据
status = inw(ioaddr + 0); // 从端口读两个字节
if (status == 0)
goto out;
handled = 1;
if (status & RX_INTR) {
/* Got a packet(s). */
net_rx(dev); // 使用这个函数net_rx来获取一个数据包 -----> receive
} // 这个函数下面会说到
#if TX_RING
if (status & TX_INTR) { // 发送数据
/* Transmit complete. */
net_tx(dev); // 发送数据使用net_tx ------> transmit
np->stats.tx_packets++; // 计数
netif_wake_queue(dev); // 处理结束,唤醒下一个队列中等待者 }
#endif
if (status & COUNTERS_INTR)
{ /* Increment the appropriate 'localstats' field. */
np->stats.tx_window_errors++;
}
out:
return IRQ_RETVAL(handled); // 返回中断
}
2:下面 需要看一下接收数据包函数net_rx
/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct net_device *dev) // 所谓接收数据包,其实就是构造skb数据结构 ^_^
{
struct net_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
int boguscount = 10;
do { // 下面是循环接收数据么
int status = inw(ioaddr); // 获取状态
int pkt_len = inw(ioaddr); // 获取包大小
if (pkt_len == 0) /* 全部接收 */
break; /* 可以结束 */
if (status & 0x40) { /* There was an error. */
lp->stats.rx_errors++;
if (status & 0x20) lp->stats.rx_frame_errors++;
if (status & 0x10) lp->stats.rx_over_errors++;
if (status & 0x08) lp->stats.rx_crc_errors++;
if (status & 0x04) lp->stats.rx_fifo_errors++;
} else {
/* Malloc up new buffer. */
struct sk_buff *skb;
lp->stats.rx_bytes+=pkt_len; // 接收的字节数+pkt_len
skb = dev_alloc_skb(pkt_len); // 需要接收多少bytes就分配多少空间给sk_buff
if (skb == NULL) { // 需要丢包
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
dev->name);
lp->stats.rx_dropped++; // 丢包数++
break;
}
skb->dev = dev; // 现在开始构建skb包
/* 'skb->data' points to the start of sk_buff data area. */
memcpy(skb_put(skb,pkt_len), (void*)dev->rmem_start, // 注意开始从dev向skb中放入数据,大小pkt_len
pkt_len);
/* or */
insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
netif_rx(skb);