Linux网卡驱动(1)-网卡驱动架构分析

1.Linux网络子系统

Linux网卡驱动(1)-网卡驱动架构分析_第1张图片

  • 我们这里研究内核空间即可,在内核空间分成5层,分别是:
    • 1、系统调用接口,它面向的客户是应用层序,为应用程序提供访问网络子系统的统一方法,比如说socket,send等函数的系统调用
    • 2、协议无关接口,它提供通用的方法来使用传输层协议,把所有的协议统一起来
    • 3、网络协议,它的作用就是实现具体的网络协议
    • 4、设备无关接口,这个接口是为了屏蔽底层硬件的差异
    • 5、设备驱动,这里实现具体的网卡驱动

2.重要的数据结构

2.1 网卡描述结构

  • 在Linux内核中,每个网卡都由一个net_device结构来描述,其中的一些重要成员有:
    • char name[IFNAMSIZ]:设备名,如:eth%d
    • unsigned long base_addr :I/O基地址
    • const struct net_device_ops *netdev_ops

2.2 网卡操作集合

  • 类似于字符设备驱动中的file_operations结构,net_device_ops结构记录了网卡所支持的操作,当用户使用网络传输数据时,内核就会找到这些操作函数来实现具体的硬件操作,比如说DM9000的操作函数是如下的:
static const struct net_device_ops dm9000_netdev_ops =
{
    .ndo_open = dm9000_open,
    .ndo_stop = dm9000_stop,
    .ndo_start_xmit = dm9000_start_xmit,
    .ndo_do_ioctl = dm9000_ioctl,
    .ndo_validate_addr = eth_validate_addr,
    .ndo_set_mac_address = eth_mac_addr,
};

2.3 网络数据包

  • 这里还有另外一个重要的结构,sk_buff。在应用程序发送数据时,数据包在内核空间中传递,由上到下或者由下到上,这个时候必须使用一个结构来描述这种数据包,sk_buf起到的作用就是这个。

Linux网卡驱动(1)-网卡驱动架构分析_第2张图片

  • Linux内核中的每个网络数据包都由一个套接字缓冲区结构 struct sk_buff 描述,即一个sk_buff结构就是一个网络包,指向sk_buff的指针通常被称做skb。在这个结构中有这4个成员:
    • 1、head,包头
    • 2、data,数据起始位置
    • 3、tail,数据结尾的位置
    • 4、end,包尾

3.网卡驱动架构分析

  • 我们这里分析CS8900这个网卡驱动,这个分析过程主要分为3步:
    • 1、网卡初始化分析
    • 2、数据发送分析
    • 3、数据接收分析

3.1 网卡初始化分析

  • 网卡驱动初始化主要在函数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);        //注册网卡驱动
}
  • 初始的第一项工作是分配一个net_device结构:
    • struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
  • 第二项工作就是初始化这个结构:
    • dev->irq = irq;  // 初始化中断号
    • dev->base_addr = io;  // 初始化设备基地址
  • 第三项工作就是调用cs89x0_probe1完成其他的初始化:
    • 1、初始化MAC地址
    • 2、初始化netdev_ops操作集函数
    • 3、初始化硬件
    • 4、注册网卡驱动

Linux网卡驱动(1)-网卡驱动架构分析_第3张图片

3.2 数据发送分析

  • 怎么寻找发送数据的函数呢?联系上面的netdev_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,
};
  • ndo_start_xmit对应的函数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]);
	}

	/* keep the upload from being interrupted, since we
                  ask the chip to start transmitting before the
                  whole packet has been completely uploaded. */

	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) {
		/*
		 * Gasp!  It hasn't.  But that shouldn't happen since
		 * we're waiting for TxOk, so return 1 and requeue this packet.
		 */

		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);

	/*
	 * We DO NOT call netif_wake_queue() here.
	 * We also DO NOT call netif_start_queue().
	 *
	 * Either of these would cause another bottom half run through
	 * net_send_packet() before this packet has fully gone out.  That causes
	 * us to hit the "Gasp!" above and the send is rescheduled.  it runs like
	 * a dog.  We just return and wait for the Tx completion interrupt handler
	 * to restart the netdevice layer
	 */

	return NETDEV_TX_OK;
}
  • 1、首先调用netif_stop_queue,这个函数用于告诉上层协议栈,暂停往网卡中发送数据。
  • 2、然后skb中的数据写入寄存器中并发送走
  • 3、释放skb空间
/* 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);
  • 但是到这里并不算完,如果这就完了上层协议还是无法向网卡发送数据,网卡不能正常工作,显然这是不正常的。那么在什么地方重新允许上层协议向网卡发送数据包呢?
  • 其实,当网卡发送走一个数据包后,会进入网卡中断程序中,查找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(dev)。

Linux网卡驱动(1)-网卡驱动架构分析_第4张图片

3.3 数据接收分析

  • 接收数据一般在中断中进行的。找到static irqreturn_t net_interrupt(int irq, void *dev_id)这个函数。这里面实现对各种中断的处理,比如说接收数据中断:
  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、读取接收状态寄存器​:status = readword(ioaddr, RX_FRAME_PORT);
  • 2、读取接收的长度:length = readword(ioaddr, RX_FRAME_PORT);
  • 3、构造一个skb:skb = dev_alloc_skb(length + 2);
  • 4、然后将受到的数据填入skb包:skb_reserve(skb, 2);
  • 5、最后把skb包交给上层协议栈:netif_rx(skb);

Linux网卡驱动(1)-网卡驱动架构分析_第5张图片

你可能感兴趣的:(Linux底层驱动,网卡驱动架构分析)