madwifi学习记录(4)
本次madwifi学习记录主要针对数据收发在内存和外设之间如何实现进行研究
1. 在ath_tx_start中,有个函数bus_map_single, 它主要用于将数据由内存拷贝到总线设备的存储空间中,同时返回设备中的地址,应该是真正的物理地址
2. 同样在ath_rx_tasklet中,有函数bus_unmap_single, 作用和bus_map_single相反
3. 在ath_rx_tasklet中出现bus_unmap_single出现之前,有函数bus_dma_sync_single,作用和bus_unmap_single基本相同,主要定义都是dma_cache_wback_inv
4. 关于bus函数的参数中,最后一个参数表明传递方向,在if_ath_ahb.h中有相关定义
#define BUS_DMA_FROMDEVICE 0
#define BUS_DMA_TODEVICE 1
5. 关于bus函数的参数说明:总线设备描述符(这个还存在疑问,注释为associated bus device),要传输的数据,数据长度,方向
6. bus_map_single和bus_unmap_single的定义在不同情况下不同,在if_ath.c的开头有这样一段
#ifdef ATH_PCI /* PCI BUS */
#include “if_ath_pci.h”
#endif /* PCI BUS */
#ifdef ATH_AHB /* AHB BUS */
#include “if_ath_ahb.h”
#endif /* AHB BUS */
在 if_ath_pci.h 中定义的bus实际上就是pci_map_single和pci_unmap_single,这两个为linux系统函数,貌似和DMA相关,具体定义可以去在线网站查询
在 if_ath_ahb.h 中定义的bus实际上就是前面说过的dma_cache_wback_inv
static __inline void bus_dma_sync_single(void *hwdev, dma_addr_t dma_handle, size_t size, int direction)
{
unsigned long addr;
addr = (unsigned long) __va(dma_handle);
dma_cache_wback_inv(addr, size);
}
static __inline dma_addr_t bus_map_single(void *hwdev, void *ptr, size_t size, int direction)
{
dma_cache_wback_inv((unsigned long) ptr, size);
return __pa(ptr);
}
{
dma_cache_wback_inv((unsigned long) ptr, size);
return __pa(ptr);
}
static __inline void bus_unmap_single(void *hwdev, dma_addr_t dma_addr, size_t size, int direction)
{
if (direction != BUS_DMA_TODEVICE) {
unsigned long addr;
addr = (unsigned long)__va(dma_addr);
dma_cache_wback_inv(addr, size);
}
}
对比三段定义发现基本都是在用一个函数,关于dma_cache_wback_inv在一个英文论坛里有部分解释:
dma_cache_wback() : This function write back the cache data to memory
dma_cache_inv : This function invalidate the cache tags. so subsequent access will fetch from memory
换句话讲, dma_cache_wback_inv应该是在进行cache和memory的数据交换,It does both write back and invalidate
{
if (direction != BUS_DMA_TODEVICE) {
unsigned long addr;
addr = (unsigned long)__va(dma_addr);
dma_cache_wback_inv(addr, size);
}
}
对比三段定义发现基本都是在用一个函数,关于dma_cache_wback_inv在一个英文论坛里有部分解释:
dma_cache_wback() : This function write back the cache data to memory
dma_cache_inv : This function invalidate the cache tags. so subsequent access will fetch from memory
换句话讲, dma_cache_wback_inv应该是在进行cache和memory的数据交换,It does both write back and invalidate
7. 在chinaunix中有一段关于DMA的说法挺好,最后引用过来:
现在的网卡大多数都是采用主动DMA的方式。也就是当网卡从网线上接收到数据之后,就会自己启动dma将数据从网卡内部的FIFO传送到配置寄存器指定的 内存地址。当一个数据包接收完成之后,产生一个中断通知driver进行处理。整个过程不需要cpu进行干涉。当driver接收到中断的时候,数据已经 在内存里存放好了。
madwifi学习记录(5)
首先做一个更正,对于学习记录(4)中出现了一些误解,有点问题。
没有什么把数据放到设备上的过程,数据包一直在内存里,网卡硬件也是去内存里读取这个包,这是不是传说的DMA咱不清楚,但关键是什么时候把这个包的地址告诉硬件,就是ath_hal_puttxbuf函数。通过把描述符的地址告诉硬件,间接地把包的地址告诉硬件。
首先要肯定的是sc只有一个,对应着物理设备。在网卡初始化时,会调用ath_desc_alloc,继而调用
ath_descdma_setup,在这里,会为sc->sc_txbuf分配200个buffer,然后为每个buffer分配一个描述符。也
就是说网卡初始化之后,就有200个tx buffer和200个描述符,一一对应地存在于内存中了。
然后将每个buffer的bf_daddr指针指向自己对应描述符的:物理地址;每个buffer的bf_desc指针则指向对应描述符的:虚拟地
址。发送包时,首先调ath_hardstart函数,在这里,用ATH_HARDSTART_GET_TX_BUF_WITH_LOCK这个宏,取得这
200个buffer中的某一个赋给bf这个变量。然后调用ath_tx_start函数,在这里,将数据包的真实物理地址,赋给此bf的
bf_skbaddr指针。
然后定义一个ds指针,将这个指针指向此bf对应的描述符,也就是bf_desc,对ds进行一系列操作,也就是把此bf对应的描述符里的有用的内
容都填好。然后将ds的ds_data指针指向bf的bf_skbaddr。这样,只要找到描述符,就可以找到数据包的物理地址了。
在ath_tx_start函数里还会根据情况选择一个txq,但是网卡硬件是用不到这个txq结构的,硬件关心的只是这是第几个q,也就是
txq->axq_qnum,最后到了ath_tx_txqaddbuf函数,在if (txq->axq_link ==
NULL)的时候,会调用ath_hal_puttxbuf(ah, txq->axq_qnum,
bf->bf_daddr),这里的参数txq->axq_qnum是q号码,bf->bf_daddr呢?就是这个bf对应的描述符
的物理地址,这个函数实际上就是把这个描述符物理地址写到一个寄存器里。比如网卡里有四个寄存器,
ds_for_q1,ds_for_q2,ds_for_q3,ds_for_q4,作用是储存4个q对应的描述符的物理地址,那么 ath_hal_puttxbuf就是把描述符物理地址写到相应寄存器里。
ds_for_q1,ds_for_q2,ds_for_q3,ds_for_q4,作用是储存4个q对应的描述符的物理地址,那么 ath_hal_puttxbuf就是把描述符物理地址写到相应寄存器里。
最后调用ath_hal_txstart(ah, txq->axq_qnum)了,参数只有一个q号码,那么接下来,硬件的动作是:
网卡通过这个q号码—–>找到对应的寄存器—–>里面放的是一个描述符的物理地址—–>找到这个描述符—–>这个描述符里有一个指针ds_data——>ds_data就是数据包的物理地址—–>找到数据包—–>发送。
madwifi学习记录(6)
madwifi学习记录的学习记录系列以后应该还会有更新,但是频率嘛,我就不好说了,呵呵。毕业设计差不多快弄完了,但是估计和madwifi的关系不会太早就结束,没准整个研究生阶段都会与它打交道,呵呵。写一点最近的学习心得。
1.
关于txq的话题其实有很多,这也是我一直所关心的问题。前文说过在madwifi中txq本身定义了四个,当然其设计时也就会用到四个,虽然其允许支持
到10个。在struct
ath_softc中有一个sc_ac2q结构,这个结构和txq是相对应的。WME_NUM_AC定义的个数是4,同样对应四中类型,可以说是与之关联
的:
#define WME_AC_BE 0 /* best effort */
#define WME_AC_BK 1 /* background */
#define WME_AC_VI 2 /* video */
#define WME_AC_VO 3 /* voice */
#define WME_AC_BE 0 /* best effort */
#define WME_AC_BK 1 /* background */
#define WME_AC_VI 2 /* video */
#define WME_AC_VO 3 /* voice */
2.
madwifi支持多种工作模式,adhoc,ap等等吧,不过最近在用的是ahdemo模式,其实是adhoc模式的一个变种,不会发送管理帧。顺便提
下管理帧的定义,在ieee80211.h中,IEEE80211_FC0_TYPE_MASK和IEEE80211_FC0_SUBTYPE_MASK
用来判断包的类型,如果是管理帧的话type应该是0,然后对应相应的subtype,比如IEEE80211_FC0_SUBTYPE_BEACON就
是0×80, 用法为:
struct ieee80211_frame *wh = (struct ieee80211_frame *)bf->bf_skb->data;
u_int8_t type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
u_int8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (type == IEEE80211_FC0_TYPE_MGT && subtype == IEEE80211_FC0_SUBTYPE_BEACON) {……}
struct ieee80211_frame *wh = (struct ieee80211_frame *)bf->bf_skb->data;
u_int8_t type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
u_int8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (type == IEEE80211_FC0_TYPE_MGT && subtype == IEEE80211_FC0_SUBTYPE_BEACON) {……}
3.
关于中断方面。首先,有一种叫做软中断的东西,是Linux的东西,madwifi把它封装了一下,然后给驱动程序用。其实本质是这样的:首先向系统注册
该中断,对应一个专属中断队列、处理函数和设备指针,在初始化设置之后,系统中的一个内核线程会周期的遍历中断向量列表,一旦发现某个软中断向量挂起,则
会迅速调用其处理函数。这种软中断的使用是非常有意义的,能够很快的进行处理,而又不会死循环,造成无法释放资源。在ath/if_ath.c中,有很多
这种软中断,在ath_attach中 ATH_INIT_TQUEUE(&sc->sc_rxtq,
ath_rx_tasklet,
dev);就是一个init,而用ATH_SCHEDULE_TQUEUE就能完成调度的申请。当然因为内核版本问题,madwifi给封装成了这两个
“函数”,需要再用个 if(needmark) {mark_bh(IMMEDIATE_BH);}
4.
当硬件产生一个中断的时候,中断会提供给ath_intr函数,该函数根据相应的情况传递给相应的处理函数,比如如果是硬件发送了一个数据包,并产生中断
的话,就在if (status & HAL_INT_TX)
处理,并传递给processq。如果想让发送的包在硬件发包后产生硬件中断的话,就在ath_tx_start函数中设置flags时设置一个位,
flags |= HAL_TXDESC_INTREQ;
flags |= HAL_TXDESC_INTREQ;
» 本文来自:橙叶WLAN » 《madwifi学习心得二》
No comments:
Post a Comment