ATH9K Driver Learning Part I: Important Structures

编程入门 行业动态 更新时间:2024-10-13 10:25:14

ATH9K Driver <a href=https://www.elefans.com/category/jswz/34/1769507.html style=Learning Part I: Important Structures"/>

ATH9K Driver Learning Part I: Important Structures

本学习系列将着手于整理ath9k中的发包过程,希望能起到抛砖引玉的效果,得到前辈高人们的斧正。首先介绍几个在ath9k中最重要的几个结构体:sk_buff,ath_softc,ath_tx_control,ath_node,ath_atx_tid,ath_atx_ac,ath_txq,ath_tx 还有 ath_buf。

sk_buff

sk_buff 定义于/include/linux/skbuff.h。它本质上就是Linux内核中在协议栈中传送的结构体,即我们想要发送的packet。sk_buff 在不同 layer 的传递过程中,其头部会被加入相应的 protocol headers,例如 Ethernet,ip,tcp,udp 等等。sk_buff 结构体中一些重要的 components 汇总如下:

// /include/linux/skbuff.h
// 与链表相关的components	
struct sk_buff		*next; //指向链表中的下一个sk_buff
struct sk_buff		*prev; //指向链表中的上一个sk_buff
struct list_head	 list; //list 是定义在该sk_buff中的链表结构 
// 与储存的数据相关的components
unsigned int 		  len; //缓冲区中数据块大小(header+data)
unsigned int 	 data_len; //数据大小(data)
unsigned int      mac_len; //Mac header 大小
unsigned int     truesize;//整个缓冲区大小(包含未使用的部分)
sk_buff_data_t       tail;
sk_buff_data_t        end;
unsigned char   *head, *data;	// the head, data pointer indicating to the corresponding parts in the packet
// 与header相关的components
sk_buff_data_t          transport_header; //L4sk_buff_data_t          network_header; //L3sk_buff_data_t          mac_header; //L2
// Other Components
ktime_t		       tstamp; //该结构体产生时的time stamp
struct sock           *sk; //这个指针指向一个套接字sock数据结构
char 			   cb[40]; //(control buffer) 缓冲控制区,用来存储私有信息的空间。
sk_buf 的链表结构

sk_buff是一个复杂的双向链表。它有next和prev指针,分别指向链表的下一个sk_buff和前一个sk_buff。串联起所有sk_buff结构的链表头被称作sk_buff_head结构。
sk_buff_head结构定义为:

// /include/linux/skbuff.h
struct sk_buff_head {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;__u32       qlen;     //代表元素节点数目spinlock_t      lock; //加锁,防止对sk_buff的内部表的并发访问};

使用一张图片来展示sk_buff_head与其他sk_buff结构之间的关系:

sk_buf 指向的数据是如何储存的

sk_buf 本质上只是一个指针的集合,不同的指针指向位于 system memory 的缓冲区 (buffer) 的不同位置。这个 buffer 才是真正储存着数据的结构。sk_buff 结构与 buffer 之间的联系如下图所示:

因为 sk_buff 所对应的 buffer 在不同 layer 传递的时候要一层层添加或去掉一些数据。(比如报头)所以当 initialize sk_buf 的时候会申请一块大的足够的内存,然后在往里放东西。真实的实际数据可能用不了这么多,所以用 data,tail 指向真实的数据的边界,head, end 指向 buffer 的边界。刚开始没填充数据时 head, data, tail 指向的是同一个地址。
More specifically, head, end 分别 指向 memory 中分配的用来存 buffer 的起始地址和结尾。一旦 sk_buff 初始化完成后,这两个指针指向的位置便固定了。data 指向真实数据的开头地址,会随着 sk_buff 所处在的 layer 的不同而作相应的变动。tail 指向真实数据的末尾。

控制 sk_buff 的函数

用来控制 sk_buff 结构体的重要 functions 主要有:alloc_skb(),skb_reserve(),skb_put(),skb_push(),还有 skb_pull()。他们的工作原理和详细说明请参见 (link)。本篇文章将从功能性角度进行分析:

alloc_skb() 是用来初始化一个新的 sk_buff 结构体的 function。它会在 system memory 中专门划分出一个空间分配给新产生的 sk_buff 来储存数据。由于此时还没有正式写入数据,head, data 和 tail 三个指针全都指向 buffer 的起始处。如下图所示:

此时便会有一个问题,即然三个指针全指在一起,他们的功能如何保障呢?接下来 skb_reserve() function 便发挥作用。如下图所示,data 和 tail 指针一起下移,与 header 所指的 buffer 头部拉开了特定的空间。这样子就给后续写入的 data 提供了可能。

以上两个 function 更多的是做 initialization work。接下来的三个 functions 会在写入或者读取 buffer 数据时用到。
skb_push() 是一个帮助写入 MAC hearder 数据的 function,它把 data 指针上移一定的空间,然后 MAC header 便写在 data 指针下方。skb_put() 是针对 payload 数据的 function,它会把 tail 指针下移,从而为直接写入 payload 的 data 提供空间。skb_pull() function 用于读取过程。当系统读取完 data pointer 指向的数据块后,skb_pull() 便让指针下移让系统总是能定位到未读取过的数据。

对 sk_buff 结构体的功能有了基本认识后,就会发现它有很强的泛用性。该结构体不单单是应用在 ath9k driver 之下,而是贯穿于整个 transmission / receive 过程。接下来介绍的结构体全是定义在 ath9k driver 内的,特点是以 ath 为开头。

ath_softc

ath_softc 结构体是硬件与 MAC 层进行交互的中间载体,在 ath9k driver 之中随处可见。由于其覆盖的面太广,本篇文章参照 (link)先挑其中几个重要的 components 进行介绍,在后续的学习过程中会逐渐进行补充。

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_softc{struct ieee80211_hw *hw;    //保存硬件信息struct device *dev;         //当前工作的设备u32 chan_bw;                // 信道带宽int chan_idx;               // 信道序号// the two structures below are used to handle intteruptionsstruct tasklet_struct intr_tq;   struct tasklet_struct bcon_tasklet;	// structures that are useful during transmitting and receiving processesstruct ath_hw *sc_ah;       //hw的包装结构体struct ath_rx rx;			//在收包过程中十分重要的结构体,relate to 硬件中收包的寄存器struct ath_tx tx;struct ath_beacon beacon;// locksint irq;spinlock_t sc_serial_rw;spinlock_t sc_pm_lock;spinlock_t sc_pcu_lock;    //进行数据读取,或者处理skb时需要的锁
}

ath_tx_control

ath_tx_control 结构体是一个基于一个 sk_buff 结构体构建出来的,它包含该 sk_buff (一个 packet )所处的节点 (node),传输队列 (txq) 和节点对应的 station 参数。

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_tx_control {struct ath_txq *txq; // Generate by: skb_get_queue_mapping(skb)struct ath_node *an; // Generate by: ath_tx_prepare(hw, skb, txctl)struct ieee80211_sta *sta; // Get from: ieee80211_tx_controlu8 paprd; // Not 100% sure, this should be a status variable meaning prepared?bool force_channel;
};

ath_node

ath_node 本质上就是 station 在 ath9k driver 中的映射,它的源代码如下:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_node {struct ath_softc *sc;struct ieee80211_sta *sta; /* station struct we're part of */struct ieee80211_vif *vif; /* interface with which we're associated */struct ath_atx_tid tid[IEEE80211_NUM_TIDS]; //Defines the maximum number of tid (thread ID queue) can be used in a node.struct ath_atx_ac ac[IEEE80211_NUM_ACS];    // Defines the maximum number of ac (access category queue) can be used in a node.u16 maxampdu;u8 mpdudensity;s8 ps_key;bool sleeping;bool no_ps_filter;#ifdef CONFIG_ATH9K_STATION_STATISTICSstruct ath_rx_rate_stats rx_rate_stats;
#endifu8 key_idx[4];u32 ackto;struct list_head list;
};

可见 ath_node 的结构可以分为三个部分。第一个部分中包含了该 node 所映射的 station and vif (virtual interface) 还有 softcore 的信息。第二个部分定义了一个 node 中可以使用多少 tid and ac 队列。第三个部分定义了 node 在工作的过程中使用的参数。第二个部分尤其重要,因为它揭示了 ath_node, ath_atx_tid and ath_atx_ac 实际上是紧密联系在一起的,共同实现把 packets 映射到发送队列的功能。

ath_atx_tid

tid queue means “thread ID queue”,thread 即为进程。如今的通信设备大多是支持多线程工作的,因此一个 node 内部很可能会有多个进程在尝试着使用 ath9k driver 来发送 packets (sk_buff)。 根据 ath_node 中的规定,一个 node 中最多支持16 + 1 (one for management frame) 个 threads。这些 tid queues 在 ath9k driver 中定义为 ath_atx_tid,其 structure 如下:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_atx_tid {struct list_head list;        //NOT 100% SURE, different ath_atx_tid structures are stringed under the same list_head: liststruct sk_buff_head buf_q;    //buf_q is the head of a list of sk_buffs waiting to be transmitted (FIFO)struct sk_buff_head retry_q;  // retry_q is the head of a list of sk_beffs waiting to be re-transmitted (FIFO)struct ath_node *an;          // the node where this tid queue belongs tostruct ath_atx_ac *ac;        //the Access Category queue associated with this tid queueunsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; 类似于Bitmap,标记窗口内已发送的数据帧u16 seq_start;	// 当前发送序号的开始u16 seq_next;	// 下一次发送序号的开始u16 baw_size;	// Block Ack Window 的大小 一般为128u8 tidno;		//标记当前传输的数据的类型:媒体数据或者是文本数据等等int baw_head;   /* first un-acked tx buffer */int baw_tail;   /* next unused tx buffer slot */s8 bar_index;	// Block Ack Request的索引bool sched;bool active;
};

从源代码中我们可以看出,buf_q 是 ath_atx_tid struct 中最重要的结构体。例如 在 function ath_tx_start() 中使用了 subfunction: __skb_queue_tail(&ath_atx_tid->buf_q, sk_buff) 来把一个 packet 给加到以 buf_q 为头部的链表结构的末尾。由于 tid queue 遵循 FIFO (first in first out), 新加入的 packet 会被优先发送出去。

ath_atx_ac

虽然 node 中已经构建起来先要传输的 packet lists 了,但不可以直接把这些 lists 扔给传输队列然后进行 transmission。原因是 IEEE 802.11e 引入了用户优先级 (user priority) 的概念,必须确保有更高优先级的 packet 可以被传输成功 (link)。

IEEE 802.11e 中定义了八个优先级,分为四个 ac (access categories)。每一个 ac 对应着两个优先级。有着优先级别 0 的 thread 中的 packets 便会被送到传输条件最好,或者说 QoS 最大的 ac queue 之中。不仅如此,当来自不同 thread 的 packets 发生冲突时,优先级高的 packet 会被确保可以成功发送。每一个 tid queue 所对应的 ac 定义在 struc ath_atx_tid 中。

struct ath_atx_ac 的源代码相对来说简单不少:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_atx_ac {struct ath_txq *txq;	// the transmission queue associated with this ac. // It will be an error if this txq is different with // the txq defined in ath_tx_controlstruct list_head list;  //???? NOT 100% SURE: different ath_atx_ac structs are stringed by the list head:liststruct list_head tid_q; // Packets from the tid queue are listed under the tid_q headerbool clear_ps_filter;bool sched;
};

ath_txq

ath9k driver 中的传输队列便是 struc ath_txq,也是一个储存 packets 的结构。它最大的特点就是包含一个 u32 axq_qnum,是 ath9k 中的 hardware queue number。ath_txq 与 hardware queue 是一一对应的。一旦 hardware queue 中收到了 packets, hardware 就会催发 interrupts 来把 packets 给传出去。

hardware queue 一共有 HAL_NUM_TX_QUEUES (10)个,因此 ath_txq 也会有十个。但只有前四个即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的需求。更多详细内容请参考 (link)

tid queue, ac queue 和 txq 之间的对应关系如下图所示:

ath_txq 的源代码如下:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_txq {int mac80211_qnum; // mac80211 queue number, NOT 100% SURE how the mac queue number is associated with hardware queue numebr// Update:mac80211_qnum is not that important... Mainly used for initialization. u32 axq_qnum; /* ath9k hardware queue number */void *axq_link;	   // This void varaible is used to store the virtual addess of the descriptorstruct list_head axq_q; // This is the head of the list storing bfs going to be transmitted in function: ath_tx_txqaddbufspinlock_t axq_lock;u32 axq_depth;u32 axq_ampdu_depth;bool axq_tx_inprogress;struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; // This is the structure specially used for EDMA (ath_tx_txqaddbuf)u8 txq_headidx;u8 txq_tailidx;int pending_frames;struct sk_buff_head complete_q;
};

ath_tx

从目前我所学到的知识而言,我认为 ath_tx 结构体宏观性的设置了在 transmission 过程中的一系列重要的参数。上 source code:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_tx {u32 txqsetup;spinlock_t txbuflock;struct list_head txbuf;	// txbuf is the head of a list storing ath_buf structures.// However, the difference between that and axq_q in ath_txq //is still not figured outstruct ath_txq txq[ATH9K_NUM_TX_QUEUES];     // Set the number of tx queues be 10struct ath_descdma txdma;struct ath_txq *txq_map[IEEE80211_NUM_ACS]; // Set the number of ac queue as 4struct ath_txq *uapsdq;u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
};

可见 ath_tx 中定义了 tx and ac queue 的数量,并且建立了一个专门的 list_head txbuf 来储存 ath_buf 结构体。相信若没有 ath_tx,这些基本的参数设置便无法完成。目前的问题是 txbuf 和 ath_txq 中的 axq_q 有什么区别?这可能与虚拟与物理地址有关系吧?因为 ath_buf 就是干这个活的,还需要进一步学习。

ath_buf

经过进一步的学习我认为 ath_buf 它其实就是一个 decriptor。在下文中的源代码可以看到 ath_buf 其实包含了除了 hardware queue number 以外的,所有用于 packet transmission 的信息。bf_daddr and bf_buf_addr 确保了不论是 CPU or Hadrware 都可以定位到 descriptor。指针 bf_mpdu 是指向要发送的 ath_buf packet的。综上所述,如果硬件想要发包,不论是 EDMA 或是由 CPU 控制的,都可以通过 ath_buf 来定位到目标 packet。事实上,hardware queue number 和 ath_buf 往往是紧密结合在一起配套使用的,function: ath9k_hw_puttxbuf 即为例证。source code 如下:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_buf {struct list_head list;struct ath_buf *bf_lastbf;	/* last buf of this unit (a frame oran aggregate) */struct ath_buf *bf_next;	/* next subframe in the aggregate */struct sk_buff *bf_mpdu;	/* Mac Protocol Data Unit, this is the structure storing packets */void *bf_desc;			/* virtual addr of desc */dma_addr_t bf_daddr;		/* physical addr of desc */dma_addr_t bf_buf_addr;	/* physical addr of data buffer, for DMA */struct ieee80211_tx_rate rates[4];struct ath_buf_state bf_state;
};

ath_rx

经过一段时间的学习,开始 touch 到 ath9k 中的收包过程了。其中在 ath_softc 下的 ath_rx 结构体非常的关键。该结构体直接 relate 到了 hardware 中储存收到的包的寄存器。上代码:

// /drivers/net/wireless/ath/ath9k/ath9k.h
struct ath_rx {u8 defant;u8 rxotherant;bool discard_next;u32 *rxlink;u32 num_pkts;struct list_head rxbuf;  struct ath_descdma rxdma;struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];struct ath_rxbuf *buf_hold;	// points to the received packet holded in ath_rxstruct sk_buff *frag;u32 ampdu_ref;
};

着重关注于 struct list_head rxbuf,rxbuf 是储存收到的包的链表的链表头结构,因此可以利用它快速地访问收包寄存器。

ath_rx_status

一旦 hardware 收到 packet, 会先在 physical layer 产生一个 ath_rx_status 结构来初步储存该 packet 的一些信息,为 physical layer preprocess 做准备。ath_rx_status 的代码如下:

// drivers/net/wireless/ath/ath9k/mac.h
struct ath_rx_status {u32 rs_tstamp;		// Record the time stamp of the received packet, it is very important// for tsf (time synchronization function) later oneu16 rs_datalen;		// The length of the received packet, can be used to help discard zero 			 length packetu8 rs_status;u8 rs_phyerr;int8_t rs_rssi;		// Very important in calculating the signal strength u8 rs_keyix;u8 rs_rate;u8 rs_antenna;u8 rs_more;int8_t rs_rssi_ctl[3];int8_t rs_rssi_ext[3];u8 rs_isaggr;u8 rs_firstaggr;u8 rs_moreaggr;u8 rs_num_delims;u8 rs_flags;bool is_mybeacon;u32 evm0;u32 evm1;u32 evm2;u32 evm3;u32 evm4;u16 enc_flags;enum rate_info_bw bw;
};

需要强调的是,ath_rx_status 主要在 function: ath9k_rx_skb_preprocess 中发挥作用,目的是对 packet 的类型(frag or not?)及其完整性来进行检查。若该 packet 没有问题,就会利用 ath_rx_status 去构建 ieee80211_rx_status 结构体,ieee80211_rx_status 才是被传至 mac layer 中进行处理的结构体。

ieee80211_rx_status

正如上文所说,ieee80211_rx_status 是在 physical layer 构建最终传入 mac layer 中进行处理的重要结构体。它初始定义于 function: ath_rx_tasklet 中, 内容构建于 function: ath9k_rx_skb_preprocess。ieee80211_rx_status 的代码如下:

// /include/net/mac80211.h
/*** struct ieee80211_rx_status - receive status** The low-level driver should provide this information (the subset* supported by hardware) to the 802.11 code with each received* frame, in the skb's control buffer (cb).** @mactime: value in microseconds of the 64-bit Time Synchronization Function* 	(TSF) timer when the first data symbol (MPDU) arrived at the hardware.* @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is*	needed only for beacons and probe responses that update the scan cache.* @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use*	it but can store it and pass it back to the driver for synchronisation* @band: the active band when this frame was received* @freq: frequency the radio was tuned to when receiving this frame, in MHz*	This field must be set for management frames, but isn't strictly needed*	for data (other) frames - for those it only affects radiotap reporting.* @freq_offset: @freq has a positive offset of 500Khz.* @signal: signal strength when receiving this frame, either in dBm, in dB or*	unspecified depending on the hardware capabilities flags*	@IEEE80211_HW_SIGNAL_** @chains: bitmask of receive chains for which separate signal strength*	values were filled.* @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't*	support dB or unspecified units)* @antenna: antenna used* @rate_idx: index of data rate into band's supported rates or MCS index if*	HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)* @nss: number of streams (VHT and HE only)* @flag: %RX_FLAG_\** @encoding: &enum mac80211_rx_encoding* @bw: &enum rate_info_bw* @enc_flags: uses bits from &enum mac80211_rx_encoding_flags* @he_ru: HE RU, from &enum nl80211_he_ru_alloc* @he_gi: HE GI, from &enum nl80211_he_gi* @he_dcm: HE DCM value* @rx_flags: internal RX flags for mac80211* @ampdu_reference: A-MPDU reference number, must be a different value for*	each A-MPDU but the same for each subframe within one A-MPDU* @ampdu_delimiter_crc: A-MPDU delimiter CRC* @zero_length_psdu_type: radiotap type of the 0-length PSDU*/
struct ieee80211_rx_status {u64 mactime;u64 boottime_ns;u32 device_timestamp;u32 ampdu_reference;u32 flag;u16 freq: 13, freq_offset: 1;u8 enc_flags;u8 encoding:2, bw:3, he_ru:3;u8 he_gi:2, he_dcm:1;u8 rate_idx;u8 nss;u8 rx_flags;u8 band;u8 antenna;s8 signal;u8 chains;s8 chain_signal[IEEE80211_MAX_CHAINS];u8 ampdu_delimiter_crc;u8 zero_length_psdu_type;
};

官网竟然给了这么多备注,惊了。
这里主要记录下一些重要的参数是如何计算出来的。mactime 其实就是用于 tsf 的参数,其计算公式为:(ath9k_process_tsf)
rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp
其中 rxs 为指向 ieee80211_rx_status 的指针,tsf 为 hardware time, rs 为一个指向 ath_rx_status 的指针。

至于计算信号强度的公式:(ath9k_process_rssi)
rxs->signal = ah->noise + rx_stats->rs_rssi;
其中 rxs 为指向 ieee80211_rx_status 的指针, ah 为指向 ath_hw 的指针,rx_stats 为一个指向 ath_rx_status 的指针。

其他的一些公式:(ath9k_rx_skb_preprocess)
rx_status->band = ah->curchan->chan->band;
rx_status->freq = ah->curchan->chan->center_freq;
rx_status->antenna = rx_stats->rs_antenna;
rx_status->flag |= RX_FLAG_MACTIME_END;
rx_status 是指向 ieee80211_rx_status 的指针

更多推荐

ATH9K Driver Learning Part I: Important Structures

本文发布于:2024-03-04 13:45:16,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1709416.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Learning   Driver   ATH9K   Structures   Important

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!