工作模式: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;
}
上面就是关于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);
}