天天看點

linux wifi分析源碼,madwifi無線網卡源代碼閱讀

在我的Doctor課題研究中,基于ARF協定設計了一個改進型的AMARF協定,該文發表在milcom06和電子科學學刊英文版上。最近,我們将PC機上,使用linux作業系統,基于madwifi開源代碼實作了AMARF協定,已經在室内固定以及移動環境測試表明,AMARF協定明顯優越于現有的協定。

2008-9-20

現在使用了madwifi程式實作AMARF協定,下面分析其代碼。

使用iwconfig指令可以設定速率:

1、iwconfig源代碼閱讀

首先下載下傳iwconfig.c代碼,源代碼包為\wireless_tools.29目錄

先看執行

iwconfig eth0

的指令的執行過程:

調用main函數,因為是兩個參數:

if(argc == 2)

print_info(skfd, argv[1], NULL, 0);

print_info調用

get_info(intskfd,

char *ifname,

struct wireless_info *info)

将網卡的各種資訊列印出來。

下面與列印發送速率為例,說明調用過程,get_info函數裡面:

if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)

{

info->has_bitrate = 1;

memcpy(&(info->bitrate), &(wrq.u.bitrate), sizeof(iwparam));

}

對資訊的擷取都是通過iw_get_ext函數來實作的,通過參數SIOCGIWRATE來識别不同的内容

iwlib.h檔案定義了iw_get_ext:

iw_get_ext(intskfd,

const char *ifname,

intrequest,

struct iwreq *pwrq)

{

strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);

return(ioctl(skfd, request, pwrq));

}

是以,真正對網卡其他參數的實作是通過ioctl函數實作的,ioctl是驅動程式的基本功能,是以,如果自己想編寫一個對網卡參數設定的程式,也應該使用ioctl函數。

下面在看使用

iwconfig eth0 rate auto

指令執行情況

main函數,當輸入參數大于2的時候,調用:

goterr = set_info(skfd, argv + 2, argc - 2, argv[1]);

set_info函數調用iwcmd = find_command(args[0]);

用來查找指令,所有的指令都存放在一個表中,隻要查找這個表即可:

static const struct iwconfig_entry iwconfig_cmds[] = {

……….

{ "bit",set_bitrate_info,1,SIOCSIWRATE,

"Set Bit Rate","{N[k|M|G]|auto|fixed}" },

{ "rate",set_bitrate_info,1,SIOCSIWRATE,

"Set Bit Rate","{N[k|M|G]|auto|fixed}" },

當第三個參數為rate的時候,就會自動調用set_bitrate_info函數,該函數的定義為:

set_bitrate_info

if(!strcasecmp(args[0], "auto"))

{

wrq.u.bitrate.value = -1;

wrq.u.bitrate.fixed = 0; //如果輸入是auto,那麼設定為bitrate.value = -1;

}

else

{

if(!strcasecmp(args[0], "fixed"))

{

if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) < 0)

return(IWERR_GET_EXT);

wrq.u.bitrate.fixed = 1;

}

……………

if(iw_set_ext(skfd, ifname, SIOCSIWRATE, &wrq) < 0)

return(IWERR_SET_EXT);

也就是說,如果選擇auto,那麼設定變量bitrate.fixed=0,最後調用iw_set_ext(skfd, ifname, SIOCSIWRATE, &wrq)函數,這個函數也是直接與網卡相關的。

與iw_get_ext函數一樣,iw_set_ext也是通過SIOCGIWRATE來識别的:

static inline int

iw_set_ext(intskfd,

const char *ifname,

intrequest,

struct iwreq *pwrq)

{

strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);

return(ioctl(skfd, request, pwrq));

}

2、madiwifi驅動和硬體說明

該說明來源于“IEEE 802.11 Rate Adaptation: A Practical Approach”

該文主要提出了AMRR算法。

本文首先談到是否能夠基于包級的速率控制需要看硬體是否支援,有些晶片在硬體裡面包含了CPU,

Texas Instruments around an ARM core,WaveLAN 802.11b,是以具有低的延時。

然而,Atheros 802.11 chipsets晶片不包含CPU,需要主機CPU來完成很多的MAC功能,而主機CPU很難實作實時的控制,基于包的速率控制是不可行的,認為是高延時的一類。

ARF算法和AARF算法、以及AMARF都是基于低延時的控制算法,

Atheros的linux驅動,稱為Multiband Atheros Driver for WiFi (MadWiFi),可以在SourceForge上找到源代碼,使用了HAL(硬體抽象層的概念),與硬體有關的部分是二進制的代碼,不提供源代碼。

Atheros硬體允許使用者建立9個發送FIFIO描述符,對發送進行排程;每個發送描述符包含了發送狀況,資料的指針和長度,并且包含了一個4對的“速率/重傳次數”對(r0/c0, r1/ c1,r2/c2,r3/c3)。

當無線信道可以發送的時候,硬體将引發處于FIFO頭的資料的發送,首先以速率r0發送,如果發送失敗,繼續以速率r0發送c0-1次,然而再次以速率r1發送,如果發送失敗,繼續以速率r1發送c1-1次……如果發送失敗了c0+c1+c2+c3次,那麼放棄這次發送。

當發送完畢,或者發送放棄,硬體會在發送描述符中報告本次發送丢失的ACK的數目,是以,通過獲知丢失的ACK的數目可以間接的得到本次的發送速率。

MadWiFi的這個機制稱為Multi Rate Retry mechanism。

3、onoe自适應速率說明

該說明來源于“Bit-rate Selection in Wireless Networks“,John C. Bicket,MIT碩士論文P29

該文主要提出了SampleRate算法,也對其他的算法進行了說明。

onoe對每個包的失敗與否并不敏感,試圖找到丢幀率小于50%的最高速率;

對于每個目的連接配接,onoe儲存了一個目前鍊路的比特速率、以及該比特率的信用度;算法跟蹤每個鍊路目前比特速率的信用度,如果丢包率較小,那麼增加該信用度;如果該信用度達到一定的門檻值,那麼嘗試增加信用度;如果發生了一些錯誤,那麼該信用度複位,速率下降。

當第一次發送到目的位址的時候,将初始的速率設定為24M(802.11a/g),11M(802.11b);onoe周期性的執行算法(預設值為1秒)

1)如果沒有資料發送成功,降低到下一個速率;

2)如果發送了10個以上的包,并且每個包的平均重傳次數大于1,降低到下一個速率;

3)如果超過10%的包需要重傳,将信用值減小(保持大于0),

4)如果少于10%的包需要重傳,增加信用值;

次數5)如果信用值大于10,增加速率;

相關代碼為:

//每次發送完資料後,都會調用該函數,來判斷發送的結果,成功還是失敗,以及重傳的次數

void

ath_rate_tx_complete(struct ath_softc *sc,

struct ath_node *an, const struct ath_desc *ds)

{

struct onoe_node *on = ATH_NODE_ONOE(an);

if (ds->ds_txstat.ts_status == 0) //如果ts_status為0,表明發送成功

on->on_tx_ok++; //統計發送成功和發送失敗的次數,

else

on->on_tx_err++;

on->on_tx_retr += ds->ds_txstat.ts_shortretry //統計重傳的次數,長重傳加上短重傳,适用于不同的長度,一般來說應該是一個為0,而另外一個不為0,

+ ds->ds_txstat.ts_longretry;

if (jiffies >= on->on_nextcheck) {//判斷現在的時間,每大約1秒鐘執行一次速率控制

ath_rate_ctl(sc, &an->an_node);

on->on_nextcheck = jiffies + (ath_rateinterval * HZ) / 1000; //ath_rateinterval定義為1000,

}

}

//這個是速率控制函數

static void

ath_rate_ctl(void *arg, struct ieee80211_node *ni)

{

struct ath_softc *sc = arg;

struct onoe_node *on = ATH_NODE_ONOE(ATH_NODE(ni));

struct ieee80211_rateset *rs = &ni->ni_rates;

int dir = 0, nrate, enough;

sc->sc_stats.ast_rate_calls++;

enough = (on->on_tx_ok + on->on_tx_err >= 10); //這兩參數是從ath_rate_tx_complete中獲得的,onoe算法要統計10次以上的發送結果才又有動作

if (on->on_tx_err > 0 && on->on_tx_ok == 0)//如果壓根就沒有任何一次發送成功,速率控制的方向是遞減,也就是算法中的第一中情況

dir = -1;

if (enough && on->on_tx_ok < on->on_tx_retr) //就是算法中的第二種情況,基本上所有的包都需要重傳,降低速率

dir = -1;

//ath_rate_raise初始值定義為10,判斷用于10%的一個變量

//如果沒有發生傳輸失敗,并且少于10%的包需要重傳,增加速率,也就是第四鐘情況

if (enough && on->on_tx_err == 0 &&

on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)

dir = 1;

DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n",

ether_sprintf(ni->ni_macaddr),

on->on_tx_ok, on->on_tx_err, on->on_tx_retr,

on->on_tx_upper, dir);

nrate = ni->ni_txrate;//目前使用的速率,實際上是一個大于等于0的編号

switch (dir) {

case 0://不增加也不減少

if (enough && on->on_tx_upper > 0)

on->on_tx_upper--;

// on_tx_upper定義為信用值

//如果發送超過了10次,并且超過10%的包需要重傳,信用值on_tx_upper減小

break;

case -1: //減少速率,

if (nrate > 0) {

nrate--; //如果大于0,減小速率

sc->sc_stats.ast_rate_drop++;

}

on->on_tx_upper = 0; //将信用值複位置0

break;

case 1:

if (++on->on_tx_upper < ath_rate_raise_threshold)

break; //信用值加1,如果信用值沒有達到門檻值,那麼直接退出,

on->on_tx_upper = 0; //否則,增加速率,将信用值複位置0

if (nrate + 1 < rs->rs_nrates) {

nrate++;

sc->sc_stats.ast_rate_raise++;

}

break;

}

if (nrate != ni->ni_txrate) {//如果速率發生了改變,更新速率

DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",

__func__,

(rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,

(rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,

on->on_tx_ok, on->on_tx_err, on->on_tx_retr);

ath_rate_update(sc, ni, nrate);

} else if (enough) //如果速率沒有發生改變,發送次數已經大約10次了,重新複位

on->on_tx_ok = on->on_tx_err = on->on_tx_retr = 0; //複位

}

//速率更新的代碼

static void

ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)

{

// ath_rate_ctl函數僅僅是确定了合适使用的速率,然而,atheor網卡需要在發送描述符中寫入r0/c0,r1/c1, r2/c2, r3/c3四個速率和retry對,如果填充這四個發送對呢?

r0速率,就是判斷的最佳發送速率,r1就是最佳速率的下一個速率(如果r0已經是最低速率,那麼r1為0);

r2是r1的下一個速率,r3是r2的下一個速率,對于retry次數,分别設定為4,2,2,2

on->on_tx_rix0 = sc->sc_rixmap[ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];

on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode;

on->on_tx_try0 = 1 + 3;//速率r0的設定,就是最佳速率rate

if (--rate >= 0) { //速率r1的設定,就是最佳速率rate的下一個速率

rix = sc->sc_rixmap[ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];

on->on_tx_rate1 = rt->info[rix].rateCode;

on->on_tx_rate1sp = on->on_tx_rate1 |

rt->info[rix].shortPreamble;

} else

on->on_tx_rate1 = on->on_tx_rate1sp = 0;

……….

速率r1/c1, r2/c2,r3/c3是通過ath_rate_setupxtxdesc函數寫入硬體的,這是與硬體相關的函數,

ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,

struct ath_desc *ds, int shortPreamble, size_t frame_size, u_int8_t rix)

{

struct onoe_node *on = ATH_NODE_ONOE(an);

ath_hal_setupxtxdesc(sc->sc_ah, ds

, on->on_tx_rate1sp, 2

, on->on_tx_rate2sp, 2

, on->on_tx_rate3sp, 2

);

}

速率r0/c0是通過ath_hal_setuptxdesc函數寫入硬體的,

ath_hal_setuptxdesc(ah, ds

, pktlen

, hdrlen

, atype

, MIN(ni->ni_txpower, 60)

, txrate, try0

, keyix

該函數在if_ath.c中每次ath_tx_start函數進行發送的時候調用

通過上面的分析,我們認為實際上使用的發送速率就是r0,那麼如何擷取呢?

速率控制的驅動中有一個函數:

void

ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,

int shortPreamble, size_t frameLen,

u_int8_t *rix, int *try0, u_int8_t *txrate)

{

struct onoe_node *on = ATH_NODE_ONOE(an);

*rix = on->on_tx_rix0;

*try0 = on->on_tx_try0;

if (shortPreamble)

*txrate = on->on_tx_rate0sp;

else

*txrate = on->on_tx_rate0;

}

傳回的txrate就是實際上使用的on->on_tx_rate0,r0

該函數也在if_ath.c中每次ath_tx_start函數進行發送的時候調用,這個函數雖然參數很多,但是檢視代碼,隻有struct ath_node *an的輸入代碼是有效的,shortPreamble是否為短符号,可以不考慮,設定為0.

是以,需要修改ioctl部分的函數,将auto部分傳回-1的代碼,修改為調用ath_rate_findrat函數,調用該函數的關鍵就是如何擷取struct ath_node *an參數。

4、AMRR代碼閱讀

AMRR是onoe的改進,使用了二進制避退(BEB)的概念,來适應改變rn/cn的周期,為了适應快速變化的無線信道,設定c0=c1=c2=c3=1,速率r3設定為最小可用速率(6Mbps 802.11a),而r1和r2由r0決定,也就是r1為r0的下一個低速率,r2為r1的下一個低速率;是以,核心是改變r0。

AMRR使用了一個定時器來周期性的進行速率控制,時間設定為1秒。

static void

ath_ratectl(unsigned long data)

{

……..

asc->timer.expires = jiffies + ((HZ * interval) / 1000);

add_timer(&asc->timer);

}

首先看ath_rate_tx_complete函數,每次發送後是如何處理的,

void

ath_rate_tx_complete(struct ath_softc *sc,

struct ath_node *an, const struct ath_desc *ds)

{

struct amrr_node *amn = ATH_NODE_AMRR(an);

int sr = ds->ds_txstat.ts_shortretry;

int lr = ds->ds_txstat.ts_longretry;

int retry_count = sr + lr;

//這個變量表明重傳的次數,需要注意c0=c1=c2=c3=1,是以,重傳次數最多為4次,

amn->amn_tx_try0_cnt++;

//amn->amn_tx_try0_cnt變量為使用速率r0發送的次數,因為c0=1,每次發送,都會并且隻會使//用r0發送一次,是以,這個變量還可以同時表示發送的包的個數(包括成功和失敗的)

if (retry_count == 1) {

//如果重傳次數是一次,由于c0=c1=c2=c3=1,必然可以得出使用速率r0發送了一次,失敗之後,又使用速率r1發送了一次

amn->amn_tx_try1_cnt++;

} else if (retry_count == 2) {

//同理可以得出如果重傳次數是2次,由于c0=c1=c2=c3=1,必然可以得出使用速率r0發送了一次,失敗之後,又使用速率r1發送了一次,再使用r2發送了一次

amn->amn_tx_try1_cnt++;

amn->amn_tx_try2_cnt++;

} else if (retry_count == 3) {

amn->amn_tx_try1_cnt++;

amn->amn_tx_try2_cnt++;

amn->amn_tx_try3_cnt++;

} else if (retry_count > 3) {

//如果次數為4次,說明各個速率都嘗試過,并且最後都發送都失敗了。

amn->amn_tx_try1_cnt++;

amn->amn_tx_try2_cnt++;

amn->amn_tx_try3_cnt++;

amn->amn_tx_failure_cnt++;

}

}

static void

ath_rate_ctl(void *arg, struct ieee80211_node *ni)

{

struct ath_softc *sc = arg;

struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni));

int old_rate;

#define is_success(amn) (amn->amn_tx_try1_cnt< (amn->amn_tx_try0_cnt / 10))

如果發送包的中小于10%的包出現了重傳,那麼認為上次速率設定發送是成功的,

#define is_enough(amn)(amn->amn_tx_try0_cnt > 10)

如果發送包的個數大于10,認為是足夠進行判斷了

#define is_failure(amn) (amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt / 3))

如果發送包中出現大約33%的包需要重傳,那麼認為上次速率設定是失敗的

#define is_max_rate(ni) ((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)

#define is_min_rate(ni) (ni->ni_txrate == 0)

old_rate = ni->ni_txrate;

if (is_success(amn) && is_enough(amn)) {

//如果發送成功,将成功的計數器加1

amn->amn_success++;

if (amn->amn_success == amn->amn_success_threshold &&

!is_max_rate(ni)) {

如果計數器達到門檻值,那麼将速率增加,并且設定一個變量,amn_recovery=1,表明處于嘗試速率增加階段

amn->amn_recovery = 1;

amn->amn_success = 0;

ni->ni_txrate++;

DPRINTF(sc, "increase rate to %d\n", ni->ni_txrate);

} else

amn->amn_recovery = 0;

} else if (is_failure(amn)) {

如果發送失敗,成功的計數器清零。

amn->amn_success = 0;

if (!is_min_rate(ni)) {

if (amn->amn_recovery) {

如果處于是處于嘗試速率增加階段,那麼将門檻值翻倍,

amn->amn_success_threshold *= 2;

amn->amn_success_threshold = min(amn->amn_success_threshold,

(u_int)ath_rate_max_success_threshold);

DPRINTF(sc, "decrease rate recovery thr: %d\n",

amn->amn_success_threshold);

} else {

否則,如果處于正常情況下的失敗,将門檻值設定為最小

amn->amn_success_threshold = ath_rate_min_success_threshold;

DPRINTF(sc, "decrease rate normal thr: %d\n",

amn->amn_success_threshold);

}

amn->amn_recovery = 0;

ni->ni_txrate--;

} else

amn->amn_recovery = 0;

}

}

可以看出來,AMRR協定類似于AARF,每次嘗試速率增加的時候,如果嘗試失敗,将門檻值翻倍。

5、SampleRate自适應速率說明

該說明來源于“Bit-rate Selection in Wireless Networks“,John C. Bicket,MIT碩士論文P29

該文主要提出了SampleRate算法,也對其他的算法進行了說明。

該算法周期性的發送其他速率的包進行探詢,看其他速率發送是否合适。

本文認為速率選擇算法需要考慮一下幾個方面:

1)不能認為因為如果某低速率性能性能差,那麼比它高一級的速率會性能更差(3.5章節的說明)

2)最高吞吐量的算法可能丢包率也多,丢包率小的速率可能吞吐量未必最高(3.2節)

3)鍊路狀态是會發生變化的,不能對鍊路狀态進行反映,會導緻低的吞吐量;

4)速率選擇算法必須要高效,不能嘗試所有的速率;

6、AMARF協定的實作

AMARF協定是ARF協定的改進,核心是給每個速率設定一個不同的門檻值,門檻值的大小可以自适應的變化,是以,AMARF适用于信道快速變化的場合。

使用madwifi可能存在的問題:

1)madwifi屬于高延時的系統,是否能夠進行每個包的控制,另外如何控制FIFIO?

2)madwifi驅動本身提供了多速率多重傳的機制,如何利用?

是以,我們提出兩種方法:

純粹的P-AMARF,結合madwifi的M-AMARF

首先分析P-AMARF:

還是從ath_rate_tx_complete函數看其,每次發送後是如何處理的。

上面的幾個算法都是周期性的對速率進行控制,引入了定時器,每次發送之後僅僅是對速率進行統計,我們的算法需要進行快速的調整速率,每次發送完之後都進行控制!!

void ath_rate_tx_complete(struct ath_softc *sc,struct ath_node *an, const struct ath_desc *ds)

{

struct amarf_node *arn = ATH_NODE_AMARF(an);

arn->amarf_txok=!(ds->ds_txstat.ts_status); //判斷是否發送成功

//printk("txok is %d\n",arn->amarf_txok);

ath_rate_ctl(sc,&an->an_node);

}

static void ath_rate_ctl(void *arg,struct ieee80211_node *ni)

{

struct ath_softc *sc=arg;

struct amarf_node *arn=ATH_NODE_AMARF(ATH_NODE(ni));

int old_rate;

sc->sc_stats.ast_rate_calls++;

#define is_max_rate(ni)((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)

#define is_min_rate(ni)(ni->ni_txrate == 0)

old_rate = ni->ni_txrate;

if(arn->amarf_txok)

{

if(arn->amarf_counter_success>=amarf_th[ni->ni_txrate])

{ arn->amarf_counter_success=0;

amarf_th[ni->ni_txrate]+=amarf_c;

DEBUG2("amarf_c is %f\n",amarf_c);

if(!is_max_rate(ni))

{ ni->ni_txrate++;

DEBUG2("increase to %d\n",ni->ni_txrate);

arn->amarf_isprobe=1;

}

}

else

arn->amarf_counter_success++;

arn->amarf_isprobe=0;

//DEBUG2("consecutive counter_success is %d\n",arn->amarf_counter_success);

}

else

{

arn->amarf_counter_success=0;

if(arn->amarf_isprobe==1)

amarf_th[ni->ni_txrate]-=amarf_a;

DEBUG2("amarf_a is %f\n",amarf_a);

else

amarf_th[ni->ni_txrate]-=amarf_b;

DEBUG2("amarf_b is %f\n",amarf_b);

if(!is_min_rate(ni))

{

ni->ni_txrate--;

DEBUG2("decrease to %d\n",ni->ni_txrate);

}

arn->amarf_isprobe=0;

}

if(ni->ni_txrate!=old_rate)

ath_rate_update(sc,ni,ni->ni_txrate);

}

7、madwifi驅動閱讀

該程式有好幾個目錄

ath:實體網卡的控制函數,例如發送等

ath_rate:速率控制的相關代碼

hal:硬體抽象層的相關庫,不公開源代碼

net80211: 802.11相關的代碼,與實體層獨立,完成諸如scan、wap、VAP等功能。

Ieee80211_wireless.c

static const iw_handler ieee80211_handlers[] = {

(iw_handler) ieee80211_ioctl_siwrate,

(iw_handler) ieee80211_ioctl_giwrate,

子產品的入口函數:if_ath_pci.c /module_init(init_ath_pci);

static int __init

init_ath_pci(void)

{

printk(KERN_INFO "%s: %s\n", dev_info, version);

if (pci_register_driver(&ath_pci_drv_id) < 0) {

printk("ath_pci: No devices found, driver not installed.\n");

return (-ENODEV);

}

#ifdef CONFIG_SYSCTL

ath_sysctl_register();

#endif

return (0);

}

module_init(init_ath_pci);

初始化會調用ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)函數,

進一步加載ath無線網卡:

if (ath_attach(vdevice, dev) != 0)

goto bad4;

If_ath.c (ath)檔案實作ath_attach函數,該函數的實作非常複雜,關鍵的代碼如下:

ath_attach(u_int16_t devid, struct net_device *dev)

{……………

ah = _ath_hal_attach(devid, sc, NULL, (void *) dev->mem_start, &status);

可能是探測與硬體相關的東西,不能顯示更進一步的代碼:

………………..

dev->hard_start_xmit = ath_hardstart;

dev->do_ioctl = ath_ioctl;

ieee80211_ifattach(ic); 該函數在Ieee80211.c檔案中實作

ic->ic_vap_create = ath_vap_create;

ic->ic_vap_delete = ath_vap_delete;

重點需要掌握ath與80211目錄的關聯,ath_vap_create是做什麼的。

static struct ieee80211vap *

ath_vap_create(struct ieee80211com *ic, const char *name, int unit,

int opmode, int flags, struct net_device *mdev)

{

……………………

ieee80211_vap_setup(ic, dev, name, unit, opmode, flags);

………….

(void) ieee80211_vap_attach(vap,

ieee80211_media_change, ieee80211_media_status);

Ieee80211_wireless.c

ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,

const char *name, int unit, int opmode, int flags)->

調用下面的函數,實作ioctl函數,

ieee80211_ioctl_vattach(struct ieee80211vap *vap)

{

struct net_device *dev = vap->iv_dev;

dev->do_ioctl = ieee80211_ioctl;

這裡就存在兩次對dev->do_ioctl指派的情況,一次是在ath_attach函數中,一次在ieee80211_ioctl_vattach函數中,懷疑第二次是不是就将第一次的覆寫了。

ieee80211_ioctl函數就有很多的子函數用以實作不同的功能,以設定和擷取速率為例:

ieee80211_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info,

struct iw_param *rrq, char *extra)

擷取速率的ioctl函數的實作

ieee80211_ioctl_giwrate(struct net_device *dev,struct iw_request_info *info,

struct iw_param *rrq, char *extra)

{

struct ieee80211vap *vap = dev->priv;

struct ifmediareq imr;

int rate;

memset(&imr, 0, sizeof(imr));

vap->iv_media.ifm_status(vap->iv_dev, &imr);

rrq->fixed = IFM_SUBTYPE(vap->iv_media.ifm_media) != IFM_AUTO;

rate = ieee80211_media2rate(imr.ifm_active);//這個函數的意思是将速率編号轉換為實際的速率

if (rate == -1)

rate = 0;//如果是auto,那麼就傳回0

rrq->value = 1000000 * (rate / 2);

return 0;

}

有關于速率的東西都在ifm_active變量中,這個變量包含了很多的屬性,包括速率、模式等等,在if_media.h檔案中

#defineIFM_IEEE80211_DS15

#defineIFM_IEEE80211_DS26

#defineIFM_IEEE80211_DS57

#defineIFM_IEEE80211_DS118

函數ieee80211com_media_status實作獲得ifm_active變量

ieee80211com_media_status(struct net_device *dev, struct ifmediareq *imr)

{

struct ieee80211com *ic = dev->priv;

imr->ifm_status = IFM_AVALID;

if (!TAILQ_EMPTY(&ic->ic_vaps))

imr->ifm_status |= IFM_ACTIVE;

imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);

}

通過上述分析,發現ifm_active變量的獲得實際上是ieee80211com *ic結構體獲得的。

資料發送過程分析:

static int

ath_hardstart(struct sk_buff *skb, struct net_device *dev)->

ath_tx_start(struct net_device *dev, struct ieee80211_node *ni, struct ath_buf *bf, struct sk_buff *skb, int nextfraglen)

{

……….

ath_rate_findrate(sc, an, shortPreamble, skb->len,

&rix, &try0, &txrate);//有關速率控制的東西

………

ath_hal_setuptxdesc(ah, ds

, pktlen

, hdrlen

, atype

, MIN(ni->ni_txpower, 60)

, txrate, try0

, keyix

, antenna

, flags

, ctsrate

, ctsduration

, icvlen

, ivlen

, comp

);//與硬體相關的函數

if (try0 != ATH_TXMAXTRY)

ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, skb->len, rix);//速率控制的描述符

}

8、有關速率擷取的問題

通過上面的代碼分析,我們認為在madwifi情況下,r0就是實際的速率,是以,需要在應用層能夠獲得r0的指,經過跟蹤,發現當調用iwconfig指令,來擷取速率的值的時候,會調用

ieee80211_ioctl_giwrate(struct net_device *dev,struct iw_request_info *info,

struct iw_param *rrq, char *extra)

{

struct ieee80211vap *vap = dev->priv;

現在當使用auto的自适應速率時候,自動傳回-1,是以,獲得速率值為0.

是以,需要修改這個函數,調用ARMARF自适應速率的ath_rate_findrate函數

通過上面的分析,我們認為實際上使用的發送速率就是r0,那麼如何擷取呢?

速率控制的驅動中有一個函數:

void

ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,

int shortPreamble, size_t frameLen,

u_int8_t *rix, int *try0, u_int8_t *txrate)

{

struct onoe_node *on = ATH_NODE_ONOE(an);

*rix = on->on_tx_rix0;

*try0 = on->on_tx_try0;

if (shortPreamble)

*txrate = on->on_tx_rate0sp;

else

*txrate = on->on_tx_rate0;

}

傳回的txrate就是實際上使用的on->on_tx_rate0,r0

該函數為實際上隻有struct ath_node *an參數是需要傳遞的,

ieee80211_ioctl_giwrate輸入的參數為net_device *dev,是以,需要将該輸入參數轉換得到struct ath_node *an參數:

是以,我們需要關注三個結構體:

struct net_device *dev結構體包含了ieee80211vap *vap結構體:

ieee80211vap *vap = dev->priv;

an = ATH_NODE(ni);

struct ath_softc {

struct ieee80211com sc_ic;

struct net_device *sc_dev;

struct semaphore sc_lock;

struct ath_node {

struct ieee80211_node an_node;

u_int16_t an_decomp_index;

u_int32_t an_avgrssi;

u_int8_tan_prevdatarix;

u_int16_t an_minffrate;

HAL_NODE_STATS an_halstats;

struct ath_buf *an_tx_ffbuf[WME_NUM_AC];

ath_bufhead an_uapsd_q;

int an_uapsd_qdepth;

ath_bufhead an_uapsd_overflowq;

int an_uapsd_overflowqdepth;

spinlock_t an_uapsd_lock;

};

struct ieee80211_node {

struct ieee80211vap *ni_vap;

struct ieee80211com *ni_ic;

struct ieee80211_node_table *ni_table;

…………..

struct ieee80211vap {

struct net_device *iv_dev;

struct net_device_statsiv_devstats;

struct ifmedia iv_media;

#ifdef CONFIG_NET_WIRELESS

struct iw_statistics iv_iwstats;

#endif

…………

struct ieee80211com *iv_ic;

……………

下面三個結構體的包含關系為:

ath_node-> ieee80211_node-> ieee80211vap

并且由于首位址是一樣的,是以,隻要獲得任意一個結構體,可以使用指針強制轉換為另外兩個結構體,例如已知ieee80211_node結構體,可以獲得ath_node,在if_ath.c的ath_tx_start函數中,使用

an = ATH_NODE(ni)指令獲得,

#defineATH_NODE(_n)((struct ath_node *)(_n))

struct ath_softc *sc = dev->priv;

struct ieee80211com *an = dev->priv;

ath_rate_findrate(sc, an, shortPreamble, skb->len, &rix, &try0, &txrate);

調試中發現,始終不能正确的傳遞參數,經過調試,發現ieee80211.c和if_ath.c代碼的struct net_device變量是不一樣的,兩者的參數名字分别為ath0和wifi0,也就是兩個網絡裝置的名字,是以,沒有辦法傳遞參數。

下面還是分析iwconfig中的參數是如何傳遞的,以ieee80211_ioctl_siwpower函數為例,設定發送功率。

ieee80211_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info,

struct iw_param *wrq, char *extra)

{

struct ieee80211vap *vap = dev->priv;

struct ieee80211com *ic = vap->iv_ic;

……………..

return IS_UP(ic->ic_dev) ? ic->ic_reset(ic->ic_dev) : 0;如果裝置已經UP,那麼以新的參數對裝置設定。

這裡面調用了一個函數指針,ic->ic_reset,經過檢查,隻有if_ath.c檔案調用了,

if_ath.c(692):ic->ic_reset = ath_reset;

ath_attach(u_int16_t devid, struct net_device *dev)

{

struct ath_softc *sc = dev->priv;

struct ieee80211com *ic = &sc->sc_ic;

struct ath_hal *ah;

………….

ic->ic_reset = ath_reset;

ath_reset(struct net_device *dev)

{

struct ath_softc *sc = dev->priv;

struct ieee80211com *ic = &sc->sc_ic;

struct ath_hal *ah = sc->sc_ah;

struct ieee80211_channel *c;

HAL_STATUS status;

iwconfig指令會最終調用ieee80211_wireless.c中的各種參數設定和控制指令,檢視代碼,發現ieee80211_wireless中本身定義了很多的參數變量,如果需要設定,再調用底層的函數将參數寫入網卡晶片,如果讀參數,就直接讀自己已經儲存好的參數,而并不是讀真正的實體層的參數!

ieee80211_ioctl_siwpower代碼最終會調用ath_reset,這兩個函數的輸入參數都是struct net_device *dev,他是如何進行參數轉換的呢?将ath0變為wifi0?

Iwconfig中,首先由輸入參數ath0(struct net_device *dev)獲得vap

struct ieee80211vap *vap = dev->priv;

該結構體定義為:

struct ieee80211vap {

struct net_device *iv_dev;

struct ieee80211com *iv_ic;

……………

然後由vap獲得ic,

struct ieee80211com *ic = vap->iv_ic;

而該結構體定義為:

struct ieee80211com {

struct net_device *ic_dev;

又關聯了一個net_device裝置,這個裝置正是wifi0,是以,調用ic_reset的時候,并不是直接将

ieee80211_ioctl_siwpower的輸出參數dev放進去,而是轉了兩個彎,

ic->ic_reset(ic->ic_dev)。

再次回到擷取速率的函數,調用下面的函數獲得速率,

ath_rate_findrate(sc, an, shortPreamble, skb->len, &rix, &try0, &txrate);

這裡,sc以前直接定義為:ieee80211_ioctl_siwpower輸入參數的dev,

struct ath_softc *sc = dev->priv;

需要将其定義為wifi0,是以,需要定義為:

struct ieee80211vap *vap = dev->priv; 由ath0 device獲得vap

struct ieee80211com *ic = vap->iv_ic;

struct net_device *wifi= ic->ic_dev;再次獲得wifi device

struct ath_softc *sc = wifi ->priv; 獲得sc

如果調用findrate函數,第二個參數ath_node需要傳遞過來,發現這比較困難,是以,暫時不使用這個方法,而是自己在ieee80211com結構體中定義一個變量,利用該變量傳遞參數。

ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,

int shortPreamble, size_t frameLen,

u_int8_t *rix, int *try0, u_int8_t *txrate)

9、madwifi程式閱讀(二)

前面的閱讀已經發現,madwifi驅動裡面有兩套net_device裝置,一套是if_ath­_xx.c程式使用的,名字為“wifi0”,另一套是ieee802.11_xx程式使用的,為ath0。

ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)

\if_ath_pci.c(179):dev = alloc_netdev(sizeof(struct ath_pci_softc), "wifi%d", ether_setup);

int ath_attach(u_int16_t devid, struct net_device *dev)

\if_ath.c(897): error = ieee80211_create_vap(ic, "ath%d", dev,

autocreatemode, IEEE80211_CLONE_BSSID);

相應的,兩個裝置都定義了自己的發送函數

這是ath0的發送函數,

int ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,

const char *name, int unit, int opmode, int flags)

\ieee80211.c(395):dev->hard_start_xmit = ieee80211_hardstart;

這是wifi的發送函數,

int ath_attach(u_int16_t devid, struct net_device *dev)

if_ath.c(670):dev->hard_start_xmit = ath_hardstart;

ieee80211_hardstart函數調用ieee80211_parent_queue_xmit,ieee80211_parent_queue_xmit繼續調用(void) dev_queue_xmit(skb),dev_queue_xmit函數是在linux核心中定義的

\net\core\dev.c(994):int dev_queue_xmit(struct sk_buff *skb),該函數會調用dev->hard_start_xmit,這個函數估計就是ath_hardstart。

ath_hardstart調用ath_tx_start函數,ath_tx_start繼續調用ath_hal_filltxdesc等與硬體相關的函數,完成最終的發送任務。

這樣就出來一個問題了,由誰來調用ieee80211_hardstart?

經過查找,兩個hard_start_xmit函數都沒有在madfiwi程式中調用,看來都是在linux核心中調用的。

也就說,madwifi同時注冊了兩個網絡裝置,一個是ath0,一個是wifi0,應用程式發送資料的流程是這樣的:應用程式->核心->ath0裝置->核心->wifi0裝置。

要搞清楚為什麼會這樣,就必須了解madwifi中的VAP(虛拟AP的概念),可以在一個實際的無線裝置上建立多個邏輯裝置,要使用裝置,必須使用下面的指令建立ath裝置

wlanconfig athX create wlandev wifiX wlanmode

例如:

wlanconfig ath0 create wlandev wifi0 wlanmode ap

wlanconfig ath1 create wlandev wifi0 wlanmode ap

wlanconfig ath2 create wlandev wifi0 wlanmode ap

iwconfig ath0 essid "lmn"

iwconfig ath1 essid "XYZ"

iwconfig ath2 essid "abc"

是以,可以認為ath是邏輯裝置,wifi是實體裝置。

(上面部分參考了madwifi.pdf文檔,一個ppt,上面對madwifi進行了粗略的介紹,包括存在的問題)

The OpenHAL ported to MADWiFi at

是以,我們在發送資料的時候,已經指明了使用ath0虛拟網卡,自然就會調用ieee80211_hardstart,問題就變成了兩次調用核心操作是如何實作的?

還是先看看幾個結構體是如何關聯的,

ath_attach(u_int16_t devid, struct net_device *dev)

{

struct ath_softc *sc = dev->priv;由wifi裝置獲得sc

struct ieee80211com *ic = &sc->sc_ic; 由wifi裝置獲得com裝置ic,

……….

ic->ic_dev = dev;ic的裝置指向wifi

ic->ic_mgtstart = ath_mgtstart;

ic->ic_init = ath_init;

ic->ic_reset = ath_reset;

ic->ic_newassoc = ath_newassoc;

ic->ic_updateslot = ath_updateslot;

………

dev->open = ath_init;

dev->stop = ath_stop;

dev->hard_start_xmit = ath_hardstart;

dev->tx_timeout = ath_tx_timeout;

ieee80211_ifattach(ic); 加載ieee802裝置

建立VAP,得到ath0裝置。

error = ieee80211_create_vap(ic, "ath%d", dev,

autocreatemode, IEEE80211_CLONE_BSSID);