linux/drivers/staging/rtl8712/ieee80211.c
<<
>>
Prefs
   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
  37static const u8 WPA_OUI_TYPE[] = {0x00, 0x50, 0xf2, 1};
  38static const u8 WPA_CIPHER_SUITE_NONE[] = {0x00, 0x50, 0xf2, 0};
  39static const u8 WPA_CIPHER_SUITE_WEP40[] = {0x00, 0x50, 0xf2, 1};
  40static const u8 WPA_CIPHER_SUITE_TKIP[] = {0x00, 0x50, 0xf2, 2};
  41static const u8 WPA_CIPHER_SUITE_CCMP[] = {0x00, 0x50, 0xf2, 4};
  42static const u8 WPA_CIPHER_SUITE_WEP104[] = {0x00, 0x50, 0xf2, 5};
  43
  44static const u8 RSN_CIPHER_SUITE_NONE[] = {0x00, 0x0f, 0xac, 0};
  45static const u8 RSN_CIPHER_SUITE_WEP40[] = {0x00, 0x0f, 0xac, 1};
  46static const u8 RSN_CIPHER_SUITE_TKIP[] = {0x00, 0x0f, 0xac, 2};
  47static const u8 RSN_CIPHER_SUITE_CCMP[] = {0x00, 0x0f, 0xac, 4};
  48static 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
  55static 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
  62static 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
  73uint 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
  86uint 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 */
 100u8 *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/*----------------------------------------------------------------------------
 111index: the information element id index, limit is the limit for search
 112-----------------------------------------------------------------------------*/
 113u8 *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
 138static 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
 159static 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
 173int r8712_generate_ie(struct registry_priv *pregistrypriv)
 174{
 175        int sz = 0, rateLen;
 176        struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
 177        u8 *ie = pdev_network->IEs;
 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);
 216        return sz;
 217}
 218
 219unsigned 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                }
 244check_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
 254unsigned 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
 259static 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
 274static 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
 289int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
 290                 int *pairwise_cipher)
 291{
 292        int i;
 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 _SUCCESS;
 328}
 329
 330int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher,
 331                  int *pairwise_cipher)
 332{
 333        int i;
 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 _SUCCESS;
 368}
 369
 370int 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
 400int 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}
 422