一、网卡驱动架构

  由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动。

二、重要数据结构

  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 = ;
  ...
  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 = ;
  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、定义并分配net_device结构,使用alloc_etherdev函数。

    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 > ) {
printk("%s: sent %d byte packet of type %x\n",
dev->name, skb->len,
(skb->data[ETH_ALEN+ETH_ALEN] << ) | skb->data[ETH_ALEN+ETH_ALEN+]);
} 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) == ) { 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 = ;   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) == ) {
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, ); /* longword align L3 header */ readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> );
if (length & )
skb->data[length-1] = readword(ioaddr, RX_FRAME_PORT); if (net_debug > ) {
printk( "%s: received %d byte packet of type %x\n",
dev->name, length,
(skb->data[ETH_ALEN+ETH_ALEN] << ) | skb->data[ETH_ALEN+ETH_ALEN+]);
} 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(length + 2);

  4、从硬件寄存器中读取数据存入skb

  5、江封装好的数据包向上发送给协议栈,使用函数netif_rx

  网卡驱动架构到这里大致就分析完了。如果有疑问或错误,欢迎指出。

最新文章

  1. SharePoint2016合规性策略中心
  2. IOS遍历网页获取网页中&lt;img&gt;标签中的图片url
  3. spring boot redis缓存JedisPool使用
  4. Filter高级开发
  5. hihocoder 1237 Farthest Point
  6. resin access.log format配置详解
  7. Android PackageInstaller 安装和卸载
  8. IO多路复用之select
  9. FormData可实现异步传输二进制文件(即异步文件上传)
  10. 强制删除sql用户链接
  11. 今天真开心,终于知道怎么打包apk了
  12. 在ASP.NET MVC中使用Web API和EntityFramework构建应用程序
  13. Kali Linux Live USB初始化+使用日记
  14. 浅谈java线程池实现
  15. Chrome插件开发,美化网页上的文件列表。chrome-extension,content-scripts
  16. MySQL ORDER BY主键id加LIMIT限制走错索引
  17. js浮点数相加、减、乘、除精确计算
  18. alpha通道与混合技术
  19. 容器(Container)Frames和Panels
  20. Kettle日常使用汇总整理

热门文章

  1. 獲取 Textarea 的光標位置(摘自網絡)
  2. Java对存储过程的调用方法 --转载
  3. Python开发【第十篇】:CSS --无内容点击-不进去(一)
  4. ViewPagerindicator 源码解析
  5. codevs1044四子连棋(Dfs)
  6. [Codeforces 501D] - Misha and Permutations Summation
  7. js 实现关键词球状旋转效果
  8. 工时统计的sql练习--包含时间处理
  9. System.Web.HttpContext.Current.Session获取值出错
  10. 『重构--改善既有代码的设计』读书笔记----Inline Method