diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/htc_drv_beacon.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c new file mode 100644 index 000000000000..c10c7d002eb7 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "htc.h" | ||
18 | |||
19 | #define FUDGE 2 | ||
20 | |||
21 | static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, | ||
22 | struct htc_beacon_config *bss_conf) | ||
23 | { | ||
24 | struct ath_common *common = ath9k_hw_common(priv->ah); | ||
25 | struct ath9k_beacon_state bs; | ||
26 | enum ath9k_int imask = 0; | ||
27 | int dtimperiod, dtimcount, sleepduration; | ||
28 | int cfpperiod, cfpcount, bmiss_timeout; | ||
29 | u32 nexttbtt = 0, intval, tsftu; | ||
30 | __be32 htc_imask = 0; | ||
31 | u64 tsf; | ||
32 | int num_beacons, offset, dtim_dec_count, cfp_dec_count; | ||
33 | int ret; | ||
34 | u8 cmd_rsp; | ||
35 | |||
36 | memset(&bs, 0, sizeof(bs)); | ||
37 | |||
38 | intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; | ||
39 | bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); | ||
40 | |||
41 | /* | ||
42 | * Setup dtim and cfp parameters according to | ||
43 | * last beacon we received (which may be none). | ||
44 | */ | ||
45 | dtimperiod = bss_conf->dtim_period; | ||
46 | if (dtimperiod <= 0) /* NB: 0 if not known */ | ||
47 | dtimperiod = 1; | ||
48 | dtimcount = 1; | ||
49 | if (dtimcount >= dtimperiod) /* NB: sanity check */ | ||
50 | dtimcount = 0; | ||
51 | cfpperiod = 1; /* NB: no PCF support yet */ | ||
52 | cfpcount = 0; | ||
53 | |||
54 | sleepduration = intval; | ||
55 | if (sleepduration <= 0) | ||
56 | sleepduration = intval; | ||
57 | |||
58 | /* | ||
59 | * Pull nexttbtt forward to reflect the current | ||
60 | * TSF and calculate dtim+cfp state for the result. | ||
61 | */ | ||
62 | tsf = ath9k_hw_gettsf64(priv->ah); | ||
63 | tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; | ||
64 | |||
65 | num_beacons = tsftu / intval + 1; | ||
66 | offset = tsftu % intval; | ||
67 | nexttbtt = tsftu - offset; | ||
68 | if (offset) | ||
69 | nexttbtt += intval; | ||
70 | |||
71 | /* DTIM Beacon every dtimperiod Beacon */ | ||
72 | dtim_dec_count = num_beacons % dtimperiod; | ||
73 | /* CFP every cfpperiod DTIM Beacon */ | ||
74 | cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; | ||
75 | if (dtim_dec_count) | ||
76 | cfp_dec_count++; | ||
77 | |||
78 | dtimcount -= dtim_dec_count; | ||
79 | if (dtimcount < 0) | ||
80 | dtimcount += dtimperiod; | ||
81 | |||
82 | cfpcount -= cfp_dec_count; | ||
83 | if (cfpcount < 0) | ||
84 | cfpcount += cfpperiod; | ||
85 | |||
86 | bs.bs_intval = intval; | ||
87 | bs.bs_nexttbtt = nexttbtt; | ||
88 | bs.bs_dtimperiod = dtimperiod*intval; | ||
89 | bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; | ||
90 | bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; | ||
91 | bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; | ||
92 | bs.bs_cfpmaxduration = 0; | ||
93 | |||
94 | /* | ||
95 | * Calculate the number of consecutive beacons to miss* before taking | ||
96 | * a BMISS interrupt. The configuration is specified in TU so we only | ||
97 | * need calculate based on the beacon interval. Note that we clamp the | ||
98 | * result to at most 15 beacons. | ||
99 | */ | ||
100 | if (sleepduration > intval) { | ||
101 | bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; | ||
102 | } else { | ||
103 | bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); | ||
104 | if (bs.bs_bmissthreshold > 15) | ||
105 | bs.bs_bmissthreshold = 15; | ||
106 | else if (bs.bs_bmissthreshold <= 0) | ||
107 | bs.bs_bmissthreshold = 1; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Calculate sleep duration. The configuration is given in ms. | ||
112 | * We ensure a multiple of the beacon period is used. Also, if the sleep | ||
113 | * duration is greater than the DTIM period then it makes senses | ||
114 | * to make it a multiple of that. | ||
115 | * | ||
116 | * XXX fixed at 100ms | ||
117 | */ | ||
118 | |||
119 | bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); | ||
120 | if (bs.bs_sleepduration > bs.bs_dtimperiod) | ||
121 | bs.bs_sleepduration = bs.bs_dtimperiod; | ||
122 | |||
123 | /* TSF out of range threshold fixed at 1 second */ | ||
124 | bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; | ||
125 | |||
126 | ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); | ||
127 | ath_print(common, ATH_DBG_BEACON, | ||
128 | "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", | ||
129 | bs.bs_bmissthreshold, bs.bs_sleepduration, | ||
130 | bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); | ||
131 | |||
132 | /* Set the computed STA beacon timers */ | ||
133 | |||
134 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | ||
135 | ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); | ||
136 | imask |= ATH9K_INT_BMISS; | ||
137 | htc_imask = cpu_to_be32(imask); | ||
138 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); | ||
139 | } | ||
140 | |||
141 | static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, | ||
142 | struct htc_beacon_config *bss_conf) | ||
143 | { | ||
144 | struct ath_common *common = ath9k_hw_common(priv->ah); | ||
145 | enum ath9k_int imask = 0; | ||
146 | u32 nexttbtt, intval; | ||
147 | __be32 htc_imask = 0; | ||
148 | int ret; | ||
149 | u8 cmd_rsp; | ||
150 | |||
151 | intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; | ||
152 | nexttbtt = intval; | ||
153 | intval |= ATH9K_BEACON_ENA; | ||
154 | if (priv->op_flags & OP_ENABLE_BEACON) | ||
155 | imask |= ATH9K_INT_SWBA; | ||
156 | |||
157 | ath_print(common, ATH_DBG_BEACON, | ||
158 | "IBSS Beacon config, intval: %d, imask: 0x%x\n", | ||
159 | bss_conf->beacon_interval, imask); | ||
160 | |||
161 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | ||
162 | ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); | ||
163 | priv->bmiss_cnt = 0; | ||
164 | htc_imask = cpu_to_be32(imask); | ||
165 | WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); | ||
166 | } | ||
167 | |||
168 | void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, | ||
169 | enum htc_endpoint_id ep_id, bool txok) | ||
170 | { | ||
171 | dev_kfree_skb_any(skb); | ||
172 | } | ||
173 | |||
174 | void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) | ||
175 | { | ||
176 | struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv; | ||
177 | struct tx_beacon_header beacon_hdr; | ||
178 | struct ath9k_htc_tx_ctl tx_ctl; | ||
179 | struct ieee80211_tx_info *info; | ||
180 | struct sk_buff *beacon; | ||
181 | u8 *tx_fhdr; | ||
182 | |||
183 | memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); | ||
184 | memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); | ||
185 | |||
186 | /* FIXME: Handle BMISS */ | ||
187 | if (beacon_pending != 0) { | ||
188 | priv->bmiss_cnt++; | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | spin_lock_bh(&priv->beacon_lock); | ||
193 | |||
194 | if (unlikely(priv->op_flags & OP_SCANNING)) { | ||
195 | spin_unlock_bh(&priv->beacon_lock); | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | /* Get a new beacon */ | ||
200 | beacon = ieee80211_beacon_get(priv->hw, priv->vif); | ||
201 | if (!beacon) { | ||
202 | spin_unlock_bh(&priv->beacon_lock); | ||
203 | return; | ||
204 | } | ||
205 | |||
206 | info = IEEE80211_SKB_CB(beacon); | ||
207 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { | ||
208 | struct ieee80211_hdr *hdr = | ||
209 | (struct ieee80211_hdr *) beacon->data; | ||
210 | priv->seq_no += 0x10; | ||
211 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); | ||
212 | hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); | ||
213 | } | ||
214 | |||
215 | tx_ctl.type = ATH9K_HTC_NORMAL; | ||
216 | beacon_hdr.vif_index = avp->index; | ||
217 | tx_fhdr = skb_push(beacon, sizeof(beacon_hdr)); | ||
218 | memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); | ||
219 | |||
220 | htc_send(priv->htc, beacon, priv->beacon_ep, &tx_ctl); | ||
221 | |||
222 | spin_unlock_bh(&priv->beacon_lock); | ||
223 | } | ||
224 | |||
225 | |||
226 | void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, | ||
227 | struct ieee80211_vif *vif) | ||
228 | { | ||
229 | struct ath_common *common = ath9k_hw_common(priv->ah); | ||
230 | struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; | ||
231 | struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; | ||
232 | |||
233 | cur_conf->beacon_interval = bss_conf->beacon_int; | ||
234 | if (cur_conf->beacon_interval == 0) | ||
235 | cur_conf->beacon_interval = 100; | ||
236 | |||
237 | cur_conf->dtim_period = bss_conf->dtim_period; | ||
238 | cur_conf->listen_interval = 1; | ||
239 | cur_conf->dtim_count = 1; | ||
240 | cur_conf->bmiss_timeout = | ||
241 | ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; | ||
242 | |||
243 | switch (vif->type) { | ||
244 | case NL80211_IFTYPE_STATION: | ||
245 | ath9k_htc_beacon_config_sta(priv, cur_conf); | ||
246 | break; | ||
247 | case NL80211_IFTYPE_ADHOC: | ||
248 | ath9k_htc_beacon_config_adhoc(priv, cur_conf); | ||
249 | break; | ||
250 | default: | ||
251 | ath_print(common, ATH_DBG_CONFIG, | ||
252 | "Unsupported beaconing mode\n"); | ||
253 | return; | ||
254 | } | ||
255 | } | ||