mac80211: fix a client mode reconnect issue
[openwrt.git] / package / mac80211 / patches / 869-brcmsmac-add-beacon-template-support.patch
1 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
2 +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
3 @@ -1,5 +1,6 @@
4  /*
5   * Copyright (c) 2010 Broadcom Corporation
6 + * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
7   *
8   * Permission to use, copy, modify, and/or distribute this software for any
9   * purpose with or without fee is hereby granted, provided that the above
10 @@ -522,9 +523,17 @@ brcms_ops_bss_info_changed(struct ieee80
11                 brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
12                 spin_unlock_bh(&wl->lock);
13         }
14 -       if (changed & BSS_CHANGED_BEACON)
15 +       if (changed & BSS_CHANGED_BEACON) {
16                 /* Beacon data changed, retrieve new beacon (beaconing modes) */
17 -               brcms_err(core, "%s: beacon changed\n", __func__);
18 +               struct sk_buff *beacon;
19 +               u16 tim_offset = 0;
20 +
21 +               spin_lock_bh(&wl->lock);
22 +               beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL);
23 +               brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset,
24 +                                      info->dtim_period);
25 +               spin_unlock_bh(&wl->lock);
26 +       }
27  
28         if (changed & BSS_CHANGED_BEACON_ENABLED) {
29                 /* Beaconing should be enabled/disabled (beaconing modes) */
30 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
31 +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
32 @@ -1,5 +1,6 @@
33  /*
34   * Copyright (c) 2010 Broadcom Corporation
35 + * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
36   *
37   * Permission to use, copy, modify, and/or distribute this software for any
38   * purpose with or without fee is hereby granted, provided that the above
39 @@ -450,6 +451,8 @@ static void brcms_c_detach_mfree(struct 
40         kfree(wlc->corestate);
41         kfree(wlc->hw->bandstate[0]);
42         kfree(wlc->hw);
43 +       if (wlc->beacon)
44 +               dev_kfree_skb_any(wlc->beacon);
45  
46         /* free the wlc */
47         kfree(wlc);
48 @@ -4086,10 +4089,14 @@ void brcms_c_wme_setparams(struct brcms_
49                                           *shm_entry++);
50         }
51  
52 -       if (suspend) {
53 +       if (suspend)
54                 brcms_c_suspend_mac_and_wait(wlc);
55 +
56 +       brcms_c_update_beacon(wlc);
57 +       brcms_c_update_probe_resp(wlc, false);
58 +
59 +       if (suspend)
60                 brcms_c_enable_mac(wlc);
61 -       }
62  }
63  
64  static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend)
65 @@ -7379,6 +7386,107 @@ int brcms_c_get_header_len(void)
66         return TXOFF;
67  }
68  
69 +static void brcms_c_beacon_write(struct brcms_c_info *wlc,
70 +                                struct sk_buff *beacon, u16 tim_offset,
71 +                                u16 dtim_period, bool bcn0, bool bcn1)
72 +{
73 +       size_t len;
74 +       struct ieee80211_tx_info *tx_info;
75 +       struct brcms_hardware *wlc_hw = wlc->hw;
76 +       struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw;
77 +
78 +       /* Get tx_info */
79 +       tx_info = IEEE80211_SKB_CB(beacon);
80 +
81 +       len = min_t(size_t, beacon->len, BCN_TMPL_LEN);
82 +       wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value;
83 +
84 +       brcms_c_compute_plcp(wlc, wlc->bcn_rspec,
85 +                            len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data);
86 +
87 +       /* "Regular" and 16 MBSS but not for 4 MBSS */
88 +       /* Update the phytxctl for the beacon based on the rspec */
89 +       brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
90 +
91 +       if (bcn0) {
92 +               /* write the probe response into the template region */
93 +               brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE,
94 +                                           (len + 3) & ~3, beacon->data);
95 +
96 +               /* write beacon length to SCR */
97 +               brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len);
98 +       }
99 +       if (bcn1) {
100 +               /* write the probe response into the template region */
101 +               brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE,
102 +                                           (len + 3) & ~3, beacon->data);
103 +
104 +               /* write beacon length to SCR */
105 +               brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len);
106 +       }
107 +
108 +       if (tim_offset != 0) {
109 +               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
110 +                                 tim_offset + D11B_PHY_HDR_LEN);
111 +               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period);
112 +       } else {
113 +               brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON,
114 +                                 len + D11B_PHY_HDR_LEN);
115 +               brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0);
116 +       }
117 +}
118 +
119 +static void brcms_c_update_beacon_hw(struct brcms_c_info *wlc,
120 +                                    struct sk_buff *beacon, u16 tim_offset,
121 +                                    u16 dtim_period)
122 +{
123 +       struct brcms_hardware *wlc_hw = wlc->hw;
124 +       struct bcma_device *core = wlc_hw->d11core;
125 +
126 +       /* Hardware beaconing for this config */
127 +       u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD;
128 +
129 +       /* Check if both templates are in use, if so sched. an interrupt
130 +        *      that will call back into this routine
131 +        */
132 +       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid)
133 +               /* clear any previous status */
134 +               bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL);
135 +
136 +       if (wlc->beacon_template_virgin) {
137 +               wlc->beacon_template_virgin = false;
138 +               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
139 +                                    true);
140 +               /* mark beacon0 valid */
141 +               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
142 +               return;
143 +       }
144 +
145 +       /* Check that after scheduling the interrupt both of the
146 +        *      templates are still busy. if not clear the int. & remask
147 +        */
148 +       if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) {
149 +               wlc->defmacintmask |= MI_BCNTPL;
150 +               return;
151 +       }
152 +
153 +       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) {
154 +               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true,
155 +                                    false);
156 +               /* mark beacon0 valid */
157 +               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD);
158 +               return;
159 +       }
160 +       if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) {
161 +               brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period,
162 +                                    false, true);
163 +               /* mark beacon0 valid */
164 +               bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD);
165 +               return;
166 +       }
167 +       return;
168 +}
169 +
170  /*
171   * Update all beacons for the system.
172   */
173 @@ -7387,9 +7495,31 @@ void brcms_c_update_beacon(struct brcms_
174         struct brcms_bss_cfg *bsscfg = wlc->bsscfg;
175  
176         if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP ||
177 -                            bsscfg->type == BRCMS_TYPE_ADHOC))
178 +                            bsscfg->type == BRCMS_TYPE_ADHOC)) {
179                 /* Clear the soft intmask */
180                 wlc->defmacintmask &= ~MI_BCNTPL;
181 +               if (!wlc->beacon)
182 +                       return;
183 +               brcms_c_update_beacon_hw(wlc, wlc->beacon,
184 +                                        wlc->beacon_tim_offset,
185 +                                        wlc->beacon_dtim_period);
186 +       }
187 +}
188 +
189 +void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon,
190 +                           u16 tim_offset, u16 dtim_period)
191 +{
192 +       if (!beacon)
193 +               return;
194 +       if (wlc->beacon)
195 +               dev_kfree_skb_any(wlc->beacon);
196 +       wlc->beacon = beacon;
197 +
198 +       /* add PLCP */
199 +       skb_push(wlc->beacon, D11_PHY_HDR_LEN);
200 +       wlc->beacon_tim_offset = tim_offset;
201 +       wlc->beacon_dtim_period = dtim_period;
202 +       brcms_c_update_beacon(wlc);
203  }
204  
205  /* Write ssid into shared memory */
206 @@ -7788,6 +7918,10 @@ bool brcms_c_dpc(struct brcms_c_info *wl
207                 brcms_rfkill_set_hw_state(wlc->wl);
208         }
209  
210 +       /* BCN template is available */
211 +       if (macintstatus & MI_BCNTPL)
212 +               brcms_c_update_beacon(wlc);
213 +
214         /* it isn't done and needs to be resched if macintstatus is non-zero */
215         return wlc->macintstatus != 0;
216  
217 @@ -7919,6 +8053,7 @@ brcms_c_attach(struct brcms_info *wl, st
218         pub->unit = unit;
219         pub->_piomode = piomode;
220         wlc->bandinit_pending = false;
221 +       wlc->beacon_template_virgin = true;
222  
223         /* populate struct brcms_c_info with default values  */
224         brcms_c_info_init(wlc, unit);
225 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h
226 +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h
227 @@ -492,6 +492,8 @@ struct brcms_c_info {
228         bool radio_monitor;
229         bool going_down;
230  
231 +       bool beacon_template_virgin;
232 +
233         struct brcms_timer *wdtimer;
234         struct brcms_timer *radio_timer;
235  
236 @@ -561,6 +563,10 @@ struct brcms_c_info {
237  
238         struct wiphy *wiphy;
239         struct scb pri_scb;
240 +
241 +       struct sk_buff *beacon;
242 +       u16 beacon_tim_offset;
243 +       u16 beacon_dtim_period;
244  };
245  
246  /* antsel module specific state */
247 @@ -630,7 +636,6 @@ extern u16 brcms_c_compute_rtscts_dur(st
248  extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw,
249                                struct ieee80211_sta *sta,
250                                void (*dma_callback_fn));
251 -extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
252  extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend);
253  extern int brcms_c_set_nmode(struct brcms_c_info *wlc);
254  extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc,
255 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h
256 +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h
257 @@ -332,5 +332,9 @@ extern bool brcms_c_check_radio_disabled
258  extern void brcms_c_mute(struct brcms_c_info *wlc, bool on);
259  extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc);
260  extern void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr);
261 +extern void brcms_c_update_beacon(struct brcms_c_info *wlc);
262 +extern void brcms_c_set_new_beacon(struct brcms_c_info *wlc,
263 +                                  struct sk_buff *beacon, u16 tim_offset,
264 +                                  u16 dtim_period);
265  
266  #endif                         /* _BRCM_PUB_H_ */