Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f8e077c authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville
Browse files

ath9k: simplify regulatory code



Now that cfg80211 has its own regulatory infrastructure we can
condense ath9k's regulatory code considerably. We only keep data
we need to provide our own regulatory_hint(), reg_notifier() and
information necessary for calibration.

Atheros hardware supports 12 world regulatory domains, since these
are custom we apply them through the the new wiphy_apply_custom_regulatory().
Although we have 12 we can consolidate these into 5 structures based on
frequency and apply a different set of flags that differentiate them on
a case by case basis through the reg_notifier().

If CRDA is not found our own custom world regulatory domain is applied,
this is identical to cfg80211's except we enable passive scan on most
frequencies.

Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 24ed1da1
Loading
Loading
Loading
Loading
+13 −31
Original line number Diff line number Diff line
@@ -457,22 +457,12 @@ struct ath9k_channel {
	struct ieee80211_channel *chan;
	u16 channel;
	u32 channelFlags;
	u8 privFlags;
	int8_t maxRegTxPower;
	int8_t maxTxPower;
	int8_t minTxPower;
	u32 chanmode;
	int32_t CalValid;
	bool oneTimeCalsDone;
	int8_t iCoff;
	int8_t qCoff;
	int16_t rawNoiseFloor;
	int8_t antennaMax;
	u32 regDmnFlags;
	u32 conformanceTestLimit[3]; /* 0:11a, 1: 11b, 2:11g */
#ifdef ATH_NF_PER_CHAN
	struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
#endif
};

#define IS_CHAN_A(_c) ((((_c)->channelFlags & CHANNEL_A) == CHANNEL_A) || \
@@ -500,7 +490,6 @@ struct ath9k_channel {
			  ((_c)->chanmode == CHANNEL_G_HT40MINUS))
#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))

#define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990)
#define IS_CHAN_A_5MHZ_SPACED(_c)			\
	((((_c)->channelFlags & CHANNEL_5GHZ) != 0) &&	\
	 (((_c)->channel % 20) != 0) &&			\
@@ -790,15 +779,13 @@ struct ath_hal {
	u16 ah_currentRD;
	u16 ah_currentRDExt;
	u16 ah_currentRDInUse;
	u16 ah_currentRD5G;
	u16 ah_currentRD2G;
	char ah_iso[4];
	char alpha2[2];
	struct reg_dmn_pair_mapping *regpair;
	enum ath9k_power_mode ah_power_mode;
	enum ath9k_power_mode ah_restore_mode;

	struct ath9k_channel ah_channels[150];
	struct ath9k_channel ah_channels[38];
	struct ath9k_channel *ah_curchan;
	u32 ah_nchan;

	bool ah_isPciExpress;
	u16 ah_txTrigLevel;
@@ -807,10 +794,7 @@ struct ath_hal {
	u32 ah_rfkill_polarity;
	u32 ah_btactive_gpio;
	u32 ah_wlanactive_gpio;

#ifndef ATH_NF_PER_CHAN
	struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
#endif

	bool sw_mgmt_crypto;
};
@@ -825,8 +809,6 @@ struct ath_rate_table;

/* Helpers */

enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah,
			       const struct ath9k_channel *chan);
bool ath9k_hw_wait(struct ath_hal *ah, u32 reg, u32 mask, u32 val);
u32 ath9k_hw_reverse_bits(u32 val, u32 n);
bool ath9k_get_channel_edges(struct ath_hal *ah,
@@ -836,7 +818,6 @@ u16 ath9k_hw_computetxtime(struct ath_hal *ah,
			   struct ath_rate_table *rates,
			   u32 frameLen, u16 rateix,
			   bool shortPreamble);
u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags);
void ath9k_hw_get_channel_centers(struct ath_hal *ah,
				  struct ath9k_channel *chan,
				  struct chan_centers *centers);
@@ -924,17 +905,18 @@ bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us);
void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode);

/* Regulatory */
u16 ath9k_regd_get_rd(struct ath_hal *ah);
bool ath9k_is_world_regd(struct ath_hal *ah);
const struct ieee80211_regdomain *ath9k_world_regdomain(struct ath_hal *ah);
const struct ieee80211_regdomain *ath9k_default_world_regdomain(void);

void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby);
void ath9k_reg_apply_radar_flags(struct wiphy *wiphy);

bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah);
struct ath9k_channel* ath9k_regd_check_channel(struct ath_hal *ah,
			 const struct ath9k_channel *c);
int ath9k_regd_init(struct ath_hal *ah);
bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah);
u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan);
u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
				   struct ath9k_channel *chan);
bool ath9k_regd_init_channels(struct ath_hal *ah,
			      u32 maxchans, u32 *nchans, u8 *regclassids,
			      u32 maxregids, u32 *nregids, u16 cc,
			      bool enableOutdoor, bool enableExtendedChannels);
int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request);

/* ANI */

+7 −34
Original line number Diff line number Diff line
@@ -625,11 +625,7 @@ void ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
	else
		chainmask = 0x3F;

#ifdef ATH_NF_PER_CHAN
	h = chan->nfCalHist;
#else
	h = ah->nfCalHist;
#endif

	for (i = 0; i < NUM_NF_READINGS; i++) {
		if (chainmask & (1 << i)) {
@@ -697,11 +693,7 @@ int16_t ath9k_hw_getnf(struct ath_hal *ah,
		}
	}

#ifdef ATH_NF_PER_CHAN
	h = chan->nfCalHist;
#else
	h = ah->nfCalHist;
#endif

	ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
	chan->rawNoiseFloor = h[0].privNF;
@@ -728,20 +720,12 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah)

s16 ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan)
{
	struct ath9k_channel *ichan;
	s16 nf;

	ichan = ath9k_regd_check_channel(ah, chan);
	if (ichan == NULL) {
		DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
			"invalid channel %u/0x%x; no mapping\n",
			chan->channel, chan->channelFlags);
		return ATH_DEFAULT_NOISE_FLOOR;
	}
	if (ichan->rawNoiseFloor == 0)
	if (chan->rawNoiseFloor == 0)
		nf = -96;
	else
		nf = ichan->rawNoiseFloor;
		nf = chan->rawNoiseFloor;

	if (!ath9k_hw_nf_in_range(ah, nf))
		nf = ATH_DEFAULT_NOISE_FLOOR;
@@ -755,21 +739,13 @@ bool ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
{
	struct ath_hal_5416 *ahp = AH5416(ah);
	struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);

	*isCalDone = true;

	if (ichan == NULL) {
		DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
			"invalid channel %u/0x%x; no mapping\n",
			chan->channel, chan->channelFlags);
		return false;
	}

	if (currCal &&
	    (currCal->calState == CAL_RUNNING ||
	     currCal->calState == CAL_WAITING)) {
		ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal,
		ath9k_hw_per_calibration(ah, chan, rxchainmask, currCal,
					 isCalDone);
		if (*isCalDone) {
			ahp->ah_cal_list_curr = currCal = currCal->calNext;
@@ -782,14 +758,12 @@ bool ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
	}

	if (longcal) {
		ath9k_hw_getnf(ah, ichan);
		ath9k_hw_getnf(ah, chan);
		ath9k_hw_loadnf(ah, ah->ah_curchan);
		ath9k_hw_start_nfcal(ah);

		if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
			chan->channelFlags |= CHANNEL_CW_INT;
			ichan->channelFlags &= ~CHANNEL_CW_INT;
		}
		if (chan->channelFlags & CHANNEL_CW_INT)
			chan->channelFlags &= ~CHANNEL_CW_INT;
	}

	return true;
@@ -894,7 +868,6 @@ bool ath9k_hw_init_cal(struct ath_hal *ah,
		       struct ath9k_channel *chan)
{
	struct ath_hal_5416 *ahp = AH5416(ah);
	struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);

	REG_WRITE(ah, AR_PHY_AGC_CONTROL,
		  REG_READ(ah, AR_PHY_AGC_CONTROL) |
@@ -942,7 +915,7 @@ bool ath9k_hw_init_cal(struct ath_hal *ah,
			ath9k_hw_reset_calibration(ah, ahp->ah_cal_list_curr);
	}

	ichan->CalValid = 0;
	chan->CalValid = 0;

	return true;
}
+0 −1
Original line number Diff line number Diff line
@@ -724,7 +724,6 @@ struct ath_softc {
	struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
	struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX];
	struct ath_rate_table *cur_rate_table;
	struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX];
	struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
	struct ath_led radio_led;
	struct ath_led assoc_led;
+9 −54
Original line number Diff line number Diff line
@@ -187,46 +187,6 @@ u16 ath9k_hw_computetxtime(struct ath_hal *ah,
	return txTime;
}

u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags)
{
	if (flags & CHANNEL_2GHZ) {
		if (freq == 2484)
			return 14;
		if (freq < 2484)
			return (freq - 2407) / 5;
		else
			return 15 + ((freq - 2512) / 20);
	} else if (flags & CHANNEL_5GHZ) {
		if (ath9k_regd_is_public_safety_sku(ah) &&
		    IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
			return ((freq * 10) +
				(((freq % 5) == 2) ? 5 : 0) - 49400) / 5;
		} else if ((flags & CHANNEL_A) && (freq <= 5000)) {
			return (freq - 4000) / 5;
		} else {
			return (freq - 5000) / 5;
		}
	} else {
		if (freq == 2484)
			return 14;
		if (freq < 2484)
			return (freq - 2407) / 5;
		if (freq < 5000) {
			if (ath9k_regd_is_public_safety_sku(ah)
			    && IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
				return ((freq * 10) +
					(((freq % 5) ==
					  2) ? 5 : 0) - 49400) / 5;
			} else if (freq > 4900) {
				return (freq - 4000) / 5;
			} else {
				return 15 + ((freq - 2512) / 20);
			}
		}
		return (freq - 5000) / 5;
	}
}

void ath9k_hw_get_channel_centers(struct ath_hal *ah,
				  struct ath9k_channel *chan,
				  struct chan_centers *centers)
@@ -1270,6 +1230,7 @@ static int ath9k_hw_process_ini(struct ath_hal *ah,
{
	int i, regWrites = 0;
	struct ath_hal_5416 *ahp = AH5416(ah);
	struct ieee80211_channel *channel = chan->chan;
	u32 modesIndex, freqIndex;
	int status;

@@ -1374,9 +1335,8 @@ static int ath9k_hw_process_ini(struct ath_hal *ah,

	status = ath9k_hw_set_txpower(ah, chan,
				      ath9k_regd_get_ctl(ah, chan),
				      ath9k_regd_get_antenna_allowed(ah,
								     chan),
				      chan->maxRegTxPower * 2,
				      channel->max_antenna_gain * 2,
				      channel->max_power * 2,
				      min((u32) MAX_RATE_POWER,
					  (u32) ah->ah_powerLimit));
	if (status != 0) {
@@ -1669,6 +1629,7 @@ static bool ath9k_hw_channel_change(struct ath_hal *ah,
				    struct ath9k_channel *chan,
				    enum ath9k_ht_macmode macmode)
{
	struct ieee80211_channel *channel = chan->chan;
	u32 synthDelay, qnum;

	for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
@@ -1705,8 +1666,8 @@ static bool ath9k_hw_channel_change(struct ath_hal *ah,

	if (ath9k_hw_set_txpower(ah, chan,
				 ath9k_regd_get_ctl(ah, chan),
				 ath9k_regd_get_antenna_allowed(ah, chan),
				 chan->maxRegTxPower * 2,
				 channel->max_antenna_gain * 2,
				 channel->max_power * 2,
				 min((u32) MAX_RATE_POWER,
				     (u32) ah->ah_powerLimit)) != 0) {
		DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
@@ -2209,13 +2170,6 @@ int ath9k_hw_reset(struct ath_hal *ah, struct ath9k_channel *chan,
		ahp->ah_rxchainmask &= 0x3;
	}

	if (ath9k_regd_check_channel(ah, chan) == NULL) {
		DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
			"invalid channel %u/0x%x; no mapping\n",
			chan->channel, chan->channelFlags);
		return -EINVAL;
	}

	if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
		return -EIO;

@@ -3718,13 +3672,14 @@ bool ath9k_hw_disable(struct ath_hal *ah)
bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit)
{
	struct ath9k_channel *chan = ah->ah_curchan;
	struct ieee80211_channel *channel = chan->chan;

	ah->ah_powerLimit = min(limit, (u32) MAX_RATE_POWER);

	if (ath9k_hw_set_txpower(ah, chan,
				 ath9k_regd_get_ctl(ah, chan),
				 ath9k_regd_get_antenna_allowed(ah, chan),
				 chan->maxRegTxPower * 2,
				 channel->max_antenna_gain * 2,
				 channel->max_power * 2,
				 min((u32) MAX_RATE_POWER,
				     (u32) ah->ah_powerLimit)) != 0)
		return false;
+142 −121
Original line number Diff line number Diff line
@@ -28,6 +28,77 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");

/* We use the hw_value as an index into our private channel structure */

#define CHAN2G(_freq, _idx)  { \
	.center_freq = (_freq), \
	.hw_value = (_idx), \
	.max_power = 30, \
}

#define CHAN5G(_freq, _idx) { \
	.band = IEEE80211_BAND_5GHZ, \
	.center_freq = (_freq), \
	.hw_value = (_idx), \
	.max_power = 30, \
}

/* Some 2 GHz radios are actually tunable on 2312-2732
 * on 5 MHz steps, we support the channels which we know
 * we have calibration data for all cards though to make
 * this static */
static struct ieee80211_channel ath9k_2ghz_chantable[] = {
	CHAN2G(2412, 0), /* Channel 1 */
	CHAN2G(2417, 1), /* Channel 2 */
	CHAN2G(2422, 2), /* Channel 3 */
	CHAN2G(2427, 3), /* Channel 4 */
	CHAN2G(2432, 4), /* Channel 5 */
	CHAN2G(2437, 5), /* Channel 6 */
	CHAN2G(2442, 6), /* Channel 7 */
	CHAN2G(2447, 7), /* Channel 8 */
	CHAN2G(2452, 8), /* Channel 9 */
	CHAN2G(2457, 9), /* Channel 10 */
	CHAN2G(2462, 10), /* Channel 11 */
	CHAN2G(2467, 11), /* Channel 12 */
	CHAN2G(2472, 12), /* Channel 13 */
	CHAN2G(2484, 13), /* Channel 14 */
};

/* Some 5 GHz radios are actually tunable on XXXX-YYYY
 * on 5 MHz steps, we support the channels which we know
 * we have calibration data for all cards though to make
 * this static */
static struct ieee80211_channel ath9k_5ghz_chantable[] = {
	/* _We_ call this UNII 1 */
	CHAN5G(5180, 14), /* Channel 36 */
	CHAN5G(5200, 15), /* Channel 40 */
	CHAN5G(5220, 16), /* Channel 44 */
	CHAN5G(5240, 17), /* Channel 48 */
	/* _We_ call this UNII 2 */
	CHAN5G(5260, 18), /* Channel 52 */
	CHAN5G(5280, 19), /* Channel 56 */
	CHAN5G(5300, 20), /* Channel 60 */
	CHAN5G(5320, 21), /* Channel 64 */
	/* _We_ call this "Middle band" */
	CHAN5G(5500, 22), /* Channel 100 */
	CHAN5G(5520, 23), /* Channel 104 */
	CHAN5G(5540, 24), /* Channel 108 */
	CHAN5G(5560, 25), /* Channel 112 */
	CHAN5G(5580, 26), /* Channel 116 */
	CHAN5G(5600, 27), /* Channel 120 */
	CHAN5G(5620, 28), /* Channel 124 */
	CHAN5G(5640, 29), /* Channel 128 */
	CHAN5G(5660, 30), /* Channel 132 */
	CHAN5G(5680, 31), /* Channel 136 */
	CHAN5G(5700, 32), /* Channel 140 */
	/* _We_ call this UNII 3 */
	CHAN5G(5745, 33), /* Channel 149 */
	CHAN5G(5765, 34), /* Channel 153 */
	CHAN5G(5785, 35), /* Channel 157 */
	CHAN5G(5805, 36), /* Channel 161 */
	CHAN5G(5825, 37), /* Channel 165 */
};

static void ath_cache_conf_rate(struct ath_softc *sc,
				struct ieee80211_conf *conf)
{
@@ -152,75 +223,6 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
	}
}

static int ath_setup_channels(struct ath_softc *sc)
{
	struct ath_hal *ah = sc->sc_ah;
	int nchan, i, a = 0, b = 0;
	u8 regclassids[ATH_REGCLASSIDS_MAX];
	u32 nregclass = 0;
	struct ieee80211_supported_band *band_2ghz;
	struct ieee80211_supported_band *band_5ghz;
	struct ieee80211_channel *chan_2ghz;
	struct ieee80211_channel *chan_5ghz;
	struct ath9k_channel *c;

	/* Fill in ah->ah_channels */
	if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (u32 *)&nchan,
				      regclassids, ATH_REGCLASSIDS_MAX,
				      &nregclass, CTRY_DEFAULT, false, 1)) {
		u32 rd = ah->ah_currentRD;
		DPRINTF(sc, ATH_DBG_FATAL,
			"Unable to collect channel list; "
			"regdomain likely %u country code %u\n",
			rd, CTRY_DEFAULT);
		return -EINVAL;
	}

	band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ];
	band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ];
	chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ];
	chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ];

	for (i = 0; i < nchan; i++) {
		c = &ah->ah_channels[i];
		if (IS_CHAN_2GHZ(c)) {
			chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
			chan_2ghz[a].center_freq = c->channel;
			chan_2ghz[a].max_power = c->maxTxPower;
			c->chan = &chan_2ghz[a];

			if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
				chan_2ghz[a].flags |= IEEE80211_CHAN_NO_IBSS;
			if (c->channelFlags & CHANNEL_PASSIVE)
				chan_2ghz[a].flags |= IEEE80211_CHAN_PASSIVE_SCAN;

			band_2ghz->n_channels = ++a;

			DPRINTF(sc, ATH_DBG_CONFIG, "2MHz channel: %d, "
				"channelFlags: 0x%x\n",
				c->channel, c->channelFlags);
		} else if (IS_CHAN_5GHZ(c)) {
			chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
			chan_5ghz[b].center_freq = c->channel;
			chan_5ghz[b].max_power = c->maxTxPower;
			c->chan = &chan_5ghz[a];

			if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
				chan_5ghz[b].flags |= IEEE80211_CHAN_NO_IBSS;
			if (c->channelFlags & CHANNEL_PASSIVE)
				chan_5ghz[b].flags |= IEEE80211_CHAN_PASSIVE_SCAN;

			band_5ghz->n_channels = ++b;

			DPRINTF(sc, ATH_DBG_CONFIG, "5MHz channel: %d, "
				"channelFlags: 0x%x\n",
				c->channel, c->channelFlags);
		}
	}

	return 0;
}

/*
 * Set/change channels.  If the channel is really being changed, it's done
 * by reseting the chip.  To accomplish this we must first cleanup any pending
@@ -582,19 +584,6 @@ irqreturn_t ath_isr(int irq, void *dev)
	return IRQ_HANDLED;
}

static int ath_get_channel(struct ath_softc *sc,
			   struct ieee80211_channel *chan)
{
	int i;

	for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
		if (sc->sc_ah->ah_channels[i].channel == chan->center_freq)
			return i;
	}

	return -1;
}

static u32 ath_get_extchanmode(struct ath_softc *sc,
			       struct ieee80211_channel *chan,
			       enum nl80211_channel_type channel_type)
@@ -1349,16 +1338,12 @@ static int ath_init(u16 devid, struct ath_softc *sc)
	for (i = 0; i < sc->sc_keymax; i++)
		ath9k_hw_keyreset(ah, (u16) i);

	/* Collect the channel list using the default country code */

	error = ath_setup_channels(sc);
	if (error)
	if (ath9k_regd_init(sc->sc_ah))
		goto bad;

	/* default to MONITOR mode */
	sc->sc_ah->ah_opmode = NL80211_IFTYPE_MONITOR;


	/* Setup rate tables */

	ath_rate_attach(sc);
@@ -1490,18 +1475,20 @@ static int ath_init(u16 devid, struct ath_softc *sc)

	/* setup channels and rates */

	sc->sbands[IEEE80211_BAND_2GHZ].channels =
		sc->channels[IEEE80211_BAND_2GHZ];
	sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
	sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
		sc->rates[IEEE80211_BAND_2GHZ];
	sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
	sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
		ARRAY_SIZE(ath9k_2ghz_chantable);

	if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
		sc->sbands[IEEE80211_BAND_5GHZ].channels =
			sc->channels[IEEE80211_BAND_5GHZ];
		sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable;
		sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
			sc->rates[IEEE80211_BAND_5GHZ];
		sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
		sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
			ARRAY_SIZE(ath9k_5ghz_chantable);
	}

	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_BT_COEX)
@@ -1550,6 +1537,9 @@ int ath_attach(u16 devid, struct ath_softc *sc)
		BIT(NL80211_IFTYPE_STATION) |
		BIT(NL80211_IFTYPE_ADHOC);

	hw->wiphy->reg_notifier = ath9k_reg_notifier;
	hw->wiphy->strict_regulatory = true;

	hw->queues = 4;
	hw->max_rates = 4;
	hw->max_rate_tries = ATH_11N_TXMAXTRY;
@@ -1588,11 +1578,36 @@ int ath_attach(u16 devid, struct ath_softc *sc)
		goto detach;
#endif

	if (ath9k_is_world_regd(sc->sc_ah)) {
		/* Anything applied here (prior to wiphy registratoin) gets
		 * saved on the wiphy orig_* parameters */
		const struct ieee80211_regdomain *regd =
			ath9k_world_regdomain(sc->sc_ah);
		hw->wiphy->custom_regulatory = true;
		hw->wiphy->strict_regulatory = false;
		wiphy_apply_custom_regulatory(sc->hw->wiphy, regd);
		ath9k_reg_apply_radar_flags(hw->wiphy);
		ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT);
	} else {
		/* This gets applied in the case of the absense of CRDA,
		 * its our own custom world regulatory domain, similar to
		 * cfg80211's but we enable passive scanning */
		const struct ieee80211_regdomain *regd =
			ath9k_default_world_regdomain();
		wiphy_apply_custom_regulatory(sc->hw->wiphy, regd);
		ath9k_reg_apply_radar_flags(hw->wiphy);
		ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_INIT);
	}

	error = ieee80211_register_hw(hw);

	if (!ath9k_is_world_regd(sc->sc_ah))
		regulatory_hint(hw->wiphy, sc->sc_ah->alpha2);

	/* Initialize LED control */
	ath_init_leds(sc);


	return 0;
detach:
	ath_detach(sc);
@@ -1818,6 +1833,37 @@ int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
	return qnum;
}

/* XXX: Remove me once we don't depend on ath9k_channel for all
 * this redundant data */
static void ath9k_update_ichannel(struct ath_softc *sc,
			  struct ath9k_channel *ichan)
{
	struct ieee80211_hw *hw = sc->hw;
	struct ieee80211_channel *chan = hw->conf.channel;
	struct ieee80211_conf *conf = &hw->conf;

	ichan->channel = chan->center_freq;
	ichan->chan = chan;

	if (chan->band == IEEE80211_BAND_2GHZ) {
		ichan->chanmode = CHANNEL_G;
		ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
	} else {
		ichan->chanmode = CHANNEL_A;
		ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
	}

	sc->tx_chan_width = ATH9K_HT_MACMODE_20;

	if (conf_is_ht(conf)) {
		if (conf_is_ht40(conf))
			sc->tx_chan_width = ATH9K_HT_MACMODE_2040;

		ichan->chanmode = ath_get_extchanmode(sc, chan,
					    conf->channel_type);
	}
}

/**********************/
/* mac80211 callbacks */
/**********************/
@@ -1834,16 +1880,10 @@ static int ath9k_start(struct ieee80211_hw *hw)

	/* setup initial channel */

	pos = ath_get_channel(sc, curchan);
	if (pos == -1) {
		DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n", curchan->center_freq);
		return -EINVAL;
	}
	pos = curchan->hw_value;

	sc->tx_chan_width = ATH9K_HT_MACMODE_20;
	sc->sc_ah->ah_channels[pos].chanmode =
		(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
	init_channel = &sc->sc_ah->ah_channels[pos];
	ath9k_update_ichannel(sc, init_channel);

	/* Reset SERDES registers */
	ath9k_hw_configpcipowersave(sc->sc_ah, 0);
@@ -2127,32 +2167,13 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)

	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
		struct ieee80211_channel *curchan = hw->conf.channel;
		int pos;
		int pos = curchan->hw_value;

		DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
			curchan->center_freq);

		pos = ath_get_channel(sc, curchan);
		if (pos == -1) {
			DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n",
				curchan->center_freq);
			mutex_unlock(&sc->mutex);
			return -EINVAL;
		}

		sc->tx_chan_width = ATH9K_HT_MACMODE_20;
		sc->sc_ah->ah_channels[pos].chanmode =
			(curchan->band == IEEE80211_BAND_2GHZ) ?
			CHANNEL_G : CHANNEL_A;

		if (conf_is_ht(conf)) {
			if (conf_is_ht40(conf))
				sc->tx_chan_width = ATH9K_HT_MACMODE_2040;

			sc->sc_ah->ah_channels[pos].chanmode =
				ath_get_extchanmode(sc, curchan,
						    conf->channel_type);
		}
		/* XXX: remove me eventualy */
		ath9k_update_ichannel(sc, &sc->sc_ah->ah_channels[pos]);

		ath_update_chainmask(sc, conf_is_ht(conf));

Loading