linux/drivers/net/wireless/marvell/mwifiex/ie.c
<<
>>
Prefs
   1/*
   2 * Marvell Wireless LAN device driver: management IE handling- setting and
   3 * deleting IE.
   4 *
   5 * Copyright (C) 2012-2014, Marvell International Ltd.
   6 *
   7 * This software file (the "File") is distributed by Marvell International
   8 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
   9 * (the "License").  You may use, redistribute and/or modify this File in
  10 * accordance with the terms and conditions of the License, a copy of which
  11 * is available by writing to the Free Software Foundation, Inc.,
  12 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
  13 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  14 *
  15 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
  16 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
  17 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
  18 * this warranty disclaimer.
  19 */
  20
  21#include "main.h"
  22
  23/* This function checks if current IE index is used by any on other interface.
  24 * Return: -1: yes, current IE index is used by someone else.
  25 *          0: no, current IE index is NOT used by other interface.
  26 */
  27static int
  28mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
  29{
  30        int i;
  31        struct mwifiex_adapter *adapter = priv->adapter;
  32        struct mwifiex_ie *ie;
  33
  34        for (i = 0; i < adapter->priv_num; i++) {
  35                if (adapter->priv[i] != priv) {
  36                        ie = &adapter->priv[i]->mgmt_ie[idx];
  37                        if (ie->mgmt_subtype_mask && ie->ie_length)
  38                                return -1;
  39                }
  40        }
  41
  42        return 0;
  43}
  44
  45/* Get unused IE index. This index will be used for setting new IE */
  46static int
  47mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
  48                       struct mwifiex_ie *ie, u16 *index)
  49{
  50        u16 mask, len, i;
  51
  52        for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
  53                mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
  54                len = le16_to_cpu(ie->ie_length);
  55
  56                if (mask == MWIFIEX_AUTO_IDX_MASK)
  57                        continue;
  58
  59                if (mask == subtype_mask) {
  60                        if (len > IEEE_MAX_IE_SIZE)
  61                                continue;
  62
  63                        *index = i;
  64                        return 0;
  65                }
  66
  67                if (!priv->mgmt_ie[i].ie_length) {
  68                        if (mwifiex_ie_index_used_by_other_intf(priv, i))
  69                                continue;
  70
  71                        *index = i;
  72                        return 0;
  73                }
  74        }
  75
  76        return -1;
  77}
  78
  79/* This function prepares IE data buffer for command to be sent to FW */
  80static int
  81mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
  82                             struct mwifiex_ie_list *ie_list)
  83{
  84        u16 travel_len, index, mask;
  85        s16 input_len, tlv_len;
  86        struct mwifiex_ie *ie;
  87        u8 *tmp;
  88
  89        input_len = le16_to_cpu(ie_list->len);
  90        travel_len = sizeof(struct mwifiex_ie_types_header);
  91
  92        ie_list->len = 0;
  93
  94        while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
  95                ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
  96                tlv_len = le16_to_cpu(ie->ie_length);
  97                travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
  98
  99                if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
 100                        return -1;
 101                index = le16_to_cpu(ie->ie_index);
 102                mask = le16_to_cpu(ie->mgmt_subtype_mask);
 103
 104                if (index == MWIFIEX_AUTO_IDX_MASK) {
 105                        /* automatic addition */
 106                        if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
 107                                return -1;
 108                        if (index == MWIFIEX_AUTO_IDX_MASK)
 109                                return -1;
 110
 111                        tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
 112                        memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
 113                        priv->mgmt_ie[index].ie_length = ie->ie_length;
 114                        priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
 115                        priv->mgmt_ie[index].mgmt_subtype_mask =
 116                                                        cpu_to_le16(mask);
 117
 118                        ie->ie_index = cpu_to_le16(index);
 119                } else {
 120                        if (mask != MWIFIEX_DELETE_MASK)
 121                                return -1;
 122                        /*
 123                         * Check if this index is being used on any
 124                         * other interface.
 125                         */
 126                        if (mwifiex_ie_index_used_by_other_intf(priv, index))
 127                                return -1;
 128
 129                        ie->ie_length = 0;
 130                        memcpy(&priv->mgmt_ie[index], ie,
 131                               sizeof(struct mwifiex_ie));
 132                }
 133
 134                le16_unaligned_add_cpu(&ie_list->len,
 135                                       le16_to_cpu(
 136                                            priv->mgmt_ie[index].ie_length) +
 137                                       MWIFIEX_IE_HDR_SIZE);
 138                input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
 139        }
 140
 141        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
 142                return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
 143                                        HostCmd_ACT_GEN_SET,
 144                                        UAP_CUSTOM_IE_I, ie_list, true);
 145
 146        return 0;
 147}
 148
 149/* Copy individual custom IEs for beacon, probe response and assoc response
 150 * and prepare single structure for IE setting.
 151 * This function also updates allocated IE indices from driver.
 152 */
 153static int
 154mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
 155                             struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
 156                             struct mwifiex_ie *pr_ie, u16 *probe_idx,
 157                             struct mwifiex_ie *ar_ie, u16 *assoc_idx)
 158{
 159        struct mwifiex_ie_list *ap_custom_ie;
 160        u8 *pos;
 161        u16 len;
 162        int ret;
 163
 164        ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
 165        if (!ap_custom_ie)
 166                return -ENOMEM;
 167
 168        ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
 169        pos = (u8 *)ap_custom_ie->ie_list;
 170
 171        if (beacon_ie) {
 172                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 173                      le16_to_cpu(beacon_ie->ie_length);
 174                memcpy(pos, beacon_ie, len);
 175                pos += len;
 176                le16_unaligned_add_cpu(&ap_custom_ie->len, len);
 177        }
 178        if (pr_ie) {
 179                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 180                      le16_to_cpu(pr_ie->ie_length);
 181                memcpy(pos, pr_ie, len);
 182                pos += len;
 183                le16_unaligned_add_cpu(&ap_custom_ie->len, len);
 184        }
 185        if (ar_ie) {
 186                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 187                      le16_to_cpu(ar_ie->ie_length);
 188                memcpy(pos, ar_ie, len);
 189                pos += len;
 190                le16_unaligned_add_cpu(&ap_custom_ie->len, len);
 191        }
 192
 193        ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
 194
 195        pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
 196        if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
 197                /* save beacon ie index after auto-indexing */
 198                *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
 199                len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
 200                      le16_to_cpu(beacon_ie->ie_length);
 201                pos += len;
 202        }
 203        if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
 204                /* save probe resp ie index after auto-indexing */
 205                *probe_idx = *((u16 *)pos);
 206                len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
 207                      le16_to_cpu(pr_ie->ie_length);
 208                pos += len;
 209        }
 210        if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
 211                /* save assoc resp ie index after auto-indexing */
 212                *assoc_idx = *((u16 *)pos);
 213
 214        kfree(ap_custom_ie);
 215        return ret;
 216}
 217
 218/* This function checks if the vendor specified IE is present in passed buffer
 219 * and copies it to mwifiex_ie structure.
 220 * Function takes pointer to struct mwifiex_ie pointer as argument.
 221 * If the vendor specified IE is present then memory is allocated for
 222 * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
 223 * this memory.
 224 */
 225static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
 226                                struct mwifiex_ie **ie_ptr, u16 mask,
 227                                unsigned int oui, u8 oui_type)
 228{
 229        struct ieee_types_header *vs_ie;
 230        struct mwifiex_ie *ie = *ie_ptr;
 231        const u8 *vendor_ie;
 232
 233        vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
 234        if (vendor_ie) {
 235                if (!*ie_ptr) {
 236                        *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
 237                                          GFP_KERNEL);
 238                        if (!*ie_ptr)
 239                                return -ENOMEM;
 240                        ie = *ie_ptr;
 241                }
 242
 243                vs_ie = (struct ieee_types_header *)vendor_ie;
 244                if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 >
 245                        IEEE_MAX_IE_SIZE)
 246                        return -EINVAL;
 247                memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
 248                       vs_ie, vs_ie->len + 2);
 249                le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2);
 250                ie->mgmt_subtype_mask = cpu_to_le16(mask);
 251                ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
 252        }
 253
 254        *ie_ptr = ie;
 255        return 0;
 256}
 257
 258/* This function parses beacon IEs, probe response IEs, association response IEs
 259 * from cfg80211_ap_settings->beacon and sets these IE to FW.
 260 */
 261static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
 262                                            struct cfg80211_beacon_data *data)
 263{
 264        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
 265        u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
 266        u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
 267        int ret = 0;
 268
 269        if (data->beacon_ies && data->beacon_ies_len) {
 270                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 271                                     &beacon_ie, MGMT_MASK_BEACON,
 272                                     WLAN_OUI_MICROSOFT,
 273                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 274                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 275                                     &beacon_ie, MGMT_MASK_BEACON,
 276                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 277        }
 278
 279        if (data->proberesp_ies && data->proberesp_ies_len) {
 280                mwifiex_update_vs_ie(data->proberesp_ies,
 281                                     data->proberesp_ies_len, &pr_ie,
 282                                     MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
 283                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 284                mwifiex_update_vs_ie(data->proberesp_ies,
 285                                     data->proberesp_ies_len, &pr_ie,
 286                                     MGMT_MASK_PROBE_RESP,
 287                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 288        }
 289
 290        if (data->assocresp_ies && data->assocresp_ies_len) {
 291                mwifiex_update_vs_ie(data->assocresp_ies,
 292                                     data->assocresp_ies_len, &ar_ie,
 293                                     MGMT_MASK_ASSOC_RESP |
 294                                     MGMT_MASK_REASSOC_RESP,
 295                                     WLAN_OUI_MICROSOFT,
 296                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 297                mwifiex_update_vs_ie(data->assocresp_ies,
 298                                     data->assocresp_ies_len, &ar_ie,
 299                                     MGMT_MASK_ASSOC_RESP |
 300                                     MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
 301                                     WLAN_OUI_TYPE_WFA_P2P);
 302        }
 303
 304        if (beacon_ie || pr_ie || ar_ie) {
 305                ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
 306                                                   &beacon_idx, pr_ie,
 307                                                   &pr_idx, ar_ie, &ar_idx);
 308                if (ret)
 309                        goto done;
 310        }
 311
 312        priv->beacon_idx = beacon_idx;
 313        priv->proberesp_idx = pr_idx;
 314        priv->assocresp_idx = ar_idx;
 315
 316done:
 317        kfree(beacon_ie);
 318        kfree(pr_ie);
 319        kfree(ar_ie);
 320
 321        return ret;
 322}
 323
 324/* This function parses  head and tail IEs, from cfg80211_beacon_data and sets
 325 * these IE to FW.
 326 */
 327static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
 328                                      struct cfg80211_beacon_data *info)
 329{
 330        struct mwifiex_ie *gen_ie;
 331        struct ieee_types_header *hdr;
 332        struct ieee80211_vendor_ie *vendorhdr;
 333        u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
 334        int left_len, parsed_len = 0;
 335        unsigned int token_len;
 336        int err = 0;
 337
 338        if (!info->tail || !info->tail_len)
 339                return 0;
 340
 341        gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
 342        if (!gen_ie)
 343                return -ENOMEM;
 344
 345        left_len = info->tail_len;
 346
 347        /* Many IEs are generated in FW by parsing bss configuration.
 348         * Let's not add them here; else we may end up duplicating these IEs
 349         */
 350        while (left_len > sizeof(struct ieee_types_header)) {
 351                hdr = (void *)(info->tail + parsed_len);
 352                token_len = hdr->len + sizeof(struct ieee_types_header);
 353                if (token_len > left_len) {
 354                        err = -EINVAL;
 355                        goto out;
 356                }
 357
 358                switch (hdr->element_id) {
 359                case WLAN_EID_SSID:
 360                case WLAN_EID_SUPP_RATES:
 361                case WLAN_EID_COUNTRY:
 362                case WLAN_EID_PWR_CONSTRAINT:
 363                case WLAN_EID_ERP_INFO:
 364                case WLAN_EID_EXT_SUPP_RATES:
 365                case WLAN_EID_HT_CAPABILITY:
 366                case WLAN_EID_HT_OPERATION:
 367                case WLAN_EID_VHT_CAPABILITY:
 368                case WLAN_EID_VHT_OPERATION:
 369                        break;
 370                case WLAN_EID_VENDOR_SPECIFIC:
 371                        /* Skip only Microsoft WMM IE */
 372                        if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
 373                                                    WLAN_OUI_TYPE_MICROSOFT_WMM,
 374                                                    (const u8 *)hdr,
 375                                                    token_len))
 376                                break;
 377                        /* fall through */
 378                default:
 379                        if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
 380                                err = -EINVAL;
 381                                goto out;
 382                        }
 383                        memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
 384                        ie_len += token_len;
 385                        break;
 386                }
 387                left_len -= token_len;
 388                parsed_len += token_len;
 389        }
 390
 391        /* parse only WPA vendor IE from tail, WMM IE is configured by
 392         * bss_config command
 393         */
 394        vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
 395                                                    WLAN_OUI_TYPE_MICROSOFT_WPA,
 396                                                    info->tail, info->tail_len);
 397        if (vendorhdr) {
 398                token_len = vendorhdr->len + sizeof(struct ieee_types_header);
 399                if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
 400                        err = -EINVAL;
 401                        goto out;
 402                }
 403                memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
 404                ie_len += token_len;
 405        }
 406
 407        if (!ie_len)
 408                goto out;
 409
 410        gen_ie->ie_index = cpu_to_le16(gen_idx);
 411        gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
 412                                                MGMT_MASK_PROBE_RESP |
 413                                                MGMT_MASK_ASSOC_RESP);
 414        gen_ie->ie_length = cpu_to_le16(ie_len);
 415
 416        if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
 417                                         NULL, NULL)) {
 418                err = -EINVAL;
 419                goto out;
 420        }
 421
 422        priv->gen_idx = gen_idx;
 423
 424 out:
 425        kfree(gen_ie);
 426        return err;
 427}
 428
 429/* This function parses different IEs-head & tail IEs, beacon IEs,
 430 * probe response IEs, association response IEs from cfg80211_ap_settings
 431 * function and sets these IE to FW.
 432 */
 433int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
 434                         struct cfg80211_beacon_data *info)
 435{
 436        int ret;
 437
 438        ret = mwifiex_uap_parse_tail_ies(priv, info);
 439
 440        if (ret)
 441                return ret;
 442
 443        return mwifiex_set_mgmt_beacon_data_ies(priv, info);
 444}
 445
 446/* This function removes management IE set */
 447int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
 448{
 449        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
 450        struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
 451        int ret = 0;
 452
 453        if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
 454                gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
 455                if (!gen_ie)
 456                        return -ENOMEM;
 457
 458                gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
 459                gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 460                gen_ie->ie_length = 0;
 461                if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
 462                                                 NULL, &priv->proberesp_idx,
 463                                                 NULL, &priv->assocresp_idx)) {
 464                        ret = -1;
 465                        goto done;
 466                }
 467
 468                priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
 469        }
 470
 471        if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
 472                beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 473                if (!beacon_ie) {
 474                        ret = -ENOMEM;
 475                        goto done;
 476                }
 477                beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
 478                beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 479                beacon_ie->ie_length = 0;
 480        }
 481        if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
 482                pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 483                if (!pr_ie) {
 484                        ret = -ENOMEM;
 485                        goto done;
 486                }
 487                pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
 488                pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 489                pr_ie->ie_length = 0;
 490        }
 491        if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
 492                ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 493                if (!ar_ie) {
 494                        ret = -ENOMEM;
 495                        goto done;
 496                }
 497                ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
 498                ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 499                ar_ie->ie_length = 0;
 500        }
 501
 502        if (beacon_ie || pr_ie || ar_ie)
 503                ret = mwifiex_update_uap_custom_ie(priv,
 504                                                   beacon_ie, &priv->beacon_idx,
 505                                                   pr_ie, &priv->proberesp_idx,
 506                                                   ar_ie, &priv->assocresp_idx);
 507
 508done:
 509        kfree(gen_ie);
 510        kfree(beacon_ie);
 511        kfree(pr_ie);
 512        kfree(ar_ie);
 513
 514        return ret;
 515}
 516