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 8ef02dca8f8cab02eca81a71de91bac67612451b..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
@@ -448,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);
@@ -1069,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
@@ -2163,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)
 {
@@ -3043,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;
@@ -3053,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;
 }
@@ -3771,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);
 
@@ -3787,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;
@@ -3821,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;
        }
 }
@@ -4091,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)
@@ -4332,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)
@@ -5072,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",
@@ -5434,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];
@@ -5528,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;
@@ -5543,12 +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)
 {
+       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;
 }
 
@@ -7291,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(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;
 }
 
 /*
@@ -7366,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 */
@@ -7390,30 +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;
-       int len = BCN_TMPL_LEN;
-
-       prb_resp = kmalloc(BCN_TMPL_LEN, GFP_ATOMIC);
-       if (!prb_resp)
-               return;
-
-       /*
-        * 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);
@@ -7427,13 +7562,11 @@ 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);
-
-       kfree(prb_resp);
 }
 
 void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend)
@@ -7441,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,
@@ -7481,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;
 }
 
 /*
@@ -7526,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;
@@ -7737,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;
 
@@ -7765,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 */
@@ -7810,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) */
@@ -7868,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);