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