
I. 网卡驱动程序体系结构
从上至下: 应用程序→系统调用接口→与协议无关的接口→网络协议栈→与设备无关的接口→设备驱动程序.
第二个重要的数据结构
1. Linux内核中的每个网卡都由net_device结构描述.
2. 网卡操作功能集: net_device_ops,此数据结构是上述net_device的成员.
3. 网络数据包: sk_buff.
三网卡驱动代码,网卡驱动程序代码分析
使用的文件是cs89x0.c,主要分析三个部分: 网卡初始化,发送数据,接收数据.
㈠网卡初始化

网卡驱动程序的初始化主要在函数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); //注册网卡驱动
}
从代码中可以看到
1. 使用alloc_etherdev函数定义和分配net_device结构.
2. 初始化net_device. (包括中断号,I / O基地址,MAC地址,netdev_ops)
3. 初始化硬件
4. 将网卡驱动程序注册到内核,使用函数register_netdev

㈡发送数据
netdev_ops初始化为值&net_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, };
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]); }
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) { 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); return NETDEV_TX_OK; }
代码的这一部分执行这些操作(以红色突出显示)
1. 通知上层协议停止向网卡发送数据.
由于网卡现在正在发送数据包,因此停止接收数据包
2. 将skb中的数据写入寄存器并发送出去

3. 释放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
㈢数据接收
网卡收到数据包后,进入网卡中断处理程序
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_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); #endif dev->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. 阅读接收状态
2,读取接收到的数据的长度
3. 分配skb结构,skb = dev_alloc_skb(长度+ 2);
4. 从硬件寄存器读取数据并将其存储在skb
5. 使用功能netif_rx
将江封装的数据包向上发送到协议栈
这里粗略分析网卡驱动程序体系结构. 如果您有任何疑问或错误,欢迎指出.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-145262-1.html
竟然有五条优质高蛋白