[email protected] 2015-9-29
【引子】
l 在调试WIFI client设备时发现基于MT7620的WIFI网关的WIFI非常不稳定; 串口上经常会出现“MgmtRingFullCount”, 导致WIFI client连不上WIFI网关,或者download firmware很慢。
“Qidx(0), notenough space in MgmtRing, MgmtRingFullCount=118!”
l WIFI标准中除了DFS(欧洲为了避免WIFI干扰5G频段的雷达)外,目前了解的情况是并没有明确规定WIFI AP如果通过自动调频来避免噪杂的环境干扰。
l 目前处于2.4G的有很多无线应用,zigbee,蓝牙,WIFI,无绳电话等等;在这些应用中,蓝牙标准通过自动动态调频来规避可能的信道拥塞。目前WIFI网关出现的问题也可能和无线电干扰有关,因此如果能够自动切换channel,可能会让网关的WIFI更灵活。家用路由器如果WIFI异常了,大不了人工重启一下,但是可能某些WIFI网关需要能够做到无人工干预的自适应。
在Ralink的WIFI driver源码中,提供了一种QBSS_LoadAlarm的机制,它可以根据系统负载自动切换WIFIchannel,这里我们尝试对该机制进行分析。
l 从RT5350使用的WIFI driver上看,本身提供一种QBSS_LoadAlarm的机制; 该机制需要修改代码,使能几个条件编译宏打开该功能;
l 然后通过iwpriv命令合理设置"qloadalarmtimethres"和"qloadalarmnumthres"两个参数;
iwpriv ra0 set qloadalarmtimethres=50
在原厂的SDK中, 这两个参数还未写入到nvram中;因此这两个参数重启后无法保存,用户必须每次在加载Ralink WIFI driver后,通过“iwpriv”命令进行设置。
l “qloadalarmtimethres”该参数设置的是driver中的QloadAlarmBusyTimeThreshold;它是百分比,就是channel繁忙的时间占用TBTT的百分比;因此” iwpriv ra0 setqloadalarmtimethres=value”中value的取值范围应该是0~100; driver中将TBTT设置为beaconPeriod, 也就是发送Beacon帧的周期;
l "qloadalarmnumthres":channel连续繁忙>=“qloadalarmtimethres”的次数超过"qloadalarmnumthres",driver就会发送一个告警,然后开始自动寻找并切换到一个相对不那么繁忙的channel。
l The continued number of busy time >= threshold islarger than number threshold so issuing an alarm.
从Datasheet中,可知通过“CH_BUSY_STA”和“CH_BUSY_STA_SEC”这两个寄存器获得“primary channel”和“secondarychannel”的繁忙情况。
至于“primary channel”和“secondarychannel”,是因为当个WIFI channel的宽度是20M,如果要实现40M宽度的话,就需要使用到两个channel;具体可以参见WIFI协议。
“CH_BUSY_STA”和“CH_BUSY_STA_SEC”这两个寄存器在datasheet中的定义如下所示:
对应的源码如下所示:
/* Count EIFS, NAV, RX busy, TX busy as channel busy and enable Channel statistic timer (bit 0) */ /* Note: if bit 0 == 0, the function will be disabled */ RTMP_IO_WRITE32(pAd, CH_TIME_CFG, 0x0000001F);
#define TBTT_SYNC_CFG 0x1118 /* txrx_csr10 */ #define TSF_TIMER_DW0 0x111C /* Local TSF timer lsb 32 bits. Read-only */ #define TSF_TIMER_DW1 0x1120 /* msb 32 bits. Read-only. */ #define TBTT_TIMER 0x1124 /* TImer remains till next TBTT. Read-only. TXRX_CSR14 */ #define INT_TIMER_CFG 0x1128 /* */ #define INT_TIMER_EN 0x112c /* GP-timer and pre-tbtt Int enable */ #define CH_IDLE_STA 0x1130 /* channel idle time */ #define CH_BUSY_STA 0x1134 /* channle busy time */ #define CH_BUSY_STA_SEC 0x1138 /* channel busy time for secondary channel */
/* in 20MHz, no need to check busy time of secondary channel */ RTMP_IO_READ32(pAd, CH_BUSY_STA_SEC, &BusyTime); pAd->QloadLatestChannelBusyTimeSec = BusyTime;
/* do busy time statistics for primary channel */ RTMP_IO_READ32(pAd, CH_BUSY_STA, &BusyTime); pAd->QloadLatestChannelBusyTimePri = BusyTime;
在QBSS_LoadUpdate这个函数中,通过上面的代码从寄存器中读取channel的繁忙情况。
通过如下的函数将用户设置的QloadAlarmBusyTimeThreshold对应的百分比,转换为micro-sec。 /* ======================================================================== Routine Description: Re-calculate busy time threshold. Arguments: pAd - WLAN control block pointer TimePeriod - TBTT Return Value: None Note: EX: TBTT=100ms, 90%, pAd->QloadBusyTimeThreshold = 90ms ======================================================================== */ static VOID QBSS_LoadAlarmBusyTimeThresholdReset( IN RTMP_ADAPTER *pAd, IN UINT32 TimePeriod) { pAd->QloadBusyTimeThreshold = TimePeriod; pAd->QloadBusyTimeThreshold *= pAd->QloadAlarmBusyTimeThreshold; pAd->QloadBusyTimeThreshold /= 100; pAd->QloadBusyTimeThreshold <<= 10; /* translate mini-sec to micro-sec */ } /* End of QBSS_LoadAlarmBusyTimeThresholdReset */
UINT8 QloadAlarmBusyTimeThreshold; /* unit: 1/100 */ UINT8 QloadAlarmBusyNumThreshold; /* unit: 1 */
QBSS_LoadAlarmBusyTimeThresholdReset(pAd, pAd->CommonCfg.BeaconPeriod);
由上面可以知道 pAd->QloadAlarmBusyTimeThreshold; 是百分比,就是channel繁忙的时间占用TBTT的百分比;因此” iwpriv ra0 set qloadalarmtimethres=value”中value的取值范围应该是0~100; 从上面代码又知道,driver中将TBTT设置为beaconPeriod;
#ifdef AP_QLOAD_SUPPORT UINT8 FlgQloadEnable; /* 1: any BSS WMM is enabled */ ULONG QloadUpTimeLast; /* last up time */ UINT8 QloadChanUtil; /* last QBSS Load, unit: us */ UINT32 QloadChanUtilTotal; /* current QBSS Load Total */ UINT8 QloadChanUtilBeaconCnt; /* 1~100, default: 50 */ UINT8 QloadChanUtilBeaconInt; /* 1~100, default: 50 */ UINT32 QloadLatestChannelBusyTimePri; UINT32 QloadLatestChannelBusyTimeSec;
/* ex: For 100ms beacon interval, if the busy time in last TBTT is smaller than 5ms, QloadBusyCount[0] ++; if the busy time in last TBTT is between 5 and 10ms, QloadBusyCount[1] ++; ...... if the busy time in last TBTT is larger than 95ms, QloadBusyCount[19] ++;
Command: "iwpriv ra0 show qload". */
/* provide busy time statistics for every TBTT */ #define QLOAD_FUNC_BUSY_TIME_STATS
/* provide busy time alarm mechanism */ /* use the function to avoid to locate in some noise environments */ #define QLOAD_FUNC_BUSY_TIME_ALARM
#ifdef QLOAD_FUNC_BUSY_TIME_STATS #define QLOAD_BUSY_INTERVALS 20 /* partition TBTT to QLOAD_BUSY_INTERVALS */ /* for primary channel & secondary channel */ UINT32 QloadBusyCountPri[QLOAD_BUSY_INTERVALS]; UINT32 QloadBusyCountSec[QLOAD_BUSY_INTERVALS]; #endif /* QLOAD_FUNC_BUSY_TIME_STATS */
#ifdef QLOAD_FUNC_BUSY_TIME_ALARM #define QLOAD_DOES_ALARM_OCCUR(pAd) (pAd->FlgQloadAlarmIsSuspended == TRUE) #define QLOAD_ALARM_EVER_OCCUR(pAd) (pAd->QloadAlarmNumber > 0) BOOLEAN FlgQloadAlarmIsSuspended; /* 1: suspend */
UINT8 QloadAlarmBusyTimeThreshold; /* unit: 1/100 */ UINT8 QloadAlarmBusyNumThreshold; /* unit: 1 */ UINT8 QloadAlarmBusyNum; UINT8 QloadAlarmDuration; /* unit: TBTT */
UINT32 QloadAlarmNumber; /* total alarm times */ BOOLEAN FlgQloadAlarm; /* 1: alarm occurs */
/* speed up use */ UINT32 QloadTimePeriodLast; UINT32 QloadBusyTimeThreshold; #else
#define QLOAD_DOES_ALARM_OCCUR(pAd) 0 #endif /* QLOAD_FUNC_BUSY_TIME_ALARM */
#endif /* AP_QLOAD_SUPPORT */ #endif /* CONFIG_AP_SUPPORT */
|
在源码中通过以”Beacon interval/20”时间长度为单位,统计channel每个采样时间内繁忙的程度。
/* ex: For 100ms beacon interval, if the busy time in last TBTT is smaller than 5ms, QloadBusyCount[0] ++; if the busy time in last TBTT is between 5 and 10ms, QloadBusyCount[1] ++; ...... if the busy time in last TBTT is larger than 95ms, QloadBusyCount[19] ++; Command: "iwpriv ra0 show qload". */ 下面WIFI channel繁忙程度的一个统计实例; # iwpriv ra0 show qload
Primary Busy Time Times 0ms ~ 5ms 9 5ms ~ 10ms 337 10ms ~ 15ms 1850 15ms ~ 20ms 2950 20ms ~ 25ms 2936 25ms ~ 30ms 2345 30ms ~ 35ms 1546 35ms ~ 40ms 1107 40ms ~ 45ms 726 45ms ~ 50ms 493 50ms ~ 55ms 361 55ms ~ 60ms 276 60ms ~ 65ms 244 65ms ~ 70ms 131 70ms ~ 75ms 98 75ms ~ 80ms 93 80ms ~ 85ms 66 85ms ~ 90ms 56 90ms ~ 95ms 52 95ms ~ 100ms 13
Secondary Busy Time Times 0ms ~ 5ms 0 5ms ~ 10ms 0 10ms ~ 15ms 0 15ms ~ 20ms 0 20ms ~ 25ms 0 25ms ~ 30ms 0 30ms ~ 35ms 0 35ms ~ 40ms 0 40ms ~ 45ms 0 45ms ~ 50ms 0 50ms ~ 55ms 0 55ms ~ 60ms 0 60ms ~ 65ms 0 65ms ~ 70ms 0 70ms ~ 75ms 0 75ms ~ 80ms 0 80ms ~ 85ms 0 85ms ~ 90ms 0 90ms ~ 95ms 0 95ms ~ 100ms 0
从上面的统计信息可知: l 由于“secondary channel”的统计数据都是零,所以WIFI是20M带宽; l “primary channel”看起来并不太繁忙,大部分时间channel的繁忙程度为10%到50%;
|
源码中通过周期性调用QBSS_LoadUpdate函数从底层获得channel繁忙程度,然后检查是否超过了用户设置的门限,如果超过了用户设置的门限,就通过QBSS_LoadAlarm函数触发自动channel切换。
#ifdef WORKQUEUE_BH void tbtt_workq(struct work_struct *work) #else void tbtt_tasklet(unsigned long data) #endif /* WORKQUEUE_BH */ { /*#define MAX_TX_IN_TBTT (16) */
#ifdef CONFIG_AP_SUPPORT #ifdef WORKQUEUE_BH POS_COOKIE pObj = container_of(work, struct os_cookie, tbtt_work); PRTMP_ADAPTER pAd = pObj->pAd_va; #else PRTMP_ADAPTER pAd = (PRTMP_ADAPTER) data; #endif /* WORKQUEUE_BH */
#ifdef RTMP_MAC_PCI if (pAd->OpMode == OPMODE_AP) { #ifdef AP_QLOAD_SUPPORT /* update channel utilization */ QBSS_LoadUpdate(pAd, 0); #endif /* AP_QLOAD_SUPPORT */
}
The continued number of busy time >= threshold is larger than number threshold so issuing an alarm.
#ifdef QLOAD_FUNC_BUSY_TIME_ALARM /* check if alarm function is enabled */ if ((pAd->FlgQloadAlarmIsSuspended == FALSE) && (pAd->QloadAlarmBusyTimeThreshold > 0)) { /* Alarm is not suspended and is enabled */
/* check if we need to issue a alarm */ if (FlgIsBusyOverThreshold == TRUE) { if (pAd->QloadAlarmDuration == 0) { /* last alarm ended so we can check new alarm */ pAd->QloadAlarmBusyNum ++; if (pAd->QloadAlarmBusyNum >= pAd->QloadAlarmBusyNumThreshold) { /* The continued number of busy time >= threshold is larger than number threshold so issuing a alarm. */ FlgIsAlarmNeeded = TRUE; pAd->QloadAlarmDuration ++; } /* End of if */ } /* End of if */ } else pAd->QloadAlarmBusyNum = 0; /* End of if */
if (pAd->QloadAlarmDuration > 0) { /* New alarm occurs so we can not re-issue new alarm during QBSS_LOAD_ALARM_DURATION * TBTT. */ if (++pAd->QloadAlarmDuration >= QBSS_LOAD_ALARM_DURATION) { /* can re-issue next alarm */ pAd->QloadAlarmDuration = 0; pAd->QloadAlarmBusyNum = 0; } /* End of if */ } /* End of if */
if (FlgIsAlarmNeeded == TRUE) QBSS_LoadAlarm(pAd); /* End of if */ } else { /* clear statistics counts */ pAd->QloadAlarmBusyNum = 0; pAd->QloadAlarmDuration = 0; pAd->FlgQloadAlarm = FALSE; } /* End of if */ #endif /* QLOAD_FUNC_BUSY_TIME_ALARM */
|
WIFI driver的另外一个task收到CMDTHREAD_CHAN_RESCAN后,
l 首先调用AP_AUTO_CH_SEL函数,通过自动channel选择机制,获得一个较好的channel作为将来的工作channel。
l 然后调用APStop和APStartUp切换到新的channel上去。
#ifdef CONFIG_AP_SUPPORT //#ifdef RTMP_PCI_SUPPORT case CMDTHREAD_CHAN_RESCAN: DBGPRINT(RT_DEBUG_TRACE, ("cmd> Re-scan channel! \n"));
pAd->CommonCfg.Channel = AP_AUTO_CH_SEL(pAd, TRUE);
#ifdef DOT11_N_SUPPORT /* If phymode > PHY_11ABGN_MIXED and BW=40 check extension channel, after select channel */ N_ChannelCheck(pAd); #endif /* DOT11_N_SUPPORT */
DBGPRINT(RT_DEBUG_TRACE, ("cmd> Switch to %d! \n", pAd->CommonCfg.Channel)); APStop(pAd); APStartUp(pAd);
#ifdef AP_QLOAD_SUPPORT QBSS_LoadAlarmResume(pAd); #endif /* AP_QLOAD_SUPPORT */ break; //#endif /* RTMP_PCI_SUPPORT */ #endif /* CONFIG_AP_SUPPORT */
|
至此,自动切换channel分析完成,小结一下:
1) Ralink WIFI driver加载后;用户需要通过iwpriv命令合理设置 "qloadalarmtimethres"和"qloadalarmnumthres"两个参数;
2) Driver周期地从底层寄存器中获得channel的繁忙程度;drvier将它和用户设置的门限进行比较;
3) 如果超过门限,则触发通过发送“CMDTHREAD_CHAN_RESCAN”告警,触发自动channel切换作业;
WIFI driver的另外一个task收到CMDTHREAD_CHAN_RESCAN后,
l 首先调用AP_AUTO_CH_SEL函数,通过自动channel选择机制,获得一个较好的channel作为将来的工作channel。
l 然后调用APStop和APStartUp切换到新的channel上去。
http://download.csdn.net/detail/junglefly/9146517