Monday, October 10, 2011

madwifi学习心得二

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);
}

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

    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就是把描述符物理地址写到相应寄存器里。


    最后调用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 */


    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) {……}


    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;



No comments:

Post a Comment