在我的Doctor课题研究中,基于ARF协议设计了一个改进型的AMARF协议,该文发表在milcom06和电子科学学刊英文版上。最近,我们将PC机上,使用linux操作系统,基于madwifi开源代码实现了AMARF协议,已经在室内固定以及移动环境测试表明,AMARF协议明显优越于现有的协议。
2008-9-20
现在使用了madwifi程序实现AMARF协议,下面分析其代码。
使用iwconfig命令可以设置速率:
1、iwconfig源代码阅读
首先下载iwconfig.c代码,源代码包为\wireless_tools.29目录
先看执行
的命令的执行过程:
调用main函数,因为是两个参数:
- if(argc == 2)
- print_info(skfd, argv[1], NULL, 0);
- print_info调用
- get_info(int skfd,
- char * ifname,
- struct wireless_info * info)
复制代码 将网卡的各种信息打印出来。
下面与打印发送速率为例,说明调用过程,get_info函数里面:
- /* Get bit rate */
- 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(int skfd, /* Socket to the kernel */
- const char * ifname, /* Device name */
- int request, /* WE ID */
- struct iwreq * pwrq) /* Fixed part of the request */
- {
- /* Set device name */
- strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);
- /* Do the request */
- return(ioctl(skfd, request, pwrq));
- }
复制代码 因此,真正对网卡其他参数的实现是通过ioctl函数实现的,ioctl是驱动程序的基本功能,因此,如果自己想编写一个对网卡参数设置的程序,也应该使用ioctl函数。
下面在看使用
命令执行情况
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"))
- {
- /* Get old bitrate */
- 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(int skfd, /* Socket to the kernel */
- const char * ifname, /* Device name */
- int request, /* WE ID */
- struct iwreq * pwrq) /* Fixed part of the request */
- {
- /* Set device name */
- strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);
- /* Do the request */
- 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);
- /* XXX halve rate for station mode */
- 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++;
- /*
- * Rate control
- * XXX: very primitive version.
- */
- enough = (on->on_tx_ok + on->on_tx_err >= 10); //这两参数是从ath_rate_tx_complete中获得的,onoe算法要统计10次以上的发送结果才又有动作
- /* no packet reached -> down */
- if (on->on_tx_err > 0 && on->on_tx_ok == 0) //如果压根就没有任何一次发送成功,速率控制的方向是递减,也就是算法中的第一中情况
- dir = -1;
- /* all packets needs retry in average -> down */
- if (enough && on->on_tx_ok < on->on_tx_retr) //就是算法中的第二种情况,基本上所有的包都需要重传,降低速率
- dir = -1;
- //ath_rate_raise初始值定义为10,判断用于10%的一个变量
- //如果没有发生传输失败,并且少于10%的包需要重传,增加速率,也就是第四钟情况
- /* no error and less than rate_raise% of packets need retry -> up */
- 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:
- /* raise rate if we hit rate_raise_threshold */
- 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; /* 4 tries at rate 0 */ //速率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 /* series 1 */
- , on->on_tx_rate2sp, 2 /* series 2 */
- , on->on_tx_rate3sp, 2 /* series 3 */
- );
- }
复制代码 速率r0/c0 是通过ath_hal_setuptxdesc函数写入硬件的,
- ath_hal_setuptxdesc(ah, ds
- , pktlen /* packet length */
- , hdrlen /* header length */
- , atype /* Atheros packet type */
- , MIN(ni->ni_txpower, 60)/* txpower */
- , txrate, try0 /* series 0 rate/tries */
- , keyix /* key cache index */
复制代码 该函数在if_ath.c 中每次ath_tx_start函数进行发送的时候调用