X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fbrcm80211%2Fbrcmsmac%2Fmain.c;h=510e9bb5228795c2d453d9eb641cc7084aa63b2b;hb=8f86f36284547fdd873769a5f190549f3ab69cca;hp=665bf8011cb6ae1295f0721f476f59edd4a1a8ef;hpb=be69c4ef462a476523f89c74e7db29f6ad207a1a;p=karo-tx-linux.git diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 665bf8011cb6..510e9bb52287 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -325,6 +325,12 @@ static u16 frametype(u32 rspec, u8 mimoframe) */ #define SSID_FMT_BUF_LEN ((4 * IEEE80211_MAX_SSID_LEN) + 1) +/* brcmu_format_flags() bit description structure */ +struct brcms_c_bit_desc { + u32 bit; + const char *name; +}; + /* * The following table lists the buffer memory allocated to xmt fifos in HW. * the size is in units of 256bytes(one block), total size is HW dependent @@ -343,53 +349,6 @@ struct d11init { __le32 value; }; -/* currently the best mechanism for determining SIFS is the band in use */ -static u16 get_sifs(struct brcms_band *band) -{ - return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : - BPHY_SIFS_TIME; -} - - -/* - * Detect Card removed. - * Even checking an sbconfig register read will not false trigger when the core - * is in reset it breaks CF address mechanism. Accessing gphy phyversion will - * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible - * reg with fixed 0/1 pattern (some platforms return all 0). - * If clocks are present, call the sb routine which will figure out if the - * device is removed. - */ -static bool brcms_deviceremoved(struct brcms_c_info *wlc) -{ - if (!wlc->hw->clk) - return ai_deviceremoved(wlc->hw->sih); - return (R_REG(&wlc->hw->regs->maccontrol) & - (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; -} - -/* sum the individual fifo tx pending packet counts */ -static s16 brcms_txpktpendtot(struct brcms_c_info *wlc) -{ - return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] + - wlc->core->txpktpend[2] + wlc->core->txpktpend[3]; -} - -static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) -{ - return wlc->pub->_nbands > 1 && !wlc->bandlocked; -} - -static int brcms_chspec_bw(u16 chanspec) -{ - if (CHSPEC_IS40(chanspec)) - return BRCMS_40_MHZ; - if (CHSPEC_IS20(chanspec)) - return BRCMS_20_MHZ; - - return BRCMS_10_MHZ; -} - struct edcf_acparam { u8 ACI; u8 ECW; @@ -465,6 +424,66 @@ static const char fifo_names[6][0]; static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); #endif +/* currently the best mechanism for determining SIFS is the band in use */ +static u16 get_sifs(struct brcms_band *band) +{ + return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : + BPHY_SIFS_TIME; +} + +/* + * Detect Card removed. + * Even checking an sbconfig register read will not false trigger when the core + * is in reset it breaks CF address mechanism. Accessing gphy phyversion will + * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible + * reg with fixed 0/1 pattern (some platforms return all 0). + * If clocks are present, call the sb routine which will figure out if the + * device is removed. + */ +static bool brcms_deviceremoved(struct brcms_c_info *wlc) +{ + if (!wlc->hw->clk) + return ai_deviceremoved(wlc->hw->sih); + return (R_REG(&wlc->hw->regs->maccontrol) & + (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; +} + +/* sum the individual fifo tx pending packet counts */ +static s16 brcms_txpktpendtot(struct brcms_c_info *wlc) +{ + return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] + + wlc->core->txpktpend[2] + wlc->core->txpktpend[3]; +} + +static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) +{ + return wlc->pub->_nbands > 1 && !wlc->bandlocked; +} + +static int brcms_chspec_bw(u16 chanspec) +{ + if (CHSPEC_IS40(chanspec)) + return BRCMS_40_MHZ; + if (CHSPEC_IS20(chanspec)) + return BRCMS_20_MHZ; + + return BRCMS_10_MHZ; +} + +/* + * return true if Minimum Power Consumption should + * be entered, false otherwise + */ +static bool brcms_c_is_non_delay_mpc(struct brcms_c_info *wlc) +{ + return false; +} + +static bool brcms_c_ismpc(struct brcms_c_info *wlc) +{ + return (wlc->mpc_delay_off == 0) && (brcms_c_is_non_delay_mpc(wlc)); +} + static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) { if (cfg == NULL) @@ -646,6 +665,79 @@ static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, } } +/* + * calculate frame duration of a given rate and length, return + * time in usec unit + */ +uint +brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, + u8 preamble_type, uint mac_len) +{ + uint nsyms, dur = 0, Ndps, kNdps; + uint rate = rspec2rate(ratespec); + + if (rate == 0) { + wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n", + wlc->pub->unit); + rate = BRCM_RATE_1M; + } + + BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n", + wlc->pub->unit, ratespec, preamble_type, mac_len); + + if (is_mcs_rate(ratespec)) { + uint mcs = ratespec & RSPEC_RATE_MASK; + int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); + + dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); + if (preamble_type == BRCMS_MM_PREAMBLE) + dur += PREN_MM_EXT; + /* 1000Ndbps = kbps * 4 */ + kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), + rspec_issgi(ratespec)) * 4; + + if (rspec_stc(ratespec) == 0) + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, kNdps); + else + /* STBC needs to have even number of symbols */ + nsyms = + 2 * + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, 2 * kNdps); + + dur += APHY_SYMBOL_TIME * nsyms; + if (wlc->band->bandtype == BRCM_BAND_2G) + dur += DOT11_OFDM_SIGNAL_EXTENSION; + } else if (is_ofdm_rate(rate)) { + dur = APHY_PREAMBLE_TIME; + dur += APHY_SIGNAL_TIME; + /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ + Ndps = rate * 2; + /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), + Ndps); + dur += APHY_SYMBOL_TIME * nsyms; + if (wlc->band->bandtype == BRCM_BAND_2G) + dur += DOT11_OFDM_SIGNAL_EXTENSION; + } else { + /* + * calc # bits * 2 so factor of 2 in rate (1/2 mbps) + * will divide out + */ + mac_len = mac_len * 8 * 2; + /* calc ceiling of bits/rate = microseconds of air time */ + dur = (mac_len + rate - 1) / rate; + if (preamble_type & BRCMS_SHORT_PREAMBLE) + dur += BPHY_PLCP_SHORT_TIME; + else + dur += BPHY_PLCP_TIME; + } + return dur; +} + static void brcms_c_write_inits(struct brcms_hardware *wlc_hw, const struct d11init *inits) { @@ -740,6 +832,26 @@ static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) } } +/* low-level band switch utility routine */ +static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) +{ + BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, + bandunit); + + wlc_hw->band = wlc_hw->bandstate[bandunit]; + + /* + * BMAC_NOTE: + * until we eliminate need for wlc->band refs in low level code + */ + wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; + + /* set gmode core flag */ + if (wlc_hw->sbclk && !wlc_hw->noreset) + ai_core_cflags(wlc_hw->sih, SICF_GMODE, + ((bandunit == 0) ? SICF_GMODE : 0)); +} + /* switch to new band but leave it inactive */ static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) { @@ -763,107 +875,45 @@ static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) return macintmask; } -/* Process received frames */ -/* - * Return true if more frames need to be processed. false otherwise. - * Param 'bound' indicates max. # frames to process before break out. - */ +/* process an individual struct tx_status */ static bool -brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) +brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) { struct sk_buff *p; - struct sk_buff *head = NULL; - struct sk_buff *tail = NULL; - uint n = 0; - uint bound_limit = bound ? RXBND : -1; - - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - /* gather received frames */ - while ((p = dma_rx(wlc_hw->di[fifo]))) { + uint queue; + struct d11txh *txh; + struct scb *scb = NULL; + bool free_pdu; + int tx_rts, tx_frame_count, tx_rts_count; + uint totlen, supr_status; + bool lastframe; + struct ieee80211_hdr *h; + u16 mcl; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *txrate; + int i; - if (!tail) - head = tail = p; - else { - tail->prev = p; - tail = p; - } + /* discard intermediate indications for ucode with one legitimate case: + * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, + * but the subsequent tx of DATA failed. so it will start rts/cts + * from the beginning (resetting the rts transmission count) + */ + if (!(txs->status & TX_STATUS_AMPDU) + && (txs->status & TX_STATUS_INTERMEDIATE)) { + wiphy_err(wlc->wiphy, "%s: INTERMEDIATE but not AMPDU\n", + __func__); + return false; + } - /* !give others some time to run! */ - if (++n >= bound_limit) - break; + queue = txs->frameid & TXFID_QUEUE_MASK; + if (queue >= NFIFO) { + p = NULL; + goto fatal; } - /* post more rbufs */ - dma_rxfill(wlc_hw->di[fifo]); - - /* process each frame */ - while ((p = head) != NULL) { - struct d11rxhdr_le *rxh_le; - struct d11rxhdr *rxh; - head = head->prev; - p->prev = NULL; - - rxh_le = (struct d11rxhdr_le *)p->data; - rxh = (struct d11rxhdr *)p->data; - - /* fixup rx header endianness */ - rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); - rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); - rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); - rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); - rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); - rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); - rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); - rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); - rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); - rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); - rxh->RxChan = le16_to_cpu(rxh_le->RxChan); - - brcms_c_recv(wlc_hw->wlc, p); - } - - return n >= bound_limit; -} - -/* process an individual struct tx_status */ -static bool -brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) -{ - struct sk_buff *p; - uint queue; - struct d11txh *txh; - struct scb *scb = NULL; - bool free_pdu; - int tx_rts, tx_frame_count, tx_rts_count; - uint totlen, supr_status; - bool lastframe; - struct ieee80211_hdr *h; - u16 mcl; - struct ieee80211_tx_info *tx_info; - struct ieee80211_tx_rate *txrate; - int i; - - /* discard intermediate indications for ucode with one legitimate case: - * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, - * but the subsequent tx of DATA failed. so it will start rts/cts - * from the beginning (resetting the rts transmission count) - */ - if (!(txs->status & TX_STATUS_AMPDU) - && (txs->status & TX_STATUS_INTERMEDIATE)) { - wiphy_err(wlc->wiphy, "%s: INTERMEDIATE but not AMPDU\n", - __func__); - return false; - } - - queue = txs->frameid & TXFID_QUEUE_MASK; - if (queue >= NFIFO) { - p = NULL; - goto fatal; - } - - p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); - if (p == NULL) - goto fatal; + p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); + if (p == NULL) + goto fatal; txh = (struct d11txh *) (p->data); mcl = le16_to_cpu(txh->MacTxControlLow); @@ -1054,97 +1104,14 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) return morepending; } -/* 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. - */ -bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) +static void brcms_c_tbtt(struct brcms_c_info *wlc) { - u32 macintstatus; - struct brcms_hardware *wlc_hw = wlc->hw; - struct d11regs __iomem *regs = wlc_hw->regs; - struct wiphy *wiphy = wlc->wiphy; - - if (brcms_deviceremoved(wlc)) { - wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - brcms_down(wlc->wl); - return false; - } - - /* grab and clear the saved software intstatus bits */ - macintstatus = wlc->macintstatus; - wlc->macintstatus = 0; - - BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n", - wlc_hw->unit, macintstatus); - - WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ - - /* tx status */ - if (macintstatus & MI_TFS) { - bool fatal; - if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) - wlc->macintstatus |= MI_TFS; - if (fatal) { - wiphy_err(wiphy, "MI_TFS: fatal\n"); - goto fatal; - } - } - - if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) - brcms_c_tbtt(wlc); - - /* ATIM window end */ - if (macintstatus & MI_ATIMWINEND) { - BCMMSG(wlc->wiphy, "end of ATIM window\n"); - OR_REG(®s->maccommand, wlc->qvalid); - wlc->qvalid = 0; - } - - /* - * received data or control frame, MI_DMAINT is - * indication of RX_FIFO interrupt - */ - if (macintstatus & MI_DMAINT) - if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) - wlc->macintstatus |= MI_DMAINT; - - /* noise sample collected */ - if (macintstatus & MI_BG_NOISE) - wlc_phy_noise_sample_intr(wlc_hw->band->pi); - - if (macintstatus & MI_GP0) { - wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d " - "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); - - printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", - __func__, wlc_hw->sih->chip, - wlc_hw->sih->chiprev); - /* big hammer */ - brcms_init(wlc->wl); - } - - /* gptimer timeout */ - if (macintstatus & MI_TO) - W_REG(®s->gptimer, 0); - - if (macintstatus & MI_RFDISABLE) { - BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the" - " RF Disable Input\n", wlc_hw->unit); - brcms_rfkill_set_hw_state(wlc->wl); - } - - /* send any enq'd tx packets. Just makes sure to jump start tx */ - if (!pktq_empty(&wlc->pkt_queue->q)) - brcms_c_send_q(wlc); - - /* it isn't done and needs to be resched if macintstatus is non-zero */ - return wlc->macintstatus != 0; - - fatal: - brcms_init(wlc->wl); - return wlc->macintstatus != 0; + if (!wlc->bsscfg->BSS) + /* + * DirFrmQ is now valid...defer setting until end + * of ATIM window + */ + wlc->qvalid |= MCMD_DIRFRMQVAL; } /* set initial host flags value */ @@ -1922,26 +1889,6 @@ static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit, WARN_ON((R_REG(&wlc_hw->regs->maccontrol) & MCTL_EN_MAC) != 0); } -/* low-level band switch utility routine */ -void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) -{ - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, - bandunit); - - wlc_hw->band = wlc_hw->bandstate[bandunit]; - - /* - * BMAC_NOTE: - * until we eliminate need for wlc->band refs in low level code - */ - wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; - - /* set gmode core flag */ - if (wlc_hw->sbclk && !wlc_hw->noreset) - ai_core_cflags(wlc_hw->sih, SICF_GMODE, - ((bandunit == 0) ? SICF_GMODE : 0)); -} - static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw) { @@ -2405,6 +2352,13 @@ void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); } +static void brcms_c_fatal_error(struct brcms_c_info *wlc) +{ + wiphy_err(wlc->wiphy, "wl%d: fatal error, reinitializing\n", + wlc->pub->unit); + brcms_init(wlc->wl); +} + static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) { bool fatal = false; @@ -2962,7 +2916,7 @@ void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) } } -void brcms_c_coredisable(struct brcms_hardware *wlc_hw) +static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) { bool dev_gone; @@ -3106,6 +3060,16 @@ brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, } } +/* Copy a buffer to shared memory. + * SHM 'offset' needs to be an even address and + * Buffer length 'len' must be an even number of bytes + */ +static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, + const void *buf, int len) +{ + brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); +} + static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, u16 SRL, u16 LRL) { @@ -3159,7 +3123,7 @@ static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) * conditions under which the PM bit should be set in outgoing frames * and STAY_AWAKE is meaningful */ -bool brcms_c_ps_allowed(struct brcms_c_info *wlc) +static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) { struct brcms_bss_cfg *cfg = wlc->bsscfg; @@ -3185,6 +3149,58 @@ bool brcms_c_ps_allowed(struct brcms_c_info *wlc) return true; } +static void brcms_c_statsupd(struct brcms_c_info *wlc) +{ + int i; + struct macstat macstats; +#ifdef BCMDBG + u16 delta; + u16 rxf0ovfl; + u16 txfunfl[NFIFO]; +#endif /* BCMDBG */ + + /* if driver down, make no sense to update stats */ + if (!wlc->pub->up) + return; + +#ifdef BCMDBG + /* save last rx fifo 0 overflow count */ + rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl; + + /* save last tx fifo underflow count */ + for (i = 0; i < NFIFO; i++) + txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i]; +#endif /* BCMDBG */ + + /* Read mac stats from contiguous shared memory */ + brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats, + sizeof(struct macstat), OBJADDR_SHM_SEL); + +#ifdef BCMDBG + /* check for rx fifo 0 overflow */ + delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl); + if (delta) + wiphy_err(wlc->wiphy, "wl%d: %u rx fifo 0 overflows!\n", + wlc->pub->unit, delta); + + /* check for tx fifo underflows */ + for (i = 0; i < NFIFO; i++) { + delta = + (u16) (wlc->core->macstat_snapshot->txfunfl[i] - + txfunfl[i]); + if (delta) + wiphy_err(wlc->wiphy, "wl%d: %u tx fifo %d underflows!" + "\n", wlc->pub->unit, delta, i); + } +#endif /* BCMDBG */ + + /* merge counters from dma module */ + for (i = 0; i < NFIFO; i++) { + if (wlc->hw->di[i]) + dma_counterreset(wlc->hw->di[i]); + } +} + static void brcms_b_reset(struct brcms_hardware *wlc_hw) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); @@ -3211,13 +3227,6 @@ void brcms_c_reset(struct brcms_c_info *wlc) brcms_b_reset(wlc->hw); } -void brcms_c_fatal_error(struct brcms_c_info *wlc) -{ - wiphy_err(wlc->wiphy, "wl%d: fatal error, reinitializing\n", - wlc->pub->unit); - brcms_init(wlc->wl); -} - /* Return the channel the driver should initialize during brcms_c_init. * the channel may have to be changed from the currently configured channel * if other configurations are in conflict (bandlocked, 11n mode disabled, @@ -3494,7 +3503,111 @@ static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc, chanspec); brcms_c_stf_ss_update(wlc, wlc->band); - +} + +static void +brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) +{ + brcms_c_rateset_default(rs, NULL, wlc->band->phytype, + wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, + (bool) (wlc->pub->_n_enab & SUPPORT_11N), + brcms_chspec_bw(wlc->default_bss->chanspec), + wlc->stf->txstreams); +} + +/* derive wlc->band->basic_rate[] table from 'rateset' */ +static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, + struct brcms_c_rateset *rateset) +{ + u8 rate; + u8 mandatory; + u8 cck_basic = 0; + u8 ofdm_basic = 0; + u8 *br = wlc->band->basic_rate; + uint i; + + /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ + memset(br, 0, BRCM_MAXRATE + 1); + + /* For each basic rate in the rates list, make an entry in the + * best basic lookup. + */ + for (i = 0; i < rateset->count; i++) { + /* only make an entry for a basic rate */ + if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) + continue; + + /* mask off basic bit */ + rate = (rateset->rates[i] & BRCMS_RATE_MASK); + + if (rate > BRCM_MAXRATE) { + wiphy_err(wlc->wiphy, "brcms_c_rate_lookup_init: " + "invalid rate 0x%X in rate set\n", + rateset->rates[i]); + continue; + } + + br[rate] = rate; + } + + /* The rate lookup table now has non-zero entries for each + * basic rate, equal to the basic rate: br[basicN] = basicN + * + * To look up the best basic rate corresponding to any + * particular rate, code can use the basic_rate table + * like this + * + * basic_rate = wlc->band->basic_rate[tx_rate] + * + * Make sure there is a best basic rate entry for + * every rate by walking up the table from low rates + * to high, filling in holes in the lookup table + */ + + for (i = 0; i < wlc->band->hw_rateset.count; i++) { + rate = wlc->band->hw_rateset.rates[i]; + + if (br[rate] != 0) { + /* This rate is a basic rate. + * Keep track of the best basic rate so far by + * modulation type. + */ + if (is_ofdm_rate(rate)) + ofdm_basic = rate; + else + cck_basic = rate; + + continue; + } + + /* This rate is not a basic rate so figure out the + * best basic rate less than this rate and fill in + * the hole in the table + */ + + br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; + + if (br[rate] != 0) + continue; + + if (is_ofdm_rate(rate)) { + /* + * In 11g and 11a, the OFDM mandatory rates + * are 6, 12, and 24 Mbps + */ + if (rate >= BRCM_RATE_24M) + mandatory = BRCM_RATE_24M; + else if (rate >= BRCM_RATE_12M) + mandatory = BRCM_RATE_12M; + else + mandatory = BRCM_RATE_6M; + } else { + /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ + mandatory = rate; + } + + br[rate] = mandatory; + } } static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, @@ -3543,6 +3656,44 @@ static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, brcms_c_set_phy_chanspec(wlc, chanspec); } +static void brcms_c_mac_bcn_promisc(struct brcms_c_info *wlc) +{ + if (wlc->bcnmisc_monitor) + brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC); + else + brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, 0); +} + +void brcms_c_mac_bcn_promisc_change(struct brcms_c_info *wlc, bool promisc) +{ + wlc->bcnmisc_monitor = promisc; + brcms_c_mac_bcn_promisc(wlc); +} + +/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */ +static void brcms_c_mac_promisc(struct brcms_c_info *wlc) +{ + u32 promisc_bits = 0; + + /* + * promiscuous mode just sets MCTL_PROMISC + * Note: APs get all BSS traffic without the need to set + * the MCTL_PROMISC bit since all BSS data traffic is + * directed at the AP + */ + if (wlc->pub->promisc) + promisc_bits |= MCTL_PROMISC; + + /* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL + * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is + * handled in brcms_c_mac_bcn_promisc() + */ + if (wlc->monitor) + promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL; + + brcms_b_mctrl(wlc->hw, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits); +} + /* * ucode, hwmac update * Channel dependent updates for ucode and hw @@ -3576,6 +3727,88 @@ static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc) brcms_c_mac_promisc(wlc); } +static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, + u8 basic_rate) +{ + u8 phy_rate, index; + u8 basic_phy_rate, basic_index; + u16 dir_table, basic_table; + u16 basic_ptr; + + /* Shared memory address for the table we are reading */ + dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; + + /* Shared memory address for the table we are writing */ + basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; + + /* + * for a given rate, the LS-nibble of the PLCP SIGNAL field is + * the index into the rate table. + */ + phy_rate = rate_info[rate] & BRCMS_RATE_MASK; + basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; + index = phy_rate & 0xf; + basic_index = basic_phy_rate & 0xf; + + /* Find the SHM pointer to the ACK rate entry by looking in the + * Direct-map Table + */ + basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); + + /* Update the SHM BSS-basic-rate-set mapping table with the pointer + * to the correct basic rate for the given incoming rate + */ + brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); +} + +static const struct brcms_c_rateset * +brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) +{ + const struct brcms_c_rateset *rs_dflt; + + if (BRCMS_PHY_11N_CAP(wlc->band)) { + if (wlc->band->bandtype == BRCM_BAND_5G) + rs_dflt = &ofdm_mimo_rates; + else + rs_dflt = &cck_ofdm_mimo_rates; + } else if (wlc->band->gmode) + rs_dflt = &cck_ofdm_rates; + else + rs_dflt = &cck_rates; + + return rs_dflt; +} + +static void brcms_c_set_ratetable(struct brcms_c_info *wlc) +{ + const struct brcms_c_rateset *rs_dflt; + struct brcms_c_rateset rs; + u8 rate, basic_rate; + uint i; + + rs_dflt = brcms_c_rateset_get_hwrs(wlc); + + brcms_c_rateset_copy(rs_dflt, &rs); + brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); + + /* walk the phy rate table and update SHM basic rate lookup table */ + for (i = 0; i < rs.count; i++) { + rate = rs.rates[i] & BRCMS_RATE_MASK; + + /* for a given rate brcms_basic_rate returns the rate at + * which a response ACK/CTS should be sent. + */ + basic_rate = brcms_basic_rate(wlc, rate); + if (basic_rate == 0) + /* This should only happen if we are using a + * restricted rateset. + */ + basic_rate = rs.rates[0] & BRCMS_RATE_MASK; + + brcms_c_write_rate_shm(wlc, rate, basic_rate); + } +} + /* band-specific init */ static void brcms_c_bsinit(struct brcms_c_info *wlc) { @@ -3655,183 +3888,35 @@ static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc) } } -void brcms_c_init(struct brcms_c_info *wlc) +/* push sw hps and wake state through hardware */ +static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) { - struct d11regs __iomem *regs; - u16 chanspec; - bool mute = false; + u32 v1, v2; + bool hps; + bool awake_before; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + hps = brcms_c_ps_allowed(wlc); - regs = wlc->regs; + BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps); - /* - * 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); + v1 = R_REG(&wlc->regs->maccontrol); + v2 = MCTL_WAKE; + if (hps) + v2 |= MCTL_HPS; - brcms_b_init(wlc->hw, chanspec, mute); + brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); - /* update beacon listen interval */ - brcms_c_bcn_li_upd(wlc); + awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); - /* write ethernet address to core */ - brcms_c_set_mac(wlc->bsscfg); - brcms_c_set_bssid(wlc->bsscfg); - - /* Update tsf_cfprep if associated and up */ - if (wlc->pub->associated && wlc->bsscfg->up) { - u32 bi; - - /* get beacon period and convert to uS */ - bi = wlc->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); - } - - brcms_c_bandinit_ordered(wlc, chanspec); - - /* init probe response timeout */ - brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); - - /* init max burst txop (framebursting) */ - brcms_b_write_shm(wlc->hw, 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) */ - 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_b_read_shm(wlc->hw, M_BOM_REV_MAJOR) << NBITS(u16); - wlc->ucode_rev |= brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); - } - - /* ..now really unleash hell (allow the MAC out of suspend) */ - brcms_c_enable_mac(wlc); - - /* clear tx flow control */ - brcms_c_txflowcontrol_reset(wlc); - - /* enable the RF Disable Delay timer */ - W_REG(&wlc->regs->rfdisabledly, RFDISABLE_DEFAULT); - - /* initialize mpc delay */ - wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; - - /* - * Initialize WME parameters; if they haven't been set by some other - * mechanism (IOVar, etc) then read them from the hardware. - */ - if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { - /* Uninitialized; read from HW */ - int ac; - - for (ac = 0; ac < AC_COUNT; ac++) - wlc->wme_retries[ac] = - brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); - } -} - -void brcms_c_mac_bcn_promisc_change(struct brcms_c_info *wlc, bool promisc) -{ - wlc->bcnmisc_monitor = promisc; - brcms_c_mac_bcn_promisc(wlc); -} - -void brcms_c_mac_bcn_promisc(struct brcms_c_info *wlc) -{ - if (wlc->bcnmisc_monitor) - brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC); - else - brcms_b_mctrl(wlc->hw, MCTL_BCNS_PROMISC, 0); -} - -/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */ -void brcms_c_mac_promisc(struct brcms_c_info *wlc) -{ - u32 promisc_bits = 0; - - /* - * promiscuous mode just sets MCTL_PROMISC - * Note: APs get all BSS traffic without the need to set - * the MCTL_PROMISC bit since all BSS data traffic is - * directed at the AP - */ - if (wlc->pub->promisc) - promisc_bits |= MCTL_PROMISC; - - /* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL - * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is - * handled in brcms_c_mac_bcn_promisc() - */ - if (wlc->monitor) - promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL; - - brcms_b_mctrl(wlc->hw, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits); -} - -/* push sw hps and wake state through hardware */ -void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) -{ - u32 v1, v2; - bool hps; - bool awake_before; - - hps = brcms_c_ps_allowed(wlc); - - BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps); - - v1 = R_REG(&wlc->regs->maccontrol); - v2 = MCTL_WAKE; - if (hps) - v2 |= MCTL_HPS; - - brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); - - awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); - - if (!awake_before) - brcms_b_wait_for_wake(wlc->hw); - -} + if (!awake_before) + brcms_b_wait_for_wake(wlc->hw); +} /* * Write this BSS config's MAC address to core. * Updates RXE match engine. */ -int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) +static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) { int err = 0; struct brcms_c_info *wlc = bsscfg->wlc; @@ -3847,7 +3932,7 @@ int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) /* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl). * Updates RXE match engine. */ -void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) +static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) { /* we need to update BSSID in RXE match registers */ brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID); @@ -3868,7 +3953,7 @@ static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot) * Suspend the the MAC and update the slot timing * for standard 11b/g (20us slots) or shortslot 11g (9us slots). */ -void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) +static void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) { /* use the override if it is set */ if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO) @@ -3882,7 +3967,7 @@ void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) brcms_b_set_shortslot(wlc->hw, shortslot); } -void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) +static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) { if (wlc->home_chanspec != chanspec) { wlc->home_chanspec = chanspec; @@ -3952,7 +4037,7 @@ static void brcms_c_setband(struct brcms_c_info *wlc, brcms_c_bsinit(wlc); } -void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) +static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) { uint bandunit; bool switchband = false; @@ -4006,31 +4091,6 @@ void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) brcms_c_ucode_mac_upd(wlc); } -u32 brcms_c_lowest_basic_rspec(struct brcms_c_info *wlc, - struct brcms_c_rateset *rs) -{ - u32 lowest_basic_rspec; - uint i; - - /* Use the lowest basic rate */ - lowest_basic_rspec = rs->rates[0] & BRCMS_RATE_MASK; - for (i = 0; i < rs->count; i++) { - if (rs->rates[i] & BRCMS_RATE_FLAG) { - lowest_basic_rspec = rs->rates[i] & BRCMS_RATE_MASK; - break; - } - } - - /* - * pick siso/cdd as default for OFDM (note no basic - * rate MCSs are supported yet) - */ - if (is_ofdm_rate(lowest_basic_rspec)) - lowest_basic_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT); - - return lowest_basic_rspec; -} - /* * This function changes the phytxctl for beacon based on current * beacon ratespec AND txant setting as per this table: @@ -4239,7 +4299,7 @@ static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); } -void brcms_c_radio_disable(struct brcms_c_info *wlc) +static void brcms_c_radio_disable(struct brcms_c_info *wlc) { if (!wlc->pub->up) { brcms_c_down_led_upd(wlc); @@ -4261,7 +4321,7 @@ static void brcms_c_radio_enable(struct brcms_c_info *wlc) brcms_up(wlc->wl); } -bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) +static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) { if (!wlc->radio_monitor) return true; @@ -4347,6 +4407,60 @@ static void brcms_b_watchdog(void *arg) wlc_phy_watchdog(wlc_hw->band->pi); } +static void brcms_c_radio_mpc_upd(struct brcms_c_info *wlc) +{ + bool mpc_radio, radio_state; + + /* + * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled + * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio + * monitor also when WL_RADIO_MPC_DISABLE is the only reason that + * the radio is going down. + */ + if (!wlc->mpc) { + if (!wlc->pub->radio_disabled) + return; + mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); + brcms_c_radio_upd(wlc); + if (!wlc->pub->radio_disabled) + brcms_c_radio_monitor_stop(wlc); + return; + } + + /* + * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in + * wlc->pub->radio_disabled to go ON, always call radio_upd + * synchronously to go OFF, postpone radio_upd to later when + * context is safe(e.g. watchdog) + */ + radio_state = + (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF : + ON); + mpc_radio = (brcms_c_ismpc(wlc) == true) ? OFF : ON; + + if (radio_state == ON && mpc_radio == OFF) + wlc->mpc_delay_off = wlc->mpc_dlycnt; + else if (radio_state == OFF && mpc_radio == ON) { + mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); + brcms_c_radio_upd(wlc); + if (wlc->mpc_offcnt < BRCMS_MPC_THRESHOLD) + wlc->mpc_dlycnt = BRCMS_MPC_MAX_DELAYCNT; + else + wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; + } + /* + * Below logic is meant to capture the transition from mpc off + * to mpc on for reasons other than wlc->mpc_delay_off keeping + * the mpc off. In that case reset wlc->mpc_delay_off to + * wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off + */ + if ((wlc->prev_non_delay_mpc == false) && + (brcms_c_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off) + wlc->mpc_delay_off = wlc->mpc_dlycnt; + + wlc->prev_non_delay_mpc = brcms_c_is_non_delay_mpc(wlc); +} + /* common watchdog code */ static void brcms_c_watchdog(void *arg) { @@ -4408,7 +4522,7 @@ 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) +static 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"); @@ -4436,7 +4550,7 @@ bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit) * 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) +static void brcms_c_info_init(struct brcms_c_info *wlc, int unit) { int i; @@ -5025,224 +5139,33 @@ static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) } } -/* - * 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 __iomem *regsva, struct pci_dev *btparam, - uint *perr) +static void brcms_c_timers_deinit(struct brcms_c_info *wlc) { - struct brcms_c_info *wlc; - uint err = 0; - uint i, j; - struct brcms_pub *pub; + /* free timer state */ + if (wlc->wdtimer) { + brcms_free_timer(wlc->wdtimer); + wlc->wdtimer = NULL; + } + if (wlc->radio_timer) { + brcms_free_timer(wlc->radio_timer); + wlc->radio_timer = NULL; + } +} - /* 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; +static void brcms_c_detach_module(struct brcms_c_info *wlc) +{ + if (wlc->asi) { + brcms_c_antsel_detach(wlc->asi); + wlc->asi = NULL; + } -#if defined(BCMDBG) - wlc_info_dbg = wlc; -#endif + if (wlc->ampdu) { + brcms_c_ampdu_detach(wlc->ampdu); + wlc->ampdu = NULL; + } - wlc->band = wlc->bandstate[0]; - wlc->core = wlc->corestate; - wlc->wl = wl; - pub->unit = unit; - pub->_piomode = piomode; - wlc->bandinit_pending = false; - - /* populate struct brcms_c_info with default values */ - brcms_c_info_init(wlc, unit); - - /* update sta/ap related parameters */ - brcms_c_ap_upd(wlc); - - /* - * low level attach steps(all hw accesses go - * inside, no more in rest of the attach) - */ - err = brcms_b_attach(wlc, vendor, device, unit, piomode, regsva, - btparam); - if (err) - goto fail; - - brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); - - pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); - - /* disable allowed duty cycle */ - wlc->tx_duty_cycle_ofdm = 0; - wlc->tx_duty_cycle_cck = 0; - - brcms_c_stf_phy_chain_calc(wlc); - - /* txchain 1: txant 0, txchain 2: txant 1 */ - if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) - wlc->stf->txant = wlc->stf->hw_txchain - 1; - - /* push to BMAC driver */ - wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, - wlc->stf->hw_rxchain); - - /* pull up some info resulting from the low attach */ - for (i = 0; i < NFIFO; i++) - wlc->core->txavail[i] = wlc->hw->txavail[i]; - - memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); - memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); - - for (j = 0; j < wlc->pub->_nbands; j++) { - wlc->band = wlc->bandstate[j]; - - if (!brcms_c_attach_stf_ant_init(wlc)) { - err = 24; - goto fail; - } - - /* default contention windows size limits */ - wlc->band->CWmin = APHY_CWMIN; - wlc->band->CWmax = PHY_CWMAX; - - /* init gmode value */ - if (wlc->band->bandtype == BRCM_BAND_2G) { - wlc->band->gmode = GMODE_AUTO; - brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, - wlc->band->gmode); - } - - /* init _n_enab supported mode */ - if (BRCMS_PHY_11N_CAP(wlc->band)) { - pub->_n_enab = SUPPORT_11N; - brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, - ((pub->_n_enab == - SUPPORT_11N) ? WL_11N_2x2 : - WL_11N_3x3)); - } - - /* init per-band default rateset, depend on band->gmode */ - brcms_default_rateset(wlc, &wlc->band->defrateset); - - /* fill in hw_rateset */ - brcms_c_rateset_filter(&wlc->band->defrateset, - &wlc->band->hw_rateset, false, - BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, - (bool) (wlc->pub->_n_enab & SUPPORT_11N)); - } - - /* - * update antenna config due to - * wlc->stf->txant/txchain/ant_rx_ovr change - */ - brcms_c_stf_phy_txant_upd(wlc); - - /* attach each modules */ - err = brcms_c_attach_module(wlc); - if (err != 0) - goto fail; - - if (!brcms_c_timers_init(wlc, unit)) { - wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, - __func__); - err = 32; - goto fail; - } - - /* depend on rateset, gmode */ - wlc->cmi = brcms_c_channel_mgr_attach(wlc); - if (!wlc->cmi) { - wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" - "\n", unit, __func__); - err = 33; - goto fail; - } - - /* init default when all parameters are ready, i.e. ->rateset */ - brcms_c_bss_default_init(wlc); - - /* - * Complete the wlc default state initializations.. - */ - - /* allocate our initial queue */ - wlc->pkt_queue = brcms_c_txq_alloc(wlc); - if (wlc->pkt_queue == NULL) { - wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n", - unit, __func__); - err = 100; - goto fail; - } - - wlc->bsscfg->wlc = wlc; - - wlc->mimoft = FT_HT; - wlc->mimo_40txbw = AUTO; - wlc->ofdm_40txbw = AUTO; - wlc->cck_40txbw = AUTO; - brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); - - /* Set default values of SGI */ - if (BRCMS_SGI_CAP_PHY(wlc)) { - brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | - BRCMS_N_SGI_40)); - } else if (BRCMS_ISSSLPNPHY(wlc->band)) { - brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | - BRCMS_N_SGI_40)); - } else { - brcms_c_ht_update_sgi_rx(wlc, 0); - } - - /* initialize radio_mpc_disable according to wlc->mpc */ - brcms_c_radio_mpc_upd(wlc); - brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); - - if (perr) - *perr = 0; - - return wlc; - - fail: - wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", - unit, __func__, err); - if (wlc) - brcms_c_detach(wlc); - - if (perr) - *perr = err; - return NULL; -} - -static void brcms_c_timers_deinit(struct brcms_c_info *wlc) -{ - /* free timer state */ - if (wlc->wdtimer) { - brcms_free_timer(wlc->wdtimer); - wlc->wdtimer = NULL; - } - if (wlc->radio_timer) { - brcms_free_timer(wlc->radio_timer); - wlc->radio_timer = NULL; - } -} - -static void brcms_c_detach_module(struct brcms_c_info *wlc) -{ - if (wlc->asi) { - brcms_c_antsel_detach(wlc->asi); - wlc->asi = NULL; - } - - if (wlc->ampdu) { - brcms_c_ampdu_detach(wlc->ampdu); - wlc->ampdu = NULL; - } - - brcms_c_stf_detach(wlc); -} + brcms_c_stf_detach(wlc); +} /* * low level detach @@ -5332,7 +5255,7 @@ uint brcms_c_detach(struct brcms_c_info *wlc) } /* update state that depends on the current value of "ap" */ -void brcms_c_ap_upd(struct brcms_c_info *wlc) +static void brcms_c_ap_upd(struct brcms_c_info *wlc) { /* STA-BSS; short capable */ wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; @@ -5341,73 +5264,6 @@ void brcms_c_ap_upd(struct brcms_c_info *wlc) wlc->mpc = true; } -/* - * 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; -} - -bool brcms_c_ismpc(struct brcms_c_info *wlc) -{ - return (wlc->mpc_delay_off == 0) && (brcms_c_is_non_delay_mpc(wlc)); -} - -void brcms_c_radio_mpc_upd(struct brcms_c_info *wlc) -{ - bool mpc_radio, radio_state; - - /* - * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled - * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio - * monitor also when WL_RADIO_MPC_DISABLE is the only reason that - * the radio is going down. - */ - if (!wlc->mpc) { - if (!wlc->pub->radio_disabled) - return; - mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); - brcms_c_radio_upd(wlc); - if (!wlc->pub->radio_disabled) - brcms_c_radio_monitor_stop(wlc); - return; - } - - /* - * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in - * wlc->pub->radio_disabled to go ON, always call radio_upd - * synchronously to go OFF, postpone radio_upd to later when - * context is safe(e.g. watchdog) - */ - radio_state = - (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF : - ON); - mpc_radio = (brcms_c_ismpc(wlc) == true) ? OFF : ON; - - if (radio_state == ON && mpc_radio == OFF) - wlc->mpc_delay_off = wlc->mpc_dlycnt; - else if (radio_state == OFF && mpc_radio == ON) { - mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); - brcms_c_radio_upd(wlc); - if (wlc->mpc_offcnt < BRCMS_MPC_THRESHOLD) - wlc->mpc_dlycnt = BRCMS_MPC_MAX_DELAYCNT; - else - wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; - } - /* - * Below logic is meant to capture the transition from mpc off - * to mpc on for reasons other than wlc->mpc_delay_off keeping - * the mpc off. In that case reset wlc->mpc_delay_off to - * wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off - */ - if ((wlc->prev_non_delay_mpc == false) && - (brcms_c_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off) - wlc->mpc_delay_off = wlc->mpc_dlycnt; - - wlc->prev_non_delay_mpc = brcms_c_is_non_delay_mpc(wlc); -} /* Initialize just the hardware when coming out of POR or S3/S5 system states */ static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) { @@ -6195,59 +6051,7 @@ void brcms_c_print_txstatus(struct tx_status *txs) #endif /* defined(BCMDBG) */ } -void brcms_c_statsupd(struct brcms_c_info *wlc) -{ - int i; - struct macstat macstats; -#ifdef BCMDBG - u16 delta; - u16 rxf0ovfl; - u16 txfunfl[NFIFO]; -#endif /* BCMDBG */ - - /* if driver down, make no sense to update stats */ - if (!wlc->pub->up) - return; - -#ifdef BCMDBG - /* save last rx fifo 0 overflow count */ - rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl; - - /* save last tx fifo underflow count */ - for (i = 0; i < NFIFO; i++) - txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i]; -#endif /* BCMDBG */ - - /* Read mac stats from contiguous shared memory */ - brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats, - sizeof(struct macstat), OBJADDR_SHM_SEL); - -#ifdef BCMDBG - /* check for rx fifo 0 overflow */ - delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl); - if (delta) - wiphy_err(wlc->wiphy, "wl%d: %u rx fifo 0 overflows!\n", - wlc->pub->unit, delta); - - /* check for tx fifo underflows */ - for (i = 0; i < NFIFO; i++) { - delta = - (u16) (wlc->core->macstat_snapshot->txfunfl[i] - - txfunfl[i]); - if (delta) - wiphy_err(wlc->wiphy, "wl%d: %u tx fifo %d underflows!" - "\n", wlc->pub->unit, delta, i); - } -#endif /* BCMDBG */ - - /* merge counters from dma module */ - for (i = 0; i < NFIFO; i++) { - if (wlc->hw->di[i]) - dma_counterreset(wlc->hw->di[i]); - } -} - -bool brcms_c_chipmatch(u16 vendor, u16 device) +bool brcms_c_chipmatch(u16 vendor, u16 device) { if (vendor != PCI_VENDOR_ID_BROADCOM) { pr_err("chipmatch: unknown vendor id %04x\n", vendor); @@ -6353,6 +6157,62 @@ void brcms_c_print_txdesc(struct d11txh *txh) } #endif /* defined(BCMDBG) */ +#if defined(BCMDBG) +int +brcms_c_format_flags(const struct brcms_c_bit_desc *bd, u32 flags, char *buf, + int len) +{ + int i; + char *p = buf; + char hexstr[16]; + int slen = 0, nlen = 0; + u32 bit; + const char *name; + + if (len < 2 || !buf) + return 0; + + buf[0] = '\0'; + + for (i = 0; flags != 0; i++) { + bit = bd[i].bit; + name = bd[i].name; + if (bit == 0 && flags != 0) { + /* print any unnamed bits */ + snprintf(hexstr, 16, "0x%X", flags); + name = hexstr; + flags = 0; /* exit loop */ + } else if ((flags & bit) == 0) + continue; + flags &= ~bit; + nlen = strlen(name); + slen += nlen; + /* count btwn flag space */ + if (flags != 0) + slen += 1; + /* need NULL char as well */ + if (len <= slen) + break; + /* copy NULL char but don't count it */ + strncpy(p, name, nlen + 1); + p += nlen; + /* copy btwn flag space and NULL char */ + if (flags != 0) + p += snprintf(p, 2, " "); + len -= slen; + } + + /* indicate the str was too short */ + if (flags != 0) { + if (len < 2) + p -= 2 - len; /* overwrite last char */ + p += snprintf(p, 2, ">"); + } + + return (int)(p - buf); +} +#endif /* defined(BCMDBG) */ + #if defined(BCMDBG) void brcms_c_print_rxh(struct d11rxhdr *rxh) { @@ -6365,7 +6225,7 @@ void brcms_c_print_rxh(struct d11rxhdr *rxh) u16 macstatus2 = rxh->RxStatus2; char flagstr[64]; char lenbuf[20]; - static const struct brcmu_bit_desc macstat_flags[] = { + static const struct brcms_c_bit_desc macstat_flags[] = { {RXS_FCSERR, "FCSErr"}, {RXS_RESPFRAMETX, "Reply"}, {RXS_PBPRES, "PADDING"}, @@ -6379,7 +6239,7 @@ void brcms_c_print_rxh(struct d11rxhdr *rxh) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, rxh, sizeof(struct d11rxhdr)); - brcmu_format_flags(macstat_flags, macstatus1, flagstr, 64); + brcms_c_format_flags(macstat_flags, macstatus1, flagstr, 64); snprintf(lenbuf, sizeof(lenbuf), "0x%x", len); @@ -6417,24 +6277,7 @@ u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); } -/* Callback for device removed */ - -/* - * Attempts to queue a packet onto a multiple-precedence queue, - * if necessary evicting a lower precedence packet from the queue. - * - * 'prec' is the precedence number that has already been mapped - * from the packet priority. - * - * Returns true if packet consumed (queued), false if not. - */ -static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q, - struct sk_buff *pkt, int prec) -{ - return brcms_c_prec_enq_head(wlc, q, pkt, prec, false); -} - -bool +static bool brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q, struct sk_buff *pkt, int prec, bool head) { @@ -6481,6 +6324,21 @@ brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q, return true; } +/* + * Attempts to queue a packet onto a multiple-precedence queue, + * if necessary evicting a lower precedence packet from the queue. + * + * 'prec' is the precedence number that has already been mapped + * from the packet priority. + * + * Returns true if packet consumed (queued), false if not. + */ +static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q, + struct sk_buff *pkt, int prec) +{ + return brcms_c_prec_enq_head(wlc, q, pkt, prec, false); +} + void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb, struct sk_buff *sdu, uint prec) { @@ -6647,6 +6505,43 @@ brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, return mac_len; } +/* + * Return true if the specified rate is supported by the specified band. + * BRCM_BAND_AUTO indicates the current band. + */ +static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, + bool verbose) +{ + struct brcms_c_rateset *hw_rateset; + uint i; + + if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) + hw_rateset = &wlc->band->hw_rateset; + else if (wlc->pub->_nbands > 1) + hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; + else + /* other band specified and we are a single band device */ + return false; + + /* check if this is a mimo rate */ + if (is_mcs_rate(rspec)) { + if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) + goto error; + + return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); + } + + for (i = 0; i < hw_rateset->count; i++) + if (hw_rateset->rates[i] == rspec2rate(rspec)) + return true; + error: + if (verbose) + wiphy_err(wlc->wiphy, "wl%d: valid_rate: rate spec 0x%x " + "not in hw_rateset\n", wlc->pub->unit, rspec); + + return false; +} + static u32 mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, u32 int_val) @@ -6764,71 +6659,278 @@ done: } /* - * 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) - * + * 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 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 ieee80211_hdr *h; - struct d11txh *txh; - u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; - int len, phylen, rts_phylen; - u16 mch, phyctl, xfts, mainrates; - u16 seq = 0, mcl = 0, status = 0, frameid = 0; - u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; - u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; - bool use_rts = false; - bool use_cts = false; - bool use_rifs = false; - bool short_preamble[2] = { false, false }; - u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; - u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; - u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; - struct ieee80211_rts *rts = NULL; - bool qos; - uint ac; - bool hwtkmic = false; - u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; -#define ANTCFG_NONE 0xFF - u8 antcfg = ANTCFG_NONE; - u8 fbantcfg = ANTCFG_NONE; - uint phyctl1_stf = 0; - u16 durid = 0; - struct ieee80211_tx_rate *txrate[2]; - int k; - struct ieee80211_tx_info *tx_info; - bool is_mcs; - 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); +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; - /* compute length of frame in bytes for use in PLCP computations */ - len = brcmu_pkttotlen(p); - phylen = len + FCS_LEN; + 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; - /* Get tx_info */ - tx_info = IEEE80211_SKB_CB(p); + 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; +} - /* add PLCP */ - plcp = skb_push(p, D11_PHY_HDR_LEN); +/* 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; +} - /* add Broadcom tx descriptor header */ - txh = (struct d11txh *) skb_push(p, D11_TXH_LEN); - memset(txh, 0, D11_TXH_LEN); +/* 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); - /* setup frameid */ - if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + /* + * 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); + + tmp = (length & 0xfff) << 5; + plcp[2] |= (tmp >> 16) & 0xff; + plcp[1] |= (tmp >> 8) & 0xff; + plcp[0] |= tmp & 0xff; +} + +/* 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); + + brcms_c_cck_plcp_set(wlc, rate, length, plcp); +} + +static void +brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, + uint length, u8 *plcp) +{ + if (is_mcs_rate(rspec)) + brcms_c_compute_mimo_plcp(rspec, length, plcp); + else if (is_ofdm_rate(rspec)) + brcms_c_compute_ofdm_plcp(rspec, length, plcp); + else + brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); +} + +/* 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; + + sifs = get_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; +} + +static 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_rate(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_rate(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; +} + +/* + * 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 ieee80211_hdr *h; + struct d11txh *txh; + u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; + int len, phylen, rts_phylen; + u16 mch, phyctl, xfts, mainrates; + u16 seq = 0, mcl = 0, status = 0, frameid = 0; + u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; + u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; + bool use_rts = false; + bool use_cts = false; + bool use_rifs = false; + bool short_preamble[2] = { false, false }; + u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; + u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; + u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; + struct ieee80211_rts *rts = NULL; + bool qos; + uint ac; + bool hwtkmic = false; + u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; +#define ANTCFG_NONE 0xFF + u8 antcfg = ANTCFG_NONE; + u8 fbantcfg = ANTCFG_NONE; + uint phyctl1_stf = 0; + u16 durid = 0; + struct ieee80211_tx_rate *txrate[2]; + int k; + struct ieee80211_tx_info *tx_info; + bool is_mcs; + 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); + + /* compute length of frame in bytes for use in PLCP computations */ + len = brcmu_pkttotlen(p); + phylen = len + FCS_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 == " @@ -7518,237 +7620,30 @@ brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); } -/* - * 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) +u32 +brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, + bool use_rspec, u16 mimo_ctlchbw) { - u16 usec = 0; - u8 le = 0; + u32 rts_rspec = 0; - 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; - - 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; -} - -/* 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; -} - -/* 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); - - /* - * 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); - - tmp = (length & 0xfff) << 5; - plcp[2] |= (tmp >> 16) & 0xff; - plcp[1] |= (tmp >> 8) & 0xff; - plcp[0] |= tmp & 0xff; -} - -/* 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); - - brcms_c_cck_plcp_set(wlc, rate, length, plcp); -} - -void -brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, - uint length, u8 *plcp) -{ - if (is_mcs_rate(rspec)) - brcms_c_compute_mimo_plcp(rspec, length, plcp); - else if (is_ofdm_rate(rspec)) - brcms_c_compute_ofdm_plcp(rspec, length, plcp); - else - brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); -} - -/* 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; - - sifs = get_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_rate(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_rate(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_rate(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 (use_rspec) + /* use frame rate as rts rate */ + rts_rspec = rspec; + else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(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 */ @@ -7772,16 +7667,6 @@ brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, return rts_rspec; } -void brcms_c_tbtt(struct brcms_c_info *wlc) -{ - if (!wlc->bsscfg->BSS) - /* - * DirFrmQ is now valid...defer setting until end - * of ATIM window - */ - wlc->qvalid |= MCMD_DIRFRMQVAL; -} - void brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend) { @@ -7796,7 +7681,7 @@ brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend) } /* Update beacon listen interval in shared memory */ -void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) +static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) { /* wake up every DTIM is the default */ if (wlc->bcn_li_dtim == 1) @@ -7993,848 +7878,898 @@ brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); } -/* Process received frames */ -/* - * Return true if more frames need to be processed. false otherwise. - * Param 'bound' indicates max. # frames to process before break out. +/* calculate frame duration for Mixed-mode L-SIG spoofing, return + * number of bytes goes in the length field + * + * Formula given by HT PHY Spec v 1.13 + * len = 3(nsyms + nstream + 3) - 3 */ -void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) +u16 +brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, + uint mac_len) { - struct d11rxhdr *rxh; - struct ieee80211_hdr *h; - uint len; - bool is_amsdu; - - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + uint nsyms, len = 0, kNdps; - /* frame starts with rxhdr */ - rxh = (struct d11rxhdr *) (p->data); + BCMMSG(wlc->wiphy, "wl%d: rate %d, len%d\n", + wlc->pub->unit, rspec2rate(ratespec), mac_len); - /* strip off rxhdr */ - skb_pull(p, BRCMS_HWRXOFF); + if (is_mcs_rate(ratespec)) { + uint mcs = ratespec & RSPEC_RATE_MASK; + int tot_streams = (mcs_2_txstreams(mcs) + 1) + + rspec_stc(ratespec); - /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ - if (rxh->RxStatus1 & RXS_PBPRES) { - if (p->len < 2) { - wiphy_err(wlc->wiphy, "wl%d: recv: rcvd runt of " - "len %d\n", wlc->pub->unit, p->len); - goto toss; - } - skb_pull(p, 2); - } + /* + * the payload duration calculation matches that + * of regular ofdm + */ + /* 1000Ndbps = kbps * 4 */ + kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), + rspec_issgi(ratespec)) * 4; - h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); - len = p->len; + if (rspec_stc(ratespec) == 0) + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, kNdps); + else + /* STBC needs to have even number of symbols */ + nsyms = + 2 * + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, 2 * kNdps); - if (rxh->RxStatus1 & RXS_FCSERR) { - if (wlc->pub->mac80211_state & MAC80211_PROMISC_BCNS) { - wiphy_err(wlc->wiphy, "FCSERR while scanning******* -" - " tossing\n"); - goto toss; - } else { - wiphy_err(wlc->wiphy, "RCSERR!!!\n"); - goto toss; - } + /* (+3) account for HT-SIG(2) and HT-STF(1) */ + nsyms += (tot_streams + 3); + /* + * 3 bytes/symbol @ legacy 6Mbps rate + * (-3) excluding service bits and tail bits + */ + len = (3 * nsyms) - 3; } - /* check received pkt has at least frame control field */ - if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) - goto toss; + return (u16) len; +} - /* not supporting A-MSDU */ - is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; - if (is_amsdu) - goto toss; +static void +brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) +{ + const struct brcms_c_rateset *rs_dflt; + struct brcms_c_rateset rs; + u8 rate; + u16 entry_ptr; + u8 plcp[D11_PHY_HDR_LEN]; + u16 dur, sifs; + uint i; - brcms_c_recvctl(wlc, rxh, p); - return; + sifs = get_sifs(wlc->band); - toss: - brcmu_pkt_buf_free_skb(p); + rs_dflt = brcms_c_rateset_get_hwrs(wlc); + + brcms_c_rateset_copy(rs_dflt, &rs); + brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); + + /* + * walk the phy rate table and update MAC core SHM + * basic rate table entries + */ + for (i = 0; i < rs.count; i++) { + rate = rs.rates[i] & BRCMS_RATE_MASK; + + entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); + + /* Calculate the Probe Response PLCP for the given rate */ + brcms_c_compute_plcp(wlc, rate, frame_len, plcp); + + /* + * Calculate the duration of the Probe Response + * frame plus SIFS for the MAC + */ + dur = (u16) brcms_c_calc_frame_time(wlc, rate, + BRCMS_LONG_PREAMBLE, frame_len); + dur += sifs; + + /* Update the SHM Rate Table entry Probe Response values */ + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, + (u16) (plcp[0] + (plcp[1] << 8))); + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, + (u16) (plcp[2] + (plcp[3] << 8))); + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); + } } -/* calculate frame duration for Mixed-mode L-SIG spoofing, return - * number of bytes goes in the length field +/* Max buffering needed for beacon template/prb resp template is 142 bytes. * - * Formula given by HT PHY Spec v 1.13 - * len = 3(nsyms + nstream + 3) - 3 + * PLCP header is 6 bytes. + * 802.11 A3 header is 24 bytes. + * Max beacon frame body template length is 112 bytes. + * Max probe resp frame body template length is 110 bytes. + * + * *len on input contains the max length of the packet available. + * + * The *len value is set to the number of bytes in buf used, and starts + * with the PLCP and included up to, but not including, the 4 byte FCS. */ -u16 -brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, - uint mac_len) +static void +brcms_c_bcn_prb_template(struct brcms_c_info *wlc, u16 type, + u32 bcn_rspec, + struct brcms_bss_cfg *cfg, u16 *buf, int *len) { - uint nsyms, len = 0, kNdps; + static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255}; + struct cck_phy_hdr *plcp; + struct ieee80211_mgmt *h; + int hdr_len, body_len; - BCMMSG(wlc->wiphy, "wl%d: rate %d, len%d\n", - wlc->pub->unit, rspec2rate(ratespec), mac_len); + hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN; - if (is_mcs_rate(ratespec)) { - uint mcs = ratespec & RSPEC_RATE_MASK; - int tot_streams = (mcs_2_txstreams(mcs) + 1) + - rspec_stc(ratespec); + /* calc buffer size provided for frame body */ + body_len = *len - hdr_len; + /* return actual size */ + *len = hdr_len + body_len; - /* - * the payload duration calculation matches that - * of regular ofdm - */ - /* 1000Ndbps = kbps * 4 */ - kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), - rspec_issgi(ratespec)) * 4; + /* format PHY and MAC headers */ + memset((char *)buf, 0, hdr_len); - if (rspec_stc(ratespec) == 0) - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, kNdps); - else - /* STBC needs to have even number of symbols */ - nsyms = - 2 * - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, 2 * kNdps); + plcp = (struct cck_phy_hdr *) buf; - /* (+3) account for HT-SIG(2) and HT-STF(1) */ - nsyms += (tot_streams + 3); - /* - * 3 bytes/symbol @ legacy 6Mbps rate - * (-3) excluding service bits and tail bits - */ - len = (3 * nsyms) - 3; - } + /* + * PLCP for Probe Response frames are filled in from + * core's rate table + */ + if (type == IEEE80211_STYPE_BEACON) + /* fill in PLCP */ + brcms_c_compute_plcp(wlc, bcn_rspec, + (DOT11_MAC_HDR_LEN + body_len + FCS_LEN), + (u8 *) plcp); - return (u16) len; + /* "Regular" and 16 MBSS but not for 4 MBSS */ + /* Update the phytxctl for the beacon based on the rspec */ + brcms_c_beacon_phytxctl_txant_upd(wlc, bcn_rspec); + + h = (struct ieee80211_mgmt *)&plcp[1]; + + /* fill in 802.11 header */ + h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type); + + /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */ + /* A1 filled in by MAC for prb resp, broadcast for bcn */ + if (type == IEEE80211_STYPE_BEACON) + memcpy(&h->da, ðer_bcast, ETH_ALEN); + memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN); + memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN); + + /* SEQ filled in by MAC */ +} + +int brcms_c_get_header_len(void) +{ + return TXOFF; } /* - * calculate frame duration of a given rate and length, return - * time in usec unit + * Update all beacons for the system. */ -uint -brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, - u8 preamble_type, uint mac_len) +void brcms_c_update_beacon(struct brcms_c_info *wlc) { - uint nsyms, dur = 0, Ndps, kNdps; - uint rate = rspec2rate(ratespec); - - if (rate == 0) { - wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n", - wlc->pub->unit); - rate = BRCM_RATE_1M; - } - - BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n", - wlc->pub->unit, ratespec, preamble_type, mac_len); + struct brcms_bss_cfg *bsscfg = wlc->bsscfg; - if (is_mcs_rate(ratespec)) { - uint mcs = ratespec & RSPEC_RATE_MASK; - int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); + if (bsscfg->up && !bsscfg->BSS) + /* Clear the soft intmask */ + wlc->defmacintmask &= ~MI_BCNTPL; +} - dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); - if (preamble_type == BRCMS_MM_PREAMBLE) - dur += PREN_MM_EXT; - /* 1000Ndbps = kbps * 4 */ - kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), - rspec_issgi(ratespec)) * 4; +/* Write ssid into shared memory */ +static void +brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) +{ + u8 *ssidptr = cfg->SSID; + u16 base = M_SSID; + u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; - if (rspec_stc(ratespec) == 0) - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, kNdps); - else - /* STBC needs to have even number of symbols */ - nsyms = - 2 * - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, 2 * kNdps); + /* padding the ssid with zero and copy it into shm */ + memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); + memcpy(ssidbuf, ssidptr, cfg->SSID_len); - dur += APHY_SYMBOL_TIME * nsyms; - if (wlc->band->bandtype == BRCM_BAND_2G) - dur += DOT11_OFDM_SIGNAL_EXTENSION; - } else if (is_ofdm_rate(rate)) { - dur = APHY_PREAMBLE_TIME; - dur += APHY_SIGNAL_TIME; - /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ - Ndps = rate * 2; - /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), - Ndps); - dur += APHY_SYMBOL_TIME * nsyms; - if (wlc->band->bandtype == BRCM_BAND_2G) - dur += DOT11_OFDM_SIGNAL_EXTENSION; - } else { - /* - * calc # bits * 2 so factor of 2 in rate (1/2 mbps) - * will divide out - */ - mac_len = mac_len * 8 * 2; - /* calc ceiling of bits/rate = microseconds of air time */ - dur = (mac_len + rate - 1) / rate; - if (preamble_type & BRCMS_SHORT_PREAMBLE) - dur += BPHY_PLCP_SHORT_TIME; - else - dur += BPHY_PLCP_TIME; - } - return dur; + brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); + brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); } -/* derive wlc->band->basic_rate[] table from 'rateset' */ -void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, - struct brcms_c_rateset *rateset) +static void +brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, + struct brcms_bss_cfg *cfg, + bool suspend) { - u8 rate; - u8 mandatory; - u8 cck_basic = 0; - u8 ofdm_basic = 0; - u8 *br = wlc->band->basic_rate; - uint i; - - /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ - memset(br, 0, BRCM_MAXRATE + 1); + u16 prb_resp[BCN_TMPL_LEN / 2]; + int len = BCN_TMPL_LEN; - /* For each basic rate in the rates list, make an entry in the - * best basic lookup. + /* + * write the probe response to hardware, or save in + * the config structure */ - for (i = 0; i < rateset->count; i++) { - /* only make an entry for a basic rate */ - if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) - continue; - /* mask off basic bit */ - rate = (rateset->rates[i] & BRCMS_RATE_MASK); + /* create the probe response template */ + brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0, + cfg, prb_resp, &len); - if (rate > BRCM_MAXRATE) { - wiphy_err(wlc->wiphy, "brcms_c_rate_lookup_init: " - "invalid rate 0x%X in rate set\n", - rateset->rates[i]); - continue; - } + if (suspend) + brcms_c_suspend_mac_and_wait(wlc); - br[rate] = rate; - } + /* write the probe response into the template region */ + brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, + (len + 3) & ~3, prb_resp); - /* The rate lookup table now has non-zero entries for each - * basic rate, equal to the basic rate: br[basicN] = basicN - * - * To look up the best basic rate corresponding to any - * particular rate, code can use the basic_rate table - * like this - * - * basic_rate = wlc->band->basic_rate[tx_rate] - * - * Make sure there is a best basic rate entry for - * every rate by walking up the table from low rates - * to high, filling in holes in the lookup table + /* write the length of the probe response frame (+PLCP/-FCS) */ + brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); + + /* write the SSID and SSID length */ + brcms_c_shm_ssid_upd(wlc, cfg); + + /* + * Write PLCP headers and durations for probe response frames + * at all rates. Use the actual frame length covered by the + * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() + * by subtracting the PLCP len and adding the FCS. */ + len += (-D11_PHY_HDR_LEN + FCS_LEN); + brcms_c_mod_prb_rsp_rate_table(wlc, (u16) len); - for (i = 0; i < wlc->band->hw_rateset.count; i++) { - rate = wlc->band->hw_rateset.rates[i]; + if (suspend) + brcms_c_enable_mac(wlc); +} - if (br[rate] != 0) { - /* This rate is a basic rate. - * Keep track of the best basic rate so far by - * modulation type. - */ - if (is_ofdm_rate(rate)) - ofdm_basic = rate; - else - cck_basic = rate; +void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) +{ + struct brcms_bss_cfg *bsscfg = wlc->bsscfg; - continue; - } + /* update AP or IBSS probe responses */ + if (bsscfg->up && !bsscfg->BSS) + brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend); +} - /* This rate is not a basic rate so figure out the - * best basic rate less than this rate and fill in - * the hole in the table - */ +/* prepares pdu for transmission. returns BCM error codes */ +int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop) +{ + uint fifo; + struct d11txh *txh; + struct ieee80211_hdr *h; + struct scb *scb; - br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; + txh = (struct d11txh *) (pdu->data); + h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); - if (br[rate] != 0) - continue; + /* get the pkt queue info. This was put at brcms_c_sendctl or + * brcms_c_send for PDU */ + fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; - if (is_ofdm_rate(rate)) { - /* - * In 11g and 11a, the OFDM mandatory rates - * are 6, 12, and 24 Mbps - */ - if (rate >= BRCM_RATE_24M) - mandatory = BRCM_RATE_24M; - else if (rate >= BRCM_RATE_12M) - mandatory = BRCM_RATE_12M; - else - mandatory = BRCM_RATE_6M; - } else { - /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ - mandatory = rate; - } + scb = NULL; - br[rate] = mandatory; + *fifop = fifo; + + /* return if insufficient dma resources */ + if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) { + /* Mark precedences related to this FIFO, unsendable */ + /* A fifo is full. Clear precedences related to that FIFO */ + wlc->tx_prec_map &= ~(wlc->fifo2prec_map[fifo]); + return -EBUSY; } + return 0; } -static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, - u8 basic_rate) +int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, + uint *blocks) { - u8 phy_rate, index; - u8 basic_phy_rate, basic_index; - u16 dir_table, basic_table; - u16 basic_ptr; + if (fifo >= NFIFO) + return -EINVAL; - /* Shared memory address for the table we are reading */ - dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; + *blocks = wlc_hw->xmtfifo_sz[fifo]; - /* Shared memory address for the table we are writing */ - basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; + return 0; +} - /* - * for a given rate, the LS-nibble of the PLCP SIGNAL field is - * the index into the rate table. - */ - phy_rate = rate_info[rate] & BRCMS_RATE_MASK; - basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; - index = phy_rate & 0xf; - basic_index = basic_phy_rate & 0xf; +void +brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, + const u8 *addr) +{ + brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); + if (match_reg_offset == RCM_BSSID_OFFSET) + memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); +} - /* Find the SHM pointer to the ACK rate entry by looking in the - * Direct-map Table - */ - basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); +/* + * Flag 'scan in progress' to withhold dynamic phy calibration + */ +void brcms_c_scan_start(struct brcms_c_info *wlc) +{ + wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); +} - /* Update the SHM BSS-basic-rate-set mapping table with the pointer - * to the correct basic rate for the given incoming rate - */ - brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); +void brcms_c_scan_stop(struct brcms_c_info *wlc) +{ + wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); } -static const struct brcms_c_rateset * -brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) +void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) { - const struct brcms_c_rateset *rs_dflt; + wlc->pub->associated = state; + wlc->bsscfg->associated = state; +} - if (BRCMS_PHY_11N_CAP(wlc->band)) { - if (wlc->band->bandtype == BRCM_BAND_5G) - rs_dflt = &ofdm_mimo_rates; - else - rs_dflt = &cck_ofdm_mimo_rates; - } else if (wlc->band->gmode) - rs_dflt = &cck_ofdm_rates; - else - rs_dflt = &cck_rates; +/* + * When a remote STA/AP is removed by Mac80211, or when it can no longer accept + * AMPDU traffic, packets pending in hardware have to be invalidated so that + * when later on hardware releases them, they can be handled appropriately. + */ +void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, + struct ieee80211_sta *sta, + void (*dma_callback_fn)) +{ + struct dma_pub *dmah; + int i; + for (i = 0; i < NFIFO; i++) { + dmah = hw->di[i]; + if (dmah != NULL) + dma_walk_packets(dmah, dma_callback_fn, sta); + } +} + +int brcms_c_get_curband(struct brcms_c_info *wlc) +{ + return wlc->band->bandunit; +} + +void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) +{ + /* flush packet queue when requested */ + if (drop) + brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL); + + /* wait for queue and DMA fifos to run dry */ + while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) + brcms_msleep(wlc->wl, 1); +} - return rs_dflt; +void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) +{ + wlc->bcn_li_bcn = interval; + if (wlc->pub->up) + brcms_c_bcn_li_upd(wlc); } -void brcms_c_set_ratetable(struct brcms_c_info *wlc) +int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) { - const struct brcms_c_rateset *rs_dflt; - struct brcms_c_rateset rs; - u8 rate, basic_rate; - uint i; + uint qdbm; - rs_dflt = brcms_c_rateset_get_hwrs(wlc); + /* Remove override bit and clip to max qdbm value */ + qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); + return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); +} - brcms_c_rateset_copy(rs_dflt, &rs); - brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); +int brcms_c_get_tx_power(struct brcms_c_info *wlc) +{ + uint qdbm; + bool override; - /* walk the phy rate table and update SHM basic rate lookup table */ - for (i = 0; i < rs.count; i++) { - rate = rs.rates[i] & BRCMS_RATE_MASK; + wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); - /* for a given rate brcms_basic_rate returns the rate at - * which a response ACK/CTS should be sent. - */ - basic_rate = brcms_basic_rate(wlc, rate); - if (basic_rate == 0) - /* This should only happen if we are using a - * restricted rateset. - */ - basic_rate = rs.rates[0] & BRCMS_RATE_MASK; + /* Return qdbm units */ + return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); +} - brcms_c_write_rate_shm(wlc, rate, basic_rate); - } +void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc) +{ + wlc->mpc = mpc; + brcms_c_radio_mpc_upd(wlc); } +/* Process received frames */ /* - * Return true if the specified rate is supported by the specified band. - * BRCM_BAND_AUTO indicates the current band. + * Return true if more frames need to be processed. false otherwise. + * Param 'bound' indicates max. # frames to process before break out. */ -bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, - bool verbose) +static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) { - struct brcms_c_rateset *hw_rateset; - uint i; + struct d11rxhdr *rxh; + struct ieee80211_hdr *h; + uint len; + bool is_amsdu; - if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) - hw_rateset = &wlc->band->hw_rateset; - else if (wlc->pub->_nbands > 1) - hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; - else - /* other band specified and we are a single band device */ - return false; + BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - /* check if this is a mimo rate */ - if (is_mcs_rate(rspec)) { - if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) - goto error; + /* frame starts with rxhdr */ + rxh = (struct d11rxhdr *) (p->data); - return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); + /* strip off rxhdr */ + skb_pull(p, BRCMS_HWRXOFF); + + /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ + if (rxh->RxStatus1 & RXS_PBPRES) { + if (p->len < 2) { + wiphy_err(wlc->wiphy, "wl%d: recv: rcvd runt of " + "len %d\n", wlc->pub->unit, p->len); + goto toss; + } + skb_pull(p, 2); } - for (i = 0; i < hw_rateset->count; i++) - if (hw_rateset->rates[i] == rspec2rate(rspec)) - return true; - error: - if (verbose) - wiphy_err(wlc->wiphy, "wl%d: valid_rate: rate spec 0x%x " - "not in hw_rateset\n", wlc->pub->unit, rspec); + h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); + len = p->len; - return false; + if (rxh->RxStatus1 & RXS_FCSERR) { + if (wlc->pub->mac80211_state & MAC80211_PROMISC_BCNS) { + wiphy_err(wlc->wiphy, "FCSERR while scanning******* -" + " tossing\n"); + goto toss; + } else { + wiphy_err(wlc->wiphy, "RCSERR!!!\n"); + goto toss; + } + } + + /* check received pkt has at least frame control field */ + if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) + goto toss; + + /* not supporting A-MSDU */ + is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; + if (is_amsdu) + goto toss; + + brcms_c_recvctl(wlc, rxh, p); + return; + + toss: + brcmu_pkt_buf_free_skb(p); } -void brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) +/* Process received frames */ +/* + * Return true if more frames need to be processed. false otherwise. + * Param 'bound' indicates max. # frames to process before break out. + */ +static bool +brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) { - const struct brcms_c_rateset *rs_dflt; - struct brcms_c_rateset rs; - u8 rate; - u16 entry_ptr; - u8 plcp[D11_PHY_HDR_LEN]; - u16 dur, sifs; - uint i; + struct sk_buff *p; + struct sk_buff *head = NULL; + struct sk_buff *tail = NULL; + uint n = 0; + uint bound_limit = bound ? RXBND : -1; - sifs = get_sifs(wlc->band); + BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + /* gather received frames */ + while ((p = dma_rx(wlc_hw->di[fifo]))) { - rs_dflt = brcms_c_rateset_get_hwrs(wlc); + if (!tail) + head = tail = p; + else { + tail->prev = p; + tail = p; + } - brcms_c_rateset_copy(rs_dflt, &rs); - brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); + /* !give others some time to run! */ + if (++n >= bound_limit) + break; + } - /* - * walk the phy rate table and update MAC core SHM - * basic rate table entries - */ - for (i = 0; i < rs.count; i++) { - rate = rs.rates[i] & BRCMS_RATE_MASK; + /* post more rbufs */ + dma_rxfill(wlc_hw->di[fifo]); - entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); + /* process each frame */ + while ((p = head) != NULL) { + struct d11rxhdr_le *rxh_le; + struct d11rxhdr *rxh; + head = head->prev; + p->prev = NULL; - /* Calculate the Probe Response PLCP for the given rate */ - brcms_c_compute_plcp(wlc, rate, frame_len, plcp); + rxh_le = (struct d11rxhdr_le *)p->data; + rxh = (struct d11rxhdr *)p->data; - /* - * Calculate the duration of the Probe Response - * frame plus SIFS for the MAC - */ - dur = (u16) brcms_c_calc_frame_time(wlc, rate, - BRCMS_LONG_PREAMBLE, frame_len); - dur += sifs; + /* fixup rx header endianness */ + rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); + rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); + rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); + rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); + rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); + rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); + rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); + rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); + rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); + rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); + rxh->RxChan = le16_to_cpu(rxh_le->RxChan); - /* Update the SHM Rate Table entry Probe Response values */ - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, - (u16) (plcp[0] + (plcp[1] << 8))); - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, - (u16) (plcp[2] + (plcp[3] << 8))); - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); + brcms_c_recv(wlc_hw->wlc, p); } + + return n >= bound_limit; } -/* Max buffering needed for beacon template/prb resp template is 142 bytes. - * - * PLCP header is 6 bytes. - * 802.11 A3 header is 24 bytes. - * Max beacon frame body template length is 112 bytes. - * Max probe resp frame body template length is 110 bytes. - * - * *len on input contains the max length of the packet available. - * - * The *len value is set to the number of bytes in buf used, and starts - * with the PLCP and included up to, but not including, the 4 byte FCS. +/* 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. */ -static void -brcms_c_bcn_prb_template(struct brcms_c_info *wlc, u16 type, - u32 bcn_rspec, - struct brcms_bss_cfg *cfg, u16 *buf, int *len) +bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) { - static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255}; - struct cck_phy_hdr *plcp; - struct ieee80211_mgmt *h; - int hdr_len, body_len; + u32 macintstatus; + struct brcms_hardware *wlc_hw = wlc->hw; + struct d11regs __iomem *regs = wlc_hw->regs; + struct wiphy *wiphy = wlc->wiphy; - hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN; + if (brcms_deviceremoved(wlc)) { + wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + brcms_down(wlc->wl); + return false; + } - /* calc buffer size provided for frame body */ - body_len = *len - hdr_len; - /* return actual size */ - *len = hdr_len + body_len; + /* grab and clear the saved software intstatus bits */ + macintstatus = wlc->macintstatus; + wlc->macintstatus = 0; + + BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n", + wlc_hw->unit, macintstatus); + + WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ + + /* tx status */ + if (macintstatus & MI_TFS) { + bool fatal; + if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) + wlc->macintstatus |= MI_TFS; + if (fatal) { + wiphy_err(wiphy, "MI_TFS: fatal\n"); + goto fatal; + } + } - /* format PHY and MAC headers */ - memset((char *)buf, 0, hdr_len); + if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) + brcms_c_tbtt(wlc); - plcp = (struct cck_phy_hdr *) buf; + /* ATIM window end */ + if (macintstatus & MI_ATIMWINEND) { + BCMMSG(wlc->wiphy, "end of ATIM window\n"); + OR_REG(®s->maccommand, wlc->qvalid); + wlc->qvalid = 0; + } /* - * PLCP for Probe Response frames are filled in from - * core's rate table + * received data or control frame, MI_DMAINT is + * indication of RX_FIFO interrupt */ - if (type == IEEE80211_STYPE_BEACON) - /* fill in PLCP */ - brcms_c_compute_plcp(wlc, bcn_rspec, - (DOT11_MAC_HDR_LEN + body_len + FCS_LEN), - (u8 *) plcp); + if (macintstatus & MI_DMAINT) + if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) + wlc->macintstatus |= MI_DMAINT; - /* "Regular" and 16 MBSS but not for 4 MBSS */ - /* Update the phytxctl for the beacon based on the rspec */ - brcms_c_beacon_phytxctl_txant_upd(wlc, bcn_rspec); + /* noise sample collected */ + if (macintstatus & MI_BG_NOISE) + wlc_phy_noise_sample_intr(wlc_hw->band->pi); - h = (struct ieee80211_mgmt *)&plcp[1]; + if (macintstatus & MI_GP0) { + wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d " + "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); - /* fill in 802.11 header */ - h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type); + printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", + __func__, wlc_hw->sih->chip, + wlc_hw->sih->chiprev); + /* big hammer */ + brcms_init(wlc->wl); + } - /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */ - /* A1 filled in by MAC for prb resp, broadcast for bcn */ - if (type == IEEE80211_STYPE_BEACON) - memcpy(&h->da, ðer_bcast, ETH_ALEN); - memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN); - memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN); + /* gptimer timeout */ + if (macintstatus & MI_TO) + W_REG(®s->gptimer, 0); - /* SEQ filled in by MAC */ -} + if (macintstatus & MI_RFDISABLE) { + BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the" + " RF Disable Input\n", wlc_hw->unit); + brcms_rfkill_set_hw_state(wlc->wl); + } -int brcms_c_get_header_len(void) -{ - return TXOFF; -} + /* send any enq'd tx packets. Just makes sure to jump start tx */ + if (!pktq_empty(&wlc->pkt_queue->q)) + brcms_c_send_q(wlc); -/* - * Update all beacons for the system. - */ -void brcms_c_update_beacon(struct brcms_c_info *wlc) -{ - struct brcms_bss_cfg *bsscfg = wlc->bsscfg; + /* it isn't done and needs to be resched if macintstatus is non-zero */ + return wlc->macintstatus != 0; - if (bsscfg->up && !bsscfg->BSS) - /* Clear the soft intmask */ - wlc->defmacintmask &= ~MI_BCNTPL; + fatal: + brcms_init(wlc->wl); + return wlc->macintstatus != 0; } -/* Write ssid into shared memory */ -void brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) +void brcms_c_init(struct brcms_c_info *wlc) { - u8 *ssidptr = cfg->SSID; - u16 base = M_SSID; - u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; + struct d11regs __iomem *regs; + u16 chanspec; + bool mute = false; - /* padding the ssid with zero and copy it into shm */ - memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); - memcpy(ssidbuf, ssidptr, cfg->SSID_len); + BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); - brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); -} + regs = wlc->regs; -void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) -{ - struct brcms_bss_cfg *bsscfg = wlc->bsscfg; + /* + * 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); - /* update AP or IBSS probe responses */ - if (bsscfg->up && !bsscfg->BSS) - brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend); -} + brcms_b_init(wlc->hw, chanspec, mute); -void -brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, - struct brcms_bss_cfg *cfg, - bool suspend) -{ - u16 prb_resp[BCN_TMPL_LEN / 2]; - int len = BCN_TMPL_LEN; + /* update beacon listen interval */ + brcms_c_bcn_li_upd(wlc); - /* - * write the probe response to hardware, or save in - * the config structure - */ + /* write ethernet address to core */ + brcms_c_set_mac(wlc->bsscfg); + brcms_c_set_bssid(wlc->bsscfg); - /* create the probe response template */ - brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0, - cfg, prb_resp, &len); + /* Update tsf_cfprep if associated and up */ + if (wlc->pub->associated && wlc->bsscfg->up) { + u32 bi; - if (suspend) - brcms_c_suspend_mac_and_wait(wlc); + /* get beacon period and convert to uS */ + bi = wlc->bsscfg->current_bss->beacon_period << 10; + /* + * update since init path would reset + * to default value + */ + W_REG(®s->tsf_cfprep, + (bi << CFPREP_CBI_SHIFT)); - /* write the probe response into the template region */ - brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, - (len + 3) & ~3, prb_resp); + /* Update maccontrol PM related bits */ + brcms_c_set_ps_ctrl(wlc); + } - /* write the length of the probe response frame (+PLCP/-FCS) */ - brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); + brcms_c_bandinit_ordered(wlc, chanspec); - /* write the SSID and SSID length */ - brcms_c_shm_ssid_upd(wlc, cfg); + /* init probe response timeout */ + brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); + + /* init max burst txop (framebursting) */ + brcms_b_write_shm(wlc->hw, 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); /* - * Write PLCP headers and durations for probe response frames - * at all rates. Use the actual frame length covered by the - * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() - * by subtracting the PLCP len and adding the FCS. + * Update some shared memory locations related to + * max AMPDU size allowed to received */ - len += (-D11_PHY_HDR_LEN + FCS_LEN); - brcms_c_mod_prb_rsp_rate_table(wlc, (u16) len); + brcms_c_ampdu_shm_upd(wlc->ampdu); - if (suspend) - brcms_c_enable_mac(wlc); -} + /* band-specific inits */ + brcms_c_bsinit(wlc); -/* prepares pdu for transmission. returns BCM error codes */ -int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop) -{ - uint fifo; - struct d11txh *txh; - struct ieee80211_hdr *h; - struct scb *scb; + /* Enable EDCF mode (while the MAC is suspended) */ + OR_REG(®s->ifs_ctl, IFS_USEEDCF); + brcms_c_edcf_setparams(wlc, false); - txh = (struct d11txh *) (pdu->data); - h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); + /* Init precedence maps for empty FIFOs */ + brcms_c_tx_prec_map_init(wlc); - /* get the pkt queue info. This was put at brcms_c_sendctl or - * brcms_c_send for PDU */ - fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; + /* read the ucode version if we have not yet done so */ + if (wlc->ucode_rev == 0) { + wlc->ucode_rev = + brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR) << NBITS(u16); + wlc->ucode_rev |= brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); + } - scb = NULL; + /* ..now really unleash hell (allow the MAC out of suspend) */ + brcms_c_enable_mac(wlc); - *fifop = fifo; + /* clear tx flow control */ + brcms_c_txflowcontrol_reset(wlc); - /* return if insufficient dma resources */ - if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) { - /* Mark precedences related to this FIFO, unsendable */ - /* A fifo is full. Clear precedences related to that FIFO */ - wlc->tx_prec_map &= ~(wlc->fifo2prec_map[fifo]); - return -EBUSY; - } - return 0; -} + /* enable the RF Disable Delay timer */ + W_REG(&wlc->regs->rfdisabledly, RFDISABLE_DEFAULT); -void brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) -{ - brcms_c_rateset_default(rs, NULL, wlc->band->phytype, - wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, - (bool) (wlc->pub->_n_enab & SUPPORT_11N), - brcms_chspec_bw(wlc->default_bss->chanspec), - wlc->stf->txstreams); -} + /* initialize mpc delay */ + wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; -/* Copy a buffer to shared memory. - * SHM 'offset' needs to be an even address and - * Buffer length 'len' must be an even number of bytes - */ -void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, const void *buf, - int len) -{ - brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); + /* + * Initialize WME parameters; if they haven't been set by some other + * mechanism (IOVar, etc) then read them from the hardware. + */ + if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { + /* Uninitialized; read from HW */ + int ac; + + for (ac = 0; ac < AC_COUNT; ac++) + wlc->wme_retries[ac] = + brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); + } } -int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, - uint *blocks) +/* + * 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 __iomem *regsva, struct pci_dev *btparam, + uint *perr) { - if (fifo >= NFIFO) - return -EINVAL; + struct brcms_c_info *wlc; + uint err = 0; + uint i, j; + struct brcms_pub *pub; - *blocks = wlc_hw->xmtfifo_sz[fifo]; + /* 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; - return 0; -} +#if defined(BCMDBG) + wlc_info_dbg = wlc; +#endif -void -brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, - const u8 *addr) -{ - brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); - if (match_reg_offset == RCM_BSSID_OFFSET) - memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); -} + wlc->band = wlc->bandstate[0]; + wlc->core = wlc->corestate; + wlc->wl = wl; + pub->unit = unit; + pub->_piomode = piomode; + wlc->bandinit_pending = false; -/* check for the particular priority flow control bit being set */ -bool -brcms_c_txflowcontrol_prio_isset(struct brcms_c_info *wlc, - struct brcms_txq_info *q, - int prio) -{ - uint prio_mask; + /* populate struct brcms_c_info with default values */ + brcms_c_info_init(wlc, unit); - if (prio == ALLPRIO) - prio_mask = TXQ_STOP_FOR_PRIOFC_MASK; - else - prio_mask = NBITVAL(prio); + /* update sta/ap related parameters */ + brcms_c_ap_upd(wlc); - return (q->stopped & prio_mask) == prio_mask; -} + /* + * low level attach steps(all hw accesses go + * inside, no more in rest of the attach) + */ + err = brcms_b_attach(wlc, vendor, device, unit, piomode, regsva, + btparam); + if (err) + goto fail; -/* propagate the flow control to all interfaces using the given tx queue */ -void brcms_c_txflowcontrol(struct brcms_c_info *wlc, - struct brcms_txq_info *qi, - bool on, int prio) -{ - uint prio_bits; - uint cur_bits; + brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); - BCMMSG(wlc->wiphy, "flow control kicks in\n"); + pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); - if (prio == ALLPRIO) - prio_bits = TXQ_STOP_FOR_PRIOFC_MASK; - else - prio_bits = NBITVAL(prio); + /* disable allowed duty cycle */ + wlc->tx_duty_cycle_ofdm = 0; + wlc->tx_duty_cycle_cck = 0; - cur_bits = qi->stopped & prio_bits; + brcms_c_stf_phy_chain_calc(wlc); - /* Check for the case of no change and return early - * Otherwise update the bit and continue - */ - if (on) { - if (cur_bits == prio_bits) - return; + /* txchain 1: txant 0, txchain 2: txant 1 */ + if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) + wlc->stf->txant = wlc->stf->hw_txchain - 1; - mboolset(qi->stopped, prio_bits); - } else { - if (cur_bits == 0) - return; + /* push to BMAC driver */ + wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, + wlc->stf->hw_rxchain); - mboolclr(qi->stopped, prio_bits); - } + /* pull up some info resulting from the low attach */ + for (i = 0; i < NFIFO; i++) + wlc->core->txavail[i] = wlc->hw->txavail[i]; - /* If there is a flow control override we will not change the external - * flow control state. - */ - if (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK) - return; + memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); + memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); - brcms_c_txflowcontrol_signal(wlc, qi, on, prio); -} + for (j = 0; j < wlc->pub->_nbands; j++) { + wlc->band = wlc->bandstate[j]; -void -brcms_c_txflowcontrol_override(struct brcms_c_info *wlc, - struct brcms_txq_info *qi, - bool on, uint override) -{ - uint prev_override; + if (!brcms_c_attach_stf_ant_init(wlc)) { + err = 24; + goto fail; + } - prev_override = (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK); + /* default contention windows size limits */ + wlc->band->CWmin = APHY_CWMIN; + wlc->band->CWmax = PHY_CWMAX; - /* Update the flow control bits and do an early return if there is - * no change in the external flow control state. - */ - if (on) { - mboolset(qi->stopped, override); - /* if there was a previous override bit on, then setting this - * makes no difference. - */ - if (prev_override) - return; + /* init gmode value */ + if (wlc->band->bandtype == BRCM_BAND_2G) { + wlc->band->gmode = GMODE_AUTO; + brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, + wlc->band->gmode); + } - brcms_c_txflowcontrol_signal(wlc, qi, ON, ALLPRIO); - } else { - mboolclr(qi->stopped, override); - /* clearing an override bit will only make a difference for - * flow control if it was the only bit set. For any other - * override setting, just return - */ - if (prev_override != override) - return; + /* init _n_enab supported mode */ + if (BRCMS_PHY_11N_CAP(wlc->band)) { + pub->_n_enab = SUPPORT_11N; + brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, + ((pub->_n_enab == + SUPPORT_11N) ? WL_11N_2x2 : + WL_11N_3x3)); + } - if (qi->stopped == 0) { - brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO); - } else { - int prio; + /* init per-band default rateset, depend on band->gmode */ + brcms_default_rateset(wlc, &wlc->band->defrateset); - for (prio = MAXPRIO; prio >= 0; prio--) { - if (!mboolisset(qi->stopped, NBITVAL(prio))) - brcms_c_txflowcontrol_signal( - wlc, qi, OFF, prio); - } - } + /* fill in hw_rateset */ + brcms_c_rateset_filter(&wlc->band->defrateset, + &wlc->band->hw_rateset, false, + BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, + (bool) (wlc->pub->_n_enab & SUPPORT_11N)); } -} -/* - * Flag 'scan in progress' to withhold dynamic phy calibration - */ -void brcms_c_scan_start(struct brcms_c_info *wlc) -{ - wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); -} + /* + * update antenna config due to + * wlc->stf->txant/txchain/ant_rx_ovr change + */ + brcms_c_stf_phy_txant_upd(wlc); -void brcms_c_scan_stop(struct brcms_c_info *wlc) -{ - wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); -} + /* attach each modules */ + err = brcms_c_attach_module(wlc); + if (err != 0) + goto fail; -void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) -{ - wlc->pub->associated = state; - wlc->bsscfg->associated = state; -} + if (!brcms_c_timers_init(wlc, unit)) { + wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, + __func__); + err = 32; + goto fail; + } -/* - * When a remote STA/AP is removed by Mac80211, or when it can no longer accept - * AMPDU traffic, packets pending in hardware have to be invalidated so that - * when later on hardware releases them, they can be handled appropriately. - */ -void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, - struct ieee80211_sta *sta, - void (*dma_callback_fn)) -{ - struct dma_pub *dmah; - int i; - for (i = 0; i < NFIFO; i++) { - dmah = hw->di[i]; - if (dmah != NULL) - dma_walk_packets(dmah, dma_callback_fn, sta); + /* depend on rateset, gmode */ + wlc->cmi = brcms_c_channel_mgr_attach(wlc); + if (!wlc->cmi) { + wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" + "\n", unit, __func__); + err = 33; + goto fail; } -} -int brcms_c_get_curband(struct brcms_c_info *wlc) -{ - return wlc->band->bandunit; -} + /* init default when all parameters are ready, i.e. ->rateset */ + brcms_c_bss_default_init(wlc); -void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) -{ - /* flush packet queue when requested */ - if (drop) - brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL); + /* + * Complete the wlc default state initializations.. + */ - /* wait for queue and DMA fifos to run dry */ - while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) - brcms_msleep(wlc->wl, 1); -} + /* allocate our initial queue */ + wlc->pkt_queue = brcms_c_txq_alloc(wlc); + if (wlc->pkt_queue == NULL) { + wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n", + unit, __func__); + err = 100; + goto fail; + } -void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) -{ - wlc->bcn_li_bcn = interval; - if (wlc->pub->up) - brcms_c_bcn_li_upd(wlc); -} + wlc->bsscfg->wlc = wlc; -int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) -{ - uint qdbm; + wlc->mimoft = FT_HT; + wlc->mimo_40txbw = AUTO; + wlc->ofdm_40txbw = AUTO; + wlc->cck_40txbw = AUTO; + brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); - /* Remove override bit and clip to max qdbm value */ - qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); - return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); -} + /* Set default values of SGI */ + if (BRCMS_SGI_CAP_PHY(wlc)) { + brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | + BRCMS_N_SGI_40)); + } else if (BRCMS_ISSSLPNPHY(wlc->band)) { + brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | + BRCMS_N_SGI_40)); + } else { + brcms_c_ht_update_sgi_rx(wlc, 0); + } -int brcms_c_get_tx_power(struct brcms_c_info *wlc) -{ - uint qdbm; - bool override; + /* initialize radio_mpc_disable according to wlc->mpc */ + brcms_c_radio_mpc_upd(wlc); + brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); - wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); + if (perr) + *perr = 0; - /* Return qdbm units */ - return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); -} + return wlc; -void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc) -{ - wlc->mpc = mpc; - brcms_c_radio_mpc_upd(wlc); + fail: + wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", + unit, __func__, err); + if (wlc) + brcms_c_detach(wlc); + + if (perr) + *perr = err; + return NULL; }