当前位置 博文首页 > liujihang88的专栏:关于ath5k网卡驱动中beacon的发送过程(特别

    liujihang88的专栏:关于ath5k网卡驱动中beacon的发送过程(特别

    作者:[db:作者] 时间:2021-07-30 15:12


    ??????????????????????????????????????????? ath5k网卡驱动中beacon

    工作模式:ad-hoc??? 内核版本:3.10.38

    ? 最近在做一个项目,要发送一个ta指针里面有一个时间戳的问题,所以研究了beacon在ath5k中的生成和发送。这个发送过程主要是网卡工作在adhoc的模式下。由于要做到时间同步,所以beacon中的timestamp字段的值表示的时间应该是这个beacon从硬件哪里发出去的时间,这样对方收到了才能够根据这个同步。而这个时间戳在这个网卡驱动下是由硬件打印上去。



    在__ieee80211_sta_join_ibss()函数(内核3.10.38中mac802111文件中ibss.c中)中

    ??????????? 主要通过对sdata->u.ibss->presp进行赋值,通过head 指针指向mac80211制定的beacon(但是这里的timestamp取值为0,这个值主要是在硬件哪里修改,随后会看到),但是这个函数中主要通过sdata->vif.bss_conf中进行配置来让网卡按照我们的配置发送beacon。最后通过函数ieee80211_bss_info_change_notify来通知网卡做相应的改动。

    struct beacon_data *presp
    struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
    .....................................
    <pre name="code" class="cpp"><pre name="code" class="html">presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
    if (!presp)
          return;
    presp->head = (void *)(presp + 1);
    mgmt = (void *) presp->head;
    mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                              IEEE80211_STYPE_PROBE_RESP);
    eth_broadcast_addr(mgmt->da);
    memset(mgmt->da,0x01,ETH_ALEN);
    memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
    memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
    memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
    mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int);
    mgmt->u.beacon.timestamp = cpu_to_le64(tsf);
    .................................... 
    rcu_assign_pointer(ifibss->presp, presp);
    <pre name="code" class="html">sdata->vif.bss_conf.enable_beacon = true;
    sdata->vif.bss_conf.beacon_int = beacon_int;
    sdata->vif.bss_conf.basic_rates = basic_rates;
    sdata->vif.bss_conf.ssid_len = ifibss->ssid_len;
    memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);
    bss_change = BSS_CHANGED_BEACON_INT;
    bss_change |= ieee80211_reset_erp_info(sdata);
    bss_change |= BSS_CHANGED_BSSID;
    bss_change |= BSS_CHANGED_BEACON;
    bss_change |= BSS_CHANGED_BEACON_ENABLED;
    bss_change |= BSS_CHANGED_BASIC_RATES;
    bss_change |= BSS_CHANGED_HT;
    bss_change |= BSS_CHANGED_IBSS;
    bss_change |= BSS_CHANGED_SSID;
    ..........................................
    ieee80211_bss_info_change_notify(sdata, bss_change);
     
    

    通过查看,容易找到ieee80211_bss_info_change_notify()这个函数最后调用的是ath5k中的ath5k_bss_info_changed()这个函数。

            struct ath5k_vif *avf = (void *)vif->drv_priv;
            struct ath5k_hw *ah = hw->priv;
            struct ath_common *common = ath5k_hw_common(ah);
            printk("in ath5k_bss_info_changed\n");
            mutex_lock(&ah->lock);
    
            if (changes & BSS_CHANGED_BSSID) {
                    /* Cache for later use during resets */
                    memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
                    common->curaid = 0;
                    ath5k_hw_set_bssid(ah);
                    mmiowb();
            }
    
            if (changes & BSS_CHANGED_BEACON_INT)
                    ah->bintval = bss_conf->beacon_int;//这里改变beacon的interval。发送间隔的时间
    
            if (changes & BSS_CHANGED_ERP_SLOT) {
                    int slot_time;
    
                    ah->ah_short_slot = bss_conf->use_short_slot;
                    slot_time = ath5k_hw_get_default_slottime(ah) +
                                3 * ah->ah_coverage_class;
                    ath5k_hw_set_ifs_intervals(ah, slot_time);
            }
    
            if (changes & BSS_CHANGED_ASSOC) {
    ??????????????? avf->assoc = bss_conf->assoc;
    ??????????????? if (bss_conf->assoc)
    ??????????????????????? ah->assoc = bss_conf->assoc;
    ??????????????? else
    ??????????????????????? ah->assoc = ath5k_any_vif_assoc(ah);
    
    ??????????????? if (ah->opmode == NL80211_IFTYPE_STATION)
    ??????????????????????? ath5k_set_beacon_filter(hw, ah->assoc);
    ??????????????? ath5k_hw_set_ledstate(ah, ah->assoc ?
    ??????????????????????? AR5K_LED_ASSOC : AR5K_LED_INIT);
    ??????????????? if (bss_conf->assoc) {
    ??????????????????????? ATH5K_DBG(ah, ATH5K_DEBUG_ANY,
    ????????????????????????????????? "Bss Info ASSOC %d, bssid: %pM\n",
    ????????????????????????????????? bss_conf->aid, common->curbssid);
    ??????????????????????? common->curaid = bss_conf->aid;
    ??????????????????????? ath5k_hw_set_bssid(ah);
    ??????????????????????? /* Once ANI is available you would start it here */
    ??????????????? }
    ??????? }
    
    ??????? if (changes & BSS_CHANGED_BEACON) {
    ??????????????? spin_lock_bh(&ah->block);
    ??????????????? ath5k_beacon_update(hw, vif);//这个函数吧beacon从mac80211层的队列中赋值出来放在ath5k存放beacon的队列中
    ??????????????? spin_unlock_bh(&ah->block);
    ??????? }
    
    ??????? if (changes & BSS_CHANGED_BEACON_ENABLED)
    ??????????????? ah->enable_beacon = bss_conf->enable_beacon;
    
    ??????? if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
    ?????????????????????? BSS_CHANGED_BEACON_INT))
    ??????????????? ath5k_beacon_config(ah);
    
    ??????? mutex_unlock(&ah->lock);
    
    
    这个函数中可以看到当修改了beacon的时候就会调用ath5k_beacon_update这个函数中主要调用了ieee80211_beacon_get()从上述建立的presp中的beacon取出来,然后吧他放在ath5k??? avf->bbuf->skb中去
    ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
    {
            int ret;
            struct ath5k_hw *ah = hw->priv;
            struct ath5k_vif *avf;
            struct sk_buff *skb;
    
            if (WARN_ON(!vif)) {
                    ret = -EINVAL;
                    goto out;
            }
    
            skb = ieee80211_beacon_get(hw, vif);
    
            if (!skb) {
                    ret = -ENOMEM;
                    goto out;
            }
    
            avf = (void *)vif->drv_priv;
            ath5k_txbuf_free_skb(ah, avf->bbuf);
            avf->bbuf->skb = skb;
            ret = ath5k_beacon_setup(ah, avf->bbuf);
    out:
            return ret;
    }
    


    接下来就主要是ath5k_beacon_config(ah)来配置beacon.

    ath5k_beacon_config(struct ath5k_hw *ah)
    {
            spin_lock_bh(&ah->block);
            ah->bmisscount = 0;
            ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
    
            if (ah->enable_beacon) {
                    /* 
                     * In IBSS mode we use a self-linked tx descriptor and let the
                     * hardware send the beacons automatically. We have to load it
                     * only once here.
                     * We use the SWBA interrupt only to keep track of the beacon
                     * timers in order to detect automatic TSF updates.
                     */
                    ath5k_beaconq_config(ah);
        
                    ah->imask |= AR5K_INT_SWBA;
        
                    if (ah->opmode == NL80211_IFTYPE_ADHOC) {
                            if (ath5k_hw_hasveol(ah))
                                    ath5k_beacon_send(ah);
                    } else
                            ath5k_beacon_update_timers(ah, -1);
            } else {
                    ath5k_hw_stop_beacon_queue(ah, ah->bhalq);
            }
    
            ath5k_hw_set_imr(ah, ah->imask);
            mmiowb();
            spin_unlock_bh(&ah->block);
    }
    
    可以看到中间主要调用了ath5k_beaconq_config()这个函数是个关键函数,主要对里面beacon发送的竞争窗口大小进行设定,这里beacon是在0和2*最小竞争窗口之间产生一个随机值,这个值就代表beacon的backoff机制。 这个代码中还有一个tqi.ready_time具体拿来干嘛不是很清楚,希望有大神指出。
    ath5k_beaconq_config(struct ath5k_hw *ah)
    {
            struct ath5k_txq_info qi;
            int ret;
    
            ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi);
            if (ret)
                    goto err;
    
            if (ah->opmode == NL80211_IFTYPE_AP ||
                ah->opmode == NL80211_IFTYPE_MESH_POINT) {
                    /*
                     * Always burst out beacon and CAB traffic
                     * (aifs = cwmin = cwmax = 0)
                     */
                    qi.tqi_aifs = 0;
                    qi.tqi_cw_min = 0;
                    qi.tqi_cw_max = 0;
            } else if (ah->opmode == NL80211_IFTYPE_ADHOC) {
                    /*
                     * Adhoc mode; backoff between 0 and (2 * cw_min).
                     */
                    qi.tqi_aifs = 0;
                    qi.tqi_cw_min = 0;
                    qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;
            }
    
            ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,
                    "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
                    qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
    
            ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi);
            if (ret) {
                    ATH5K_ERR(ah, "%s: unable to update parameters for beacon "
                            "hardware queue!\n", __func__);
                    goto err;
            }
    ??????? ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */
    ??????? if (ret)
    ??????????????? goto err;
    
    ??????? /* reconfigure cabq with ready time to 80% of beacon_interval */
    ??????? ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
    ??????? if (ret)
    ??????????????? goto err;
    
    ??????? qi.tqi_ready_time = (ah->bintval * 80) / 100;//<span style="color:#FFCC00;"><span style="background-color: rgb(153, 0, 0);">这个地方我不太懂,也不知道这个时间拿来干嘛?希望有大神指出</span>。</span>
    ??????? ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
    ??????? if (ret)
    ??????????????? goto err;
    
    ??????? ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
    err:
    ??????? return ret;
    }
    
    
    ?


    最后调用的就是ath5k_beacon_send()这个函数不是真正的发送beacon而是往beacon控制寄存器中写一些控制。就是告诉网卡,去哪里通过DMA把这个beacon缓存到网卡中来,到了beacon发送的时间就自动发送(这个时间是,beacon的4个定时器来通知)。




    上面就是关于beacon发送的流程,但是上述主要讲述了beacon的生成,没有讲述到beacon的发送,而beacon的发送主要是通过硬件实现的。主要有4个关于beacon的时钟,我们通过这个时钟来定时发送beacon

    这次我按照调用关系反这来说,首先是最下面的函数,ath5k_hw_init_beacon_timers()这个函数主要设定四个寄存器,存放时钟的值。当tsf到达指定时间,网卡就发出相应的中断(这里发出中断应该是硬件发出)。

    timer0 :里面存放的是下一个发送beacon的时间(这个时间是按照网卡里面的tsf,即tsf到达指定的值,就发送beacon,以下的时间同理)。

    timer1:是一个关于DMA的时间,在每次发送beacon前,我们要用DMA技术,从内存中吧相应的数据复制过来,让网卡发送,这个时钟的时间会在这个timer0之前,这个道理很明显。

    timer2:是一个关于Software beacon alert.这个在adhoc模式下是跟新一个值的作用。

    timer3:这个在beacon发送后做一些动作的时钟。

    上面时钟每次到了后,都会自动根据interval,加上这个值来跟新自己下一次中断的时间

    ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |? AR5K_BEACON_ENABLE),? AR5K_BEACON);这里向beacon控制寄存器AR5K_BEACON里面写值。主要写入interval和beacon使能的值




    void
    ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
    {
            u32 timer1, timer2, timer3;
            /*
             * Set the additional timers by mode
             */
            switch (ah->opmode) {
            case NL80211_IFTYPE_MONITOR:
            case NL80211_IFTYPE_STATION:
                    /* In STA mode timer1 is used as next wakeup
                     * timer and timer2 as next CFP duration start
                     * timer. Both in 1/8TUs. */
                    /* TODO: PCF handling */
                    if (ah->ah_version == AR5K_AR5210) {
                            timer1 = 0xffffffff;
                            timer2 = 0xffffffff;
                    } else {
                            timer1 = 0x0000ffff;
                            timer2 = 0x0007ffff;
                    }
                    /* Mark associated AP as PCF incapable for now */
                    AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF);
                    break;
            case NL80211_IFTYPE_ADHOC:
                    AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM);
            default:
                    /* On non-STA modes timer1 is used as next DMA
                     * beacon alert (DBA) timer and timer2 as next
                     * software beacon alert. Both in 1/8TUs. */
                    timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3;
                    timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3;
                    break;
            }
    
            /* Timer3 marks the end of our ATIM window
             * a zero length window is not allowed because
             * we 'll get no beacons */
            timer3 = next_beacon + 1;
    
            /*
             * Set the beacon register and enable all timers.
             */
            /* When in AP or Mesh Point mode zero timer0 to start TSF */
            if (ah->opmode == NL80211_IFTYPE_AP ||
                ah->opmode == NL80211_IFTYPE_MESH_POINT)
                    ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);
    
            ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
            ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1);
            ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2);
            ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3);
    
            /* Force a TSF reset if requested and enable beacons */
            if (interval & AR5K_BEACON_RESET_TSF)
                    ath5k_hw_reset_tsf(ah);
    
            ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |
                                            AR5K_BEACON_ENABLE),
                                                    AR5K_BEACON);
    
           
            /* Flush any pending BMISS interrupts on ISR by
             * performing a clear-on-write operation on PISR
             * register for the BMISS bit (writing a bit on
             * ISR toggles a reset for that bit and leaves
             * the remaining bits intact) */
            if (ah->ah_version == AR5K_AR5210)
                    ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR);
            else
                    ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR);
    
            /* TODO: Set enhanced sleep registers on AR5212
             * based on vif->bss_conf params, until then
             * disable power save reporting.*/
            AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV);
    
    }
    


    在追踪一个这个函数被那些函数调用,我们可以发现这个在adhoc模式下,只有上层即mac80211层发出重新启动网卡????? 和?????? 每次接受到beacon后在一定条件下才会触发上述函数,才会真正的更改beacon发送的时间和间隔。





    cs