linux/drivers/net/wireless/mwifiex/ie.c
<<
>>
Prefs
   1/*
   2 * Marvell Wireless LAN device driver: management IE handling- setting and
   3 * deleting IE.
   4 *
   5 * Copyright (C) 2012, 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;
  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 > 0) {
  95                ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
  96                input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
  97                travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE;
  98
  99                index = le16_to_cpu(ie->ie_index);
 100                mask = le16_to_cpu(ie->mgmt_subtype_mask);
 101
 102                if (index == MWIFIEX_AUTO_IDX_MASK) {
 103                        /* automatic addition */
 104                        if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
 105                                return -1;
 106                        if (index == MWIFIEX_AUTO_IDX_MASK)
 107                                return -1;
 108
 109                        tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
 110                        memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
 111                        priv->mgmt_ie[index].ie_length = ie->ie_length;
 112                        priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
 113                        priv->mgmt_ie[index].mgmt_subtype_mask =
 114                                                        cpu_to_le16(mask);
 115
 116                        ie->ie_index = cpu_to_le16(index);
 117                } else {
 118                        if (mask != MWIFIEX_DELETE_MASK)
 119                                return -1;
 120                        /*
 121                         * Check if this index is being used on any
 122                         * other interface.
 123                         */
 124                        if (mwifiex_ie_index_used_by_other_intf(priv, index))
 125                                return -1;
 126
 127                        ie->ie_length = 0;
 128                        memcpy(&priv->mgmt_ie[index], ie,
 129                               sizeof(struct mwifiex_ie));
 130                }
 131
 132                le16_add_cpu(&ie_list->len,
 133                             le16_to_cpu(priv->mgmt_ie[index].ie_length) +
 134                             MWIFIEX_IE_HDR_SIZE);
 135        }
 136
 137        if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
 138                return mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG,
 139                                              HostCmd_ACT_GEN_SET,
 140                                              UAP_CUSTOM_IE_I, ie_list);
 141
 142        return 0;
 143}
 144
 145/* Copy individual custom IEs for beacon, probe response and assoc response
 146 * and prepare single structure for IE setting.
 147 * This function also updates allocated IE indices from driver.
 148 */
 149static int
 150mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
 151                             struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
 152                             struct mwifiex_ie *pr_ie, u16 *probe_idx,
 153                             struct mwifiex_ie *ar_ie, u16 *assoc_idx)
 154{
 155        struct mwifiex_ie_list *ap_custom_ie;
 156        u8 *pos;
 157        u16 len;
 158        int ret;
 159
 160        ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
 161        if (!ap_custom_ie)
 162                return -ENOMEM;
 163
 164        ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
 165        pos = (u8 *)ap_custom_ie->ie_list;
 166
 167        if (beacon_ie) {
 168                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 169                      le16_to_cpu(beacon_ie->ie_length);
 170                memcpy(pos, beacon_ie, len);
 171                pos += len;
 172                le16_add_cpu(&ap_custom_ie->len, len);
 173        }
 174        if (pr_ie) {
 175                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 176                      le16_to_cpu(pr_ie->ie_length);
 177                memcpy(pos, pr_ie, len);
 178                pos += len;
 179                le16_add_cpu(&ap_custom_ie->len, len);
 180        }
 181        if (ar_ie) {
 182                len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
 183                      le16_to_cpu(ar_ie->ie_length);
 184                memcpy(pos, ar_ie, len);
 185                pos += len;
 186                le16_add_cpu(&ap_custom_ie->len, len);
 187        }
 188
 189        ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
 190
 191        pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
 192        if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
 193                /* save beacon ie index after auto-indexing */
 194                *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
 195                len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
 196                      le16_to_cpu(beacon_ie->ie_length);
 197                pos += len;
 198        }
 199        if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
 200                /* save probe resp ie index after auto-indexing */
 201                *probe_idx = *((u16 *)pos);
 202                len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
 203                      le16_to_cpu(pr_ie->ie_length);
 204                pos += len;
 205        }
 206        if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
 207                /* save assoc resp ie index after auto-indexing */
 208                *assoc_idx = *((u16 *)pos);
 209
 210        kfree(ap_custom_ie);
 211        return ret;
 212}
 213
 214/* This function checks if the vendor specified IE is present in passed buffer
 215 * and copies it to mwifiex_ie structure.
 216 * Function takes pointer to struct mwifiex_ie pointer as argument.
 217 * If the vendor specified IE is present then memory is allocated for
 218 * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
 219 * this memory.
 220 */
 221static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
 222                                struct mwifiex_ie **ie_ptr, u16 mask,
 223                                unsigned int oui, u8 oui_type)
 224{
 225        struct ieee_types_header *vs_ie;
 226        struct mwifiex_ie *ie = *ie_ptr;
 227        const u8 *vendor_ie;
 228
 229        vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
 230        if (vendor_ie) {
 231                if (!*ie_ptr) {
 232                        *ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
 233                                          GFP_KERNEL);
 234                        if (!*ie_ptr)
 235                                return -ENOMEM;
 236                        ie = *ie_ptr;
 237                }
 238
 239                vs_ie = (struct ieee_types_header *)vendor_ie;
 240                memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
 241                       vs_ie, vs_ie->len + 2);
 242                le16_add_cpu(&ie->ie_length, vs_ie->len + 2);
 243                ie->mgmt_subtype_mask = cpu_to_le16(mask);
 244                ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
 245        }
 246
 247        *ie_ptr = ie;
 248        return 0;
 249}
 250
 251/* This function parses beacon IEs, probe response IEs, association response IEs
 252 * from cfg80211_ap_settings->beacon and sets these IE to FW.
 253 */
 254static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
 255                                            struct cfg80211_beacon_data *data)
 256{
 257        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
 258        u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
 259        u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
 260        int ret = 0;
 261
 262        if (data->beacon_ies && data->beacon_ies_len) {
 263                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 264                                     &beacon_ie, MGMT_MASK_BEACON,
 265                                     WLAN_OUI_MICROSOFT,
 266                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 267                mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
 268                                     &beacon_ie, MGMT_MASK_BEACON,
 269                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 270        }
 271
 272        if (data->proberesp_ies && data->proberesp_ies_len) {
 273                mwifiex_update_vs_ie(data->proberesp_ies,
 274                                     data->proberesp_ies_len, &pr_ie,
 275                                     MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
 276                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 277                mwifiex_update_vs_ie(data->proberesp_ies,
 278                                     data->proberesp_ies_len, &pr_ie,
 279                                     MGMT_MASK_PROBE_RESP,
 280                                     WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
 281        }
 282
 283        if (data->assocresp_ies && data->assocresp_ies_len) {
 284                mwifiex_update_vs_ie(data->assocresp_ies,
 285                                     data->assocresp_ies_len, &ar_ie,
 286                                     MGMT_MASK_ASSOC_RESP |
 287                                     MGMT_MASK_REASSOC_RESP,
 288                                     WLAN_OUI_MICROSOFT,
 289                                     WLAN_OUI_TYPE_MICROSOFT_WPS);
 290                mwifiex_update_vs_ie(data->assocresp_ies,
 291                                     data->assocresp_ies_len, &ar_ie,
 292                                     MGMT_MASK_ASSOC_RESP |
 293                                     MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
 294                                     WLAN_OUI_TYPE_WFA_P2P);
 295        }
 296
 297        if (beacon_ie || pr_ie || ar_ie) {
 298                ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
 299                                                   &beacon_idx, pr_ie,
 300                                                   &pr_idx, ar_ie, &ar_idx);
 301                if (ret)
 302                        goto done;
 303        }
 304
 305        priv->beacon_idx = beacon_idx;
 306        priv->proberesp_idx = pr_idx;
 307        priv->assocresp_idx = ar_idx;
 308
 309done:
 310        kfree(beacon_ie);
 311        kfree(pr_ie);
 312        kfree(ar_ie);
 313
 314        return ret;
 315}
 316
 317/* This function parses different IEs-tail IEs, beacon IEs, probe response IEs,
 318 * association response IEs from cfg80211_ap_settings function and sets these IE
 319 * to FW.
 320 */
 321int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
 322                         struct cfg80211_beacon_data *info)
 323{
 324        struct mwifiex_ie *gen_ie;
 325        struct ieee_types_header *rsn_ie, *wpa_ie = NULL;
 326        u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
 327        const u8 *vendor_ie;
 328
 329        if (info->tail && info->tail_len) {
 330                gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 331                if (!gen_ie)
 332                        return -ENOMEM;
 333                gen_ie->ie_index = cpu_to_le16(rsn_idx);
 334                gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
 335                                                        MGMT_MASK_PROBE_RESP |
 336                                                        MGMT_MASK_ASSOC_RESP);
 337
 338                rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN,
 339                                                  info->tail, info->tail_len);
 340                if (rsn_ie) {
 341                        memcpy(gen_ie->ie_buffer, rsn_ie, rsn_ie->len + 2);
 342                        ie_len = rsn_ie->len + 2;
 343                        gen_ie->ie_length = cpu_to_le16(ie_len);
 344                }
 345
 346                vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
 347                                                    WLAN_OUI_TYPE_MICROSOFT_WPA,
 348                                                    info->tail,
 349                                                    info->tail_len);
 350                if (vendor_ie) {
 351                        wpa_ie = (struct ieee_types_header *)vendor_ie;
 352                        memcpy(gen_ie->ie_buffer + ie_len,
 353                               wpa_ie, wpa_ie->len + 2);
 354                        ie_len += wpa_ie->len + 2;
 355                        gen_ie->ie_length = cpu_to_le16(ie_len);
 356                }
 357
 358                if (rsn_ie || wpa_ie) {
 359                        if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx,
 360                                                         NULL, NULL,
 361                                                         NULL, NULL)) {
 362                                kfree(gen_ie);
 363                                return -1;
 364                        }
 365                        priv->rsn_idx = rsn_idx;
 366                }
 367
 368                kfree(gen_ie);
 369        }
 370
 371        return mwifiex_set_mgmt_beacon_data_ies(priv, info);
 372}
 373
 374/* This function removes management IE set */
 375int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
 376{
 377        struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
 378        struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
 379        int ret = 0;
 380
 381        if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) {
 382                rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 383                if (!rsn_ie)
 384                        return -ENOMEM;
 385
 386                rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx);
 387                rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 388                rsn_ie->ie_length = 0;
 389                if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx,
 390                                                 NULL, &priv->proberesp_idx,
 391                                                 NULL, &priv->assocresp_idx)) {
 392                        ret = -1;
 393                        goto done;
 394                }
 395
 396                priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
 397        }
 398
 399        if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
 400                beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 401                if (!beacon_ie) {
 402                        ret = -ENOMEM;
 403                        goto done;
 404                }
 405                beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
 406                beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 407                beacon_ie->ie_length = 0;
 408        }
 409        if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
 410                pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 411                if (!pr_ie) {
 412                        ret = -ENOMEM;
 413                        goto done;
 414                }
 415                pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
 416                pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 417                pr_ie->ie_length = 0;
 418        }
 419        if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
 420                ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
 421                if (!ar_ie) {
 422                        ret = -ENOMEM;
 423                        goto done;
 424                }
 425                ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
 426                ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
 427                ar_ie->ie_length = 0;
 428        }
 429
 430        if (beacon_ie || pr_ie || ar_ie)
 431                ret = mwifiex_update_uap_custom_ie(priv,
 432                                                   beacon_ie, &priv->beacon_idx,
 433                                                   pr_ie, &priv->proberesp_idx,
 434                                                   ar_ie, &priv->assocresp_idx);
 435
 436done:
 437        kfree(beacon_ie);
 438        kfree(pr_ie);
 439        kfree(ar_ie);
 440        kfree(rsn_ie);
 441
 442        return ret;
 443}
 444