diff --git a/sys/dev/ath/if_ath_lna_div.c b/sys/dev/ath/if_ath_lna_div.c index 1b20591fc64e..0755bb667716 100644 --- a/sys/dev/ath/if_ath_lna_div.c +++ b/sys/dev/ath/if_ath_lna_div.c @@ -1,1016 +1,1010 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #include /* * 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 #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #include #include /* Linux compatibility 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 | 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); } /* * XXX need to low_rssi_thresh config from ath9k, to support CUS198 * antenna diversity correctly. */ 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 (ieee80211_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; } diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c index 72f0a802aa5f..a242eab7a694 100644 --- a/sys/dev/ath/if_ath_pci.c +++ b/sys/dev/ath/if_ath_pci.c @@ -1,397 +1,392 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * 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. */ #include /* * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver. */ #include "opt_ath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include #include #endif /* ATH_EEPROM_FIRMWARE */ /* * PCI glue. */ struct ath_pci_softc { struct ath_softc sc_sc; struct resource *sc_sr; /* memory resource */ struct resource *sc_irq; /* irq resource */ void *sc_ih; /* interrupt handler */ }; #define PCI_VDEVICE(v, d) \ PCI_DEV(v,d) #define PCI_DEVICE_SUB(v, d, sv, sd) \ PCI_DEV(v, d), PCI_SUBDEV(sv, sd) #define PCI_VENDOR_ID_ATHEROS 0x168c #define PCI_VENDOR_ID_SAMSUNG 0x144d #define PCI_VENDOR_ID_AZWAVE 0x1a3b #define PCI_VENDOR_ID_FOXCONN 0x105b #define PCI_VENDOR_ID_ATTANSIC 0x1969 #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_VENDOR_ID_DELL 0x1028 #define PCI_VENDOR_ID_QMI 0x1a32 #define PCI_VENDOR_ID_LENOVO 0x17aa #define PCI_VENDOR_ID_HP 0x103c #include "if_ath_pci_devlist.h" #define BS_BAR 0x10 #define PCIR_RETRY_TIMEOUT 0x41 #define PCIR_CFG_PMCSR 0x48 #define DEFAULT_CACHESIZE 32 static void ath_pci_setup(device_t dev) { uint8_t cz; /* XXX TODO: need to override the _system_ saved copies of this */ /* * If the cache line size is 0, force it to a reasonable * value. */ cz = pci_read_config(dev, PCIR_CACHELNSZ, 1); if (cz == 0) { pci_write_config(dev, PCIR_CACHELNSZ, DEFAULT_CACHESIZE / 4, 1); } /* Override the system latency timer */ pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); /* If a PCI NIC, force wakeup */ #ifdef ATH_PCI_WAKEUP_WAR /* XXX TODO: don't do this for non-PCI (ie, PCIe, Cardbus!) */ if (1) { uint16_t pmcsr; pmcsr = pci_read_config(dev, PCIR_CFG_PMCSR, 2); pmcsr |= 3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); pmcsr &= ~3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); } #endif /* * Disable retry timeout to keep PCI Tx retries from * interfering with C3 CPU state. */ pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1); } static int ath_pci_probe(device_t dev) { const char* devname; devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev)); if (devname != NULL) { device_set_desc(dev, devname); return BUS_PROBE_DEFAULT; } return ENXIO; } static int ath_pci_attach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; int error = ENXIO; int rid; #ifdef ATH_EEPROM_FIRMWARE const struct firmware *fw = NULL; const char *buf; #endif const struct pci_device_table *pd; sc->sc_dev = dev; /* Do this lookup anyway; figure out what to do with it later */ pd = PCI_MATCH(dev, ath_pci_id_table); if (pd) sc->sc_pci_devinfo = pd->driver_data; /* * Enable bus mastering. */ pci_enable_busmaster(dev); /* * Setup other PCI bus configuration parameters. */ ath_pci_setup(dev); /* * Setup memory-mapping of PCI registers. */ rid = BS_BAR; psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (psc->sc_sr == NULL) { device_printf(dev, "cannot map register space\n"); goto bad; } sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) * that arrive before the HAL is setup are discarded. */ sc->sc_invalid = 1; ATH_LOCK_INIT(sc); ATH_PCU_LOCK_INIT(sc); ATH_RX_LOCK_INIT(sc); ATH_TX_LOCK_INIT(sc); ATH_TXSTATUS_LOCK_INIT(sc); /* * Arrange interrupt line. */ rid = 0; psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (psc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto bad1; } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ath_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } /* * Setup DMA descriptor area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ ATH_MAX_SCATTER, /* nsegments */ 0x3ffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto bad3; } #ifdef ATH_EEPROM_FIRMWARE /* * If there's an EEPROM firmware image, load that in. */ if (resource_string_value(device_get_name(dev), device_get_unit(dev), "eeprom_firmware", &buf) == 0) { if (bootverbose) device_printf(dev, "%s: looking up firmware @ '%s'\n", __func__, buf); fw = firmware_get(buf); if (fw == NULL) { device_printf(dev, "%s: couldn't find firmware\n", __func__); goto bad4; } device_printf(dev, "%s: EEPROM firmware @ %p\n", __func__, fw->data); sc->sc_eepromdata = malloc(fw->datasize, M_TEMP, M_WAITOK | M_ZERO); - if (! sc->sc_eepromdata) { - device_printf(dev, "%s: can't malloc eepromdata\n", - __func__); - goto bad4; - } memcpy(sc->sc_eepromdata, fw->data, fw->datasize); firmware_put(fw, 0); } #endif /* ATH_EEPROM_FIRMWARE */ error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; #ifdef ATH_EEPROM_FIRMWARE bad4: #endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_RX_LOCK_DESTROY(sc); ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); bad: return (error); } static int ath_pci_detach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; /* check if device was removed */ sc->sc_invalid = !bus_child_present(dev); /* * Do a config read to clear pre-existing pci error status. */ (void) pci_read_config(dev, PCIR_COMMAND, 4); ath_detach(sc); bus_generic_detach(dev); bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_RX_LOCK_DESTROY(sc); ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); } static int ath_pci_shutdown(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_shutdown(&psc->sc_sc); return (0); } static int ath_pci_suspend(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_suspend(&psc->sc_sc); return (0); } static int ath_pci_resume(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); /* * Suspend/resume resets the PCI configuration space. */ ath_pci_setup(dev); ath_resume(&psc->sc_sc); return (0); } static device_method_t ath_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ath_pci_probe), DEVMETHOD(device_attach, ath_pci_attach), DEVMETHOD(device_detach, ath_pci_detach), DEVMETHOD(device_shutdown, ath_pci_shutdown), DEVMETHOD(device_suspend, ath_pci_suspend), DEVMETHOD(device_resume, ath_pci_resume), { 0,0 } }; static driver_t ath_pci_driver = { "ath", ath_pci_methods, sizeof (struct ath_pci_softc) }; DRIVER_MODULE(if_ath_pci, pci, ath_pci_driver, 0, 0); MODULE_VERSION(if_ath_pci, 1); MODULE_DEPEND(if_ath_pci, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(if_ath_pci, ath_main, 1, 1, 1); /* if_ath driver */ MODULE_DEPEND(if_ath_pci, ath_hal, 1, 1, 1); /* ath HAL */ diff --git a/sys/dev/ath/if_ath_spectral.c b/sys/dev/ath/if_ath_spectral.c index 58f21b526e93..951d66605981 100644 --- a/sys/dev/ath/if_ath_spectral.c +++ b/sys/dev/ath/if_ath_spectral.c @@ -1,297 +1,290 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #include /* * 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 #include #include /* XXX for ether_sprintf */ #include #include #ifdef INET #include #include #endif #include #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); ATH_LOCK(sc); ath_power_set_power_state(sc, HAL_PM_AWAKE); ATH_UNLOCK(sc); 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 | 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); ATH_LOCK(sc); ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return (error); }