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

时间:2021-01-13 12:19:55

转自:http://blog.csdn.net/liujihang88/article/details/39700385


                                            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来通知网卡做相应的改动。

[cpp] view plain copy
  1. struct beacon_data *presp  
[html] view plain copy
  1. struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  
  2. .....................................  
  3. <pre name="code" class="cpp"><pre name="code" class="html">presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);  
  4. if (!presp)  
  5.       return;  
  6. presp->head = (void *)(presp + 1);  
  7. mgmt = (void *) presp->head;  
  8. mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  
  9.                                           IEEE80211_STYPE_PROBE_RESP);  
  10. eth_broadcast_addr(mgmt->da);  
  11. memset(mgmt->da,0x01,ETH_ALEN);  
  12. memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  
  13. memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  
  14. memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);  
  15. mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int);  
  16. mgmt->u.beacon.timestamp = cpu_to_le64(tsf);  
  17. ....................................   
  18. rcu_assign_pointer(ifibss->presp, presp);  
  19. <pre name="code" class="html">sdata->vif.bss_conf.enable_beacon = true;  
  20. sdata->vif.bss_conf.beacon_int = beacon_int;  
  21. sdata->vif.bss_conf.basic_rates = basic_rates;  
  22. sdata->vif.bss_conf.ssid_len = ifibss->ssid_len;  
  23. memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);  
  24. bss_change = BSS_CHANGED_BEACON_INT;  
  25. bss_change |= ieee80211_reset_erp_info(sdata);  
  26. bss_change |= BSS_CHANGED_BSSID;  
  27. bss_change |= BSS_CHANGED_BEACON;  
  28. bss_change |= BSS_CHANGED_BEACON_ENABLED;  
  29. bss_change |= BSS_CHANGED_BASIC_RATES;  
  30. bss_change |= BSS_CHANGED_HT;  
  31. bss_change |= BSS_CHANGED_IBSS;  
  32. bss_change |= BSS_CHANGED_SSID;  
  33. ..........................................  
  34. ieee80211_bss_info_change_notify(sdata, bss_change);  
 

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

[html] view plain copy
  1.         struct ath5k_vif *avf = (void *)vif->drv_priv;  
  2.         struct ath5k_hw *ah = hw->priv;  
  3.         struct ath_common *common = ath5k_hw_common(ah);  
  4.         printk("in ath5k_bss_info_changed\n");  
  5.         mutex_lock(&ah->lock);  
  6.   
  7.         if (changes & BSS_CHANGED_BSSID) {  
  8.                 /* Cache for later use during resets */  
  9.                 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);  
  10.                 common->curaid = 0;  
  11.                 ath5k_hw_set_bssid(ah);  
  12.                 mmiowb();  
  13.         }  
  14.   
  15.         if (changes & BSS_CHANGED_BEACON_INT)  
  16.                 ah->bintval = bss_conf->beacon_int;//这里改变beacon的interval。发送间隔的时间  
  17.   
  18.         if (changes & BSS_CHANGED_ERP_SLOT) {  
  19.                 int slot_time;  
  20.   
  21.                 ah->ah_short_slot = bss_conf->use_short_slot;  
  22.                 slot_time = ath5k_hw_get_default_slottime(ah) +  
  23.                             3 * ah->ah_coverage_class;  
  24.                 ath5k_hw_set_ifs_intervals(ah, slot_time);  
  25.         }  
  26.   
  27.         if (changes & BSS_CHANGED_ASSOC) {  
  28.                 avf->assoc = bss_conf->assoc;  
  29.                 if (bss_conf->assoc)  
  30.                         ah->assoc = bss_conf->assoc;  
  31.                 else  
  32.                         ah->assoc = ath5k_any_vif_assoc(ah);  
  33.   
  34.                 if (ah->opmode == NL80211_IFTYPE_STATION)  
  35.                         ath5k_set_beacon_filter(hw, ah->assoc);  
  36.                 ath5k_hw_set_ledstate(ah, ah->assoc ?  
  37.                         AR5K_LED_ASSOC : AR5K_LED_INIT);  
  38.                 if (bss_conf->assoc) {  
  39.                         ATH5K_DBG(ah, ATH5K_DEBUG_ANY,  
  40.                                   "Bss Info ASSOC %d, bssid: %pM\n",  
  41.                                   bss_conf->aid, common->curbssid);  
  42.                         common->curaid = bss_conf->aid;  
  43.                         ath5k_hw_set_bssid(ah);  
  44.                         /* Once ANI is available you would start it here */  
  45.                 }  
  46.         }  
  47.   
  48.         if (changes & BSS_CHANGED_BEACON) {  
  49.                 spin_lock_bh(&ah->block);  
  50.                 ath5k_beacon_update(hw, vif);//这个函数吧beacon从mac80211层的队列中赋值出来放在ath5k存放beacon的队列中  
  51.                 spin_unlock_bh(&ah->block);  
  52.         }  
  53.   
  54.         if (changes & BSS_CHANGED_BEACON_ENABLED)  
  55.                 ah->enable_beacon = bss_conf->enable_beacon;  
  56.   
  57.         if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |  
  58.                        BSS_CHANGED_BEACON_INT))  
  59.                 ath5k_beacon_config(ah);  
  60.   
  61.         mutex_unlock(&ah->lock);  
这个函数中可以看到当修改了beacon的时候就会调用ath5k_beacon_update这个函数中主要调用了ieee80211_beacon_get()从上述建立的presp中的beacon取出来,然后吧他放在ath5k    avf->bbuf->skb中去 [html] view plain copy
  1. ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)  
  2. {  
  3.         int ret;  
  4.         struct ath5k_hw *ah = hw->priv;  
  5.         struct ath5k_vif *avf;  
  6.         struct sk_buff *skb;  
  7.   
  8.         if (WARN_ON(!vif)) {  
  9.                 ret = -EINVAL;  
  10.                 goto out;  
  11.         }  
  12.   
  13.         skb = ieee80211_beacon_get(hw, vif);  
  14.   
  15.         if (!skb) {  
  16.                 ret = -ENOMEM;  
  17.                 goto out;  
  18.         }  
  19.   
  20.         avf = (void *)vif->drv_priv;  
  21.         ath5k_txbuf_free_skb(ah, avf->bbuf);  
  22.         avf->bbuf->skb = skb;  
  23.         ret = ath5k_beacon_setup(ah, avf->bbuf);  
  24. out:  
  25.         return ret;  
  26. }  


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

[html] view plain copy
  1. ath5k_beacon_config(struct ath5k_hw *ah)  
  2. {  
  3.         spin_lock_bh(&ah->block);  
  4.         ah->bmisscount = 0;  
  5.         ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);  
  6.   
  7.         if (ah->enable_beacon) {  
  8.                 /*   
  9.                  * In IBSS mode we use a self-linked tx descriptor and let the  
  10.                  * hardware send the beacons automatically. We have to load it  
  11.                  * only once here.  
  12.                  * We use the SWBA interrupt only to keep track of the beacon  
  13.                  * timers in order to detect automatic TSF updates.  
  14.                  */  
  15.                 ath5k_beaconq_config(ah);  
  16.       
  17.                 ah->imask |= AR5K_INT_SWBA;  
  18.       
  19.                 if (ah->opmode == NL80211_IFTYPE_ADHOC) {  
  20.                         if (ath5k_hw_hasveol(ah))  
  21.                                 ath5k_beacon_send(ah);  
  22.                 } else  
  23.                         ath5k_beacon_update_timers(ah, -1);  
  24.         } else {  
  25.                 ath5k_hw_stop_beacon_queue(ah, ah->bhalq);  
  26.         }  
  27.   
  28.         ath5k_hw_set_imr(ah, ah->imask);  
  29.         mmiowb();  
  30.         spin_unlock_bh(&ah->block);  
  31. }  
可以看到中间主要调用了ath5k_beaconq_config()这个函数是个关键函数,主要对里面beacon发送的竞争窗口大小进行设定,这里beacon是在0和2*最小竞争窗口之间产生一个随机值,这个值就代表beacon的backoff机制。 这个代码中还有一个tqi.ready_time具体拿来干嘛不是很清楚,希望有大神指出。 [html] view plain copy
  1. ath5k_beaconq_config(struct ath5k_hw *ah)  
  2. {  
  3.         struct ath5k_txq_info qi;  
  4.         int ret;  
  5.   
  6.         ret = ath5k_hw_get_tx_queueprops(ah, ah->bhalq, &qi);  
  7.         if (ret)  
  8.                 goto err;  
  9.   
  10.         if (ah->opmode == NL80211_IFTYPE_AP ||  
  11.             ah->opmode == NL80211_IFTYPE_MESH_POINT) {  
  12.                 /*  
  13.                  * Always burst out beacon and CAB traffic  
  14.                  * (aifs = cwmin = cwmax = 0)  
  15.                  */  
  16.                 qi.tqi_aifs = 0;  
  17.                 qi.tqi_cw_min = 0;  
  18.                 qi.tqi_cw_max = 0;  
  19.         } else if (ah->opmode == NL80211_IFTYPE_ADHOC) {  
  20.                 /*  
  21.                  * Adhoc mode; backoff between 0 and (2 * cw_min).  
  22.                  */  
  23.                 qi.tqi_aifs = 0;  
  24.                 qi.tqi_cw_min = 0;  
  25.                 qi.tqi_cw_max = 2 * AR5K_TUNE_CWMIN;  
  26.         }  
  27.   
  28.         ATH5K_DBG(ah, ATH5K_DEBUG_BEACON,  
  29.                 "beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",  
  30.                 qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);  
  31.   
  32.         ret = ath5k_hw_set_tx_queueprops(ah, ah->bhalq, &qi);  
  33.         if (ret) {  
  34.                 ATH5K_ERR(ah, "%s: unable to update parameters for beacon "  
  35.                         "hardware queue!\n", __func__);  
  36.                 goto err;  
  37.         }  
  38.         ret = ath5k_hw_reset_tx_queue(ah, ah->bhalq); /* push to h/w */  
  39.         if (ret)  
  40.                 goto err;  
  41.   
  42.         /* reconfigure cabq with ready time to 80% of beacon_interval */  
  43.         ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);  
  44.         if (ret)  
  45.                 goto err;  
  46.   
  47.         qi.tqi_ready_time = (ah->bintval * 80) / 100;//<span style="color:#FFCC00;"><span style="background-color: rgb(153, 0, 0);">这个地方我不太懂,也不知道这个时间拿来干嘛?希望有大神指出</span></span>  
  48.         ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);  
  49.         if (ret)  
  50.                 goto err;  
  51.   
  52.         ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);  
  53. err:  
  54.         return ret;  
  55. }  
  56.   
  57.   
  58.    


最后调用的就是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使能的值




[html] view plain copy
  1. void  
  2. ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval)  
  3. {  
  4.         u32 timer1, timer2, timer3;  
  5.         /*  
  6.          * Set the additional timers by mode  
  7.          */  
  8.         switch (ah->opmode) {  
  9.         case NL80211_IFTYPE_MONITOR:  
  10.         case NL80211_IFTYPE_STATION:  
  11.                 /* In STA mode timer1 is used as next wakeup  
  12.                  * timer and timer2 as next CFP duration start  
  13.                  * timer. Both in 1/8TUs. */  
  14.                 /* TODO: PCF handling */  
  15.                 if (ah->ah_version == AR5K_AR5210) {  
  16.                         timer1 = 0xffffffff;  
  17.                         timer2 = 0xffffffff;  
  18.                 } else {  
  19.                         timer1 = 0x0000ffff;  
  20.                         timer2 = 0x0007ffff;  
  21.                 }  
  22.                 /* Mark associated AP as PCF incapable for now */  
  23.                 AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF);  
  24.                 break;  
  25.         case NL80211_IFTYPE_ADHOC:  
  26.                 AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM);  
  27.         default:  
  28.                 /* On non-STA modes timer1 is used as next DMA  
  29.                  * beacon alert (DBA) timer and timer2 as next  
  30.                  * software beacon alert. Both in 1/8TUs. */  
  31.                 timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3;  
  32.                 timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3;  
  33.                 break;  
  34.         }  
  35.   
  36.         /* Timer3 marks the end of our ATIM window  
  37.          * a zero length window is not allowed because  
  38.          * we 'll get no beacons */  
  39.         timer3 = next_beacon + 1;  
  40.   
  41.         /*  
  42.          * Set the beacon register and enable all timers.  
  43.          */  
  44.         /* When in AP or Mesh Point mode zero timer0 to start TSF */  
  45.         if (ah->opmode == NL80211_IFTYPE_AP ||  
  46.             ah->opmode == NL80211_IFTYPE_MESH_POINT)  
  47.                 ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);  
  48.   
  49.         ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);  
  50.         ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1);  
  51.         ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2);  
  52.         ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3);  
  53.   
  54.         /* Force a TSF reset if requested and enable beacons */  
  55.         if (interval & AR5K_BEACON_RESET_TSF)  
  56.                 ath5k_hw_reset_tsf(ah);  
  57.   
  58.         ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |  
  59.                                         AR5K_BEACON_ENABLE),  
  60.                                                 AR5K_BEACON);  
  61.   
  62.          
  63.         /* Flush any pending BMISS interrupts on ISR by  
  64.          * performing a clear-on-write operation on PISR  
  65.          * register for the BMISS bit (writing a bit on  
  66.          * ISR toggles a reset for that bit and leaves  
  67.          * the remaining bits intact) */  
  68.         if (ah->ah_version == AR5K_AR5210)  
  69.                 ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR);  
  70.         else  
  71.                 ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR);  
  72.   
  73.         /* TODO: Set enhanced sleep registers on AR5212  
  74.          * based on vif->bss_conf params, until then  
  75.          * disable power save reporting.*/  
  76.         AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV);  
  77.   
  78. }  
《转》关于ath5k网卡驱动中beacon的发送过程(特别是timestamp字段)


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