linux-2.6.22.6内核网卡驱动框架分析

news2024/11/17 11:40:17

网络协议分为很多层,而驱动这层对应于实际的物理网卡部分,这也是最底层的部分,以cs89x0.c这个驱动程序为例来分析下网卡驱动程序框架。

正常开发一个驱动程序时,一般都遵循以下几个步骤:
1.分配某个结构体
2.设置该结构体
3.注册
4.硬件相关操作

首先分析cs89x0.c的入口函数

int __init init_module(void)
{
	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
	struct net_local *lp;
	int ret = 0;

#if defined(CONFIG_ARCH_S3C2410)
    unsigned int oldval_bwscon;     /* 用来保存BWSCON寄存器的值 */
    unsigned int oldval_bankcon3;   /* 用来保存S3C2410_BANKCON3寄存器的值 */
#endif

#if DEBUGGING
	net_debug = debug;
#else
	debug = 0;
#endif
	if (!dev)
		return -ENOMEM;

#if defined(CONFIG_ARCH_S3C2410)
    // 将CS8900A的物理地址转换为虚拟地址,0x300是CS8900A内部的IO空间的偏移地址
    dev->base_addr = io = (unsigned int)ioremap(S3C24XX_PA_CS8900, SZ_1M) + 0x300;
    dev->irq = irq = cs8900_irq_map[0]; /* 中断号 */

    /* 设置默认MAC地址,
     * MAC地址可以由CS8900A外接的EEPROM设定(有些单板没接EEPROM),
     * 或者启动系统后使用ifconfig修改
     */
    dev->dev_addr[0] = 0x08;
    dev->dev_addr[1] = 0x89;
    dev->dev_addr[2] = 0x89;
    dev->dev_addr[3] = 0x89;
    dev->dev_addr[4] = 0x89;
    dev->dev_addr[5] = 0x89;

    /* 设置Bank3: 总线宽度为16, 使能nWAIT, 使能UB/LB。by www.100ask.net */
    oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON);
    *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<12)) \
        | S3C2410_BWSCON_DW3_16 | S3C2410_BWSCON_WS3 | S3C2410_BWSCON_ST3;

    /* 设置BANK3的时间参数, by www.100ask.net */
    oldval_bankcon3 = *((volatile unsigned int *)S3C2410_BANKCON3);
    *((volatile unsigned int *)S3C2410_BANKCON3) = 0x1f7c;    
#else
	dev->irq = irq;
	dev->base_addr = io;
#endif
	lp = netdev_priv(dev);

#if ALLOW_DMA
	if (use_dma) {
		lp->use_dma = use_dma;
		lp->dma = dma;
		lp->dmasize = dmasize;
	}
#endif

	spin_lock_init(&lp->lock);

        /* boy, they'd better get these right */
        if (!strcmp(media, "rj45"))
		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
	else if (!strcmp(media, "aui"))
		lp->adapter_cnf = A_CNF_MEDIA_AUI   | A_CNF_AUI;
	else if (!strcmp(media, "bnc"))
		lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
	else
		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;

        if (duplex==-1)
		lp->auto_neg_cnf = AUTO_NEG_ENABLE;

        if (io == 0) {
                printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
                printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
                ret = -EPERM;
		goto out;
        } else if (io <= 0x1ff) {
		ret = -ENXIO;
		goto out;
	}

#if ALLOW_DMA
	if (use_dma && dmasize != 16 && dmasize != 64) {
		printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
		ret = -EPERM;
		goto out;
	}
#endif
	ret = cs89x0_probe1(dev, io, 1);
	if (ret)
		goto out;

	dev_cs89x0 = dev;
	return 0;
out:
#if defined(CONFIG_ARCH_S3C2410)
    iounmap(dev->base_addr);

    /* 恢复寄存器原来的值 */
    *((volatile unsigned int *)S3C2410_BWSCON) = oldval_bwscon;
    *((volatile unsigned int *)S3C2410_BANKCON3) = oldval_bankcon3;
#endif    
	free_netdev(dev);
	return ret;
}

入口函数里,首先分配了net_device 结构体,然后对该结构体进行进行填充,最后调用cs89x0_probe1进行下一步处理。

cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
{
......

.......

    dev->open		= net_open;
	dev->stop		= net_close;
	dev->tx_timeout		= net_timeout;
	dev->watchdog_timeo	= HZ;
	**dev->hard_start_xmit 	= net_send_packet;**
	dev->get_stats		= net_get_stats;
	dev->set_multicast_list = set_multicast_list;
	dev->set_mac_address 	= set_mac_address;
.....
.....
	retval = register_netdev(dev);

}

cs89x0_probe1里又进一步对net_device 进行了填充,其中hard_start_xmit 就是发送数据函数,然后通过register_netdev进行注册。

进一步查看net_send_packet

static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
	struct net_local *lp = netdev_priv(dev);

	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_irq(&lp->lock);
	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_irq(&lp->lock);
		if (net_debug) printk("cs89x0: Tx buffer not free!\n");
		return 1;
	}
	/* Write the contents of the packet */
	writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
	spin_unlock_irq(&lp->lock);
	lp->stats.tx_bytes += skb->len;
	dev->trans_start = jiffies;
	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 0;
}

net_send_packet里用到了sk_buff 这个结构体,sk_buff 就是数据的载体,net_send_packet里通过sk_buff 发送了数据,那数据又是如何接受的呢,其实是通过中断接受数据的,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);

	/* we MUST read all the events out of the ISQ, otherwise we'll never
           get interrupted again.  As a consequence, we can't have any limit
           on the number of times we loop in the interrupt handler.  The
           hardware guarantees that eventually we'll run out of events.  Of
           course, if you're on a slow machine, and packets are arriving
           faster than you can read them off, you're screwed.  Hasta la
           vista, baby!  */
	while ((status = readword(dev->base_addr, ISQ_PORT))) {
		if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
		handled = 1;
		switch(status & ISQ_EVENT_MASK) {
		case ISQ_RECEIVER_EVENT:
			/* Got a packet(s). */
			net_rx(dev);
			break;
		case ISQ_TRANSMITTER_EVENT:
			lp->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) lp->stats.tx_errors++;
				if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
				if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
				if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
				if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
			}
			break;
		case ISQ_BUFFER_EVENT:
			if (status & READY_FOR_TX) {
				/* we tried to transmit a packet earlier,
                                   but inexplicably ran out of buffers.
                                   That shouldn't happen since we only ever
                                   load one packet.  Shrug.  Do the right
                                   thing anyway. */
				netif_wake_queue(dev);	/* Inform upper layers. */
			}
			if (status & TX_UNDERRUN) {
				if (net_debug > 0) printk("%s: transmit underrun\n", dev->name);
                                lp->send_underrun++;
                                if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
                                else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
				/* transmit cycle is done, although
				   frame wasn't transmitted - this
				   avoids having to wait for the upper
				   layers to timeout on us, in the
				   event of a tx underrun */
				netif_wake_queue(dev);	/* Inform upper layers. */
                        }
#if ALLOW_DMA
			if (lp->use_dma && (status & RX_DMA)) {
				int count = readreg(dev, PP_DmaFrameCnt);
				while(count) {
					if (net_debug > 5)
						printk("%s: receiving %d DMA frames\n", dev->name, count);
					if (net_debug > 2 && count >1)
						printk("%s: receiving %d DMA frames\n", dev->name, count);
					dma_rx(dev);
					if (--count == 0)
						count = readreg(dev, PP_DmaFrameCnt);
					if (net_debug > 2 && count > 0)
						printk("%s: continuing with %d DMA frames\n", dev->name, count);
				}
			}
#endif
			break;
		case ISQ_RX_MISS_EVENT:
			lp->stats.rx_missed_errors += (status >>6);
			break;
		case ISQ_TX_COL_EVENT:
			lp->stats.collisions += (status >>6);
			break;
		}
	}
	return IRQ_RETVAL(handled);
}

net_interrupt里又调用net_rx(dev);进行处理

net_rx(struct net_device *dev)
{
	struct net_local *lp = netdev_priv(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, lp);
		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
		lp->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->last_rx = jiffies;
	lp->stats.rx_packets++;
	lp->stats.rx_bytes += length;
}

net_rx里也会构造一个sk_buff 结构体,然后调用netif_rx(skb);进行发包。

总结:发送数据和接受数据是通过hard_start_xmit 和netif_rx完成的,而数据的载体都是sk_buff 结构体。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/707329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA将java项目打包为jar包

方法 首先在src -> resources目录下建立一个文件夹&#xff0c;然后再在新建文件夹里面建立META-INF文件夹&#xff08;不推荐直接建立META-INF&#xff0c;否则后面打包完的jar包需要手动修改配置&#xff09; 然后点击File -> Project Structure -> Artifacts -&g…

第三章:Faster R-CNN网络详解(《Faster R-CNN: 基于区域提议网络的实时目标检测》)

(目标检测篇&#xff09;系列文章目录 第一章:R-CNN网络详解 第二章:Fast R-CNN网络详解 第三章:Faster R-CNN网络详解 第四章:YOLO v1网络详解 第五章:YOLO v2网络详解 第六章:YOLO v3网络详解 文章目录 系列文章目录技术干货集锦前言一、摘要二、正文分析 1.引入库2.读…

Mysql的逻辑架构_读写锁_事物

概览 一. MySql的逻辑架构1. 逻辑架构图2. 连接管理与安全性 二. 并发控制1. 读写锁2. 锁粒度 三. 事务1. 特性2. 隔离级别3. 死锁4. 事物日志&#xff1f;5.MySql中的事物 mysql最与众不同的特性&#xff1a;存储引擎架构 架构的设计&#xff1a; 将查询处理(Query Processin…

7、注解与自定义注解

1 注解 注解很厉害&#xff0c;它可以增强我们的java代码&#xff0c;同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。 传统我们通过xml文本文件声明方式(如下图,但是XML比较繁琐且不易检查)&#xff0c;而现在最主流的开发都是基于注解方式&#xff0c…

房贷计算器——新增选择还款方式

房贷计算器——新增选择还款方式 #!/usr/bin/env python # coding: utf-8# In[4]: 文字‘房贷计算器’ 文字‘贷款总金额’&#xff1a;输入框 文字‘贷款期限’&#xff1a;输入框 文字‘年利率’&#xff1a;输入框 按钮‘开始计算’ 返回&#xff1a; 月供 总利息 from tki…

【Framework】bindService启动流程

前言 在【Service启动流程之startService】 中&#xff0c;我们已经分析了startService的流程&#xff0c;这篇就继续讲bindService的流程&#xff0c;他们两有很多相似之处。同样&#xff0c;流程图在总结处。 我们在调用bindService方法时候&#xff0c;实际调用的是Contex…

台庆|三联开关怎么接线?

三联开关是一种常见的开关类型&#xff0c;通常用于控制一个电路中的三个不同的电器或灯具。它的用途非常广泛&#xff0c;因此了解如何正确接线是非常重要的。在本文中&#xff0c;我们将详细讨论三联开关的接线方法。 我们先来看看三联开关实物图与线路图&#xff1a; 接下来…

【音视频处理】FFmpeg详解,命令行、源码、编译安装

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论FFmpeg。 这里先提一个问题&#xff0c;FFmpeg命令行功能如此强大&#xff0c;为什么还需要舍近求远地调用库函数呢 &#xff1f; 我们按这样的顺序讨论 &#xff1a; 1、 FFmpeg命令行说明 2、 FFmpeg代码结构…

如何在 JavaScript 中压缩字符串

在 JavaScript 中&#xff0c;可以有范围很广的压缩&#xff0c;比如 gzip 之类的文件压缩等等。 在这里&#xff0c;我们将讨论两种压缩字符串的方法。 最初&#xff0c;我们将重点介绍霍夫曼算法。 稍后&#xff0c;我们将介绍解决任务的 LZString 方法。 在 JavaScript 中使…

主成分分析系列(一)概览及数据为何要中心化

一、概览 主成分分析&#xff08;Principle Component Analysis&#xff0c;PCA&#xff09;算法属于数据降维算法里面的一种。数据降维算法的主要想法是从高维度数据中找到一种结构&#xff0c;这种结构蕴含了数据中的大部分信息&#xff0c;从而将高维数据降维到低维数据&am…

Apikit 自学日记:参数构造器

构造器是测试时系统提供的快速生成请求数据的工具。一般用于快速对数据进行加密和生成随机数值。可在请求参数中某个字段的右侧选择构造器操作&#xff0c;通过构造器生成该字段的参数值。构造器由两种类型的操作组成&#xff1a;设置初始数据和多重操作。 设置初始数据 其中初…

基于C语言的开源csv解析库:MiniCSV使用示例

文章目录 MiniCSV简介官方示例csv文件解析示例CodeBlocks工程下载 MiniCSV简介 之前写了一篇基于C语言字符串操作函数的csv文件解析&#xff1a;C语言解析csv格式文件&#xff0c;本文介绍一个开源简洁的csv解析库的使用&#xff1a;MiniCSV&#xff0c;使用标准C语言设计。 …

Spring Boot 中的 Redis 的数据操作配置和使用

Spring Boot 中的 Redis 的数据操作配置和使用 Redis 是一种高性能的 NoSQL 数据库&#xff0c;它支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合和有序集合。Redis 还提供了丰富的命令&#xff0c;可以对数据进行快速的 CRUD 操作。Spring Boot 是一个基于 Sprin…

数据结构--栈的引用--前中后缀表达式(前部分)

数据结构–栈的引用–前中后缀表达式(前部分) 常见的算数表达式 由三个部分组成: 操作数、运算符、界限符 \color{red}操作数、运算符、界限符 操作数、运算符、界限符 ps:界限符是必不可少的,反映了计算的先后顺序 波兰表达式(让计算机更容易识别的算数表达式) Reverse Po…

高性能分布式缓存Redis(一) 快速实战

一、缓存发展史&缓存分类 1.1、大型网站中缓存的使用 访问量越大&#xff0c;响应力越差&#xff0c;用户体验越差。 引入缓存、示意图如下&#xff1a; 读写策略&#xff1a; Cache Aside Pattern&#xff08;旁路缓存模式&#xff09;Read/Write Through Pattern&am…

AA-TransUNet github: 用于预测任务的注意力增强的TransUNet

文章目录 来源AA_TransUNet架构数据集和预训练模型使用作者 来源 github地址 AA_TransUNet架构 数据集和预训练模型 如果你对本文中使用的数据集&#xff08;降水图和云量数据集&#xff09;感兴趣&#xff0c;请访问SmaAt-UNet了解更多细节。 对于预训练的AA_TransUNet模型…

从磁盘看 IO

计算机上的易失和非易失存储器 常见磁盘可以分为两类&#xff1a;机械磁盘和固态磁盘。 第一类&#xff0c;机械磁盘&#xff0c;也称为硬盘驱动器&#xff08;Hard Disk Driver&#xff09;&#xff0c;通常缩写为 HDD。机械磁 盘主要由盘片和读写磁头组成&#xff0c;数据就…

认识固态继电器及其工作原理

什么是固态继电器&#xff0c;有什么优缺点&#xff1f; 固态继电器 简称SSR&#xff0c;又被称之为“无触点开关”它利用电子元件&#xff08;如双向可控硅等半导体器件&#xff09;的开关特性&#xff0c;可到达无触点无火花地接通和断开电路。 固态继电器工作可靠&#…

1.3 Metasploit 生成SSL加密载荷

在本节中&#xff0c;我们将介绍如何通过使用Metasploit生成加密载荷&#xff0c;以隐藏网络特征。前一章节我们已经通过Metasploit生成了一段明文的ShellCode&#xff0c;但明文的网络传输存在安全隐患&#xff0c;因此本节将介绍如何通过生成SSL证书来加密ShellCode&#xff…

ChatGPT应用工具推荐

ChatGPT作为一种先进的自然生成技术&#xff0c;已经在各个领域展现出了其强大的应用能力&#xff0c;下面将给大家介绍一些ChatGPT的功能应用。 简介 此系统是基于likeadmin—PHP开发的智能对话系统&#xff0c;ChatGPT是一种基于人工智能技术的聊天机器人&#xff0c;它可以…