Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / brcm80211 / brcmsmac / main.c
index 8b5839008af32a11afef6d4042db67a0d8a9c16d..59d438409dfbe144c20dba2b71b8094859d97dd0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010 Broadcom Corporation
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #define DOT11_RTS_LEN                  16
 #define DOT11_CTS_LEN                  10
 #define DOT11_BA_BITMAP_LEN            128
-#define DOT11_MIN_BEACON_PERIOD                1
-#define DOT11_MAX_BEACON_PERIOD                0xFFFF
 #define DOT11_MAXNUMFRAGS              16
 #define DOT11_MAX_FRAG_LEN             2346
 
@@ -450,6 +449,10 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
        kfree(wlc->corestate);
        kfree(wlc->hw->bandstate[0]);
        kfree(wlc->hw);
+       if (wlc->beacon)
+               dev_kfree_skb_any(wlc->beacon);
+       if (wlc->probe_resp)
+               dev_kfree_skb_any(wlc->probe_resp);
 
        /* free the wlc */
        kfree(wlc);
@@ -1071,7 +1074,7 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
 
 static void brcms_c_tbtt(struct brcms_c_info *wlc)
 {
-       if (!wlc->bsscfg->BSS)
+       if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC)
                /*
                 * DirFrmQ is now valid...defer setting until end
                 * of ATIM window
@@ -2165,6 +2168,32 @@ void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode)
        }
 }
 
+void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr)
+{
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       wlc->bsscfg->type = BRCMS_TYPE_STATION;
+}
+
+void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid,
+                     u8 *ssid, size_t ssid_len)
+{
+       brcms_c_set_ssid(wlc, ssid, ssid_len);
+
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID));
+       wlc->bsscfg->type = BRCMS_TYPE_AP;
+
+       brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA);
+}
+
+void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr)
+{
+       memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
+       wlc->bsscfg->type = BRCMS_TYPE_ADHOC;
+
+       brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0);
+}
+
 /* Initialize GPIOs that are controlled by D11 core */
 static void brcms_c_gpio_init(struct brcms_c_info *wlc)
 {
@@ -2466,6 +2495,7 @@ static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
 static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
 {
        static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+       u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr;
 
        if (mute_tx) {
                /* suspend tx fifos */
@@ -2475,8 +2505,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
                brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO);
 
                /* zero the address match register so we do not send ACKs */
-               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
-                                      null_ether_addr);
+               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr);
        } else {
                /* resume tx fifos */
                brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO);
@@ -2485,8 +2514,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx)
                brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO);
 
                /* Restore address */
-               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
-                                      wlc_hw->etheraddr);
+               brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr);
        }
 
        wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0);
@@ -3046,8 +3074,6 @@ static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail)
  */
 static bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
 {
-       struct brcms_bss_cfg *cfg = wlc->bsscfg;
-
        /* disallow PS when one of the following global conditions meets */
        if (!wlc->pub->associated)
                return false;
@@ -3056,16 +3082,11 @@ static bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
        if (wlc->filter_flags & FIF_PROMISC_IN_BSS)
                return false;
 
-       if (cfg->associated) {
-               /*
-                * disallow PS when one of the following
-                * bsscfg specific conditions meets
-                */
-               if (!cfg->BSS)
-                       return false;
+       if (wlc->bsscfg->type == BRCMS_TYPE_AP)
+               return false;
 
+       if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC)
                return false;
-       }
 
        return true;
 }
@@ -3141,8 +3162,7 @@ void brcms_c_reset(struct brcms_c_info *wlc)
        brcms_c_statsupd(wlc);
 
        /* reset our snapshot of macstat counters */
-       memset((char *)wlc->core->macstat_snapshot, 0,
-               sizeof(struct macstat));
+       memset(wlc->core->macstat_snapshot, 0, sizeof(struct macstat));
 
        brcms_b_reset(wlc->hw);
 }
@@ -3775,7 +3795,7 @@ static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg)
        struct brcms_c_info *wlc = bsscfg->wlc;
 
        /* enter the MAC addr into the RXE match registers */
-       brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, bsscfg->cur_etheraddr);
+       brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, wlc->pub->cur_etheraddr);
 
        brcms_c_ampdu_macaddr_upd(wlc);
 
@@ -3791,6 +3811,15 @@ static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg)
        brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID);
 }
 
+void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len)
+{
+       u8 len = min_t(u8, sizeof(wlc->bsscfg->SSID), ssid_len);
+       memset(wlc->bsscfg->SSID, 0, sizeof(wlc->bsscfg->SSID));
+
+       memcpy(wlc->bsscfg->SSID, ssid, len);
+       wlc->bsscfg->SSID_len = len;
+}
+
 static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot)
 {
        wlc_hw->shortslot = shortslot;
@@ -3825,7 +3854,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec)
        if (wlc->home_chanspec != chanspec) {
                wlc->home_chanspec = chanspec;
 
-               if (wlc->bsscfg->associated)
+               if (wlc->pub->associated)
                        wlc->bsscfg->current_bss->chanspec = chanspec;
        }
 }
@@ -4055,7 +4084,7 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
                return;
        }
 
-       memset((char *)&acp_shm, 0, sizeof(struct shm_acparams));
+       memset(&acp_shm, 0, sizeof(struct shm_acparams));
        /* fill in shm ac params struct */
        acp_shm.txop = params->txop;
        /* convert from units of 32us to us for ucode */
@@ -4095,10 +4124,14 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci,
                                          *shm_entry++);
        }
 
-       if (suspend) {
+       if (suspend)
                brcms_c_suspend_mac_and_wait(wlc);
+
+       brcms_c_update_beacon(wlc);
+       brcms_c_update_probe_resp(wlc, false);
+
+       if (suspend)
                brcms_c_enable_mac(wlc);
-       }
 }
 
 static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend)
@@ -4336,7 +4369,6 @@ static void brcms_c_info_init(struct brcms_c_info *wlc, int unit)
 
        /* WME QoS mode is Auto by default */
        wlc->pub->_ampdu = AMPDU_AGG_HOST;
-       wlc->pub->bcmerror = 0;
 }
 
 static uint brcms_c_attach_module(struct brcms_c_info *wlc)
@@ -4771,7 +4803,7 @@ static void brcms_c_bss_default_init(struct brcms_c_info *wlc)
        struct brcms_bss_info *bi = wlc->default_bss;
 
        /* init default and target BSS with some sane initial values */
-       memset((char *)(bi), 0, sizeof(struct brcms_bss_info));
+       memset(bi, 0, sizeof(*bi));
        bi->beacon_period = BEACON_INTERVAL_DEFAULT;
 
        /* fill the default channel as the first valid channel
@@ -5076,8 +5108,8 @@ int brcms_c_up(struct brcms_c_info *wlc)
                                struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
                                mboolset(wlc->pub->radio_disabled,
                                         WL_RADIO_HW_DISABLE);
-
-                               if (bsscfg->enable && bsscfg->BSS)
+                               if (bsscfg->type == BRCMS_TYPE_STATION ||
+                                   bsscfg->type == BRCMS_TYPE_ADHOC)
                                        brcms_err(wlc->hw->d11core,
                                                  "wl%d: up: rfdisable -> "
                                                  "bsscfg_disable()\n",
@@ -5300,7 +5332,7 @@ int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config)
                brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode);
 
        /* Clear rateset override */
-       memset(&rs, 0, sizeof(struct brcms_c_rateset));
+       memset(&rs, 0, sizeof(rs));
 
        switch (gmode) {
        case GMODE_LEGACY_B:
@@ -5438,7 +5470,7 @@ static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc)
        u8 r;
        bool war = false;
 
-       if (wlc->bsscfg->associated)
+       if (wlc->pub->associated)
                r = wlc->bsscfg->current_bss->rateset.rates[0];
        else
                r = wlc->default_bss->rateset.rates[0];
@@ -5523,7 +5555,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
        if (rs->count > BRCMS_NUMRATES)
                return -ENOBUFS;
 
-       memset(&internal_rs, 0, sizeof(struct brcms_c_rateset));
+       memset(&internal_rs, 0, sizeof(internal_rs));
 
        /* Copy only legacy rateset section */
        internal_rs.count = rs->count;
@@ -5532,7 +5564,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
        /* merge rateset coming in with the current mcsset */
        if (wlc->pub->_n_enab & SUPPORT_11N) {
                struct brcms_bss_info *mcsset_bss;
-               if (wlc->bsscfg->associated)
+               if (wlc->pub->associated)
                        mcsset_bss = wlc->bsscfg->current_bss;
                else
                        mcsset_bss = wlc->default_bss;
@@ -5547,13 +5579,36 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
        return bcmerror;
 }
 
+static void brcms_c_time_lock(struct brcms_c_info *wlc)
+{
+       bcma_set32(wlc->hw->d11core, D11REGOFFS(maccontrol), MCTL_TBTTHOLD);
+       /* Commit the write */
+       bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol));
+}
+
+static void brcms_c_time_unlock(struct brcms_c_info *wlc)
+{
+       bcma_mask32(wlc->hw->d11core, D11REGOFFS(maccontrol), ~MCTL_TBTTHOLD);
+       /* Commit the write */
+       bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol));
+}
+
 int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period)
 {
-       if (period < DOT11_MIN_BEACON_PERIOD ||
-           period > DOT11_MAX_BEACON_PERIOD)
+       u32 bcnint_us;
+
+       if (period == 0)
                return -EINVAL;
 
        wlc->default_bss->beacon_period = period;
+
+       bcnint_us = period << 10;
+       brcms_c_time_lock(wlc);
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfprep),
+                    (bcnint_us << CFPREP_CBI_SHIFT));
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfpstart), bcnint_us);
+       brcms_c_time_unlock(wlc);
+
        return 0;
 }
 
@@ -5627,7 +5682,7 @@ int brcms_c_module_unregister(struct brcms_pub *pub, const char *name,
        for (i = 0; i < BRCMS_MAXMODULES; i++) {
                if (!strcmp(wlc->modulecb[i].name, name) &&
                    (wlc->modulecb[i].hdl == hdl)) {
-                       memset(&wlc->modulecb[i], 0, sizeof(struct modulecb));
+                       memset(&wlc->modulecb[i], 0, sizeof(wlc->modulecb[i]));
                        return 0;
                }
        }
@@ -6447,10 +6502,9 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
 
                        if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
                            && (!is_mcs_rate(rspec[k]))) {
-                               brcms_err(wlc->hw->d11core,
-                                         "wl%d: %s: IEEE80211_TX_"
-                                         "RC_MCS != is_mcs_rate(rspec)\n",
-                                         wlc->pub->unit, __func__);
+                               brcms_warn(wlc->hw->d11core,
+                                          "wl%d: %s: IEEE80211_TX_RC_MCS != is_mcs_rate(rspec)\n",
+                                          wlc->pub->unit, __func__);
                        }
 
                        if (is_mcs_rate(rspec[k])) {
@@ -6683,11 +6737,9 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
                                        (struct ofdm_phy_hdr *) rts_plcp) :
                                rts_plcp[0]) << 8;
        } else {
-               memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
-               memset((char *)&txh->rts_frame, 0,
-                       sizeof(struct ieee80211_rts));
-               memset((char *)txh->RTSPLCPFallback, 0,
-                     sizeof(txh->RTSPLCPFallback));
+               memset(txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
+               memset(&txh->rts_frame, 0, sizeof(struct ieee80211_rts));
+               memset(txh->RTSPLCPFallback, 0, sizeof(txh->RTSPLCPFallback));
                txh->RTSDurFallback = 0;
        }
 
@@ -6842,21 +6894,19 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw,
                                        wlc->fragthresh[queue] =
                                            (u16) newfragthresh;
                        } else {
-                               brcms_err(wlc->hw->d11core,
-                                         "wl%d: %s txop invalid "
-                                         "for rate %d\n",
-                                         wlc->pub->unit, fifo_names[queue],
-                                         rspec2rate(rspec[0]));
+                               brcms_warn(wlc->hw->d11core,
+                                          "wl%d: %s txop invalid for rate %d\n",
+                                          wlc->pub->unit, fifo_names[queue],
+                                          rspec2rate(rspec[0]));
                        }
 
                        if (dur > wlc->edcf_txop[ac])
-                               brcms_err(wlc->hw->d11core,
-                                         "wl%d: %s: %s txop "
-                                         "exceeded phylen %d/%d dur %d/%d\n",
-                                         wlc->pub->unit, __func__,
-                                         fifo_names[queue],
-                                         phylen, wlc->fragthresh[queue],
-                                         dur, wlc->edcf_txop[ac]);
+                               brcms_warn(wlc->hw->d11core,
+                                          "wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n",
+                                          wlc->pub->unit, __func__,
+                                          fifo_names[queue],
+                                          phylen, wlc->fragthresh[queue],
+                                          dur, wlc->edcf_txop[ac]);
                }
        }
 
@@ -7301,72 +7351,110 @@ brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len)
        }
 }
 
-/*     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.
- */
-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)
+int brcms_c_get_header_len(void)
 {
-       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;
-
-       hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN;
+       return TXOFF;
+}
 
-       /* calc buffer size provided for frame body */
-       body_len = *len - hdr_len;
-       /* return actual size */
-       *len = hdr_len + body_len;
+static void brcms_c_beacon_write(struct brcms_c_info *wlc,
+                                struct sk_buff *beacon, u16 tim_offset,
+                                u16 dtim_period, bool bcn0, bool bcn1)
+{
+       size_t len;
+       struct ieee80211_tx_info *tx_info;
+       struct brcms_hardware *wlc_hw = wlc->hw;
+       struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw;
 
-       /* format PHY and MAC headers */
-       memset((char *)buf, 0, hdr_len);
+       /* Get tx_info */
+       tx_info = IEEE80211_SKB_CB(beacon);
 
-       plcp = (struct cck_phy_hdr *) buf;
+       len = min_t(size_t, beacon->len, BCN_TMPL_LEN);
+       wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value;
 
-       /*
-        * 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);
+       brcms_c_compute_plcp(wlc, wlc->bcn_rspec,
+                            len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data);
 
        /* "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);
+       brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
 
-       h = (struct ieee80211_mgmt *)&plcp[1];
+       if (bcn0) {
+               /* write the probe response into the template region */
+               brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE,
+                                           (len + 3) & ~3, beacon->data);
 
-       /* fill in 802.11 header */
-       h->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | type);
+               /* write beacon length to SCR */
+               brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len);
+       }
+       if (bcn1) {
+               /* write the probe response into the template region */
+               brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE,
+                                           (len + 3) & ~3, beacon->data);
 
-       /* 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, &ether_bcast, ETH_ALEN);
-       memcpy(&h->sa, &cfg->cur_etheraddr, ETH_ALEN);
-       memcpy(&h->bssid, &cfg->BSSID, ETH_ALEN);
+               /* write beacon length to SCR */
+               brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len);
+       }
 
-       /* SEQ filled in by MAC */
+       if (tim_offset != 0) {
+               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
+                                 tim_offset + D11B_PHY_HDR_LEN);
+               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period);
+       } else {
+               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
+                                 len + D11B_PHY_HDR_LEN);
+               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0);
+       }
 }
 
-int brcms_c_get_header_len(void)
+static void brcms_c_update_beacon_hw(struct brcms_c_info *wlc,
+                                    struct sk_buff *beacon, u16 tim_offset,
+                                    u16 dtim_period)
 {
-       return TXOFF;
+       struct brcms_hardware *wlc_hw = wlc->hw;
+       struct bcma_device *core = wlc_hw->d11core;
+
+       /* Hardware beaconing for this config */
+       u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD;
+
+       /* Check if both templates are in use, if so sched. an interrupt
+        *      that will call back into this routine
+        */
+       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid)
+               /* clear any previous status */
+               bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL);
+
+       if (wlc->beacon_template_virgin) {
+               wlc->beacon_template_virgin = false;
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
+                                    true);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
+               return;
+       }
+
+       /* Check that after scheduling the interrupt both of the
+        *      templates are still busy. if not clear the int. & remask
+        */
+       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) {
+               wlc->defmacintmask |= MI_BCNTPL;
+               return;
+       }
+
+       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) {
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
+                                    false);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
+               return;
+       }
+       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) {
+               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period,
+                                    false, true);
+               /* mark beacon0 valid */
+               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD);
+               return;
+       }
+       return;
 }
 
 /*
@@ -7376,9 +7464,57 @@ void brcms_c_update_beacon(struct brcms_c_info *wlc)
 {
        struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
 
-       if (bsscfg->up && !bsscfg->BSS)
+       if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP ||
+                            bsscfg->type == BRCMS_TYPE_ADHOC)) {
                /* Clear the soft intmask */
                wlc->defmacintmask &= ~MI_BCNTPL;
+               if (!wlc->beacon)
+                       return;
+               brcms_c_update_beacon_hw(wlc, wlc->beacon,
+                                        wlc->beacon_tim_offset,
+                                        wlc->beacon_dtim_period);
+       }
+}
+
+void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon,
+                           u16 tim_offset, u16 dtim_period)
+{
+       if (!beacon)
+               return;
+       if (wlc->beacon)
+               dev_kfree_skb_any(wlc->beacon);
+       wlc->beacon = beacon;
+
+       /* add PLCP */
+       skb_push(wlc->beacon, D11_PHY_HDR_LEN);
+       wlc->beacon_tim_offset = tim_offset;
+       wlc->beacon_dtim_period = dtim_period;
+       brcms_c_update_beacon(wlc);
+}
+
+void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc,
+                               struct sk_buff *probe_resp)
+{
+       if (!probe_resp)
+               return;
+       if (wlc->probe_resp)
+               dev_kfree_skb_any(wlc->probe_resp);
+       wlc->probe_resp = probe_resp;
+
+       /* add PLCP */
+       skb_push(wlc->probe_resp, D11_PHY_HDR_LEN);
+       brcms_c_update_probe_resp(wlc, false);
+}
+
+void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable)
+{
+       /*
+        * prevent ucode from sending probe responses by setting the timeout
+        * to 1, it can not send it in that time frame.
+        */
+       wlc->prb_resp_timeout = enable ? BRCMS_PRB_RESP_TIMEOUT : 1;
+       brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+       /* TODO: if (enable) => also deactivate receiving of probe request */
 }
 
 /* Write ssid into shared memory */
@@ -7400,26 +7536,19 @@ brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg)
 static void
 brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc,
                              struct brcms_bss_cfg *cfg,
+                             struct sk_buff *probe_resp,
                              bool suspend)
 {
-       u16 prb_resp[BCN_TMPL_LEN / 2];
-       int len = BCN_TMPL_LEN;
-
-       /*
-        * write the probe response to hardware, or save in
-        * the config structure
-        */
+       int len;
 
-       /* create the probe response template */
-       brcms_c_bcn_prb_template(wlc, IEEE80211_STYPE_PROBE_RESP, 0,
-                                cfg, prb_resp, &len);
+       len = min_t(size_t, probe_resp->len, BCN_TMPL_LEN);
 
        if (suspend)
                brcms_c_suspend_mac_and_wait(wlc);
 
        /* write the probe response into the template region */
        brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE,
-                                   (len + 3) & ~3, prb_resp);
+                                   (len + 3) & ~3, probe_resp->data);
 
        /* write the length of the probe response frame (+PLCP/-FCS) */
        brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len);
@@ -7433,8 +7562,8 @@ brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc,
         * 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);
+       brcms_c_mod_prb_rsp_rate_table(wlc,
+                                     (u16)len + FCS_LEN - D11_PHY_HDR_LEN);
 
        if (suspend)
                brcms_c_enable_mac(wlc);
@@ -7445,8 +7574,13 @@ void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend)
        struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
 
        /* update AP or IBSS probe responses */
-       if (bsscfg->up && !bsscfg->BSS)
-               brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend);
+       if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP ||
+                            bsscfg->type == BRCMS_TYPE_ADHOC)) {
+               if (!wlc->probe_resp)
+                       return;
+               brcms_c_bss_update_probe_resp(wlc, bsscfg, wlc->probe_resp,
+                                             suspend);
+       }
 }
 
 int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
@@ -7485,7 +7619,6 @@ void brcms_c_scan_stop(struct brcms_c_info *wlc)
 void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state)
 {
        wlc->pub->associated = state;
-       wlc->bsscfg->associated = state;
 }
 
 /*
@@ -7530,6 +7663,36 @@ void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval)
                brcms_c_bcn_li_upd(wlc);
 }
 
+u64 brcms_c_tsf_get(struct brcms_c_info *wlc)
+{
+       u32 tsf_h, tsf_l;
+       u64 tsf;
+
+       brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h);
+
+       tsf = tsf_h;
+       tsf <<= 32;
+       tsf |= tsf_l;
+
+       return tsf;
+}
+
+void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf)
+{
+       u32 tsf_h, tsf_l;
+
+       brcms_c_time_lock(wlc);
+
+       tsf_l = tsf;
+       tsf_h = (tsf >> 32);
+
+       /* read the tsf timer low, then high to get an atomic read */
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerlow), tsf_l);
+       bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerhigh), tsf_h);
+
+       brcms_c_time_unlock(wlc);
+}
+
 int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr)
 {
        uint qdbm;
@@ -7617,7 +7780,7 @@ brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound)
 
        uint n = 0;
        uint bound_limit = bound ? RXBND : -1;
-       bool morepending;
+       bool morepending = false;
 
        skb_queue_head_init(&recv_frames);
 
@@ -7741,6 +7904,10 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded)
                brcms_rfkill_set_hw_state(wlc->wl);
        }
 
+       /* BCN template is available */
+       if (macintstatus & MI_BCNTPL)
+               brcms_c_update_beacon(wlc);
+
        /* it isn't done and needs to be resched if macintstatus is non-zero */
        return wlc->macintstatus != 0;
 
@@ -7769,7 +7936,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx)
        brcms_c_set_bssid(wlc->bsscfg);
 
        /* Update tsf_cfprep if associated and up */
-       if (wlc->pub->associated && wlc->bsscfg->up) {
+       if (wlc->pub->associated && wlc->pub->up) {
                u32 bi;
 
                /* get beacon period and convert to uS */
@@ -7814,9 +7981,14 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx)
 
        /* 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);
+               u16 rev;
+               u16 patch;
+
+               rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR);
+               patch = brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR);
+               wlc->ucode_rev = (rev << NBITS(u16)) | patch;
+               snprintf(wlc->wiphy->fw_version,
+                        sizeof(wlc->wiphy->fw_version), "%u.%u", rev, patch);
        }
 
        /* ..now really unleash hell (allow the MAC out of suspend) */
@@ -7872,6 +8044,7 @@ brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit,
        pub->unit = unit;
        pub->_piomode = piomode;
        wlc->bandinit_pending = false;
+       wlc->beacon_template_virgin = true;
 
        /* populate struct brcms_c_info with default values  */
        brcms_c_info_init(wlc, unit);