root/drivers/net/wireless/marvell/mwifiex/ie.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mwifiex_ie_index_used_by_other_intf
  2. mwifiex_ie_get_autoidx
  3. mwifiex_update_autoindex_ies
  4. mwifiex_update_uap_custom_ie
  5. mwifiex_update_vs_ie
  6. mwifiex_set_mgmt_beacon_data_ies
  7. mwifiex_uap_parse_tail_ies
  8. mwifiex_set_mgmt_ies
  9. mwifiex_del_mgmt_ies

   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  */
  27 static int
  28 mwifiex_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 */
  46 static int
  47 mwifiex_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 */
  80 static int
  81 mwifiex_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  */
 153 static int
 154 mwifiex_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  */
 225 static 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  */
 261 static 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 
 316 done:
 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  */
 327 static 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  */
 433 int 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 */
 447 int 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 
 508 done:
 509         kfree(gen_ie);
 510         kfree(beacon_ie);
 511         kfree(pr_ie);
 512         kfree(ar_ie);
 513 
 514         return ret;
 515 }

/* [<][>][^][v][top][bottom][index][help] */