Ralink WIFI driver QBSS_LoadAlarm自动切换channel研究


[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,这里我们尝试对该机制进行分析。

 

QBSS_LoadAlarm简介

 

l  从RT5350使用的WIFI driver上看,本身提供一种QBSS_LoadAlarm的机制; 该机制需要修改代码,使能几个条件编译宏打开该功能;

 

l  然后通过iwpriv命令合理设置"qloadalarmtimethres"和"qloadalarmnumthres"两个参数;

iwpriv ra0 set qloadalarmtimethres=50

 

在原厂的SDK中, 这两个参数还未写入到nvram中;因此这两个参数重启后无法保存,用户必须每次在加载Ralink WIFI driver后,通过“iwpriv”命令进行设置。

 

qloadalarmtimethres该参数设置的是driver中的QloadAlarmBusyTimeThreshold;它是百分比,就是channel繁忙的时间占用TBTT的百分比;因此 iwpriv ra0 setqloadalarmtimethres=value”value的取值范围应该是0~100; driver中将TBTT设置为beaconPeriod, 也就是发送Beacon帧的周期;

l  "qloadalarmnumthres":channel连续繁忙>=qloadalarmtimethres的次数超过"qloadalarmnumthres",driver就会发送一个告警,然后开始自动寻找并切换到一个相对不那么繁忙的channel。

The continued number of busy time >= threshold islarger than number threshold so issuing an alarm.

 

 

从底层获取channel方法状况

 

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中的定义如下所示:

 Ralink WIFI driver QBSS_LoadAlarm自动切换channel研究_第1张图片

 

 

对应的源码如下所示:

 

/* 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

 

从上面的统计信息可知:

由于“secondary channel”的统计数据都是零,所以WIFI20M带宽;

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 */

 

 

 

 

 

如何自动切换channel

WIFI driver的另外一个task收到CMDTHREAD_CHAN_RESCAN后,

l  首先调用AP_AUTO_CH_SEL函数,通过自动channel选择机制,获得一个较好的channel作为将来的工作channel。

l  然后调用APStopAPStartUp切换到新的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  然后调用APStopAPStartUp切换到新的channel上去。

 

http://download.csdn.net/detail/junglefly/9146517


你可能感兴趣的:(wifi,Ralink)