关于ath5k网卡驱动中beacon的发送过程(特别是timestamp字段)


                                            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;
.....................................
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);
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;//这个地方我不太懂,也不知道这个时间拿来干嘛?希望有大神指出
        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发送的时间和间隔。





你可能感兴趣的:(内核,kernel,timestamp,网卡)