Index: stable/10/sys/dev/ath/if_ath_btcoex.c =================================================================== --- stable/10/sys/dev/ath/if_ath_btcoex.c (revision 332319) +++ stable/10/sys/dev/ath/if_ath_btcoex.c (revision 332320) @@ -1,345 +1,346 @@ /*- * Copyright (c) 2013 Adrian Chadd * 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 __FBSDID("$FreeBSD$"); /* * This implements some very basic bluetooth coexistence methods for * the ath(4) hardware. */ #include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include /* * Initial AR9285 / (WB195) bluetooth coexistence settings, * just for experimentation. * * Return 0 for OK; errno for error. * * XXX TODO: There needs to be a PCIe workaround to disable ASPM if * bluetooth coexistence is enabled. */ static int ath_btcoex_cfg_wb195(struct ath_softc *sc) { HAL_BT_COEX_INFO btinfo; HAL_BT_COEX_CONFIG btconfig; struct ath_hal *ah = sc->sc_ah; if (! ath_hal_btcoex_supported(ah)) return (EINVAL); bzero(&btinfo, sizeof(btinfo)); bzero(&btconfig, sizeof(btconfig)); device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n"); btinfo.bt_module = HAL_BT_MODULE_JANUS; btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE; /* * These are the three GPIO pins hooked up between the AR9285 and * the AR3011. */ btinfo.bt_gpio_bt_active = 6; btinfo.bt_gpio_bt_priority = 7; btinfo.bt_gpio_wlan_active = 5; btinfo.bt_active_polarity = 1; /* XXX not used */ btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */ btinfo.bt_isolation = 0; /* in dB, not used */ ath_hal_btcoex_set_info(ah, &btinfo); btconfig.bt_time_extend = 0; btconfig.bt_txstate_extend = 1; /* true */ btconfig.bt_txframe_extend = 1; /* true */ btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; btconfig.bt_quiet_collision = 1; /* true */ btconfig.bt_rxclear_polarity = 1; /* true */ btconfig.bt_priority_time = 2; btconfig.bt_first_slot_time = 5; btconfig.bt_hold_rxclear = 1; /* true */ ath_hal_btcoex_set_config(ah, &btconfig); /* * Enable antenna diversity. */ ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1); return (0); } /* * Initial AR9485 / (WB225) bluetooth coexistence settings, * just for experimentation. * * Return 0 for OK; errno for error. */ static int ath_btcoex_cfg_wb225(struct ath_softc *sc) { HAL_BT_COEX_INFO btinfo; HAL_BT_COEX_CONFIG btconfig; struct ath_hal *ah = sc->sc_ah; if (! ath_hal_btcoex_supported(ah)) return (EINVAL); bzero(&btinfo, sizeof(btinfo)); bzero(&btconfig, sizeof(btconfig)); device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n"); btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */ btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE; /* * These are the three GPIO pins hooked up between the AR9485 and * the bluetooth module. */ btinfo.bt_gpio_bt_active = 4; btinfo.bt_gpio_bt_priority = 8; btinfo.bt_gpio_wlan_active = 5; btinfo.bt_active_polarity = 1; /* XXX not used */ btinfo.bt_single_ant = 1; /* 1 antenna on ar9285 ? */ btinfo.bt_isolation = 0; /* in dB, not used */ ath_hal_btcoex_set_info(ah, &btinfo); btconfig.bt_time_extend = 0; btconfig.bt_txstate_extend = 1; /* true */ btconfig.bt_txframe_extend = 1; /* true */ btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; btconfig.bt_quiet_collision = 1; /* true */ btconfig.bt_rxclear_polarity = 1; /* true */ btconfig.bt_priority_time = 2; btconfig.bt_first_slot_time = 5; btconfig.bt_hold_rxclear = 1; /* true */ ath_hal_btcoex_set_config(ah, &btconfig); /* * Enable antenna diversity. */ ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1); return (0); } #if 0 /* * When using bluetooth coexistence, ASPM needs to be disabled * otherwise the sleeping interferes with the bluetooth (USB) * operation and the MAC sleep/wakeup hardware. * * The PCIe powersave routine also needs to not be called * by the driver during suspend/resume, else things will get * a little odd. Check Linux ath9k for more details. */ static int ath_btcoex_aspm_wb195(struct ath_softc *sc) { /* XXX TODO: clear device ASPM L0S and L1 */ /* XXX TODO: clear _parent_ ASPM L0S and L1 */ } #endif /* * Methods which are required */ /* * Attach btcoex to the given interface */ int ath_btcoex_attach(struct ath_softc *sc) { int ret; struct ath_hal *ah = sc->sc_ah; const char *profname; /* * No chipset bluetooth coexistence? Then do nothing. */ if (! ath_hal_btcoex_supported(ah)) return (0); /* * Look at the hints to determine which bluetooth * profile to configure. */ ret = resource_string_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "btcoex_profile", &profname); if (ret != 0) { /* nothing to do */ return (0); } if (strncmp(profname, "wb195", 5) == 0) { ret = ath_btcoex_cfg_wb195(sc); } else if (strncmp(profname, "wb225", 5) == 0) { ret = ath_btcoex_cfg_wb225(sc); } else { return (0); } /* * Propagate up failure from the actual attach phase. */ if (ret != 0) return (ret); return (0); } /* * Detach btcoex from the given interface */ int ath_btcoex_detach(struct ath_softc *sc) { return (0); } /* * Configure or disable bluetooth coexistence on the given channel. * * For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just * assume bluetooth coexistence is always on. * * For AR9462, we may see a 5GHz channel; bluetooth coexistence should * not be enabled on those channels. */ int ath_btcoex_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_btcoex_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); + outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO); if (outdata == NULL) { error = ENOMEM; goto bad; } } switch (id) { default: error = EINVAL; + goto bad; } 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); } Index: stable/10/sys/dev/ath/if_ath_lna_div.c =================================================================== --- stable/10/sys/dev/ath/if_ath_lna_div.c (revision 332319) +++ stable/10/sys/dev/ath/if_ath_lna_div.c (revision 332320) @@ -1,1012 +1,1013 @@ /*- * Copyright (c) 2013 Adrian Chadd * 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 __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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include /* 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; HAL_ANT_COMB_CONFIG div_ant_conf; /* 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); } /* Fetch the hardware configuration */ OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf)); ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf); /* Figure out what the hardware specific bits should be */ if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) || (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) { ss->lna1_lna2_delta = -9; } else { ss->lna1_lna2_delta = -3; } /* 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); + outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO); if (outdata == NULL) { error = ENOMEM; goto bad; } } switch (id) { default: error = EINVAL; + goto bad; } 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_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb, int alt_ratio, int alt_ant_ratio_th, u_int config_group, HAL_ANT_COMB_CONFIG *pdiv_ant_conf) { if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) { switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x10): //LNA2 A-B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x13): //LNA2 A+B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x20): //LNA1 A-B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x23): //LNA1 A+B if ((antcomb->scan == 0) && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { pdiv_ant_conf->fast_div_bias = 0x3f; } else { pdiv_ant_conf->fast_div_bias = 0x1; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; default: break; } } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) { switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x10): //LNA2 A-B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x13): //LNA2 A+B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x20): //LNA1 A-B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x23): //LNA1 A+B if ((antcomb->scan == 0) && (alt_ratio > alt_ant_ratio_th)) { pdiv_ant_conf->fast_div_bias = 0x1; } else { pdiv_ant_conf->fast_div_bias = 0x2; } pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x1; pdiv_ant_conf->main_gaintb = 0; pdiv_ant_conf->alt_gaintb = 0; break; default: break; } } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */ switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) { case (0x01): //A-B LNA2 pdiv_ant_conf->fast_div_bias = 0x3b; break; case (0x02): //A-B LNA1 pdiv_ant_conf->fast_div_bias = 0x3d; break; case (0x03): //A-B A+B pdiv_ant_conf->fast_div_bias = 0x1; break; case (0x10): //LNA2 A-B pdiv_ant_conf->fast_div_bias = 0x7; break; case (0x12): //LNA2 LNA1 pdiv_ant_conf->fast_div_bias = 0x2; break; case (0x13): //LNA2 A+B pdiv_ant_conf->fast_div_bias = 0x7; break; case (0x20): //LNA1 A-B pdiv_ant_conf->fast_div_bias = 0x6; break; case (0x21): //LNA1 LNA2 pdiv_ant_conf->fast_div_bias = 0x0; break; case (0x23): //LNA1 A+B pdiv_ant_conf->fast_div_bias = 0x6; break; case (0x30): //A+B A-B pdiv_ant_conf->fast_div_bias = 0x1; break; case (0x31): //A+B LNA2 pdiv_ant_conf->fast_div_bias = 0x3b; break; case (0x32): //A+B LNA1 pdiv_ant_conf->fast_div_bias = 0x3d; break; default: break; } } } /* * AR9485/AR933x TODO: * + Select a ratio based on whether RSSI is low or not; but I need * to figure out what "low_rssi_th" is sourced from. * + What's ath_ant_div_comb_alt_check() in the reference driver do? * + .. and there's likely a bunch of other things to include in this. */ /* 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 0 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total pkt=%d, aggr=%d, short_scan=%d\n", __func__, antcomb->total_pkt_count, !! (rs->rs_moreaggr), !! (short_scan)); #endif 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 + antcomb->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: #if 0 ath_ant_div_conf_fast_divbias(&div_ant_conf); #endif ath_ant_adjust_fast_divbias(antcomb, alt_ratio, ATH_ANT_DIV_COMB_ALT_ANT_RATIO, div_ant_conf.antdiv_configgroup, &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; } Index: stable/10/sys/dev/ath/if_ath_spectral.c =================================================================== --- stable/10/sys/dev/ath/if_ath_spectral.c (revision 332319) +++ stable/10/sys/dev/ath/if_ath_spectral.c (revision 332320) @@ -1,288 +1,289 @@ /*- * Copyright (c) 2013 Adrian Chadd * 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 __FBSDID("$FreeBSD$"); /* * Implement some basic spectral scan control logic. */ #include "opt_ath.h" #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include struct ath_spectral_state { HAL_SPECTRAL_PARAM spectral_state; /* * Should we enable spectral scan upon * each network interface reset/change? * * This is intended to allow spectral scan * frame reporting during channel scans. * * Later on it can morph into a larger * scale config method where it pushes * a "channel scan" config into the hardware * rather than just the spectral_state * config. */ int spectral_enable_after_reset; }; /* * Methods which are required */ /* * Attach spectral to the given interface */ int ath_spectral_attach(struct ath_softc *sc) { struct ath_spectral_state *ss; /* * If spectral isn't supported, don't error - just * quietly complete. */ if (! ath_hal_spectral_supported(sc->sc_ah)) return (0); ss = malloc(sizeof(struct ath_spectral_state), M_TEMP, M_WAITOK | M_ZERO); if (ss == NULL) { device_printf(sc->sc_dev, "%s: failed to alloc memory\n", __func__); return (-ENOMEM); } sc->sc_spectral = ss; (void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state); return (0); } /* * Detach spectral from the given interface */ int ath_spectral_detach(struct ath_softc *sc) { if (! ath_hal_spectral_supported(sc->sc_ah)) return (0); if (sc->sc_spectral != NULL) { free(sc->sc_spectral, M_TEMP); } return (0); } /* * Check whether spectral needs enabling and if so, * flip it on. */ int ath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch) { struct ath_spectral_state *ss = sc->sc_spectral; /* Default to disable spectral PHY reporting */ sc->sc_dospectral = 0; if (ss == NULL) return (0); if (ss->spectral_enable_after_reset) { ath_hal_spectral_configure(sc->sc_ah, &ss->spectral_state); (void) ath_hal_spectral_start(sc->sc_ah); sc->sc_dospectral = 1; } 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_ioctl_spectral(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; HAL_SPECTRAL_PARAM peout; HAL_SPECTRAL_PARAM *pe; struct ath_spectral_state *ss = sc->sc_spectral; int val; if (! ath_hal_spectral_supported(sc->sc_ah)) return (EINVAL); 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); + outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO); if (outdata == NULL) { error = ENOMEM; goto bad; } } switch (id) { case SPECTRAL_CONTROL_GET_PARAMS: memset(&peout, 0, sizeof(peout)); outsize = sizeof(HAL_SPECTRAL_PARAM); ath_hal_spectral_get_config(sc->sc_ah, &peout); pe = (HAL_SPECTRAL_PARAM *) outdata; memcpy(pe, &peout, sizeof(*pe)); break; case SPECTRAL_CONTROL_SET_PARAMS: if (insize < sizeof(HAL_SPECTRAL_PARAM)) { error = EINVAL; break; } pe = (HAL_SPECTRAL_PARAM *) indata; ath_hal_spectral_configure(sc->sc_ah, pe); /* Save a local copy of the updated parameters */ ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state); break; case SPECTRAL_CONTROL_START: ath_hal_spectral_configure(sc->sc_ah, &ss->spectral_state); (void) ath_hal_spectral_start(sc->sc_ah); sc->sc_dospectral = 1; /* XXX need to update the PHY mask in the driver */ break; case SPECTRAL_CONTROL_STOP: (void) ath_hal_spectral_stop(sc->sc_ah); sc->sc_dospectral = 0; /* XXX need to update the PHY mask in the driver */ break; case SPECTRAL_CONTROL_ENABLE_AT_RESET: if (insize < sizeof(int)) { device_printf(sc->sc_dev, "%d != %d\n", insize, (int) sizeof(int)); error = EINVAL; break; } if (indata == NULL) { device_printf(sc->sc_dev, "indata=NULL\n"); error = EINVAL; break; } val = * ((int *) indata); if (val == 0) ss->spectral_enable_after_reset = 0; else ss->spectral_enable_after_reset = 1; break; case SPECTRAL_CONTROL_ENABLE: /* XXX TODO */ case SPECTRAL_CONTROL_DISABLE: /* XXX TODO */ break; default: error = EINVAL; + goto bad; } 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); } Index: stable/10 =================================================================== --- stable/10 (revision 332319) +++ stable/10 (revision 332320) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r327499-327500