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                memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
 245                       vs_ie, vs_ie->len + 2);
 246                le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2);
 247                ie->mgmt_subtype_mask = cpu_to_le16(mask);
 248                ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
 249        }
 250
 251        *ie_ptr = ie;
 252        return 0;
 253}
 254
 255/* This function parses beacon IEs, probe response IEs, association response IEs
 256 * from cfg80211_ap_settings->beacon and sets these IE to FW.
 257 */
 258static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
 259                                            struct cfg80211_beacon_data *data)
 260{
 261        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
 262        u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
 263        u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
 264        int ret = 0;
 265
 266        if (data->beacon_ies && data->beacon_ies_len) {
 267                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 268                                     &beacon_ie, MGMT_MASK_BEACON,
 269                                     WLAN_OUI_MICROSOFT,
 270                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 271                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 272                                     &beacon_ie, MGMT_MASK_BEACON,
 273                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 274        }
 275
 276        if (data->proberesp_ies && data->proberesp_ies_len) {
 277                mwifiex_update_vs_ie(data->proberesp_ies,
 278                                     data->proberesp_ies_len, &pr_ie,
 279                                     MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
 280                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 281                mwifiex_update_vs_ie(data->proberesp_ies,
 282                                     data->proberesp_ies_len, &pr_ie,
 283                                     MGMT_MASK_PROBE_RESP,
 284                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 285        }
 286
 287        if (data->assocresp_ies && data->assocresp_ies_len) {
 288                mwifiex_update_vs_ie(data->assocresp_ies,
 289                                     data->assocresp_ies_len, &ar_ie,
 290                                     MGMT_MASK_ASSOC_RESP |
 291                                     MGMT_MASK_REASSOC_RESP,
 292                                     WLAN_OUI_MICROSOFT,
 293                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 294                mwifiex_update_vs_ie(data->assocresp_ies,
 295                                     data->assocresp_ies_len, &ar_ie,
 296                                     MGMT_MASK_ASSOC_RESP |
 297                                     MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
 298                                     WLAN_OUI_TYPE_WFA_P2P);
 299        }
 300
 301        if (beacon_ie || pr_ie || ar_ie) {
 302                ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
 303                                                   &beacon_idx, pr_ie,
 304                                                   &pr_idx, ar_ie, &ar_idx);
 305                if (ret)
 306                        goto done;
 307        }
 308
 309        priv->beacon_idx = beacon_idx;
 310        priv->proberesp_idx = pr_idx;
 311        priv->assocresp_idx = ar_idx;
 312
 313done:
 314        kfree(beacon_ie);
 315        kfree(pr_ie);
 316        kfree(ar_ie);
 317
 318        return ret;
 319}
 320
 321/* This function parses  head and tail IEs, from cfg80211_beacon_data and sets
 322 * these IE to FW.
 323 */
 324static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv,
 325                                      struct cfg80211_beacon_data *info)
 326{
 327        struct mwifiex_ie *gen_ie;
 328        struct ieee_types_header *hdr;
 329        struct ieee80211_vendor_ie *vendorhdr;
 330        u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
 331        int left_len, parsed_len = 0;
 332
 333        if (!info->tail || !info->tail_len)
 334                return 0;
 335
 336        gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
 337        if (!gen_ie)
 338                return -ENOMEM;
 339
 340        left_len = info->tail_len;
 341
 342        /* Many IEs are generated in FW by parsing bss configuration.
 343         * Let's not add them here; else we may end up duplicating these IEs
 344         */
 345        while (left_len > sizeof(struct ieee_types_header)) {
 346                hdr = (void *)(info->tail + parsed_len);
 347                switch (hdr->element_id) {
 348                case WLAN_EID_SSID:
 349                case WLAN_EID_SUPP_RATES:
 350                case WLAN_EID_COUNTRY:
 351                case WLAN_EID_PWR_CONSTRAINT:
 352                case WLAN_EID_ERP_INFO:
 353                case WLAN_EID_EXT_SUPP_RATES:
 354                case WLAN_EID_HT_CAPABILITY:
 355                case WLAN_EID_HT_OPERATION:
 356                case WLAN_EID_VHT_CAPABILITY:
 357                case WLAN_EID_VHT_OPERATION:
 358                        break;
 359                case WLAN_EID_VENDOR_SPECIFIC:
 360                        /* Skip only Microsoft WMM IE */
 361                        if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
 362                                                    WLAN_OUI_TYPE_MICROSOFT_WMM,
 363                                                    (const u8 *)hdr,
 364                                                    hdr->len + sizeof(struct ieee_types_header)))
 365                                break;
 366                        /* fall through */
 367                default:
 368                        memcpy(gen_ie->ie_buffer + ie_len, hdr,
 369                               hdr->len + sizeof(struct ieee_types_header));
 370                        ie_len += hdr->len + sizeof(struct ieee_types_header);
 371                        break;
 372                }
 373                left_len -= hdr->len + sizeof(struct ieee_types_header);
 374                parsed_len += hdr->len + sizeof(struct ieee_types_header);
 375        }
 376
 377        /* parse only WPA vendor IE from tail, WMM IE is configured by
 378         * bss_config command
 379         */
 380        vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
 381                                                    WLAN_OUI_TYPE_MICROSOFT_WPA,
 382                                                    info->tail, info->tail_len);
 383        if (vendorhdr) {
 384                memcpy(gen_ie->ie_buffer + ie_len, vendorhdr,
 385                       vendorhdr->len + sizeof(struct ieee_types_header));
 386                ie_len += vendorhdr->len + sizeof(struct ieee_types_header);
 387        }
 388
 389        if (!ie_len) {
 390                kfree(gen_ie);
 391                return 0;
 392        }
 393
 394        gen_ie->ie_index = cpu_to_le16(gen_idx);
 395        gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
 396                                                MGMT_MASK_PROBE_RESP |
 397                                                MGMT_MASK_ASSOC_RESP);
 398        gen_ie->ie_length = cpu_to_le16(ie_len);
 399
 400        if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
 401                                         NULL, NULL)) {
 402                kfree(gen_ie);
 403                return -1;
 404        }
 405
 406        priv->gen_idx = gen_idx;
 407        kfree(gen_ie);
 408        return 0;
 409}
 410
 411/* This function parses different IEs-head & tail IEs, beacon IEs,
 412 * probe response IEs, association response IEs from cfg80211_ap_settings
 413 * function and sets these IE to FW.
 414 */
 415int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
 416                         struct cfg80211_beacon_data *info)
 417{
 418        int ret;
 419
 420        ret = mwifiex_uap_parse_tail_ies(priv, info);
 421
 422        if (ret)
 423                return ret;
 424
 425        return mwifiex_set_mgmt_beacon_data_ies(priv, info);
 426}
 427
 428/* This function removes management IE set */
 429int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
 430{
 431        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
 432        struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL;
 433        int ret = 0;
 434
 435        if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) {
 436                gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
 437                if (!gen_ie)
 438                        return -ENOMEM;
 439
 440                gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
 441                gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 442                gen_ie->ie_length = 0;
 443                if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
 444                                                 NULL, &priv->proberesp_idx,
 445                                                 NULL, &priv->assocresp_idx)) {
 446                        ret = -1;
 447                        goto done;
 448                }
 449
 450                priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
 451        }
 452
 453        if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
 454                beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 455                if (!beacon_ie) {
 456                        ret = -ENOMEM;
 457                        goto done;
 458                }
 459                beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
 460                beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 461                beacon_ie->ie_length = 0;
 462        }
 463        if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
 464                pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 465                if (!pr_ie) {
 466                        ret = -ENOMEM;
 467                        goto done;
 468                }
 469                pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
 470                pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 471                pr_ie->ie_length = 0;
 472        }
 473        if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
 474                ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 475                if (!ar_ie) {
 476                        ret = -ENOMEM;
 477                        goto done;
 478                }
 479                ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
 480                ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 481                ar_ie->ie_length = 0;
 482        }
 483
 484        if (beacon_ie || pr_ie || ar_ie)
 485                ret = mwifiex_update_uap_custom_ie(priv,
 486                                                   beacon_ie, &priv->beacon_idx,
 487                                                   pr_ie, &priv->proberesp_idx,
 488                                                   ar_ie, &priv->assocresp_idx);
 489
 490done:
 491        kfree(gen_ie);
 492        kfree(beacon_ie);
 493        kfree(pr_ie);
 494        kfree(ar_ie);
 495
 496        return ret;
 497}
 498