]>
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; | |
879cb0c9 DC |
81 | i++; |
82 | } | |
83 | return false; | |
2865d42c LF |
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 | ||
4fd8cba1 PV |
110 | /* --------------------------------------------------------------------------- |
111 | * index: the information element id index, limit is the limit for search | |
112 | * --------------------------------------------------------------------------- | |
113 | */ | |
2865d42c LF |
114 | u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit) |
115 | { | |
116 | sint tmp, i; | |
117 | u8 *p; | |
118 | ||
119 | if (limit < 1) | |
120 | return NULL; | |
121 | p = pbuf; | |
122 | i = 0; | |
123 | *len = 0; | |
124 | while (1) { | |
125 | if (*p == index) { | |
126 | *len = *(p + 1); | |
127 | return p; | |
2865d42c | 128 | } |
e92c3511 NK |
129 | tmp = *(p + 1); |
130 | p += (tmp + 2); | |
131 | i += (tmp + 2); | |
2865d42c LF |
132 | if (i >= limit) |
133 | break; | |
134 | } | |
135 | return NULL; | |
136 | } | |
137 | ||
7fb539ed | 138 | static void set_supported_rate(u8 *rates, uint mode) |
2865d42c | 139 | { |
7fb539ed | 140 | memset(rates, 0, NDIS_802_11_LENGTH_RATES_EX); |
2865d42c LF |
141 | switch (mode) { |
142 | case WIRELESS_11B: | |
7fb539ed | 143 | memcpy(rates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); |
2865d42c LF |
144 | break; |
145 | case WIRELESS_11G: | |
146 | case WIRELESS_11A: | |
7fb539ed | 147 | memcpy(rates, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); |
2865d42c LF |
148 | break; |
149 | case WIRELESS_11BG: | |
7fb539ed JC |
150 | memcpy(rates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); |
151 | memcpy(rates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, | |
2865d42c LF |
152 | IEEE80211_NUM_OFDM_RATESLEN); |
153 | break; | |
154 | } | |
155 | } | |
156 | ||
157 | static uint r8712_get_rateset_len(u8 *rateset) | |
158 | { | |
159 | uint i = 0; | |
160 | ||
161 | while (1) { | |
162 | if ((rateset[i]) == 0) | |
163 | break; | |
164 | if (i > 12) | |
165 | break; | |
166 | i++; | |
167 | } | |
168 | return i; | |
169 | } | |
170 | ||
ee5b1aad | 171 | int r8712_generate_ie(struct registry_priv *pregistrypriv) |
2865d42c LF |
172 | { |
173 | int sz = 0, rateLen; | |
174 | struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; | |
175 | u8 *ie = pdev_network->IEs; | |
2865d42c LF |
176 | |
177 | /*timestamp will be inserted by hardware*/ | |
178 | sz += 8; | |
179 | ie += sz; | |
180 | /*beacon interval : 2bytes*/ | |
181 | *(u16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod); | |
182 | sz += 2; | |
183 | ie += 2; | |
184 | /*capability info*/ | |
185 | *(u16 *)ie = 0; | |
186 | *(u16 *)ie |= cpu_to_le16(cap_IBSS); | |
187 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
188 | *(u16 *)ie |= cpu_to_le16(cap_ShortPremble); | |
189 | if (pdev_network->Privacy) | |
190 | *(u16 *)ie |= cpu_to_le16(cap_Privacy); | |
191 | sz += 2; | |
192 | ie += 2; | |
193 | /*SSID*/ | |
194 | ie = r8712_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, | |
195 | pdev_network->Ssid.Ssid, &sz); | |
196 | /*supported rates*/ | |
7fb539ed JC |
197 | set_supported_rate(pdev_network->rates, pregistrypriv->wireless_mode); |
198 | rateLen = r8712_get_rateset_len(pdev_network->rates); | |
2865d42c LF |
199 | if (rateLen > 8) { |
200 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, 8, | |
7fb539ed | 201 | pdev_network->rates, &sz); |
2865d42c | 202 | ie = r8712_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), |
7fb539ed | 203 | (pdev_network->rates + 8), &sz); |
2865d42c LF |
204 | } else |
205 | ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, | |
7fb539ed | 206 | rateLen, pdev_network->rates, &sz); |
2865d42c LF |
207 | /*DS parameter set*/ |
208 | ie = r8712_set_ie(ie, _DSSET_IE_, 1, | |
209 | (u8 *)&(pdev_network->Configuration.DSConfig), &sz); | |
210 | /*IBSS Parameter Set*/ | |
211 | ie = r8712_set_ie(ie, _IBSS_PARA_IE_, 2, | |
212 | (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz); | |
2865d42c LF |
213 | return sz; |
214 | } | |
215 | ||
216 | unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) | |
217 | { | |
218 | int len; | |
219 | u16 val16; | |
220 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
221 | u8 *pbuf = pie; | |
222 | ||
223 | while (1) { | |
224 | pbuf = r8712_get_ie(pbuf, _WPA_IE_ID_, &len, limit); | |
225 | if (pbuf) { | |
226 | /*check if oui matches...*/ | |
227 | if (memcmp((pbuf + 2), wpa_oui_type, | |
228 | sizeof(wpa_oui_type))) | |
229 | goto check_next_ie; | |
230 | /*check version...*/ | |
231 | memcpy((u8 *)&val16, (pbuf + 6), sizeof(val16)); | |
232 | val16 = le16_to_cpu(val16); | |
233 | if (val16 != 0x0001) | |
234 | goto check_next_ie; | |
235 | *wpa_ie_len = *(pbuf + 1); | |
236 | return pbuf; | |
2865d42c | 237 | } |
e92c3511 NK |
238 | *wpa_ie_len = 0; |
239 | return NULL; | |
2865d42c LF |
240 | check_next_ie: |
241 | limit = limit - (pbuf - pie) - 2 - len; | |
242 | if (limit <= 0) | |
243 | break; | |
244 | pbuf += (2 + len); | |
245 | } | |
246 | *wpa_ie_len = 0; | |
247 | return NULL; | |
248 | } | |
249 | ||
250 | unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit) | |
251 | { | |
252 | return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
253 | } | |
254 | ||
255 | static int r8712_get_wpa_cipher_suite(u8 *s) | |
256 | { | |
257 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) | |
258 | return WPA_CIPHER_NONE; | |
259 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) | |
260 | return WPA_CIPHER_WEP40; | |
261 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) | |
262 | return WPA_CIPHER_TKIP; | |
263 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) | |
264 | return WPA_CIPHER_CCMP; | |
265 | if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) | |
266 | return WPA_CIPHER_WEP104; | |
267 | return 0; | |
268 | } | |
269 | ||
270 | static int r8712_get_wpa2_cipher_suite(u8 *s) | |
271 | { | |
272 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) | |
273 | return WPA_CIPHER_NONE; | |
274 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) | |
275 | return WPA_CIPHER_WEP40; | |
276 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) | |
277 | return WPA_CIPHER_TKIP; | |
278 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) | |
279 | return WPA_CIPHER_CCMP; | |
280 | if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) | |
281 | return WPA_CIPHER_WEP104; | |
282 | return 0; | |
283 | } | |
284 | ||
285 | int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, | |
286 | int *pairwise_cipher) | |
287 | { | |
8ffca9ea | 288 | int i; |
2865d42c LF |
289 | int left, count; |
290 | u8 *pos; | |
291 | ||
292 | if (wpa_ie_len <= 0) { | |
293 | /* No WPA IE - fail silently */ | |
294 | return _FAIL; | |
295 | } | |
296 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) | |
297 | || (memcmp(wpa_ie + 2, (void *)WPA_OUI_TYPE, WPA_SELECTOR_LEN))) | |
298 | return _FAIL; | |
299 | pos = wpa_ie; | |
300 | pos += 8; | |
301 | left = wpa_ie_len - 8; | |
302 | /*group_cipher*/ | |
303 | if (left >= WPA_SELECTOR_LEN) { | |
304 | *group_cipher = r8712_get_wpa_cipher_suite(pos); | |
305 | pos += WPA_SELECTOR_LEN; | |
306 | left -= WPA_SELECTOR_LEN; | |
168a2c10 | 307 | } else if (left > 0) { |
2865d42c | 308 | return _FAIL; |
168a2c10 | 309 | } |
2865d42c LF |
310 | /*pairwise_cipher*/ |
311 | if (left >= 2) { | |
312 | count = le16_to_cpu(*(u16 *)pos); | |
313 | pos += 2; | |
314 | left -= 2; | |
315 | if (count == 0 || left < count * WPA_SELECTOR_LEN) | |
316 | return _FAIL; | |
317 | for (i = 0; i < count; i++) { | |
318 | *pairwise_cipher |= r8712_get_wpa_cipher_suite(pos); | |
319 | pos += WPA_SELECTOR_LEN; | |
320 | left -= WPA_SELECTOR_LEN; | |
321 | } | |
168a2c10 | 322 | } else if (left == 1) { |
2865d42c | 323 | return _FAIL; |
168a2c10 | 324 | } |
8ffca9ea | 325 | return _SUCCESS; |
2865d42c LF |
326 | } |
327 | ||
328 | int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, | |
329 | int *pairwise_cipher) | |
330 | { | |
8ffca9ea | 331 | int i; |
2865d42c LF |
332 | int left, count; |
333 | u8 *pos; | |
334 | ||
335 | if (rsn_ie_len <= 0) { | |
336 | /* No RSN IE - fail silently */ | |
337 | return _FAIL; | |
338 | } | |
4ef2de5a LB |
339 | if ((*rsn_ie != _WPA2_IE_ID_) || |
340 | (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2))) | |
2865d42c LF |
341 | return _FAIL; |
342 | pos = rsn_ie; | |
343 | pos += 4; | |
344 | left = rsn_ie_len - 4; | |
345 | /*group_cipher*/ | |
346 | if (left >= RSN_SELECTOR_LEN) { | |
347 | *group_cipher = r8712_get_wpa2_cipher_suite(pos); | |
348 | pos += RSN_SELECTOR_LEN; | |
349 | left -= RSN_SELECTOR_LEN; | |
168a2c10 | 350 | } else if (left > 0) { |
2865d42c | 351 | return _FAIL; |
168a2c10 | 352 | } |
2865d42c LF |
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 | } | |
168a2c10 | 365 | } else if (left == 1) { |
2865d42c | 366 | return _FAIL; |
168a2c10 | 367 | } |
8ffca9ea | 368 | return _SUCCESS; |
2865d42c LF |
369 | } |
370 | ||
371 | int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, | |
372 | u8 *wpa_ie, u16 *wpa_len) | |
373 | { | |
e29d3ebc | 374 | u8 authmode; |
2865d42c LF |
375 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; |
376 | uint cnt; | |
377 | ||
378 | /*Search required WPA or WPA2 IE and copy to sec_ie[ ]*/ | |
379 | cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); | |
2865d42c LF |
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); | |
4ef2de5a | 385 | *wpa_len = in_ie[cnt + 1] + 2; |
2865d42c LF |
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); | |
4ef2de5a LB |
391 | *rsn_len = in_ie[cnt + 1] + 2; |
392 | cnt += in_ie[cnt + 1] + 2; /*get next*/ | |
168a2c10 | 393 | } else { |
4ef2de5a | 394 | cnt += in_ie[cnt + 1] + 2; /*get next*/ |
168a2c10 | 395 | } |
2865d42c LF |
396 | } |
397 | } | |
398 | return *rsn_len + *wpa_len; | |
399 | } | |
400 | ||
401 | int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
402 | { | |
403 | int match; | |
404 | uint cnt; | |
405 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
406 | ||
407 | cnt = 12; | |
408 | match = false; | |
409 | while (cnt < in_len) { | |
410 | eid = in_ie[cnt]; | |
411 | if ((eid == _WPA_IE_ID_) && | |
4ef2de5a LB |
412 | (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { |
413 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); | |
414 | *wps_ielen = in_ie[cnt + 1] + 2; | |
415 | cnt += in_ie[cnt + 1] + 2; | |
2865d42c LF |
416 | match = true; |
417 | break; | |
e92c3511 | 418 | } |
4ef2de5a | 419 | cnt += in_ie[cnt + 1] + 2; /* goto next */ |
2865d42c LF |
420 | } |
421 | return match; | |
422 | } |