Commit | Line | Data |
---|---|---|
2865d42c LF |
1 | /****************************************************************************** |
2 | * ieee80211.c | |
3 | * | |
4 | * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. | |
5 | * Linux device driver for RTL8192SU | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
19 | * | |
20 | * Modifications for inclusion into the Linux staging tree are | |
21 | * Copyright(c) 2010 Larry Finger. All rights reserved. | |
22 | * | |
23 | * Contact information: | |
24 | * WLAN FAE <wlanfae@realtek.com>. | |
25 | * Larry Finger <Larry.Finger@lwfinger.net> | |
26 | * | |
27 | ******************************************************************************/ | |
28 | ||
29 | #define _IEEE80211_C | |
30 | ||
31 | #include "drv_types.h" | |
32 | #include "ieee80211.h" | |
33 | #include "wifi.h" | |
34 | #include "osdep_service.h" | |
35 | #include "wlan_bssdef.h" | |
36 | ||
37 | static const u8 WPA_OUI_TYPE[] = {0x00, 0x50, 0xf2, 1}; | |
38 | static const u8 WPA_CIPHER_SUITE_NONE[] = {0x00, 0x50, 0xf2, 0}; | |
39 | static const u8 WPA_CIPHER_SUITE_WEP40[] = {0x00, 0x50, 0xf2, 1}; | |
40 | static const u8 WPA_CIPHER_SUITE_TKIP[] = {0x00, 0x50, 0xf2, 2}; | |
41 | static const u8 WPA_CIPHER_SUITE_CCMP[] = {0x00, 0x50, 0xf2, 4}; | |
42 | static const u8 WPA_CIPHER_SUITE_WEP104[] = {0x00, 0x50, 0xf2, 5}; | |
43 | ||
44 | static const u8 RSN_CIPHER_SUITE_NONE[] = {0x00, 0x0f, 0xac, 0}; | |
45 | static const u8 RSN_CIPHER_SUITE_WEP40[] = {0x00, 0x0f, 0xac, 1}; | |
46 | static const u8 RSN_CIPHER_SUITE_TKIP[] = {0x00, 0x0f, 0xac, 2}; | |
47 | static const u8 RSN_CIPHER_SUITE_CCMP[] = {0x00, 0x0f, 0xac, 4}; | |
48 | static const u8 RSN_CIPHER_SUITE_WEP104[] = {0x00, 0x0f, 0xac, 5}; | |
49 | ||
50 | /*----------------------------------------------------------- | |
51 | * for adhoc-master to generate ie and provide supported-rate to fw | |
52 | *----------------------------------------------------------- | |
53 | */ | |
54 | ||
55 | static u8 WIFI_CCKRATES[] = { | |
56 | (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), | |
57 | (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), | |
58 | (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), | |
59 | (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK) | |
60 | }; | |
61 | ||
62 | static u8 WIFI_OFDMRATES[] = { | |
63 | (IEEE80211_OFDM_RATE_6MB), | |
64 | (IEEE80211_OFDM_RATE_9MB), | |
65 | (IEEE80211_OFDM_RATE_12MB), | |
66 | (IEEE80211_OFDM_RATE_18MB), | |
67 | (IEEE80211_OFDM_RATE_24MB), | |
68 | (IEEE80211_OFDM_RATE_36MB), | |
69 | (IEEE80211_OFDM_RATE_48MB), | |
70 | (IEEE80211_OFDM_RATE_54MB) | |
71 | }; | |
72 | ||
73 | uint r8712_is_cckrates_included(u8 *rate) | |
74 | { | |
75 | u32 i = 0; | |
76 | ||
77 | while (rate[i] != 0) { | |
78 | if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || | |
79 | (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) | |
80 | return true; | |
81 | i++; | |
82 | } | |
83 | return false; | |
84 | } | |
85 | ||
86 | uint r8712_is_cckratesonly_included(u8 *rate) | |
87 | { | |
88 | u32 i = 0; | |
89 | ||
90 | while (rate[i] != 0) { | |
91 | if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && | |
92 | (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) | |
93 | return false; | |
94 | i++; | |
95 | } | |
96 | return true; | |
97 | } | |
98 | ||
99 | /* r8712_set_ie will update frame length */ | |
100 | u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen) | |
101 | { | |
102 | *pbuf = (u8)index; | |
103 | *(pbuf + 1) = (u8)len; | |
104 | if (len > 0) | |
105 | memcpy((void *)(pbuf + 2), (void *)source, len); | |
106 | *frlen = *frlen + (len + 2); | |
107 | return pbuf + len + 2; | |
108 | } | |
109 | ||
110 | /*---------------------------------------------------------------------------- | |
111 | index: the information element id index, limit is the limit for search | |
112 | -----------------------------------------------------------------------------*/ | |
113 | u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit) | |
114 | { | |
115 | sint tmp, i; | |
116 | u8 *p; | |
117 | ||
118 | if (limit < 1) | |
119 | return NULL; | |
120 | p = pbuf; | |
121 | i = 0; | |
122 | *len = 0; | |
123 | while (1) { | |
124 | if (*p == index) { | |
125 | *len = *(p + 1); | |
126 | return p; | |
127 | } else { | |
128 | tmp = *(p + 1); | |
129 | p += (tmp + 2); | |
130 | i += (tmp + 2); | |
131 | } | |
132 | if (i >= limit) | |
133 | break; | |
134 | } | |
135 | return NULL; | |
136 | } | |
137 | ||
138 | static void set_supported_rate(u8 *SupportedRates, uint mode) | |
139 | { | |
140 | memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); | |
141 | switch (mode) { | |
142 | case WIRELESS_11B: | |
143 | memcpy(SupportedRates, WIFI_CCKRATES, | |
144 | IEEE80211_CCK_RATE_LEN); | |
145 | break; | |
146 | case WIRELESS_11G: | |
147 | case WIRELESS_11A: | |
148 | memcpy(SupportedRates, WIFI_OFDMRATES, | |
149 | IEEE80211_NUM_OFDM_RATESLEN); | |
150 | break; | |
151 | case WIRELESS_11BG: | |
152 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
153 | memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, | |
154 | IEEE80211_NUM_OFDM_RATESLEN); | |
155 | break; | |
156 | } | |
157 | } | |
158 | ||
159 | static uint r8712_get_rateset_len(u8 *rateset) | |
160 | { | |
161 | uint i = 0; | |
162 | ||
163 | while (1) { | |
164 | if ((rateset[i]) == 0) | |
165 | break; | |
166 | if (i > 12) | |
167 | break; | |
168 | i++; | |
169 | } | |
170 | return i; | |
171 | } | |
172 | ||
ee5b1aad | 173 | int r8712_generate_ie(struct registry_priv *pregistrypriv) |
2865d42c LF |
174 | { |
175 | int sz = 0, rateLen; | |
176 | struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; | |
177 | u8 *ie = pdev_network->IEs; | |
2865d42c LF |
178 | |
179 | /*timestamp will be inserted by hardware*/ | |
180 | sz += 8; | |
181 | ie += sz; | |
182 | /*beacon interval : 2bytes*/ | |
183 | *(u16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod); | |
184 | sz += 2; | |
185 | ie += 2; | |
186 | /*capability info*/ | |
187 | *(u16 *)ie = 0; | |
188 | *(u16 *)ie |= cpu_to_le16(cap_IBSS); | |
189 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
190 | *(u16 *)ie |= cpu_to_le16(cap_ShortPremble); | |
191 | if (pdev_network->Privacy) | |
192 | *(u16 *)ie |= cpu_to_le16(cap_Privacy); | |
193 | sz += 2; | |
194 | ie += 2; | |
195 | /*SSID*/ | |
196 | ie = r8712_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, | |
197 | pdev_network->Ssid.Ssid, &sz); | |
198 | /*supported rates*/ | |
199 | set_supported_rate(pdev_network->SupportedRates, | |
200 | pregistrypriv->wireless_mode); | |
201 | rateLen = r8712_get_rateset_len(pdev_network->SupportedRates); | |
202 | if (rateLen > 8) { | |
203 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, 8, | |
204 | pdev_network->SupportedRates, &sz); | |
205 | ie = r8712_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), | |
206 | (pdev_network->SupportedRates + 8), &sz); | |
207 | } else | |
208 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, | |
209 | rateLen, pdev_network->SupportedRates, &sz); | |
210 | /*DS parameter set*/ | |
211 | ie = r8712_set_ie(ie, _DSSET_IE_, 1, | |
212 | (u8 *)&(pdev_network->Configuration.DSConfig), &sz); | |
213 | /*IBSS Parameter Set*/ | |
214 | ie = r8712_set_ie(ie, _IBSS_PARA_IE_, 2, | |
215 | (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz); | |
2865d42c LF |
216 | return sz; |
217 | } | |
218 | ||
219 | unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) | |
220 | { | |
221 | int len; | |
222 | u16 val16; | |
223 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
224 | u8 *pbuf = pie; | |
225 | ||
226 | while (1) { | |
227 | pbuf = r8712_get_ie(pbuf, _WPA_IE_ID_, &len, limit); | |
228 | if (pbuf) { | |
229 | /*check if oui matches...*/ | |
230 | if (memcmp((pbuf + 2), wpa_oui_type, | |
231 | sizeof(wpa_oui_type))) | |
232 | goto check_next_ie; | |
233 | /*check version...*/ | |
234 | memcpy((u8 *)&val16, (pbuf + 6), sizeof(val16)); | |
235 | val16 = le16_to_cpu(val16); | |
236 | if (val16 != 0x0001) | |
237 | goto check_next_ie; | |
238 | *wpa_ie_len = *(pbuf + 1); | |
239 | return pbuf; | |
240 | } else { | |
241 | *wpa_ie_len = 0; | |
242 | return NULL; | |
243 | } | |
244 | check_next_ie: | |
245 | limit = limit - (pbuf - pie) - 2 - len; | |
246 | if (limit <= 0) | |
247 | break; | |
248 | pbuf += (2 + len); | |
249 | } | |
250 | *wpa_ie_len = 0; | |
251 | return NULL; | |
252 | } | |
253 | ||
254 | unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit) | |
255 | { | |
256 | return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
257 | } | |
258 | ||
259 | static int r8712_get_wpa_cipher_suite(u8 *s) | |
260 | { | |
261 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) | |
262 | return WPA_CIPHER_NONE; | |
263 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) | |
264 | return WPA_CIPHER_WEP40; | |
265 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) | |
266 | return WPA_CIPHER_TKIP; | |
267 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) | |
268 | return WPA_CIPHER_CCMP; | |
269 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) | |
270 | return WPA_CIPHER_WEP104; | |
271 | return 0; | |
272 | } | |
273 | ||
274 | static int r8712_get_wpa2_cipher_suite(u8 *s) | |
275 | { | |
276 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) | |
277 | return WPA_CIPHER_NONE; | |
278 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) | |
279 | return WPA_CIPHER_WEP40; | |
280 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) | |
281 | return WPA_CIPHER_TKIP; | |
282 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) | |
283 | return WPA_CIPHER_CCMP; | |
284 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) | |
285 | return WPA_CIPHER_WEP104; | |
286 | return 0; | |
287 | } | |
288 | ||
289 | int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, | |
290 | int *pairwise_cipher) | |
291 | { | |
292 | int i, ret = _SUCCESS; | |
293 | int left, count; | |
294 | u8 *pos; | |
295 | ||
296 | if (wpa_ie_len <= 0) { | |
297 | /* No WPA IE - fail silently */ | |
298 | return _FAIL; | |
299 | } | |
300 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) | |
301 | || (memcmp(wpa_ie + 2, (void *)WPA_OUI_TYPE, WPA_SELECTOR_LEN))) | |
302 | return _FAIL; | |
303 | pos = wpa_ie; | |
304 | pos += 8; | |
305 | left = wpa_ie_len - 8; | |
306 | /*group_cipher*/ | |
307 | if (left >= WPA_SELECTOR_LEN) { | |
308 | *group_cipher = r8712_get_wpa_cipher_suite(pos); | |
309 | pos += WPA_SELECTOR_LEN; | |
310 | left -= WPA_SELECTOR_LEN; | |
311 | } else if (left > 0) | |
312 | return _FAIL; | |
313 | /*pairwise_cipher*/ | |
314 | if (left >= 2) { | |
315 | count = le16_to_cpu(*(u16 *)pos); | |
316 | pos += 2; | |
317 | left -= 2; | |
318 | if (count == 0 || left < count * WPA_SELECTOR_LEN) | |
319 | return _FAIL; | |
320 | for (i = 0; i < count; i++) { | |
321 | *pairwise_cipher |= r8712_get_wpa_cipher_suite(pos); | |
322 | pos += WPA_SELECTOR_LEN; | |
323 | left -= WPA_SELECTOR_LEN; | |
324 | } | |
325 | } else if (left == 1) | |
326 | return _FAIL; | |
327 | return ret; | |
328 | } | |
329 | ||
330 | int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, | |
331 | int *pairwise_cipher) | |
332 | { | |
333 | int i, ret = _SUCCESS; | |
334 | int left, count; | |
335 | u8 *pos; | |
336 | ||
337 | if (rsn_ie_len <= 0) { | |
338 | /* No RSN IE - fail silently */ | |
339 | return _FAIL; | |
340 | } | |
341 | if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) | |
342 | return _FAIL; | |
343 | pos = rsn_ie; | |
344 | pos += 4; | |
345 | left = rsn_ie_len - 4; | |
346 | /*group_cipher*/ | |
347 | if (left >= RSN_SELECTOR_LEN) { | |
348 | *group_cipher = r8712_get_wpa2_cipher_suite(pos); | |
349 | pos += RSN_SELECTOR_LEN; | |
350 | left -= RSN_SELECTOR_LEN; | |
351 | } else if (left > 0) | |
352 | return _FAIL; | |
353 | /*pairwise_cipher*/ | |
354 | if (left >= 2) { | |
355 | count = le16_to_cpu(*(u16 *)pos); | |
356 | pos += 2; | |
357 | left -= 2; | |
358 | if (count == 0 || left < count * RSN_SELECTOR_LEN) | |
359 | return _FAIL; | |
360 | for (i = 0; i < count; i++) { | |
361 | *pairwise_cipher |= r8712_get_wpa2_cipher_suite(pos); | |
362 | pos += RSN_SELECTOR_LEN; | |
363 | left -= RSN_SELECTOR_LEN; | |
364 | } | |
365 | } else if (left == 1) | |
366 | return _FAIL; | |
367 | return ret; | |
368 | } | |
369 | ||
370 | int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, | |
371 | u8 *wpa_ie, u16 *wpa_len) | |
372 | { | |
373 | u8 authmode, sec_idx; | |
374 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; | |
375 | uint cnt; | |
376 | ||
377 | /*Search required WPA or WPA2 IE and copy to sec_ie[ ]*/ | |
378 | cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); | |
379 | sec_idx = 0; | |
380 | while (cnt < in_len) { | |
381 | authmode = in_ie[cnt]; | |
382 | if ((authmode == _WPA_IE_ID_) && | |
383 | (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { | |
384 | memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); | |
385 | *wpa_len = in_ie[cnt+1]+2; | |
386 | cnt += in_ie[cnt + 1] + 2; /*get next */ | |
387 | } else { | |
388 | if (authmode == _WPA2_IE_ID_) { | |
389 | memcpy(rsn_ie, &in_ie[cnt], | |
390 | in_ie[cnt + 1] + 2); | |
391 | *rsn_len = in_ie[cnt+1] + 2; | |
392 | cnt += in_ie[cnt+1] + 2; /*get next*/ | |
393 | } else | |
394 | cnt += in_ie[cnt+1] + 2; /*get next*/ | |
395 | } | |
396 | } | |
397 | return *rsn_len + *wpa_len; | |
398 | } | |
399 | ||
400 | int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
401 | { | |
402 | int match; | |
403 | uint cnt; | |
404 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
405 | ||
406 | cnt = 12; | |
407 | match = false; | |
408 | while (cnt < in_len) { | |
409 | eid = in_ie[cnt]; | |
410 | if ((eid == _WPA_IE_ID_) && | |
411 | (!memcmp(&in_ie[cnt+2], wps_oui, 4))) { | |
412 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
413 | *wps_ielen = in_ie[cnt+1]+2; | |
414 | cnt += in_ie[cnt+1]+2; | |
415 | match = true; | |
416 | break; | |
417 | } else | |
418 | cnt += in_ie[cnt+1]+2; /* goto next */ | |
419 | } | |
420 | return match; | |
421 | } |