/*
* Copyright (c) 2008 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include "core.h"
#include "hw.h"
#include "regd.h"
#include "regd_common.h"
static int ath9k_regd_chansort(const void *a, const void *b)
{
const struct ath9k_channel *ca = a;
const struct ath9k_channel *cb = b;
return (ca->channel == cb->channel) ?
(ca->channelFlags & CHAN_FLAGS) -
(cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel;
}
static void
ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp)
{
u8 *aa = a;
u8 *ai, *t;
for (ai = aa + size; --n >= 1; ai += size)
for (t = ai; t > aa; t -= size) {
u8 *u = t - size;
if (cmp(u, t) <= 0)
break;
swap(u, t, size);
}
}
static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah)
{
return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG;
}
static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask)
{
int i;
for (i = 0; i < BMLEN; i++) {
if (bitmask[i] != 0)
return false;
}
return true;
}
static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
{
u16 rd = ath9k_regd_get_eepromRD(ah);
int i;
if (rd & COUNTRY_ERD_FLAG) {
u16 cc = rd & ~COUNTRY_ERD_FLAG;
for (i = 0; i < ARRAY_SIZE(allCountries); i++)
if (allCountries[i].countryCode == cc)
return true;
} else {
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
if (regDomainPairs[i].regDmnEnum == rd)
return true;
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: invalid regulatory domain/country code 0x%x\n",
__func__, rd);
return false;
}
static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah)
{
u32 regcap;
regcap = ah->ah_caps.reg_cap;
if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
return true;
else
return false;
}
static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
u16 cc)
{
u16 rd;
int i;
if (cc == CTRY_DEFAULT)
return true;
if (cc == CTRY_DEBUG)
return true;
rd = ath9k_regd_get_eepromRD(ah);
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
__func__, rd);
if (rd & COUNTRY_ERD_FLAG) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: EEPROM setting is country code %u\n",
__func__, rd & ~COUNTRY_ERD_FLAG);
return cc == (rd & ~COUNTRY_ERD_FLAG);
}
for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
if (cc == allCountries[i].countryCode) {
#ifdef AH_SUPPORT_11D
if ((rd &