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

news/2024/11/29 9:42:50/

1.Linux网络子系统

  • 我们这里研究内核空间即可,在内核空间分成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内核中的每个网络数据包都由一个套接字缓冲区结构 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、注册网卡驱动

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 weask the chip to start transmitting before thewhole 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)。

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


http://www.ppmy.cn/news/382850.html

相关文章

网络驱动

工作需要写了我们公司一块网卡的Linux驱动程序。经历一个从无到有的过程&#xff0c; 深感技术交流的重要。Linux作为挑战微软垄断的强有力武器&#xff0c;日益受到大家的喜 爱。真希望她能在中国迅速成长。把程序文档贴出来&#xff0c;希望和大家探讨Linux技术 和应用&#…

关于debian网卡驱动

1、查看驱动信息的命令&#xff1a; 查看基本信息&#xff1a;lspci 22:00.0 有线网卡 25:00.0 无线网卡 26:00.0 Nvidia独立显卡 查看详细信息&#xff1a;lspci -vvv 有线网卡使用的驱动为&#xff1a;r8169 无线网卡使用的驱动为&#xff1a;iwlwif…

断网怎么装网卡驱动?

我们都知道电脑需要安装网卡驱动才能连接上网&#xff0c;不过有些网友重装系统或者执行了一些操作后导致电脑断网了&#xff0c;不知道断网状态下如何安装网卡驱动。下面教下大家电脑断网状态下安装网卡驱动的方法。 断网怎么装网卡驱动&#xff1f; 方法一&#xff1a;更新驱…

网络适配器、网卡和网卡驱动

网络适配器&#xff08;网卡&#xff09; 网络适配器其实就是计算机内部的网络连接设备&#xff0c;也就是俗称的网卡。网卡分为有线网卡和无线网卡&#xff0c;有线网卡能够支持宽带有线网络的连接和网络访问&#xff0c;而无线网卡则支持无线wifi局域网的网络连接和访问。 …

如何装计算机网络驱动,网卡驱动安装,详细教您电脑网卡驱动怎么安装

现在&#xff0c;我们经常会跟电脑打交道&#xff0c;电脑就像是我们的朋友一样。给我们带来极大的帮助&#xff0c;但是也会给我们制造问题的时候&#xff0c;例如电脑用着用着就没有了网络了&#xff0c;经过一番检查发现是网卡驱动出现了故障&#xff0c;那么该怎么去下载安…

Linux系统安装网卡驱动

有时候我们装完Centos或Ubuntu系统会发现没有网卡信息&#xff0c;说明网卡驱动不配置&#xff0c;所以我们要重新安装。 首先去服务器主板官网下载网卡驱动&#xff0c;注意32位和64位。 先查看网卡型号 lspci | grep -i ethernet 针对i219-v网卡的linux版本的驱动下载地址…

STL之stack和queue

目录 stack和queue模拟实现一.介绍1.stack的类模板2.queue的类模板3.容器适配器 二. deque类1. 简介2.常用成员函数 三. stack模拟实现1.成员函数2.代码 四.queue的模拟实现1.成员函数2.代码 五.小结1.容器适配器 stack和queue模拟实现 一.介绍 1.stack的类模板 LIFO&#xf…

蓝牙耳机什么牌子的好,蓝牙耳机十大品牌榜单

蓝牙耳机什么牌子的好&#xff01;耳机已然成为了很多年轻人出门的必备数码装备&#xff0c;对于现在喜欢简单生活的他们来说&#xff0c;现在的耳机很多都采用了有线连接&#xff0c;让用户聆听的时候还需要解开线团&#xff0c;非常的麻烦&#xff0c;蓝牙耳机的出现就帮助用…