u32 preamble_ovr; /* preamble override */
};
-/* local prototypes */
-static void brcms_b_clkctl_clk(struct brcms_hardware *wlc, uint mode);
-
-/* used by wlc_wakeucode_init() */
-static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
- const struct d11init *inits);
-static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const u32 ucode[],
- const uint nbytes);
-static void brcms_ucode_download(struct brcms_hardware *wlc);
-static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw);
-
-/* used by brcms_c_down() */
-static void brcms_c_flushqueues(struct brcms_c_info *wlc);
-
-static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs);
-static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw);
-static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw);
-static bool brcms_b_tx_fifo_suspended(struct brcms_hardware *wlc_hw,
- uint tx_fifo);
-static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
- uint tx_fifo);
-
-/* Low Level Prototypes */
-
-struct brcms_b_state;
-
-static int brcms_b_attach(struct brcms_c_info *wlc, u16 vendor, u16 device,
- uint unit, bool piomode, void *regsva,
- struct pci_dev *btparam);
-
-/* up/down, reset, clk */
-static void brcms_b_reset(struct brcms_hardware *wlc_hw);
-static int brcms_b_state_get(struct brcms_hardware *wlc_hw,
- struct brcms_b_state *state);
-static void brcms_b_copyfrom_vars(struct brcms_hardware *wlc_hw, char **buf,
- uint *len);
-
-static void brcms_b_hw_etheraddr(struct brcms_hardware *wlc_hw,
- u8 *ea);
-
-static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw);
-static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw);
-
-static void brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw,
- int match_reg_offset,
- const u8 *addr);
-static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin);
-static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax);
-
-static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, u16 SRL,
- u16 LRL);
-
-static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw);
-
-static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set,
- u32 req_bit);
-static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw,
- u32 antsel_avail);
-static int brcms_b_bandtype(struct brcms_hardware *wlc_hw);
-static void brcms_b_info_init(struct brcms_hardware *wlc_hw);
-static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want);
-static u16 brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset,
- u32 sel);
-static void brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset,
- u16 v, u32 sel);
-static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk);
-static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme);
-static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw);
-static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw);
-static bool brcms_c_validboardtype(struct brcms_hardware *wlc);
-static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw);
-static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw);
-static char *brcms_c_get_macaddr(struct brcms_hardware *wlc_hw);
-static void brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init);
-static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw);
-static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool want, u32 flags);
-static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw);
-static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw);
-static u32 brcms_c_wlintrsoff(struct brcms_c_info *wlc);
-static void brcms_c_wlintrsrestore(struct brcms_c_info *wlc, u32 macintmask);
-static void brcms_c_gpio_init(struct brcms_c_info *wlc);
-static void brcms_c_write_hw_bcntemplate0(struct brcms_hardware *wlc_hw,
- u16 bcn[], int len);
-static void brcms_c_write_hw_bcntemplate1(struct brcms_hardware *wlc_hw,
- u16 bcn[], int len);
-static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec);
-static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit);
-static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit,
- u16 chanspec);
-static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw,
- bool shortslot);
-static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw);
-static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
- u8 rate);
-
-static u16 brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc,
- struct ieee80211_hw *hw,
- struct sk_buff *p,
- struct scb *scb, uint frag,
- uint nfrags, uint queue,
- uint next_frag_len,
- struct wsec_key *key,
- u32 rspec_override);
-static void brcms_c_bss_default_init(struct brcms_c_info *wlc);
-static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc);
-static u32 mac80211_wlc_set_nrate(struct brcms_c_info *wlc,
- struct brcms_band *cur_band,
- u32 int_val);
-static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc);
-static void brcms_c_watchdog(void *arg);
-static void brcms_c_watchdog_by_timer(void *arg);
-static u16 brcms_c_rate_shm_offset(struct brcms_c_info *wlc, u8 rate);
-static int brcms_c_set_rateset(struct brcms_c_info *wlc,
- struct brcms_c_rateset *rs_arg);
-static u8 brcms_c_local_constraint_qdbm(struct brcms_c_info *wlc);
-
-/* send and receive */
-static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc);
-static void brcms_c_txq_free(struct brcms_c_info *wlc,
- struct brcms_txq_info *qi);
-static void brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc,
- struct brcms_txq_info *qi,
- bool on, int prio);
-static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc);
-static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rate,
- uint length, u8 *plcp);
-static void brcms_c_compute_ofdm_plcp(u32 rate, uint length, u8 *plcp);
-static void brcms_c_compute_mimo_plcp(u32 rate, uint length, u8 *plcp);
-static u16 brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type, uint next_frag_len);
-static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc,
- struct brcms_d11rxhdr *rxh);
-static void brcms_c_recvctl(struct brcms_c_info *wlc,
- struct d11rxhdr *rxh, struct sk_buff *p);
-static uint brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type, uint dur);
-static uint brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type);
-static uint brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type);
-/* interrupt, up/down, band */
-static void brcms_c_setband(struct brcms_c_info *wlc, uint bandunit);
-static u16 brcms_c_init_chanspec(struct brcms_c_info *wlc);
-static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc,
- u16 chanspec);
-static void brcms_c_bsinit(struct brcms_c_info *wlc);
-static int brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle,
- bool isOFDM, bool writeToShm);
-static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc);
-static bool brcms_c_radio_monitor_start(struct brcms_c_info *wlc);
-static void brcms_c_radio_timer(void *arg);
-static void brcms_c_radio_enable(struct brcms_c_info *wlc);
-static void brcms_c_radio_upd(struct brcms_c_info *wlc);
-
-/* scan, association, BSS */
-static uint brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type);
-static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap);
-static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val);
-static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val);
-static void brcms_c_war16165(struct brcms_c_info *wlc, bool tx);
-
-static void brcms_c_wme_retries_write(struct brcms_c_info *wlc);
-static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc);
-static uint brcms_c_attach_module(struct brcms_c_info *wlc);
-static void brcms_c_detach_module(struct brcms_c_info *wlc);
-static void brcms_c_timers_deinit(struct brcms_c_info *wlc);
-static void brcms_c_down_led_upd(struct brcms_c_info *wlc);
-static uint brcms_c_down_del_timer(struct brcms_c_info *wlc);
-static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc);
-static int _brcms_c_ioctl(struct brcms_c_info *wlc, int cmd, void *arg, int len,
- struct brcms_c_if *wlcif);
-static void brcms_c_write_hw_bcntemplates(struct brcms_c_info *wlc, u16 bcn[],
- int len, bool both);
-
const u8 prio2fifo[NUMPRIO] = {
TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */
TX_AC_BK_FIFO, /* 1 BK AC_BK Background */
}
}
+static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
+ const struct d11init *inits)
+{
+ int i;
+ u8 *base;
+ u8 *addr;
+ u16 size;
+ u32 value;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ base = (u8 *)wlc_hw->regs;
+
+ for (i = 0; inits[i].addr != 0xffff; i++) {
+ size = le16_to_cpu(inits[i].size);
+ addr = base + le16_to_cpu(inits[i].addr);
+ value = le32_to_cpu(inits[i].value);
+ if (size == 2)
+ W_REG((u16 *)addr, value);
+ else if (size == 4)
+ W_REG((u32 *)addr, value);
+ else
+ break;
+ }
+}
+
+static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs)
+{
+ u8 idx;
+ u16 addr[] = {
+ M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+ M_HOST_FLAGS5
+ };
+
+ for (idx = 0; idx < MHFMAX; idx++)
+ brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]);
+}
+
static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw)
{
struct wiphy *wiphy = wlc_hw->wlc->wiphy;
}
}
+static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk);
+
+ wlc_hw->phyclk = clk;
+
+ if (OFF == clk) { /* clear gmode bit, put phy into reset */
+
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC | SICF_GMODE),
+ (SICF_PRST | SICF_FGC));
+ udelay(1);
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_PRST);
+ udelay(1);
+
+ } else { /* take phy out of reset */
+
+ ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_FGC);
+ udelay(1);
+ ai_core_cflags(wlc_hw->sih, (SICF_FGC), 0);
+ udelay(1);
+
+ }
+}
+
/* switch to new band but leave it inactive */
static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit)
{
return morepending;
}
+/* brcms_b_tx_fifo_suspended:
+ * Check the MAC's tx suspend status for a tx fifo.
+ *
+ * When the MAC acknowledges a tx suspend, it indicates that no more
+ * packets will be transmitted out the radio. This is independent of
+ * DMA channel suspension---the DMA may have finished suspending, or may still
+ * be pulling data into a tx fifo, by the time the MAC acks the suspend
+ * request.
+ */
+static bool brcms_b_tx_fifo_suspended(struct brcms_hardware *wlc_hw,
+ uint tx_fifo)
+{
+ /* check that a suspend has been requested and is no longer pending */
+
+ /*
+ * for DMA mode, the suspend request is set in xmtcontrol of the DMA
+ * engine, and the tx fifo suspend at the lower end of the MAC is
+ * acknowledged in the chnstatus register.
+ *
+ * The tx fifo suspend completion is independent of the DMA suspend
+ * completion and may be acked before or after the DMA is suspended.
+ */
+ if (dma_txsuspended(wlc_hw->di[tx_fifo]) &&
+ (R_REG(&wlc_hw->regs->chnstatus) &
+ (1 << tx_fifo)) == 0)
+ return true;
+
+ return false;
+}
+
/* second-level interrupt processing
* Return true if another dpc needs to be re-scheduled. false otherwise.
* Param 'bounded' indicates if applicable loops should be bounded.
return 0;
}
+/* set initial host flags value */
+static void
+brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ memset(mhfs, 0, MHFMAX * sizeof(u16));
+
+ mhfs[MHF2] |= mhf2_init;
+
+ /* prohibit use of slowclock on multifunction boards */
+ if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+ mhfs[MHF1] |= MHF1_FORCEFASTCLK;
+
+ if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
+ mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
+ mhfs[MHF1] |= MHF1_IQSWAP_WAR;
+ }
+}
+
static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme)
{
uint i;
}
}
-/* set initial host flags value */
-static void
-brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init)
-{
- struct brcms_hardware *wlc_hw = wlc->hw;
-
- memset(mhfs, 0, MHFMAX * sizeof(u16));
-
- mhfs[MHF2] |= mhf2_init;
-
- /* prohibit use of slowclock on multifunction boards */
- if (wlc_hw->boardflags & BFL_NOPLLDOWN)
- mhfs[MHF1] |= MHF1_FORCEFASTCLK;
-
- if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
- mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
- mhfs[MHF1] |= MHF1_IQSWAP_WAR;
- }
-}
-
/* set or clear ucode host flag bits
* it has an optimization for no-change write
* it only writes through shared memory when the core has clock;
}
}
-static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs)
-{
- u8 idx;
- u16 addr[] = {
- M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
- M_HOST_FLAGS5
- };
-
- for (idx = 0; idx < MHFMAX; idx++)
- brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]);
-}
-
/* set the maccontrol register to desired reset state and
* initialize the sw cache of the register
*/
brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE);
}
+/*
+ * write the software state of maccontrol and
+ * overrides to the maccontrol register
+ */
+static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw)
+{
+ u32 maccontrol = wlc_hw->maccontrol;
+
+ /* OR in the wake bit if overridden */
+ if (wlc_hw->wake_override)
+ maccontrol |= MCTL_WAKE;
+
+ /* set AP and INFRA bits for mute if needed */
+ if (wlc_hw->mute_override) {
+ maccontrol &= ~(MCTL_AP);
+ maccontrol |= MCTL_INFRA;
+ }
+
+ W_REG(&wlc_hw->regs->maccontrol, maccontrol);
+}
+
/* set or clear maccontrol bits */
void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val)
{
brcms_c_mctrl_write(wlc_hw);
}
-/*
- * write the software state of maccontrol and
- * overrides to the maccontrol register
- */
-static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw)
-{
- u32 maccontrol = wlc_hw->maccontrol;
-
- /* OR in the wake bit if overridden */
- if (wlc_hw->wake_override)
- maccontrol |= MCTL_WAKE;
-
- /* set AP and INFRA bits for mute if needed */
- if (wlc_hw->mute_override) {
- maccontrol &= ~(MCTL_AP);
- maccontrol |= MCTL_INFRA;
- }
-
- W_REG(&wlc_hw->regs->maccontrol, maccontrol);
-}
-
void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
u32 override_bit)
{
brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v);
}
-/* band-specific init */
-static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec)
+static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw)
{
- struct brcms_hardware *wlc_hw = wlc->hw;
+ u16 phyctl;
+ u16 phytxant = wlc_hw->bmac_phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
- BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
- wlc_hw->band->bandunit);
+ /* set the Probe Response frame phy control word */
+ phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
+ phyctl = (phyctl & ~mask) | phytxant;
+ brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);
- brcms_c_ucode_bsinit(wlc_hw);
+ /* set the Response (ACK/CTS) frame phy control word */
+ phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD);
+ phyctl = (phyctl & ~mask) | phytxant;
+ brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
+}
- wlc_phy_init(wlc_hw->band->pi, chanspec);
+static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
+ u8 rate)
+{
+ uint i;
+ u8 plcp_rate = 0;
+ struct plcp_signal_rate_lookup {
+ u8 rate;
+ u8 signal_rate;
+ };
+ /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
+ const struct plcp_signal_rate_lookup rate_lookup[] = {
+ {BRCM_RATE_6M, 0xB},
+ {BRCM_RATE_9M, 0xF},
+ {BRCM_RATE_12M, 0xA},
+ {BRCM_RATE_18M, 0xE},
+ {BRCM_RATE_24M, 0x9},
+ {BRCM_RATE_36M, 0xD},
+ {BRCM_RATE_48M, 0x8},
+ {BRCM_RATE_54M, 0xC}
+ };
- brcms_c_ucode_txant_set(wlc_hw);
+ for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
+ if (rate == rate_lookup[i].rate) {
+ plcp_rate = rate_lookup[i].signal_rate;
+ break;
+ }
+ }
- /*
- * cwmin is band-specific, update hardware
- * with value for current band
+ /* Find the SHM pointer to the rate table entry by looking in the
+ * Direct-map Table
*/
- brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
- brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax);
+ return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
+}
- brcms_b_update_slot_timing(wlc_hw,
+static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw)
+{
+ u8 rate;
+ u8 rates[8] = {
+ BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M,
+ BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M
+ };
+ u16 entry_ptr;
+ u16 pctl1;
+ uint i;
+
+ if (!BRCMS_PHY_11N_CAP(wlc_hw->band))
+ return;
+
+ /* walk the phy rate table and update the entries */
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ rate = rates[i];
+
+ entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate);
+
+ /* read the SHM Rate Table entry OFDM PCTL1 values */
+ pctl1 =
+ brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);
+
+ /* modify the value */
+ pctl1 &= ~PHY_TXC1_MODE_MASK;
+ pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);
+
+ /* Update the SHM Rate Table entry OFDM PCTL1 values */
+ brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
+ pctl1);
+ }
+}
+
+/* band-specific init */
+static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec)
+{
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+ wlc_hw->band->bandunit);
+
+ brcms_c_ucode_bsinit(wlc_hw);
+
+ wlc_phy_init(wlc_hw->band->pi, chanspec);
+
+ brcms_c_ucode_txant_set(wlc_hw);
+
+ /*
+ * cwmin is band-specific, update hardware
+ * with value for current band
+ */
+ brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
+ brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax);
+
+ brcms_b_update_slot_timing(wlc_hw,
BAND_5G(wlc_hw->band->
bandtype) ? true : wlc_hw->
shortslot);
brcms_b_upd_synthpu(wlc_hw);
}
-static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk)
-{
- BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk);
-
- wlc_hw->phyclk = clk;
-
- if (OFF == clk) { /* clear gmode bit, put phy into reset */
-
- ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC | SICF_GMODE),
- (SICF_PRST | SICF_FGC));
- udelay(1);
- ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_PRST);
- udelay(1);
-
- } else { /* take phy out of reset */
-
- ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_FGC);
- udelay(1);
- ai_core_cflags(wlc_hw->sih, (SICF_FGC), 0);
- udelay(1);
-
- }
-}
-
/* Perform a soft reset of the PHY PLL */
void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw)
{
return macaddr;
}
+/* power both the pll and external oscillator on/off */
+static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want)
+{
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want);
+
+ /*
+ * dont power down if plldown is false or
+ * we must poll hw radio disable
+ */
+ if (!want && wlc_hw->pllreq)
+ return;
+
+ if (wlc_hw->sih)
+ ai_clkctl_xtal(wlc_hw->sih, XTAL | PLL, want);
+
+ wlc_hw->sbclk = want;
+ if (!wlc_hw->sbclk) {
+ wlc_hw->clk = false;
+ if (wlc_hw->band && wlc_hw->band->pi)
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+ }
+}
+
/*
* Return true if radio is disabled, otherwise false.
* hw radio disable signal is an external pin, users activate it asynchronously
ai_gpiocontrol(wlc_hw->sih, gm, gc, GPIO_DRV_PRIORITY);
}
+static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const u32 ucode[],
+ const uint nbytes) {
+ struct d11regs *regs = wlc_hw->regs;
+ uint i;
+ uint count;
+
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ count = (nbytes / sizeof(u32));
+
+ W_REG(®s->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
+ (void)R_REG(®s->objaddr);
+ for (i = 0; i < count; i++)
+ W_REG(®s->objdata, le32_to_cpu(ucode[i]));
+
+}
+
static void brcms_ucode_download(struct brcms_hardware *wlc_hw)
{
struct brcms_c_info *wlc;
}
}
-static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const u32 ucode[],
- const uint nbytes) {
- struct d11regs *regs = wlc_hw->regs;
- uint i;
- uint count;
-
- BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
-
- count = (nbytes / sizeof(u32));
-
- W_REG(®s->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
- (void)R_REG(®s->objaddr);
- for (i = 0; i < count; i++)
- W_REG(®s->objdata, le32_to_cpu(ucode[i]));
-
-}
-
-static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
- const struct d11init *inits)
-{
- int i;
- u8 *base;
- u8 *addr;
- u16 size;
- u32 value;
-
- BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
-
- base = (u8 *)wlc_hw->regs;
-
- for (i = 0; inits[i].addr != 0xffff; i++) {
- size = le16_to_cpu(inits[i].size);
- addr = base + le16_to_cpu(inits[i].addr);
- value = le32_to_cpu(inits[i].value);
- if (size == 2)
- W_REG((u16 *)addr, value);
- else if (size == 4)
- W_REG((u32 *)addr, value);
- else
- break;
- }
-}
-
-static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw)
-{
- u16 phyctl;
- u16 phytxant = wlc_hw->bmac_phytxant;
- u16 mask = PHY_TXC_ANT_MASK;
-
- /* set the Probe Response frame phy control word */
- phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
- phyctl = (phyctl & ~mask) | phytxant;
- brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);
-
- /* set the Response (ACK/CTS) frame phy control word */
- phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD);
- phyctl = (phyctl & ~mask) | phytxant;
- brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
-}
-
void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant)
{
/* update sw state */
}
}
+static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
+ uint tx_fifo)
+{
+ /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case
+ * but need to be done here for PIO otherwise the watchdog will catch
+ * the inconsistency and fire
+ */
+ /* Two clients of this code, 11h Quiet period and scanning. */
+ if (wlc_hw->di[tx_fifo])
+ dma_txresume(wlc_hw->di[tx_fifo]);
+
+ /* allow core to sleep again */
+ if (wlc_hw->suspended_fifos == 0)
+ return;
+ else {
+ wlc_hw->suspended_fifos &= ~(1 << tx_fifo);
+ if (wlc_hw->suspended_fifos == 0)
+ brcms_c_ucode_wake_override_clear(wlc_hw,
+ BRCMS_WAKE_OVERRIDE_TXFIFO);
+ }
+}
+
static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool on, u32 flags)
{
u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
brcms_c_ucode_mute_override_clear(wlc_hw);
}
-/* brcms_b_tx_fifo_suspended:
- * Check the MAC's tx suspend status for a tx fifo.
- *
- * When the MAC acknowledges a tx suspend, it indicates that no more
- * packets will be transmitted out the radio. This is independent of
- * DMA channel suspension---the DMA may have finished suspending, or may still
- * be pulling data into a tx fifo, by the time the MAC acks the suspend
- * request.
- */
-static bool brcms_b_tx_fifo_suspended(struct brcms_hardware *wlc_hw,
- uint tx_fifo)
-{
- /* check that a suspend has been requested and is no longer pending */
-
- /*
- * for DMA mode, the suspend request is set in xmtcontrol of the DMA
- * engine, and the tx fifo suspend at the lower end of the MAC is
- * acknowledged in the chnstatus register.
- *
- * The tx fifo suspend completion is independent of the DMA suspend
- * completion and may be acked before or after the DMA is suspended.
- */
- if (dma_txsuspended(wlc_hw->di[tx_fifo]) &&
- (R_REG(&wlc_hw->regs->chnstatus) &
- (1 << tx_fifo)) == 0)
- return true;
-
- return false;
-}
-
-static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
- uint tx_fifo)
-{
- /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case
- * but need to be done here for PIO otherwise the watchdog will catch
- * the inconsistency and fire
- */
- /* Two clients of this code, 11h Quiet period and scanning. */
- if (wlc_hw->di[tx_fifo])
- dma_txresume(wlc_hw->di[tx_fifo]);
-
- /* allow core to sleep again */
- if (wlc_hw->suspended_fifos == 0)
- return;
- else {
- wlc_hw->suspended_fifos &= ~(1 << tx_fifo);
- if (wlc_hw->suspended_fifos == 0)
- brcms_c_ucode_wake_override_clear(wlc_hw,
- BRCMS_WAKE_OVERRIDE_TXFIFO);
- }
-}
-
/*
* Read and clear macintmask and macintstatus and intstatus registers.
* This routine should be called with interrupts off
BRCMS_WAKE_OVERRIDE_MACSUSPEND);
}
-static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw)
-{
- u8 rate;
- u8 rates[8] = {
- BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M,
- BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M
- };
- u16 entry_ptr;
- u16 pctl1;
- uint i;
-
- if (!BRCMS_PHY_11N_CAP(wlc_hw->band))
- return;
-
- /* walk the phy rate table and update the entries */
- for (i = 0; i < ARRAY_SIZE(rates); i++) {
- rate = rates[i];
-
- entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate);
-
- /* read the SHM Rate Table entry OFDM PCTL1 values */
- pctl1 =
- brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);
-
- /* modify the value */
- pctl1 &= ~PHY_TXC1_MODE_MASK;
- pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);
-
- /* Update the SHM Rate Table entry OFDM PCTL1 values */
- brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
- pctl1);
- }
-}
-
-static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
- u8 rate)
-{
- uint i;
- u8 plcp_rate = 0;
- struct plcp_signal_rate_lookup {
- u8 rate;
- u8 signal_rate;
- };
- /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
- const struct plcp_signal_rate_lookup rate_lookup[] = {
- {BRCM_RATE_6M, 0xB},
- {BRCM_RATE_9M, 0xF},
- {BRCM_RATE_12M, 0xA},
- {BRCM_RATE_18M, 0xE},
- {BRCM_RATE_24M, 0x9},
- {BRCM_RATE_36M, 0xD},
- {BRCM_RATE_48M, 0x8},
- {BRCM_RATE_54M, 0xC}
- };
-
- for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
- if (rate == rate_lookup[i].rate) {
- plcp_rate = rate_lookup[i].signal_rate;
- break;
- }
- }
-
- /* Find the SHM pointer to the rate table entry by looking in the
- * Direct-map Table
- */
- return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
-}
-
-void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode)
+void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode)
{
wlc_hw->hw_stf_ss_opmode = stf_mode;
wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
}
-/* power both the pll and external oscillator on/off */
-static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want)
-{
- BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want);
-
- /*
- * dont power down if plldown is false or
- * we must poll hw radio disable
- */
- if (!want && wlc_hw->pllreq)
- return;
-
- if (wlc_hw->sih)
- ai_clkctl_xtal(wlc_hw->sih, XTAL | PLL, want);
-
- wlc_hw->sbclk = want;
- if (!wlc_hw->sbclk) {
- wlc_hw->clk = false;
- if (wlc_hw->band && wlc_hw->band->pi)
- wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
- }
-}
-
static void brcms_c_flushqueues(struct brcms_c_info *wlc)
{
struct brcms_hardware *wlc_hw = wlc->hw;
dma_rxreclaim(wlc_hw->di[RX_FIFO]);
}
-u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset)
-{
- return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
-}
-
-void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v)
-{
- brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL);
-}
-
static u16
brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel)
{
W_REG(objdata_lo, v);
}
+u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset)
+{
+ return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
+}
+
+void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v)
+{
+ brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL);
+}
+
/* Copy a buffer to shared memory of specified type .
* SHM 'offset' needs to be an even address and
* Buffer length 'len' must be an even number of bytes
brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
}
-void brcms_c_init(struct brcms_c_info *wlc)
+static u8 brcms_c_local_constraint_qdbm(struct brcms_c_info *wlc)
{
- struct d11regs *regs;
- u16 chanspec;
- int i;
- struct brcms_bss_cfg *bsscfg;
- bool mute = false;
+ u8 local;
+ s16 local_max;
- BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+ local = BRCMS_TXPWR_MAX;
+ if (wlc->pub->associated &&
+ (brcmu_chspec_ctlchan(wlc->chanspec) ==
+ brcmu_chspec_ctlchan(wlc->home_chanspec))) {
- regs = wlc->regs;
+ /* get the local power constraint if we are on the AP's
+ * channel [802.11h, 7.3.2.13]
+ */
+ /* Clamp the value between 0 and BRCMS_TXPWR_MAX w/o
+ * overflowing the target */
+ local_max =
+ (wlc->txpwr_local_max -
+ wlc->txpwr_local_constraint) * BRCMS_TXPWR_DB_FACTOR;
+ if (local_max > 0 && local_max < BRCMS_TXPWR_MAX)
+ return (u8) local_max;
+ if (local_max < 0)
+ return 0;
+ }
- /*
- * This will happen if a big-hammer was executed. In
- * that case, we want to go back to the channel that
- * we were on and not new channel
- */
- if (wlc->pub->associated)
- chanspec = wlc->home_chanspec;
- else
- chanspec = brcms_c_init_chanspec(wlc);
+ return local;
+}
- brcms_b_init(wlc->hw, chanspec, mute);
+static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc,
+ u16 chanspec)
+{
+ /* Save our copy of the chanspec */
+ wlc->chanspec = chanspec;
- /* update beacon listen interval */
- brcms_c_bcn_li_upd(wlc);
+ /* Set the chanspec and power limits for this locale after computing
+ * any 11h local tx power constraints.
+ */
+ brcms_c_channel_set_chanspec(wlc->cmi, chanspec,
+ brcms_c_local_constraint_qdbm(wlc));
- /* the world is new again, so is our reported rate */
- brcms_c_reprate_init(wlc);
+ if (wlc->stf->ss_algosel_auto)
+ brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
+ chanspec);
- /* write ethernet address to core */
- FOREACH_BSS(wlc, i, bsscfg)
- brcms_c_set_mac(bsscfg);
- brcms_c_set_bssid(bsscfg);
- END_FOREACH_BSS()
+ brcms_c_stf_ss_update(wlc, wlc->band);
- /* Update tsf_cfprep if associated and up */
- if (wlc->pub->associated) {
- FOREACH_BSS(wlc, i, bsscfg)
- if (bsscfg->up) {
- u32 bi;
+}
- /* get beacon period and convert to uS */
- bi = bsscfg->current_bss->beacon_period << 10;
- /*
- * update since init path would reset
- * to default value
- */
- W_REG(®s->tsf_cfprep,
- (bi << CFPREP_CBI_SHIFT));
+static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc,
+ u16 chanspec)
+{
+ struct brcms_c_rateset default_rateset;
+ uint parkband;
+ uint i, band_order[2];
- /* Update maccontrol PM related bits */
- brcms_c_set_ps_ctrl(wlc);
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+ /*
+ * We might have been bandlocked during down and the chip
+ * power-cycled (hibernate). Figure out the right band to park on
+ */
+ if (wlc->bandlocked || NBANDS(wlc) == 1) {
+ /* updated in brcms_c_bandlock() */
+ parkband = wlc->band->bandunit;
+ band_order[0] = band_order[1] = parkband;
+ } else {
+ /* park on the band of the specified chanspec */
+ parkband = CHSPEC_BANDUNIT(chanspec);
- break;
- }
- END_FOREACH_BSS()
+ /* order so that parkband initialize last */
+ band_order[0] = parkband ^ 1;
+ band_order[1] = parkband;
}
- brcms_c_bandinit_ordered(wlc, chanspec);
-
- brcms_c_init_scb(wlc, &global_scb);
-
- /* init probe response timeout */
- brcms_c_write_shm(wlc, M_PRS_MAXTIME, wlc->prb_resp_timeout);
-
- /* init max burst txop (framebursting) */
- brcms_c_write_shm(wlc, M_MBURST_TXOP,
- (wlc->
- _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
+ /* make each band operational, software state init */
+ for (i = 0; i < NBANDS(wlc); i++) {
+ uint j = band_order[i];
- /* initialize maximum allowed duty cycle */
- brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
- brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
+ wlc->band = wlc->bandstate[j];
- /*
- * Update some shared memory locations related to
- * max AMPDU size allowed to received
- */
- brcms_c_ampdu_shm_upd(wlc->ampdu);
+ brcms_default_rateset(wlc, &default_rateset);
- /* band-specific inits */
- brcms_c_bsinit(wlc);
+ /* fill in hw_rate */
+ brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset,
+ false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK,
+ (bool) N_ENAB(wlc->pub));
- /* Enable EDCF mode (while the MAC is suspended) */
- if (EDCF_ENAB(wlc->pub)) {
- OR_REG(®s->ifs_ctl, IFS_USEEDCF);
- brcms_c_edcf_setparams(wlc, false);
+ /* init basic rate lookup */
+ brcms_c_rate_lookup_init(wlc, &default_rateset);
}
- /* Init precedence maps for empty FIFOs */
- brcms_c_tx_prec_map_init(wlc);
+ /* sync up phy/radio chanspec */
+ brcms_c_set_phy_chanspec(wlc, chanspec);
+}
- /* read the ucode version if we have not yet done so */
- if (wlc->ucode_rev == 0) {
+/*
+ * ucode, hwmac update
+ * Channel dependent updates for ucode and hw
+ */
+static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc)
+{
+ /* enable or disable any active IBSSs depending on whether or not
+ * we are on the home channel
+ */
+ if (wlc->home_chanspec == BRCMS_BAND_PI_RADIO_CHANSPEC) {
+ if (wlc->pub->associated) {
+ /*
+ * BMAC_NOTE: This is something that should be fixed
+ * in ucode inits. I think that the ucode inits set
+ * up the bcn templates and shm values with a bogus
+ * beacon. This should not be done in the inits. If
+ * ucode needs to set up a beacon for testing, the
+ * test routines should write it down, not expect the
+ * inits to populate a bogus beacon.
+ */
+ if (BRCMS_PHY_11N_CAP(wlc->band))
+ brcms_c_write_shm(wlc, M_BCN_TXTSF_OFFSET,
+ wlc->band->bcntsfoff);
+ }
+ } else {
+ /* disable an active IBSS if we are not on the home channel */
+ }
+
+ /* update the various promisc bits */
+ brcms_c_mac_bcn_promisc(wlc);
+ brcms_c_mac_promisc(wlc);
+}
+
+/* band-specific init */
+static void brcms_c_bsinit(struct brcms_c_info *wlc)
+{
+ BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n",
+ wlc->pub->unit, wlc->band->bandunit);
+
+ /* write ucode ACK/CTS rate table */
+ brcms_c_set_ratetable(wlc);
+
+ /* update some band specific mac configuration */
+ brcms_c_ucode_mac_upd(wlc);
+
+ /* init antenna selection */
+ brcms_c_antsel_init(wlc->asi);
+
+}
+
+/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */
+static int
+brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM,
+ bool writeToShm)
+{
+ int idle_busy_ratio_x_16 = 0;
+ uint offset =
+ isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM :
+ M_TX_IDLE_BUSY_RATIO_X_16_CCK;
+ if (duty_cycle > 100 || duty_cycle < 0) {
+ wiphy_err(wlc->wiphy, "wl%d: duty cycle value off limit\n",
+ wlc->pub->unit);
+ return -EINVAL;
+ }
+ if (duty_cycle)
+ idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle;
+ /* Only write to shared memory when wl is up */
+ if (writeToShm)
+ brcms_c_write_shm(wlc, offset, (u16) idle_busy_ratio_x_16);
+
+ if (isOFDM)
+ wlc->tx_duty_cycle_ofdm = (u16) duty_cycle;
+ else
+ wlc->tx_duty_cycle_cck = (u16) duty_cycle;
+
+ return 0;
+}
+
+/*
+ * Initialize the base precedence map for dequeueing
+ * from txq based on WME settings
+ */
+static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc)
+{
+ wlc->tx_prec_map = BRCMS_PREC_BMP_ALL;
+ memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16));
+
+ /*
+ * For non-WME, both fifos have overlapping MAXPRIO. So just
+ * disable all precedences if either is full.
+ */
+ if (!EDCF_ENAB(wlc->pub)) {
+ wlc->fifo2prec_map[TX_DATA_FIFO] = BRCMS_PREC_BMP_ALL;
+ wlc->fifo2prec_map[TX_CTL_FIFO] = BRCMS_PREC_BMP_ALL;
+ } else {
+ wlc->fifo2prec_map[TX_AC_BK_FIFO] = BRCMS_PREC_BMP_AC_BK;
+ wlc->fifo2prec_map[TX_AC_BE_FIFO] = BRCMS_PREC_BMP_AC_BE;
+ wlc->fifo2prec_map[TX_AC_VI_FIFO] = BRCMS_PREC_BMP_AC_VI;
+ wlc->fifo2prec_map[TX_AC_VO_FIFO] = BRCMS_PREC_BMP_AC_VO;
+ }
+}
+
+static void
+brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc,
+ struct brcms_txq_info *qi, bool on, int prio)
+{
+ /* transmit flowcontrol is not yet implemented */
+}
+
+static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc)
+{
+ struct brcms_txq_info *qi;
+
+ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
+ if (qi->stopped) {
+ brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
+ qi->stopped = 0;
+ }
+ }
+}
+
+void brcms_c_init(struct brcms_c_info *wlc)
+{
+ struct d11regs *regs;
+ u16 chanspec;
+ int i;
+ struct brcms_bss_cfg *bsscfg;
+ bool mute = false;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ regs = wlc->regs;
+
+ /*
+ * This will happen if a big-hammer was executed. In
+ * that case, we want to go back to the channel that
+ * we were on and not new channel
+ */
+ if (wlc->pub->associated)
+ chanspec = wlc->home_chanspec;
+ else
+ chanspec = brcms_c_init_chanspec(wlc);
+
+ brcms_b_init(wlc->hw, chanspec, mute);
+
+ /* update beacon listen interval */
+ brcms_c_bcn_li_upd(wlc);
+
+ /* the world is new again, so is our reported rate */
+ brcms_c_reprate_init(wlc);
+
+ /* write ethernet address to core */
+ FOREACH_BSS(wlc, i, bsscfg)
+ brcms_c_set_mac(bsscfg);
+ brcms_c_set_bssid(bsscfg);
+ END_FOREACH_BSS()
+
+ /* Update tsf_cfprep if associated and up */
+ if (wlc->pub->associated) {
+ FOREACH_BSS(wlc, i, bsscfg)
+ if (bsscfg->up) {
+ u32 bi;
+
+ /* get beacon period and convert to uS */
+ bi = bsscfg->current_bss->beacon_period << 10;
+ /*
+ * update since init path would reset
+ * to default value
+ */
+ W_REG(®s->tsf_cfprep,
+ (bi << CFPREP_CBI_SHIFT));
+
+ /* Update maccontrol PM related bits */
+ brcms_c_set_ps_ctrl(wlc);
+
+ break;
+ }
+ END_FOREACH_BSS()
+ }
+
+ brcms_c_bandinit_ordered(wlc, chanspec);
+
+ brcms_c_init_scb(wlc, &global_scb);
+
+ /* init probe response timeout */
+ brcms_c_write_shm(wlc, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+
+ /* init max burst txop (framebursting) */
+ brcms_c_write_shm(wlc, M_MBURST_TXOP,
+ (wlc->
+ _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
+
+ /* initialize maximum allowed duty cycle */
+ brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
+ brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
+
+ /*
+ * Update some shared memory locations related to
+ * max AMPDU size allowed to received
+ */
+ brcms_c_ampdu_shm_upd(wlc->ampdu);
+
+ /* band-specific inits */
+ brcms_c_bsinit(wlc);
+
+ /* Enable EDCF mode (while the MAC is suspended) */
+ if (EDCF_ENAB(wlc->pub)) {
+ OR_REG(®s->ifs_ctl, IFS_USEEDCF);
+ brcms_c_edcf_setparams(wlc, false);
+ }
+
+ /* Init precedence maps for empty FIFOs */
+ brcms_c_tx_prec_map_init(wlc);
+
+ /* read the ucode version if we have not yet done so */
+ if (wlc->ucode_rev == 0) {
wlc->ucode_rev =
brcms_c_read_shm(wlc, M_BOM_REV_MAJOR) << NBITS(u16);
wlc->ucode_rev |= brcms_c_read_shm(wlc, M_BOM_REV_MINOR);
brcms_b_set_shortslot(wlc->hw, shortslot);
}
-static u8 brcms_c_local_constraint_qdbm(struct brcms_c_info *wlc)
-{
- u8 local;
- s16 local_max;
-
- local = BRCMS_TXPWR_MAX;
- if (wlc->pub->associated &&
- (brcmu_chspec_ctlchan(wlc->chanspec) ==
- brcmu_chspec_ctlchan(wlc->home_chanspec))) {
-
- /* get the local power constraint if we are on the AP's
- * channel [802.11h, 7.3.2.13]
- */
- /* Clamp the value between 0 and BRCMS_TXPWR_MAX w/o
- * overflowing the target */
- local_max =
- (wlc->txpwr_local_max -
- wlc->txpwr_local_constraint) * BRCMS_TXPWR_DB_FACTOR;
- if (local_max > 0 && local_max < BRCMS_TXPWR_MAX)
- return (u8) local_max;
- if (local_max < 0)
- return 0;
- }
-
- return local;
-}
-
/*
* propagate home chanspec to all bsscfgs in
* case bsscfg->current_bss->chanspec is referenced
}
}
-static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc,
- u16 chanspec)
+void
+brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec,
+ bool mute, struct txpwr_limits *txpwr)
{
- /* Save our copy of the chanspec */
- wlc->chanspec = chanspec;
+ uint bandunit;
- /* Set the chanspec and power limits for this locale after computing
- * any 11h local tx power constraints.
- */
- brcms_c_channel_set_chanspec(wlc->cmi, chanspec,
- brcms_c_local_constraint_qdbm(wlc));
-
- if (wlc->stf->ss_algosel_auto)
- brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
- chanspec);
-
- brcms_c_stf_ss_update(wlc, wlc->band);
-
-}
-
-void
-brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec,
- bool mute, struct txpwr_limits *txpwr)
-{
- uint bandunit;
-
- BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec);
+ BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec);
wlc_hw->chanspec = chanspec;
}
}
+/* switch to and initialize new band */
+static void brcms_c_setband(struct brcms_c_info *wlc,
+ uint bandunit)
+{
+ int idx;
+ struct brcms_bss_cfg *cfg;
+
+ wlc->band = wlc->bandstate[bandunit];
+
+ if (!wlc->pub->up)
+ return;
+
+ /* wait for at least one beacon before entering sleeping state */
+ for (idx = 0; idx < BRCMS_MAXBSSCFG; idx++) {
+ cfg = wlc->bsscfg[idx];
+ if (cfg && BSSCFG_STA(cfg) && cfg->associated)
+ cfg->PMawakebcn = true;
+ }
+ brcms_c_set_ps_ctrl(wlc);
+
+ /* band-specific initializations */
+ brcms_c_bsinit(wlc);
+}
+
void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec)
{
uint bandunit;
}
}
-/*
- * ucode, hwmac update
- * Channel dependent updates for ucode and hw
- */
-static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc)
-{
- /* enable or disable any active IBSSs depending on whether or not
- * we are on the home channel
- */
- if (wlc->home_chanspec == BRCMS_BAND_PI_RADIO_CHANSPEC) {
- if (wlc->pub->associated) {
- /*
- * BMAC_NOTE: This is something that should be fixed
- * in ucode inits. I think that the ucode inits set
- * up the bcn templates and shm values with a bogus
- * beacon. This should not be done in the inits. If
- * ucode needs to set up a beacon for testing, the
- * test routines should write it down, not expect the
- * inits to populate a bogus beacon.
- */
- if (BRCMS_PHY_11N_CAP(wlc->band))
- brcms_c_write_shm(wlc, M_BCN_TXTSF_OFFSET,
- wlc->band->bcntsfoff);
- }
- } else {
- /* disable an active IBSS if we are not on the home channel */
- }
-
- /* update the various promisc bits */
- brcms_c_mac_bcn_promisc(wlc);
- brcms_c_mac_promisc(wlc);
-}
-
-static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc,
- u16 chanspec)
-{
- struct brcms_c_rateset default_rateset;
- uint parkband;
- uint i, band_order[2];
-
- BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
- /*
- * We might have been bandlocked during down and the chip
- * power-cycled (hibernate). Figure out the right band to park on
- */
- if (wlc->bandlocked || NBANDS(wlc) == 1) {
- /* updated in brcms_c_bandlock() */
- parkband = wlc->band->bandunit;
- band_order[0] = band_order[1] = parkband;
- } else {
- /* park on the band of the specified chanspec */
- parkband = CHSPEC_BANDUNIT(chanspec);
-
- /* order so that parkband initialize last */
- band_order[0] = parkband ^ 1;
- band_order[1] = parkband;
- }
-
- /* make each band operational, software state init */
- for (i = 0; i < NBANDS(wlc); i++) {
- uint j = band_order[i];
-
- wlc->band = wlc->bandstate[j];
-
- brcms_default_rateset(wlc, &default_rateset);
-
- /* fill in hw_rate */
- brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset,
- false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK,
- (bool) N_ENAB(wlc->pub));
-
- /* init basic rate lookup */
- brcms_c_rate_lookup_init(wlc, &default_rateset);
- }
-
- /* sync up phy/radio chanspec */
- brcms_c_set_phy_chanspec(wlc, chanspec);
-}
-
-/* band-specific init */
-static void brcms_c_bsinit(struct brcms_c_info *wlc)
-{
- BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n",
- wlc->pub->unit, wlc->band->bandunit);
-
- /* write ucode ACK/CTS rate table */
- brcms_c_set_ratetable(wlc);
-
- /* update some band specific mac configuration */
- brcms_c_ucode_mac_upd(wlc);
-
- /* init antenna selection */
- brcms_c_antsel_init(wlc->asi);
-
-}
-
-/* switch to and initialize new band */
-static void brcms_c_setband(struct brcms_c_info *wlc,
- uint bandunit)
-{
- int idx;
- struct brcms_bss_cfg *cfg;
-
- wlc->band = wlc->bandstate[bandunit];
-
- if (!wlc->pub->up)
- return;
-
- /* wait for at least one beacon before entering sleeping state */
- for (idx = 0; idx < BRCMS_MAXBSSCFG; idx++) {
- cfg = wlc->bsscfg[idx];
- if (cfg && BSSCFG_STA(cfg) && cfg->associated)
- cfg->PMawakebcn = true;
- }
- brcms_c_set_ps_ctrl(wlc);
-
- /* band-specific initializations */
- brcms_c_bsinit(wlc);
-}
-
/*
* Initialize a WME Parameter Info Element with default
* STA parameters from WMM Spec, Table 12
}
-bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit)
+/* maintain LED behavior in down state */
+static void brcms_c_down_led_upd(struct brcms_c_info *wlc)
{
- wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer,
- wlc, "watchdog");
- if (!wlc->wdtimer) {
- wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer "
- "failed\n", unit);
- goto fail;
- }
+ /*
+ * maintain LEDs while in down state, turn on sbclk if
+ * not available yet. Turn on sbclk if necessary
+ */
+ if (!AP_ENAB(wlc->pub)) {
+ brcms_c_pllreq(wlc, true, BRCMS_PLLREQ_FLIP);
- wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer,
- wlc, "radio");
- if (!wlc->radio_timer) {
- wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer "
- "failed\n", unit);
- goto fail;
+ brcms_c_pllreq(wlc, false, BRCMS_PLLREQ_FLIP);
}
+}
- return true;
+static bool brcms_c_radio_monitor_start(struct brcms_c_info *wlc)
+{
+ /* Don't start the timer if HWRADIO feature is disabled */
+ if (wlc->radio_monitor || (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO))
+ return true;
- fail:
- return false;
+ wlc->radio_monitor = true;
+ brcms_c_pllreq(wlc, true, BRCMS_PLLREQ_RADIO_MON);
+ brcms_add_timer(wlc->wl, wlc->radio_timer, TIMER_INTERVAL_RADIOCHK,
+ true);
+ return true;
}
-/*
- * Initialize brcms_c_info default values ...
- * may get overrides later in this function
- */
-void brcms_c_info_init(struct brcms_c_info *wlc, int unit)
+void brcms_c_radio_disable(struct brcms_c_info *wlc)
{
- int i;
- /* Assume the device is there until proven otherwise */
- wlc->device_present = true;
-
- /* Save our copy of the chanspec */
- wlc->chanspec = CH20MHZ_CHSPEC(1);
+ if (!wlc->pub->up) {
+ brcms_c_down_led_upd(wlc);
+ return;
+ }
- /* various 802.11g modes */
- wlc->shortslot = false;
- wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO;
+ brcms_c_radio_monitor_start(wlc);
+ brcms_down(wlc->wl);
+}
- brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO);
- brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false);
+static void brcms_c_radio_enable(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->up)
+ return;
- brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR,
- BRCMS_PROTECTION_AUTO);
- brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF);
- brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR,
- BRCMS_PROTECTION_AUTO);
- brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false);
- brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO);
+ if (DEVICEREMOVED(wlc))
+ return;
- brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP,
- BRCMS_PROTECTION_CTL_OVERLAP);
+ brcms_up(wlc->wl);
+}
- /* 802.11g draft 4.0 NonERP elt advertisement */
- wlc->include_legacy_erp = true;
+bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc)
+{
+ if (!wlc->radio_monitor)
+ return true;
- wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF;
- wlc->stf->txant = ANT_TX_DEF;
+ wlc->radio_monitor = false;
+ brcms_c_pllreq(wlc, false, BRCMS_PLLREQ_RADIO_MON);
+ return brcms_del_timer(wlc->wl, wlc->radio_timer);
+}
- wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT;
+/* read hwdisable state and propagate to wlc flag */
+static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO || wlc->pub->hw_off)
+ return;
+
+ if (brcms_b_radio_read_hwdisabled(wlc->hw))
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+ else
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+}
+
+/*
+ * centralized radio disable/enable function,
+ * invoke radio enable/disable after updating hwradio status
+ */
+static void brcms_c_radio_upd(struct brcms_c_info *wlc)
+{
+ if (wlc->pub->radio_disabled)
+ brcms_c_radio_disable(wlc);
+ else
+ brcms_c_radio_enable(wlc);
+}
+
+/* update hwradio status and return it */
+bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc)
+{
+ brcms_c_radio_hwdisable_upd(wlc);
+
+ return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ?
+ true : false;
+}
+
+/* periodical query hw radio button while driver is "down" */
+static void brcms_c_radio_timer(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+
+ if (DEVICEREMOVED(wlc)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+
+ /* cap mpc off count */
+ if (wlc->mpc_offcnt < BRCMS_MPC_MAX_DELAYCNT)
+ wlc->mpc_offcnt++;
+
+ brcms_c_radio_hwdisable_upd(wlc);
+ brcms_c_radio_upd(wlc);
+}
+
+/* common low-level watchdog code */
+void brcms_b_watchdog(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+ struct brcms_hardware *wlc_hw = wlc->hw;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+ if (!wlc_hw->up)
+ return;
+
+ /* increment second count */
+ wlc_hw->now++;
+
+ /* Check for FIFO error interrupts */
+ brcms_b_fifoerrors(wlc_hw);
+
+ /* make sure RX dma has buffers */
+ dma_rxfill(wlc->hw->di[RX_FIFO]);
+
+ wlc_phy_watchdog(wlc_hw->band->pi);
+}
+
+/* common watchdog code */
+static void brcms_c_watchdog(void *arg)
+{
+ struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+ int i;
+ struct brcms_bss_cfg *cfg;
+
+ BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+ if (!wlc->pub->up)
+ return;
+
+ if (DEVICEREMOVED(wlc)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
+ __func__);
+ brcms_down(wlc->wl);
+ return;
+ }
+
+ /* increment second count */
+ wlc->pub->now++;
+
+ /* delay radio disable */
+ if (wlc->mpc_delay_off) {
+ if (--wlc->mpc_delay_off == 0) {
+ mboolset(wlc->pub->radio_disabled,
+ WL_RADIO_MPC_DISABLE);
+ if (wlc->mpc && brcms_c_ismpc(wlc))
+ wlc->mpc_offcnt = 0;
+ wlc->mpc_laston_ts = OSL_SYSUPTIME();
+ }
+ }
+
+ /* mpc sync */
+ brcms_c_radio_mpc_upd(wlc);
+ /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */
+ brcms_c_radio_hwdisable_upd(wlc);
+ brcms_c_radio_upd(wlc);
+ /* if radio is disable, driver may be down, quit here */
+ if (wlc->pub->radio_disabled)
+ return;
+
+ brcms_b_watchdog(wlc);
+
+ /*
+ * occasionally sample mac stat counters to
+ * detect 16-bit counter wrap
+ */
+ if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0)
+ brcms_c_statsupd(wlc);
+
+ /* Manage TKIP countermeasures timers */
+ FOREACH_BSS(wlc, i, cfg)
+ if (cfg->tk_cm_dt)
+ cfg->tk_cm_dt--;
+ if (cfg->tk_cm_bt)
+ cfg->tk_cm_bt--;
+ END_FOREACH_BSS()
+
+ if (BRCMS_ISNPHY(wlc->band) && !wlc->pub->tempsense_disable &&
+ ((wlc->pub->now - wlc->tempsense_lasttime) >=
+ BRCMS_TEMPSENSE_PERIOD)) {
+ wlc->tempsense_lasttime = wlc->pub->now;
+ brcms_c_tempsense_upd(wlc);
+ }
+}
+
+static void brcms_c_watchdog_by_timer(void *arg)
+{
+ brcms_c_watchdog(arg);
+}
+
+bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit)
+{
+ wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer,
+ wlc, "watchdog");
+ if (!wlc->wdtimer) {
+ wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer "
+ "failed\n", unit);
+ goto fail;
+ }
+
+ wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer,
+ wlc, "radio");
+ if (!wlc->radio_timer) {
+ wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer "
+ "failed\n", unit);
+ goto fail;
+ }
+
+ return true;
+
+ fail:
+ return false;
+}
+
+/*
+ * Initialize brcms_c_info default values ...
+ * may get overrides later in this function
+ */
+void brcms_c_info_init(struct brcms_c_info *wlc, int unit)
+{
+ int i;
+ /* Assume the device is there until proven otherwise */
+ wlc->device_present = true;
+
+ /* Save our copy of the chanspec */
+ wlc->chanspec = CH20MHZ_CHSPEC(1);
+
+ /* various 802.11g modes */
+ wlc->shortslot = false;
+ wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO;
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false);
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR,
+ BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR,
+ BRCMS_PROTECTION_AUTO);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false);
+ brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO);
+
+ brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP,
+ BRCMS_PROTECTION_CTL_OVERLAP);
+
+ /* 802.11g draft 4.0 NonERP elt advertisement */
+ wlc->include_legacy_erp = true;
+
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF;
+ wlc->stf->txant = ANT_TX_DEF;
+
+ wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT;
wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN;
for (i = 0; i < NFIFO; i++)
return err;
}
-/*
- * The common driver entry routine. Error codes should be unique
- */
-struct brcms_c_info *
-brcms_c_attach(struct brcms_info *wl, u16 vendor, u16 device, uint unit,
- bool piomode, void *regsva, struct pci_dev *btparam, uint *perr)
+static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc)
{
- struct brcms_c_info *wlc;
- uint err = 0;
- uint j;
- struct brcms_pub *pub;
- uint n_disabled;
+ uint unit;
+ unit = wlc->pub->unit;
- /* allocate struct brcms_c_info state and its substructures */
- wlc = (struct brcms_c_info *) brcms_c_attach_malloc(unit, &err, device);
- if (wlc == NULL)
- goto fail;
- wlc->wiphy = wl->wiphy;
- pub = wlc->pub;
+ if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
+ /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
+ wlc->band->antgain = 8;
+ } else if (wlc->band->antgain == -1) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
+ " srom, using 2dB\n", unit, __func__);
+ wlc->band->antgain = 8;
+ } else {
+ s8 gain, fract;
+ /* Older sroms specified gain in whole dbm only. In order
+ * be able to specify qdbm granularity and remain backward
+ * compatible the whole dbms are now encoded in only
+ * low 6 bits and remaining qdbms are encoded in the hi 2 bits.
+ * 6 bit signed number ranges from -32 - 31.
+ *
+ * Examples:
+ * 0x1 = 1 db,
+ * 0xc1 = 1.75 db (1 + 3 quarters),
+ * 0x3f = -1 (-1 + 0 quarters),
+ * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm.
+ * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm.
+ */
+ gain = wlc->band->antgain & 0x3f;
+ gain <<= 2; /* Sign extend */
+ gain >>= 2;
+ fract = (wlc->band->antgain & 0xc0) >> 6;
+ wlc->band->antgain = 4 * gain + fract;
+ }
+}
-#if defined(BCMDBG)
- wlc_info_dbg = wlc;
+static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
+{
+ int aa;
+ uint unit;
+ char *vars;
+ int bandtype;
+
+ unit = wlc->pub->unit;
+ vars = wlc->pub->vars;
+ bandtype = wlc->band->bandtype;
+
+ /* get antennas available */
+ aa = (s8) getintvar(vars, (BAND_5G(bandtype) ? "aa5g" : "aa2g"));
+ if (aa == 0)
+ aa = (s8) getintvar(vars,
+ (BAND_5G(bandtype) ? "aa1" : "aa0"));
+ if ((aa < 1) || (aa > 15)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
+ " srom (0x%x), using 3\n", unit, __func__, aa);
+ aa = 3;
+ }
+
+ /* reset the defaults if we have a single antenna */
+ if (aa == 1) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0;
+ wlc->stf->txant = ANT_TX_FORCE_0;
+ } else if (aa == 2) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1;
+ wlc->stf->txant = ANT_TX_FORCE_1;
+ } else {
+ }
+
+ /* Compute Antenna Gain */
+ wlc->band->antgain =
+ (s8) getintvar(vars, (BAND_5G(bandtype) ? "ag1" : "ag0"));
+ brcms_c_attach_antgain_init(wlc);
+
+ return true;
+}
+
+static void brcms_c_bss_default_init(struct brcms_c_info *wlc)
+{
+ u16 chanspec;
+ struct brcms_band *band;
+ struct brcms_bss_info *bi = wlc->default_bss;
+
+ /* init default and target BSS with some sane initial values */
+ memset((char *)(bi), 0, sizeof(struct brcms_bss_info));
+ bi->beacon_period = BEACON_INTERVAL_DEFAULT;
+ bi->dtim_period = DTIM_INTERVAL_DEFAULT;
+
+ /* fill the default channel as the first valid channel
+ * starting from the 2G channels
+ */
+ chanspec = CH20MHZ_CHSPEC(1);
+ wlc->home_chanspec = bi->chanspec = chanspec;
+
+ /* find the band of our default channel */
+ band = wlc->band;
+ if (NBANDS(wlc) > 1 && band->bandunit != CHSPEC_BANDUNIT(chanspec))
+ band = wlc->bandstate[OTHERBANDUNIT(wlc)];
+
+ /* init bss rates to the band specific default rate set */
+ brcms_c_rateset_default(&bi->rateset, NULL, band->phytype,
+ band->bandtype, false, BRCMS_RATE_MASK_FULL,
+ (bool) N_ENAB(wlc->pub), CHSPEC_WLC_BW(chanspec),
+ wlc->stf->txstreams);
+
+ if (N_ENAB(wlc->pub))
+ bi->flags |= BRCMS_BSS_HT;
+}
+
+static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc)
+{
+ struct brcms_txq_info *qi, *p;
+
+ qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC);
+ if (qi != NULL) {
+ /*
+ * Have enough room for control packets along with HI watermark
+ * Also, add room to txq for total psq packets if all the SCBs
+ * leave PS mode. The watermark for flowcontrol to OS packets
+ * will remain the same
+ */
+ brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT,
+ (2 * wlc->pub->tunables->datahiwat) + PKTQ_LEN_DEFAULT
+ + wlc->pub->psq_pkts_total);
+
+ /* add this queue to the the global list */
+ p = wlc->tx_queues;
+ if (p == NULL) {
+ wlc->tx_queues = qi;
+ } else {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = qi;
+ }
+ }
+ return qi;
+}
+
+static void brcms_c_txq_free(struct brcms_c_info *wlc,
+ struct brcms_txq_info *qi)
+{
+ struct brcms_txq_info *p;
+
+ if (qi == NULL)
+ return;
+
+ /* remove the queue from the linked list */
+ p = wlc->tx_queues;
+ if (p == qi)
+ wlc->tx_queues = p->next;
+ else {
+ while (p != NULL && p->next != qi)
+ p = p->next;
+ if (p != NULL)
+ p->next = p->next->next;
+ }
+
+ kfree(qi);
+}
+
+static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap)
+{
+ uint i;
+ struct brcms_band *band;
+
+ for (i = 0; i < NBANDS(wlc); i++) {
+ if (IS_SINGLEBAND_5G(wlc->deviceid))
+ i = BAND_5G_INDEX;
+ band = wlc->bandstate[i];
+ if (band->bandtype == BRCM_BAND_5G) {
+ if ((bwcap == BRCMS_N_BW_40ALL)
+ || (bwcap == BRCMS_N_BW_20IN2G_40IN5G))
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ } else {
+ if (bwcap == BRCMS_N_BW_40ALL)
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ }
+ }
+}
+
+/*
+ * The common driver entry routine. Error codes should be unique
+ */
+struct brcms_c_info *
+brcms_c_attach(struct brcms_info *wl, u16 vendor, u16 device, uint unit,
+ bool piomode, void *regsva, struct pci_dev *btparam, uint *perr)
+{
+ struct brcms_c_info *wlc;
+ uint err = 0;
+ uint j;
+ struct brcms_pub *pub;
+ uint n_disabled;
+
+ /* allocate struct brcms_c_info state and its substructures */
+ wlc = (struct brcms_c_info *) brcms_c_attach_malloc(unit, &err, device);
+ if (wlc == NULL)
+ goto fail;
+ wlc->wiphy = wl->wiphy;
+ pub = wlc->pub;
+
+#if defined(BCMDBG)
+ wlc_info_dbg = wlc;
#endif
wlc->band = wlc->bandstate[0];
return NULL;
}
-static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc)
-{
- uint unit;
- unit = wlc->pub->unit;
-
- if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
- /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
- wlc->band->antgain = 8;
- } else if (wlc->band->antgain == -1) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
- " srom, using 2dB\n", unit, __func__);
- wlc->band->antgain = 8;
- } else {
- s8 gain, fract;
- /* Older sroms specified gain in whole dbm only. In order
- * be able to specify qdbm granularity and remain backward
- * compatible the whole dbms are now encoded in only
- * low 6 bits and remaining qdbms are encoded in the hi 2 bits.
- * 6 bit signed number ranges from -32 - 31.
- *
- * Examples:
- * 0x1 = 1 db,
- * 0xc1 = 1.75 db (1 + 3 quarters),
- * 0x3f = -1 (-1 + 0 quarters),
- * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm.
- * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm.
- */
- gain = wlc->band->antgain & 0x3f;
- gain <<= 2; /* Sign extend */
- gain >>= 2;
- fract = (wlc->band->antgain & 0xc0) >> 6;
- wlc->band->antgain = 4 * gain + fract;
- }
-}
-
-static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
-{
- int aa;
- uint unit;
- char *vars;
- int bandtype;
-
- unit = wlc->pub->unit;
- vars = wlc->pub->vars;
- bandtype = wlc->band->bandtype;
-
- /* get antennas available */
- aa = (s8) getintvar(vars, (BAND_5G(bandtype) ? "aa5g" : "aa2g"));
- if (aa == 0)
- aa = (s8) getintvar(vars,
- (BAND_5G(bandtype) ? "aa1" : "aa0"));
- if ((aa < 1) || (aa > 15)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
- " srom (0x%x), using 3\n", unit, __func__, aa);
- aa = 3;
- }
-
- /* reset the defaults if we have a single antenna */
- if (aa == 1) {
- wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0;
- wlc->stf->txant = ANT_TX_FORCE_0;
- } else if (aa == 2) {
- wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1;
- wlc->stf->txant = ANT_TX_FORCE_1;
- } else {
- }
-
- /* Compute Antenna Gain */
- wlc->band->antgain =
- (s8) getintvar(vars, (BAND_5G(bandtype) ? "ag1" : "ag0"));
- brcms_c_attach_antgain_init(wlc);
-
- return true;
-}
-
-
static void brcms_c_timers_deinit(struct brcms_c_info *wlc)
{
/* free timer state */
wlc->mpc = true;
}
-/* read hwdisable state and propagate to wlc flag */
-static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc)
+/*
+ * return true if Minimum Power Consumption should
+ * be entered, false otherwise
+ */
+bool brcms_c_is_non_delay_mpc(struct brcms_c_info *wlc)
{
- if (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO || wlc->pub->hw_off)
- return;
-
- if (brcms_b_radio_read_hwdisabled(wlc->hw))
- mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
- else
- mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
-}
-
-/*
- * return true if Minimum Power Consumption should
- * be entered, false otherwise
- */
-bool brcms_c_is_non_delay_mpc(struct brcms_c_info *wlc)
-{
- return false;
-}
+ return false;
+}
bool brcms_c_ismpc(struct brcms_c_info *wlc)
{
wlc->prev_non_delay_mpc = brcms_c_is_non_delay_mpc(wlc);
}
-
-/*
- * centralized radio disable/enable function,
- * invoke radio enable/disable after updating hwradio status
- */
-static void brcms_c_radio_upd(struct brcms_c_info *wlc)
-{
- if (wlc->pub->radio_disabled)
- brcms_c_radio_disable(wlc);
- else
- brcms_c_radio_enable(wlc);
-}
-
-/* maintain LED behavior in down state */
-static void brcms_c_down_led_upd(struct brcms_c_info *wlc)
-{
- /*
- * maintain LEDs while in down state, turn on sbclk if
- * not available yet. Turn on sbclk if necessary
- */
- if (!AP_ENAB(wlc->pub)) {
- brcms_c_pllreq(wlc, true, BRCMS_PLLREQ_FLIP);
-
- brcms_c_pllreq(wlc, false, BRCMS_PLLREQ_FLIP);
- }
-}
-
-/* update hwradio status and return it */
-bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc)
-{
- brcms_c_radio_hwdisable_upd(wlc);
-
- return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ?
- true : false;
-}
-
-void brcms_c_radio_disable(struct brcms_c_info *wlc)
-{
- if (!wlc->pub->up) {
- brcms_c_down_led_upd(wlc);
- return;
- }
-
- brcms_c_radio_monitor_start(wlc);
- brcms_down(wlc->wl);
-}
-
-static void brcms_c_radio_enable(struct brcms_c_info *wlc)
-{
- if (wlc->pub->up)
- return;
-
- if (DEVICEREMOVED(wlc))
- return;
-
- brcms_up(wlc->wl);
-}
-
-/* periodical query hw radio button while driver is "down" */
-static void brcms_c_radio_timer(void *arg)
-{
- struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
-
- if (DEVICEREMOVED(wlc)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
- __func__);
- brcms_down(wlc->wl);
- return;
- }
-
- /* cap mpc off count */
- if (wlc->mpc_offcnt < BRCMS_MPC_MAX_DELAYCNT)
- wlc->mpc_offcnt++;
-
- brcms_c_radio_hwdisable_upd(wlc);
- brcms_c_radio_upd(wlc);
-}
-
-static bool brcms_c_radio_monitor_start(struct brcms_c_info *wlc)
-{
- /* Don't start the timer if HWRADIO feature is disabled */
- if (wlc->radio_monitor || (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO))
- return true;
-
- wlc->radio_monitor = true;
- brcms_c_pllreq(wlc, true, BRCMS_PLLREQ_RADIO_MON);
- brcms_add_timer(wlc->wl, wlc->radio_timer, TIMER_INTERVAL_RADIOCHK,
- true);
- return true;
-}
-
-bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc)
-{
- if (!wlc->radio_monitor)
- return true;
-
- wlc->radio_monitor = false;
- brcms_c_pllreq(wlc, false, BRCMS_PLLREQ_RADIO_MON);
- return brcms_del_timer(wlc->wl, wlc->radio_timer);
-}
-
-/* common low-level watchdog code */
-void brcms_b_watchdog(void *arg)
-{
- struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
- struct brcms_hardware *wlc_hw = wlc->hw;
-
- BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
-
- if (!wlc_hw->up)
- return;
-
- /* increment second count */
- wlc_hw->now++;
-
- /* Check for FIFO error interrupts */
- brcms_b_fifoerrors(wlc_hw);
-
- /* make sure RX dma has buffers */
- dma_rxfill(wlc->hw->di[RX_FIFO]);
-
- wlc_phy_watchdog(wlc_hw->band->pi);
-}
-
-static void brcms_c_watchdog_by_timer(void *arg)
-{
- brcms_c_watchdog(arg);
-}
-
-/* common watchdog code */
-static void brcms_c_watchdog(void *arg)
-{
- struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
- int i;
- struct brcms_bss_cfg *cfg;
-
- BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
-
- if (!wlc->pub->up)
- return;
-
- if (DEVICEREMOVED(wlc)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit,
- __func__);
- brcms_down(wlc->wl);
- return;
- }
-
- /* increment second count */
- wlc->pub->now++;
-
- /* delay radio disable */
- if (wlc->mpc_delay_off) {
- if (--wlc->mpc_delay_off == 0) {
- mboolset(wlc->pub->radio_disabled,
- WL_RADIO_MPC_DISABLE);
- if (wlc->mpc && brcms_c_ismpc(wlc))
- wlc->mpc_offcnt = 0;
- wlc->mpc_laston_ts = OSL_SYSUPTIME();
- }
- }
-
- /* mpc sync */
- brcms_c_radio_mpc_upd(wlc);
- /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */
- brcms_c_radio_hwdisable_upd(wlc);
- brcms_c_radio_upd(wlc);
- /* if radio is disable, driver may be down, quit here */
- if (wlc->pub->radio_disabled)
- return;
-
- brcms_b_watchdog(wlc);
-
- /*
- * occasionally sample mac stat counters to
- * detect 16-bit counter wrap
- */
- if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0)
- brcms_c_statsupd(wlc);
-
- /* Manage TKIP countermeasures timers */
- FOREACH_BSS(wlc, i, cfg)
- if (cfg->tk_cm_dt)
- cfg->tk_cm_dt--;
- if (cfg->tk_cm_bt)
- cfg->tk_cm_bt--;
- END_FOREACH_BSS()
-
- if (BRCMS_ISNPHY(wlc->band) && !wlc->pub->tempsense_disable &&
- ((wlc->pub->now - wlc->tempsense_lasttime) >=
- BRCMS_TEMPSENSE_PERIOD)) {
- wlc->tempsense_lasttime = wlc->pub->now;
- brcms_c_tempsense_upd(wlc);
- }
-}
-
/* Initialize just the hardware when coming out of POR or S3/S5 system states */
void brcms_b_hw_up(struct brcms_hardware *wlc_hw)
{
return 0;
}
+/*
+ * Write WME tunable parameters for retransmit/max rate
+ * from wlc struct to ucode
+ */
+static void brcms_c_wme_retries_write(struct brcms_c_info *wlc)
+{
+ int ac;
+
+ /* Need clock to do this */
+ if (!wlc->clk)
+ return;
+
+ for (ac = 0; ac < AC_COUNT; ac++)
+ brcms_c_write_shm(wlc, M_AC_TXLMT_ADDR(ac),
+ wlc->wme_retries[ac]);
+}
+
/* make interface operational */
int brcms_c_up(struct brcms_c_info *wlc)
{
return 0;
}
-/*
- * Initialize the base precedence map for dequeueing
- * from txq based on WME settings
- */
-static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc)
-{
- wlc->tx_prec_map = BRCMS_PREC_BMP_ALL;
- memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16));
-
- /*
- * For non-WME, both fifos have overlapping MAXPRIO. So just
- * disable all precedences if either is full.
- */
- if (!EDCF_ENAB(wlc->pub)) {
- wlc->fifo2prec_map[TX_DATA_FIFO] = BRCMS_PREC_BMP_ALL;
- wlc->fifo2prec_map[TX_CTL_FIFO] = BRCMS_PREC_BMP_ALL;
- } else {
- wlc->fifo2prec_map[TX_AC_BK_FIFO] = BRCMS_PREC_BMP_AC_BK;
- wlc->fifo2prec_map[TX_AC_BE_FIFO] = BRCMS_PREC_BMP_AC_BE;
- wlc->fifo2prec_map[TX_AC_VI_FIFO] = BRCMS_PREC_BMP_AC_VI;
- wlc->fifo2prec_map[TX_AC_VO_FIFO] = BRCMS_PREC_BMP_AC_VO;
- }
-}
-
static uint brcms_c_down_del_timer(struct brcms_c_info *wlc)
{
uint callbacks = 0;
return;
}
-int
-brcms_c_ioctl(struct brcms_c_info *wlc, int cmd, void *arg, int len,
- struct brcms_c_if *wlcif)
-{
- return _brcms_c_ioctl(wlc, cmd, arg, len, wlcif);
-}
-
/* common ioctl handler. return: 0=ok, -1=error, positive=particular error */
static int
_brcms_c_ioctl(struct brcms_c_info *wlc, int cmd, void *arg, int len,
return bcmerror;
}
+int
+brcms_c_ioctl(struct brcms_c_info *wlc, int cmd, void *arg, int len,
+ struct brcms_c_if *wlcif)
+{
+ return _brcms_c_ioctl(wlc, cmd, arg, len, wlcif);
+}
+
/*
* register watchdog and down handlers.
*/
return -ENODATA;
}
-/*
- * Write WME tunable parameters for retransmit/max rate
- * from wlc struct to ucode
- */
-static void brcms_c_wme_retries_write(struct brcms_c_info *wlc)
-{
- int ac;
-
- /* Need clock to do this */
- if (!wlc->clk)
- return;
-
- for (ac = 0; ac < AC_COUNT; ac++)
- brcms_c_write_shm(wlc, M_AC_TXLMT_ADDR(ac),
- wlc->wme_retries[ac]);
-}
-
#ifdef BCMDBG
static const char * const supr_reason[] = {
"None", "PMQ Entry", "Flush request",
}
}
-bool
-brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
- struct ieee80211_hw *hw)
-{
- u8 prio;
- uint fifo;
- struct scb *scb = &global_scb;
- struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data);
-
- /*
- * 802.11 standard requires management traffic
- * to go at highest priority
- */
- prio = ieee80211_is_data(d11_header->frame_control) ? sdu->priority :
- MAXPRIO;
- fifo = prio2fifo[prio];
- if (unlikely
- (brcms_c_d11hdrs_mac80211(
- wlc, hw, sdu, scb, 0, 1, fifo, 0, NULL, 0)))
- return -EINVAL;
- brcms_c_txq_enq(wlc, scb, sdu, BRCMS_PRIO_TO_PREC(prio));
- brcms_c_send_q(wlc);
- return 0;
-}
-
-void brcms_c_send_q(struct brcms_c_info *wlc)
-{
- struct sk_buff *pkt[DOT11_MAXNUMFRAGS];
- int prec;
- u16 prec_map;
- int err = 0, i, count;
- uint fifo;
- struct brcms_txq_info *qi = wlc->pkt_queue;
- struct pktq *q = &qi->q;
- struct ieee80211_tx_info *tx_info;
-
- if (in_send_q)
- return;
- else
- in_send_q = true;
-
- prec_map = wlc->tx_prec_map;
-
- /* Send all the enq'd pkts that we can.
- * Dequeue packets with precedence with empty HW fifo only
- */
- while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) {
- tx_info = IEEE80211_SKB_CB(pkt[0]);
- if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
- err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec);
- } else {
- count = 1;
- err = brcms_c_prep_pdu(wlc, pkt[0], &fifo);
- if (!err) {
- for (i = 0; i < count; i++)
- brcms_c_txfifo(wlc, fifo, pkt[i], true,
- 1);
- }
- }
-
- if (err == -EBUSY) {
- brcmu_pktq_penq_head(q, prec, pkt[0]);
- /*
- * If send failed due to any other reason than a
- * change in HW FIFO condition, quit. Otherwise,
- * read the new prec_map!
- */
- if (prec_map == wlc->tx_prec_map)
- break;
- prec_map = wlc->tx_prec_map;
- }
- }
-
- /*
- * Check if flow control needs to be turned off after
- * sending the packet
- */
- if (!EDCF_ENAB(wlc->pub)
- || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
- if (brcms_c_txflowcontrol_prio_isset(wlc, qi, ALLPRIO)
- && (pktq_len(q) < wlc->pub->tunables->datahiwat / 2))
- brcms_c_txflowcontrol(wlc, qi, OFF, ALLPRIO);
- } else if (wlc->pub->_priofc) {
- int prio;
- for (prio = MAXPRIO; prio >= 0; prio--) {
- if (brcms_c_txflowcontrol_prio_isset(wlc, qi, prio) &&
- (pktq_plen(q, wlc_prio2prec_map[prio]) <
- wlc->pub->tunables->datahiwat / 2))
- brcms_c_txflowcontrol(wlc, qi, OFF, prio);
- }
- }
- in_send_q = false;
-}
-
/*
* bcmc_fid_generate:
* Generate frame ID for a BCMC packet. The frag field is not used
return frameid;
}
-void
-brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p,
- bool commit, s8 txpktpend)
+static uint
+brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
{
- u16 frameid = INVALIDFID;
- struct d11txh *txh;
-
- txh = (struct d11txh *) (p->data);
-
- /* When a BC/MC frame is being committed to the BCMC fifo
- * via DMA (NOT PIO), update ucode or BSS info as appropriate.
- */
- if (fifo == TX_BCMC_FIFO)
- frameid = le16_to_cpu(txh->TxFrameID);
-
- if (BRCMS_WAR16165(wlc))
- brcms_c_war16165(wlc, true);
-
+ uint dur = 0;
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type);
/*
- * Bump up pending count for if not using rpc. If rpc is
- * used, this will be handled in brcms_b_txfifo()
+ * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
+ * is less than or equal to the rate of the immediately previous
+ * frame in the FES
*/
- if (commit) {
- TXPKTPENDINC(wlc, fifo, txpktpend);
- BCMMSG(wlc->wiphy, "pktpend inc %d to %d\n",
- txpktpend, TXPKTPENDGET(wlc, fifo));
- }
-
- /* Commit BCMC sequence number in the SHM frame ID location */
- if (frameid != INVALIDFID)
- BCMCFID(wlc, frameid);
-
- if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0)
- wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n");
+ rspec = BRCMS_BASIC_RATE(wlc, rspec);
+ /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */
+ dur =
+ brcms_c_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_ACK_LEN + FCS_LEN));
+ return dur;
}
-void
-brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec,
- uint length, u8 *plcp)
+static uint
+brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
{
- if (IS_MCS(rspec))
- brcms_c_compute_mimo_plcp(rspec, length, plcp);
- else if (IS_OFDM(rspec))
- brcms_c_compute_ofdm_plcp(rspec, length, plcp);
- else
- brcms_c_compute_cck_plcp(wlc, rspec, length, plcp);
- return;
+ BCMMSG(wlc->wiphy, "wl%d: ratespec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type);
+ return brcms_c_calc_ack_time(wlc, rspec, preamble_type);
}
-/* Rate: 802.11 rate code, length: PSDU length in octets */
-static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp)
+static uint
+brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec,
+ u8 preamble_type)
{
- u8 mcs = (u8) (rspec & RSPEC_RATE_MASK);
- plcp[0] = mcs;
- if (RSPEC_IS40MHZ(rspec) || (mcs == 32))
- plcp[0] |= MIMO_PLCP_40MHZ;
- BRCMS_SET_MIMO_PLCP_LEN(plcp, length);
- plcp[3] = RSPEC_MIMOPLCP3(rspec); /* rspec already holds this byte */
- plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */
- plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */
- plcp[5] = 0;
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, "
+ "preamble_type %d\n", wlc->pub->unit, rspec, preamble_type);
+ /*
+ * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
+ * is less than or equal to the rate of the immediately previous
+ * frame in the FES
+ */
+ rspec = BRCMS_BASIC_RATE(wlc, rspec);
+ /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */
+ return brcms_c_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN +
+ FCS_LEN));
}
-/* Rate: 802.11 rate code, length: PSDU length in octets */
-static void
-brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp)
+/* brcms_c_compute_frame_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for MPDU
+ * DUR for a single frame = 1 SIFS + 1 ACK
+ * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time
+ *
+ * rate MPDU rate in unit of 500kbps
+ * next_frag_len next MPDU length in bytes
+ * preamble_type use short/GF or long/MM PLCP header
+ */
+static u16
+brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate,
+ u8 preamble_type, uint next_frag_len)
{
- u8 rate_signal;
- u32 tmp = 0;
- int rate = RSPEC2RATE(rspec);
+ u16 dur, sifs;
- /*
- * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb
- * transmitted first
- */
- rate_signal = rate_info[rate] & BRCMS_RATE_MASK;
- memset(plcp, 0, D11_PHY_HDR_LEN);
- D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal);
+ sifs = SIFS(wlc->band);
- tmp = (length & 0xfff) << 5;
- plcp[2] |= (tmp >> 16) & 0xff;
- plcp[1] |= (tmp >> 8) & 0xff;
- plcp[0] |= tmp & 0xff;
+ dur = sifs;
+ dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type);
- return;
+ if (next_frag_len) {
+ /* Double the current DUR to get 2 SIFS + 2 ACKs */
+ dur *= 2;
+ /* add another SIFS and the frag time */
+ dur += sifs;
+ dur +=
+ (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type,
+ next_frag_len);
+ }
+ return dur;
}
-/*
- * Compute PLCP, but only requires actual rate and length of pkt.
- * Rate is given in the driver standard multiple of 500 kbps.
- * le is set for 11 Mbps rate if necessary.
- * Broken out for PRQ.
- */
-
-static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500,
- uint length, u8 *plcp)
+/* The opposite of brcms_c_calc_frame_time */
+static uint
+brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec,
+ u8 preamble_type, uint dur)
{
- u16 usec = 0;
- u8 le = 0;
+ uint nsyms, mac_len, Ndps, kNdps;
+ uint rate = RSPEC2RATE(ratespec);
- switch (rate_500) {
- case BRCM_RATE_1M:
- usec = length << 3;
- break;
- case BRCM_RATE_2M:
- usec = length << 2;
- break;
- case BRCM_RATE_5M5:
- usec = (length << 4) / 11;
- if ((length << 4) - (usec * 11) > 0)
- usec++;
- break;
- case BRCM_RATE_11M:
- usec = (length << 3) / 11;
- if ((length << 3) - (usec * 11) > 0) {
- usec++;
- if ((usec * 11) - (length << 3) >= 8)
- le = D11B_PLCP_SIGNAL_LE;
- }
- break;
+ BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, dur %d\n",
+ wlc->pub->unit, ratespec, preamble_type, dur);
- default:
- wiphy_err(wlc->wiphy,
- "brcms_c_cck_plcp_set: unsupported rate %d\n",
- rate_500);
- rate_500 = BRCM_RATE_1M;
- usec = length << 3;
- break;
+ if (IS_MCS(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
+ dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
+ /* payload calculation matches that of regular ofdm */
+ if (BAND_2G(wlc->band->bandtype))
+ dur -= DOT11_OFDM_SIGNAL_EXTENSION;
+ /* kNdbps = kbps * 4 */
+ kNdps =
+ MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
+ RSPEC_ISSGI(ratespec)) * 4;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * kNdps) -
+ ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000;
+ } else if (IS_OFDM(ratespec)) {
+ dur -= APHY_PREAMBLE_TIME;
+ dur -= APHY_SIGNAL_TIME;
+ /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
+ Ndps = rate * 2;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * Ndps) -
+ (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8;
+ } else {
+ if (preamble_type & BRCMS_SHORT_PREAMBLE)
+ dur -= BPHY_PLCP_SHORT_TIME;
+ else
+ dur -= BPHY_PLCP_TIME;
+ mac_len = dur * rate;
+ /* divide out factor of 2 in rate (1/2 mbps) */
+ mac_len = mac_len / 8 / 2;
}
- /* PLCP signal byte */
- plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */
- /* PLCP service byte */
- plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED);
- /* PLCP length u16, little endian */
- plcp[2] = usec & 0xff;
- plcp[3] = (usec >> 8) & 0xff;
- /* PLCP CRC16 */
- plcp[4] = 0;
- plcp[5] = 0;
+ return mac_len;
}
-/* Rate: 802.11 rate code, length: PSDU length in octets */
-static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec,
- uint length, u8 *plcp)
+static u32
+mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band,
+ u32 int_val)
{
- int rate = RSPEC2RATE(rspec);
-
- brcms_c_cck_plcp_set(wlc, rate, length, plcp);
-}
+ u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT;
+ u8 rate = int_val & NRATE_RATE_MASK;
+ u32 rspec;
+ bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE);
+ bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT);
+ bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY)
+ == NRATE_OVERRIDE_MCS_ONLY);
+ int bcmerror = 0;
-/* brcms_c_compute_frame_dur()
- *
- * Calculate the 802.11 MAC header DUR field for MPDU
- * DUR for a single frame = 1 SIFS + 1 ACK
- * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time
- *
- * rate MPDU rate in unit of 500kbps
- * next_frag_len next MPDU length in bytes
- * preamble_type use short/GF or long/MM PLCP header
- */
-static u16
-brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate,
- u8 preamble_type, uint next_frag_len)
-{
- u16 dur, sifs;
+ if (!ismcs)
+ return (u32) rate;
- sifs = SIFS(wlc->band);
+ /* validate the combination of rate/mcs/stf is allowed */
+ if (N_ENAB(wlc->pub) && ismcs) {
+ /* mcs only allowed when nmode */
+ if (stf > PHY_TXC1_MODE_SDM) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid stf\n",
+ BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
- dur = sifs;
- dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type);
+ /* mcs 32 is a special case, DUP mode 40 only */
+ if (rate == 32) {
+ if (!CHSPEC_IS40(wlc->home_chanspec) ||
+ ((stf != PHY_TXC1_MODE_SISO)
+ && (stf != PHY_TXC1_MODE_CDD))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid mcs "
+ "32\n", BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ /* mcs > 7 must use stf SDM */
+ } else if (rate > HIGHEST_SINGLE_STREAM_MCS) {
+ /* mcs > 7 must use stf SDM */
+ if (stf != PHY_TXC1_MODE_SDM) {
+ BCMMSG(wlc->wiphy, "wl%d: enabling "
+ "SDM mode for mcs %d\n",
+ BRCMS_UNIT(wlc), rate);
+ stf = PHY_TXC1_MODE_SDM;
+ }
+ } else {
+ /*
+ * MCS 0-7 may use SISO, CDD, and for
+ * phy_rev >= 3 STBC
+ */
+ if ((stf > PHY_TXC1_MODE_STBC) ||
+ (!BRCMS_STBC_CAP_PHY(wlc)
+ && (stf == PHY_TXC1_MODE_STBC))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid STBC"
+ "\n", BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ }
+ } else if (IS_OFDM(rate)) {
+ if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid OFDM\n",
+ BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ } else if (IS_CCK(rate)) {
+ if ((cur_band->bandtype != BRCM_BAND_2G)
+ || (stf != PHY_TXC1_MODE_SISO)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Invalid CCK\n",
+ BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ } else {
+ wiphy_err(wlc->wiphy, "wl%d: %s: Unknown rate type\n",
+ BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
+ /* make sure multiple antennae are available for non-siso rates */
+ if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: SISO antenna but !SISO "
+ "request\n", BRCMS_UNIT(wlc), __func__);
+ bcmerror = -EINVAL;
+ goto done;
+ }
- if (next_frag_len) {
- /* Double the current DUR to get 2 SIFS + 2 ACKs */
- dur *= 2;
- /* add another SIFS and the frag time */
- dur += sifs;
- dur +=
- (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type,
- next_frag_len);
+ rspec = rate;
+ if (ismcs) {
+ rspec |= RSPEC_MIMORATE;
+ /* For STBC populate the STC field of the ratespec */
+ if (stf == PHY_TXC1_MODE_STBC) {
+ u8 stc;
+ stc = 1; /* Nss for single stream is always 1 */
+ rspec |= (stc << RSPEC_STC_SHIFT);
+ }
}
- return dur;
+
+ rspec |= (stf << RSPEC_STF_SHIFT);
+
+ if (override_mcs_only)
+ rspec |= RSPEC_OVERRIDE_MCS_ONLY;
+
+ if (issgi)
+ rspec |= RSPEC_SHORT_GI;
+
+ if ((rate != 0)
+ && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true))
+ return rate;
+
+ return rspec;
+done:
+ return rate;
}
-/* brcms_c_compute_rtscts_dur()
+/*
+ * Add struct d11txh, struct cck_phy_hdr.
*
- * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame
- * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK
- * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK
+ * 'p' data must start with 802.11 MAC header
+ * 'p' must allow enough bytes of local headers to be "pushed" onto the packet
+ *
+ * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes)
*
- * cts cts-to-self or rts/cts
- * rts_rate rts or cts rate in unit of 500kbps
- * rate next MPDU rate in unit of 500kbps
- * frame_len next MPDU frame length in bytes
*/
-u16
-brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
- u32 rts_rate,
- u32 frame_rate, u8 rts_preamble_type,
- u8 frame_preamble_type, uint frame_len, bool ba)
-{
- u16 dur, sifs;
-
- sifs = SIFS(wlc->band);
-
- if (!cts_only) {
- /* RTS/CTS */
- dur = 3 * sifs;
- dur +=
- (u16) brcms_c_calc_cts_time(wlc, rts_rate,
- rts_preamble_type);
- } else {
- /* CTS-TO-SELF */
- dur = 2 * sifs;
- }
-
- dur +=
- (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type,
- frame_len);
- if (ba)
- dur +=
- (u16) brcms_c_calc_ba_time(wlc, frame_rate,
- BRCMS_SHORT_PREAMBLE);
- else
- dur +=
- (u16) brcms_c_calc_ack_time(wlc, frame_rate,
- frame_preamble_type);
- return dur;
-}
-
-u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec)
-{
- u16 phyctl1 = 0;
- u16 bw;
-
- if (BRCMS_ISLCNPHY(wlc->band)) {
- bw = PHY_TXC1_BW_20MHZ;
- } else {
- bw = RSPEC_GET_BW(rspec);
- /* 10Mhz is not supported yet */
- if (bw < PHY_TXC1_BW_20MHZ) {
- wiphy_err(wlc->wiphy, "phytxctl1_calc: bw %d is "
- "not supported yet, set to 20L\n", bw);
- bw = PHY_TXC1_BW_20MHZ;
- }
- }
-
- if (IS_MCS(rspec)) {
- uint mcs = rspec & RSPEC_RATE_MASK;
-
- /* bw, stf, coding-type is part of RSPEC_PHYTXBYTE2 returns */
- phyctl1 = RSPEC_PHYTXBYTE2(rspec);
- /* set the upper byte of phyctl1 */
- phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8);
- } else if (IS_CCK(rspec) && !BRCMS_ISLCNPHY(wlc->band)
- && !BRCMS_ISSSLPNPHY(wlc->band)) {
- /*
- * In CCK mode LPPHY overloads OFDM Modulation bits with CCK
- * Data Rate. Eventually MIMOPHY would also be converted to
- * this format
- */
- /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
- phyctl1 = (bw | (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
- } else { /* legacy OFDM/CCK */
- s16 phycfg;
- /* get the phyctl byte from rate phycfg table */
- phycfg = brcms_c_rate_legacy_phyctl(RSPEC2RATE(rspec));
- if (phycfg == -1) {
- wiphy_err(wlc->wiphy, "phytxctl1_calc: wrong "
- "legacy OFDM/CCK rate\n");
- phycfg = 0;
- }
- /* set the upper byte of phyctl1 */
- phyctl1 =
- (bw | (phycfg << 8) |
- (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
- }
- return phyctl1;
-}
-
-u32
-brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec,
- bool use_rspec, u16 mimo_ctlchbw)
-{
- u32 rts_rspec = 0;
-
- if (use_rspec)
- /* use frame rate as rts rate */
- rts_rspec = rspec;
- else if (wlc->band->gmode && wlc->protection->_g && !IS_CCK(rspec))
- /* Use 11Mbps as the g protection RTS target rate and fallback.
- * Use the BRCMS_BASIC_RATE() lookup to find the best basic rate
- * under the target in case 11 Mbps is not Basic.
- * 6 and 9 Mbps are not usually selected by rate selection, but
- * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11
- * is more robust.
- */
- rts_rspec = BRCMS_BASIC_RATE(wlc, BRCM_RATE_11M);
- else
- /* calculate RTS rate and fallback rate based on the frame rate
- * RTS must be sent at a basic rate since it is a
- * control frame, sec 9.6 of 802.11 spec
- */
- rts_rspec = BRCMS_BASIC_RATE(wlc, rspec);
-
- if (BRCMS_PHY_11N_CAP(wlc->band)) {
- /* set rts txbw to correct side band */
- rts_rspec &= ~RSPEC_BW_MASK;
-
- /*
- * if rspec/rspec_fallback is 40MHz, then send RTS on both
- * 20MHz channel (DUP), otherwise send RTS on control channel
- */
- if (RSPEC_IS40MHZ(rspec) && !IS_CCK(rts_rspec))
- rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT);
- else
- rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
-
- /* pick siso/cdd as default for ofdm */
- if (IS_OFDM(rts_rspec)) {
- rts_rspec &= ~RSPEC_STF_MASK;
- rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
- }
- }
- return rts_rspec;
-}
-
-/*
- * Add struct d11txh, struct cck_phy_hdr.
- *
- * 'p' data must start with 802.11 MAC header
- * 'p' must allow enough bytes of local headers to be "pushed" onto the packet
- *
- * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes)
- *
- */
-static u16
-brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
- struct sk_buff *p, struct scb *scb, uint frag,
- uint nfrags, uint queue, uint next_frag_len,
- struct wsec_key *key, u32 rspec_override)
+static u16
+brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
+ struct sk_buff *p, struct scb *scb, uint frag,
+ uint nfrags, uint queue, uint next_frag_len,
+ struct wsec_key *key, u32 rspec_override)
{
struct ieee80211_hdr *h;
struct d11txh *txh;
u16 mimo_txbw;
u8 mimo_preamble_type;
- /* locate 802.11 MAC header */
- h = (struct ieee80211_hdr *)(p->data);
- qos = ieee80211_is_data_qos(h->frame_control);
+ /* locate 802.11 MAC header */
+ h = (struct ieee80211_hdr *)(p->data);
+ qos = ieee80211_is_data_qos(h->frame_control);
+
+ /* compute length of frame in bytes for use in PLCP computations */
+ len = brcmu_pkttotlen(p);
+ phylen = len + FCS_LEN;
+
+ /* If WEP enabled, add room in phylen for the additional bytes of
+ * ICV which MAC generates. We do NOT add the additional bytes to
+ * the packet itself, thus phylen = packet length + ICV_LEN + FCS_LEN
+ * in this case
+ */
+ if (key)
+ phylen += key->icv_len;
+
+ /* Get tx_info */
+ tx_info = IEEE80211_SKB_CB(p);
+
+ /* add PLCP */
+ plcp = skb_push(p, D11_PHY_HDR_LEN);
+
+ /* add Broadcom tx descriptor header */
+ txh = (struct d11txh *) skb_push(p, D11_TXH_LEN);
+ memset(txh, 0, D11_TXH_LEN);
+
+ /* setup frameid */
+ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ /* non-AP STA should never use BCMC queue */
+ if (queue == TX_BCMC_FIFO) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: ASSERT queue == "
+ "TX_BCMC!\n", BRCMS_UNIT(wlc), __func__);
+ frameid = bcmc_fid_generate(wlc, NULL, txh);
+ } else {
+ /* Increment the counter for first fragment */
+ if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+ SCB_SEQNUM(scb, p->priority)++;
+
+ /* extract fragment number from frame first */
+ seq = le16_to_cpu(seq) & FRAGNUM_MASK;
+ seq |= (SCB_SEQNUM(scb, p->priority) << SEQNUM_SHIFT);
+ h->seq_ctrl = cpu_to_le16(seq);
+
+ frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
+ (queue & TXFID_QUEUE_MASK);
+ }
+ }
+ frameid |= queue & TXFID_QUEUE_MASK;
+
+ /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
+ if (SCB_PS(scb) || ieee80211_is_beacon(h->frame_control))
+ mcl |= TXC_IGNOREPMQ;
+
+ txrate[0] = tx_info->control.rates;
+ txrate[1] = txrate[0] + 1;
+
+ /*
+ * if rate control algorithm didn't give us a fallback
+ * rate, use the primary rate
+ */
+ if (txrate[1]->idx < 0)
+ txrate[1] = txrate[0];
+
+ for (k = 0; k < hw->max_rates; k++) {
+ is_mcs[k] =
+ txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
+ if (!is_mcs[k]) {
+ if ((txrate[k]->idx >= 0)
+ && (txrate[k]->idx <
+ hw->wiphy->bands[tx_info->band]->n_bitrates)) {
+ rate_val[k] =
+ hw->wiphy->bands[tx_info->band]->
+ bitrates[txrate[k]->idx].hw_value;
+ short_preamble[k] =
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
+ true : false;
+ } else {
+ rate_val[k] = BRCM_RATE_1M;
+ }
+ } else {
+ rate_val[k] = txrate[k]->idx;
+ }
+
+ /*
+ * Currently only support same setting for primay and
+ * fallback rates. Unify flags for each rate into a
+ * single value for the frame
+ */
+ use_rts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
+ use_cts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;
+
+ if (is_mcs[k])
+ rate_val[k] |= NRATE_MCS_INUSE;
+
+ rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, rate_val[k]);
+
+ /*
+ * (1) RATE:
+ * determine and validate primary rate
+ * and fallback rates
+ */
+ if (!RSPEC_ACTIVE(rspec[k])) {
+ rspec[k] = BRCM_RATE_1M;
+ } else {
+ if (!is_multicast_ether_addr(h->addr1)) {
+ /* set tx antenna config */
+ brcms_c_antsel_antcfg_get(wlc->asi, false,
+ false, 0, 0, &antcfg, &fbantcfg);
+ }
+ }
+ }
+
+ phyctl1_stf = wlc->stf->ss_opmode;
+
+ if (N_ENAB(wlc->pub)) {
+ for (k = 0; k < hw->max_rates; k++) {
+ /*
+ * apply siso/cdd to single stream mcs's or ofdm
+ * if rspec is auto selected
+ */
+ if (((IS_MCS(rspec[k]) &&
+ IS_SINGLE_STREAM(rspec[k] & RSPEC_RATE_MASK)) ||
+ IS_OFDM(rspec[k]))
+ && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
+ || !(rspec[k] & RSPEC_OVERRIDE))) {
+ rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);
+
+ /* For SISO MCS use STBC if possible */
+ if (IS_MCS(rspec[k])
+ && BRCMS_STF_SS_STBC_TX(wlc, scb)) {
+ u8 stc;
+
+ /* Nss for single stream is always 1 */
+ stc = 1;
+ rspec[k] |= (PHY_TXC1_MODE_STBC <<
+ RSPEC_STF_SHIFT) |
+ (stc << RSPEC_STC_SHIFT);
+ } else
+ rspec[k] |=
+ (phyctl1_stf << RSPEC_STF_SHIFT);
+ }
+
+ /*
+ * Is the phy configured to use 40MHZ frames? If
+ * so then pick the desired txbw
+ */
+ if (CHSPEC_WLC_BW(wlc->chanspec) == BRCMS_40_MHZ) {
+ /* default txbw is 20in40 SB */
+ mimo_ctlchbw = mimo_txbw =
+ CHSPEC_SB_UPPER(BRCMS_BAND_PI_RADIO_CHANSPEC)
+ ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+
+ if (IS_MCS(rspec[k])) {
+ /* mcs 32 must be 40b/w DUP */
+ if ((rspec[k] & RSPEC_RATE_MASK)
+ == 32) {
+ mimo_txbw =
+ PHY_TXC1_BW_40MHZ_DUP;
+ /* use override */
+ } else if (wlc->mimo_40txbw != AUTO)
+ mimo_txbw = wlc->mimo_40txbw;
+ /* else check if dst is using 40 Mhz */
+ else if (scb->flags & SCB_IS40)
+ mimo_txbw = PHY_TXC1_BW_40MHZ;
+ } else if (IS_OFDM(rspec[k])) {
+ if (wlc->ofdm_40txbw != AUTO)
+ mimo_txbw = wlc->ofdm_40txbw;
+ } else if (wlc->cck_40txbw != AUTO) {
+ mimo_txbw = wlc->cck_40txbw;
+ }
+ } else {
+ /*
+ * mcs32 is 40 b/w only.
+ * This is possible for probe packets on
+ * a STA during SCAN
+ */
+ if ((rspec[k] & RSPEC_RATE_MASK) == 32)
+ /* mcs 0 */
+ rspec[k] = RSPEC_MIMORATE;
+
+ mimo_txbw = PHY_TXC1_BW_20MHZ;
+ }
+
+ /* Set channel width */
+ rspec[k] &= ~RSPEC_BW_MASK;
+ if ((k == 0) || ((k > 0) && IS_MCS(rspec[k])))
+ rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
+ else
+ rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+
+ /* Disable short GI, not supported yet */
+ rspec[k] &= ~RSPEC_SHORT_GI;
+
+ mimo_preamble_type = BRCMS_MM_PREAMBLE;
+ if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
+ mimo_preamble_type = BRCMS_GF_PREAMBLE;
+
+ if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
+ && (!IS_MCS(rspec[k]))) {
+ wiphy_err(wlc->wiphy, "wl%d: %s: IEEE80211_TX_"
+ "RC_MCS != IS_MCS(rspec)\n",
+ BRCMS_UNIT(wlc), __func__);
+ }
+
+ if (IS_MCS(rspec[k])) {
+ preamble_type[k] = mimo_preamble_type;
+
+ /*
+ * if SGI is selected, then forced mm
+ * for single stream
+ */
+ if ((rspec[k] & RSPEC_SHORT_GI)
+ && IS_SINGLE_STREAM(rspec[k] &
+ RSPEC_RATE_MASK))
+ preamble_type[k] = BRCMS_MM_PREAMBLE;
+ }
+
+ /* should be better conditionalized */
+ if (!IS_MCS(rspec[0])
+ && (tx_info->control.rates[0].
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
+ preamble_type[k] = BRCMS_SHORT_PREAMBLE;
+ }
+ } else {
+ for (k = 0; k < hw->max_rates; k++) {
+ /* Set ctrlchbw as 20Mhz */
+ rspec[k] &= ~RSPEC_BW_MASK;
+ rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);
+
+ /* for nphy, stf of ofdm frames must follow policies */
+ if (BRCMS_ISNPHY(wlc->band) && IS_OFDM(rspec[k])) {
+ rspec[k] &= ~RSPEC_STF_MASK;
+ rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
+ }
+ }
+ }
+
+ /* Reset these for use with AMPDU's */
+ txrate[0]->count = 0;
+ txrate[1]->count = 0;
+
+ /* (2) PROTECTION, may change rspec */
+ if ((ieee80211_is_data(h->frame_control) ||
+ ieee80211_is_mgmt(h->frame_control)) &&
+ (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1))
+ use_rts = true;
+
+ /* (3) PLCP: determine PLCP header and MAC duration,
+ * fill struct d11txh */
+ brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp);
+ brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
+ memcpy(&txh->FragPLCPFallback,
+ plcp_fallback, sizeof(txh->FragPLCPFallback));
+
+ /* Length field now put in CCK FBR CRC field */
+ if (IS_CCK(rspec[1])) {
+ txh->FragPLCPFallback[4] = phylen & 0xff;
+ txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
+ }
+
+ /* MIMO-RATE: need validation ?? */
+ mainrates = IS_OFDM(rspec[0]) ?
+ D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) :
+ plcp[0];
+
+ /* DUR field for main rate */
+ if (!ieee80211_is_pspoll(h->frame_control) &&
+ !is_multicast_ether_addr(h->addr1) && !use_rifs) {
+ durid =
+ brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0],
+ next_frag_len);
+ h->duration_id = cpu_to_le16(durid);
+ } else if (use_rifs) {
+ /* NAV protect to end of next max packet size */
+ durid =
+ (u16) brcms_c_calc_frame_time(wlc, rspec[0],
+ preamble_type[0],
+ DOT11_MAX_FRAG_LEN);
+ durid += RIFS_11N_TIME;
+ h->duration_id = cpu_to_le16(durid);
+ }
+
+ /* DUR field for fallback rate */
+ if (ieee80211_is_pspoll(h->frame_control))
+ txh->FragDurFallback = h->duration_id;
+ else if (is_multicast_ether_addr(h->addr1) || use_rifs)
+ txh->FragDurFallback = 0;
+ else {
+ durid = brcms_c_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], next_frag_len);
+ txh->FragDurFallback = cpu_to_le16(durid);
+ }
- /* compute length of frame in bytes for use in PLCP computations */
- len = brcmu_pkttotlen(p);
- phylen = len + FCS_LEN;
+ /* (4) MAC-HDR: MacTxControlLow */
+ if (frag == 0)
+ mcl |= TXC_STARTMSDU;
- /* If WEP enabled, add room in phylen for the additional bytes of
- * ICV which MAC generates. We do NOT add the additional bytes to
- * the packet itself, thus phylen = packet length + ICV_LEN + FCS_LEN
- * in this case
- */
- if (key)
- phylen += key->icv_len;
+ if (!is_multicast_ether_addr(h->addr1))
+ mcl |= TXC_IMMEDACK;
- /* Get tx_info */
- tx_info = IEEE80211_SKB_CB(p);
+ if (BAND_5G(wlc->band->bandtype))
+ mcl |= TXC_FREQBAND_5G;
- /* add PLCP */
- plcp = skb_push(p, D11_PHY_HDR_LEN);
+ if (CHSPEC_IS40(BRCMS_BAND_PI_RADIO_CHANSPEC))
+ mcl |= TXC_BW_40;
- /* add Broadcom tx descriptor header */
- txh = (struct d11txh *) skb_push(p, D11_TXH_LEN);
- memset(txh, 0, D11_TXH_LEN);
+ /* set AMIC bit if using hardware TKIP MIC */
+ if (hwtkmic)
+ mcl |= TXC_AMIC;
- /* setup frameid */
- if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
- /* non-AP STA should never use BCMC queue */
- if (queue == TX_BCMC_FIFO) {
- wiphy_err(wlc->wiphy, "wl%d: %s: ASSERT queue == "
- "TX_BCMC!\n", BRCMS_UNIT(wlc), __func__);
- frameid = bcmc_fid_generate(wlc, NULL, txh);
- } else {
- /* Increment the counter for first fragment */
- if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
- SCB_SEQNUM(scb, p->priority)++;
+ txh->MacTxControlLow = cpu_to_le16(mcl);
- /* extract fragment number from frame first */
- seq = le16_to_cpu(seq) & FRAGNUM_MASK;
- seq |= (SCB_SEQNUM(scb, p->priority) << SEQNUM_SHIFT);
- h->seq_ctrl = cpu_to_le16(seq);
+ /* MacTxControlHigh */
+ mch = 0;
- frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
- (queue & TXFID_QUEUE_MASK);
- }
+ /* Set fallback rate preamble type */
+ if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) ||
+ (preamble_type[1] == BRCMS_GF_PREAMBLE)) {
+ if (RSPEC2RATE(rspec[1]) != BRCM_RATE_1M)
+ mch |= TXC_PREAMBLE_DATA_FB_SHORT;
}
- frameid |= queue & TXFID_QUEUE_MASK;
- /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
- if (SCB_PS(scb) || ieee80211_is_beacon(h->frame_control))
- mcl |= TXC_IGNOREPMQ;
+ /* MacFrameControl */
+ memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16));
+ txh->TxFesTimeNormal = cpu_to_le16(0);
- txrate[0] = tx_info->control.rates;
- txrate[1] = txrate[0] + 1;
+ txh->TxFesTimeFallback = cpu_to_le16(0);
+
+ /* TxFrameRA */
+ memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN);
+
+ /* TxFrameID */
+ txh->TxFrameID = cpu_to_le16(frameid);
/*
- * if rate control algorithm didn't give us a fallback
- * rate, use the primary rate
+ * TxStatus, Note the case of recreating the first frag of a suppressed
+ * frame then we may need to reset the retry cnt's via the status reg
*/
- if (txrate[1]->idx < 0)
- txrate[1] = txrate[0];
+ txh->TxStatus = cpu_to_le16(status);
- for (k = 0; k < hw->max_rates; k++) {
- is_mcs[k] =
- txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
- if (!is_mcs[k]) {
- if ((txrate[k]->idx >= 0)
- && (txrate[k]->idx <
- hw->wiphy->bands[tx_info->band]->n_bitrates)) {
- rate_val[k] =
- hw->wiphy->bands[tx_info->band]->
- bitrates[txrate[k]->idx].hw_value;
- short_preamble[k] =
- txrate[k]->
- flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
- true : false;
- } else {
- rate_val[k] = BRCM_RATE_1M;
- }
- } else {
- rate_val[k] = txrate[k]->idx;
- }
+ /*
+ * extra fields for ucode AMPDU aggregation, the new fields are added to
+ * the END of previous structure so that it's compatible in driver.
+ */
+ txh->MaxNMpdus = cpu_to_le16(0);
+ txh->MaxABytes_MRT = cpu_to_le16(0);
+ txh->MaxABytes_FBR = cpu_to_le16(0);
+ txh->MinMBytes = cpu_to_le16(0);
- /*
- * Currently only support same setting for primay and
- * fallback rates. Unify flags for each rate into a
- * single value for the frame
- */
- use_rts |=
- txrate[k]->
- flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
- use_cts |=
- txrate[k]->
- flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;
+ /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration,
+ * furnish struct d11txh */
+ /* RTS PLCP header and RTS frame */
+ if (use_rts || use_cts) {
+ if (use_rts && use_cts)
+ use_cts = false;
- if (is_mcs[k])
- rate_val[k] |= NRATE_MCS_INUSE;
+ for (k = 0; k < 2; k++) {
+ rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k],
+ false,
+ mimo_ctlchbw);
+ }
- rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, rate_val[k]);
+ if (!IS_OFDM(rts_rspec[0]) &&
+ !((RSPEC2RATE(rts_rspec[0]) == BRCM_RATE_1M) ||
+ (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
+ rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
+ }
- /*
- * (1) RATE:
- * determine and validate primary rate
- * and fallback rates
- */
- if (!RSPEC_ACTIVE(rspec[k])) {
- rspec[k] = BRCM_RATE_1M;
+ if (!IS_OFDM(rts_rspec[1]) &&
+ !((RSPEC2RATE(rts_rspec[1]) == BRCM_RATE_1M) ||
+ (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
+ rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_FB_SHORT;
+ }
+
+ /* RTS/CTS additions to MacTxControlLow */
+ if (use_cts) {
+ txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS);
} else {
- if (!is_multicast_ether_addr(h->addr1)) {
- /* set tx antenna config */
- brcms_c_antsel_antcfg_get(wlc->asi, false,
- false, 0, 0, &antcfg, &fbantcfg);
- }
+ txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS);
+ txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME);
}
- }
- phyctl1_stf = wlc->stf->ss_opmode;
+ /* RTS PLCP header */
+ rts_plcp = txh->RTSPhyHeader;
+ if (use_cts)
+ rts_phylen = DOT11_CTS_LEN + FCS_LEN;
+ else
+ rts_phylen = DOT11_RTS_LEN + FCS_LEN;
- if (N_ENAB(wlc->pub)) {
- for (k = 0; k < hw->max_rates; k++) {
- /*
- * apply siso/cdd to single stream mcs's or ofdm
- * if rspec is auto selected
- */
- if (((IS_MCS(rspec[k]) &&
- IS_SINGLE_STREAM(rspec[k] & RSPEC_RATE_MASK)) ||
- IS_OFDM(rspec[k]))
- && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
- || !(rspec[k] & RSPEC_OVERRIDE))) {
- rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);
+ brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);
- /* For SISO MCS use STBC if possible */
- if (IS_MCS(rspec[k])
- && BRCMS_STF_SS_STBC_TX(wlc, scb)) {
- u8 stc;
+ /* fallback rate version of RTS PLCP header */
+ brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen,
+ rts_plcp_fallback);
+ memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback,
+ sizeof(txh->RTSPLCPFallback));
- /* Nss for single stream is always 1 */
- stc = 1;
- rspec[k] |= (PHY_TXC1_MODE_STBC <<
- RSPEC_STF_SHIFT) |
- (stc << RSPEC_STC_SHIFT);
- } else
- rspec[k] |=
- (phyctl1_stf << RSPEC_STF_SHIFT);
- }
+ /* RTS frame fields... */
+ rts = (struct ieee80211_rts *)&txh->rts_frame;
+
+ durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
+ rspec[0], rts_preamble_type[0],
+ preamble_type[0], phylen, false);
+ rts->duration = cpu_to_le16(durid);
+ /* fallback rate version of RTS DUR field */
+ durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
+ rts_rspec[1], rspec[1],
+ rts_preamble_type[1],
+ preamble_type[1], phylen, false);
+ txh->RTSDurFallback = cpu_to_le16(durid);
+
+ if (use_cts) {
+ rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_CTS);
+
+ memcpy(&rts->ra, &h->addr2, ETH_ALEN);
+ } else {
+ rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN);
+ }
+
+ /* mainrate
+ * low 8 bits: main frag rate/mcs,
+ * high 8 bits: rts/cts rate/mcs
+ */
+ mainrates |= (IS_OFDM(rts_rspec[0]) ?
+ D11A_PHY_HDR_GRATE(
+ (struct ofdm_phy_hdr *) rts_plcp) :
+ rts_plcp[0]) << 8;
+ } else {
+ memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
+ memset((char *)&txh->rts_frame, 0,
+ sizeof(struct ieee80211_rts));
+ memset((char *)txh->RTSPLCPFallback, 0,
+ sizeof(txh->RTSPLCPFallback));
+ txh->RTSDurFallback = 0;
+ }
- /*
- * Is the phy configured to use 40MHZ frames? If
- * so then pick the desired txbw
- */
- if (CHSPEC_WLC_BW(wlc->chanspec) == BRCMS_40_MHZ) {
- /* default txbw is 20in40 SB */
- mimo_ctlchbw = mimo_txbw =
- CHSPEC_SB_UPPER(BRCMS_BAND_PI_RADIO_CHANSPEC)
- ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+#ifdef SUPPORT_40MHZ
+ /* add null delimiter count */
+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && IS_MCS(rspec))
+ txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
+ brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
- if (IS_MCS(rspec[k])) {
- /* mcs 32 must be 40b/w DUP */
- if ((rspec[k] & RSPEC_RATE_MASK)
- == 32) {
- mimo_txbw =
- PHY_TXC1_BW_40MHZ_DUP;
- /* use override */
- } else if (wlc->mimo_40txbw != AUTO)
- mimo_txbw = wlc->mimo_40txbw;
- /* else check if dst is using 40 Mhz */
- else if (scb->flags & SCB_IS40)
- mimo_txbw = PHY_TXC1_BW_40MHZ;
- } else if (IS_OFDM(rspec[k])) {
- if (wlc->ofdm_40txbw != AUTO)
- mimo_txbw = wlc->ofdm_40txbw;
- } else if (wlc->cck_40txbw != AUTO) {
- mimo_txbw = wlc->cck_40txbw;
- }
- } else {
- /*
- * mcs32 is 40 b/w only.
- * This is possible for probe packets on
- * a STA during SCAN
- */
- if ((rspec[k] & RSPEC_RATE_MASK) == 32)
- /* mcs 0 */
- rspec[k] = RSPEC_MIMORATE;
+#endif
- mimo_txbw = PHY_TXC1_BW_20MHZ;
- }
+ /*
+ * Now that RTS/RTS FB preamble types are updated, write
+ * the final value
+ */
+ txh->MacTxControlHigh = cpu_to_le16(mch);
- /* Set channel width */
- rspec[k] &= ~RSPEC_BW_MASK;
- if ((k == 0) || ((k > 0) && IS_MCS(rspec[k])))
- rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
- else
- rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+ /*
+ * MainRates (both the rts and frag plcp rates have
+ * been calculated now)
+ */
+ txh->MainRates = cpu_to_le16(mainrates);
- /* Disable short GI, not supported yet */
- rspec[k] &= ~RSPEC_SHORT_GI;
+ /* XtraFrameTypes */
+ xfts = FRAMETYPE(rspec[1], wlc->mimoft);
+ xfts |= (FRAMETYPE(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
+ xfts |= (FRAMETYPE(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
+ xfts |=
+ CHSPEC_CHANNEL(BRCMS_BAND_PI_RADIO_CHANSPEC) << XFTS_CHANNEL_SHIFT;
+ txh->XtraFrameTypes = cpu_to_le16(xfts);
- mimo_preamble_type = BRCMS_MM_PREAMBLE;
- if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
- mimo_preamble_type = BRCMS_GF_PREAMBLE;
+ /* PhyTxControlWord */
+ phyctl = FRAMETYPE(rspec[0], wlc->mimoft);
+ if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) ||
+ (preamble_type[0] == BRCMS_GF_PREAMBLE)) {
+ if (RSPEC2RATE(rspec[0]) != BRCM_RATE_1M)
+ phyctl |= PHY_TXC_SHORT_HDR;
+ }
- if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
- && (!IS_MCS(rspec[k]))) {
- wiphy_err(wlc->wiphy, "wl%d: %s: IEEE80211_TX_"
- "RC_MCS != IS_MCS(rspec)\n",
- BRCMS_UNIT(wlc), __func__);
- }
+ /* phytxant is properly bit shifted */
+ phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
+ txh->PhyTxControlWord = cpu_to_le16(phyctl);
- if (IS_MCS(rspec[k])) {
- preamble_type[k] = mimo_preamble_type;
+ /* PhyTxControlWord_1 */
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ u16 phyctl1 = 0;
- /*
- * if SGI is selected, then forced mm
- * for single stream
- */
- if ((rspec[k] & RSPEC_SHORT_GI)
- && IS_SINGLE_STREAM(rspec[k] &
- RSPEC_RATE_MASK))
- preamble_type[k] = BRCMS_MM_PREAMBLE;
- }
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]);
+ txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1);
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]);
+ txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1);
- /* should be better conditionalized */
- if (!IS_MCS(rspec[0])
- && (tx_info->control.rates[0].
- flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
- preamble_type[k] = BRCMS_SHORT_PREAMBLE;
+ if (use_rts || use_cts) {
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]);
+ txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1);
+ phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]);
+ txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1);
}
- } else {
- for (k = 0; k < hw->max_rates; k++) {
- /* Set ctrlchbw as 20Mhz */
- rspec[k] &= ~RSPEC_BW_MASK;
- rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);
- /* for nphy, stf of ofdm frames must follow policies */
- if (BRCMS_ISNPHY(wlc->band) && IS_OFDM(rspec[k])) {
- rspec[k] &= ~RSPEC_STF_MASK;
- rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
- }
+ /*
+ * For mcs frames, if mixedmode(overloaded with long preamble)
+ * is going to be set, fill in non-zero MModeLen and/or
+ * MModeFbrLen it will be unnecessary if they are separated
+ */
+ if (IS_MCS(rspec[0]) &&
+ (preamble_type[0] == BRCMS_MM_PREAMBLE)) {
+ u16 mmodelen =
+ brcms_c_calc_lsig_len(wlc, rspec[0], phylen);
+ txh->MModeLen = cpu_to_le16(mmodelen);
+ }
+
+ if (IS_MCS(rspec[1]) &&
+ (preamble_type[1] == BRCMS_MM_PREAMBLE)) {
+ u16 mmodefbrlen =
+ brcms_c_calc_lsig_len(wlc, rspec[1], phylen);
+ txh->MModeFbrLen = cpu_to_le16(mmodefbrlen);
}
}
- /* Reset these for use with AMPDU's */
- txrate[0]->count = 0;
- txrate[1]->count = 0;
+ ac = skb_get_queue_mapping(p);
+ if (SCB_WME(scb) && qos && wlc->edcf_txop[ac]) {
+ uint frag_dur, dur, dur_fallback;
- /* (2) PROTECTION, may change rspec */
- if ((ieee80211_is_data(h->frame_control) ||
- ieee80211_is_mgmt(h->frame_control)) &&
- (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1))
- use_rts = true;
+ /* WME: Update TXOP threshold */
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) {
+ frag_dur =
+ brcms_c_calc_frame_time(wlc, rspec[0],
+ preamble_type[0], phylen);
- /* (3) PLCP: determine PLCP header and MAC duration,
- * fill struct d11txh */
- brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp);
- brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
- memcpy(&txh->FragPLCPFallback,
- plcp_fallback, sizeof(txh->FragPLCPFallback));
+ if (rts) {
+ /* 1 RTS or CTS-to-self frame */
+ dur =
+ brcms_c_calc_cts_time(wlc, rts_rspec[0],
+ rts_preamble_type[0]);
+ dur_fallback =
+ brcms_c_calc_cts_time(wlc, rts_rspec[1],
+ rts_preamble_type[1]);
+ /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
+ dur += le16_to_cpu(rts->duration);
+ dur_fallback +=
+ le16_to_cpu(txh->RTSDurFallback);
+ } else if (use_rifs) {
+ dur = frag_dur;
+ dur_fallback = 0;
+ } else {
+ /* frame + SIFS + ACK */
+ dur = frag_dur;
+ dur +=
+ brcms_c_compute_frame_dur(wlc, rspec[0],
+ preamble_type[0], 0);
- /* Length field now put in CCK FBR CRC field */
- if (IS_CCK(rspec[1])) {
- txh->FragPLCPFallback[4] = phylen & 0xff;
- txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
- }
+ dur_fallback =
+ brcms_c_calc_frame_time(wlc, rspec[1],
+ preamble_type[1],
+ phylen);
+ dur_fallback +=
+ brcms_c_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], 0);
+ }
+ /* NEED to set TxFesTimeNormal (hard) */
+ txh->TxFesTimeNormal = cpu_to_le16((u16) dur);
+ /*
+ * NEED to set fallback rate version of
+ * TxFesTimeNormal (hard)
+ */
+ txh->TxFesTimeFallback =
+ cpu_to_le16((u16) dur_fallback);
- /* MIMO-RATE: need validation ?? */
- mainrates = IS_OFDM(rspec[0]) ?
- D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) :
- plcp[0];
+ /*
+ * update txop byte threshold (txop minus intraframe
+ * overhead)
+ */
+ if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
+ uint newfragthresh;
- /* DUR field for main rate */
- if (!ieee80211_is_pspoll(h->frame_control) &&
- !is_multicast_ether_addr(h->addr1) && !use_rifs) {
- durid =
- brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0],
- next_frag_len);
- h->duration_id = cpu_to_le16(durid);
- } else if (use_rifs) {
- /* NAV protect to end of next max packet size */
- durid =
- (u16) brcms_c_calc_frame_time(wlc, rspec[0],
- preamble_type[0],
- DOT11_MAX_FRAG_LEN);
- durid += RIFS_11N_TIME;
- h->duration_id = cpu_to_le16(durid);
- }
+ newfragthresh =
+ brcms_c_calc_frame_len(wlc,
+ rspec[0], preamble_type[0],
+ (wlc->edcf_txop[ac] -
+ (dur - frag_dur)));
+ /* range bound the fragthreshold */
+ if (newfragthresh < DOT11_MIN_FRAG_LEN)
+ newfragthresh =
+ DOT11_MIN_FRAG_LEN;
+ else if (newfragthresh >
+ wlc->usr_fragthresh)
+ newfragthresh =
+ wlc->usr_fragthresh;
+ /* update the fragthresh and do txc update */
+ if (wlc->fragthresh[queue] !=
+ (u16) newfragthresh)
+ wlc->fragthresh[queue] =
+ (u16) newfragthresh;
+ } else {
+ wiphy_err(wlc->wiphy, "wl%d: %s txop invalid "
+ "for rate %d\n",
+ wlc->pub->unit, fifo_names[queue],
+ RSPEC2RATE(rspec[0]));
+ }
- /* DUR field for fallback rate */
- if (ieee80211_is_pspoll(h->frame_control))
- txh->FragDurFallback = h->duration_id;
- else if (is_multicast_ether_addr(h->addr1) || use_rifs)
- txh->FragDurFallback = 0;
- else {
- durid = brcms_c_compute_frame_dur(wlc, rspec[1],
- preamble_type[1], next_frag_len);
- txh->FragDurFallback = cpu_to_le16(durid);
+ if (dur > wlc->edcf_txop[ac])
+ wiphy_err(wlc->wiphy, "wl%d: %s: %s txop "
+ "exceeded phylen %d/%d dur %d/%d\n",
+ wlc->pub->unit, __func__,
+ fifo_names[queue],
+ phylen, wlc->fragthresh[queue],
+ dur, wlc->edcf_txop[ac]);
+ }
}
- /* (4) MAC-HDR: MacTxControlLow */
- if (frag == 0)
- mcl |= TXC_STARTMSDU;
+ return 0;
+}
- if (!is_multicast_ether_addr(h->addr1))
- mcl |= TXC_IMMEDACK;
+bool
+brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu,
+ struct ieee80211_hw *hw)
+{
+ u8 prio;
+ uint fifo;
+ struct scb *scb = &global_scb;
+ struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data);
- if (BAND_5G(wlc->band->bandtype))
- mcl |= TXC_FREQBAND_5G;
+ /*
+ * 802.11 standard requires management traffic
+ * to go at highest priority
+ */
+ prio = ieee80211_is_data(d11_header->frame_control) ? sdu->priority :
+ MAXPRIO;
+ fifo = prio2fifo[prio];
+ if (unlikely
+ (brcms_c_d11hdrs_mac80211(
+ wlc, hw, sdu, scb, 0, 1, fifo, 0, NULL, 0)))
+ return -EINVAL;
+ brcms_c_txq_enq(wlc, scb, sdu, BRCMS_PRIO_TO_PREC(prio));
+ brcms_c_send_q(wlc);
+ return 0;
+}
- if (CHSPEC_IS40(BRCMS_BAND_PI_RADIO_CHANSPEC))
- mcl |= TXC_BW_40;
+void brcms_c_send_q(struct brcms_c_info *wlc)
+{
+ struct sk_buff *pkt[DOT11_MAXNUMFRAGS];
+ int prec;
+ u16 prec_map;
+ int err = 0, i, count;
+ uint fifo;
+ struct brcms_txq_info *qi = wlc->pkt_queue;
+ struct pktq *q = &qi->q;
+ struct ieee80211_tx_info *tx_info;
- /* set AMIC bit if using hardware TKIP MIC */
- if (hwtkmic)
- mcl |= TXC_AMIC;
+ if (in_send_q)
+ return;
+ else
+ in_send_q = true;
- txh->MacTxControlLow = cpu_to_le16(mcl);
+ prec_map = wlc->tx_prec_map;
- /* MacTxControlHigh */
- mch = 0;
+ /* Send all the enq'd pkts that we can.
+ * Dequeue packets with precedence with empty HW fifo only
+ */
+ while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) {
+ tx_info = IEEE80211_SKB_CB(pkt[0]);
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec);
+ } else {
+ count = 1;
+ err = brcms_c_prep_pdu(wlc, pkt[0], &fifo);
+ if (!err) {
+ for (i = 0; i < count; i++)
+ brcms_c_txfifo(wlc, fifo, pkt[i], true,
+ 1);
+ }
+ }
- /* Set fallback rate preamble type */
- if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) ||
- (preamble_type[1] == BRCMS_GF_PREAMBLE)) {
- if (RSPEC2RATE(rspec[1]) != BRCM_RATE_1M)
- mch |= TXC_PREAMBLE_DATA_FB_SHORT;
+ if (err == -EBUSY) {
+ brcmu_pktq_penq_head(q, prec, pkt[0]);
+ /*
+ * If send failed due to any other reason than a
+ * change in HW FIFO condition, quit. Otherwise,
+ * read the new prec_map!
+ */
+ if (prec_map == wlc->tx_prec_map)
+ break;
+ prec_map = wlc->tx_prec_map;
+ }
}
- /* MacFrameControl */
- memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16));
- txh->TxFesTimeNormal = cpu_to_le16(0);
-
- txh->TxFesTimeFallback = cpu_to_le16(0);
-
- /* TxFrameRA */
- memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN);
-
- /* TxFrameID */
- txh->TxFrameID = cpu_to_le16(frameid);
-
- /*
- * TxStatus, Note the case of recreating the first frag of a suppressed
- * frame then we may need to reset the retry cnt's via the status reg
- */
- txh->TxStatus = cpu_to_le16(status);
-
/*
- * extra fields for ucode AMPDU aggregation, the new fields are added to
- * the END of previous structure so that it's compatible in driver.
+ * Check if flow control needs to be turned off after
+ * sending the packet
*/
- txh->MaxNMpdus = cpu_to_le16(0);
- txh->MaxABytes_MRT = cpu_to_le16(0);
- txh->MaxABytes_FBR = cpu_to_le16(0);
- txh->MinMBytes = cpu_to_le16(0);
-
- /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration,
- * furnish struct d11txh */
- /* RTS PLCP header and RTS frame */
- if (use_rts || use_cts) {
- if (use_rts && use_cts)
- use_cts = false;
-
- for (k = 0; k < 2; k++) {
- rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k],
- false,
- mimo_ctlchbw);
+ if (!EDCF_ENAB(wlc->pub)
+ || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
+ if (brcms_c_txflowcontrol_prio_isset(wlc, qi, ALLPRIO)
+ && (pktq_len(q) < wlc->pub->tunables->datahiwat / 2))
+ brcms_c_txflowcontrol(wlc, qi, OFF, ALLPRIO);
+ } else if (wlc->pub->_priofc) {
+ int prio;
+ for (prio = MAXPRIO; prio >= 0; prio--) {
+ if (brcms_c_txflowcontrol_prio_isset(wlc, qi, prio) &&
+ (pktq_plen(q, wlc_prio2prec_map[prio]) <
+ wlc->pub->tunables->datahiwat / 2))
+ brcms_c_txflowcontrol(wlc, qi, OFF, prio);
}
+ }
+ in_send_q = false;
+}
- if (!IS_OFDM(rts_rspec[0]) &&
- !((RSPEC2RATE(rts_rspec[0]) == BRCM_RATE_1M) ||
- (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
- rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE;
- mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
- }
+static void brcms_c_war16165(struct brcms_c_info *wlc, bool tx)
+{
+ if (tx) {
+ /* the post-increment is used in STAY_AWAKE macro */
+ if (wlc->txpend16165war++ == 0)
+ brcms_c_set_ps_ctrl(wlc);
+ } else {
+ wlc->txpend16165war--;
+ if (wlc->txpend16165war == 0)
+ brcms_c_set_ps_ctrl(wlc);
+ }
+}
- if (!IS_OFDM(rts_rspec[1]) &&
- !((RSPEC2RATE(rts_rspec[1]) == BRCM_RATE_1M) ||
- (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) {
- rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE;
- mch |= TXC_PREAMBLE_RTS_FB_SHORT;
- }
+void
+brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p,
+ bool commit, s8 txpktpend)
+{
+ u16 frameid = INVALIDFID;
+ struct d11txh *txh;
- /* RTS/CTS additions to MacTxControlLow */
- if (use_cts) {
- txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS);
- } else {
- txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS);
- txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME);
- }
+ txh = (struct d11txh *) (p->data);
- /* RTS PLCP header */
- rts_plcp = txh->RTSPhyHeader;
- if (use_cts)
- rts_phylen = DOT11_CTS_LEN + FCS_LEN;
- else
- rts_phylen = DOT11_RTS_LEN + FCS_LEN;
+ /* When a BC/MC frame is being committed to the BCMC fifo
+ * via DMA (NOT PIO), update ucode or BSS info as appropriate.
+ */
+ if (fifo == TX_BCMC_FIFO)
+ frameid = le16_to_cpu(txh->TxFrameID);
- brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);
+ if (BRCMS_WAR16165(wlc))
+ brcms_c_war16165(wlc, true);
- /* fallback rate version of RTS PLCP header */
- brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen,
- rts_plcp_fallback);
- memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback,
- sizeof(txh->RTSPLCPFallback));
- /* RTS frame fields... */
- rts = (struct ieee80211_rts *)&txh->rts_frame;
+ /*
+ * Bump up pending count for if not using rpc. If rpc is
+ * used, this will be handled in brcms_b_txfifo()
+ */
+ if (commit) {
+ TXPKTPENDINC(wlc, fifo, txpktpend);
+ BCMMSG(wlc->wiphy, "pktpend inc %d to %d\n",
+ txpktpend, TXPKTPENDGET(wlc, fifo));
+ }
- durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
- rspec[0], rts_preamble_type[0],
- preamble_type[0], phylen, false);
- rts->duration = cpu_to_le16(durid);
- /* fallback rate version of RTS DUR field */
- durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
- rts_rspec[1], rspec[1],
- rts_preamble_type[1],
- preamble_type[1], phylen, false);
- txh->RTSDurFallback = cpu_to_le16(durid);
+ /* Commit BCMC sequence number in the SHM frame ID location */
+ if (frameid != INVALIDFID)
+ BCMCFID(wlc, frameid);
- if (use_cts) {
- rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
- IEEE80211_STYPE_CTS);
+ if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0)
+ wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n");
+}
- memcpy(&rts->ra, &h->addr2, ETH_ALEN);
- } else {
- rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
- IEEE80211_STYPE_RTS);
+/*
+ * Compute PLCP, but only requires actual rate and length of pkt.
+ * Rate is given in the driver standard multiple of 500 kbps.
+ * le is set for 11 Mbps rate if necessary.
+ * Broken out for PRQ.
+ */
+
+static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500,
+ uint length, u8 *plcp)
+{
+ u16 usec = 0;
+ u8 le = 0;
- memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN);
+ switch (rate_500) {
+ case BRCM_RATE_1M:
+ usec = length << 3;
+ break;
+ case BRCM_RATE_2M:
+ usec = length << 2;
+ break;
+ case BRCM_RATE_5M5:
+ usec = (length << 4) / 11;
+ if ((length << 4) - (usec * 11) > 0)
+ usec++;
+ break;
+ case BRCM_RATE_11M:
+ usec = (length << 3) / 11;
+ if ((length << 3) - (usec * 11) > 0) {
+ usec++;
+ if ((usec * 11) - (length << 3) >= 8)
+ le = D11B_PLCP_SIGNAL_LE;
}
+ break;
- /* mainrate
- * low 8 bits: main frag rate/mcs,
- * high 8 bits: rts/cts rate/mcs
- */
- mainrates |= (IS_OFDM(rts_rspec[0]) ?
- D11A_PHY_HDR_GRATE(
- (struct ofdm_phy_hdr *) rts_plcp) :
- rts_plcp[0]) << 8;
- } else {
- memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
- memset((char *)&txh->rts_frame, 0,
- sizeof(struct ieee80211_rts));
- memset((char *)txh->RTSPLCPFallback, 0,
- sizeof(txh->RTSPLCPFallback));
- txh->RTSDurFallback = 0;
+ default:
+ wiphy_err(wlc->wiphy,
+ "brcms_c_cck_plcp_set: unsupported rate %d\n",
+ rate_500);
+ rate_500 = BRCM_RATE_1M;
+ usec = length << 3;
+ break;
}
+ /* PLCP signal byte */
+ plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */
+ /* PLCP service byte */
+ plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED);
+ /* PLCP length u16, little endian */
+ plcp[2] = usec & 0xff;
+ plcp[3] = (usec >> 8) & 0xff;
+ /* PLCP CRC16 */
+ plcp[4] = 0;
+ plcp[5] = 0;
+}
-#ifdef SUPPORT_40MHZ
- /* add null delimiter count */
- if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && IS_MCS(rspec))
- txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
- brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp)
+{
+ u8 mcs = (u8) (rspec & RSPEC_RATE_MASK);
+ plcp[0] = mcs;
+ if (RSPEC_IS40MHZ(rspec) || (mcs == 32))
+ plcp[0] |= MIMO_PLCP_40MHZ;
+ BRCMS_SET_MIMO_PLCP_LEN(plcp, length);
+ plcp[3] = RSPEC_MIMOPLCP3(rspec); /* rspec already holds this byte */
+ plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */
+ plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */
+ plcp[5] = 0;
+}
-#endif
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void
+brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp)
+{
+ u8 rate_signal;
+ u32 tmp = 0;
+ int rate = RSPEC2RATE(rspec);
/*
- * Now that RTS/RTS FB preamble types are updated, write
- * the final value
+ * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb
+ * transmitted first
*/
- txh->MacTxControlHigh = cpu_to_le16(mch);
+ rate_signal = rate_info[rate] & BRCMS_RATE_MASK;
+ memset(plcp, 0, D11_PHY_HDR_LEN);
+ D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal);
- /*
- * MainRates (both the rts and frag plcp rates have
- * been calculated now)
- */
- txh->MainRates = cpu_to_le16(mainrates);
+ tmp = (length & 0xfff) << 5;
+ plcp[2] |= (tmp >> 16) & 0xff;
+ plcp[1] |= (tmp >> 8) & 0xff;
+ plcp[0] |= tmp & 0xff;
- /* XtraFrameTypes */
- xfts = FRAMETYPE(rspec[1], wlc->mimoft);
- xfts |= (FRAMETYPE(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
- xfts |= (FRAMETYPE(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
- xfts |=
- CHSPEC_CHANNEL(BRCMS_BAND_PI_RADIO_CHANSPEC) << XFTS_CHANNEL_SHIFT;
- txh->XtraFrameTypes = cpu_to_le16(xfts);
+ return;
+}
- /* PhyTxControlWord */
- phyctl = FRAMETYPE(rspec[0], wlc->mimoft);
- if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) ||
- (preamble_type[0] == BRCMS_GF_PREAMBLE)) {
- if (RSPEC2RATE(rspec[0]) != BRCM_RATE_1M)
- phyctl |= PHY_TXC_SHORT_HDR;
- }
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec,
+ uint length, u8 *plcp)
+{
+ int rate = RSPEC2RATE(rspec);
- /* phytxant is properly bit shifted */
- phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
- txh->PhyTxControlWord = cpu_to_le16(phyctl);
+ brcms_c_cck_plcp_set(wlc, rate, length, plcp);
+}
- /* PhyTxControlWord_1 */
- if (BRCMS_PHY_11N_CAP(wlc->band)) {
- u16 phyctl1 = 0;
+void
+brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec,
+ uint length, u8 *plcp)
+{
+ if (IS_MCS(rspec))
+ brcms_c_compute_mimo_plcp(rspec, length, plcp);
+ else if (IS_OFDM(rspec))
+ brcms_c_compute_ofdm_plcp(rspec, length, plcp);
+ else
+ brcms_c_compute_cck_plcp(wlc, rspec, length, plcp);
+ return;
+}
- phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]);
- txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1);
- phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]);
- txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1);
+/* brcms_c_compute_rtscts_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame
+ * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK
+ * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK
+ *
+ * cts cts-to-self or rts/cts
+ * rts_rate rts or cts rate in unit of 500kbps
+ * rate next MPDU rate in unit of 500kbps
+ * frame_len next MPDU frame length in bytes
+ */
+u16
+brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only,
+ u32 rts_rate,
+ u32 frame_rate, u8 rts_preamble_type,
+ u8 frame_preamble_type, uint frame_len, bool ba)
+{
+ u16 dur, sifs;
- if (use_rts || use_cts) {
- phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]);
- txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1);
- phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]);
- txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1);
- }
+ sifs = SIFS(wlc->band);
- /*
- * For mcs frames, if mixedmode(overloaded with long preamble)
- * is going to be set, fill in non-zero MModeLen and/or
- * MModeFbrLen it will be unnecessary if they are separated
- */
- if (IS_MCS(rspec[0]) &&
- (preamble_type[0] == BRCMS_MM_PREAMBLE)) {
- u16 mmodelen =
- brcms_c_calc_lsig_len(wlc, rspec[0], phylen);
- txh->MModeLen = cpu_to_le16(mmodelen);
- }
+ if (!cts_only) {
+ /* RTS/CTS */
+ dur = 3 * sifs;
+ dur +=
+ (u16) brcms_c_calc_cts_time(wlc, rts_rate,
+ rts_preamble_type);
+ } else {
+ /* CTS-TO-SELF */
+ dur = 2 * sifs;
+ }
- if (IS_MCS(rspec[1]) &&
- (preamble_type[1] == BRCMS_MM_PREAMBLE)) {
- u16 mmodefbrlen =
- brcms_c_calc_lsig_len(wlc, rspec[1], phylen);
- txh->MModeFbrLen = cpu_to_le16(mmodefbrlen);
+ dur +=
+ (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type,
+ frame_len);
+ if (ba)
+ dur +=
+ (u16) brcms_c_calc_ba_time(wlc, frame_rate,
+ BRCMS_SHORT_PREAMBLE);
+ else
+ dur +=
+ (u16) brcms_c_calc_ack_time(wlc, frame_rate,
+ frame_preamble_type);
+ return dur;
+}
+
+u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec)
+{
+ u16 phyctl1 = 0;
+ u16 bw;
+
+ if (BRCMS_ISLCNPHY(wlc->band)) {
+ bw = PHY_TXC1_BW_20MHZ;
+ } else {
+ bw = RSPEC_GET_BW(rspec);
+ /* 10Mhz is not supported yet */
+ if (bw < PHY_TXC1_BW_20MHZ) {
+ wiphy_err(wlc->wiphy, "phytxctl1_calc: bw %d is "
+ "not supported yet, set to 20L\n", bw);
+ bw = PHY_TXC1_BW_20MHZ;
}
}
- ac = skb_get_queue_mapping(p);
- if (SCB_WME(scb) && qos && wlc->edcf_txop[ac]) {
- uint frag_dur, dur, dur_fallback;
+ if (IS_MCS(rspec)) {
+ uint mcs = rspec & RSPEC_RATE_MASK;
- /* WME: Update TXOP threshold */
- if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) {
- frag_dur =
- brcms_c_calc_frame_time(wlc, rspec[0],
- preamble_type[0], phylen);
+ /* bw, stf, coding-type is part of RSPEC_PHYTXBYTE2 returns */
+ phyctl1 = RSPEC_PHYTXBYTE2(rspec);
+ /* set the upper byte of phyctl1 */
+ phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8);
+ } else if (IS_CCK(rspec) && !BRCMS_ISLCNPHY(wlc->band)
+ && !BRCMS_ISSSLPNPHY(wlc->band)) {
+ /*
+ * In CCK mode LPPHY overloads OFDM Modulation bits with CCK
+ * Data Rate. Eventually MIMOPHY would also be converted to
+ * this format
+ */
+ /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
+ phyctl1 = (bw | (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
+ } else { /* legacy OFDM/CCK */
+ s16 phycfg;
+ /* get the phyctl byte from rate phycfg table */
+ phycfg = brcms_c_rate_legacy_phyctl(RSPEC2RATE(rspec));
+ if (phycfg == -1) {
+ wiphy_err(wlc->wiphy, "phytxctl1_calc: wrong "
+ "legacy OFDM/CCK rate\n");
+ phycfg = 0;
+ }
+ /* set the upper byte of phyctl1 */
+ phyctl1 =
+ (bw | (phycfg << 8) |
+ (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
+ }
+ return phyctl1;
+}
- if (rts) {
- /* 1 RTS or CTS-to-self frame */
- dur =
- brcms_c_calc_cts_time(wlc, rts_rspec[0],
- rts_preamble_type[0]);
- dur_fallback =
- brcms_c_calc_cts_time(wlc, rts_rspec[1],
- rts_preamble_type[1]);
- /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
- dur += le16_to_cpu(rts->duration);
- dur_fallback +=
- le16_to_cpu(txh->RTSDurFallback);
- } else if (use_rifs) {
- dur = frag_dur;
- dur_fallback = 0;
- } else {
- /* frame + SIFS + ACK */
- dur = frag_dur;
- dur +=
- brcms_c_compute_frame_dur(wlc, rspec[0],
- preamble_type[0], 0);
+u32
+brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec,
+ bool use_rspec, u16 mimo_ctlchbw)
+{
+ u32 rts_rspec = 0;
- dur_fallback =
- brcms_c_calc_frame_time(wlc, rspec[1],
- preamble_type[1],
- phylen);
- dur_fallback +=
- brcms_c_compute_frame_dur(wlc, rspec[1],
- preamble_type[1], 0);
- }
- /* NEED to set TxFesTimeNormal (hard) */
- txh->TxFesTimeNormal = cpu_to_le16((u16) dur);
- /*
- * NEED to set fallback rate version of
- * TxFesTimeNormal (hard)
- */
- txh->TxFesTimeFallback =
- cpu_to_le16((u16) dur_fallback);
+ if (use_rspec)
+ /* use frame rate as rts rate */
+ rts_rspec = rspec;
+ else if (wlc->band->gmode && wlc->protection->_g && !IS_CCK(rspec))
+ /* Use 11Mbps as the g protection RTS target rate and fallback.
+ * Use the BRCMS_BASIC_RATE() lookup to find the best basic rate
+ * under the target in case 11 Mbps is not Basic.
+ * 6 and 9 Mbps are not usually selected by rate selection, but
+ * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11
+ * is more robust.
+ */
+ rts_rspec = BRCMS_BASIC_RATE(wlc, BRCM_RATE_11M);
+ else
+ /* calculate RTS rate and fallback rate based on the frame rate
+ * RTS must be sent at a basic rate since it is a
+ * control frame, sec 9.6 of 802.11 spec
+ */
+ rts_rspec = BRCMS_BASIC_RATE(wlc, rspec);
- /*
- * update txop byte threshold (txop minus intraframe
- * overhead)
- */
- if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
- uint newfragthresh;
+ if (BRCMS_PHY_11N_CAP(wlc->band)) {
+ /* set rts txbw to correct side band */
+ rts_rspec &= ~RSPEC_BW_MASK;
- newfragthresh =
- brcms_c_calc_frame_len(wlc,
- rspec[0], preamble_type[0],
- (wlc->edcf_txop[ac] -
- (dur - frag_dur)));
- /* range bound the fragthreshold */
- if (newfragthresh < DOT11_MIN_FRAG_LEN)
- newfragthresh =
- DOT11_MIN_FRAG_LEN;
- else if (newfragthresh >
- wlc->usr_fragthresh)
- newfragthresh =
- wlc->usr_fragthresh;
- /* update the fragthresh and do txc update */
- if (wlc->fragthresh[queue] !=
- (u16) newfragthresh)
- wlc->fragthresh[queue] =
- (u16) newfragthresh;
- } else {
- wiphy_err(wlc->wiphy, "wl%d: %s txop invalid "
- "for rate %d\n",
- wlc->pub->unit, fifo_names[queue],
- RSPEC2RATE(rspec[0]));
- }
+ /*
+ * if rspec/rspec_fallback is 40MHz, then send RTS on both
+ * 20MHz channel (DUP), otherwise send RTS on control channel
+ */
+ if (RSPEC_IS40MHZ(rspec) && !IS_CCK(rts_rspec))
+ rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT);
+ else
+ rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
- if (dur > wlc->edcf_txop[ac])
- wiphy_err(wlc->wiphy, "wl%d: %s: %s txop "
- "exceeded phylen %d/%d dur %d/%d\n",
- wlc->pub->unit, __func__,
- fifo_names[queue],
- phylen, wlc->fragthresh[queue],
- dur, wlc->edcf_txop[ac]);
+ /* pick siso/cdd as default for ofdm */
+ if (IS_OFDM(rts_rspec)) {
+ rts_rspec &= ~RSPEC_STF_MASK;
+ rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
}
}
-
- return 0;
+ return rts_rspec;
}
void brcms_c_tbtt(struct brcms_c_info *wlc)
wlc->qvalid |= MCMD_DIRFRMQVAL;
}
-static void brcms_c_war16165(struct brcms_c_info *wlc, bool tx)
-{
- if (tx) {
- /* the post-increment is used in STAY_AWAKE macro */
- if (wlc->txpend16165war++ == 0)
- brcms_c_set_ps_ctrl(wlc);
- } else {
- wlc->txpend16165war--;
- if (wlc->txpend16165war == 0)
- brcms_c_set_ps_ctrl(wlc);
- }
-}
-
/* process an individual struct tx_status */
bool
brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs, u32 frm_tx2)
return dur;
}
-/* The opposite of brcms_c_calc_frame_time */
-static uint
-brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec,
- u8 preamble_type, uint dur)
-{
- uint nsyms, mac_len, Ndps, kNdps;
- uint rate = RSPEC2RATE(ratespec);
-
- BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, dur %d\n",
- wlc->pub->unit, ratespec, preamble_type, dur);
-
- if (IS_MCS(ratespec)) {
- uint mcs = ratespec & RSPEC_RATE_MASK;
- int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
- dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
- /* payload calculation matches that of regular ofdm */
- if (BAND_2G(wlc->band->bandtype))
- dur -= DOT11_OFDM_SIGNAL_EXTENSION;
- /* kNdbps = kbps * 4 */
- kNdps =
- MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
- RSPEC_ISSGI(ratespec)) * 4;
- nsyms = dur / APHY_SYMBOL_TIME;
- mac_len =
- ((nsyms * kNdps) -
- ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000;
- } else if (IS_OFDM(ratespec)) {
- dur -= APHY_PREAMBLE_TIME;
- dur -= APHY_SIGNAL_TIME;
- /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
- Ndps = rate * 2;
- nsyms = dur / APHY_SYMBOL_TIME;
- mac_len =
- ((nsyms * Ndps) -
- (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8;
- } else {
- if (preamble_type & BRCMS_SHORT_PREAMBLE)
- dur -= BPHY_PLCP_SHORT_TIME;
- else
- dur -= BPHY_PLCP_TIME;
- mac_len = dur * rate;
- /* divide out factor of 2 in rate (1/2 mbps) */
- mac_len = mac_len / 8 / 2;
- }
- return mac_len;
-}
-
-static uint
-brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec,
- u8 preamble_type)
-{
- BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, "
- "preamble_type %d\n", wlc->pub->unit, rspec, preamble_type);
- /*
- * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
- * is less than or equal to the rate of the immediately previous
- * frame in the FES
- */
- rspec = BRCMS_BASIC_RATE(wlc, rspec);
- /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */
- return brcms_c_calc_frame_time(wlc, rspec, preamble_type,
- (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN +
- FCS_LEN));
-}
-
-static uint
-brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec,
- u8 preamble_type)
-{
- uint dur = 0;
-
- BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d\n",
- wlc->pub->unit, rspec, preamble_type);
- /*
- * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that
- * is less than or equal to the rate of the immediately previous
- * frame in the FES
- */
- rspec = BRCMS_BASIC_RATE(wlc, rspec);
- /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */
- dur =
- brcms_c_calc_frame_time(wlc, rspec, preamble_type,
- (DOT11_ACK_LEN + FCS_LEN));
- return dur;
-}
-
-static uint
-brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec,
- u8 preamble_type)
-{
- BCMMSG(wlc->wiphy, "wl%d: ratespec 0x%x, preamble_type %d\n",
- wlc->pub->unit, rspec, preamble_type);
- return brcms_c_calc_ack_time(wlc, rspec, preamble_type);
-}
-
/* derive wlc->band->basic_rate[] table from 'rateset' */
void brcms_c_rate_lookup_init(struct brcms_c_info *wlc,
struct brcms_c_rateset *rateset)
return false;
}
-static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap)
-{
- uint i;
- struct brcms_band *band;
-
- for (i = 0; i < NBANDS(wlc); i++) {
- if (IS_SINGLEBAND_5G(wlc->deviceid))
- i = BAND_5G_INDEX;
- band = wlc->bandstate[i];
- if (band->bandtype == BRCM_BAND_5G) {
- if ((bwcap == BRCMS_N_BW_40ALL)
- || (bwcap == BRCMS_N_BW_20IN2G_40IN5G))
- band->mimo_cap_40 = true;
- else
- band->mimo_cap_40 = false;
- } else {
- if (bwcap == BRCMS_N_BW_40ALL)
- band->mimo_cap_40 = true;
- else
- band->mimo_cap_40 = false;
- }
- }
-}
-
void brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len)
{
const struct brcms_c_rateset *rs_dflt;
}
}
+void brcms_c_write_hw_bcntemplates(struct brcms_c_info *wlc, u16 bcn[], int len,
+ bool both)
+{
+ brcms_b_write_hw_bcntemplates(wlc->hw, bcn, len, both);
+}
+
/*
* Update a beacon for a particular BSS
* For MBSS, this updates the software template and sets "latest" to
wlc->stf->txstreams);
}
-static void brcms_c_bss_default_init(struct brcms_c_info *wlc)
-{
- u16 chanspec;
- struct brcms_band *band;
- struct brcms_bss_info *bi = wlc->default_bss;
-
- /* init default and target BSS with some sane initial values */
- memset((char *)(bi), 0, sizeof(struct brcms_bss_info));
- bi->beacon_period = BEACON_INTERVAL_DEFAULT;
- bi->dtim_period = DTIM_INTERVAL_DEFAULT;
-
- /* fill the default channel as the first valid channel
- * starting from the 2G channels
- */
- chanspec = CH20MHZ_CHSPEC(1);
- wlc->home_chanspec = bi->chanspec = chanspec;
-
- /* find the band of our default channel */
- band = wlc->band;
- if (NBANDS(wlc) > 1 && band->bandunit != CHSPEC_BANDUNIT(chanspec))
- band = wlc->bandstate[OTHERBANDUNIT(wlc)];
-
- /* init bss rates to the band specific default rate set */
- brcms_c_rateset_default(&bi->rateset, NULL, band->phytype,
- band->bandtype, false, BRCMS_RATE_MASK_FULL,
- (bool) N_ENAB(wlc->pub), CHSPEC_WLC_BW(chanspec),
- wlc->stf->txstreams);
-
- if (N_ENAB(wlc->pub))
- bi->flags |= BRCMS_BSS_HT;
-}
-
-static u32
-mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band,
- u32 int_val)
-{
- u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT;
- u8 rate = int_val & NRATE_RATE_MASK;
- u32 rspec;
- bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE);
- bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT);
- bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY)
- == NRATE_OVERRIDE_MCS_ONLY);
- int bcmerror = 0;
-
- if (!ismcs)
- return (u32) rate;
-
- /* validate the combination of rate/mcs/stf is allowed */
- if (N_ENAB(wlc->pub) && ismcs) {
- /* mcs only allowed when nmode */
- if (stf > PHY_TXC1_MODE_SDM) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid stf\n",
- BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
-
- /* mcs 32 is a special case, DUP mode 40 only */
- if (rate == 32) {
- if (!CHSPEC_IS40(wlc->home_chanspec) ||
- ((stf != PHY_TXC1_MODE_SISO)
- && (stf != PHY_TXC1_MODE_CDD))) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid mcs "
- "32\n", BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
- /* mcs > 7 must use stf SDM */
- } else if (rate > HIGHEST_SINGLE_STREAM_MCS) {
- /* mcs > 7 must use stf SDM */
- if (stf != PHY_TXC1_MODE_SDM) {
- BCMMSG(wlc->wiphy, "wl%d: enabling "
- "SDM mode for mcs %d\n",
- BRCMS_UNIT(wlc), rate);
- stf = PHY_TXC1_MODE_SDM;
- }
- } else {
- /*
- * MCS 0-7 may use SISO, CDD, and for
- * phy_rev >= 3 STBC
- */
- if ((stf > PHY_TXC1_MODE_STBC) ||
- (!BRCMS_STBC_CAP_PHY(wlc)
- && (stf == PHY_TXC1_MODE_STBC))) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid STBC"
- "\n", BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
- }
- } else if (IS_OFDM(rate)) {
- if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid OFDM\n",
- BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
- } else if (IS_CCK(rate)) {
- if ((cur_band->bandtype != BRCM_BAND_2G)
- || (stf != PHY_TXC1_MODE_SISO)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: Invalid CCK\n",
- BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
- } else {
- wiphy_err(wlc->wiphy, "wl%d: %s: Unknown rate type\n",
- BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
- /* make sure multiple antennae are available for non-siso rates */
- if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) {
- wiphy_err(wlc->wiphy, "wl%d: %s: SISO antenna but !SISO "
- "request\n", BRCMS_UNIT(wlc), __func__);
- bcmerror = -EINVAL;
- goto done;
- }
-
- rspec = rate;
- if (ismcs) {
- rspec |= RSPEC_MIMORATE;
- /* For STBC populate the STC field of the ratespec */
- if (stf == PHY_TXC1_MODE_STBC) {
- u8 stc;
- stc = 1; /* Nss for single stream is always 1 */
- rspec |= (stc << RSPEC_STC_SHIFT);
- }
- }
-
- rspec |= (stf << RSPEC_STF_SHIFT);
-
- if (override_mcs_only)
- rspec |= RSPEC_OVERRIDE_MCS_ONLY;
-
- if (issgi)
- rspec |= RSPEC_SHORT_GI;
-
- if ((rate != 0)
- && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true))
- return rate;
-
- return rspec;
-done:
- return rate;
-}
-
-/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */
-static int
-brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM,
- bool writeToShm)
-{
- int idle_busy_ratio_x_16 = 0;
- uint offset =
- isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM :
- M_TX_IDLE_BUSY_RATIO_X_16_CCK;
- if (duty_cycle > 100 || duty_cycle < 0) {
- wiphy_err(wlc->wiphy, "wl%d: duty cycle value off limit\n",
- wlc->pub->unit);
- return -EINVAL;
- }
- if (duty_cycle)
- idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle;
- /* Only write to shared memory when wl is up */
- if (writeToShm)
- brcms_c_write_shm(wlc, offset, (u16) idle_busy_ratio_x_16);
-
- if (isOFDM)
- wlc->tx_duty_cycle_ofdm = (u16) duty_cycle;
- else
- wlc->tx_duty_cycle_cck = (u16) duty_cycle;
-
- return 0;
-}
-
/* Read a single u16 from shared memory.
* SHM 'offset' needs to be an even address
*/
brcms_b_write_template_ram(wlc->hw, offset, len, buf);
}
-void brcms_c_write_hw_bcntemplates(struct brcms_c_info *wlc, u16 bcn[], int len,
- bool both)
-{
- brcms_b_write_hw_bcntemplates(wlc->hw, bcn, len, both);
-}
-
void
brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset,
const u8 *addr)
}
}
-static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc)
-{
- struct brcms_txq_info *qi;
-
- for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
- if (qi->stopped) {
- brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
- qi->stopped = 0;
- }
- }
-}
-
-static void
-brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc,
- struct brcms_txq_info *qi, bool on, int prio)
-{
- /* wlcif_list is never filled so this function is not functional yet */
-}
-
-static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc)
-{
- struct brcms_txq_info *qi, *p;
-
- qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC);
- if (qi != NULL) {
- /*
- * Have enough room for control packets along with HI watermark
- * Also, add room to txq for total psq packets if all the SCBs
- * leave PS mode. The watermark for flowcontrol to OS packets
- * will remain the same
- */
- brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT,
- (2 * wlc->pub->tunables->datahiwat) + PKTQ_LEN_DEFAULT
- + wlc->pub->psq_pkts_total);
-
- /* add this queue to the the global list */
- p = wlc->tx_queues;
- if (p == NULL) {
- wlc->tx_queues = qi;
- } else {
- while (p->next != NULL)
- p = p->next;
- p->next = qi;
- }
- }
- return qi;
-}
-
-static void brcms_c_txq_free(struct brcms_c_info *wlc,
- struct brcms_txq_info *qi)
-{
- struct brcms_txq_info *p;
-
- if (qi == NULL)
- return;
-
- /* remove the queue from the linked list */
- p = wlc->tx_queues;
- if (p == qi)
- wlc->tx_queues = p->next;
- else {
- while (p != NULL && p->next != qi)
- p = p->next;
- if (p != NULL)
- p->next = p->next->next;
- }
-
- kfree(qi);
-}
-
/*
* Flag 'scan in progress' to withhold dynamic phy calibration
*/