通过上面的分析,我们认为实际上使用的发送速率就是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) {
- /* recovery failure. */
- 如果处于是处于尝试速率增加阶段,那么将阈值翻倍,
- 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 {
- /* simple failure. */
- 否则,如果处于正常情况下的失败,将阈值设置为最小
- 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) /* ni_txrate : index to ni_rates[] */
- 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, /* SIOCSIWRATE */
- (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */
复制代码 模块的入口函数: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;
- /* media status will have the current xmit rate if available */
- rate = ieee80211_media2rate(imr.ifm_active); //这个函数的意思是将速率编号转换为实际的速率
- if (rate == -1) /* IFM_AUTO */
- rate = 0; //如果是auto,那么就返回0
- rrq->value = 1000000 * (rate / 2);
- return 0;
- }
复制代码 有关于速率的东西都在ifm_active变量中,这个变量包含了很多的属性,包括速率、模式等等,在if_media.h文件中
- #define IFM_IEEE80211_DS1 5 /* Direct Sequence 1Mbps */
- #define IFM_IEEE80211_DS2 6 /* Direct Sequence 2Mbps */
- #define IFM_IEEE80211_DS5 7 /* Direct Sequence 5.5Mbps */
- #define IFM_IEEE80211_DS11 8 /* Direct Sequence 11Mbps */
- 函数ieee80211com_media_status实现获得ifm_active变量
- ieee80211com_media_status(struct net_device *dev, struct ifmediareq *imr)
- {
- struct ieee80211com *ic = dev->priv; /*XXX*/
- 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 /* 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 */
- , antenna /* antenna mode */
- , flags /* flags */
- , ctsrate /* rts/cts rate */
- , ctsduration /* rts/cts duration */
- , icvlen /* comp icv len */
- , ivlen /* comp iv len */
- , comp /* comp scheme */
- ); //与硬件相关的函数
- 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