二 WLAN驱动设计
这个章节主要介绍了大体的WLAN驱动设计思路。所有驱动支持的功能,将在后续章节有更深入的描述。
主要数据结构
整个WLAN驱动的处理和各模块间的访问,主要通过下面的数据结构来完成的。每一层都有自己的数据结构, 对这些层的访问,必须通过这些层提供的API,并封装其对应的数据结构来进行。全程是没有全局数据的, 这样也使WLAN驱动能够支持不同的AP平台配置,比如多radios、同一radios的多BSS等。
图 2-1 表述了WLAN驱动各层次的主要obj,以及他们之间的联系。后续章节会对这些内容的细节进行说明。
系统接口(OSIF)数据结构
ath_softc_net80211
ath_softc_net80211是一个面向radio设备的数据结构。在OSIF层内部被缩写为“scn”。
这是驱动为每一个AP平台内的WLAN radio接口,提供的一个系统接口的抽象。这个结构映射了网络协议栈提供 的网络设备私有数据结构。主要映射“wifi”接口。下面这些是一些存储在这个结构中的数据:
- UMAC通用的设备结构
- 面向LMAC设备的句柄(handle)
- LMAC接口函数
- OS设备句柄(handle)
- 同步锁
- node到key index映射的引用
osif_dev
osif_dev是一个OS接口设备结构体。他被定义成os_if_t类型,并在OSIF层内部被缩写为“osdev”。
这个是OS接口抽象,由驱动提供,用于每个由系统创建的WLAN网络接口。这个结构体通过OS栈,映射网络设备的私有数据结构。 这个结构体,帮助维护OS结构层的网络设备驱动信息。这个obj在网络栈上映射“ath”接口。一些在这个结构体中维护的数据有:
- 对网络设备的引用
- 对父类网络设备的引用
- 间接指向UMAC wlan接口的指针——用于UMAC API调用
- 间接指向UMAC通用接口(radio结构)——用于UMAC API调用
- VlanID和vlgrp
- UMAC连接状态机的句柄和扫描相关的句柄
UMAC数据结构
ieee80211com
ieee80211com是UMAC radio设备结构体。被定义成wlan_dev_t结构体类型,并在UMAC中被缩写为“ic”。
UMAC对所有radio相关的信息进行了抽象,并放在ieee80211com结构体中。这些数据对所有网络接口通用。 由底层共享出来的状态信息和UMAC层,暴露在这里。
存储在这个结构体中的信息有:
- 间接的osdev的handle
- 到scan table和scanner object的引用
- 所有的UMAC网络结构object的list
- 所有的用于连接管理的UMAC Node object(指向Node table的指针)的list
- 访问设备参数和数据的函数指针(API)
ieee80211vap
ieee80211vap 是一个UMAC网络接口结构体。每一个WLAN网络接口在UMAC中用“Virtual AP” (VAP) 来表示, 每一个结构都有一个底层物理设备对应。每一个VAP都可以有很多操作模式,比如host Access Point、IBSS或者Station模式。 每个VAP都有对应的OS设备,可以用于数据流以及供用户空间程序使用。这个部分包含的信息有:
- 间接的osdev的handle
- BSS node引用
- 所有的VAP相关的数据结构
ieee80211_node
UMAC Node结构体. 结构体类型为wlan_node_t通常在UMAC中缩写为“ni”。可以代表一个infrastructure网络中的BSS, 或者一个IBSS模式下的ad-hoc station,或者在HOSTAP模式下一个已连接的station。对ieee80211_node的设置 可以表明设备在网络中的本地视角。比如一个在station模式的设备,唯一的ieee80211_node对象, 是和这个设备连接上的对端AP。而一个在AP模式的设备,对ieee80211_node对象的设置,代表了对当前BSS的station的设置。 对于一个在ad-hoc模式的设备,对ieee80211_node对象的设置代表了可见的邻近设备(不论在不在IBSS内)。
LMAC数据结构
ath_dev: LMAC device结构体
定位为ath_softc,结构体类型为ath_dev_t(不被其他层直接访问)。在LMAC内部代码被缩写为“sc”。
LMAC将底层的驱动组建,封装成一个Qualcomm Atheros设备对象。ath_dev对上层不直接可见, 需要通过一些定义好的函数表(ath_ops)来访问,即LMAC API。这样的好处是,不论底层硬件是什么, 协议栈都可以通过这些API以相同的方式,对设备对象进行访问。比如,协议栈通过通过相同的方式来 访问PCI设备或USB设备,不必担心硬件上的不同。
ath_vap: LMAC vap结构体
在LMAC中被称为“vap”。每个ath_vap对象代表一个VAP(virtual AP)实体。这个对象直接对应UMAC中的ieee80211vap对象。 默认情况下,每个物理设备都只有一个VAP实体,根据需求可以通过协议栈增加或删除VAP。每个ath_vap可以通过一个 唯一的index被其他层访问。Qualcomm Atheros硬件最多可以支持16个VAP,所以有效的index范围是0到15。
ath_node: LMAC node结构体 (对应每个UMAC node)
定义成ath_node_t类型,在代码中缩写为“an”。每个ath_node对象代表了和他对应的ieee80211_node对象, 相关结点信息存储在LMAC中。和ath_dev相同,每个ath_node不直接暴露给其他层,也是需要通过ath_ops表来访问。
HAL Data Structures
ath_hal: HAL device结构体
在代码中被缩写为“ah”。设备中的HAL client调用ath_hal_attach来得到ath_hal数据结构。和硬件相关的操作, 必须作为call back函数,通过第一个参数传递给HAL。这个结构体抽象了一些底层硬件芯片相关的数据结构, 用于上层使用,并提供了一些通用的接口。实际每个设备相关的HAL结构体,根据不同的硬件芯片类型, 是分开定义的(比如ath_hal_9300,ath_hal_5416,和ath_hal_5212)。
上下文及同步处理
WLAN驱动在不同的上下文处理中执行,比如
- ISR上下文
- Softirq(软中断)/tasklet(内核软中断延迟机制)上下文
- Process(进程)上下文
ISR处理
WLAN设备成功附着之后(ath_attach函数调用),驱动会为这个设备请求一个IRQ资源,并注册ath_isr中断服务例程(ISR)。 驱动同时会初始化一个叫做ath_tasklet的tasklet资源。ath_isr首先会通过扫描WLAN硬件的待处理的中断, 来检查这个中断是否是来自这个设备。一旦这个中断被确认是来自WLAN设备的中断,他将根据中断状态寄存器, 来运用tasklet,进行中断对应的处理。有一些时间敏感的处理,比如软件beacon alert处理,UAPSD触发处理等, 会在ISR上下文中直接处理。其中一些时间敏感的处理,可以在编译时,指定其在tasklet上下文中运行,运行所需的时间可以设置。 ath_isr处理的最后阶段,如果tasklet开始执行,所有的WLAN设备中断会被禁用,除了那些时间敏感的中断。
Softirq/Tasklet处理
ath_tasklet的处理,是在tasklet上下文中执行的。ath_tasklet检查存储的中断状态值(通过ath_isr),并执行相应的动作。一些ath_tasklet的功能包括:
- 异常处理
- 传输结束处理
- 收到帧时的处理
在tasklet的最后,WLAN设备中断,会根据当前中断mask的设定值,被再次使能。网络设备上的网络帧传输, 在NETIF softirq上下文中进行。所有的OS timers处理,都在TIMER softirq上下文中进行。
进程上下文(Process Context)
所有对驱动进行的访问,都通过运行在进程上下文中的ioctl系统接口来进行。
同步处理(Synchronization)
上面提到的上下文(context)之间的同步处理是必须的,因为有一些data是各个context之间share的。
大多数WLAN驱动使用的同步方法是spin_lock。Linux提供了不同类型的spin_lock,比如spin_lock、spin_lock_bh、 spin_lock_irq和spin_lock_irqsave。表2-1大体描述了各个context之间使用了何种spin_lock来完成同步机制。
Table 2-1 同步方法
-
ISR Softirq/Tasklets Process context ISR - spin_lock_irqsave spin_lock_irqsave Softirq/Tasklets spin_lock_irqsave spin_lock spin_lock_bh Process context spin_lock_irqsave spin_lock_bh -
驱动中使用到的其他一些同步技术有:
- 原子操作(Atomic):任何主要的操作都是uninterruptable的。
- 读写锁(Read/write lock):读写锁和spin_lock类似,但主要用于从driver中分离处理的read/write操作。Linux提供了许多和spin_lock相似的读写锁来完成context之间的同步,他们的主要思想和用法都是类似的,主要是用于不同类型的read/write操作。
Buffer管理(Buffer Management)
这部分主要对驱动内部的buffer进行一个抽象的描述,之所以抽象出来,是因为“frame”可以作为一个通用的概念, 其与网络协议栈buffer的匹配原理,可以独立于不同的OS环境以方便描述。本部分还对TX/RX frame buffer在驱动 内部是如何管理的,进行了描述。
WBUF抽象(WBUF abstraction)
Wbuf
每个wbuf(wireless buffer)对象都代表一个独立的network buffer。在WLAN中,它还可以代表一个从协议栈传递下来的MSDU。 底层的驱动模块,会将wbuf作为一个wbuf_t类型的对象来处理,并通过定义好的API来对其进行访问。 wbuf相关的API定义在include/wbuf.h文件中。每个平台都应该在其OS抽象层(OS abstraction layer)提供这类API。 通常wbuf会跟native network buffer结构体匹配。在Linux平台中,wbuf跟skb结构体匹配。
WBUF的类型(WBUF Type)
每个WBUF对象都有其对应的类型。如表2-2所示:
表 2-2 WBUF类型
类型 | 释义 |
---|---|
WBUF_TX_DATA | 由网络协议栈下发的normal Tx data frame |
WBUF_TX_MGMT | 内部生成的management frame |
WBUF_TX_BEACON | 内部生成的beacon frame |
WBUF_RX Rx | DMA中用到的Rx buffer |
WBUF_TX_CTL | 内部生成的control frame |
一些开发规则(Limitations)
理论上讲,抽象化的wbuf应该有等同于native network buffer一样的功能。即wbuf抽象化后,其目的是想支持不同的平台, 不过目前,只能支持一些具有相同子集的API的操作系统,比如Linux、NetBSD、Windows Vista。需要注意的是:
- 开发人员不应该认为wbuf在物理上是连续的。一个wbuf由多个物理内存碎片组成。
- 开发人员不应该将一个packet等同于一个Ethernet frame或者IEEE 802.11 frame。只有经ieee80211_encap()函数调用后,才能得到一个视为IEEE 802.11 MPDU的wbuf。
- 开发人员不应该将LLC/SNAP header所占用的物理内存碎片,视为与WLAN header一样。同理,TCP/IP header也不能视之一样。
- 开发人员不应认为wbuf与一块足够大的context区域相匹配。
- wbuf可以通过一些公共的API来访问(函数定义在include/wbuf.h文件中),但是并不是所有的API都可以应用到每一个wbuf型。
描述符抽象化(Descriptors Abstractions)
WLAN硬件使用descriptor作为载体,在driver和MAC hardware之间来传输frame(抽象为wbuf)。low-level 的driver 主要负责为MAC hardware提供一系列的descriptor。然后MAC会解析descriptor,并完成成一系列data的传输。 对于MAC处理descriptor的一些细节,请参考WLAN hardware芯片的data sheet文档。
在WLAN driver中, descriptor被组织成许多对physical和virtual的descriptor。driver使用physical descriptor与MAC进行交互。 他们通常在整个driver的生命周期中,有固定的DMA区域,他们不能缓存一些能够阻止CPU检测MAC hardware或vice versa产生的 更新的数据。一个physical descriptor由ath_desc结构体表示,定义在hal/ah_desc.h文件里。
physical descriptor不应该作为一个罕见的资源来看待。任何仅由driver访问的control和status信息,都应该放到 virtual descriptor中,virtual descriptor会从normal memory pool中分配。virtual descriptor和 physical descriptor是一对一的关系。ath_buf结构体代表了一个virtual descriptor实例, 定义在ath_dev/ath_desc.h文件中。ath_buf结构体的指针bf_desc,会指向其对应的physical descriptor。
virtual descriptor由一个BSD类型的尾队列(TAILQ)——ath_dev实例来管理。physical descriptor只能由其对应的 virtual descriptor来访问。descriptor list如表2-2所示:
表 2-2 Descriptor List
保存在ath_buf结构体中的信息,有以下内容:
- 对driver和hardware(bf_mpdu)之间,传递数据的载体——wbuf的引用。
- data所处的Physical和Virtual地址之间的转换。
- LMAC TX/RX处理所需的TX descriptor flags和RX status flags。
接收buffer的管理(Receive Buffer Management)
作为RX初始化流沉的一部分,ATH_RXBUF宏,定义了RX descriptor的数目,根据这个数目,ath_buf会被分配出来。 对于每一个ath_buf,能够预先分配一些WBUF_RX类型的wbuf。所有这些ath_buf都会link成TAILQ,TAILQ_HEAD在sc_rxbuf存储。
从硬件收到frame,LMAC就会从sc_rxbuf TAILQ中移除对应的ath_buf,并完成frame Rx处理。然后从ath_buf中移除WBUF的索引, 并将这个索引传递到UMAC和网络协议栈继续处理。然后,一个新的WBUF会被分配出来,ath_buf会link到这个新的wbuf, 然后被追加到sc_rxbuf TAILQ的尾部。
发送buffer的管理(Transmit Buffer Management)
作为TX初始化流程的一部分,ATH_TXBUF宏,定义了TX descriptor的数目,根据这个数目,ath_buf会被分配出来。 这些ath_buf会link成TAILQ ,TAILQ_HEAD在sc_txbuf存储。对于任何从UMAC传递到LMAC的frame(已经放在wbuf中), LMAC层都会从sc_txbuf list中将其对应的ath_buf移除。一旦这一Tx frame处理结束,LMAC会释放其对应的wbuf, 并将ath_buf重新排列到sc_txbuf list中。
队列管理(Queue Management)
硬件队列(Hardware Queues)
WLAN硬件,根据frame的不同类型,提供了一些传输queue,来优化frame的传输。HAL模块提供了一些API, 供LAMC层去修改这些queue的属性,比如AIFS、CWMin、CWMax、TXOPLimit等。本设计最多支持HAL_NUM_TX_QUEUES个队列 (HAL设置此宏为10)。其队列的优先级是降序的,也就是说,queue9是最高优先级的,queue0是最低优先级的。
LMAC队列的支持(LMAC Queuing Support)
ath_txq
ath_txq是LMAC层的数据结构,它跟hardware queue是一一对应的关系。ath_txq作为结构体ath_softc的成员变量, 定义成这样:sc_txq[HAL_NUM_TX_QUEUES],所以共有HAL_NUM_TX_QUEUES (10)个ath_txq(在LMAC中,缩写成“txq”)。
在这10个txq中,前4个,即queue0-3被称为DATA queue,他们分别与4个WMM access类型相对应:background、best effort、video、voice。
txq-9叫做beacon queue (bcnq) ,beacon frame会在这个queue中管理。 txq-8叫做content-after-beacon (CAB) queue (cabq)。这个queue主要用于,在beacon传输后,分发从每个VAP得到的组播frame。 剩下的txq 4-7,用于其他类型。其中一些会用于满足一些featrue的需求,详细说明请参看对应的feature章节。
软件队列(Software Queues)
LMAC还提供software queue,分离于hardware queue。driver提供的transmit descriptor是有限资源, 因此software queue主要用于流控(flow control)。
LMAC提供两种software queue:
TID队列(Traffic Identifier (TID) Queues)
IEEE 802.11标准为每个流量(traffic classification)节点提供最多16个TID。因此LMAC也提供16个TID queue, 与LMAC自己创建的ath_node一一对应,用于数据流。这些TID queue用数据结构ath_atx_tid来表示。
UMAC的data frame,根据其packet类型,会选择一个TID queue使用。而对于management frame, 结构体ath_node中增加了一个额外的TID queue (TID-17),用于处理除beacon以外的所有management frame。 17个TID queue中所有的frame都通过hardware data queues 0-3来传输。那这17个TID queue(与 ath_node对应) 是如何分配到这4个hardware data queue的,主要是通过一个叫做access category queue的媒介来进行的。 缩写为“acq”,由结构体ath_atx_ac来代表。
- Tid queue 0和1,与acq0 list对应(Background access category queue,txq0)。
- Tid queue 2和3,与acq1 list对应(Best effort access category queue,txq1)。
- Tid queue 4和5,与acq2 list对应(Video access category queue,txq2)。
- Tid queue 6和17,与acq3 list对应(Voice access category queue,txq3)。
组播帧队列(Multicast Frame Queues)
对于每个VAP实例(avp),LMAC都提供一个software queue用于multicast frame。这些queue在结构体ath_vap 中定义为“av_mcastq”。从VAP得到的multicast frame会放到av_mcastq队列中,并通过CAB queue(txq8)来传输出去。
图 2-3 说明了TID queue、mcast queue与hardware queue的对应关系。
代码流程(Code Flow)
这部分讲解一些主要的配置通路、收发数据的通路。下面这些颜色,用于区分不同的模块。
配置通路(Configuration Path)
WLAN驱动的配置路径主要包含:radio接口的初始化和去初始化(比如wifi network接口)、 VAP的创建和删除(比如说ath network接口)。
驱动线程(Driver Threads)
同一时间可能有多个线程被启动。这些线程有:
- 传输线程(Transmit (OS context))
- 中断线程(Interrupt)
- 延迟中断处理线程(Deferred Interrupt Handler)
- Timer处理线程(Timers)
- Beacon处理线程(Beacon handling)
- ADDBA帧处理线(Addba exchange)
- 接收包乱序处理(Receive Reordering)
- MLME timer处理(鉴权/关联)(MLME timers (authentication/association))
- 同步处理(Synchronization (Lock Macros: OS-specific abstraction))
Tx和Rx的初始化(Tx and Rx Initialization)
Tx和Rx初始化包含如下内容:
- 为transmit & receive分配Descriptor
- 针对不同OS,初始化过程会有一些不同:
- NDIS请求驱动来分配receive buffer
- Unix OS变量维护一个common的buffer pool
- 对于AR93xx设备,status会进入到环状的packet buffer
设备attach过程(Device attach)
图2-4解释了radio interface通过PCI bus interface的初始化过程(通过其他bus interface的 初始化过程不在本次讨论之列)。WLAN driver load后,radio interface通过ath_pci_probe进行初始化。 每个radio interface都会进行一次device attach。
图 2-4 Radio Interface初始化(PCI Mode)
设备detach过程(Device detach)
driver unload的时候,会remove掉radio interface,使用ath_pci_remove函数来进行。 这个函数是注册在pci_driver结构体中的,作为remove动作时的例行函数。
图 2-5 Radio Interface卸载(PCI Mode)
VAP的创建(VAP create)
VAP network interface通过“wifi” radio network interface ioctl SIOC80211IFCREATE进行创建。
这个“wifi” interface是这个VAP interface的父设备。图2-5说明了VAP创建的代码流程。每个VAP都有与之对应的“ath” network interface。
VAP的删除(VAP delete)
VAP的删除,通过application调用VAP network接口ioctl SIOC80211IFDESTROY来完成。
VAP的启动(VAP start)
VAP的启动,是通过VAP创建时注册的network device open callback函数来完成。
VAP的停止(VAP stop)
VAP的停止,是通过VAP创建时注册的network device stop callback函数来完成。
数据通路(Data Path)
传输流(Transmit flow)
传输流主要存在于两种不同的执行路径:WLAN驱动从network stack中获取data packet,以及驱动处理过后, 获取存储在software TID队列,或发送到硬件的data packet(假定这里在硬件队列中没有pending的传输packet)。 packet传输完成后,硬件产生传输完成interrupt,驱动启动tx_tasklet任务来完成transmit流程。作为tx_tasklet的一部分, 驱动释放已完成传输的packet,并运行TID scheduler,将软件TID queue中的packet,放到hardware queue中。
数据传输通路主要在OS shim层进行,并有如下特征:
- 调用wlan_vap_send (umac/txrx/ieee80211_output.c) 来启动传输流程
- Win7/Vista处理802.11 packet,其他的处理802.3
- Node查找与鉴权
- UMAC调用ath_tx_send (umac/if_lmac/if_ath.c)传递packet,构建packet
- 封装crypto headers, tid, crypto
- Packet传递到ath_tx_start_dma (lmac/ath_dev/ath_xmit.c)
- 完成Physical mapping to descriptor
- Descriptors分配与创建
- 若硬件queue中的阀值packet比较少,则操作单一packet入队列,或者将packe放到“tid”队列中处理
- 处理中断时创建Aggregate
- 中断对应queue
- tx中断发生时,驱动扫描有packet发送的node
- 通过“round robin”算法从中选择一个node
- 创建Aggregate并放到硬件队列中
传输中断的处理(Transmit Interrupt Handling)
传输中断与queue一一对应。中断发生时,ath_tx_edma_tasklet()将被调用。Rate信息可通过ath_rate_tx_complete_11n函数获取。
- 对于single packet,如果软件允许retry,则会有retry动作。
- 对于aggregate,ath_tx_complete_aggr_rifs() 函数用于处理block ack并return没有完成的传输queue的head(node,tid)
- 如果retry次数达到最大,则将packet送至OS shim层完成处理
- 通过调用ath_txq_schedule() (ath_xmit_ht.c)函数,来调度下一次传输处理
- 根据发送的packet筛选node(round robin算法,即轮询)
- 如果addba完成,则创建一个aggregate(之前failed的packet将做为新aggregate的一部分)
- 查找速率控制和选择速率序列
- 用于hardware传输的Queue packet/aggregate
传输通路管理(Transmit Path Management)
ieee80211_send_mgmt (wlan/umac/mlme/ieee80211_mgmt.c) 函数主要作用有:
- probe request
- probe response
- beacon
- action frames
- null data packets
这里的packet跟data packet很像。速率强制设定为最小速率,voice queue用于所有的management packet。 传输路径管理接口,通过ath_tx_mgt_send()函数提供给ath层。management task结束之后的代码流程与data packet的相同。
中断流程(Interrupt Path)
驱动初始化的过程中会注册中断. 流程如下:
- 入口 sc -> sc_ops -> isr() 映射到 ath_intr
- ath_intr (lmac/ath_dev/ath_main.c)调用HAL层(ah_isInterruptPending)来获取pending状态的中断
- ah_isInterruptPending根据平台映射到不同函数——AR93xx平台HAL使用的是ar9300IsInterruptPending()
- HAL层将不同平台的中断寄存器的每个bit的意义抽象出来,做一个common的映射: HAL_INT_SWBA HAL_INT_RX HAL_INT_TX
- ath_intr() 返回一个bool值来表明这个中断是否为当前使用者所创建的
- ath_handle_intr()通过OS来调度。
- Windows平台中的DPC接口
- MacOS平台的handle_interrupt接口
- netBSD平台的紧急调用
- 调用指定的tasklet,来响应每个中断
- 发送中断的处理,在文件ath_edma_xmit.c的ath_tx_edma_tasklet()函数中结束。
- 接收中断的处理,在文件ath_edma_recv.c的ath_rx_edma_tasklet()函数中结束。
- 其他的中断也有类似处理。
- Beacon
- GPIO
- 错误处理,如overrun/underrun/fatal-chip-error
AR93xx的传输(AR93xx Transmit)
对于AR93xx的设备,有一个通用的循环链结构会上报传输的结束状态。每个硬件descriptor指针都会指向4个物理内存块。descriptor可以有多个链在一起。
- TX descriptor指针(TXDP) FIFO
- 长度为8个元素
- 每个队列有一个FIFO
- 2 个(最多)
数据传输流程(Data Transmit flow)
数据传输完毕的处理流程(Data Transmit Completion Flow)
图 2-10 说明了TX完成中断的处理,以及tx是如何调度的,这些都是传输完毕的处理内容。
接收流程(Receive flow)
硬件的接收帧通过HAL_INT_RX发送带指定驱动。有一些接收处理是延迟敏感的(如UAPSD触发),在这种情况下, 处理在ISR上下文中处理。这些rx处理应该尽可能的保持到最小。其他主要的接收处理流程, 在tasklet上下文中处理(ath_rx_tasklet)。rx帧的处理细节参看图2-11。
单包接收(Single Data Packet Receive)
在单包接收task中,由ieee80211_input_data()进行处理。并根据STA/HOSTAP/IBSS这些不同的mode来进行解析和排序, 并使用不同的广播。其他的event包括:
- Crypto DeCap和DeMic; 给软件提供处理crypto和demic的机会。
- 按需进行AMSDU的处理(ieee80211_amsdu_input)
- 按需调用ieee80211_deliver_data 转换成802.3
- 在HostAP mode下,如果目的节点已经associated,则包可以转发到wifi mediumu或转发到上面的桥接接口。
- 在STA mode下,包由栈处理。
聚合接收(Aggregate Receive)
在Aggregate接收中,包由AMPDU处理流程中的ath层进行处理。
ath_ampdu_input() (in lmac/ath_dev/ath_recv_ht.c)会获取packet。
- 如果处理的内容是qos data,则tid会从中解析出来。
- tid相关的rxbaw结构会从node中被抽取出来。
- Seq#映射到baw中,所有流程中的帧,此时会交由上层 (umac)进行处理。
- 传输通道会维护一个timer,如果timer到了,则所有的包都处理结束,window向下一个迁移。
处理完毕的packet,会与上面提到的单包处理的流程一样,继续进行处理。
AR93xx的接收处理(AR93xx Receive Handling)
AR93xx芯片支持高低优先级的接收ring。高优先级的用于UAPSD。低优先级的是通常的TID traffic。 ath_rx_edma_init() (在wlan/lmac/ath_dev/ath_edma_recv.c中) 会初始化一个固定大小的receive buffer、 FIFO软件数据结构,并在ath_osdep.c调用OS-specific的函数来分配buffer。
接收准备就绪时,通过调用th_edma_startrecv() 来传递已分配的buffer、使能硬件上的的接收FIFO,设置MAC地址,并启动DMA引擎。
接收中断(Receive Interrupt)
当接收中断触发时,DPC函数会调用ath_rx_edma_tasklet(),来处理接收的高低优先级队列。
这个函数有如下功能:
- 取出第一个“完成”位被置位的buffer
- 如果完成了,则检查状态,取出所有完整的packet,并处理
- 将损坏的packet放回到硬件队列中以便重用
在闭环结构中处理完毕的packet,会传递到ath_rx_process (在the ath_recv.c文件中)。
针对不同硬件,这个函数是一个通用的函数。状态位处理后,带有状态的packet会传递到ath_osdep文件中的ath_rx_indicate()函数。
MacOS萍提的ath_rx_indicate()(ath_rx_indicate() for MacOS)
在这个函数中,packet会通过调用入口函数sc_ieee_ops -> rx_indicate()来传递给上层(UMAC)。 一个新的buffer分配后,会发送到硬件的ready队列。
其他的接收event(Other Receive Events)
sc_ieee_ops -> rx_indicate()映射到if_ath.c文件(在umac/if_lmac路径下)的ath_net80211_rx()函数。
包含如下功能:
- 能够在监控mode的接口上打印packet内容
- 定位与这个packet associated的节点
- 针对ampdu节点,在ath层调用 sc_ops -> rx_proc_frame
- 对于单packet,会在ieee80211_input() (在umac/txrx/ieee80211_input.c文件中)函数中做进一步处理
- 数据packet会在ieee80211_input_data()函数中处理
- 管理packet会在ieee80211_recv_mgt()函数中处理
数据通路——局部卸载(Data Path — Partial Offload)
在新的一代802.11ac无线芯片中,比如QCA988x/989x和QCA999x/998x,多数的数据处理由主CPU转移到芯片本身来处理(目标处理器), 这样可以降低主CPU负载,提高数据处理效率。AP SOC平台上的处理器,如AP135等,会运行一个小的host数据通路代码。 在AP平台上,“低延迟”的代码路径被启用。host和target之间的通信,通过DMA来进行,这被称作Copy Engine。 “Copy Engine”有8个管道。每个管道都被配制成双向通信。
所有的taget到host的通知,都是由一系列中断产生,中断的handler有如下功能:
- 检查中断是否有错误
- 关闭中断
- 调度延迟进程进行进一步处理。(比如Linux的tasklet)
在Partial Offload架构中,host和target的功能划分可以大体按照下面进行:
Host Tx
- 栈中的单个包或多个包所使用的buffer直接由DMA映射
- 建立基于target的扩展数据
- 建立copy engine来传输扩展数据或部分数据包
- Tx闭环处理,包含两个步骤:
- Tx descriptor下载完毕
- Tx packet处理t完毕
只有上面两个部分都结束,packet才会被释放。
Target Tx
- 通过DMA发送Rx buffer到MAC硬件。
- 丢弃、重排序或转发packet到栈中(取决于HTT消息)。
Target Rx
- Block Ack窗口管理和发送Block Ack.
- 发送HTT Rx indication消息到host
header的封装/解封,以及加密/解密(如果使能了安全机制),在MAC硬件中进行。 图 2-13 列出了数据通路的相关模块:
Tx处理(Tx Processing)
对于Partial Offload的通路,Tx处理流程可以分为两个部分:
- packet的传输
- packet的结束
- 标记完成传输的packet
WLAN驱动可以从栈中接收下面几种类型的packet: (取决于配置):
- 普通Ethernet packet (包含或不包含一个VLAN header)
- 一个本地的Wi-Fi (802.11)packet(包含或不包含一个VLAN header)
- 一个裸802.11packet
下面是Tx处理的主要feature:
packet的传输
- packet通过OS适配层提供的主要节点进入驱动。DMA到buffer list的映射在此时完成。同时packet会下放到通用“Offload”(OL)层做进一步处理。
- OL层的入口是 ol_tx_ll()
- ol_tx_ll()
- 分配OL层Tx descriptor
- 存储packet中控制块里的meta-data碎片 (linux中使用结构体struct cb {})
- 准备HTT descriptor,并嵌入到OL Tx descriptor
- 填充HTT descriptor中的分散-收集list
- 调用 ol_tx_send()
- ol_tx_send() 将packet传递到HTT层。
- htt_tx_send_std() 添加HTT/HTC fragment到qdf_nbuf meta-data,设置每个fragment的endianness,分配并准备一个HTC (仅软件内部) packet,并通知HTC层。
- HTCSendDataPkt() 检查可用的HIF资源,如果没有足够的资源则将packet丢弃。这个函数会填充HTCdescriptor,并传递给HIF层。
- HIFSend_head() 添加fragment到qdf_nbuf _cb的meta-data (fragment由stack +HTT/HTC fragment组成),并放到Copy Engine (CE) send-list中和激活CE。
- CE_sendlist_send() 通过将copy engine环索引,写入target上对应的CE register来传递buffer。CE slot的数量与包中的fragment数量(= DMA已映射的地址) 对等。如果linux从栈中一次发送一个packet,则每个packet所使用的CE slot数为2。
- CE 4 用于从host到target的 HTT + HTCdescriptor,以及数据packet的一部分。
NOTE 对于一个已知的packet,驱动会下载24byte的HTT/HTC header + 一部分固定的数据packet到target端。 这个下载动作很有必要,即使这部分数据packet已经由MAC层h/w的host内存,放在了DMA中,也需要进行, 这样target端才能解析packet header,并按照packet类型分类。下载的长度包括 L2 headers + L3 headers的2 bytes(Ipv4 TOS / IPV6 Flow label) 长度的信息。下载从行度在驱动初始化的时候设置,使用下面的值: Ethernet packet -> 28 bytes Native 802.11 packet -> 44 bytes Raw 802.11 packet -> 50 bytes
传输packet的完成
Tx完毕通知包含两部分:
- target端完成下载descriptor。
- 这个完毕通知只是一个从target到host的中断,用来通知descriptor传输结束。
- 这个可以用于释放Copy Engine索引,但不是实际的packet的meta-data。
- 为了CPU更有效率,用于CE 4的这个中断已经被禁用了。替代的方案是,这个CE硬件索引会被轮询,用于查找已完成的descriptor数。
- 真实packet的传输结束。
- 会产生一个从target到host的HTT消息。
- 会在CE 1上的每个Tx PPDU都产生中断作为通知,主要用于从target到host的HTT消息。
- HTT消息携带所有完成的packet的ID。根据这些ID,所有实际的packet和所有的meta-data,比如HTC packet,OL Tx descriptor等等,都会被释放。
- 释放每个copy engine所处理的HTT消息buffer。这些buffer会在函数return之前,重新分配,并通过HTT消息handler传递回去。
Rx处理(Rx Processing)
接收处理主要包含两个部分:
- 处理由target发向host的HTT消息中的Rx通知。
- 分配并传递Rx buffer到MAC硬件,来处理实际的数据。
处理Rx标识
- 从target到host的Rx标识,是在每一个PPDU中断中的。
- 从target到host的Rx标识通过HTT消息来传递,因此他使用target到host的专用copy engine 1。
- CEhandler从TID队列中,获取HTT消息buffer,并经由HIF、HTC、最终到达HTT。
- HTT消息handler(htt_t2h_message_handler())分解消息,并调用OL handler函数ol_rx_inidication_handler()。
- ol_rx_indication_handler()查找Rxdescriptor(实际的Rx buffer),并从MPDU抽取MSDU,并将每个MSDU入栈。
Rx buffer的分配和发送
Rx buffer在ol_rx_indication_handler()函数返回之前,会传递给硬件
数据通路和数据结构
下面的章节,列举了一些offload层数据路径的重要结构。
ol_txrx_pdev_t: 与TxRx物理设备对应
这个结构主要是,与WLAN驱动协作的物理设备,在offload mode下的收发特性。 一个OL物理设备代表一个射频接口(比如wifi0,wifi1等等)。这个结构体维护下面这些重要的信息:
- 非透明OS设备的handler
- 与更底层的物理设备的接口,比如HTT
- 针对特殊物理设备(Ethernet, native Wi-Fi, raw等等)的帧格式
- 与当前物理设备(PDEV)对应的虚拟设备(VDEV)列表
- Peer Id到Peer Object的映射数组
- 接收处理产生的信息;比如说Rx re-order数组
- 调用OS接口层注册的Rx处理回调函数
- TxRx host统计计数TxRx host statistics
- Tx descriptor池
ol_txrx_vdev_t: 与TxRx虚拟设备对应
这个结构主要指,对于一个已知的VAP,offload mode下的虚拟设备与之对应的OL物理设备的收发特性。 一个VDEV实体对应一个网络接口(如 ath0, ath1等等)。多个VDEV实体可以映射到OS层中,可见的、单个OL物理设备(PDEV)
- TxRx物理设备的handler(ol_txrx_vdev_t)
- VAP的MAC地址
- 当前vdev的虚拟设备ID
- 与当前VAP(vdev)连接的对端设备列表
- Tx和Rx处理函数的函数指针
- 速率控制信息(在host端完成的)
- VAP的可选mode
ol_tx_desc_t: 与Tx descriptor对应
这个结构体代表OL Tx descriptor。一个Tx descriptor池空间会在驱动初始化的时候申请。池的size等于1K, 这与射频接口的传输descriptor数量是一致的。对于每个有驱动传输的packet,都有一个对应的OL Tx descriptor会 被分配出来与之对应。每个OL Tx descriptor都有自己的唯一ID。这个ID会在meta-data中携带给target。Target会将 这个Tx descriptor的ID作为Tx传输结束message的一部分,依次返回给host,并由host释放Tx包和OL Tx包。
ol_txrx_peer_t: 与peer node对应
这个结构体存储了已连接的对端节点的重要状态信息。这个数据结构主要用在Rx流程中。其包含的一些重要的信息如下
- 对端已连接的VEDV的handler。
- 每个已连接的对端ID列表。这些对端ID主要用于获取对应的节点结构体(利用对端ID到节点的映射机制)。
- 已连接对端,所对应的本地VAP的MAC地址。
- 每个TID的Rx重排序数组。这个数组会缓存Rx packet,直到当这个驱动从Target接收到一段连续的packet,或者从Target收到FLUSH消息。当收到FLUSH消息时,整个数组会被发送到网络栈中,不管TID packet数组是否已存有连续的packet。
- 安全信息。
cvg_nbuf_cb: 聚会网络buffer控制块
这是一个非常重要的meta-data结构,存储于inline或作为一个在OS专用data buffer结构中 (比如linux的sk_buff,NetBSD的mbuf,Windows的NBLs)的指针。
这个meta-data结构,作为每个packet的一部分,维护了下面这些内容:
- 分散或集聚由OS下发到驱动的,物理地址&长度的碎片list。
- 由驱动添加的物理地址碎片和碎片的长度(在Tx通路中,target所需求的HTT/HTC meta-data)。
OL数据结构(OL Data Structures)
对于offload架构解决方案,OL(Off-Load)层是处于Unified MAC和其底层中间, 比如target固件的WMI (无线管理接口) / WDI (无线数据接口)。参看图 2-18
从图2-18中可以很明显的看出。大部分OSIF层和UMAC层数据结构没有什么变化,并可以在offload模型中被重用。 LMAC和其底层并没有应用在offload模式下,而是使用OL和其底层,比如WMI和WDI来代替。OL层有一些新的数据结构, 将会在下面的小节中说明。
ol_ath_softc_net80211: OL radio device结构
这个结构属于OL层的“scn”模块,主要抽象出了radio接口。在offload架构中,目标固件code实例和radio接口之间有 一个1:1的映射。这个数据结构还会截取在指定radio接口上运行的code信息。它嵌套了UMAC层ieee80211结构体。
本结构体中的一些重要内容如下:
- UMAC通用设备结构体 (sc_ic)
- OS
- 设备handler
- OS层注册的回调函数,会在目标迁移到service ready状态时调用.
- 目标的状态信息;如version/type/status
- 固件下载相关信息 (BMI information)
- 其他层的handler如WMI, HTT, HTC, HIF
- Channel和Tx功率信息
- 存储TxRx统计计数的结构体
- 同步锁
ol_ath_vap_net80211: OL network interface结构
ol_ath_vap_ieee80211是WLAN network接口在offload层的抽象。每个WLAN network接口在UMAC层通过“Virtual AP” (VAP)来表示,并与底层物理device/radio接口对应,比如“wifi0”, “wifi1”等等。一个VAP主要呈现出access point的 操作模式,access point可以是一个host access point,一个IBSS或一个station。
存储在这个结构图中的重要信息如下:
- UMAC层VAP object (av_vap)
- OL层底层的物理设备 (OL层scn模块)
- 每个VAP数据通路结构体的handler(av_txrx_handle)
- 用于beacon的相关信息(如offload mode, beacon buffer, probe templates etc.)
- Timer,主要用于cleanup。
- 同步锁。
ol_ath_node_net80211: OL network interface结构
ol_ath_node_ieee80211是offload层在UMAC节点的抽象。一个UMAC节点代表了一个在HOST AP模式下已连接的station, 或一个IBSS模式下的ad-hoc station,或一个infra-structure模式下的BSS。已连接的节点list,代表VAP的本地网络。 OL节点结构包含下面内容:
- UMAC层节点 (an_node)
- VAP数据通路objecthandler (an_txrx_handle)
NOTE 其他的 OL 数据结构,如WMI / HTT / HTC / HIF and CE这些模块,此次已包含在上面章节中不再独立表述。