• Linux网卡驱动架构分析


    一、网卡驱动架构

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

    二、重要数据结构

      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、定义并分配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 > 3) {
            printk("%s: sent %d byte packet of type %x
    ",
                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! "); 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.
    ", 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
    ",
                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(length + 2);

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

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

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

  • 相关阅读:
    nginx反向代理
    遇到的好玩的mvc路由
    有意思的OWIN,附脱离iis的webapi
    nginx转发配置
    SQL 2016安装中遇到的问题
    3级级联 国家--城市
    box.css
    common.css
    节假日设置
    Order_Leave.aspx
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4333419.html
Copyright © 2020-2023  润新知