linux/drivers/net/wireless/mwifiex/sta_ioctl.c
<<
>>
Prefs
   1/*
   2 * Marvell Wireless LAN device driver: functions for station ioctl
   3 *
   4 * Copyright (C) 2011-2014, Marvell International Ltd.
   5 *
   6 * This software file (the "File") is distributed by Marvell International
   7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
   8 * (the "License").  You may use, redistribute and/or modify this File in
   9 * accordance with the terms and conditions of the License, a copy of which
  10 * is available by writing to the Free Software Foundation, Inc.,
  11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
  12 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  13 *
  14 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
  15 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
  16 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
  17 * this warranty disclaimer.
  18 */
  19
  20#include "decl.h"
  21#include "ioctl.h"
  22#include "util.h"
  23#include "fw.h"
  24#include "main.h"
  25#include "wmm.h"
  26#include "11n.h"
  27#include "cfg80211.h"
  28
  29static int disconnect_on_suspend;
  30module_param(disconnect_on_suspend, int, 0644);
  31
  32/*
  33 * Copies the multicast address list from device to driver.
  34 *
  35 * This function does not validate the destination memory for
  36 * size, and the calling function must ensure enough memory is
  37 * available.
  38 */
  39int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
  40                            struct net_device *dev)
  41{
  42        int i = 0;
  43        struct netdev_hw_addr *ha;
  44
  45        netdev_for_each_mc_addr(ha, dev)
  46                memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
  47
  48        return i;
  49}
  50
  51/*
  52 * Wait queue completion handler.
  53 *
  54 * This function waits on a cmd wait queue. It also cancels the pending
  55 * request after waking up, in case of errors.
  56 */
  57int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
  58                                struct cmd_ctrl_node *cmd_queued)
  59{
  60        int status;
  61
  62        /* Wait for completion */
  63        status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
  64                                                  *(cmd_queued->condition),
  65                                                  (12 * HZ));
  66        if (status <= 0) {
  67                if (status == 0)
  68                        status = -ETIMEDOUT;
  69                mwifiex_dbg(adapter, ERROR,
  70                            "cmd_wait_q terminated: %d\n", status);
  71                mwifiex_cancel_all_pending_cmd(adapter);
  72                return status;
  73        }
  74
  75        status = adapter->cmd_wait_q.status;
  76        adapter->cmd_wait_q.status = 0;
  77
  78        return status;
  79}
  80
  81/*
  82 * This function prepares the correct firmware command and
  83 * issues it to set the multicast list.
  84 *
  85 * This function can be used to enable promiscuous mode, or enable all
  86 * multicast packets, or to enable selective multicast.
  87 */
  88int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
  89                                struct mwifiex_multicast_list *mcast_list)
  90{
  91        int ret = 0;
  92        u16 old_pkt_filter;
  93
  94        old_pkt_filter = priv->curr_pkt_filter;
  95
  96        if (mcast_list->mode == MWIFIEX_PROMISC_MODE) {
  97                mwifiex_dbg(priv->adapter, INFO,
  98                            "info: Enable Promiscuous mode\n");
  99                priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
 100                priv->curr_pkt_filter &=
 101                        ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
 102        } else {
 103                /* Multicast */
 104                priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE;
 105                if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) {
 106                        mwifiex_dbg(priv->adapter, INFO,
 107                                    "info: Enabling All Multicast!\n");
 108                        priv->curr_pkt_filter |=
 109                                HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
 110                } else {
 111                        priv->curr_pkt_filter &=
 112                                ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
 113                        mwifiex_dbg(priv->adapter, INFO,
 114                                    "info: Set multicast list=%d\n",
 115                                    mcast_list->num_multicast_addr);
 116                        /* Send multicast addresses to firmware */
 117                        ret = mwifiex_send_cmd(priv,
 118                                               HostCmd_CMD_MAC_MULTICAST_ADR,
 119                                               HostCmd_ACT_GEN_SET, 0,
 120                                               mcast_list, false);
 121                }
 122        }
 123        mwifiex_dbg(priv->adapter, INFO,
 124                    "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
 125                    old_pkt_filter, priv->curr_pkt_filter);
 126        if (old_pkt_filter != priv->curr_pkt_filter) {
 127                ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
 128                                       HostCmd_ACT_GEN_SET,
 129                                       0, &priv->curr_pkt_filter, false);
 130        }
 131
 132        return ret;
 133}
 134
 135/*
 136 * This function fills bss descriptor structure using provided
 137 * information.
 138 * beacon_ie buffer is allocated in this function. It is caller's
 139 * responsibility to free the memory.
 140 */
 141int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
 142                              struct cfg80211_bss *bss,
 143                              struct mwifiex_bssdescriptor *bss_desc)
 144{
 145        u8 *beacon_ie;
 146        size_t beacon_ie_len;
 147        struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
 148        const struct cfg80211_bss_ies *ies;
 149
 150        rcu_read_lock();
 151        ies = rcu_dereference(bss->ies);
 152        beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
 153        beacon_ie_len = ies->len;
 154        bss_desc->timestamp = ies->tsf;
 155        rcu_read_unlock();
 156
 157        if (!beacon_ie) {
 158                mwifiex_dbg(priv->adapter, ERROR,
 159                            " failed to alloc beacon_ie\n");
 160                return -ENOMEM;
 161        }
 162
 163        memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
 164        bss_desc->rssi = bss->signal;
 165        /* The caller of this function will free beacon_ie */
 166        bss_desc->beacon_buf = beacon_ie;
 167        bss_desc->beacon_buf_size = beacon_ie_len;
 168        bss_desc->beacon_period = bss->beacon_interval;
 169        bss_desc->cap_info_bitmap = bss->capability;
 170        bss_desc->bss_band = bss_priv->band;
 171        bss_desc->fw_tsf = bss_priv->fw_tsf;
 172        if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
 173                mwifiex_dbg(priv->adapter, INFO,
 174                            "info: InterpretIE: AP WEP enabled\n");
 175                bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
 176        } else {
 177                bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL;
 178        }
 179        if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS)
 180                bss_desc->bss_mode = NL80211_IFTYPE_ADHOC;
 181        else
 182                bss_desc->bss_mode = NL80211_IFTYPE_STATION;
 183
 184        /* Disable 11ac by default. Enable it only where there
 185         * exist VHT_CAP IE in AP beacon
 186         */
 187        bss_desc->disable_11ac = true;
 188
 189        if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
 190                bss_desc->sensed_11h = true;
 191
 192        return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 193}
 194
 195void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
 196{
 197        if (priv->adapter->dt_node) {
 198                char txpwr[] = {"marvell,00_txpwrlimit"};
 199
 200                memcpy(&txpwr[8], priv->adapter->country_code, 2);
 201                mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
 202        }
 203}
 204
 205static int mwifiex_process_country_ie(struct mwifiex_private *priv,
 206                                      struct cfg80211_bss *bss)
 207{
 208        const u8 *country_ie;
 209        u8 country_ie_len;
 210        struct mwifiex_802_11d_domain_reg *domain_info =
 211                                        &priv->adapter->domain_reg;
 212
 213        rcu_read_lock();
 214        country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
 215        if (!country_ie) {
 216                rcu_read_unlock();
 217                return 0;
 218        }
 219
 220        country_ie_len = country_ie[1];
 221        if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
 222                rcu_read_unlock();
 223                return 0;
 224        }
 225
 226        if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
 227                rcu_read_unlock();
 228                mwifiex_dbg(priv->adapter, INFO,
 229                            "11D: skip setting domain info in FW\n");
 230                return 0;
 231        }
 232        memcpy(priv->adapter->country_code, &country_ie[2], 2);
 233
 234        domain_info->country_code[0] = country_ie[2];
 235        domain_info->country_code[1] = country_ie[3];
 236        domain_info->country_code[2] = ' ';
 237
 238        country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
 239
 240        domain_info->no_of_triplet =
 241                country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
 242
 243        memcpy((u8 *)domain_info->triplet,
 244               &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
 245
 246        rcu_read_unlock();
 247
 248        if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
 249                             HostCmd_ACT_GEN_SET, 0, NULL, false)) {
 250                mwifiex_dbg(priv->adapter, ERROR,
 251                            "11D: setting domain info in FW fail\n");
 252                return -1;
 253        }
 254
 255        mwifiex_dnld_txpwr_table(priv);
 256
 257        return 0;
 258}
 259
 260/*
 261 * In Ad-Hoc mode, the IBSS is created if not found in scan list.
 262 * In both Ad-Hoc and infra mode, an deauthentication is performed
 263 * first.
 264 */
 265int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
 266                      struct cfg80211_ssid *req_ssid)
 267{
 268        int ret;
 269        struct mwifiex_adapter *adapter = priv->adapter;
 270        struct mwifiex_bssdescriptor *bss_desc = NULL;
 271
 272        priv->scan_block = false;
 273
 274        if (bss) {
 275                mwifiex_process_country_ie(priv, bss);
 276
 277                /* Allocate and fill new bss descriptor */
 278                bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
 279                                   GFP_KERNEL);
 280                if (!bss_desc)
 281                        return -ENOMEM;
 282
 283                ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
 284                if (ret)
 285                        goto done;
 286        }
 287
 288        if (priv->bss_mode == NL80211_IFTYPE_STATION ||
 289            priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
 290                u8 config_bands;
 291
 292                if (!bss_desc)
 293                        return -1;
 294
 295                if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
 296                                                HostCmd_SCAN_RADIO_TYPE_BG) {
 297                        config_bands = BAND_B | BAND_G | BAND_GN;
 298                } else {
 299                        config_bands = BAND_A | BAND_AN;
 300                        if (adapter->fw_bands & BAND_AAC)
 301                                config_bands |= BAND_AAC;
 302                }
 303
 304                if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
 305                        adapter->config_bands = config_bands;
 306
 307                ret = mwifiex_check_network_compatibility(priv, bss_desc);
 308                if (ret)
 309                        goto done;
 310
 311                if (mwifiex_11h_get_csa_closed_channel(priv) ==
 312                                                        (u8)bss_desc->channel) {
 313                        mwifiex_dbg(adapter, ERROR,
 314                                    "Attempt to reconnect on csa closed chan(%d)\n",
 315                                    bss_desc->channel);
 316                        goto done;
 317                }
 318
 319                mwifiex_dbg(adapter, INFO,
 320                            "info: SSID found in scan list ...\t"
 321                            "associating...\n");
 322
 323                mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 324                if (netif_carrier_ok(priv->netdev))
 325                        netif_carrier_off(priv->netdev);
 326
 327                /* Clear any past association response stored for
 328                 * application retrieval */
 329                priv->assoc_rsp_size = 0;
 330                ret = mwifiex_associate(priv, bss_desc);
 331
 332                /* If auth type is auto and association fails using open mode,
 333                 * try to connect using shared mode */
 334                if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
 335                    priv->sec_info.is_authtype_auto &&
 336                    priv->sec_info.wep_enabled) {
 337                        priv->sec_info.authentication_mode =
 338                                                NL80211_AUTHTYPE_SHARED_KEY;
 339                        ret = mwifiex_associate(priv, bss_desc);
 340                }
 341
 342                if (bss)
 343                        cfg80211_put_bss(priv->adapter->wiphy, bss);
 344        } else {
 345                /* Adhoc mode */
 346                /* If the requested SSID matches current SSID, return */
 347                if (bss_desc && bss_desc->ssid.ssid_len &&
 348                    (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor.
 349                                       ssid, &bss_desc->ssid))) {
 350                        ret = 0;
 351                        goto done;
 352                }
 353
 354                priv->adhoc_is_link_sensed = false;
 355
 356                ret = mwifiex_check_network_compatibility(priv, bss_desc);
 357
 358                mwifiex_stop_net_dev_queue(priv->netdev, adapter);
 359                if (netif_carrier_ok(priv->netdev))
 360                        netif_carrier_off(priv->netdev);
 361
 362                if (!ret) {
 363                        mwifiex_dbg(adapter, INFO,
 364                                    "info: network found in scan\t"
 365                                    " list. Joining...\n");
 366                        ret = mwifiex_adhoc_join(priv, bss_desc);
 367                        if (bss)
 368                                cfg80211_put_bss(priv->adapter->wiphy, bss);
 369                } else {
 370                        mwifiex_dbg(adapter, INFO,
 371                                    "info: Network not found in\t"
 372                                    "the list, creating adhoc with ssid = %s\n",
 373                                    req_ssid->ssid);
 374                        ret = mwifiex_adhoc_start(priv, req_ssid);
 375                }
 376        }
 377
 378done:
 379        /* beacon_ie buffer was allocated in function
 380         * mwifiex_fill_new_bss_desc(). Free it now.
 381         */
 382        if (bss_desc)
 383                kfree(bss_desc->beacon_buf);
 384        kfree(bss_desc);
 385        return ret;
 386}
 387
 388/*
 389 * IOCTL request handler to set host sleep configuration.
 390 *
 391 * This function prepares the correct firmware command and
 392 * issues it.
 393 */
 394int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
 395                          int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
 396
 397{
 398        struct mwifiex_adapter *adapter = priv->adapter;
 399        int status = 0;
 400        u32 prev_cond = 0;
 401
 402        if (!hs_cfg)
 403                return -ENOMEM;
 404
 405        switch (action) {
 406        case HostCmd_ACT_GEN_SET:
 407                if (adapter->pps_uapsd_mode) {
 408                        mwifiex_dbg(adapter, INFO,
 409                                    "info: Host Sleep IOCTL\t"
 410                                    "is blocked in UAPSD/PPS mode\n");
 411                        status = -1;
 412                        break;
 413                }
 414                if (hs_cfg->is_invoke_hostcmd) {
 415                        if (hs_cfg->conditions == HS_CFG_CANCEL) {
 416                                if (!adapter->is_hs_configured)
 417                                        /* Already cancelled */
 418                                        break;
 419                                /* Save previous condition */
 420                                prev_cond = le32_to_cpu(adapter->hs_cfg
 421                                                        .conditions);
 422                                adapter->hs_cfg.conditions =
 423                                                cpu_to_le32(hs_cfg->conditions);
 424                        } else if (hs_cfg->conditions) {
 425                                adapter->hs_cfg.conditions =
 426                                                cpu_to_le32(hs_cfg->conditions);
 427                                adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
 428                                if (hs_cfg->gap)
 429                                        adapter->hs_cfg.gap = (u8)hs_cfg->gap;
 430                        } else if (adapter->hs_cfg.conditions ==
 431                                   cpu_to_le32(HS_CFG_CANCEL)) {
 432                                /* Return failure if no parameters for HS
 433                                   enable */
 434                                status = -1;
 435                                break;
 436                        }
 437
 438                        status = mwifiex_send_cmd(priv,
 439                                                  HostCmd_CMD_802_11_HS_CFG_ENH,
 440                                                  HostCmd_ACT_GEN_SET, 0,
 441                                                  &adapter->hs_cfg,
 442                                                  cmd_type == MWIFIEX_SYNC_CMD);
 443
 444                        if (hs_cfg->conditions == HS_CFG_CANCEL)
 445                                /* Restore previous condition */
 446                                adapter->hs_cfg.conditions =
 447                                                cpu_to_le32(prev_cond);
 448                } else {
 449                        adapter->hs_cfg.conditions =
 450                                                cpu_to_le32(hs_cfg->conditions);
 451                        adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
 452                        adapter->hs_cfg.gap = (u8)hs_cfg->gap;
 453                }
 454                break;
 455        case HostCmd_ACT_GEN_GET:
 456                hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
 457                hs_cfg->gpio = adapter->hs_cfg.gpio;
 458                hs_cfg->gap = adapter->hs_cfg.gap;
 459                break;
 460        default:
 461                status = -1;
 462                break;
 463        }
 464
 465        return status;
 466}
 467
 468/*
 469 * Sends IOCTL request to cancel the existing Host Sleep configuration.
 470 *
 471 * This function allocates the IOCTL request buffer, fills it
 472 * with requisite parameters and calls the IOCTL handler.
 473 */
 474int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
 475{
 476        struct mwifiex_ds_hs_cfg hscfg;
 477
 478        hscfg.conditions = HS_CFG_CANCEL;
 479        hscfg.is_invoke_hostcmd = true;
 480
 481        return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
 482                                    cmd_type, &hscfg);
 483}
 484EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
 485
 486/*
 487 * Sends IOCTL request to cancel the existing Host Sleep configuration.
 488 *
 489 * This function allocates the IOCTL request buffer, fills it
 490 * with requisite parameters and calls the IOCTL handler.
 491 */
 492int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
 493{
 494        struct mwifiex_ds_hs_cfg hscfg;
 495        struct mwifiex_private *priv;
 496        int i;
 497
 498        if (disconnect_on_suspend) {
 499                for (i = 0; i < adapter->priv_num; i++) {
 500                        priv = adapter->priv[i];
 501                        if (priv)
 502                                mwifiex_deauthenticate(priv, NULL);
 503                }
 504        }
 505
 506        if (adapter->hs_activated) {
 507                mwifiex_dbg(adapter, CMD,
 508                            "cmd: HS Already activated\n");
 509                return true;
 510        }
 511
 512        adapter->hs_activate_wait_q_woken = false;
 513
 514        memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
 515        hscfg.is_invoke_hostcmd = true;
 516
 517        adapter->hs_enabling = true;
 518        mwifiex_cancel_all_pending_cmd(adapter);
 519
 520        if (mwifiex_set_hs_params(mwifiex_get_priv(adapter,
 521                                                   MWIFIEX_BSS_ROLE_STA),
 522                                  HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD,
 523                                  &hscfg)) {
 524                mwifiex_dbg(adapter, ERROR,
 525                            "IOCTL request HS enable failed\n");
 526                return false;
 527        }
 528
 529        if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
 530                                             adapter->hs_activate_wait_q_woken,
 531                                             (10 * HZ)) <= 0) {
 532                mwifiex_dbg(adapter, ERROR,
 533                            "hs_activate_wait_q terminated\n");
 534                return false;
 535        }
 536
 537        return true;
 538}
 539EXPORT_SYMBOL_GPL(mwifiex_enable_hs);
 540
 541/*
 542 * IOCTL request handler to get BSS information.
 543 *
 544 * This function collates the information from different driver structures
 545 * to send to the user.
 546 */
 547int mwifiex_get_bss_info(struct mwifiex_private *priv,
 548                         struct mwifiex_bss_info *info)
 549{
 550        struct mwifiex_adapter *adapter = priv->adapter;
 551        struct mwifiex_bssdescriptor *bss_desc;
 552
 553        if (!info)
 554                return -1;
 555
 556        bss_desc = &priv->curr_bss_params.bss_descriptor;
 557
 558        info->bss_mode = priv->bss_mode;
 559
 560        memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
 561
 562        memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
 563
 564        info->bss_chan = bss_desc->channel;
 565
 566        memcpy(info->country_code, adapter->country_code,
 567               IEEE80211_COUNTRY_STRING_LEN);
 568
 569        info->media_connected = priv->media_connected;
 570
 571        info->max_power_level = priv->max_tx_power_level;
 572        info->min_power_level = priv->min_tx_power_level;
 573
 574        info->adhoc_state = priv->adhoc_state;
 575
 576        info->bcn_nf_last = priv->bcn_nf_last;
 577
 578        if (priv->sec_info.wep_enabled)
 579                info->wep_status = true;
 580        else
 581                info->wep_status = false;
 582
 583        info->is_hs_configured = adapter->is_hs_configured;
 584        info->is_deep_sleep = adapter->is_deep_sleep;
 585
 586        return 0;
 587}
 588
 589/*
 590 * The function disables auto deep sleep mode.
 591 */
 592int mwifiex_disable_auto_ds(struct mwifiex_private *priv)
 593{
 594        struct mwifiex_ds_auto_ds auto_ds;
 595
 596        auto_ds.auto_ds = DEEP_SLEEP_OFF;
 597
 598        return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 599                                DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
 600}
 601EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds);
 602
 603/*
 604 * Sends IOCTL request to get the data rate.
 605 *
 606 * This function allocates the IOCTL request buffer, fills it
 607 * with requisite parameters and calls the IOCTL handler.
 608 */
 609int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate)
 610{
 611        int ret;
 612
 613        ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY,
 614                               HostCmd_ACT_GEN_GET, 0, NULL, true);
 615
 616        if (!ret) {
 617                if (priv->is_data_rate_auto)
 618                        *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate,
 619                                                           priv->tx_htinfo);
 620                else
 621                        *rate = priv->data_rate;
 622        }
 623
 624        return ret;
 625}
 626
 627/*
 628 * IOCTL request handler to set tx power configuration.
 629 *
 630 * This function prepares the correct firmware command and
 631 * issues it.
 632 *
 633 * For non-auto power mode, all the following power groups are set -
 634 *      - Modulation class HR/DSSS
 635 *      - Modulation class OFDM
 636 *      - Modulation class HTBW20
 637 *      - Modulation class HTBW40
 638 */
 639int mwifiex_set_tx_power(struct mwifiex_private *priv,
 640                         struct mwifiex_power_cfg *power_cfg)
 641{
 642        int ret;
 643        struct host_cmd_ds_txpwr_cfg *txp_cfg;
 644        struct mwifiex_types_power_group *pg_tlv;
 645        struct mwifiex_power_group *pg;
 646        u8 *buf;
 647        u16 dbm = 0;
 648
 649        if (!power_cfg->is_power_auto) {
 650                dbm = (u16) power_cfg->power_level;
 651                if ((dbm < priv->min_tx_power_level) ||
 652                    (dbm > priv->max_tx_power_level)) {
 653                        mwifiex_dbg(priv->adapter, ERROR,
 654                                    "txpower value %d dBm\t"
 655                                    "is out of range (%d dBm-%d dBm)\n",
 656                                    dbm, priv->min_tx_power_level,
 657                                    priv->max_tx_power_level);
 658                        return -1;
 659                }
 660        }
 661        buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
 662        if (!buf)
 663                return -ENOMEM;
 664
 665        txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf;
 666        txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
 667        if (!power_cfg->is_power_auto) {
 668                txp_cfg->mode = cpu_to_le32(1);
 669                pg_tlv = (struct mwifiex_types_power_group *)
 670                         (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
 671                pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
 672                pg_tlv->length =
 673                        cpu_to_le16(4 * sizeof(struct mwifiex_power_group));
 674                pg = (struct mwifiex_power_group *)
 675                     (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
 676                      + sizeof(struct mwifiex_types_power_group));
 677                /* Power group for modulation class HR/DSSS */
 678                pg->first_rate_code = 0x00;
 679                pg->last_rate_code = 0x03;
 680                pg->modulation_class = MOD_CLASS_HR_DSSS;
 681                pg->power_step = 0;
 682                pg->power_min = (s8) dbm;
 683                pg->power_max = (s8) dbm;
 684                pg++;
 685                /* Power group for modulation class OFDM */
 686                pg->first_rate_code = 0x00;
 687                pg->last_rate_code = 0x07;
 688                pg->modulation_class = MOD_CLASS_OFDM;
 689                pg->power_step = 0;
 690                pg->power_min = (s8) dbm;
 691                pg->power_max = (s8) dbm;
 692                pg++;
 693                /* Power group for modulation class HTBW20 */
 694                pg->first_rate_code = 0x00;
 695                pg->last_rate_code = 0x20;
 696                pg->modulation_class = MOD_CLASS_HT;
 697                pg->power_step = 0;
 698                pg->power_min = (s8) dbm;
 699                pg->power_max = (s8) dbm;
 700                pg->ht_bandwidth = HT_BW_20;
 701                pg++;
 702                /* Power group for modulation class HTBW40 */
 703                pg->first_rate_code = 0x00;
 704                pg->last_rate_code = 0x20;
 705                pg->modulation_class = MOD_CLASS_HT;
 706                pg->power_step = 0;
 707                pg->power_min = (s8) dbm;
 708                pg->power_max = (s8) dbm;
 709                pg->ht_bandwidth = HT_BW_40;
 710        }
 711        ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG,
 712                               HostCmd_ACT_GEN_SET, 0, buf, true);
 713
 714        kfree(buf);
 715        return ret;
 716}
 717
 718/*
 719 * IOCTL request handler to get power save mode.
 720 *
 721 * This function prepares the correct firmware command and
 722 * issues it.
 723 */
 724int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode)
 725{
 726        int ret;
 727        struct mwifiex_adapter *adapter = priv->adapter;
 728        u16 sub_cmd;
 729
 730        if (*ps_mode)
 731                adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
 732        else
 733                adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
 734        sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
 735        ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 736                               sub_cmd, BITMAP_STA_PS, NULL, true);
 737        if ((!ret) && (sub_cmd == DIS_AUTO_PS))
 738                ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH,
 739                                       GET_PS, 0, NULL, false);
 740
 741        return ret;
 742}
 743
 744/*
 745 * IOCTL request handler to set/reset WPA IE.
 746 *
 747 * The supplied WPA IE is treated as a opaque buffer. Only the first field
 748 * is checked to determine WPA version. If buffer length is zero, the existing
 749 * WPA IE is reset.
 750 */
 751static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv,
 752                                     u8 *ie_data_ptr, u16 ie_len)
 753{
 754        if (ie_len) {
 755                if (ie_len > sizeof(priv->wpa_ie)) {
 756                        mwifiex_dbg(priv->adapter, ERROR,
 757                                    "failed to copy WPA IE, too big\n");
 758                        return -1;
 759                }
 760                memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
 761                priv->wpa_ie_len = (u8) ie_len;
 762                mwifiex_dbg(priv->adapter, CMD,
 763                            "cmd: Set Wpa_ie_len=%d IE=%#x\n",
 764                            priv->wpa_ie_len, priv->wpa_ie[0]);
 765
 766                if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
 767                        priv->sec_info.wpa_enabled = true;
 768                } else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
 769                        priv->sec_info.wpa2_enabled = true;
 770                } else {
 771                        priv->sec_info.wpa_enabled = false;
 772                        priv->sec_info.wpa2_enabled = false;
 773                }
 774        } else {
 775                memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
 776                priv->wpa_ie_len = 0;
 777                mwifiex_dbg(priv->adapter, INFO,
 778                            "info: reset wpa_ie_len=%d IE=%#x\n",
 779                            priv->wpa_ie_len, priv->wpa_ie[0]);
 780                priv->sec_info.wpa_enabled = false;
 781                priv->sec_info.wpa2_enabled = false;
 782        }
 783
 784        return 0;
 785}
 786
 787/*
 788 * IOCTL request handler to set/reset WAPI IE.
 789 *
 790 * The supplied WAPI IE is treated as a opaque buffer. Only the first field
 791 * is checked to internally enable WAPI. If buffer length is zero, the existing
 792 * WAPI IE is reset.
 793 */
 794static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
 795                               u8 *ie_data_ptr, u16 ie_len)
 796{
 797        if (ie_len) {
 798                if (ie_len > sizeof(priv->wapi_ie)) {
 799                        mwifiex_dbg(priv->adapter, ERROR,
 800                                    "info: failed to copy WAPI IE, too big\n");
 801                        return -1;
 802                }
 803                memcpy(priv->wapi_ie, ie_data_ptr, ie_len);
 804                priv->wapi_ie_len = ie_len;
 805                mwifiex_dbg(priv->adapter, CMD,
 806                            "cmd: Set wapi_ie_len=%d IE=%#x\n",
 807                            priv->wapi_ie_len, priv->wapi_ie[0]);
 808
 809                if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY)
 810                        priv->sec_info.wapi_enabled = true;
 811        } else {
 812                memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie));
 813                priv->wapi_ie_len = ie_len;
 814                mwifiex_dbg(priv->adapter, INFO,
 815                            "info: Reset wapi_ie_len=%d IE=%#x\n",
 816                            priv->wapi_ie_len, priv->wapi_ie[0]);
 817                priv->sec_info.wapi_enabled = false;
 818        }
 819        return 0;
 820}
 821
 822/*
 823 * IOCTL request handler to set/reset WPS IE.
 824 *
 825 * The supplied WPS IE is treated as a opaque buffer. Only the first field
 826 * is checked to internally enable WPS. If buffer length is zero, the existing
 827 * WPS IE is reset.
 828 */
 829static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
 830                               u8 *ie_data_ptr, u16 ie_len)
 831{
 832        if (ie_len) {
 833                if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
 834                        mwifiex_dbg(priv->adapter, ERROR,
 835                                    "info: failed to copy WPS IE, too big\n");
 836                        return -1;
 837                }
 838
 839                priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
 840                if (!priv->wps_ie)
 841                        return -ENOMEM;
 842
 843                memcpy(priv->wps_ie, ie_data_ptr, ie_len);
 844                priv->wps_ie_len = ie_len;
 845                mwifiex_dbg(priv->adapter, CMD,
 846                            "cmd: Set wps_ie_len=%d IE=%#x\n",
 847                            priv->wps_ie_len, priv->wps_ie[0]);
 848        } else {
 849                kfree(priv->wps_ie);
 850                priv->wps_ie_len = ie_len;
 851                mwifiex_dbg(priv->adapter, INFO,
 852                            "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
 853        }
 854        return 0;
 855}
 856
 857/*
 858 * IOCTL request handler to set WAPI key.
 859 *
 860 * This function prepares the correct firmware command and
 861 * issues it.
 862 */
 863static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv,
 864                               struct mwifiex_ds_encrypt_key *encrypt_key)
 865{
 866
 867        return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
 868                                HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED,
 869                                encrypt_key, true);
 870}
 871
 872/*
 873 * IOCTL request handler to set WEP network key.
 874 *
 875 * This function prepares the correct firmware command and
 876 * issues it, after validation checks.
 877 */
 878static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
 879                              struct mwifiex_ds_encrypt_key *encrypt_key)
 880{
 881        struct mwifiex_adapter *adapter = priv->adapter;
 882        int ret;
 883        struct mwifiex_wep_key *wep_key;
 884        int index;
 885
 886        if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
 887                priv->wep_key_curr_index = 0;
 888        wep_key = &priv->wep_key[priv->wep_key_curr_index];
 889        index = encrypt_key->key_index;
 890        if (encrypt_key->key_disable) {
 891                priv->sec_info.wep_enabled = 0;
 892        } else if (!encrypt_key->key_len) {
 893                /* Copy the required key as the current key */
 894                wep_key = &priv->wep_key[index];
 895                if (!wep_key->key_length) {
 896                        mwifiex_dbg(adapter, ERROR,
 897                                    "key not set, so cannot enable it\n");
 898                        return -1;
 899                }
 900
 901                if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) {
 902                        memcpy(encrypt_key->key_material,
 903                               wep_key->key_material, wep_key->key_length);
 904                        encrypt_key->key_len = wep_key->key_length;
 905                }
 906
 907                priv->wep_key_curr_index = (u16) index;
 908                priv->sec_info.wep_enabled = 1;
 909        } else {
 910                wep_key = &priv->wep_key[index];
 911                memset(wep_key, 0, sizeof(struct mwifiex_wep_key));
 912                /* Copy the key in the driver */
 913                memcpy(wep_key->key_material,
 914                       encrypt_key->key_material,
 915                       encrypt_key->key_len);
 916                wep_key->key_index = index;
 917                wep_key->key_length = encrypt_key->key_len;
 918                priv->sec_info.wep_enabled = 1;
 919        }
 920        if (wep_key->key_length) {
 921                void *enc_key;
 922
 923                if (encrypt_key->key_disable) {
 924                        memset(&priv->wep_key[index], 0,
 925                               sizeof(struct mwifiex_wep_key));
 926                        if (wep_key->key_length)
 927                                goto done;
 928                        }
 929
 930                if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
 931                        enc_key = encrypt_key;
 932                else
 933                        enc_key = NULL;
 934
 935                /* Send request to firmware */
 936                ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
 937                                       HostCmd_ACT_GEN_SET, 0, enc_key, false);
 938                if (ret)
 939                        return ret;
 940        }
 941
 942done:
 943        if (priv->sec_info.wep_enabled)
 944                priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE;
 945        else
 946                priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE;
 947
 948        ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL,
 949                               HostCmd_ACT_GEN_SET, 0,
 950                               &priv->curr_pkt_filter, true);
 951
 952        return ret;
 953}
 954
 955/*
 956 * IOCTL request handler to set WPA key.
 957 *
 958 * This function prepares the correct firmware command and
 959 * issues it, after validation checks.
 960 *
 961 * Current driver only supports key length of up to 32 bytes.
 962 *
 963 * This function can also be used to disable a currently set key.
 964 */
 965static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv,
 966                              struct mwifiex_ds_encrypt_key *encrypt_key)
 967{
 968        int ret;
 969        u8 remove_key = false;
 970        struct host_cmd_ds_802_11_key_material *ibss_key;
 971
 972        /* Current driver only supports key length of up to 32 bytes */
 973        if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
 974                mwifiex_dbg(priv->adapter, ERROR,
 975                            "key length too long\n");
 976                return -1;
 977        }
 978
 979        if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
 980                /*
 981                 * IBSS/WPA-None uses only one key (Group) for both receiving
 982                 * and sending unicast and multicast packets.
 983                 */
 984                /* Send the key as PTK to firmware */
 985                encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
 986                ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
 987                                       HostCmd_ACT_GEN_SET,
 988                                       KEY_INFO_ENABLED, encrypt_key, false);
 989                if (ret)
 990                        return ret;
 991
 992                ibss_key = &priv->aes_key;
 993                memset(ibss_key, 0,
 994                       sizeof(struct host_cmd_ds_802_11_key_material));
 995                /* Copy the key in the driver */
 996                memcpy(ibss_key->key_param_set.key, encrypt_key->key_material,
 997                       encrypt_key->key_len);
 998                memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len,
 999                       sizeof(ibss_key->key_param_set.key_len));
1000                ibss_key->key_param_set.key_type_id
1001                        = cpu_to_le16(KEY_TYPE_ID_TKIP);
1002                ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED);
1003
1004                /* Send the key as GTK to firmware */
1005                encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST;
1006        }
1007
1008        if (!encrypt_key->key_index)
1009                encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST;
1010
1011        if (remove_key)
1012                ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1013                                       HostCmd_ACT_GEN_SET,
1014                                       !KEY_INFO_ENABLED, encrypt_key, true);
1015        else
1016                ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL,
1017                                       HostCmd_ACT_GEN_SET,
1018                                       KEY_INFO_ENABLED, encrypt_key, true);
1019
1020        return ret;
1021}
1022
1023/*
1024 * IOCTL request handler to set/get network keys.
1025 *
1026 * This is a generic key handling function which supports WEP, WPA
1027 * and WAPI.
1028 */
1029static int
1030mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv,
1031                              struct mwifiex_ds_encrypt_key *encrypt_key)
1032{
1033        int status;
1034
1035        if (encrypt_key->is_wapi_key)
1036                status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key);
1037        else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
1038                status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key);
1039        else
1040                status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key);
1041        return status;
1042}
1043
1044/*
1045 * This function returns the driver version.
1046 */
1047int
1048mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
1049                               int max_len)
1050{
1051        union {
1052                __le32 l;
1053                u8 c[4];
1054        } ver;
1055        char fw_ver[32];
1056
1057        ver.l = cpu_to_le32(adapter->fw_release_number);
1058        sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
1059
1060        snprintf(version, max_len, driver_version, fw_ver);
1061
1062        mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version);
1063
1064        return 0;
1065}
1066
1067/*
1068 * Sends IOCTL request to set encoding parameters.
1069 *
1070 * This function allocates the IOCTL request buffer, fills it
1071 * with requisite parameters and calls the IOCTL handler.
1072 */
1073int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp,
1074                       const u8 *key, int key_len, u8 key_index,
1075                       const u8 *mac_addr, int disable)
1076{
1077        struct mwifiex_ds_encrypt_key encrypt_key;
1078
1079        memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key));
1080        encrypt_key.key_len = key_len;
1081        encrypt_key.key_index = key_index;
1082
1083        if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
1084                encrypt_key.is_igtk_key = true;
1085
1086        if (!disable) {
1087                if (key_len)
1088                        memcpy(encrypt_key.key_material, key, key_len);
1089                else
1090                        encrypt_key.is_current_wep_key = true;
1091
1092                if (mac_addr)
1093                        memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1094                if (kp && kp->seq && kp->seq_len) {
1095                        memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
1096                        encrypt_key.pn_len = kp->seq_len;
1097                        encrypt_key.is_rx_seq_valid = true;
1098                }
1099        } else {
1100                if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
1101                        return 0;
1102                encrypt_key.key_disable = true;
1103                if (mac_addr)
1104                        memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
1105        }
1106
1107        return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key);
1108}
1109
1110/*
1111 * Sends IOCTL request to get extended version.
1112 *
1113 * This function allocates the IOCTL request buffer, fills it
1114 * with requisite parameters and calls the IOCTL handler.
1115 */
1116int
1117mwifiex_get_ver_ext(struct mwifiex_private *priv)
1118{
1119        struct mwifiex_ver_ext ver_ext;
1120
1121        memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext));
1122        if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
1123                             HostCmd_ACT_GEN_GET, 0, &ver_ext, true))
1124                return -1;
1125
1126        return 0;
1127}
1128
1129int
1130mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action,
1131                           struct ieee80211_channel *chan,
1132                           unsigned int duration)
1133{
1134        struct host_cmd_ds_remain_on_chan roc_cfg;
1135        u8 sc;
1136
1137        memset(&roc_cfg, 0, sizeof(roc_cfg));
1138        roc_cfg.action = cpu_to_le16(action);
1139        if (action == HostCmd_ACT_GEN_SET) {
1140                roc_cfg.band_cfg = chan->band;
1141                sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
1142                roc_cfg.band_cfg |= (sc << 2);
1143
1144                roc_cfg.channel =
1145                        ieee80211_frequency_to_channel(chan->center_freq);
1146                roc_cfg.duration = cpu_to_le32(duration);
1147        }
1148        if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN,
1149                             action, 0, &roc_cfg, true)) {
1150                mwifiex_dbg(priv->adapter, ERROR,
1151                            "failed to remain on channel\n");
1152                return -1;
1153        }
1154
1155        return roc_cfg.status;
1156}
1157
1158/*
1159 * Sends IOCTL request to get statistics information.
1160 *
1161 * This function allocates the IOCTL request buffer, fills it
1162 * with requisite parameters and calls the IOCTL handler.
1163 */
1164int
1165mwifiex_get_stats_info(struct mwifiex_private *priv,
1166                       struct mwifiex_ds_get_stats *log)
1167{
1168        return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG,
1169                                HostCmd_ACT_GEN_GET, 0, log, true);
1170}
1171
1172/*
1173 * IOCTL request handler to read/write register.
1174 *
1175 * This function prepares the correct firmware command and
1176 * issues it.
1177 *
1178 * Access to the following registers are supported -
1179 *      - MAC
1180 *      - BBP
1181 *      - RF
1182 *      - PMIC
1183 *      - CAU
1184 */
1185static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv,
1186                                        struct mwifiex_ds_reg_rw *reg_rw,
1187                                        u16 action)
1188{
1189        u16 cmd_no;
1190
1191        switch (le32_to_cpu(reg_rw->type)) {
1192        case MWIFIEX_REG_MAC:
1193                cmd_no = HostCmd_CMD_MAC_REG_ACCESS;
1194                break;
1195        case MWIFIEX_REG_BBP:
1196                cmd_no = HostCmd_CMD_BBP_REG_ACCESS;
1197                break;
1198        case MWIFIEX_REG_RF:
1199                cmd_no = HostCmd_CMD_RF_REG_ACCESS;
1200                break;
1201        case MWIFIEX_REG_PMIC:
1202                cmd_no = HostCmd_CMD_PMIC_REG_ACCESS;
1203                break;
1204        case MWIFIEX_REG_CAU:
1205                cmd_no = HostCmd_CMD_CAU_REG_ACCESS;
1206                break;
1207        default:
1208                return -1;
1209        }
1210
1211        return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
1212}
1213
1214/*
1215 * Sends IOCTL request to write to a register.
1216 *
1217 * This function allocates the IOCTL request buffer, fills it
1218 * with requisite parameters and calls the IOCTL handler.
1219 */
1220int
1221mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type,
1222                  u32 reg_offset, u32 reg_value)
1223{
1224        struct mwifiex_ds_reg_rw reg_rw;
1225
1226        reg_rw.type = cpu_to_le32(reg_type);
1227        reg_rw.offset = cpu_to_le32(reg_offset);
1228        reg_rw.value = cpu_to_le32(reg_value);
1229
1230        return mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_SET);
1231}
1232
1233/*
1234 * Sends IOCTL request to read from a register.
1235 *
1236 * This function allocates the IOCTL request buffer, fills it
1237 * with requisite parameters and calls the IOCTL handler.
1238 */
1239int
1240mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type,
1241                 u32 reg_offset, u32 *value)
1242{
1243        int ret;
1244        struct mwifiex_ds_reg_rw reg_rw;
1245
1246        reg_rw.type = cpu_to_le32(reg_type);
1247        reg_rw.offset = cpu_to_le32(reg_offset);
1248        ret = mwifiex_reg_mem_ioctl_reg_rw(priv, &reg_rw, HostCmd_ACT_GEN_GET);
1249
1250        if (ret)
1251                goto done;
1252
1253        *value = le32_to_cpu(reg_rw.value);
1254
1255done:
1256        return ret;
1257}
1258
1259/*
1260 * Sends IOCTL request to read from EEPROM.
1261 *
1262 * This function allocates the IOCTL request buffer, fills it
1263 * with requisite parameters and calls the IOCTL handler.
1264 */
1265int
1266mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes,
1267                    u8 *value)
1268{
1269        int ret;
1270        struct mwifiex_ds_read_eeprom rd_eeprom;
1271
1272        rd_eeprom.offset = cpu_to_le16((u16) offset);
1273        rd_eeprom.byte_count = cpu_to_le16((u16) bytes);
1274
1275        /* Send request to firmware */
1276        ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS,
1277                               HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true);
1278
1279        if (!ret)
1280                memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA);
1281        return ret;
1282}
1283
1284/*
1285 * This function sets a generic IE. In addition to generic IE, it can
1286 * also handle WPA, WPA2 and WAPI IEs.
1287 */
1288static int
1289mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
1290                          u16 ie_len)
1291{
1292        int ret = 0;
1293        struct ieee_types_vendor_header *pvendor_ie;
1294        const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
1295        const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
1296
1297        /* If the passed length is zero, reset the buffer */
1298        if (!ie_len) {
1299                priv->gen_ie_buf_len = 0;
1300                priv->wps.session_enable = false;
1301
1302                return 0;
1303        } else if (!ie_data_ptr) {
1304                return -1;
1305        }
1306        pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
1307        /* Test to see if it is a WPA IE, if not, then it is a gen IE */
1308        if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
1309             (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) ||
1310            (pvendor_ie->element_id == WLAN_EID_RSN)) {
1311
1312                /* IE is a WPA/WPA2 IE so call set_wpa function */
1313                ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len);
1314                priv->wps.session_enable = false;
1315
1316                return ret;
1317        } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
1318                /* IE is a WAPI IE so call set_wapi function */
1319                ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len);
1320
1321                return ret;
1322        }
1323        /*
1324         * Verify that the passed length is not larger than the
1325         * available space remaining in the buffer
1326         */
1327        if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
1328
1329                /* Test to see if it is a WPS IE, if so, enable
1330                 * wps session flag
1331                 */
1332                pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
1333                if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
1334                    (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) {
1335                        priv->wps.session_enable = true;
1336                        mwifiex_dbg(priv->adapter, INFO,
1337                                    "info: WPS Session Enabled.\n");
1338                        ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len);
1339                }
1340
1341                /* Append the passed data to the end of the
1342                   genIeBuffer */
1343                memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr,
1344                       ie_len);
1345                /* Increment the stored buffer length by the
1346                   size passed */
1347                priv->gen_ie_buf_len += ie_len;
1348        } else {
1349                /* Passed data does not fit in the remaining
1350                   buffer space */
1351                ret = -1;
1352        }
1353
1354        /* Return 0, or -1 for error case */
1355        return ret;
1356}
1357
1358/*
1359 * IOCTL request handler to set/get generic IE.
1360 *
1361 * In addition to various generic IEs, this function can also be
1362 * used to set the ARP filter.
1363 */
1364static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv,
1365                                     struct mwifiex_ds_misc_gen_ie *gen_ie,
1366                                     u16 action)
1367{
1368        struct mwifiex_adapter *adapter = priv->adapter;
1369
1370        switch (gen_ie->type) {
1371        case MWIFIEX_IE_TYPE_GEN_IE:
1372                if (action == HostCmd_ACT_GEN_GET) {
1373                        gen_ie->len = priv->wpa_ie_len;
1374                        memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
1375                } else {
1376                        mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data,
1377                                                  (u16) gen_ie->len);
1378                }
1379                break;
1380        case MWIFIEX_IE_TYPE_ARP_FILTER:
1381                memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
1382                if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
1383                        adapter->arp_filter_size = 0;
1384                        mwifiex_dbg(adapter, ERROR,
1385                                    "invalid ARP filter size\n");
1386                        return -1;
1387                } else {
1388                        memcpy(adapter->arp_filter, gen_ie->ie_data,
1389                               gen_ie->len);
1390                        adapter->arp_filter_size = gen_ie->len;
1391                }
1392                break;
1393        default:
1394                mwifiex_dbg(adapter, ERROR, "invalid IE type\n");
1395                return -1;
1396        }
1397        return 0;
1398}
1399
1400/*
1401 * Sends IOCTL request to set a generic IE.
1402 *
1403 * This function allocates the IOCTL request buffer, fills it
1404 * with requisite parameters and calls the IOCTL handler.
1405 */
1406int
1407mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len)
1408{
1409        struct mwifiex_ds_misc_gen_ie gen_ie;
1410
1411        if (ie_len > IEEE_MAX_IE_SIZE)
1412                return -EFAULT;
1413
1414        gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE;
1415        gen_ie.len = ie_len;
1416        memcpy(gen_ie.ie_data, ie, ie_len);
1417        if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET))
1418                return -EFAULT;
1419
1420        return 0;
1421}
1422