mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-12-02 19:22:47 +00:00
Migrate the LNA mixing diversity machinery from the AR9285 HAL to the driver.
The AR9485 chip and AR933x SoC both implement LNA diversity. There are a few extra things that need to happen before this can be flipped on for those chips (mostly to do with setting up the different bias values and LNA1/LNA2 RSSI differences) but the first stage is putting this code into the driver layer so it can be reused. This has the added benefit of making it easier to expose configuration options and diagnostic information via the ioctl API. That's not yet being done but it sure would be nice to do so. Tested: * AR9285, with LNA diversity enabled * AR9285, with LNA diversity disabled in EEPROM
This commit is contained in:
parent
ccba710262
commit
216ca2346f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=251655
@ -733,6 +733,8 @@ dev/ath/if_ath_keycache.c optional ath \
|
||||
compile-with "${NORMAL_C} -I$S/dev/ath"
|
||||
dev/ath/if_ath_led.c optional ath \
|
||||
compile-with "${NORMAL_C} -I$S/dev/ath"
|
||||
dev/ath/if_ath_lna_div.c optional ath \
|
||||
compile-with "${NORMAL_C} -I$S/dev/ath"
|
||||
dev/ath/if_ath_tx.c optional ath \
|
||||
compile-with "${NORMAL_C} -I$S/dev/ath"
|
||||
dev/ath/if_ath_tx_edma.c optional ath \
|
||||
|
@ -1402,9 +1402,6 @@ struct ath_hal {
|
||||
const struct ieee80211_channel *);
|
||||
void __ahdecl(*ah_procMibEvent)(struct ath_hal *,
|
||||
const HAL_NODE_STATS *);
|
||||
void __ahdecl(*ah_rxAntCombDiversity)(struct ath_hal *,
|
||||
struct ath_rx_status *,
|
||||
unsigned long, int);
|
||||
|
||||
/* Misc Functions */
|
||||
HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *,
|
||||
@ -1585,6 +1582,12 @@ struct ath_hal {
|
||||
uint32_t, uint32_t);
|
||||
void __ahdecl(*ah_btCoexDisable)(struct ath_hal *);
|
||||
int __ahdecl(*ah_btCoexEnable)(struct ath_hal *);
|
||||
|
||||
/* LNA diversity configuration */
|
||||
void __ahdecl(*ah_divLnaConfGet)(struct ath_hal *,
|
||||
HAL_ANT_COMB_CONFIG *);
|
||||
void __ahdecl(*ah_divLnaConfSet)(struct ath_hal *,
|
||||
HAL_ANT_COMB_CONFIG *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -20,42 +20,12 @@
|
||||
|
||||
#include "ar5416/ar5416.h"
|
||||
|
||||
struct ar9285_ant_comb {
|
||||
uint16_t count;
|
||||
uint16_t total_pkt_count;
|
||||
HAL_BOOL scan;
|
||||
HAL_BOOL scan_not_start;
|
||||
int main_total_rssi;
|
||||
int alt_total_rssi;
|
||||
int alt_recv_cnt;
|
||||
int main_recv_cnt;
|
||||
int rssi_lna1;
|
||||
int rssi_lna2;
|
||||
int rssi_add;
|
||||
int rssi_sub;
|
||||
int rssi_first;
|
||||
int rssi_second;
|
||||
int rssi_third;
|
||||
HAL_BOOL alt_good;
|
||||
int quick_scan_cnt;
|
||||
int main_conf;
|
||||
HAL_ANT_DIV_COMB_LNA_CONF first_quick_scan_conf;
|
||||
HAL_ANT_DIV_COMB_LNA_CONF second_quick_scan_conf;
|
||||
int first_bias;
|
||||
int second_bias;
|
||||
HAL_BOOL first_ratio;
|
||||
HAL_BOOL second_ratio;
|
||||
unsigned long scan_start_time;
|
||||
};
|
||||
|
||||
struct ath_hal_9285 {
|
||||
struct ath_hal_5416 ah_5416;
|
||||
|
||||
HAL_INI_ARRAY ah_ini_txgain;
|
||||
HAL_INI_ARRAY ah_ini_rxgain;
|
||||
|
||||
struct ar9285_ant_comb ant_comb; /* Kite Antenna comb/diversity */
|
||||
|
||||
struct {
|
||||
int32_t prev_offset; /* Previous value of PA offset value */
|
||||
int8_t max_skipcount; /* Max No. of times PACAL can be skipped */
|
||||
@ -71,7 +41,6 @@ struct ath_hal_9285 {
|
||||
#define AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ -127
|
||||
#define AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ -108
|
||||
|
||||
HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING);
|
||||
HAL_BOOL ar9285RfAttach(struct ath_hal *, HAL_STATUS *);
|
||||
|
||||
extern HAL_BOOL ar9285SetTransmitPower(struct ath_hal *,
|
||||
|
@ -180,6 +180,8 @@ ar9285Attach(uint16_t devid, HAL_SOFTC sc,
|
||||
ah->ah_setTxPower = ar9285SetTransmitPower;
|
||||
ah->ah_setBoardValues = ar9285SetBoardValues;
|
||||
ah->ah_btCoexSetParameter = ar9285BTCoexSetParameter;
|
||||
ah->ah_divLnaConfGet = ar9285_antdiv_comb_conf_get;
|
||||
ah->ah_divLnaConfSet = ar9285_antdiv_comb_conf_set;
|
||||
|
||||
AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal;
|
||||
AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal;
|
||||
@ -341,7 +343,6 @@ ar9285Attach(uint16_t devid, HAL_SOFTC sc,
|
||||
/* Print out whether the EEPROM settings enable AR9285 diversity */
|
||||
if (ar9285_check_div_comb(ah)) {
|
||||
ath_hal_printf(ah, "[ath] Enabling diversity for Kite\n");
|
||||
ah->ah_rxAntCombDiversity = ar9285_ant_comb_scan;
|
||||
}
|
||||
|
||||
/* Disable 11n for the AR2427 */
|
||||
|
@ -33,609 +33,13 @@
|
||||
#include "ah_eeprom_v4k.h"
|
||||
|
||||
#include "ar9002/ar9280.h"
|
||||
#include "ar9002/ar9285_diversity.h"
|
||||
#include "ar9002/ar9285.h"
|
||||
#include "ar5416/ar5416reg.h"
|
||||
#include "ar5416/ar5416phy.h"
|
||||
#include "ar9002/ar9285phy.h"
|
||||
#include "ar9002/ar9285_phy.h"
|
||||
|
||||
|
||||
/* Linux compability macros */
|
||||
/*
|
||||
* XXX these don't handle rounding, underflow, overflow, wrapping!
|
||||
*/
|
||||
#define msecs_to_jiffies(a) ( (a) * hz / 1000 )
|
||||
#define time_after(a, b) ( (long) (b) - (long) (a) < 0 )
|
||||
|
||||
static HAL_BOOL
|
||||
ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
|
||||
int main_rssi_avg, int alt_rssi_avg, int pkt_count)
|
||||
{
|
||||
return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
|
||||
(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
|
||||
}
|
||||
|
||||
static void
|
||||
ath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb,
|
||||
HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
|
||||
{
|
||||
antcomb->quick_scan_cnt = 0;
|
||||
|
||||
if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
|
||||
switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
|
||||
case (0x10): /* LNA2 A-B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
break;
|
||||
case (0x20): /* LNA1 A-B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
break;
|
||||
case (0x21): /* LNA1 LNA2 */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case (0x12): /* LNA2 LNA1 */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case (0x13): /* LNA2 A+B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
break;
|
||||
case (0x23): /* LNA1 A+B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb,
|
||||
HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
|
||||
int alt_rssi_avg, int alt_ratio)
|
||||
{
|
||||
/* alt_good */
|
||||
switch (antcomb->quick_scan_cnt) {
|
||||
case 0:
|
||||
/* set alt to main, and alt to first conf */
|
||||
div_ant_conf->main_lna_conf = antcomb->main_conf;
|
||||
div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
|
||||
break;
|
||||
case 1:
|
||||
/* set alt to main, and alt to first conf */
|
||||
div_ant_conf->main_lna_conf = antcomb->main_conf;
|
||||
div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
|
||||
antcomb->rssi_first = main_rssi_avg;
|
||||
antcomb->rssi_second = alt_rssi_avg;
|
||||
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
|
||||
/* main is LNA1 */
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
} else {
|
||||
if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
|
||||
(alt_rssi_avg > main_rssi_avg)) &&
|
||||
(antcomb->total_pkt_count > 50))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
antcomb->alt_good = AH_FALSE;
|
||||
antcomb->scan_not_start = AH_FALSE;
|
||||
antcomb->scan = AH_FALSE;
|
||||
antcomb->rssi_first = main_rssi_avg;
|
||||
antcomb->rssi_third = alt_rssi_avg;
|
||||
|
||||
if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = alt_rssi_avg;
|
||||
else if (antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = alt_rssi_avg;
|
||||
else if (antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
}
|
||||
|
||||
if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
|
||||
div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
} else {
|
||||
if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
|
||||
(alt_rssi_avg > main_rssi_avg)) &&
|
||||
(antcomb->total_pkt_count > 50))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
}
|
||||
|
||||
/* set alt to the conf with maximun ratio */
|
||||
if (antcomb->first_ratio && antcomb->second_ratio) {
|
||||
if (antcomb->rssi_second > antcomb->rssi_third) {
|
||||
/* first alt*/
|
||||
if ((antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2*/
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->first_quick_scan_conf;
|
||||
} else if ((antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)) {
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
} else {
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->second_quick_scan_conf;
|
||||
}
|
||||
} else if (antcomb->first_ratio) {
|
||||
/* first alt */
|
||||
if ((antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->first_quick_scan_conf;
|
||||
} else if (antcomb->second_ratio) {
|
||||
/* second alt */
|
||||
if ((antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->second_quick_scan_conf;
|
||||
} else {
|
||||
/* main is largest */
|
||||
if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf = antcomb->main_conf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ath_ant_div_conf_fast_divbias(HAL_ANT_COMB_CONFIG *ant_conf)
|
||||
{
|
||||
/* Adjust the fast_div_bias based on main and alt lna conf */
|
||||
switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
|
||||
case (0x01): /* A-B LNA2 */
|
||||
ant_conf->fast_div_bias = 0x3b;
|
||||
break;
|
||||
case (0x02): /* A-B LNA1 */
|
||||
ant_conf->fast_div_bias = 0x3d;
|
||||
break;
|
||||
case (0x03): /* A-B A+B */
|
||||
ant_conf->fast_div_bias = 0x1;
|
||||
break;
|
||||
case (0x10): /* LNA2 A-B */
|
||||
ant_conf->fast_div_bias = 0x7;
|
||||
break;
|
||||
case (0x12): /* LNA2 LNA1 */
|
||||
ant_conf->fast_div_bias = 0x2;
|
||||
break;
|
||||
case (0x13): /* LNA2 A+B */
|
||||
ant_conf->fast_div_bias = 0x7;
|
||||
break;
|
||||
case (0x20): /* LNA1 A-B */
|
||||
ant_conf->fast_div_bias = 0x6;
|
||||
break;
|
||||
case (0x21): /* LNA1 LNA2 */
|
||||
ant_conf->fast_div_bias = 0x0;
|
||||
break;
|
||||
case (0x23): /* LNA1 A+B */
|
||||
ant_conf->fast_div_bias = 0x6;
|
||||
break;
|
||||
case (0x30): /* A+B A-B */
|
||||
ant_conf->fast_div_bias = 0x1;
|
||||
break;
|
||||
case (0x31): /* A+B LNA2 */
|
||||
ant_conf->fast_div_bias = 0x3b;
|
||||
break;
|
||||
case (0x32): /* A+B LNA1 */
|
||||
ant_conf->fast_div_bias = 0x3d;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Antenna diversity and combining */
|
||||
void
|
||||
ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
|
||||
unsigned long ticks, int hz)
|
||||
{
|
||||
HAL_ANT_COMB_CONFIG div_ant_conf;
|
||||
struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb;
|
||||
int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
|
||||
int curr_main_set, curr_bias;
|
||||
int main_rssi = rs->rs_rssi_ctl[0];
|
||||
int alt_rssi = rs->rs_rssi_ctl[1];
|
||||
int rx_ant_conf, main_ant_conf, alt_ant_conf;
|
||||
HAL_BOOL short_scan = AH_FALSE;
|
||||
|
||||
rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
|
||||
main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
|
||||
alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
|
||||
|
||||
#if 0
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; FastDiv: %d\n",
|
||||
__func__, main_rssi, alt_rssi, main_ant_conf,alt_ant_conf, rx_ant_conf,
|
||||
!!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40));
|
||||
#endif
|
||||
|
||||
if (! ar9285_check_div_comb(ah))
|
||||
return;
|
||||
|
||||
if (AH5212(ah)->ah_diversity == AH_FALSE)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n",
|
||||
__func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf);
|
||||
#endif
|
||||
|
||||
/* Record packet only when alt_rssi is positive */
|
||||
if (main_rssi > 0 && alt_rssi > 0) {
|
||||
antcomb->total_pkt_count++;
|
||||
antcomb->main_total_rssi += main_rssi;
|
||||
antcomb->alt_total_rssi += alt_rssi;
|
||||
if (main_ant_conf == rx_ant_conf)
|
||||
antcomb->main_recv_cnt++;
|
||||
else
|
||||
antcomb->alt_recv_cnt++;
|
||||
}
|
||||
|
||||
/* Short scan check */
|
||||
if (antcomb->scan && antcomb->alt_good) {
|
||||
if (time_after(ticks, antcomb->scan_start_time +
|
||||
msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
|
||||
short_scan = AH_TRUE;
|
||||
else
|
||||
if (antcomb->total_pkt_count ==
|
||||
ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
|
||||
alt_ratio = ((antcomb->alt_recv_cnt * 100) /
|
||||
antcomb->total_pkt_count);
|
||||
if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
|
||||
short_scan = AH_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
|
||||
rs->rs_moreaggr) && !short_scan)
|
||||
return;
|
||||
|
||||
if (antcomb->total_pkt_count) {
|
||||
alt_ratio = ((antcomb->alt_recv_cnt * 100) /
|
||||
antcomb->total_pkt_count);
|
||||
main_rssi_avg = (antcomb->main_total_rssi /
|
||||
antcomb->total_pkt_count);
|
||||
alt_rssi_avg = (antcomb->alt_total_rssi /
|
||||
antcomb->total_pkt_count);
|
||||
}
|
||||
|
||||
OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
|
||||
ar9285_antdiv_comb_conf_get(ah, &div_ant_conf);
|
||||
curr_alt_set = div_ant_conf.alt_lna_conf;
|
||||
curr_main_set = div_ant_conf.main_lna_conf;
|
||||
curr_bias = div_ant_conf.fast_div_bias;
|
||||
|
||||
antcomb->count++;
|
||||
|
||||
if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
|
||||
if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
|
||||
ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
|
||||
main_rssi_avg);
|
||||
antcomb->alt_good = AH_TRUE;
|
||||
} else {
|
||||
antcomb->alt_good = AH_FALSE;
|
||||
}
|
||||
|
||||
antcomb->count = 0;
|
||||
antcomb->scan = AH_TRUE;
|
||||
antcomb->scan_not_start = AH_TRUE;
|
||||
}
|
||||
|
||||
if (!antcomb->scan) {
|
||||
if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
|
||||
if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
|
||||
/* Switch main and alt LNA */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
|
||||
goto div_comb_done;
|
||||
} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
|
||||
(curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
|
||||
/* Set alt to another LNA */
|
||||
if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
|
||||
goto div_comb_done;
|
||||
}
|
||||
|
||||
if ((alt_rssi_avg < (main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
|
||||
goto div_comb_done;
|
||||
}
|
||||
|
||||
if (!antcomb->scan_not_start) {
|
||||
switch (curr_alt_set) {
|
||||
case HAL_ANT_DIV_COMB_LNA2:
|
||||
antcomb->rssi_lna2 = alt_rssi_avg;
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1:
|
||||
antcomb->rssi_lna1 = alt_rssi_avg;
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
|
||||
antcomb->rssi_add = alt_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A-B */
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
|
||||
antcomb->rssi_sub = alt_rssi_avg;
|
||||
antcomb->scan = AH_FALSE;
|
||||
if (antcomb->rssi_lna2 >
|
||||
(antcomb->rssi_lna1 +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
|
||||
/* use LNA2 as main LNA */
|
||||
if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
|
||||
(antcomb->rssi_add > antcomb->rssi_sub)) {
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
} else if (antcomb->rssi_sub >
|
||||
antcomb->rssi_lna1) {
|
||||
/* set to A-B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
} else {
|
||||
/* set to LNA1 */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
}
|
||||
} else {
|
||||
/* use LNA1 as main LNA */
|
||||
if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
|
||||
(antcomb->rssi_add > antcomb->rssi_sub)) {
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
} else if (antcomb->rssi_sub >
|
||||
antcomb->rssi_lna1) {
|
||||
/* set to A-B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
} else {
|
||||
/* set to LNA2 */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!antcomb->alt_good) {
|
||||
antcomb->scan_not_start = AH_FALSE;
|
||||
/* Set alt to another LNA */
|
||||
if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
goto div_comb_done;
|
||||
}
|
||||
}
|
||||
|
||||
ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
alt_ratio);
|
||||
|
||||
antcomb->quick_scan_cnt++;
|
||||
|
||||
div_comb_done:
|
||||
ath_ant_div_conf_fast_divbias(&div_ant_conf);
|
||||
|
||||
ar9285_antdiv_comb_conf_set(ah, &div_ant_conf);
|
||||
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
|
||||
__func__, antcomb->total_pkt_count);
|
||||
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
|
||||
__func__, antcomb->main_total_rssi);
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
|
||||
__func__, antcomb->alt_total_rssi);
|
||||
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
|
||||
__func__, main_rssi_avg);
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
|
||||
__func__, alt_rssi_avg);
|
||||
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
|
||||
__func__, antcomb->main_recv_cnt);
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
|
||||
__func__, antcomb->alt_recv_cnt);
|
||||
|
||||
// if (curr_alt_set != div_ant_conf.alt_lna_conf)
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
|
||||
__func__, curr_alt_set, div_ant_conf.alt_lna_conf);
|
||||
// if (curr_main_set != div_ant_conf.main_lna_conf)
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
|
||||
__func__, curr_main_set, div_ant_conf.main_lna_conf);
|
||||
// if (curr_bias != div_ant_conf.fast_div_bias)
|
||||
HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
|
||||
__func__, curr_bias, div_ant_conf.fast_div_bias);
|
||||
|
||||
antcomb->scan_start_time = ticks;
|
||||
antcomb->total_pkt_count = 0;
|
||||
antcomb->main_total_rssi = 0;
|
||||
antcomb->alt_total_rssi = 0;
|
||||
antcomb->main_recv_cnt = 0;
|
||||
antcomb->alt_recv_cnt = 0;
|
||||
}
|
||||
#include "ar9002/ar9285_diversity.h"
|
||||
|
||||
/*
|
||||
* Set the antenna switch to control RX antenna diversity.
|
||||
|
@ -28,26 +28,6 @@
|
||||
#ifndef __AR9285_DIVERSITY_H__
|
||||
#define __AR9285_DIVERSITY_H__
|
||||
|
||||
/* Antenna diversity/combining */
|
||||
#define ATH_ANT_RX_CURRENT_SHIFT 4
|
||||
#define ATH_ANT_RX_MAIN_SHIFT 2
|
||||
#define ATH_ANT_RX_MASK 0x3
|
||||
|
||||
#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50
|
||||
#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100
|
||||
#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200
|
||||
#define ATH_ANT_DIV_COMB_INIT_COUNT 95
|
||||
#define ATH_ANT_DIV_COMB_MAX_COUNT 100
|
||||
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
|
||||
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
|
||||
|
||||
#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3
|
||||
#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
|
||||
|
||||
extern void ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
|
||||
unsigned long ticks, int hz);
|
||||
extern HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING);
|
||||
|
||||
#endif
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "ar9002/ar9002phy.h"
|
||||
#include "ar9002/ar9285phy.h"
|
||||
#include "ar9002/ar9285an.h"
|
||||
#include "ar9002/ar9285_diversity.h"
|
||||
|
||||
/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
|
||||
#define EEP_MINOR(_ah) \
|
||||
|
@ -113,6 +113,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/ath/if_ath_beacon.h>
|
||||
#include <dev/ath/if_ath_btcoex.h>
|
||||
#include <dev/ath/if_ath_spectral.h>
|
||||
#include <dev/ath/if_ath_lna_div.h>
|
||||
#include <dev/ath/if_athdfs.h>
|
||||
|
||||
#ifdef ATH_TX99_DIAG
|
||||
@ -530,6 +531,14 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
|
||||
goto bad2;
|
||||
}
|
||||
|
||||
/* Attach LNA diversity module */
|
||||
if (ath_lna_div_attach(sc) < 0) {
|
||||
device_printf(sc->sc_dev,
|
||||
"%s: unable to attach LNA diversity\n", __func__);
|
||||
error = EIO;
|
||||
goto bad2;
|
||||
}
|
||||
|
||||
/* Start DFS processing tasklet */
|
||||
TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc);
|
||||
|
||||
@ -680,6 +689,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
|
||||
sc->sc_rxtsf32 = ath_hal_has_long_rxdesc_tsf(ah);
|
||||
sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah);
|
||||
sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah);
|
||||
sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah);
|
||||
|
||||
if (ath_hal_hasfastframes(ah))
|
||||
ic->ic_caps |= IEEE80211_C_FF;
|
||||
wmodes = ath_hal_getwirelessmodes(ah);
|
||||
@ -1038,6 +1049,7 @@ ath_detach(struct ath_softc *sc)
|
||||
#ifdef ATH_DEBUG_ALQ
|
||||
if_ath_alq_tidyup(&sc->sc_alq);
|
||||
#endif
|
||||
ath_lna_div_detach(sc);
|
||||
ath_btcoex_detach(sc);
|
||||
ath_spectral_detach(sc);
|
||||
ath_dfs_detach(sc);
|
||||
|
@ -67,6 +67,7 @@ enum {
|
||||
ATH_DEBUG_EDMA_RX = 0x200000000ULL, /* RX EDMA state */
|
||||
ATH_DEBUG_SW_TX_FILT = 0x400000000ULL, /* SW TX FF */
|
||||
ATH_DEBUG_NODE_PWRSAVE = 0x800000000ULL, /* node powersave */
|
||||
ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */
|
||||
|
||||
ATH_DEBUG_ANY = 0xffffffffffffffffULL
|
||||
};
|
||||
|
798
sys/dev/ath/if_ath_lna_div.c
Normal file
798
sys/dev/ath/if_ath_lna_div.c
Normal file
@ -0,0 +1,798 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* This module handles LNA diversity for those chips which implement LNA
|
||||
* mixing (AR9285/AR9485.)
|
||||
*/
|
||||
#include "opt_ath.h"
|
||||
#include "opt_inet.h"
|
||||
#include "opt_wlan.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_media.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/ethernet.h> /* XXX for ether_sprintf */
|
||||
|
||||
#include <net80211/ieee80211_var.h>
|
||||
|
||||
#include <net/bpf.h>
|
||||
|
||||
#ifdef INET
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#endif
|
||||
|
||||
#include <dev/ath/if_athvar.h>
|
||||
#include <dev/ath/if_ath_debug.h>
|
||||
#include <dev/ath/if_ath_lna_div.h>
|
||||
|
||||
/* Linux compability macros */
|
||||
/*
|
||||
* XXX these don't handle rounding, underflow, overflow, wrapping!
|
||||
*/
|
||||
#define msecs_to_jiffies(a) ( (a) * hz / 1000 )
|
||||
|
||||
/*
|
||||
* Methods which are required
|
||||
*/
|
||||
|
||||
/*
|
||||
* Attach the LNA diversity to the given interface
|
||||
*/
|
||||
int
|
||||
ath_lna_div_attach(struct ath_softc *sc)
|
||||
{
|
||||
struct if_ath_ant_comb_state *ss;
|
||||
|
||||
/* Only do this if diversity is enabled */
|
||||
if (! ath_hal_hasdivantcomb(sc->sc_ah))
|
||||
return (0);
|
||||
|
||||
ss = malloc(sizeof(struct if_ath_ant_comb_state),
|
||||
M_TEMP, M_WAITOK | M_ZERO);
|
||||
if (ss == NULL) {
|
||||
device_printf(sc->sc_dev, "%s: failed to allocate\n",
|
||||
__func__);
|
||||
/* Don't fail at this point */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Let's flip this on */
|
||||
sc->sc_lna_div = ss;
|
||||
sc->sc_dolnadiv = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detach the LNA diversity state from the given interface
|
||||
*/
|
||||
int
|
||||
ath_lna_div_detach(struct ath_softc *sc)
|
||||
{
|
||||
if (sc->sc_lna_div != NULL) {
|
||||
free(sc->sc_lna_div, M_TEMP);
|
||||
sc->sc_lna_div = NULL;
|
||||
}
|
||||
sc->sc_dolnadiv = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable LNA diversity on the current channel if it's required.
|
||||
*/
|
||||
int
|
||||
ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ioctl requests from the diagnostic interface.
|
||||
*
|
||||
* The initial part of this code resembles ath_ioctl_diag();
|
||||
* it's likely a good idea to reduce duplication between
|
||||
* these two routines.
|
||||
*/
|
||||
int
|
||||
ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
|
||||
{
|
||||
unsigned int id = ad->ad_id & ATH_DIAG_ID;
|
||||
void *indata = NULL;
|
||||
void *outdata = NULL;
|
||||
u_int32_t insize = ad->ad_in_size;
|
||||
u_int32_t outsize = ad->ad_out_size;
|
||||
int error = 0;
|
||||
// int val;
|
||||
|
||||
if (ad->ad_id & ATH_DIAG_IN) {
|
||||
/*
|
||||
* Copy in data.
|
||||
*/
|
||||
indata = malloc(insize, M_TEMP, M_NOWAIT);
|
||||
if (indata == NULL) {
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
error = copyin(ad->ad_in_data, indata, insize);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
if (ad->ad_id & ATH_DIAG_DYN) {
|
||||
/*
|
||||
* Allocate a buffer for the results (otherwise the HAL
|
||||
* returns a pointer to a buffer where we can read the
|
||||
* results). Note that we depend on the HAL leaving this
|
||||
* pointer for us to use below in reclaiming the buffer;
|
||||
* may want to be more defensive.
|
||||
*/
|
||||
outdata = malloc(outsize, M_TEMP, M_NOWAIT);
|
||||
if (outdata == NULL) {
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
switch (id) {
|
||||
default:
|
||||
error = EINVAL;
|
||||
}
|
||||
if (outsize < ad->ad_out_size)
|
||||
ad->ad_out_size = outsize;
|
||||
if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
|
||||
error = EFAULT;
|
||||
bad:
|
||||
if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
|
||||
free(indata, M_TEMP);
|
||||
if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
|
||||
free(outdata, M_TEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static HAL_BOOL
|
||||
ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
|
||||
int main_rssi_avg, int alt_rssi_avg, int pkt_count)
|
||||
{
|
||||
return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
|
||||
(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
|
||||
}
|
||||
|
||||
static void
|
||||
ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
|
||||
HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
|
||||
{
|
||||
antcomb->quick_scan_cnt = 0;
|
||||
|
||||
if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
|
||||
switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
|
||||
case (0x10): /* LNA2 A-B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
break;
|
||||
case (0x20): /* LNA1 A-B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
break;
|
||||
case (0x21): /* LNA1 LNA2 */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case (0x12): /* LNA2 LNA1 */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case (0x13): /* LNA2 A+B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
break;
|
||||
case (0x23): /* LNA1 A+B */
|
||||
antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
antcomb->first_quick_scan_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
|
||||
HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
|
||||
int alt_rssi_avg, int alt_ratio)
|
||||
{
|
||||
/* alt_good */
|
||||
switch (antcomb->quick_scan_cnt) {
|
||||
case 0:
|
||||
/* set alt to main, and alt to first conf */
|
||||
div_ant_conf->main_lna_conf = antcomb->main_conf;
|
||||
div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
|
||||
break;
|
||||
case 1:
|
||||
/* set alt to main, and alt to first conf */
|
||||
div_ant_conf->main_lna_conf = antcomb->main_conf;
|
||||
div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
|
||||
antcomb->rssi_first = main_rssi_avg;
|
||||
antcomb->rssi_second = alt_rssi_avg;
|
||||
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
|
||||
/* main is LNA1 */
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
} else {
|
||||
if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
|
||||
(alt_rssi_avg > main_rssi_avg)) &&
|
||||
(antcomb->total_pkt_count > 50))
|
||||
antcomb->first_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->first_ratio = AH_FALSE;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
antcomb->alt_good = AH_FALSE;
|
||||
antcomb->scan_not_start = AH_FALSE;
|
||||
antcomb->scan = AH_FALSE;
|
||||
antcomb->rssi_first = main_rssi_avg;
|
||||
antcomb->rssi_third = alt_rssi_avg;
|
||||
|
||||
if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = alt_rssi_avg;
|
||||
else if (antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = alt_rssi_avg;
|
||||
else if (antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
}
|
||||
|
||||
if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
|
||||
div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
|
||||
|
||||
if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
|
||||
if (ath_is_alt_ant_ratio_better(alt_ratio,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
antcomb->total_pkt_count))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
} else {
|
||||
if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
|
||||
(alt_rssi_avg > main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
|
||||
(alt_rssi_avg > main_rssi_avg)) &&
|
||||
(antcomb->total_pkt_count > 50))
|
||||
antcomb->second_ratio = AH_TRUE;
|
||||
else
|
||||
antcomb->second_ratio = AH_FALSE;
|
||||
}
|
||||
|
||||
/* set alt to the conf with maximun ratio */
|
||||
if (antcomb->first_ratio && antcomb->second_ratio) {
|
||||
if (antcomb->rssi_second > antcomb->rssi_third) {
|
||||
/* first alt*/
|
||||
if ((antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2*/
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->first_quick_scan_conf;
|
||||
} else if ((antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)) {
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
} else {
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->second_quick_scan_conf;
|
||||
}
|
||||
} else if (antcomb->first_ratio) {
|
||||
/* first alt */
|
||||
if ((antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->first_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->first_quick_scan_conf;
|
||||
} else if (antcomb->second_ratio) {
|
||||
/* second alt */
|
||||
if ((antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->second_quick_scan_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf =
|
||||
antcomb->second_quick_scan_conf;
|
||||
} else {
|
||||
/* main is largest */
|
||||
if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
|
||||
(antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
|
||||
/* Set alt LNA1 or LNA2 */
|
||||
if (div_ant_conf->main_lna_conf ==
|
||||
HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else
|
||||
div_ant_conf->alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
else
|
||||
/* Set alt to A+B or A-B */
|
||||
div_ant_conf->alt_lna_conf = antcomb->main_conf;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ath_ant_div_conf_fast_divbias(HAL_ANT_COMB_CONFIG *ant_conf)
|
||||
{
|
||||
/* Adjust the fast_div_bias based on main and alt lna conf */
|
||||
switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
|
||||
case (0x01): /* A-B LNA2 */
|
||||
ant_conf->fast_div_bias = 0x3b;
|
||||
break;
|
||||
case (0x02): /* A-B LNA1 */
|
||||
ant_conf->fast_div_bias = 0x3d;
|
||||
break;
|
||||
case (0x03): /* A-B A+B */
|
||||
ant_conf->fast_div_bias = 0x1;
|
||||
break;
|
||||
case (0x10): /* LNA2 A-B */
|
||||
ant_conf->fast_div_bias = 0x7;
|
||||
break;
|
||||
case (0x12): /* LNA2 LNA1 */
|
||||
ant_conf->fast_div_bias = 0x2;
|
||||
break;
|
||||
case (0x13): /* LNA2 A+B */
|
||||
ant_conf->fast_div_bias = 0x7;
|
||||
break;
|
||||
case (0x20): /* LNA1 A-B */
|
||||
ant_conf->fast_div_bias = 0x6;
|
||||
break;
|
||||
case (0x21): /* LNA1 LNA2 */
|
||||
ant_conf->fast_div_bias = 0x0;
|
||||
break;
|
||||
case (0x23): /* LNA1 A+B */
|
||||
ant_conf->fast_div_bias = 0x6;
|
||||
break;
|
||||
case (0x30): /* A+B A-B */
|
||||
ant_conf->fast_div_bias = 0x1;
|
||||
break;
|
||||
case (0x31): /* A+B LNA2 */
|
||||
ant_conf->fast_div_bias = 0x3b;
|
||||
break;
|
||||
case (0x32): /* A+B LNA1 */
|
||||
ant_conf->fast_div_bias = 0x3d;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Antenna diversity and combining */
|
||||
void
|
||||
ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
|
||||
unsigned long ticks, int hz)
|
||||
{
|
||||
HAL_ANT_COMB_CONFIG div_ant_conf;
|
||||
struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
|
||||
int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
|
||||
int curr_main_set, curr_bias;
|
||||
int main_rssi = rs->rs_rssi_ctl[0];
|
||||
int alt_rssi = rs->rs_rssi_ctl[1];
|
||||
int rx_ant_conf, main_ant_conf, alt_ant_conf;
|
||||
HAL_BOOL short_scan = AH_FALSE;
|
||||
|
||||
rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
|
||||
main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
|
||||
alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
|
||||
|
||||
#if 0
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY,
|
||||
"%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
|
||||
"FastDiv: %d\n",
|
||||
__func__,
|
||||
main_rssi,
|
||||
alt_rssi,
|
||||
main_ant_conf,
|
||||
alt_ant_conf,
|
||||
rx_ant_conf,
|
||||
!!(rs->rs_rssi_ctl[2] & 0x80),
|
||||
!!(rs->rs_rssi_ctl[2] & 0x40),
|
||||
!!(rs->rs_rssi_ext[2] & 0x40));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If LNA diversity combining isn't enabled, don't run this.
|
||||
*/
|
||||
if (! sc->sc_dolnadiv)
|
||||
return;
|
||||
|
||||
/*
|
||||
* XXX this is ugly, but the HAL code attaches the
|
||||
* LNA diversity to the TX antenna settings.
|
||||
* I don't know why.
|
||||
*/
|
||||
if (sc->sc_txantenna != HAL_ANT_VARIABLE)
|
||||
return;
|
||||
|
||||
/* Record packet only when alt_rssi is positive */
|
||||
if (main_rssi > 0 && alt_rssi > 0) {
|
||||
antcomb->total_pkt_count++;
|
||||
antcomb->main_total_rssi += main_rssi;
|
||||
antcomb->alt_total_rssi += alt_rssi;
|
||||
if (main_ant_conf == rx_ant_conf)
|
||||
antcomb->main_recv_cnt++;
|
||||
else
|
||||
antcomb->alt_recv_cnt++;
|
||||
}
|
||||
|
||||
/* Short scan check */
|
||||
if (antcomb->scan && antcomb->alt_good) {
|
||||
if (time_after(ticks, antcomb->scan_start_time +
|
||||
msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
|
||||
short_scan = AH_TRUE;
|
||||
else
|
||||
if (antcomb->total_pkt_count ==
|
||||
ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
|
||||
alt_ratio = ((antcomb->alt_recv_cnt * 100) /
|
||||
antcomb->total_pkt_count);
|
||||
if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
|
||||
short_scan = AH_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
|
||||
rs->rs_moreaggr) && !short_scan)
|
||||
return;
|
||||
|
||||
if (antcomb->total_pkt_count) {
|
||||
alt_ratio = ((antcomb->alt_recv_cnt * 100) /
|
||||
antcomb->total_pkt_count);
|
||||
main_rssi_avg = (antcomb->main_total_rssi /
|
||||
antcomb->total_pkt_count);
|
||||
alt_rssi_avg = (antcomb->alt_total_rssi /
|
||||
antcomb->total_pkt_count);
|
||||
}
|
||||
|
||||
OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
|
||||
|
||||
ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
|
||||
curr_alt_set = div_ant_conf.alt_lna_conf;
|
||||
curr_main_set = div_ant_conf.main_lna_conf;
|
||||
curr_bias = div_ant_conf.fast_div_bias;
|
||||
|
||||
antcomb->count++;
|
||||
|
||||
if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
|
||||
if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
|
||||
ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
|
||||
main_rssi_avg);
|
||||
antcomb->alt_good = AH_TRUE;
|
||||
} else {
|
||||
antcomb->alt_good = AH_FALSE;
|
||||
}
|
||||
|
||||
antcomb->count = 0;
|
||||
antcomb->scan = AH_TRUE;
|
||||
antcomb->scan_not_start = AH_TRUE;
|
||||
}
|
||||
|
||||
if (!antcomb->scan) {
|
||||
if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
|
||||
if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
|
||||
/* Switch main and alt LNA */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
|
||||
goto div_comb_done;
|
||||
} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
|
||||
(curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
|
||||
/* Set alt to another LNA */
|
||||
if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
|
||||
goto div_comb_done;
|
||||
}
|
||||
|
||||
if ((alt_rssi_avg < (main_rssi_avg +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
|
||||
goto div_comb_done;
|
||||
}
|
||||
|
||||
if (!antcomb->scan_not_start) {
|
||||
switch (curr_alt_set) {
|
||||
case HAL_ANT_DIV_COMB_LNA2:
|
||||
antcomb->rssi_lna2 = alt_rssi_avg;
|
||||
antcomb->rssi_lna1 = main_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1:
|
||||
antcomb->rssi_lna1 = alt_rssi_avg;
|
||||
antcomb->rssi_lna2 = main_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
|
||||
antcomb->rssi_add = alt_rssi_avg;
|
||||
antcomb->scan = AH_TRUE;
|
||||
/* set to A-B */
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
break;
|
||||
case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
|
||||
antcomb->rssi_sub = alt_rssi_avg;
|
||||
antcomb->scan = AH_FALSE;
|
||||
if (antcomb->rssi_lna2 >
|
||||
(antcomb->rssi_lna1 +
|
||||
ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
|
||||
/* use LNA2 as main LNA */
|
||||
if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
|
||||
(antcomb->rssi_add > antcomb->rssi_sub)) {
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
} else if (antcomb->rssi_sub >
|
||||
antcomb->rssi_lna1) {
|
||||
/* set to A-B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
} else {
|
||||
/* set to LNA1 */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
}
|
||||
} else {
|
||||
/* use LNA1 as main LNA */
|
||||
if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
|
||||
(antcomb->rssi_add > antcomb->rssi_sub)) {
|
||||
/* set to A+B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
|
||||
} else if (antcomb->rssi_sub >
|
||||
antcomb->rssi_lna1) {
|
||||
/* set to A-B */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
|
||||
} else {
|
||||
/* set to LNA2 */
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!antcomb->alt_good) {
|
||||
antcomb->scan_not_start = AH_FALSE;
|
||||
/* Set alt to another LNA */
|
||||
if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
|
||||
div_ant_conf.main_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA1;
|
||||
div_ant_conf.alt_lna_conf =
|
||||
HAL_ANT_DIV_COMB_LNA2;
|
||||
}
|
||||
goto div_comb_done;
|
||||
}
|
||||
}
|
||||
|
||||
ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
|
||||
main_rssi_avg, alt_rssi_avg,
|
||||
alt_ratio);
|
||||
|
||||
antcomb->quick_scan_cnt++;
|
||||
|
||||
div_comb_done:
|
||||
ath_ant_div_conf_fast_divbias(&div_ant_conf);
|
||||
|
||||
ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
|
||||
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
|
||||
__func__, antcomb->total_pkt_count);
|
||||
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
|
||||
__func__, antcomb->main_total_rssi);
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
|
||||
__func__, antcomb->alt_total_rssi);
|
||||
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
|
||||
__func__, main_rssi_avg);
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
|
||||
__func__, alt_rssi_avg);
|
||||
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
|
||||
__func__, antcomb->main_recv_cnt);
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
|
||||
__func__, antcomb->alt_recv_cnt);
|
||||
|
||||
// if (curr_alt_set != div_ant_conf.alt_lna_conf)
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
|
||||
__func__, curr_alt_set, div_ant_conf.alt_lna_conf);
|
||||
// if (curr_main_set != div_ant_conf.main_lna_conf)
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
|
||||
__func__, curr_main_set, div_ant_conf.main_lna_conf);
|
||||
// if (curr_bias != div_ant_conf.fast_div_bias)
|
||||
DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
|
||||
__func__, curr_bias, div_ant_conf.fast_div_bias);
|
||||
|
||||
antcomb->scan_start_time = ticks;
|
||||
antcomb->total_pkt_count = 0;
|
||||
antcomb->main_total_rssi = 0;
|
||||
antcomb->alt_total_rssi = 0;
|
||||
antcomb->main_recv_cnt = 0;
|
||||
antcomb->alt_recv_cnt = 0;
|
||||
}
|
||||
|
89
sys/dev/ath/if_ath_lna_div.h
Normal file
89
sys/dev/ath/if_ath_lna_div.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
|
||||
* redistribution must be conditioned upon including a substantially
|
||||
* similar Disclaimer requirement for further binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef __IF_ATH_LNA_DIV_H__
|
||||
#define __IF_ATH_LNA_DIV_H__
|
||||
|
||||
#define ATH_ANT_RX_CURRENT_SHIFT 4
|
||||
#define ATH_ANT_RX_MAIN_SHIFT 2
|
||||
#define ATH_ANT_RX_MASK 0x3
|
||||
|
||||
#define ATH_ANT_DIV_COMB_SHORT_SCAN_INTR 50
|
||||
#define ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT 0x100
|
||||
#define ATH_ANT_DIV_COMB_MAX_PKTCOUNT 0x200
|
||||
#define ATH_ANT_DIV_COMB_INIT_COUNT 95
|
||||
#define ATH_ANT_DIV_COMB_MAX_COUNT 100
|
||||
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
|
||||
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
|
||||
|
||||
#define ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA -3
|
||||
#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2
|
||||
#define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2
|
||||
|
||||
struct if_ath_ant_comb_state {
|
||||
uint16_t count;
|
||||
uint16_t total_pkt_count;
|
||||
HAL_BOOL scan;
|
||||
HAL_BOOL scan_not_start;
|
||||
int main_total_rssi;
|
||||
int alt_total_rssi;
|
||||
int alt_recv_cnt;
|
||||
int main_recv_cnt;
|
||||
int rssi_lna1;
|
||||
int rssi_lna2;
|
||||
int rssi_add;
|
||||
int rssi_sub;
|
||||
int rssi_first;
|
||||
int rssi_second;
|
||||
int rssi_third;
|
||||
HAL_BOOL alt_good;
|
||||
int quick_scan_cnt;
|
||||
int main_conf;
|
||||
HAL_ANT_DIV_COMB_LNA_CONF first_quick_scan_conf;
|
||||
HAL_ANT_DIV_COMB_LNA_CONF second_quick_scan_conf;
|
||||
int first_bias;
|
||||
int second_bias;
|
||||
HAL_BOOL first_ratio;
|
||||
HAL_BOOL second_ratio;
|
||||
unsigned long scan_start_time;
|
||||
};
|
||||
|
||||
extern int ath_lna_div_attach(struct ath_softc *sc);
|
||||
extern int ath_lna_div_detach(struct ath_softc *sc);
|
||||
extern int ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad);
|
||||
extern int ath_lna_div_enable(struct ath_softc *sc,
|
||||
const struct ieee80211_channel *ch);
|
||||
|
||||
extern void ath_lna_rx_comb_scan(struct ath_softc *sc,
|
||||
struct ath_rx_status *rs, unsigned long ticks, int hz);
|
||||
|
||||
#endif /* __IF_ATH_LNA_DIV_H__ */
|
@ -119,6 +119,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/ath/if_ath_alq.h>
|
||||
#endif
|
||||
|
||||
#include <dev/ath/if_ath_lna_div.h>
|
||||
|
||||
/*
|
||||
* Calculate the receive filter according to the
|
||||
* operating mode and state:
|
||||
@ -516,7 +518,6 @@ ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
|
||||
uint64_t tsf, int nf, HAL_RX_QUEUE qtype, struct ath_buf *bf,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct ath_hal *ah = sc->sc_ah;
|
||||
uint64_t rstamp;
|
||||
int len, type;
|
||||
struct ifnet *ifp = sc->sc_ifp;
|
||||
@ -843,10 +844,10 @@ rx_accept:
|
||||
sc->sc_rxotherant = 0;
|
||||
}
|
||||
|
||||
/* Newer school diversity - kite specific for now */
|
||||
/* XXX perhaps migrate the normal diversity code to this? */
|
||||
if ((ah)->ah_rxAntCombDiversity)
|
||||
(*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz);
|
||||
/* Handle slow diversity if enabled */
|
||||
if (sc->sc_dolnadiv) {
|
||||
ath_lna_rx_comb_scan(sc, rs, ticks, hz);
|
||||
}
|
||||
|
||||
if (sc->sc_softled) {
|
||||
/*
|
||||
|
@ -630,6 +630,7 @@ struct ath_softc {
|
||||
sc_rx_stbc : 1,
|
||||
sc_tx_stbc : 1,
|
||||
sc_hasenforcetxop : 1, /* support enforce TxOP */
|
||||
sc_hasdivcomb : 1, /* RX diversity combining */
|
||||
sc_rx_lnamixer : 1; /* RX using LNA mixing */
|
||||
|
||||
int sc_cabq_enable; /* Enable cabq transmission */
|
||||
@ -841,6 +842,10 @@ struct ath_softc {
|
||||
void *sc_spectral;
|
||||
int sc_dospectral;
|
||||
|
||||
/* LNA diversity related state */
|
||||
void *sc_lna_div;
|
||||
int sc_dolnadiv;
|
||||
|
||||
/* ALQ */
|
||||
#ifdef ATH_DEBUG_ALQ
|
||||
struct if_ath_alq sc_alq;
|
||||
@ -1272,6 +1277,9 @@ void ath_intr(void *);
|
||||
#define ath_hal_hasrxlnamixer(_ah) \
|
||||
(ath_hal_getcapability(_ah, HAL_CAP_RX_LNA_MIXING, 0, NULL) == HAL_OK)
|
||||
|
||||
#define ath_hal_hasdivantcomb(_ah) \
|
||||
(ath_hal_getcapability(_ah, HAL_CAP_ANT_DIV_COMB, 0, NULL) == HAL_OK)
|
||||
|
||||
/* EDMA definitions */
|
||||
#define ath_hal_hasedma(_ah) \
|
||||
(ath_hal_getcapability(_ah, HAL_CAP_ENHANCED_DMA_SUPPORT, \
|
||||
@ -1458,4 +1466,9 @@ void ath_intr(void *);
|
||||
#define ath_hal_btcoex_disable(_ah) \
|
||||
((*(_ah)->ah_btCoexDisable)((_ah)))
|
||||
|
||||
#define ath_hal_div_comb_conf_get(_ah, _conf) \
|
||||
((*(_ah)->ah_divLnaConfGet)((_ah), (_conf)))
|
||||
#define ath_hal_div_comb_conf_set(_ah, _conf) \
|
||||
((*(_ah)->ah_divLnaConfSet)((_ah), (_conf)))
|
||||
|
||||
#endif /* _DEV_ATH_ATHVAR_H */
|
||||
|
@ -38,7 +38,7 @@ KMOD= if_ath
|
||||
SRCS= if_ath.c if_ath_alq.c if_ath_debug.c if_ath_keycache.c if_ath_sysctl.c
|
||||
SRCS+= if_ath_tx.c if_ath_tx_ht.c if_ath_led.c if_ath_rx.c if_ath_tdma.c
|
||||
SRCS+= if_ath_beacon.c if_ath_rx_edma.c if_ath_tx_edma.c if_ath_spectral.c
|
||||
SRCS+= if_ath_btcoex.c
|
||||
SRCS+= if_ath_btcoex.c if_ath_lna_div.c
|
||||
# NB: v3 eeprom support used by both AR5211 and AR5212; just include it
|
||||
SRCS+= ah_osdep.c ah.c ah_regdomain.c ah_eeprom_v3.c
|
||||
SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h
|
||||
|
Loading…
Reference in New Issue
Block a user