diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 65ef483ebf50..f40e1b21520e 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -282,6 +282,7 @@ enum ATH_DEBUG { ATH_DBG_CHAN_CTX = 0x00040000, ATH_DBG_DYNACK = 0x00080000, ATH_DBG_SPECTRAL_SCAN = 0x00100000, + ATH_DBG_XMIT_PAYLOAD = 0x00200000, ATH_DBG_ANY = 0xffffffff }; diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index ecda613c2d54..88e6ac7f6adc 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -41,7 +41,8 @@ ath9k_hw-y:= \ ar9002_mac.o \ ar9003_mac.o \ ar9003_eeprom.o \ - ar9003_paprd.o + ar9003_paprd.o \ + ar9003_azimuth.o ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o diff --git a/drivers/net/wireless/ath/ath9k/ar9003_azimuth.c b/drivers/net/wireless/ath/ath9k/ar9003_azimuth.c new file mode 100644 index 000000000000..155af0c987d8 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_azimuth.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2016 Adrian Chadd, Eva Automation 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 +#include +#include "ath9k.h" +#include "hw.h" + +#define AR9003_AZIMUTH_MODE 0x8300 +#define AR9003_AZIMUTH_MODE_TSFUPDIS BIT(0) +#define AR9003_AZIMUTH_MODE_TSFUPDIS_S 0 +#define AR9003_AZIMUTH_MODE_TX_TSF BIT(2) +#define AR9003_AZIMUTH_MODE_TX_TSF_S 2 +#define AR9003_AZIMUTH_MODE_RX_TSF BIT(3) +#define AR9003_AZIMUTH_MODE_RX_TSF_S 3 +#define AR9003_AZIMUTH_MODE_CLK25_EN BIT(4) +#define AR9003_AZIMUTH_MODE_CLK25_EN_S 4 +#define AR9003_AZIMUTH_MODE_WMAC_CLK_EN BIT(8) +#define AR9003_AZIMUTH_MODE_WMAC_CLK_EN_S 8 + +#define AR9003_AZIMUTH_TIMESTAMP 0x8314 + +/* GPIO defines for azimuth mode */ +/* These define the value of the line if the _EN bit is 0 */ +#define AR_GPIO_INPUT_EN_VAL_RST_TSF BIT(0) +#define AR_GPIO_INPUT_EN_VAL_RST_TSF_S 0 +#define AR_GPIO_INPUT_EN_VAL_RST_AZM_TS BIT(1) +#define AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_S 1 +#define AR_GPIO_INPUT_EN_VAL_CLK25 BIT(6) +#define AR_GPIO_INPUT_EN_VAL_CLK25_S 6 + +/* + * This define whether the line value is GPIO (1) or the above bits under + * software control (0). + */ +#define AR_GPIO_INPUT_EN_VAL_RST_TSF_EN BIT(8) +#define AR_GPIO_INPUT_EN_VAL_RST_TSF_EN_S 8 +#define AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN BIT(9) +#define AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN_S 9 +#define AR_GPIO_INPUT_EN_VAL_CLK25_EN BIT(14) +#define AR_GPIO_INPUT_EN_VAL_CLK25_EN_S 14 + +/* + * These bits define the field for the GPIO input mux for clk25 and azm_ts_reset. + */ +#define AR_GPIO_INPUT_MUX1_RST_AZM 0x000000f0 +#define AR_GPIO_INPUT_MUX1_RST_AZM_S 4 +#define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f +#define AR_GPIO_INPUT_MUX2_CLK25_S 0 + +static void +ar9003_azimuth_timer(unsigned long data) +{ + struct ath_hw *ah = (void *) data; + struct ath_common *common = ath9k_hw_common(ah); + + /* This is triggered to re-enable interrupts */ + ath_info(common, "Azimuth: re-enabling interrupts\n"); + /* Re-add the GPIO interrupt */ +#if 0 + if (ah->azimuth.ext_reset_gpio > -1) { + ath9k_hw_cfg_gpio_intr(ah, ah->azimuth.ext_reset_gpio, true, true); + ath9k_hw_update_interrupts(ah); + } +#endif +} + +void +ar9003_azimuth_init(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + ah->azimuth.ext_reset_gpio = -1; + ah->azimuth.ext_clk25_gpio = -1; + ah->azimuth.ext_reset_notif_gpio = -1; + + /* XXX TODO: Pull default config from the module load parameters */ + + ath_info(common, "Azimuth: reset=%d, clk25=%d, reset_notif=%d\n", + ah->azimuth.ext_reset_gpio, + ah->azimuth.ext_clk25_gpio, + ah->azimuth.ext_reset_notif_gpio); + + setup_timer(&ah->azimuth.reset_timer, ar9003_azimuth_timer, (unsigned long) ah); +} +EXPORT_SYMBOL(ar9003_azimuth_init); + +void +ar9003_azimuth_destroy(struct ath_hw *ah) +{ + + del_timer_sync(&ah->azimuth.reset_timer); +} +EXPORT_SYMBOL(ar9003_azimuth_destroy); + +/* + * Read the current azimuth timestamp. + */ +u32 ar9003_azimuth_read_timestamp(struct ath_hw *hw) +{ + + return REG_READ(hw, AR9003_AZIMUTH_TIMESTAMP); +} + +static void +ar9003_azimuth_toggle_reset_notif(struct ath_hw *ah) +{ + + if (ah->azimuth.ext_reset_notif_gpio > -1) { + ath9k_hw_set_gpio(ah, ah->azimuth.ext_reset_notif_gpio, 1); + udelay(1); + ath9k_hw_set_gpio(ah, ah->azimuth.ext_reset_notif_gpio, 0); + udelay(1); + } +} + +/* + * Called to reset the azimuth timestamp from some external + * path, like the GPIO interrupt path. + */ +void ar9003_azimuth_reset_timestamp(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + ath_info(common, "Azimuth: resetting timestamp\n"); + + /* + * Only clear the timestamp if we have the reset notification + * path provided. Otherwise, we may reset azimuth but not yet + * actually /start/ it again. + */ + if (ah->azimuth.ext_reset_notif_gpio > -1) { + REG_WRITE(ah, AR9003_AZIMUTH_TIMESTAMP, 0); + } + +#if 0 + /* Clear the GPIO interrupt */ + if (ah->azimuth.ext_reset_gpio > -1) { + ath9k_hw_cfg_gpio_intr(ah, ah->azimuth.ext_reset_gpio, false, false); + ath9k_hw_update_interrupts(ah); + } +#endif + + /* Notify the CPLD and reset ourselves if it's looped back into us */ + ar9003_azimuth_toggle_reset_notif(ah); + + /* Timer to resume things after 100ms */ + mod_timer(&ah->azimuth.reset_timer, jiffies + msecs_to_jiffies(100)); +} +EXPORT_SYMBOL(ar9003_azimuth_reset_timestamp); + +/* + * Called during the reset path to enable/disable the azimuth configuration + * as appropriate. + * + * This assumes that the GPIO mux has been configured. + */ +void ar9003_azimuth_reset_hw(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + u32 reg; + + del_timer_sync(&ah->azimuth.reset_timer); + + reg = REG_READ(ah, AR9003_AZIMUTH_MODE); + + /* Flip on azimuth mode timestamping clock */ + reg |= AR9003_AZIMUTH_MODE_CLK25_EN; + + /* + * Flip on the right clock source based on the GPIO pin configuration. + * This handles whether it's internal or external. + */ + if (ah->azimuth.ext_clk25_gpio == -1) { + reg |= AR9003_AZIMUTH_MODE_WMAC_CLK_EN; + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_CLK25_EN); + } else { + reg &= ~AR9003_AZIMUTH_MODE_WMAC_CLK_EN; + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_CLK25_EN); + + /* GPIO input pin for clk25 */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX2, + AR_GPIO_INPUT_MUX2_CLK25, + ah->azimuth.ext_clk25_gpio); + /* configure as input */ + ath9k_hw_cfg_gpio_input(ah, ah->azimuth.ext_clk25_gpio); + /* No pullup */ + ath9k_hw_cfg_gpio_pdpu(ah, ah->azimuth.ext_clk25_gpio, AR_GPIO_PDPU_VAL_NONE); + } + + /* Always enable TX/RX TSF descriptor stamping */ + reg |= AR9003_AZIMUTH_MODE_TX_TSF; + reg |= AR9003_AZIMUTH_MODE_RX_TSF; + + REG_WRITE(ah, AR9003_AZIMUTH_MODE, reg); + + /* + * Next, flip on the right external GPIO pin configuration for the + * azimuth clock input. + */ + if (ah->azimuth.ext_reset_gpio == -1) { + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN); + } else { + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN); + /* GPIO input pin for azimuth timestamp reset */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_RST_AZM, + ah->azimuth.ext_reset_gpio); + /* configure as input */ + ath9k_hw_cfg_gpio_input(ah, ah->azimuth.ext_reset_gpio); + /* Ensure it's pull down */ + ath9k_hw_cfg_gpio_pdpu(ah, ah->azimuth.ext_reset_gpio, AR_GPIO_PDPU_VAL_PULL_DOWN); +#if 0 + /* Configure the reset line as an interrupt line too - enable, active-high */ + ath9k_hw_cfg_gpio_intr(ah, ah->azimuth.ext_reset_gpio, true, true); +#endif + } + + /* And the reset notification pin */ + if (ah->azimuth.ext_reset_notif_gpio > -1) { + ath9k_hw_cfg_output(ah, ah->azimuth.ext_reset_notif_gpio, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_cfg_gpio_pdpu(ah, ah->azimuth.ext_reset_notif_gpio, AR_GPIO_PDPU_VAL_NONE); + } + ath_dbg(common, RESET, "%s: AZIMUTH_MODE=0x%08x\n", + __func__, + reg); +} + +/* + * Called to reset the azimuth timestamp. This is done via + * GPIO mux/input value toggling. + */ +void ar9003_azimuth_reset_timer(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + ath_dbg(common, RESET, "%s: resetting timer\n", __func__); + + /* First - ensure we disable the GPIO reset as we need to do it ourselves */ + if (ah->azimuth.ext_reset_gpio == -1) + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN); + + /* write out the reset timestamp value */ + REG_WRITE(ah, AR9003_AZIMUTH_TIMESTAMP, 0); + + /* + * Notify the CPLD and reset ourselves if it's looped back into us or the CPLD + * will do it for us! + */ + ar9003_azimuth_toggle_reset_notif(ah); + + /* If we don't have a reset notification line, go do the GPIO reset bits */ + if (ah->azimuth.ext_reset_gpio == -1) { + /* + * XXX TODO: figure out the right delay; I'm just guessing for now. + * My guess is it needs to be valid for like, a CLK25 transition, + * which is 250nS. + * + * Note: this only works IFF the clock is running and configured correctly. + * If the clock input isn't configured correctly then it'll fail to start things. + */ + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS); + udelay(10); + + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS); + udelay(10); + } + + /* Finally - if we have a reset gpio line, configure that */ + if (ah->azimuth.ext_reset_gpio != -1) + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RST_AZM_TS_EN); +} + +/* + * Extract the various current timestamp information as close together + * as possible. + */ +ssize_t read_file_azimuth_timestamp(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned int len = 0, size = 128; + ssize_t retval; + char *buf; + u32 azm_timestamp; + u64 tsf64; + cycles_t cc; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Read tsf, azimuth timestamp and system clock together */ + azm_timestamp = REG_READ(ah, AR9003_AZIMUTH_TIMESTAMP); + tsf64 = ath9k_hw_gettsf64(ah); + cc = get_cycles(); + + len += scnprintf(buf + len, size - len, "azm_ts=%u, tsf64=%llu, tsc=%llu, gen=%llu\n", + (unsigned) azm_timestamp, + (unsigned long long) tsf64, + (unsigned long long) cc, + (unsigned long long) ah->reset_gen); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} +EXPORT_SYMBOL(read_file_azimuth_timestamp); + +ssize_t read_file_azimuth_clk(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->sc_ah->azimuth.ext_clk25_gpio); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} +EXPORT_SYMBOL(read_file_azimuth_clk); + +ssize_t write_file_azimuth_clk(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + long gpio; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtol(buf, 0, &gpio)) + return -EINVAL; + + sc->sc_ah->azimuth.ext_clk25_gpio = gpio; + + ar9003_azimuth_reset_hw(sc->sc_ah); + ar9003_azimuth_reset_timer(sc->sc_ah); + + return count; +} +EXPORT_SYMBOL(write_file_azimuth_clk); + +ssize_t read_file_azimuth_reset(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->sc_ah->azimuth.ext_reset_gpio); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} +EXPORT_SYMBOL(read_file_azimuth_reset); + +ssize_t write_file_azimuth_reset(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + long gpio; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtol(buf, 0, &gpio)) + return -EINVAL; + + sc->sc_ah->azimuth.ext_reset_gpio = gpio; + + ar9003_azimuth_reset_hw(sc->sc_ah); + ar9003_azimuth_reset_timer(sc->sc_ah); + + return count; +} +EXPORT_SYMBOL(write_file_azimuth_reset); + +ssize_t read_file_azimuth_reset_notif(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->sc_ah->azimuth.ext_reset_notif_gpio); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} +EXPORT_SYMBOL(read_file_azimuth_reset_notif); + +ssize_t write_file_azimuth_reset_notif(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + long gpio; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtol(buf, 0, &gpio)) + return -EINVAL; + + sc->sc_ah->azimuth.ext_reset_notif_gpio = gpio; + + ar9003_azimuth_reset_hw(sc->sc_ah); + ar9003_azimuth_reset_timer(sc->sc_ah); + + return count; +} +EXPORT_SYMBOL(write_file_azimuth_reset_notif); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_azimuth.h b/drivers/net/wireless/ath/ath9k/ar9003_azimuth.h new file mode 100644 index 000000000000..5e61e6960eb2 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_azimuth.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Adrian Chadd, Eva Automation 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. + */ +#ifndef _AR9003_AZIMUTH_H_ +#define _AR9003_AZIMUTH_H_ + +void ar9003_azimuth_init(struct ath_hw *hw); +void ar9003_azimuth_destroy(struct ath_hw *ah); +u32 ar9003_azimuth_read_timestamp(struct ath_hw *hw); +void ar9003_azimuth_reset_timestamp(struct ath_hw *hw); +void ar9003_azimuth_reset_hw(struct ath_hw *hw); +void ar9003_azimuth_reset_timer(struct ath_hw *hw); + +ssize_t read_file_azimuth_timestamp(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + +ssize_t read_file_azimuth_clk(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t write_file_azimuth_clk(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t read_file_azimuth_reset(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t write_file_azimuth_reset(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t read_file_azimuth_reset_notif(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t write_file_azimuth_reset_notif(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos); + + +#endif diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index da84b705cbcd..c14e42cf5fd0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -113,6 +113,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) SM(i->keyix, AR_DestIdx) : 0) | SM(i->type, AR_FrameType) | (i->flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) + | (i->flags & ATH9K_TXDESC_INSERT_TS ? AR_InsertTS : 0) | (i->flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) | (i->flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); @@ -188,26 +189,46 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, struct ath_common *common = ath9k_hw_common(ah); u32 sync_cause = 0, async_cause, async_mask = AR_INTR_MAC_IRQ; bool fatal_int; + u32 async_gpio_mask; + u32 sync_gpio_mask; + bool gpio_int = false; if (ath9k_hw_mci_is_enabled(ah)) async_mask |= AR_INTR_ASYNC_MASK_MCI; async_cause = REG_READ(ah, AR_INTR_ASYNC_CAUSE); + /* Ensure we /see/ the async GPIO interrupts */ + async_gpio_mask = ah->gpio_intr_async_mask << AR_INTR_ASYNC_ENABLE_GPIO_S; + sync_gpio_mask = ah->gpio_intr_sync_mask << AR_INTR_SYNC_ENABLE_GPIO_S; + if (async_cause & async_mask) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) isr = REG_READ(ah, AR_ISR); } - - sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; + sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) & (AR_INTR_SYNC_DEFAULT | + AR_INTR_SYNC_MASK_GPIO); *masked = 0; if (!isr && !sync_cause && !async_cause) return false; + gpio_int = false; + if (async_cause & async_gpio_mask) { + ath_dbg(common, RESET, "%s: gpio: async_cause=0x%08x\n", __func__, async_cause); + ah->gpio_intr_status |= ((async_cause & async_gpio_mask) >> AR_INTR_ASYNC_ENABLE_GPIO_S); + gpio_int = true; + } + + if (sync_cause & sync_gpio_mask) { + ath_dbg(common, RESET, "%s: gpio: sync_cause=0x%08x\n", __func__, sync_cause); + ah->gpio_intr_status |= ((sync_cause & sync_gpio_mask) >> AR_INTR_SYNC_ENABLE_GPIO_S); + gpio_int = true; + } + if (isr) { if (isr & AR_ISR_BCNMISC) { u32 isr2; @@ -311,6 +332,10 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, ar9003_hw_bb_watchdog_read(ah); } + if (gpio_int) { + *masked |= ATH9K_INT_GPIO; + } + if (async_cause & AR_INTR_ASYNC_MASK_MCI) ar9003_mci_get_isr(ah, masked); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 201425e7f9cb..35d4a7365cf8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -889,6 +889,9 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, else modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; + /* Bump generation counter */ + ah->reset_gen++; + /* * SOC, MAC, BB, RADIO initvals. */ @@ -1704,6 +1707,9 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, goto set_rfmode; } + /* Bump generation counter */ + ah->reset_gen++; + ar9003_hw_prog_ini(ah, &ah->iniSOC[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniMac[ATH_INI_POST], modesIndex); ar9003_hw_prog_ini(ah, &ah->iniBB[ATH_INI_POST], modesIndex); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b42f4a963ef4..fe383f8c8007 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -182,6 +182,7 @@ struct ath_frame_info { u8 baw_tracked : 1; u8 tx_power; enum ath9k_key_type keytype:2; + u8 do_insert_ts; }; struct ath_rxbuf { diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6de64cface3c..8cbd6bfb8dfc 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -20,6 +20,7 @@ #include #include "ath9k.h" +#include "ar9003_azimuth.h" #define REG_WRITE_D(_ah, _reg, _val) \ ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) @@ -1167,6 +1168,33 @@ static ssize_t write_file_tpc(struct file *file, const char __user *user_buf, return count; } +static const struct file_operations fops_azimuth_timestamp = { + .read = read_file_azimuth_timestamp, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +static const struct file_operations fops_azimuth_clk = { + .read = read_file_azimuth_clk, + .write = write_file_azimuth_clk, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +static const struct file_operations fops_azimuth_reset = { + .read = read_file_azimuth_reset, + .write = write_file_azimuth_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +static const struct file_operations fops_azimuth_reset_notif = { + .read = read_file_azimuth_reset_notif, + .write = write_file_azimuth_reset_notif, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; static const struct file_operations fops_tpc = { .read = read_file_tpc, .write = write_file_tpc, @@ -1393,5 +1421,14 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("tpc", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_tpc); + debugfs_create_file("azimuth_timestamp", S_IRUSR, + sc->debug.debugfs_phy, sc, &fops_azimuth_timestamp); + debugfs_create_file("azimuth_gpio_clk", S_IRUSR, + sc->debug.debugfs_phy, sc, &fops_azimuth_clk); + debugfs_create_file("azimuth_gpio_reset", S_IRUSR, + sc->debug.debugfs_phy, sc, &fops_azimuth_reset); + debugfs_create_file("azimuth_gpio_reset_notif", S_IRUSR, + sc->debug.debugfs_phy, sc, &fops_azimuth_reset_notif); + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 41382f89abe1..9efc0e0621a7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -28,6 +28,7 @@ #include "ar9003_mac.h" #include "ar9003_mci.h" #include "ar9003_phy.h" +#include "ar9003_azimuth.h" #include "ath9k.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -388,7 +389,7 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.cwm_ignore_extcca = false; ah->config.analog_shiftreg = 1; - ah->config.rx_intr_mitigation = true; + ah->config.rx_intr_mitigation = false; if (AR_SREV_9300_20_OR_LATER(ah)) { ah->config.rimt_last = 500; @@ -1782,6 +1783,19 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) if (!ret) goto fail; + /* If we're doing azimuth mode, flip it on */ + if (AR_SREV_9300_20_OR_LATER(ah)) { + ar9003_azimuth_reset_hw(ah); + + /* + * And for now, reset the timer hardware so + * we can immediately start using it. + * Later on, it'll be controlled by a GPIO + * from the CPLD, and/or we'll reset both at once. + */ + ar9003_azimuth_reset_timer(ah); + } + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_2g5g_switch(ah, false); @@ -1835,6 +1849,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; + /* Track resets! */ + ah->reset_gen++; + if (ah->curchan && !ah->chip_fullsleep) ath9k_hw_getnf(ah, ah->curchan); @@ -1917,6 +1934,19 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); + /* If we're doing azimuth mode, flip it on */ + if (AR_SREV_9300_20_OR_LATER(ah)) { + ar9003_azimuth_reset_hw(ah); + + /* + * And for now, reset the timer hardware so + * we can immediately start using it. + * Later on, it'll be controlled by a GPIO + * from the CPLD, and/or we'll reset both at once. + */ + ar9003_azimuth_reset_timer(ah); + } + /* * Some AR91xx SoC devices frequently fail to accept TSF writes * right after the chip reset. When that happens, write a new @@ -2385,6 +2415,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) u8 ant_div_ctl1, tx_chainmask, rx_chainmask; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0); + /* XXX adrian temporary hack */ + ath_warn(common, + "%s: NOTE: overriding regulatory domain to 0x10 (FCC)\n", + __func__); + eeval = 0x10; regulatory->current_rd = eeval; if (ah->opmode != NL80211_IFTYPE_AP && @@ -2652,6 +2687,49 @@ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio) } EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input); +void ath9k_hw_cfg_gpio_pdpu(struct ath_hw *ah, u32 gpio, int pval) +{ + struct ath_common *common = ath9k_hw_common(ah); + + BUG_ON(gpio >= ah->caps.num_gpio_pins); + /* + * XXX TODO: we can't do pins > 15 at the moment; that would require + * a second register. + */ + if (gpio > 15) { + ath_info(common, "%s: called for gpio pin %d; can't yet do!\n", __func__, gpio); + return; + } + REG_RMW(ah, AR_GPIO_PDPU, (pval << (gpio * 2)), (0x3 << (gpio * 2))); +} +EXPORT_SYMBOL(ath9k_hw_cfg_gpio_pdpu); + +void ath9k_hw_cfg_gpio_intr(struct ath_hw *ah, u32 gpio, bool enable, bool active_hi) +{ + + BUG_ON(gpio >= ah->caps.num_gpio_pins); + + /* + * Note: changing async<->sync requires ensuring the interrupt + * handling routine doesn't mask out the GPIO bits... + * + * Note: we can't use async interrupts for GPIO if the chip is asleep. + * That's why SYNC GPIO interrupts are a possibility. + */ + if (enable) { + ah->gpio_intr_sync_mask |= (1 << gpio); + if (active_hi) + REG_CLR_BIT(ah, AR_GPIO_INTR_POL, (1 << gpio)); + else + REG_SET_BIT(ah, AR_GPIO_INTR_POL, (1 << gpio)); + } else { + ah->gpio_intr_sync_mask &= ~(1 << gpio); + } + + /* XXX TODO: pull-up configuration (AR_GPIO_PDPU) */ +} +EXPORT_SYMBOL(ath9k_hw_cfg_gpio_intr); + u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) { #define MS_REG_READ(x, y) \ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 831a54415a25..a1ab2c52f61f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -406,8 +406,7 @@ enum ath9k_int { ATH9K_INT_RXPHY | ATH9K_INT_RXKCM | ATH9K_INT_SWBA | - ATH9K_INT_BMISS | - ATH9K_INT_GPIO, + ATH9K_INT_BMISS, ATH9K_INT_NOCARD = 0xffffffff }; @@ -816,6 +815,9 @@ struct ath_hw { struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; enum ath9k_int imask; + u32 gpio_intr_async_mask; + u32 gpio_intr_sync_mask; + u32 gpio_intr_status; u32 imrs2_reg; u32 txok_interrupt_mask; u32 txerr_interrupt_mask; @@ -975,6 +977,19 @@ struct ath_hw { bool tpc_enabled; u8 tx_power[Ar5416RateSize]; u8 tx_power_stbc[Ar5416RateSize]; + + /* azimuth mode configuration */ + struct { + /* -1 if it's not hooked up to GPIO */ + int ext_reset_gpio; + /* -1 if it's not hooked up to GPIO */ + int ext_clk25_gpio; + /* -1 if it's not hooked up to GPIO */ + int ext_reset_notif_gpio; + /* Yeah, it shouldn't be in here */ + struct timer_list reset_timer; + } azimuth; + u64 reset_gen; }; struct ath_bus_ops { @@ -1020,6 +1035,8 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan); /* GPIO / RFKILL / Antennae */ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio); +void ath9k_hw_cfg_gpio_intr(struct ath_hw *ah, u32 gpio, bool is_enabled, bool active_hi); +void ath9k_hw_cfg_gpio_pdpu(struct ath_hw *ah, u32 gpio, int pval); u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 2e2b92ba96b8..dda48426b79b 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -24,6 +24,7 @@ #include #include "ath9k.h" +#include "ar9003_azimuth.h" struct ath9k_eeprom_ctx { struct completion complete; @@ -664,6 +665,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath_chanctx_init(sc); ath9k_offchannel_init(sc); + ar9003_azimuth_init(sc->sc_ah); + if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); @@ -998,6 +1001,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) ath9k_deinit_p2p(sc); ath9k_deinit_btcoex(sc); + ar9003_azimuth_destroy(sc->sc_ah); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index bba85d1a6cd1..1b004f8b568d 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -829,6 +829,10 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) if (ah->imask & ATH9K_INT_MCI) async_mask |= AR_INTR_ASYNC_MASK_MCI; + /* Enable the GPIO interrupt(s) */ + async_mask |= ah->gpio_intr_async_mask << AR_INTR_ASYNC_ENABLE_GPIO_S; + sync_default |= ah->gpio_intr_sync_mask << AR_INTR_SYNC_ENABLE_GPIO_S; + ath_dbg(common, INTERRUPT, "enable IER\n"); REG_WRITE(ah, AR_IER, AR_IER_ENABLE); if (!AR_SREV_9100(ah)) { @@ -843,6 +847,47 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_enable_interrupts); +/* + * This is called to update the mask registers to allow for + * the GPIO interrupt mask to be enabled/disabled as appropriate. + * + * However, note that it should only run if interrupts ARE running; + * otherwise just wait for the mask to be updated as appropriate. + */ +void ath9k_hw_update_interrupts(struct ath_hw *ah) +{ + u32 ae, am, se, sm; + + if (AR_SREV_9100(ah)) { + return; + } + + ae = REG_READ(ah, AR_INTR_ASYNC_ENABLE); + am = REG_READ(ah, AR_INTR_ASYNC_MASK); + se = REG_READ(ah, AR_INTR_SYNC_ENABLE); + sm = REG_READ(ah, AR_INTR_SYNC_MASK); + + /* Mask out the GPIO bits */ + ae &= ~(AR_INTR_ASYNC_MASK_GPIO); + am &= ~(AR_INTR_ASYNC_MASK_GPIO); + se &= ~(AR_INTR_SYNC_MASK_GPIO); + sm &= ~(AR_INTR_SYNC_MASK_GPIO); + + /* Update the gpio bits */ + ae |= ah->gpio_intr_async_mask << AR_INTR_ASYNC_ENABLE_GPIO_S; + am |= ah->gpio_intr_async_mask << AR_INTR_ASYNC_ENABLE_GPIO_S; + se |= ah->gpio_intr_sync_mask << AR_INTR_SYNC_ENABLE_GPIO_S; + sm |= ah->gpio_intr_sync_mask << AR_INTR_SYNC_ENABLE_GPIO_S; + + /* Write back */ + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, ae); + REG_WRITE(ah, AR_INTR_ASYNC_MASK, am); + + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, se); + REG_WRITE(ah, AR_INTR_SYNC_MASK, sm); +} +EXPORT_SYMBOL(ath9k_hw_update_interrupts); + void ath9k_hw_set_interrupts(struct ath_hw *ah) { enum ath9k_int ints = ah->imask; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 7fbf7f965f61..a8bab76561a1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -265,6 +265,7 @@ struct ath_desc { #define ATH9K_TXDESC_LOWRXCHAIN 0x0400 #define ATH9K_TXDESC_LDPC 0x0800 #define ATH9K_TXDESC_CLRDMASK 0x1000 +#define ATH9K_TXDESC_INSERT_TS 0x2000 #define ATH9K_TXDESC_PAPRD 0x70000 #define ATH9K_TXDESC_PAPRD_S 16 @@ -746,6 +747,7 @@ void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set); bool ath9k_hw_intrpend(struct ath_hw *ah); void ath9k_hw_set_interrupts(struct ath_hw *ah); void ath9k_hw_enable_interrupts(struct ath_hw *ah); +void ath9k_hw_update_interrupts(struct ath_hw *ah); void ath9k_hw_disable_interrupts(struct ath_hw *ah); void ath9k_hw_kill_interrupts(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d184e682e636..23da8d474c77 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -18,6 +18,7 @@ #include #include "ath9k.h" #include "btcoex.h" +#include "ar9003_azimuth.h" u8 ath9k_parse_mpdudensity(u8 mpdudensity) { @@ -568,6 +569,29 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_hw_set_interrupts(ah); } + /* + * This is a hack to fast-path reset the azimuth timestamp + * and notify the caller that it should again toggle the reset + * line. + * + * It's disabled for now as we are currently driving the + * azimuth timestamp reset path and will notify them via + * a GPIO line as to what's going on. If we also flip this + * on then we'll get an interrupt on that line as well! + */ + if (status & ATH9K_INT_GPIO) { +#if 0 + /* Handle GPIO interrupts */ + ath_dbg(common, RESET, "%s: status=0x%08x, gpio mask=0x%08x\n", __func__, status, ah->gpio_intr_status); + if ((ah->azimuth.ext_reset_gpio > -1) && + (ah->gpio_intr_status & (1 << (ah->azimuth.ext_reset_gpio)))) { + /* Ok, the line is in reset, do the reset thing */ + ar9003_azimuth_reset_timestamp(ah); + } +#endif + ah->gpio_intr_status = 0; + } + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) if (status & ATH9K_INT_TIM_TIMER) { if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle)) @@ -691,6 +715,9 @@ static int ath9k_start(struct ieee80211_hw *hw) ATH9K_INT_RXORN | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; + /* GPIO interrupts */ + ah->imask |= ATH9K_INT_GPIO; + if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ah->imask |= ATH9K_INT_RXHP | ATH9K_INT_RXLP; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 994daf6c6297..cd2f064ad23a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -18,8 +18,142 @@ #include "ath9k.h" #include "ar9003_mac.h" +/* Azimuth timestamp */ +#include +#include +#include + #define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb)) +/* from xmit.c */ +extern u64 ath_pkt_duration_nsec(struct ath_softc *sc, u8 rix, int pktlen, + int width, int half_gi, bool shortPreamble); + +/* + * This for legacy frame length calculations (11abg). + * + * rate is in 100kbps, need to fix + * erp is "am i 11g rate" + * shift is for 5/10MHz channels + * + * Return duration in microseconds. + */ +static u64 +ath_abg_frame_dur(struct ath_softc *sc, enum ieee80211_band band, + size_t len, int rate, int erp, int short_preamble, int shift) +{ + u64 dur; + + /* calculate duration (in microseconds, rounded up to next higher + * integer if it includes a fractional microsecond) to send frame of + * len bytes at the given rate. + * + * rate is in 100 kbps, so divident is multiplied by 10 in the + * DIV_ROUND_UP() operations. + * + * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and + * is assumed to be 0 otherwise. + */ + + if (band == IEEE80211_BAND_5GHZ || erp) { + /* + * OFDM: + * + * N_DBPS = DATARATE x 4 + * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) + * (16 = SIGNAL time, 6 = tail bits) + * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext + * + * T_SYM = 4 usec + * 802.11a - 18.5.2: aSIFSTime = 16 usec + * 802.11g - 19.8.4: aSIFSTime = 10 usec + + * signal ext = 6 usec + */ +// dur = 16; /* SIFS + signal ext */ + /* Don't do SIFS */ + if (band == IEEE80211_BAND_5GHZ) + dur = 0; + else + dur = 6; + + dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ + dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ + + /* + * At least for 2G OFDM, we're seeing another 2uS overhead. + * I'm not sure what that is just yet, but it's not symbol + * rounding. + */ + if (erp) + dur += 2; + + /* IEEE 802.11-2012 18.3.2.4: all values above are: + * * times 4 for 5 MHz + * * times 2 for 10 MHz + */ + dur *= 1 << shift; + + /* + * rates should already consider the channel bandwidth, + * don't apply divisor again. + */ + + dur += 4 * DIV_ROUND_UP(((16 + 8 * (len) + 6) * 10), + 4 * rate); /* T_SYM x N_SYM */ + } else { + /* + * ADRIAN: XXX TODO we're still out by 2.4uS in our + * testing on 1, 2, 5.5, 11mbit, long preamble. + * (with 130-300 byte payloads.) + */ + + /* + * 802.11b or 802.11g with 802.11b compatibility: + * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + + * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. + * + * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 + * aSIFSTime = 10 usec + * aPreambleLength = 144 usec or 72 usec with short preamble + * aPLCPHeaderLength = 48 usec or 24 usec with short preamble + */ + //dur = 10; /* aSIFSTime = 10 usec */ + dur = 0; /* no SIFS time */ + dur += short_preamble ? (72 + 24) : (144 + 48); + dur += DIV_ROUND_UP((8 * (len) * 10), rate); + } + + return dur; +} + +/* return frame duration in nanoseconds */ +static u64 +ath_rx_frame_dur(struct ath_softc *sc, const struct ieee80211_rx_status *rxs, int pktlen) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + bool is_40 = !! (rxs->flag & RX_FLAG_40MHZ); + bool half_gi = !! (rxs->flag & RX_FLAG_SHORT_GI); + bool sp = !! (rxs->flag & RX_FLAG_SHORTPRE); + bool is_erp; + + band = ah->curchan->chan->band; + sband = hw->wiphy->bands[band]; + + /* Check rate - if it's 11n, return that */ + if (rxs->flag & RX_FLAG_HT) { + return ath_pkt_duration_nsec(sc, rxs->rate_idx & 0x7f, pktlen, is_40, half_gi, sp); + } + + /* it's legacy, do that */ + is_erp = !! (sband->bitrates[rxs->rate_idx].flags & IEEE80211_RATE_ERP_G); + return ath_abg_frame_dur(sc, band, pktlen, + sband->bitrates[rxs->rate_idx].bitrate, + is_erp, sp, 0) * 1000; +} + static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { return sc->ps_enabled && @@ -785,7 +919,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs, u64 tsf) { u32 tsf_lower = tsf & 0xffffffff; - rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp; if (rs->rs_tstamp > tsf_lower && unlikely(rs->rs_tstamp - tsf_lower > 0x10000000)) @@ -796,6 +929,22 @@ static void ath9k_process_tsf(struct ath_rx_status *rs, rxs->mactime += 0x100000000ULL; } +static void ath9k_process_tsf_azm(struct ath_rx_status *rs, + struct ieee80211_rx_status *rxs, u64 tsf) +{ + + /* + * If we're doing azimuth mode, then we can't use the + * descriptor timestamp as it'll be "wrong". + * Instead, just give every frame the 64 bit timestamp + * provided. + * + * We'll figure out the right way to punt up a higher + * resolution timestamp later. + */ + rxs->mactime = tsf; +} + /* * For Decrypt or Demic errors, we only mark packet status here and always push * up the frame up to let mac80211 handle the actual error case, be it no @@ -856,7 +1005,13 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len); - ath9k_process_tsf(rx_stats, rx_status, tsf); + /* Temporary hack for azimuth mode - if not scanning, assume azimuth mode */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags)) { + ath9k_process_tsf(rx_stats, rx_status, tsf); + } else { + ath9k_process_tsf_azm(rx_stats, rx_status, tsf); + } + ath_debug_stat_rx(sc, rx_stats); /* @@ -918,6 +1073,13 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, rx_status->antenna = rx_stats->rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_END; + /* + * XXX TODO: store the azimuth RX timestamp somewhere + * so we can eventually store it in the payload + * of 'magic packets' when we send them back up the + * stack. + */ + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT if (ieee80211_is_data_present(hdr->frame_control) && !ieee80211_is_qos_nullfunc(hdr->frame_control)) @@ -988,6 +1150,196 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc, } } +/* XXX duplicate from the TX side, should unify! */ + +/* + * Only match on full UDP frames, no IPv4 fragments, and ensure + * we have the full payload first. + */ +static int azimuth_rx_skb_check_and_add(struct ath_softc *sc, + const struct ath_rx_status *rs, + const struct ieee80211_rx_status *rxs, + struct sk_buff *skb, int orig_len) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct iphdr *ip; + struct udphdr *udp; + struct ieee80211_hdr *hdr; + char *d; + int hdrlen; + int ether_len; + int ipv4_len; + int ipv4_hdrlen; +// int padsize; + u16 eth_type; + u32 rgen; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + /* Must be a data frame */ + if (! ieee80211_is_data(hdr->frame_control)) { +// ath_dbg(common, XMIT_PAYLOAD, "%s: not data\n", __func__); + return (0); + } + + +#if 0 + /* Figure out the padding to the ethernet payload */ + hdrlen = roundup(hdrlen, 4); + if (hdrlen >= skb->len) { + ath_dbg(common, XMIT_PAYLOAD, "%s: too short; hdrlen=%d, skblen=%d\n", __func__, hdrlen, skb->len); + return (0); + } +#endif + + /* Must be long enough to have an ethernet payload */ + ether_len = eth_get_headlen(skb->data + hdrlen, skb->len - hdrlen); + if (ether_len >= skb->len - hdrlen) { + ath_dbg(common, XMIT_PAYLOAD, "%s: short ether frame? ether_len=%d, skb_len=%d, hdrlen=%d\n", + __func__, ether_len, skb->len, hdrlen); + return (0); + } + + /* XXX for now */ + d = skb->data + hdrlen + ether_len; + + /* ether_len gets us to the ethertype; so we add 2 to skip that */ + ipv4_len = skb->len - (hdrlen + ether_len + 2); + + ath_dbg(common, XMIT_PAYLOAD, "%s: called, len=%d, hdrlen=%d, ether_len=%d, ipv4_len=%d\n", __func__, skb->len, hdrlen, ether_len, ipv4_len); + + if (ipv4_len < 0) { + return (0); + } + eth_type = be16_to_cpu(((u16 *)d)[0]); + if (eth_type != 0x0800) { + ath_dbg(common, XMIT_PAYLOAD, "%s: not IPv4 (type 0x%04x)\n", + __func__, + (int) (eth_type) & 0xffff); + return (0); + } + +#if 0 + if (common->debug_mask & ATH_DBG_XMIT_PAYLOAD) { + //print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, d, ipv4_len, 1); + print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, skb->data, skb->len, 1); + } +#endif + + + /* Everything below this is manual; the sk_buff transport/proto stuff isn't done */ + /* Forward to the UDP header */ + ip = (struct iphdr *) (d + sizeof(u16)); + + /* Must be UDP */ + if (ip->protocol != IPPROTO_UDP) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr is not UDP (proto %d)\n", __func__, + ip->protocol); + return (0); + } + +#define IP_OFFMASK 0x1fff +#define IP_MF 0x2000 + /* Must not be a fragment */ + if ((ntohs(ip->frag_off) & IP_OFFMASK) != 0) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr has a fragment offset\n", __func__); + return (0); + } + if (ntohs(ip->frag_off) & IP_MF) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr has more-fragment bit set\n", __func__); + return (0); + } +#undef IP_MF +#undef IP_OFFMASK + + /* Skip IP header + IP options */ + ipv4_hdrlen = (ip->ihl * 4); + ath_dbg(common, XMIT_PAYLOAD, "%s: ipv4_hdrlen (%d); ipv4_len (%d)\n", + __func__, ipv4_hdrlen, ipv4_len); + + if (ipv4_hdrlen < 20 || ipv4_hdrlen >= ipv4_len) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ipv4_hdrlen (%d) > ipv4_len (%d)\n", + __func__, ipv4_hdrlen, ipv4_len); + return (0); + } + + udp = (struct udphdr *) (((char *) ip) + ipv4_hdrlen); + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, check: (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + + /* Ensure the UDP length isn't bogus and is within the current frame we have */ + /* XXX TODO */ + + /* check source/dest match and payload length */ + if (be16_to_cpu(udp->source) != 42424) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, wrong sport (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + if (be16_to_cpu(udp->dest) != 42424) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, wrong dport (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + if (be16_to_cpu(udp->len) < (sizeof(struct udphdr) + 20)) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, too short (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + + /* + * Ok, so now zero out the udp header checksum as we don't want to send that, + * insert the timestamp in the second last DWORD, and return true. + */ + ath_dbg(common, XMIT_PAYLOAD, "%s: yes! inserting timestamp %d (%x)\n", __func__, rs->rs_tstamp, rs->rs_tstamp); + memcpy(((char *) udp) + be16_to_cpu(udp->len) - 8, + &rs->rs_tstamp, 4); + + /* + * Now - is it an aggregate? If it is, we can't trust the + * timestamping, so just zero everything out. + */ + if (rs->rs_isaggr) { + ath_dbg(common, XMIT_PAYLOAD, "%s: received aggregate, zero'ing\n", __func__); + memset(((char *) udp) + be16_to_cpu(udp->len) - 20, 0, 20); + return 1; + } + + /* And insert the current generation counter */ + rgen = (u32) cpu_to_le32(sc->sc_ah->reset_gen); + memcpy(((char *) udp) + be16_to_cpu(udp->len) - 16, + &rgen, 4); + + /* And insert the calculated frame duration, in nsec */ + /* XXX TODO: hope the frame isn't longer than 4ms .. :) */ + rgen = (u32) cpu_to_le32(ath_rx_frame_dur(sc, rxs, orig_len)); + memcpy(((char *) udp) + be16_to_cpu(udp->len) - 20, + &rgen, 4); + + /* Make sure the checksum is zeroed out */ + udp->check = 0; + + if (common->debug_mask & ATH_DBG_XMIT_PAYLOAD) { + //print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, d, ipv4_len, 1); + print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, skb->data, skb->len, 1); + } + + return (1); +} + int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { struct ath_rxbuf *bf; @@ -1006,6 +1358,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) dma_addr_t new_buf_addr; unsigned int budget = 512; struct ieee80211_hdr *hdr; + int orig_len; if (edma) dma_type = DMA_BIDIRECTIONAL; @@ -1120,6 +1473,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) skb = hdr_skb; } + /* Take a copy of the orig_len before we trim the MMIC */ + orig_len = skb->len; + if (rxs->flag & RX_FLAG_MMIC_STRIPPED) skb_trim(skb, skb->len - 8); @@ -1139,6 +1495,22 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (ieee80211_is_ack(hdr->frame_control)) ath_dynack_sample_ack_ts(sc->sc_ah, skb, rs.rs_tstamp); + /* + * XXX TODO: + * + * Here's where we look at the original RX TSF for azimuth mode + * and if it's a magic packet, we override the second-last DWORD + * in the payload (before the IV/CRC!) with the azimuth TSF in + * the descriptor. + * + * TODO: we should do it for the last frame in an A-MPDU, right? + * So don't copy in azimuth timestamps for non-last frames. + * + * original descriptor timestamp is in rs.rs_tstamp + */ + if (! test_bit(ATH_OP_SCANNING, &common->op_flags)) { + (void) azimuth_rx_skb_check_and_add(sc, &rs, rxs, skb, orig_len); + } ieee80211_rx(hw, skb); requeue_drop_frag: diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index caba54ddad25..2bd828d0a65f 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -1215,7 +1215,14 @@ enum { #define AR_OBS (AR_SREV_9340(ah) ? 0x405c : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x4088 : 0x4080)) +/* + * AR_GPIO_PDPU controls the pull up/down for the first 16 GPIO registers. + * Each GPIO has a 2 bit field (below) controlling the pull down/up configuration. + */ #define AR_GPIO_PDPU (AR_SREV_9300_20_OR_LATER(ah) ? 0x4090 : 0x4088) +#define AR_GPIO_PDPU_VAL_NONE 0 +#define AR_GPIO_PDPU_VAL_PULL_DOWN 1 +#define AR_GPIO_PDPU_VAL_PULL_UP 2 #define AR_PCIE_MSI (AR_SREV_9340(ah) ? 0x40d8 : \ (AR_SREV_9300_20_OR_LATER(ah) ? 0x40a4 : 0x4094)) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3e3dac3d7060..e8fac7b4a87b 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -18,6 +18,11 @@ #include "ath9k.h" #include "ar9003_mac.h" +/* Azimuth timestamp */ +#include +#include +#include + #define BITS_PER_BYTE 8 #define OFDM_PLCP_BITS 22 #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) @@ -1024,7 +1029,7 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq, * width - 0 for 20 MHz, 1 for 40 MHz * half_gi - to use 4us v/s 3.6 us for symbol time */ -static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, +u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, int width, int half_gi, bool shortPreamble) { u32 nbits, nsymbits, duration, nsymbols; @@ -1047,6 +1052,49 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, int pktlen, return duration; } +/* + * rix - rate index + * pktlen - total bytes (delims + data + fcs + pads + pad delims) + * width - 0 for 20 MHz, 1 for 40 MHz + * half_gi - to use 4us v/s 3.6 us for symbol time + */ +u64 ath_pkt_duration_nsec(struct ath_softc *sc, u8 rix, int pktlen, + int width, int half_gi, bool shortPreamble) +{ + u64 duration, nsymbols; + u32 nbits, nsymbits; + int streams; + + /* find number of symbols: PLCP + data */ + streams = HT_RC_2_STREAMS(rix); + nbits = (pktlen << 3) + OFDM_PLCP_BITS; + nsymbits = bits_per_symbol[rix % 8][width] * streams; + nsymbols = (nbits + nsymbits - 1) / nsymbits; + + // Up it to nsec + nsymbols *= 1000; + + if (!half_gi) + duration = SYMBOL_TIME(nsymbols); + else + duration = SYMBOL_TIME_HALFGI(nsymbols); + + /* addup duration for legacy/ht training and signal fields */ + duration += (L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams)) * 1000; + + /* Osprey? XXX check */ +#if 0 + /* We're always 8uS (2 symbols?) out; why's that? */ + duration += (8 * 1000); +#endif + /* Peacock */ + duration += 2000; + if (streams > 2) + duration += 4000; + + return duration; +} + static int ath_max_framelen(int usec, int mcs, bool ht40, bool sgi) { int streams = HT_RC_2_STREAMS(mcs); @@ -1336,6 +1384,10 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (tx_info->flags & IEEE80211_TX_CTL_LDPC) info.flags |= ATH9K_TXDESC_LDPC; + /* Azimuth timestamp insert in payload */ + if (fi->do_insert_ts) + info.flags |= ATH9K_TXDESC_INSERT_TS; + if (bf->bf_state.bfs_paprd) info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S; @@ -2263,6 +2315,146 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, return 0; } +static bool ath_do_insert_azimuth_ts(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct iphdr *ip; + struct udphdr *udp; + struct ieee80211_hdr *hdr; + char *d; + int hdrlen; + int ether_len; + int ipv4_len; + u32 rgen; + u16 eth_type; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + ath_dbg(common, XMIT_PAYLOAD, "%s: called, len=%d, hdrlen=%d\n", __func__, skb->len, hdrlen); + + /* Must be a data frame */ + if (! ieee80211_is_data(hdr->frame_control)) { + ath_dbg(common, XMIT_PAYLOAD, "%s: not data\n", __func__); + return (0); + } + + /* Figure out the padding to the ethernet payload */ + hdrlen = roundup(hdrlen, 4); + if (hdrlen >= skb->len) { + ath_dbg(common, XMIT_PAYLOAD, "%s: too short; hdrlen=%d, skblen=%d\n", __func__, hdrlen, skb->len); + return (0); + } + + /* Must be long enough to have an ethernet payload */ + ether_len = eth_get_headlen(skb->data + hdrlen, skb->len - hdrlen); + if (ether_len >= skb->len - hdrlen) { + ath_dbg(common, XMIT_PAYLOAD, "%s: short ether frame? ether_len=%d, skb_len=%d, hdrlen=%d\n", + __func__, ether_len, skb->len, hdrlen); + return (0); + } + + /* XXX for now */ + d = skb->data + hdrlen + ether_len; + ipv4_len = skb->len - (hdrlen + ether_len); + + if (ipv4_len < 0) { + return (0); + } + eth_type = be16_to_cpu(((u16 *)d)[0]); + if (eth_type != 0x0800) { + ath_dbg(common, XMIT_PAYLOAD, "%s: not IPv4 (type 0x%04x)\n", + __func__, + (int) (eth_type) & 0xffff); + return (0); + } + + /* Forward to the UDP header */ + ip = ip_hdr(skb); + if (ip == NULL) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr returned NULL\n", __func__); + return (0); + } + + /* Must be UDP */ + if (ip->protocol != IPPROTO_UDP) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr is not UDP (proto %d)\n", __func__, + ip->protocol); + return (0); + } + +#define IP_OFFMASK 0x1fff +#define IP_MF 0x2000 + /* Must not be a fragment */ + if ((ntohs(ip->frag_off) & IP_OFFMASK) != 0) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr has a fragment offset\n", __func__); + return (0); + } + if (ntohs(ip->frag_off) & IP_MF) { + ath_dbg(common, XMIT_PAYLOAD, "%s: ip_hdr has more-fragment bit set\n", __func__); + return (0); + } +#undef IP_MF +#undef IP_OFFMASK + + udp = udp_hdr(skb); + if (udp == NULL) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr returned NULL\n", __func__); + return (0); + } + + /* check source/dest match and payload length */ + if (be16_to_cpu(udp->source) != 42424) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, wrong sport (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + if (be16_to_cpu(udp->dest) != 42424) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, wrong dport (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + if (be16_to_cpu(udp->len) < (sizeof(struct udphdr) + 20)) { + ath_dbg(common, XMIT_PAYLOAD, "%s: udp_hdr, too short (src=%d, dst=%d, len=%d)\n", + __func__, + be16_to_cpu(udp->source), + be16_to_cpu(udp->dest), + be16_to_cpu(udp->len)); + return (0); + } + + /* Check UDP length fits within the current payload */ + /* XXX TODO */ + + /* + * Ok, so now zero out the udp header checksum as we don't want to send that, + * and we then return true. + */ + ath_dbg(common, XMIT_PAYLOAD, "%s: yes!\n", __func__); + + /* This sets the checksum field to zero */ + udp_set_csum(false, skb, 0, 0, 0); + + /* Populate the generation counter */ + rgen = (u32) cpu_to_le32(sc->sc_ah->reset_gen); + memcpy(((char *) udp) + be16_to_cpu(udp->len) - 12, + &rgen, 4); + + udp->check = 0; + + if (common->debug_mask & ATH_DBG_XMIT_PAYLOAD) { + //print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, d, ipv4_len, 1); + print_hex_dump(KERN_DEBUG, "payload:", DUMP_PREFIX_ADDRESS, 16, 1, d, ipv4_len, 1); + } + + return (1); +} /* Upon failure caller should free skb */ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, @@ -2299,6 +2491,14 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, * info are no longer valid (overwritten by the ath_frame_info data. */ + /* + * Check to see if this frame should go out with the AR_insert_ts + * flag set. + */ + if (ath_do_insert_azimuth_ts(sc, skb)) { + fi->do_insert_ts = 1; + } + q = skb_get_queue_mapping(skb); ath_txq_lock(sc, txq); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 06ea6cc9e30a..e7bfe437179e 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -37,18 +37,18 @@ static int __ath_regd_init(struct ath_regulatory *reg); /* We enable active scan on these a case by case basis by regulatory domain */ #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ - NL80211_RRF_NO_IR) + 0) #define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ - NL80211_RRF_NO_IR | \ + 0 | \ NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ #define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ - NL80211_RRF_NO_IR) + 0) #define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ - NL80211_RRF_NO_IR) + 0) #define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ - NL80211_RRF_NO_IR) + 0) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ ATH9K_2GHZ_CH12_13, \ @@ -74,42 +74,40 @@ static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = { /* Can be used by 0x63 and 0x65 */ static const struct ieee80211_regdomain ath_world_regdom_63_65 = { - .n_reg_rules = 4, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { - ATH9K_2GHZ_CH01_11, - ATH9K_2GHZ_CH12_13, - ATH9K_5GHZ_NO_MIDBAND, + ATH9K_2GHZ_ALL, + ATH9K_5GHZ_ALL, } }; /* Can be used by 0x64 only */ static const struct ieee80211_regdomain ath_world_regdom_64 = { - .n_reg_rules = 3, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { - ATH9K_2GHZ_CH01_11, - ATH9K_5GHZ_NO_MIDBAND, + ATH9K_2GHZ_ALL, + ATH9K_5GHZ_ALL, } }; /* Can be used by 0x66 and 0x69 */ static const struct ieee80211_regdomain ath_world_regdom_66_69 = { - .n_reg_rules = 3, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { - ATH9K_2GHZ_CH01_11, + ATH9K_2GHZ_ALL, ATH9K_5GHZ_ALL, } }; /* Can be used by 0x67, 0x68, 0x6A and 0x6C */ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = { - .n_reg_rules = 4, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { - ATH9K_2GHZ_CH01_11, - ATH9K_2GHZ_CH12_13, + ATH9K_2GHZ_ALL, ATH9K_5GHZ_ALL, } };