转自: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来通知网卡做相应的改动。
- 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()这个函数。
[html] view plain copy- 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);
- 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.
[html] view plain copy- 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(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发送的时间和间隔。