1fb9987d0SSujith /*
25b68138eSSujith Manoharan * Copyright (c) 2010-2011 Atheros Communications Inc.
3fb9987d0SSujith *
4fb9987d0SSujith * Permission to use, copy, modify, and/or distribute this software for any
5fb9987d0SSujith * purpose with or without fee is hereby granted, provided that the above
6fb9987d0SSujith * copyright notice and this permission notice appear in all copies.
7fb9987d0SSujith *
8fb9987d0SSujith * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9fb9987d0SSujith * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10fb9987d0SSujith * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11fb9987d0SSujith * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12fb9987d0SSujith * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13fb9987d0SSujith * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14fb9987d0SSujith * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15fb9987d0SSujith */
16fb9987d0SSujith
17fb9987d0SSujith #include "htc.h"
18fb9987d0SSujith
19fb9987d0SSujith #define FUDGE 2
20fb9987d0SSujith
ath9k_htc_beaconq_config(struct ath9k_htc_priv * priv)212493a547SSujith Manoharan void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
222493a547SSujith Manoharan {
232493a547SSujith Manoharan struct ath_hw *ah = priv->ah;
242493a547SSujith Manoharan struct ath9k_tx_queue_info qi, qi_be;
252493a547SSujith Manoharan
262493a547SSujith Manoharan memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
272493a547SSujith Manoharan memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info));
282493a547SSujith Manoharan
29a099874eSOleksij Rempel ath9k_hw_get_txq_props(ah, priv->beacon.beaconq, &qi);
302493a547SSujith Manoharan
31594e65b6SJavier Cardona if (priv->ah->opmode == NL80211_IFTYPE_AP ||
32594e65b6SJavier Cardona priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
332493a547SSujith Manoharan qi.tqi_aifs = 1;
342493a547SSujith Manoharan qi.tqi_cwmin = 0;
352493a547SSujith Manoharan qi.tqi_cwmax = 0;
362493a547SSujith Manoharan } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) {
37bea843c7SSujith Manoharan int qnum = priv->hwq_map[IEEE80211_AC_BE];
382493a547SSujith Manoharan
392493a547SSujith Manoharan ath9k_hw_get_txq_props(ah, qnum, &qi_be);
402493a547SSujith Manoharan
412493a547SSujith Manoharan qi.tqi_aifs = qi_be.tqi_aifs;
422493a547SSujith Manoharan
432493a547SSujith Manoharan /*
442493a547SSujith Manoharan * For WIFI Beacon Distribution
452493a547SSujith Manoharan * Long slot time : 2x cwmin
462493a547SSujith Manoharan * Short slot time : 4x cwmin
472493a547SSujith Manoharan */
4811b0ac2eSBenjamin Berg if (ah->slottime == 20)
492493a547SSujith Manoharan qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
502493a547SSujith Manoharan else
512493a547SSujith Manoharan qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
522493a547SSujith Manoharan
532493a547SSujith Manoharan qi.tqi_cwmax = qi_be.tqi_cwmax;
542493a547SSujith Manoharan
552493a547SSujith Manoharan }
562493a547SSujith Manoharan
57a099874eSOleksij Rempel if (!ath9k_hw_set_txq_props(ah, priv->beacon.beaconq, &qi)) {
582493a547SSujith Manoharan ath_err(ath9k_hw_common(ah),
59a099874eSOleksij Rempel "Unable to update beacon queue %u!\n", priv->beacon.beaconq);
602493a547SSujith Manoharan } else {
61a099874eSOleksij Rempel ath9k_hw_resettxqueue(ah, priv->beacon.beaconq);
622493a547SSujith Manoharan }
632493a547SSujith Manoharan }
642493a547SSujith Manoharan
657f5c4c83SOleksij Rempel /*
667f5c4c83SOleksij Rempel * Both nexttbtt and intval have to be in usecs.
677f5c4c83SOleksij Rempel */
ath9k_htc_beacon_init(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf,bool reset_tsf)687f5c4c83SOleksij Rempel static void ath9k_htc_beacon_init(struct ath9k_htc_priv *priv,
697f5c4c83SOleksij Rempel struct ath_beacon_config *conf,
707f5c4c83SOleksij Rempel bool reset_tsf)
717f5c4c83SOleksij Rempel {
727f5c4c83SOleksij Rempel struct ath_hw *ah = priv->ah;
737f5c4c83SOleksij Rempel int ret __attribute__ ((unused));
747f5c4c83SOleksij Rempel __be32 htc_imask = 0;
757f5c4c83SOleksij Rempel u8 cmd_rsp;
767f5c4c83SOleksij Rempel
775f667642SOleksij Rempel if (conf->intval >= TU_TO_USEC(DEFAULT_SWBA_RESPONSE))
785f667642SOleksij Rempel ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE;
795f667642SOleksij Rempel else
805f667642SOleksij Rempel ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE;
815f667642SOleksij Rempel
827f5c4c83SOleksij Rempel WMI_CMD(WMI_DISABLE_INTR_CMDID);
837f5c4c83SOleksij Rempel if (reset_tsf)
847f5c4c83SOleksij Rempel ath9k_hw_reset_tsf(ah);
857f5c4c83SOleksij Rempel ath9k_htc_beaconq_config(priv);
867f5c4c83SOleksij Rempel ath9k_hw_beaconinit(ah, conf->nexttbtt, conf->intval);
877f5c4c83SOleksij Rempel priv->beacon.bmisscnt = 0;
887f5c4c83SOleksij Rempel htc_imask = cpu_to_be32(ah->imask);
897f5c4c83SOleksij Rempel WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
907f5c4c83SOleksij Rempel }
917f5c4c83SOleksij Rempel
ath9k_htc_beacon_config_sta(struct ath9k_htc_priv * priv,struct ath_beacon_config * bss_conf)92fb9987d0SSujith static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
933c4816d9SOleksij Rempel struct ath_beacon_config *bss_conf)
94fb9987d0SSujith {
95fb9987d0SSujith struct ath9k_beacon_state bs;
96fb9987d0SSujith enum ath9k_int imask = 0;
977f1f5a00SSujith __be32 htc_imask = 0;
980ff2b5c0SSujith Manoharan int ret __attribute__ ((unused));
99fb9987d0SSujith u8 cmd_rsp;
100fb9987d0SSujith
101f8422440SOleksij Rempel if (ath9k_cmn_beacon_config_sta(priv->ah, bss_conf, &bs) == -EPERM)
102f8422440SOleksij Rempel return;
103fb9987d0SSujith
104fb9987d0SSujith WMI_CMD(WMI_DISABLE_INTR_CMDID);
105fb9987d0SSujith ath9k_hw_set_sta_beacon_timers(priv->ah, &bs);
106fb9987d0SSujith imask |= ATH9K_INT_BMISS;
107fb9987d0SSujith htc_imask = cpu_to_be32(imask);
108fb9987d0SSujith WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
109fb9987d0SSujith }
110fb9987d0SSujith
ath9k_htc_beacon_config_ap(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf)111a5fae37dSSujith Manoharan static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv,
1124b2d841fSOleksij Rempel struct ath_beacon_config *conf)
113a5fae37dSSujith Manoharan {
1144b2d841fSOleksij Rempel struct ath_hw *ah = priv->ah;
1154b2d841fSOleksij Rempel ah->imask = 0;
116a5fae37dSSujith Manoharan
1174b2d841fSOleksij Rempel ath9k_cmn_beacon_config_ap(ah, conf, ATH9K_HTC_MAX_BCN_VIF);
1184b2d841fSOleksij Rempel ath9k_htc_beacon_init(priv, conf, false);
119a5fae37dSSujith Manoharan }
120a5fae37dSSujith Manoharan
ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv * priv,struct ath_beacon_config * conf)121fb9987d0SSujith static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv,
12212f53c30SOleksij Rempel struct ath_beacon_config *conf)
123fb9987d0SSujith {
124f7197924SOleksij Rempel struct ath_hw *ah = priv->ah;
12512f53c30SOleksij Rempel ah->imask = 0;
126fb9987d0SSujith
12712f53c30SOleksij Rempel ath9k_cmn_beacon_config_adhoc(ah, conf);
12812f53c30SOleksij Rempel ath9k_htc_beacon_init(priv, conf, conf->ibss_creator);
129fb9987d0SSujith }
130fb9987d0SSujith
ath9k_htc_beaconep(void * drv_priv,struct sk_buff * skb,enum htc_endpoint_id ep_id,bool txok)1319c6dda4eSSujith void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
1329c6dda4eSSujith enum htc_endpoint_id ep_id, bool txok)
133fb9987d0SSujith {
1349c6dda4eSSujith dev_kfree_skb_any(skb);
135fb9987d0SSujith }
136fb9987d0SSujith
ath9k_htc_send_buffered(struct ath9k_htc_priv * priv,int slot)1377d547eb4SSujith Manoharan static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
1387d547eb4SSujith Manoharan int slot)
1397d547eb4SSujith Manoharan {
1407d547eb4SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
1417d547eb4SSujith Manoharan struct ieee80211_vif *vif;
1427d547eb4SSujith Manoharan struct sk_buff *skb;
1437d547eb4SSujith Manoharan struct ieee80211_hdr *hdr;
1442c5d57f0SSujith Manoharan int padpos, padsize, ret, tx_slot;
1457d547eb4SSujith Manoharan
1467d547eb4SSujith Manoharan spin_lock_bh(&priv->beacon_lock);
1477d547eb4SSujith Manoharan
1483c4816d9SOleksij Rempel vif = priv->beacon.bslot[slot];
1497d547eb4SSujith Manoharan
1507d547eb4SSujith Manoharan skb = ieee80211_get_buffered_bc(priv->hw, vif);
1517d547eb4SSujith Manoharan
1527d547eb4SSujith Manoharan while(skb) {
1537d547eb4SSujith Manoharan hdr = (struct ieee80211_hdr *) skb->data;
1547d547eb4SSujith Manoharan
155c60c9929SFelix Fietkau padpos = ieee80211_hdrlen(hdr->frame_control);
1567d547eb4SSujith Manoharan padsize = padpos & 3;
1577d547eb4SSujith Manoharan if (padsize && skb->len > padpos) {
1587d547eb4SSujith Manoharan if (skb_headroom(skb) < padsize) {
1597d547eb4SSujith Manoharan dev_kfree_skb_any(skb);
1607d547eb4SSujith Manoharan goto next;
1617d547eb4SSujith Manoharan }
1627d547eb4SSujith Manoharan skb_push(skb, padsize);
1637d547eb4SSujith Manoharan memmove(skb->data, skb->data + padsize, padpos);
1647d547eb4SSujith Manoharan }
1657d547eb4SSujith Manoharan
1662c5d57f0SSujith Manoharan tx_slot = ath9k_htc_tx_get_slot(priv);
167cea3235cSRajkumar Manoharan if (tx_slot < 0) {
168d2182b69SJoe Perches ath_dbg(common, XMIT, "No free CAB slot\n");
1697d547eb4SSujith Manoharan dev_kfree_skb_any(skb);
1702c5d57f0SSujith Manoharan goto next;
1712c5d57f0SSujith Manoharan }
1722c5d57f0SSujith Manoharan
17336323f81SThomas Huehn ret = ath9k_htc_tx_start(priv, NULL, skb, tx_slot, true);
1742c5d57f0SSujith Manoharan if (ret != 0) {
1752c5d57f0SSujith Manoharan ath9k_htc_tx_clear_slot(priv, tx_slot);
1762c5d57f0SSujith Manoharan dev_kfree_skb_any(skb);
1772c5d57f0SSujith Manoharan
178d2182b69SJoe Perches ath_dbg(common, XMIT, "Failed to send CAB frame\n");
1798e86a547SSujith Manoharan } else {
1808e86a547SSujith Manoharan spin_lock_bh(&priv->tx.tx_lock);
1818e86a547SSujith Manoharan priv->tx.queued_cnt++;
1828e86a547SSujith Manoharan spin_unlock_bh(&priv->tx.tx_lock);
1837d547eb4SSujith Manoharan }
1847d547eb4SSujith Manoharan next:
1857d547eb4SSujith Manoharan skb = ieee80211_get_buffered_bc(priv->hw, vif);
1867d547eb4SSujith Manoharan }
1877d547eb4SSujith Manoharan
1887d547eb4SSujith Manoharan spin_unlock_bh(&priv->beacon_lock);
1897d547eb4SSujith Manoharan }
1907d547eb4SSujith Manoharan
ath9k_htc_send_beacon(struct ath9k_htc_priv * priv,int slot)191832f6a18SSujith Manoharan static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
192832f6a18SSujith Manoharan int slot)
193fb9987d0SSujith {
194b0a6ba98SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
195832f6a18SSujith Manoharan struct ieee80211_vif *vif;
196832f6a18SSujith Manoharan struct ath9k_htc_vif *avp;
197fb9987d0SSujith struct tx_beacon_header beacon_hdr;
19840dc9e4bSSujith Manoharan struct ath9k_htc_tx_ctl *tx_ctl;
199fb9987d0SSujith struct ieee80211_tx_info *info;
2009b674a02SSujith Manoharan struct ieee80211_mgmt *mgmt;
2019c6dda4eSSujith struct sk_buff *beacon;
202fb9987d0SSujith u8 *tx_fhdr;
203b0a6ba98SSujith Manoharan int ret;
204fb9987d0SSujith
205fb9987d0SSujith memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header));
206fb9987d0SSujith
207fb9987d0SSujith spin_lock_bh(&priv->beacon_lock);
208fb9987d0SSujith
2093c4816d9SOleksij Rempel vif = priv->beacon.bslot[slot];
210832f6a18SSujith Manoharan avp = (struct ath9k_htc_vif *)vif->drv_priv;
211832f6a18SSujith Manoharan
21292c3f7efSOleksij Rempel if (unlikely(test_bit(ATH_OP_SCANNING, &common->op_flags))) {
213fb9987d0SSujith spin_unlock_bh(&priv->beacon_lock);
214fb9987d0SSujith return;
215fb9987d0SSujith }
216fb9987d0SSujith
217fb9987d0SSujith /* Get a new beacon */
218*6e8912a5SShaul Triebitz beacon = ieee80211_beacon_get(priv->hw, vif, 0);
2199c6dda4eSSujith if (!beacon) {
220fb9987d0SSujith spin_unlock_bh(&priv->beacon_lock);
221fb9987d0SSujith return;
222fb9987d0SSujith }
223fb9987d0SSujith
2249b674a02SSujith Manoharan /*
2259b674a02SSujith Manoharan * Update the TSF adjust value here, the HW will
2269b674a02SSujith Manoharan * add this value for every beacon.
2279b674a02SSujith Manoharan */
2289b674a02SSujith Manoharan mgmt = (struct ieee80211_mgmt *)beacon->data;
2299b674a02SSujith Manoharan mgmt->u.beacon.timestamp = avp->tsfadjust;
2309b674a02SSujith Manoharan
2319c6dda4eSSujith info = IEEE80211_SKB_CB(beacon);
232fb9987d0SSujith if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
233fb9987d0SSujith struct ieee80211_hdr *hdr =
2349c6dda4eSSujith (struct ieee80211_hdr *) beacon->data;
2359a3d025bSSujith Manoharan avp->seq_no += 0x10;
236fb9987d0SSujith hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
2379a3d025bSSujith Manoharan hdr->seq_ctrl |= cpu_to_le16(avp->seq_no);
238fb9987d0SSujith }
239fb9987d0SSujith
24040dc9e4bSSujith Manoharan tx_ctl = HTC_SKB_CB(beacon);
24140dc9e4bSSujith Manoharan memset(tx_ctl, 0, sizeof(*tx_ctl));
24240dc9e4bSSujith Manoharan
24340dc9e4bSSujith Manoharan tx_ctl->type = ATH9K_HTC_BEACON;
244d67ee533SSujith Manoharan tx_ctl->epid = priv->beacon_ep;
24540dc9e4bSSujith Manoharan
246fb9987d0SSujith beacon_hdr.vif_index = avp->index;
2479c6dda4eSSujith tx_fhdr = skb_push(beacon, sizeof(beacon_hdr));
248fb9987d0SSujith memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr));
249fb9987d0SSujith
250d67ee533SSujith Manoharan ret = htc_send(priv->htc, beacon);
251b0a6ba98SSujith Manoharan if (ret != 0) {
252b0a6ba98SSujith Manoharan if (ret == -ENOMEM) {
253d2182b69SJoe Perches ath_dbg(common, BSTUCK,
254b0a6ba98SSujith Manoharan "Failed to send beacon, no free TX buffer\n");
255b0a6ba98SSujith Manoharan }
256b0a6ba98SSujith Manoharan dev_kfree_skb_any(beacon);
257b0a6ba98SSujith Manoharan }
258fb9987d0SSujith
259fb9987d0SSujith spin_unlock_bh(&priv->beacon_lock);
260f0e44962SChun-Yeow Yeoh
261f0e44962SChun-Yeow Yeoh ath9k_htc_csa_is_finished(priv);
262fb9987d0SSujith }
263fb9987d0SSujith
ath9k_htc_choose_bslot(struct ath9k_htc_priv * priv,struct wmi_event_swba * swba)264f4c88991SSujith Manoharan static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv,
265f4c88991SSujith Manoharan struct wmi_event_swba *swba)
266832f6a18SSujith Manoharan {
267832f6a18SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
268832f6a18SSujith Manoharan u64 tsf;
269832f6a18SSujith Manoharan u32 tsftu;
270832f6a18SSujith Manoharan u16 intval;
271832f6a18SSujith Manoharan int slot;
272832f6a18SSujith Manoharan
273f29f5c08SRajkumar Manoharan intval = priv->cur_beacon_conf.beacon_interval;
274832f6a18SSujith Manoharan
275f4c88991SSujith Manoharan tsf = be64_to_cpu(swba->tsf);
276832f6a18SSujith Manoharan tsftu = TSF_TO_TU(tsf >> 32, tsf);
277832f6a18SSujith Manoharan slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
278832f6a18SSujith Manoharan slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
279832f6a18SSujith Manoharan
280d2182b69SJoe Perches ath_dbg(common, BEACON,
281832f6a18SSujith Manoharan "Choose slot: %d, tsf: %llu, tsftu: %u, intval: %u\n",
282832f6a18SSujith Manoharan slot, tsf, tsftu, intval);
283832f6a18SSujith Manoharan
284832f6a18SSujith Manoharan return slot;
285832f6a18SSujith Manoharan }
286832f6a18SSujith Manoharan
ath9k_htc_swba(struct ath9k_htc_priv * priv,struct wmi_event_swba * swba)287f4c88991SSujith Manoharan void ath9k_htc_swba(struct ath9k_htc_priv *priv,
288f4c88991SSujith Manoharan struct wmi_event_swba *swba)
289832f6a18SSujith Manoharan {
290832f6a18SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
291832f6a18SSujith Manoharan int slot;
292832f6a18SSujith Manoharan
293f4c88991SSujith Manoharan if (swba->beacon_pending != 0) {
2943c4816d9SOleksij Rempel priv->beacon.bmisscnt++;
2953c4816d9SOleksij Rempel if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
296d2182b69SJoe Perches ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n");
297f4c88991SSujith Manoharan ieee80211_queue_work(priv->hw,
298f4c88991SSujith Manoharan &priv->fatal_work);
299832f6a18SSujith Manoharan }
300832f6a18SSujith Manoharan return;
301832f6a18SSujith Manoharan }
302832f6a18SSujith Manoharan
3033c4816d9SOleksij Rempel if (priv->beacon.bmisscnt) {
304d2182b69SJoe Perches ath_dbg(common, BSTUCK,
305832f6a18SSujith Manoharan "Resuming beacon xmit after %u misses\n",
3063c4816d9SOleksij Rempel priv->beacon.bmisscnt);
3073c4816d9SOleksij Rempel priv->beacon.bmisscnt = 0;
308832f6a18SSujith Manoharan }
309832f6a18SSujith Manoharan
310f4c88991SSujith Manoharan slot = ath9k_htc_choose_bslot(priv, swba);
311832f6a18SSujith Manoharan spin_lock_bh(&priv->beacon_lock);
3123c4816d9SOleksij Rempel if (priv->beacon.bslot[slot] == NULL) {
313832f6a18SSujith Manoharan spin_unlock_bh(&priv->beacon_lock);
314832f6a18SSujith Manoharan return;
315832f6a18SSujith Manoharan }
316832f6a18SSujith Manoharan spin_unlock_bh(&priv->beacon_lock);
317832f6a18SSujith Manoharan
3187d547eb4SSujith Manoharan ath9k_htc_send_buffered(priv, slot);
319832f6a18SSujith Manoharan ath9k_htc_send_beacon(priv, slot);
320832f6a18SSujith Manoharan }
321832f6a18SSujith Manoharan
ath9k_htc_assign_bslot(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)322832f6a18SSujith Manoharan void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv,
323832f6a18SSujith Manoharan struct ieee80211_vif *vif)
324832f6a18SSujith Manoharan {
325832f6a18SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
326832f6a18SSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
327832f6a18SSujith Manoharan int i = 0;
328832f6a18SSujith Manoharan
329832f6a18SSujith Manoharan spin_lock_bh(&priv->beacon_lock);
330832f6a18SSujith Manoharan for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) {
3313c4816d9SOleksij Rempel if (priv->beacon.bslot[i] == NULL) {
332832f6a18SSujith Manoharan avp->bslot = i;
333832f6a18SSujith Manoharan break;
334832f6a18SSujith Manoharan }
335832f6a18SSujith Manoharan }
336832f6a18SSujith Manoharan
3373c4816d9SOleksij Rempel priv->beacon.bslot[avp->bslot] = vif;
338832f6a18SSujith Manoharan spin_unlock_bh(&priv->beacon_lock);
339832f6a18SSujith Manoharan
340d2182b69SJoe Perches ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
341d2182b69SJoe Perches avp->bslot);
342832f6a18SSujith Manoharan }
343832f6a18SSujith Manoharan
ath9k_htc_remove_bslot(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)344832f6a18SSujith Manoharan void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv,
345832f6a18SSujith Manoharan struct ieee80211_vif *vif)
346832f6a18SSujith Manoharan {
347832f6a18SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
348832f6a18SSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
349832f6a18SSujith Manoharan
350832f6a18SSujith Manoharan spin_lock_bh(&priv->beacon_lock);
3513c4816d9SOleksij Rempel priv->beacon.bslot[avp->bslot] = NULL;
352832f6a18SSujith Manoharan spin_unlock_bh(&priv->beacon_lock);
353832f6a18SSujith Manoharan
354d2182b69SJoe Perches ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n",
355d2182b69SJoe Perches avp->bslot);
356832f6a18SSujith Manoharan }
357832f6a18SSujith Manoharan
3589b674a02SSujith Manoharan /*
3599b674a02SSujith Manoharan * Calculate the TSF adjustment value for all slots
3609b674a02SSujith Manoharan * other than zero.
3619b674a02SSujith Manoharan */
ath9k_htc_set_tsfadjust(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)3629b674a02SSujith Manoharan void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv,
3639b674a02SSujith Manoharan struct ieee80211_vif *vif)
3649b674a02SSujith Manoharan {
3659b674a02SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
3669b674a02SSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv;
3673c4816d9SOleksij Rempel struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
3689b674a02SSujith Manoharan u64 tsfadjust;
3699b674a02SSujith Manoharan
3709b674a02SSujith Manoharan if (avp->bslot == 0)
3719b674a02SSujith Manoharan return;
3729b674a02SSujith Manoharan
3739b674a02SSujith Manoharan /*
3749b674a02SSujith Manoharan * The beacon interval cannot be different for multi-AP mode,
3759b674a02SSujith Manoharan * and we reach here only for VIF slots greater than zero,
3769b674a02SSujith Manoharan * so beacon_interval is guaranteed to be set in cur_conf.
3779b674a02SSujith Manoharan */
3789b674a02SSujith Manoharan tsfadjust = cur_conf->beacon_interval * avp->bslot / ATH9K_HTC_MAX_BCN_VIF;
3799b674a02SSujith Manoharan avp->tsfadjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
3809b674a02SSujith Manoharan
381d2182b69SJoe Perches ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n",
3829b674a02SSujith Manoharan (unsigned long long)tsfadjust, avp->bslot);
3839b674a02SSujith Manoharan }
3849b674a02SSujith Manoharan
ath9k_htc_beacon_iter(void * data,u8 * mac,struct ieee80211_vif * vif)385e7a2a4f5SSujith Manoharan static void ath9k_htc_beacon_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
386e7a2a4f5SSujith Manoharan {
38750c8cd44SHimanshu Jha bool *beacon_configured = data;
388e7a2a4f5SSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
389e7a2a4f5SSujith Manoharan
390e7a2a4f5SSujith Manoharan if (vif->type == NL80211_IFTYPE_STATION &&
391e7a2a4f5SSujith Manoharan avp->beacon_configured)
392e7a2a4f5SSujith Manoharan *beacon_configured = true;
393e7a2a4f5SSujith Manoharan }
394e7a2a4f5SSujith Manoharan
ath9k_htc_check_beacon_config(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)395e7a2a4f5SSujith Manoharan static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv,
3961c3652a5SVivek Natarajan struct ieee80211_vif *vif)
397fb9987d0SSujith {
398fb9987d0SSujith struct ath_common *common = ath9k_hw_common(priv->ah);
3993c4816d9SOleksij Rempel struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
4001c3652a5SVivek Natarajan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
401e7a2a4f5SSujith Manoharan bool beacon_configured;
402fcb9392fSSujith
403a5fae37dSSujith Manoharan /*
404a5fae37dSSujith Manoharan * Changing the beacon interval when multiple AP interfaces
405a5fae37dSSujith Manoharan * are configured will affect beacon transmission of all
406a5fae37dSSujith Manoharan * of them.
407a5fae37dSSujith Manoharan */
408a5fae37dSSujith Manoharan if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
409a5fae37dSSujith Manoharan (priv->num_ap_vif > 1) &&
410a5fae37dSSujith Manoharan (vif->type == NL80211_IFTYPE_AP) &&
411a5fae37dSSujith Manoharan (cur_conf->beacon_interval != bss_conf->beacon_int)) {
412d2182b69SJoe Perches ath_dbg(common, CONFIG,
413a5fae37dSSujith Manoharan "Changing beacon interval of multiple AP interfaces !\n");
414e7a2a4f5SSujith Manoharan return false;
415a5fae37dSSujith Manoharan }
416a5fae37dSSujith Manoharan
417a5fae37dSSujith Manoharan /*
418a5fae37dSSujith Manoharan * If the HW is operating in AP mode, any new station interfaces that
419a5fae37dSSujith Manoharan * are added cannot change the beacon parameters.
420a5fae37dSSujith Manoharan */
421a5fae37dSSujith Manoharan if (priv->num_ap_vif &&
422a5fae37dSSujith Manoharan (vif->type != NL80211_IFTYPE_AP)) {
423d2182b69SJoe Perches ath_dbg(common, CONFIG,
424a5fae37dSSujith Manoharan "HW in AP mode, cannot set STA beacon parameters\n");
425e7a2a4f5SSujith Manoharan return false;
426a5fae37dSSujith Manoharan }
427a5fae37dSSujith Manoharan
428e7a2a4f5SSujith Manoharan /*
429e7a2a4f5SSujith Manoharan * The beacon parameters are configured only for the first
430e7a2a4f5SSujith Manoharan * station interface.
431e7a2a4f5SSujith Manoharan */
432e7a2a4f5SSujith Manoharan if ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
433e7a2a4f5SSujith Manoharan (priv->num_sta_vif > 1) &&
434e7a2a4f5SSujith Manoharan (vif->type == NL80211_IFTYPE_STATION)) {
435e7a2a4f5SSujith Manoharan beacon_configured = false;
4368b2c9824SJohannes Berg ieee80211_iterate_active_interfaces_atomic(
4378b2c9824SJohannes Berg priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
4388b2c9824SJohannes Berg ath9k_htc_beacon_iter, &beacon_configured);
439e7a2a4f5SSujith Manoharan
440e7a2a4f5SSujith Manoharan if (beacon_configured) {
441d2182b69SJoe Perches ath_dbg(common, CONFIG,
442e7a2a4f5SSujith Manoharan "Beacon already configured for a station interface\n");
443e7a2a4f5SSujith Manoharan return false;
444e7a2a4f5SSujith Manoharan }
445e7a2a4f5SSujith Manoharan }
446e7a2a4f5SSujith Manoharan
447e7a2a4f5SSujith Manoharan return true;
448e7a2a4f5SSujith Manoharan }
449e7a2a4f5SSujith Manoharan
ath9k_htc_beacon_config(struct ath9k_htc_priv * priv,struct ieee80211_vif * vif)450e7a2a4f5SSujith Manoharan void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
451e7a2a4f5SSujith Manoharan struct ieee80211_vif *vif)
452e7a2a4f5SSujith Manoharan {
453e7a2a4f5SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
4543c4816d9SOleksij Rempel struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
455e7a2a4f5SSujith Manoharan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
456e7a2a4f5SSujith Manoharan struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
457e7a2a4f5SSujith Manoharan
458e7a2a4f5SSujith Manoharan if (!ath9k_htc_check_beacon_config(priv, vif))
459e7a2a4f5SSujith Manoharan return;
460e7a2a4f5SSujith Manoharan
4611c3652a5SVivek Natarajan cur_conf->beacon_interval = bss_conf->beacon_int;
462fcb9392fSSujith if (cur_conf->beacon_interval == 0)
463fcb9392fSSujith cur_conf->beacon_interval = 100;
464fcb9392fSSujith
4651c3652a5SVivek Natarajan cur_conf->dtim_period = bss_conf->dtim_period;
4661c3652a5SVivek Natarajan cur_conf->bmiss_timeout =
4671c3652a5SVivek Natarajan ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
4681c3652a5SVivek Natarajan
469fcb9392fSSujith switch (vif->type) {
470fb9987d0SSujith case NL80211_IFTYPE_STATION:
4711c3652a5SVivek Natarajan ath9k_htc_beacon_config_sta(priv, cur_conf);
472e7a2a4f5SSujith Manoharan avp->beacon_configured = true;
473fb9987d0SSujith break;
474fb9987d0SSujith case NL80211_IFTYPE_ADHOC:
4751c3652a5SVivek Natarajan ath9k_htc_beacon_config_adhoc(priv, cur_conf);
476fb9987d0SSujith break;
477594e65b6SJavier Cardona case NL80211_IFTYPE_MESH_POINT:
478a5fae37dSSujith Manoharan case NL80211_IFTYPE_AP:
479a5fae37dSSujith Manoharan ath9k_htc_beacon_config_ap(priv, cur_conf);
480a5fae37dSSujith Manoharan break;
481fb9987d0SSujith default:
482d2182b69SJoe Perches ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
483fb9987d0SSujith return;
484fb9987d0SSujith }
485fb9987d0SSujith }
4867c277349SSujith Manoharan
ath9k_htc_beacon_reconfig(struct ath9k_htc_priv * priv)4877c277349SSujith Manoharan void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
4887c277349SSujith Manoharan {
4897c277349SSujith Manoharan struct ath_common *common = ath9k_hw_common(priv->ah);
4903c4816d9SOleksij Rempel struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf;
4917c277349SSujith Manoharan
4927c277349SSujith Manoharan switch (priv->ah->opmode) {
4937c277349SSujith Manoharan case NL80211_IFTYPE_STATION:
4947c277349SSujith Manoharan ath9k_htc_beacon_config_sta(priv, cur_conf);
4957c277349SSujith Manoharan break;
4967c277349SSujith Manoharan case NL80211_IFTYPE_ADHOC:
4977c277349SSujith Manoharan ath9k_htc_beacon_config_adhoc(priv, cur_conf);
4987c277349SSujith Manoharan break;
499594e65b6SJavier Cardona case NL80211_IFTYPE_MESH_POINT:
500a5fae37dSSujith Manoharan case NL80211_IFTYPE_AP:
501a5fae37dSSujith Manoharan ath9k_htc_beacon_config_ap(priv, cur_conf);
502a5fae37dSSujith Manoharan break;
5037c277349SSujith Manoharan default:
504d2182b69SJoe Perches ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
5057c277349SSujith Manoharan return;
5067c277349SSujith Manoharan }
5077c277349SSujith Manoharan }
508f0e44962SChun-Yeow Yeoh
ath9k_htc_csa_is_finished(struct ath9k_htc_priv * priv)509f0e44962SChun-Yeow Yeoh bool ath9k_htc_csa_is_finished(struct ath9k_htc_priv *priv)
510f0e44962SChun-Yeow Yeoh {
511f0e44962SChun-Yeow Yeoh struct ieee80211_vif *vif;
512f0e44962SChun-Yeow Yeoh
513f0e44962SChun-Yeow Yeoh vif = priv->csa_vif;
514d0a9123eSJohannes Berg if (!vif || !vif->bss_conf.csa_active)
515f0e44962SChun-Yeow Yeoh return false;
516f0e44962SChun-Yeow Yeoh
5178552a434SJohn Crispin if (!ieee80211_beacon_cntdwn_is_complete(vif))
518f0e44962SChun-Yeow Yeoh return false;
519f0e44962SChun-Yeow Yeoh
520f0e44962SChun-Yeow Yeoh ieee80211_csa_finish(vif);
521f0e44962SChun-Yeow Yeoh
522f0e44962SChun-Yeow Yeoh priv->csa_vif = NULL;
523f0e44962SChun-Yeow Yeoh return true;
524f0e44962SChun-Yeow Yeoh }
525