首页 > 其他分享 >mac80211子系统学习-rx部分

mac80211子系统学习-rx部分

时间:2024-05-09 16:34:32浏览次数:24  
标签:struct hw rx ieee80211 mac80211 skb local 子系统

mac80211子系统学习-rx部分

wifi网卡架构

Linux将wifi芯片分成了full mac和soft mac两种类型,两种类型的区别在于mlme被driver + hardware实现还是被kernel实现,用wireless wiki上的话来讲:

SoftMAC is a term used to describe a type of WNIC where the MLME is expected to be managed in software. mac80211 is a driver API for SoftMAC WNIC, for example.

FullMAC is a term used to describe a type of wireless card where the MLME is managed in hardware. You would not use mac80211 to write a FullMAC wireless driver.

在代码的实现层面,full mac需要自己实现net_device_ops回调函数集合用于数据的发送,需要实现cfg80211_ops回调函数集合用于对网卡进行管理(mlme);而soft mac架构的网卡,其驱动仅需实现数据面的ieee80211_ops回调函数集合即可。

两种不同架构的网卡并无世代前后之分,各家对自己的无线网卡都有自己的理解,瑞昱,联发科,高通等厂商既发布full mac驱动也发布soft mac驱动;

虽然Linux为两种架构的网卡划分了清晰的分界线,实际上大多数驱动会在实现ieee80211_ops的基础上,对cfg80211_ops进行改造,以最大程度的发挥硬件加速的优势;

以高通的无线mac驱动ath11k为例,其在驱动中实现了一部分mlme的工作(在ath11k中被称为UMAC)作为对mac80211的补充。

rx数据通路

对于简单的softmac架构的wifi网卡和驱动来说,接收数据的流程可以按照帧的类型分为两个部分:

  1. 对于控制帧和管理帧,在mac80211子系统中消化掉;

  2. 对于数据帧,转化为802.3协议的帧,然后提交给kernel处理;

对于复杂的网卡来说,以高通为例,其将部分mac层转发(Switch),甚至网络层路由功能(router)等对延时要求比较高的部分都放到了Network subsystem中使用NPU(实际上是一个ubicom32 DSP)来做,这种仙人成就过于高深,实在难懂;

一台计算机不可能从一开机就知道要接受空中的哪些数据帧,也不可能在等待接收数据帧的时间段内停机等待,因此Linux的wifi驱动的接收操作由中断触发;

注册中断

接收中断的注册写在与ieee80211_ops回调函数集合中start绑定的方法中;当网卡驱动被加载到kernel中,并使用ifconfig up使能该网卡工作后,start函数被触发,之后开始初始化与wifi设备相关的参数,例如tx、rx的DMA通道,例如此处提到的rx的接收中断;以openwifi的wifi驱动为例:

static int openwifi_start(struct ieee80211_hw *dev)
{
    ...
    priv->irq_rx = irq_of_parse_and_map(priv->pdev->dev.of_node, 1);
	ret = request_irq(priv->irq_rx, openwifi_rx_interrupt,
			IRQF_SHARED, "sdr,rx_pkt_intr", dev);
	if (ret) {
		wiphy_err(dev->wiphy, "openwifi_start:failed to register IRQ handler openwifi_rx_interrupt\n");
		goto err_free_rings;
	} else {
		printk("%s openwifi_start: irq_rx %d\n", sdr_compatible_str, priv->irq_rx);
	}
    ...
}

其向Linux的中断系统注册了一个硬终端,中断号为和设备树中的设备结点绑定的1号中断,中断处理函数为openwifi_rx_interrupt

按照对中断的朴素理解,中断处理函数应当被快速执行完成,防止阻塞到下个中断,但显然接收操作并不可能在短时间内被处理完毕;Linux中将这类不能及时完成操作的中断分成中断的上半部和下半部两个部分,下半部用于处理耗时的操作,上半部用于对中断的快速响应;两个部分靠任务队列机制连接,这有点类似于生产者--消费者模型(笑)

这也产生了不可预测的延时,操作系统对于下半部的任务调度是不可知的,解决方案可能只能依靠其他技术。。。

触发中断--上半部

当导线上的电平发生变动后,中断处理函数立刻开始响应执行中断的上半部分,以openwifi的驱动为例,该部分主要是组建sk_buff结构体的data部分和ieee80211_rx_status部分,这两部分的依赖与硬件dma设计和寄存器对应于内存中的地址:

static irqreturn_t openwifi_rx_interrupt(int irq, void *dev_id)
{
    ...
    skb = dev_alloc_skb(len);
			if (skb) {
				skb_put_data(skb,pdata_tmp+16,len);
                ...
                memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));

这之后驱动调用mac80211系统中断上半部的剩余部分,向任务队列中添加新的接收处理任务:

ieee80211_rx_irqsafe(dev, skb);

在mac80211系统的ieee80211_rx_irqsafe中的任务队列并非是计算机中熟知的指令与数据同一存放的冯诺依曼架构,而是数据和任务分离存放的哈佛架构,为追求迅速执行,代码中仅包含两个入队操作,甚至没有多余的判断分支:

void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
{
	struct ieee80211_local *local = hw_to_local(hw);

	BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));

	skb->pkt_type = IEEE80211_RX_MSG;
	skb_queue_tail(&local->skb_queue, skb);
	tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee80211_rx_irqsafe);

触发中断-下半部

上半部将数据和任务放入到队列中,但此时我们还并不知道放入队列的任务会被哪个处理方法调度。驱动的生命开始于用户手动 insmod ,此时会触发驱动回调函数集合中和probe绑定的函数,用来进行驱动的初始化工作。

这里就有几个问题:

  1. kernel是如何将该驱动和某个设备对应上的?

  2. 网络设备是如何与自定义的ieee80211_ops绑定的?

  3. kernel是如何将该设备识别为网络设备的?

前一个问题和设备树相关,在driver编写时需要将设备树中的结点名称和驱动进行绑定;

后几个问题在probe方法中进行解答,在openwifi_probe方法中创造了一个与自定义回调函数集合绑定的ieee80211_hw结构体:

static int openwifi_dev_probe(struct platform_device *pdev)
{
	struct ieee80211_hw *dev;
    ...
    dev = ieee80211_alloc_hw(sizeof(*priv), &openwifi_ops);

之后又将该hw注册到了内核当中:

err = ieee80211_register_hw(dev);

在创建hw时,经过一条调用链,可以定位到如下的对任务队列的初始化方法:

struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
					   const struct ieee80211_ops *ops,
					   const char *requested_name)
{
    tasklet_init(&local->tasklet,
                ieee80211_tasklet_handler,
                (unsigned long) local);

当local->tasklet被调度时,Kernel会自动的触发ieee80211_tasklet_handler函数,并利用传入的参数local进行对skb队列的操作:

static void ieee80211_tasklet_handler(unsigned long data)
{
	struct ieee80211_local *local = (struct ieee80211_local *) data;
	struct sk_buff *skb;

	while ((skb = skb_dequeue(&local->skb_queue)) ||
	       (skb = skb_dequeue(&local->skb_queue_unreliable))) {
		switch (skb->pkt_type) {
		case IEEE80211_RX_MSG:
			/* Clear skb->pkt_type in order to not confuse kernel
			 * netstack. */
			skb->pkt_type = 0;
			ieee80211_rx(&local->hw, skb);
			break;
            ...

接下来就是rx操作的主要部分,ieee80211_rx(&local->hw, skb),参考ath9k驱动的函数调用即可

https://github.com/WeitaoZhu/wi-fi_books/blob/master/driver-mac80211_intro.pdf

标签:struct,hw,rx,ieee80211,mac80211,skb,local,子系统
From: https://www.cnblogs.com/polariszg/p/18182083

相关文章

  • 初探pinctrl子系统和GPIO子系统
    前言:在前面的led驱动程序和按键驱动程序中,无论是最传统的方法,还是总线设备驱动模型,还是基于设备树的总线设备驱动模型,都是直接操作寄存器的方法。驱动开发的本质确实是操作寄存器,但是一个芯片有几百个引脚,只是操作少数的几个引脚还好,如果是大量的引脚,比如LCD接口的引脚几十个,一个......
  • 0507 高频考点ABRX
    1.求A,B,X大部分用假设分配2.R:1.一般增长率直接套用公式R=X/A2.隔年增长率R=R1+R2+R1R23.比值增长率,符合表达式A=B/C,材料中有B、C的增长率,求A的增长率,即为比值增长率(多以平均数增长率形式出现),公式为R1-R2/1+R24.乘积增长率,符合表达式A=B*C,材料中有B、C增长率,求A的增长率,......
  • 一种车载蓝牙显示终端(QCC3040 QCC5125 OLED RX 显示蓝牙歌曲名)
    作为车载多媒体一种延伸,车载蓝牙终端通常作为手机与车机中转站,可以作为点烟器扩展产品。 通过播放暂停音乐、接打挂断电话、媒体音量加减、上下一曲功能来进行人机交互。 产品通常还有3.5mm输入接口。  通过车载IDB-CIDB-MIDB-Wireless等多媒体协议解析出车载媒体控......
  • 19_输入子系统
    输入子系统什么是输入子系统?​输入子系统是Linux专门做的一套框架来处理输入事件的,像鼠标,键盘,触摸屏这些都输入设备。但是这些输入设备的类型又都不是一样的,所以为了统一这些输入设备驱动标准应运而生的。​统一了以后,在节点/dev/input下面则是我们输入设备的节点,如下......
  • Android显示子系统相关基础概念
    1.fence概念:acquireFence、releaseFence、retireFence每一个layer都有一个acquireFence和releaseFence,每一个系列layes都有一个retirefence,注意这边的是layers!多个layer。acquireFence用于生产者通知消费者生产已完成,releaseFence用于消费者通知生产者消费已完成。acquireFence:......
  • 15_pinctl和gpio子系统
    pinctl和gpio子系统1.什么是pinctrl和gpio子系统?​ pinctrl子系统是用来设置引脚的复用关系和电气属性的,gpio子系统是当pinctrl子系统把引脚的复用关系设置为gpio功能以后就可以使用gpio子系统来操作引脚了,比如引脚的输入输出,高低电平等2.LinuxPinctrl子系统提供的功能是......
  • 30 天精通 RxJS (31):如何 Debug?
    Debug一直是RxJS的难题,原因是当我们使用RxJS后,代码就会变得高度抽象化;实际上抽象并不是什么坏事,抽象会让代码显得简洁、干净,但同时也带来了除错上的困难。在编写程序时,我们都会希望代码是简洁且可读的。但当我们用简洁的代码来处理复杂的问题,就表示我们的代码会变得......
  • 30 天精通 RxJS (30):Cold & Hot Observable
    HotObservable跟ColdObservable的差别,其实就是资料源(DataSource)在Observable内部建立还是外部建立。在RxJS中很常会看到ColdObservable跟HotObservable这两个名词,其实他们是在区分不同行为的Observable,所谓的ColdObservable就是指每次订阅都是独立的......
  • 30 天精通 RxJS (29):30 天感言
    30天悄悄的就过了,这30篇的文章基本上已经把RxJS一个核心三个重点(Observable+Observer+Subject+Scheduler)以及各个operators几乎也都有写到。最开始写这个系列的文章是希望能让RxJS的学习曲线降低,所以文章的前后顺序及内容都是特别规划过的,不知道我到底是不是......
  • 30 天精通 RxJS (28):Scheduler 基本观念
    不晓得读者们还记不记得,我们在前面的文章中有提到Scheduler是为了解决RxJS衍生的最后一个问题,而我们现在就在揭晓这个谜底。其实RxJS用久了之后就会发现Observable有一个优势是可以同时处理同步和非同步行为,但这个优势也带来了一个问题,就是我们常常会搞不清处现在的......