无线网卡收包流程

news2024/10/6 8:22:12

环境

x86_64 Linux, AR9462 无线网卡,PCIe 接口
ath9k 驱动
在这里插入图片描述

收包过程

在这里插入图片描述
① 无线网卡从空气中捕获到无线数据包 【物理层】
② 无线网卡把帧 DMA 到内存的 Ring Buffer
③ 无线网卡向 CPU 发起中断请求
④ CPU 响应中断,执行 ISR,调度 tasklet,退出 ISR
⑤ tasklet 从 Ring Buffer 中取走数据包,保存为一个 skb 【数据链路层】
⑥ 驱动调用 netif_receive_skb() 将包送往内核网络协议栈
⑦ 根据包的类型,协议栈逐级向上送达
⑧ ip_rcv() 【网络层】
⑨ tcp_v4_rcv() 【传输层】
⑩ socket 接收队列
⑪ read() 【应用层】

准备工作

要想完成上面的流程,事先要一些准备工作。
无线网卡能够从空气中捕获到无线数据包的前提,是无线网卡得能正常工作,如何让无线网卡正常工作,那就是

  • 硬件正常(供电、时钟、连线),这部分是硬件工程师的工作,软件工程师不必过多关心
  • 无线网卡配置正常
    • 安装驱动
      • 初始化无线网卡(使能、读写配置空间、注册中断、初始化 DMA)
      • 初始化软件资源(初始化中断下半部(tasklet、workqueue、softirq)、申请 RingBuff)
      • 向内核注册网络设备

其中

  • 使能无线网卡,是为了让无线网卡开始工作
  • 注册中断,是为了无线网卡有数据时可以通知 CPU
  • 初始化 DMA,是为了无线网卡能够直接向内存读写数据,提高吞吐量
  • 初始化中断下半部,是为了让上半部能尽可能快的返回,将剩余任务留给下半部执行。减轻中断对 CPU 响应其它中断的影响,减轻中断对 CPU 调度进程的影响。
  • 申请 RingBuff 主要是给 DMA 使用
  • 向内核注册网络设备,是为了和内核网络协议栈打通

准备工作代码分析

module_init(ath9k_init);
	ath9k_init()
		ath_pci_init()
			pci_register_driver(ath_pci_driver)

static struct pci_driver ath_pci_driver = {
	.name       = "ath9k",
	.id_table   = ath_pci_id_table,
	.probe      = ath_pci_probe,
	.remove     = ath_pci_remove,
	.driver.pm  = ATH9K_PM_OPS,
};

ath_pci_probe
	pcim_enable_device()    // 在驱动程序可以访问 PCI 设备的任何设备资源之前(I/O 区域或者中断),驱动程序必须先调用 pci_enable_device(),该函数会激活设备,把设备唤醒,在某些情况下还指派它的中断线和 I/O 区域
		pci_enable_device() // 追代码,发现其实就是将 Command 域的 bit0 和 bit1 置为 1,从而达到开启设备的目的
			pci_enable_device_flags()//使能设备
			pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);    // 读取电源状态,Power Management Registers
			pci_upstream_bridge() // 所在的桥
			pci_enable_bridge(bridge); // 使能桥
			do_pci_enable_device()
			如果开启 msi 中断,直接 return,
			否则 pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); //读取中断引脚,并使能中断
			pci_set_dma_mask()
			pci_set_consistent_dma_mask() // 会将掩码赋值给 struct device 结构体中的 coherent_dma_mask,当调用dma_alloc_coherent 申请 DMA 内存时,会检测 DMA mask,从而根据对应的掩码来分配地址
			pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); // 读取缓存行大小
			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
	pci_set_master(pdev);    // 将该设备设置为主设备
	ath9k_fill_chanctx_ops()
	hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); // This must be called once for each hardware device
	request_irq(, ath_isr()) // ath_isr() : interrupt service routine 中断服务程序
		request_threaded_irq()
			__setup_irq() // 注册中断
				enable_irq(irq);    // 使能中断
	ath9k_init_device()
		ath9k_init_softc() // Bring up device
			ah->reg_ops.read = ath9k_ioread32;              
			ah->reg_ops.write = ath9k_iowrite32;
			init_waitqueue_head(&sc->tx_wait);
			ath9k_init_soc_platform()
			ath9k_eeprom_request(sc, pdata->eeprom_name);
			spin_lock_init(&common->cc_lock);spin_lock_init(&sc->sc_serial_rw);
			spin_lock_init(&sc->sc_pm_lock);
			spin_lock_init(&sc->chan_lock);
			mutex_init(&sc->mutex);
			tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
			tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc);
			setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
			INIT_WORK(&sc->hw_reset_work, ath_reset_work);
			INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
			INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
			ath9k_init_channel_context()
			ath_read_cachesize(common, &csz); // 读取缓存行大小,一般为 64 字节 ,用来和硬件通信
			ath9k_hw_init(ah) // 初始化硬件
				__ath9k_hw_init(ah);
					ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON) //reset
						ath9k_hw_set_reset_power_on()
							ath9k_hw_set_reset(ah, ATH9K_RESET_WARM)
						ath9k_hw_init_defaults(ah);//硬件初始化为默认值
						ath9k_hw_attach_ops()
							ar9003_hw_attach_ops(ah);
						ath9k_hw_post_init()
							ath9k_hw_ani_init()//ATH9K_ANI_OFDM,ATH9K_ANI_CCK
								ath9k_ani_restart(ah);     // reset
				get_eeprom(ah, EEP_REG_0)//eeprom
				ath9k_hw_fill_cap_info()//填充硬件能力
				ath9k_hw_init_macaddr()//初始化MAC
				ath9k_hw_init_hang_checks()//hang 检测
			ath9k_init_queues()
				ath9k_hw_beaconq_setup()
					ath9k_hw_setuptxqueue()
						ath9k_hw_set_txq_props()
							ath9k_hw_set_txq_props()
				ath_txq_setup()
			ath9k_cmn_init_channels_rates() // 初始化信道速率
			ath9k_init_p2p()
			ath9k_cmn_init_crypto()
			ath_fill_led_pin()
		ath_tx_init(sc, ATH_TXBUF) /* Setup TX DMA */ ATH_TXBUF = 512
			ath_descdma_setup()//描述符 DMA
		ath_rx_init(sc, ATH_RXBUF);/* Setup RX DMA */
		ath9k_init_txpower_limits()//发射功率限制
		ieee80211_register_hw() // Register with mac80211 注册网络接口,内核实现
			wiphy_register()
				rfkill_register()
			ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,  NL80211_IFTYPE_STATION, &params);
				cfg80211_register_netdevice()
					register_netdevice()
		ath9k_init_debug()
		ath_start_rfkill_poll()
	ath9k_hw_name()

几个重点函数

  • pci_enable_device() : 设备使能
  • request_irq() : 注册中断
  • ieee80211_register_hw() : 向内核注册网络设备
  • tasklet_init() : 初始化 tasklet,为中断下半部做好准备

收包流程代码分析

ath_isr()
    tasklet_schedule(&sc->intr_tq); // 调度 tasklet
        ath9k_tasklet()
            case rxmask: // 处理 Rx 方向数据包
                ath_rx_tasklet()
                    bf = ath_edma_get_next_rx_buf(sc, &rs, qtype);
                        ath_edma_get_buffers(sc, qtype, rs, &bf)
                    ath9k_rx_skb_preprocess();
                    ieee80211_rx(hw, skb); // include/net/mac80211.h
                        ieee80211_rx_napi() // net/mac80211/rx.c
                            ieee80211_rx_monitor()
                            netif_receive_skb() // net/core/dev.c,将包送往内核网络协议栈
                                netif_receive_skb_internal()
                                    __netif_receive_skb()
                                        __netif_receive_skb_core()
                                            type = skb->protocol; // ip
                                                deliver_ptype_list_skb()
                                                    deliver_skb()
                                                        pt_prev->func() // 协议对应的回调函数,对于 IP 包来说,就会进入 ip_rcv()
                                                            ip_rcv()
                                                                ip_rcv_finish()
                                                                    ip_route_input_noref()
                                                                        ip_route_input_rcu()
                                                                            ip_route_input_mc()
                                                                                rth->dst.input = ip_mr_input;
                                                                    dst_input()
                                                                        ip_mr_input()
                                                                            ip_local_deliver()
                                                                                ip_local_deliver_finish()
                                                                                    ipprot = rcu_dereference(inet_protos[protocol]); // inet_protos 中保存着 tcp_v4_rcv 和 udp_rcv 的函数地址
                                                                                        tcp_v4_rcv()

几个重点函数

  • ath_isr():硬中断,中断上半部
  • tasklet_schedule():调度 tasklet
  • ath9k_tasklet():中断下半部(中断下半部除了这里使用的 tasklet,还有 softirq 和 workqueue)
  • netif_receive_skb():将包送往内核网络协议栈
  • ip_rcv():到达网络层
  • tcp_v4_rcv():到达传输层

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

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

相关文章

fatfs相关宏定义说明

fat16文件系统DBR 宏定义如下: #define BS_JmpBoot 0 //跳转指令。3字节。/* x86 jump instruction (3-byte) */ #define BS_OEMName 3 //OEM名称。8字节。/* OEM name (8-byte) */ #define BPB_BytsPerSec 11 //扇区字节数。2字节。/* Sector size [byte…

Python学习基础笔记四十五——包

C:\Program Files\Python310\Lib 1、 包的概念:把解决同一类问题的模块放在同一个文件夹里,我们就称为包。 2、在PyCharm中创建一个包: 文件夹中会有一个init.py文件。 在Python3中没有这个__init__.py文件也没有问题。 3、在包A和包B下有同…

Go设计与实现 -- map哈希表

Go语言中的哈希表有它自己的一套实现方式。和Java的哈希表还是有些不同的,但是为了保证知识体系的完整性,我还是准备从头开始说起。 哈希表设计原理 哈希表的读写时间复杂度是O(1),因为它提供了键值之间的映射。要实现一个性能优异的哈希表…

spring——AOP面向切面编程——基于XML的AspectJ AOP开发(转载)

我们可以在 Spring 项目中通过 XML 配置,对切面(Aspect 或 Advisor)、切点(PointCut)以及通知(Advice)进行定义和管理,以实现基于 AspectJ 的 AOP 开发。Spring 提供了基于 XML 的 AOP 支持,并提供了一个名为“aop”的命名空间,该…

Docker笔记--使用Docker部署MySQL

1--Docker部署MySQL ① 搜索MySQL镜像 docker search mysql ② 拉取MySQL镜像 docker pull mysql:latest ③ 创建MySQL容器 首先在宿主机创建MySQL的映射目录,即数据卷: cd /home mkdir mysql cd mysql 之后在当前目录创建MySQL容器($PW…

【事故复盘】吐血整理一场线上事故——CPU飙升200%

🔍 经验使你在第二次犯相同错误时及时发现。 —— 琼斯 🔖 事故时间:12.7日 17:43 ~ 21:03 🔖 事故影响:服务接口查询慢,用户查看数据转圈 🔖 事故过程: 那是一个宛如平静的下午&…

128-150-mysql-高级篇-索引及调优篇

115-mysql-高级篇-索引及调优篇: 索引及调优篇 1、索引的创建与设计原则 1. 索引的声明与使用 1.1 索引的分类 从功能逻辑上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引、全文索引。按照物理实现方式,索引可以分…

BUUCTF Web1

[极客大挑战 2019]EasySQL 试了个万能密码就能上去了 flag: flag{8e685472-02e4-440a-b04e-b0d9b6d9c27f} [HCTF 2018]WarmUp 看源码有个source.php <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source&qu…

Mysql 创建存储过程和函数及各种例子

Mysql 创建存储过程和函数及各种例子1. Mysql 创建存储过程1.1 前言知识1.1.1 语法结构1.1.2 简单解释1.2 创建存储过程入门例子1.2.1 无参存储过程1.2.1.1 不带变量1.2.1.2 带变量1.2.2 有入参的存储过程1.2.3 有出参的存储过程1.2.4 有入参和存储的存储过程1.2.5 inout的存储…

如何将镜像上传至阿里云?如何从阿里云中拉取自己的镜像?

目录 如何将制作好的镜像上传至阿里云&#xff1f; 一、前期准备 1、注册阿里云账户 2、登录账号 3、配置Docker加速器 4、创建镜像仓库的命名空间&#xff08;私有的&#xff09; 5.创建镜像仓库(创建镜像仓库时要绑定一个代码托管网站&#xff0c;例如&#xff1a;githu…

Zookeeper集群搭建

文章目录前言Zookeeper集群搭建&#xff08;一&#xff09;LeaderFollower模式&#xff08;二&#xff09;下载zookeeeper的压缩包&#xff08;三&#xff09;解压&#xff08;四&#xff09;修改配置文件&#xff08;五&#xff09;添加myid配置&#xff08;六&#xff09;安装…

痞子衡嵌入式:存储器大厂Micron的NOR Flash芯片特殊丝印设计(FBGA代码)

大家好&#xff0c;我是痞子衡&#xff0c;是正经搞技术的痞子。今天痞子衡给大家讲的是存储器大厂Micron的NOR Flash芯片特殊丝印设计(FBGA代码)。 痞子衡之前写过一篇文章 《J-Flash在Micron Flash固定区域下载校验失败的故事》&#xff0c;这篇文章里提及了 Micron 家的串行…

明峰医疗IPO终止:亏损超过14亿元,王瑶法、潘华素夫妇为实控人

近日&#xff0c;上海证券交易所科创板披露的信息显示&#xff0c;明峰医疗系统股份有限公司&#xff08;下称“明峰医疗”&#xff09;向上海证券交易所提交了撤回上市申请文件的申请&#xff0c;保荐人海通证券提交了撤回保荐的申请。 因此&#xff0c;上海证券交易所决定终…

设计模式之创建型模式---单例模式

文章目录1.介绍2.应用场景3.实现3.1 结构3.2 类图3.3 代码示例3.3.1 饿汉式3.3.2 懒汉式3.3.3 双重检验锁3.3.3 静态内部类实现单例3.3.4 枚举类实现单例总结1.介绍 单例模式(singleton) 是指某个类中能生成一个实例&#xff0c;该类提供了一个全局访问点&#xff0c;提供一个唯…

JUC并发编程与源码分析笔记07-volatile与JMM

被volatile修饰的变量有两大特点 可见性、有序性&#xff0c;但是不保证原子性。 当写一个volatile变量时&#xff0c;JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。当读一个volatile变量时&#xff0c;JMM会把该线程对应的本地内存设置为无效&#xff0c;重…

Java开发:汇编语言

一、为什么学习汇编语言 中国人和中国人沟通需要学习汉语 中国人和美国人沟通在会汉语的基础上还要学习英语 那么&#xff0c;人和机器沟通的话需要学习哪些语言呢&#xff1f; 答案是&#xff1a;人类的语言机器语言 但是&#xff0c;机器语言都是由0和1组成&#xff0c;人类…

opencv图像直方图

灰度直方图&#xff1a;从数学上来说&#xff0c;图像直方图是描述图像的各个灰度级的统计特性&#xff0c;它是图像灰度值的函数&#xff0c;统计图像中各个灰度级出现的次数或频率。从图像上来说&#xff0c;灰度直方图是一个二维图像&#xff0c;横坐标为图像中各个像素点的…

git stash命令用法详解(临时存储代码)

1、需求背景 有时候在开发过程中&#xff0c;在一个分支上&#xff08;dev1&#xff09;已经写了一部分代码&#xff0c;但是需要紧急切换到别的分支(dev2)上修改某个代码&#xff0c;这时候不能直接从dev1分支上切换到dev2分支上&#xff0c;提示你需要保存代码。此时dev1分支…

Linux内存模型

sparse内存模型前言1.SPARSEMEM原理:2.vmemmap在虚拟地址空间位置3.virt&#xff0c;phys&#xff0c;page&#xff0c;pfn之间的转换关系3.1内核态虚拟地址和物理内存地址转换关系3.2页帧pfn、物理内存的page指针的关系3.3其他快捷的转换总结前言 Linux中的物理内存被按页框划…

408 考研《操作系统》第二章第五节:信号量机制和用信号量机制实现进程互斥、同步、前驱关系

文章目录教程1. 信号量机制1.1 概念1.2 信号量机制——整型信号量1.3 信号量机制——记录型信号量&#xff08;important&#xff09;1.4 总结2. 用信号量机制实现进程互斥、同步、前驱关系2.1 信号量机制实现进程互斥&#xff08;important&#xff09;2.2 信号量机制实现进程…