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; //L4

sk_buff_data_t          network_header; //L3

sk_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 intteruptions
    struct tasklet_struct intr_tq;   
    struct tasklet_struct bcon_tasklet;	
    
    // structures that are useful during transmitting and receiving processes
    struct ath_hw *sc_ah;       //hw的包装结构体
    struct ath_rx rx;			//在收包过程中十分重要的结构体,relate to 硬件中收包的寄存器
	struct ath_tx tx;
	struct ath_beacon beacon;
	
	// locks
	int 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_control
	u8 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_STATISTICS
	struct ath_rx_rate_stats rx_rate_stats;
#endif
	u8 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: list
	struct 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 to
	struct ath_atx_ac *ac;        //the Access Category queue associated with this tid queue
	unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)]; 类似于Bitmap,标记窗口内已发送的数据帧
	u16 seq_start;	// 当前发送序号的开始
	u16 seq_next;	// 下一次发送序号的开始
	u16 baw_size;	// Block Ack Window 的大小 一般为128
	u8 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_control
	struct list_head list;  //???? NOT 100% SURE: different ath_atx_ac structs are stringed by the list head:list
	struct list_head tid_q; // Packets from the tid queue are listed under the tid_q header
	bool 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 descriptor
	
	struct list_head axq_q; // This is the head of the list storing bfs going to be transmitted in function: ath_tx_txqaddbuf
	spinlock_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 out
	struct ath_txq txq[ATH9K_NUM_TX_QUEUES];     // Set the number of tx queues be 10
	struct ath_descdma txdma;
	struct ath_txq *txq_map[IEEE80211_NUM_ACS]; // Set the number of ac queue as 4
	struct 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 or
   				   an 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_rx
	struct 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 one
	u16 rs_datalen;		// The length of the received packet, can be used to help discard zero 			 length packet
	u8 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 的指针

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值