173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen * nxpwifi: functions for station ioctl
473b01e57SJeff Chen *
573b01e57SJeff Chen * Copyright 2011-2024 NXP
673b01e57SJeff Chen */
773b01e57SJeff Chen
873b01e57SJeff Chen #include "cfg.h"
973b01e57SJeff Chen #include "util.h"
1073b01e57SJeff Chen #include "fw.h"
1173b01e57SJeff Chen #include "main.h"
1273b01e57SJeff Chen #include "cmdevt.h"
1373b01e57SJeff Chen #include "wmm.h"
1473b01e57SJeff Chen #include "11n.h"
1573b01e57SJeff Chen #include "cfg80211.h"
1673b01e57SJeff Chen
1773b01e57SJeff Chen static int disconnect_on_suspend;
1873b01e57SJeff Chen
1973b01e57SJeff Chen /* Copies the multicast address list from device to driver */
nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list * mlist,struct net_device * dev)2073b01e57SJeff Chen int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist,
2173b01e57SJeff Chen struct net_device *dev)
2273b01e57SJeff Chen {
2373b01e57SJeff Chen int i = 0;
2473b01e57SJeff Chen struct netdev_hw_addr *ha;
2573b01e57SJeff Chen
2673b01e57SJeff Chen netdev_for_each_mc_addr(ha, dev)
2773b01e57SJeff Chen memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
2873b01e57SJeff Chen
2973b01e57SJeff Chen return i;
3073b01e57SJeff Chen }
3173b01e57SJeff Chen
3273b01e57SJeff Chen /* Wait queue completion handler */
nxpwifi_wait_queue_complete(struct nxpwifi_adapter * adapter,struct cmd_ctrl_node * cmd_queued)3373b01e57SJeff Chen int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter,
3473b01e57SJeff Chen struct cmd_ctrl_node *cmd_queued)
3573b01e57SJeff Chen {
3673b01e57SJeff Chen int status;
3773b01e57SJeff Chen
3873b01e57SJeff Chen /* Wait for completion */
3973b01e57SJeff Chen status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
4073b01e57SJeff Chen *cmd_queued->condition,
4173b01e57SJeff Chen (12 * HZ));
4273b01e57SJeff Chen if (status <= 0) {
4373b01e57SJeff Chen if (status == 0)
4473b01e57SJeff Chen status = -ETIMEDOUT;
4573b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
4673b01e57SJeff Chen status);
4773b01e57SJeff Chen nxpwifi_cancel_all_pending_cmd(adapter);
48*068986fdSLinmao Li
49*068986fdSLinmao Li /*
50*068986fdSLinmao Li * The response path writes through cmd_node->data_buf. The
51*068986fdSLinmao Li * caller can release a stack-allocated data_buf after an
52*068986fdSLinmao Li * interrupted wait while a late response is still pending.
53*068986fdSLinmao Li * Detach it from the current command before returning.
54*068986fdSLinmao Li */
55*068986fdSLinmao Li spin_lock_bh(&adapter->nxpwifi_cmd_lock);
56*068986fdSLinmao Li if (adapter->curr_cmd == cmd_queued)
57*068986fdSLinmao Li adapter->curr_cmd->data_buf = NULL;
58*068986fdSLinmao Li spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
59*068986fdSLinmao Li
6073b01e57SJeff Chen return status;
6173b01e57SJeff Chen }
6273b01e57SJeff Chen
6373b01e57SJeff Chen status = adapter->cmd_wait_q.status;
6473b01e57SJeff Chen adapter->cmd_wait_q.status = 0;
6573b01e57SJeff Chen
6673b01e57SJeff Chen return status;
6773b01e57SJeff Chen }
6873b01e57SJeff Chen
6973b01e57SJeff Chen /* Set multicast list by issuing the proper firmware command */
7073b01e57SJeff Chen int
nxpwifi_request_set_multicast_list(struct nxpwifi_private * priv,struct nxpwifi_multicast_list * mcast_list)7173b01e57SJeff Chen nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv,
7273b01e57SJeff Chen struct nxpwifi_multicast_list *mcast_list)
7373b01e57SJeff Chen {
7473b01e57SJeff Chen int ret = 0;
7573b01e57SJeff Chen u16 old_pkt_filter;
7673b01e57SJeff Chen
7773b01e57SJeff Chen old_pkt_filter = priv->curr_pkt_filter;
7873b01e57SJeff Chen
7973b01e57SJeff Chen if (mcast_list->mode == NXPWIFI_PROMISC_MODE) {
8073b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
8173b01e57SJeff Chen "info: Enable Promiscuous mode\n");
8273b01e57SJeff Chen priv->curr_pkt_filter |= HOST_ACT_MAC_PROMISCUOUS_ENABLE;
8373b01e57SJeff Chen priv->curr_pkt_filter &=
8473b01e57SJeff Chen ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
8573b01e57SJeff Chen } else {
8673b01e57SJeff Chen /* Multicast */
8773b01e57SJeff Chen priv->curr_pkt_filter &= ~HOST_ACT_MAC_PROMISCUOUS_ENABLE;
8873b01e57SJeff Chen if (mcast_list->mode == NXPWIFI_ALL_MULTI_MODE) {
8973b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
9073b01e57SJeff Chen "info: Enabling All Multicast!\n");
9173b01e57SJeff Chen priv->curr_pkt_filter |=
9273b01e57SJeff Chen HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
9373b01e57SJeff Chen } else {
9473b01e57SJeff Chen priv->curr_pkt_filter &=
9573b01e57SJeff Chen ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
9673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
9773b01e57SJeff Chen "info: Set multicast list=%d\n",
9873b01e57SJeff Chen mcast_list->num_multicast_addr);
9973b01e57SJeff Chen /* Send multicast addresses to firmware */
10073b01e57SJeff Chen ret = nxpwifi_send_cmd(priv,
10173b01e57SJeff Chen HOST_CMD_MAC_MULTICAST_ADR,
10273b01e57SJeff Chen HOST_ACT_GEN_SET, 0,
10373b01e57SJeff Chen mcast_list, false);
10473b01e57SJeff Chen }
10573b01e57SJeff Chen }
10673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
10773b01e57SJeff Chen "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
10873b01e57SJeff Chen old_pkt_filter, priv->curr_pkt_filter);
10973b01e57SJeff Chen if (old_pkt_filter != priv->curr_pkt_filter) {
11073b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
11173b01e57SJeff Chen HOST_ACT_GEN_SET,
11273b01e57SJeff Chen 0, &priv->curr_pkt_filter, false);
11373b01e57SJeff Chen }
11473b01e57SJeff Chen
11573b01e57SJeff Chen return ret;
11673b01e57SJeff Chen }
11773b01e57SJeff Chen
11873b01e57SJeff Chen /* Fill BSS descriptor from cfg80211_bss */
nxpwifi_fill_new_bss_desc(struct nxpwifi_private * priv,struct cfg80211_bss * bss,struct nxpwifi_bssdescriptor * bss_desc)11973b01e57SJeff Chen int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv,
12073b01e57SJeff Chen struct cfg80211_bss *bss,
12173b01e57SJeff Chen struct nxpwifi_bssdescriptor *bss_desc)
12273b01e57SJeff Chen {
12373b01e57SJeff Chen u8 *beacon_ie;
12473b01e57SJeff Chen size_t beacon_ie_len;
12573b01e57SJeff Chen struct nxpwifi_bss_priv *bss_priv = (void *)bss->priv;
12673b01e57SJeff Chen const struct cfg80211_bss_ies *ies;
12773b01e57SJeff Chen
12873b01e57SJeff Chen rcu_read_lock();
12973b01e57SJeff Chen ies = rcu_dereference(bss->ies);
13073b01e57SJeff Chen beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
13173b01e57SJeff Chen beacon_ie_len = ies->len;
13273b01e57SJeff Chen bss_desc->timestamp = ies->tsf;
13373b01e57SJeff Chen rcu_read_unlock();
13473b01e57SJeff Chen
13573b01e57SJeff Chen if (!beacon_ie) {
13673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
13773b01e57SJeff Chen " failed to alloc beacon_ie\n");
13873b01e57SJeff Chen return -ENOMEM;
13973b01e57SJeff Chen }
14073b01e57SJeff Chen
14173b01e57SJeff Chen memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
14273b01e57SJeff Chen bss_desc->rssi = bss->signal;
14373b01e57SJeff Chen /* The caller of this function will free beacon_ie */
14473b01e57SJeff Chen bss_desc->beacon_buf = beacon_ie;
14573b01e57SJeff Chen bss_desc->beacon_buf_size = beacon_ie_len;
14673b01e57SJeff Chen bss_desc->beacon_period = bss->beacon_interval;
14773b01e57SJeff Chen bss_desc->cap_info_bitmap = bss->capability;
14873b01e57SJeff Chen bss_desc->bss_band = bss_priv->band;
14973b01e57SJeff Chen bss_desc->fw_tsf = bss_priv->fw_tsf;
15073b01e57SJeff Chen if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
15173b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
15273b01e57SJeff Chen "info: InterpretIE: AP WEP enabled\n");
15373b01e57SJeff Chen bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_8021X_WEP;
15473b01e57SJeff Chen } else {
15573b01e57SJeff Chen bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL;
15673b01e57SJeff Chen }
15773b01e57SJeff Chen bss_desc->bss_mode = NL80211_IFTYPE_STATION;
15873b01e57SJeff Chen
15973b01e57SJeff Chen /* Disable 11ac by default */
16073b01e57SJeff Chen bss_desc->disable_11ac = true;
16173b01e57SJeff Chen /* Disable 11ax by default */
16273b01e57SJeff Chen bss_desc->disable_11ax = true;
16373b01e57SJeff Chen
16473b01e57SJeff Chen if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
16573b01e57SJeff Chen bss_desc->sensed_11h = true;
16673b01e57SJeff Chen
16773b01e57SJeff Chen return nxpwifi_update_bss_desc_with_ie(priv->adapter, bss_desc);
16873b01e57SJeff Chen }
16973b01e57SJeff Chen
nxpwifi_process_country_ie(struct nxpwifi_private * priv,struct cfg80211_bss * bss)17073b01e57SJeff Chen static int nxpwifi_process_country_ie(struct nxpwifi_private *priv,
17173b01e57SJeff Chen struct cfg80211_bss *bss)
17273b01e57SJeff Chen {
17373b01e57SJeff Chen const u8 *country_ie;
17473b01e57SJeff Chen u8 country_ie_len;
17573b01e57SJeff Chen struct nxpwifi_802_11d_domain_reg *domain_info =
17673b01e57SJeff Chen &priv->adapter->domain_reg;
17773b01e57SJeff Chen int ret;
17873b01e57SJeff Chen
17973b01e57SJeff Chen rcu_read_lock();
18073b01e57SJeff Chen country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
18173b01e57SJeff Chen if (!country_ie) {
18273b01e57SJeff Chen rcu_read_unlock();
18373b01e57SJeff Chen return 0;
18473b01e57SJeff Chen }
18573b01e57SJeff Chen
18673b01e57SJeff Chen country_ie_len = country_ie[1];
18773b01e57SJeff Chen if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
18873b01e57SJeff Chen rcu_read_unlock();
18973b01e57SJeff Chen return 0;
19073b01e57SJeff Chen }
19173b01e57SJeff Chen
19273b01e57SJeff Chen if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
19373b01e57SJeff Chen rcu_read_unlock();
19473b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
19573b01e57SJeff Chen "11D: skip setting domain info in FW\n");
19673b01e57SJeff Chen return 0;
19773b01e57SJeff Chen }
19873b01e57SJeff Chen
19973b01e57SJeff Chen if (country_ie_len >
20073b01e57SJeff Chen (IEEE80211_COUNTRY_STRING_LEN + NXPWIFI_MAX_TRIPLET_802_11D)) {
20173b01e57SJeff Chen rcu_read_unlock();
20273b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
20373b01e57SJeff Chen "11D: country_ie_len overflow!, deauth AP\n");
20473b01e57SJeff Chen return -EINVAL;
20573b01e57SJeff Chen }
20673b01e57SJeff Chen
20773b01e57SJeff Chen memcpy(priv->adapter->country_code, &country_ie[2], 2);
20873b01e57SJeff Chen
20973b01e57SJeff Chen domain_info->country_code[0] = country_ie[2];
21073b01e57SJeff Chen domain_info->country_code[1] = country_ie[3];
21173b01e57SJeff Chen domain_info->country_code[2] = ' ';
21273b01e57SJeff Chen
21373b01e57SJeff Chen country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
21473b01e57SJeff Chen
21573b01e57SJeff Chen domain_info->no_of_triplet =
21673b01e57SJeff Chen country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
21773b01e57SJeff Chen
21873b01e57SJeff Chen memcpy((u8 *)domain_info->triplet,
21973b01e57SJeff Chen &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
22073b01e57SJeff Chen
22173b01e57SJeff Chen rcu_read_unlock();
22273b01e57SJeff Chen
22373b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO,
22473b01e57SJeff Chen HOST_ACT_GEN_SET, 0, NULL, false);
22573b01e57SJeff Chen if (ret)
22673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
22773b01e57SJeff Chen "11D: setting domain info in FW fail\n");
22873b01e57SJeff Chen
22973b01e57SJeff Chen return ret;
23073b01e57SJeff Chen }
23173b01e57SJeff Chen
23273b01e57SJeff Chen /* In infra mode, an deauthentication is performed first */
nxpwifi_bss_start(struct nxpwifi_private * priv,struct cfg80211_bss * bss,struct cfg80211_ssid * req_ssid)23373b01e57SJeff Chen int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss,
23473b01e57SJeff Chen struct cfg80211_ssid *req_ssid)
23573b01e57SJeff Chen {
23673b01e57SJeff Chen int ret;
23773b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
23873b01e57SJeff Chen struct nxpwifi_bssdescriptor *bss_desc = NULL;
23973b01e57SJeff Chen u16 config_bands;
24073b01e57SJeff Chen
24173b01e57SJeff Chen priv->scan_block = false;
24273b01e57SJeff Chen
24373b01e57SJeff Chen if (adapter->region_code == 0x00 &&
24473b01e57SJeff Chen nxpwifi_process_country_ie(priv, bss))
24573b01e57SJeff Chen return -EINVAL;
24673b01e57SJeff Chen
24773b01e57SJeff Chen /* Allocate and fill new bss descriptor */
24873b01e57SJeff Chen bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL);
24973b01e57SJeff Chen if (!bss_desc)
25073b01e57SJeff Chen return -ENOMEM;
25173b01e57SJeff Chen
25273b01e57SJeff Chen ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
25373b01e57SJeff Chen if (ret)
25473b01e57SJeff Chen goto done;
25573b01e57SJeff Chen
25673b01e57SJeff Chen if (nxpwifi_band_to_radio_type(bss_desc->bss_band) ==
25773b01e57SJeff Chen HOST_SCAN_RADIO_TYPE_BG) {
25873b01e57SJeff Chen config_bands = BAND_B | BAND_G | BAND_GN;
25973b01e57SJeff Chen if (adapter->fw_bands & BAND_GAC)
26073b01e57SJeff Chen config_bands |= BAND_GAC;
26173b01e57SJeff Chen if (adapter->fw_bands & BAND_GAX)
26273b01e57SJeff Chen config_bands |= BAND_GAX;
26373b01e57SJeff Chen } else {
26473b01e57SJeff Chen config_bands = BAND_A | BAND_AN;
26573b01e57SJeff Chen if (adapter->fw_bands & BAND_AAC)
26673b01e57SJeff Chen config_bands |= BAND_AAC;
26773b01e57SJeff Chen if (adapter->fw_bands & BAND_AAX)
26873b01e57SJeff Chen config_bands |= BAND_AAX;
26973b01e57SJeff Chen }
27073b01e57SJeff Chen
27173b01e57SJeff Chen if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
27273b01e57SJeff Chen priv->config_bands = config_bands;
27373b01e57SJeff Chen
27473b01e57SJeff Chen ret = nxpwifi_check_network_compatibility(priv, bss_desc);
27573b01e57SJeff Chen if (ret)
27673b01e57SJeff Chen goto done;
27773b01e57SJeff Chen
27873b01e57SJeff Chen if (nxpwifi_11h_get_csa_closed_channel(priv) == (u8)bss_desc->channel) {
27973b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR,
28073b01e57SJeff Chen "Attempt to reconnect on csa closed chan(%d)\n",
28173b01e57SJeff Chen bss_desc->channel);
28273b01e57SJeff Chen ret = -EINVAL;
28373b01e57SJeff Chen goto done;
28473b01e57SJeff Chen }
28573b01e57SJeff Chen
28673b01e57SJeff Chen nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
28773b01e57SJeff Chen netif_carrier_off(priv->netdev);
28873b01e57SJeff Chen
28973b01e57SJeff Chen /* Clear any past association response stored for application retrieval */
29073b01e57SJeff Chen priv->assoc_rsp_size = 0;
29173b01e57SJeff Chen ret = nxpwifi_associate(priv, bss_desc);
29273b01e57SJeff Chen
29373b01e57SJeff Chen /*
29473b01e57SJeff Chen * If auth type is auto and association fails using open mode, try to connect
29573b01e57SJeff Chen * using shared mode
29673b01e57SJeff Chen */
29773b01e57SJeff Chen if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
29873b01e57SJeff Chen priv->sec_info.is_authtype_auto &&
29973b01e57SJeff Chen priv->sec_info.wep_enabled) {
30073b01e57SJeff Chen priv->sec_info.authentication_mode =
30173b01e57SJeff Chen NL80211_AUTHTYPE_SHARED_KEY;
30273b01e57SJeff Chen ret = nxpwifi_associate(priv, bss_desc);
30373b01e57SJeff Chen }
30473b01e57SJeff Chen
30573b01e57SJeff Chen done:
30673b01e57SJeff Chen /* beacon_ie buffer was allocated in function nxpwifi_fill_new_bss_desc() */
30773b01e57SJeff Chen if (bss_desc)
30873b01e57SJeff Chen kfree(bss_desc->beacon_buf);
30973b01e57SJeff Chen kfree(bss_desc);
31073b01e57SJeff Chen
31173b01e57SJeff Chen if (ret < 0)
31273b01e57SJeff Chen priv->attempted_bss_desc = NULL;
31373b01e57SJeff Chen
31473b01e57SJeff Chen return ret;
31573b01e57SJeff Chen }
31673b01e57SJeff Chen
31773b01e57SJeff Chen /* IOCTL request handler to set host sleep configuration */
nxpwifi_set_hs_params(struct nxpwifi_private * priv,u16 action,int cmd_type,struct nxpwifi_ds_hs_cfg * hs_cfg)31873b01e57SJeff Chen int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action,
31973b01e57SJeff Chen int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg)
32073b01e57SJeff Chen
32173b01e57SJeff Chen {
32273b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
32373b01e57SJeff Chen int status = 0;
32473b01e57SJeff Chen u32 prev_cond = 0;
32573b01e57SJeff Chen
32673b01e57SJeff Chen if (!hs_cfg)
32773b01e57SJeff Chen return -ENOMEM;
32873b01e57SJeff Chen
32973b01e57SJeff Chen switch (action) {
33073b01e57SJeff Chen case HOST_ACT_GEN_SET:
33173b01e57SJeff Chen if (adapter->pps_uapsd_mode) {
33273b01e57SJeff Chen nxpwifi_dbg(adapter, INFO,
33373b01e57SJeff Chen "info: Host Sleep IOCTL\t"
33473b01e57SJeff Chen "is blocked in UAPSD/PPS mode\n");
33573b01e57SJeff Chen status = -EPERM;
33673b01e57SJeff Chen break;
33773b01e57SJeff Chen }
33873b01e57SJeff Chen if (hs_cfg->is_invoke_hostcmd) {
33973b01e57SJeff Chen if (hs_cfg->conditions == HS_CFG_CANCEL) {
34073b01e57SJeff Chen if (!test_bit(NXPWIFI_IS_HS_CONFIGURED,
34173b01e57SJeff Chen &adapter->work_flags))
34273b01e57SJeff Chen /* Already cancelled */
34373b01e57SJeff Chen break;
34473b01e57SJeff Chen /* Save previous condition */
34573b01e57SJeff Chen prev_cond = le32_to_cpu(adapter->hs_cfg
34673b01e57SJeff Chen .conditions);
34773b01e57SJeff Chen adapter->hs_cfg.conditions =
34873b01e57SJeff Chen cpu_to_le32(hs_cfg->conditions);
34973b01e57SJeff Chen } else if (hs_cfg->conditions) {
35073b01e57SJeff Chen adapter->hs_cfg.conditions =
35173b01e57SJeff Chen cpu_to_le32(hs_cfg->conditions);
35273b01e57SJeff Chen adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
35373b01e57SJeff Chen if (hs_cfg->gap)
35473b01e57SJeff Chen adapter->hs_cfg.gap = (u8)hs_cfg->gap;
35573b01e57SJeff Chen } else if (adapter->hs_cfg.conditions ==
35673b01e57SJeff Chen cpu_to_le32(HS_CFG_CANCEL)) {
35773b01e57SJeff Chen status = -EINVAL;
35873b01e57SJeff Chen break;
35973b01e57SJeff Chen }
36073b01e57SJeff Chen
36173b01e57SJeff Chen status = nxpwifi_send_cmd(priv,
36273b01e57SJeff Chen HOST_CMD_802_11_HS_CFG_ENH,
36373b01e57SJeff Chen HOST_ACT_GEN_SET, 0,
36473b01e57SJeff Chen &adapter->hs_cfg,
36573b01e57SJeff Chen cmd_type == NXPWIFI_SYNC_CMD);
36673b01e57SJeff Chen
36773b01e57SJeff Chen if (hs_cfg->conditions == HS_CFG_CANCEL)
36873b01e57SJeff Chen /* Restore previous condition */
36973b01e57SJeff Chen adapter->hs_cfg.conditions =
37073b01e57SJeff Chen cpu_to_le32(prev_cond);
37173b01e57SJeff Chen } else {
37273b01e57SJeff Chen adapter->hs_cfg.conditions =
37373b01e57SJeff Chen cpu_to_le32(hs_cfg->conditions);
37473b01e57SJeff Chen adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
37573b01e57SJeff Chen adapter->hs_cfg.gap = (u8)hs_cfg->gap;
37673b01e57SJeff Chen }
37773b01e57SJeff Chen break;
37873b01e57SJeff Chen case HOST_ACT_GEN_GET:
37973b01e57SJeff Chen hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
38073b01e57SJeff Chen hs_cfg->gpio = adapter->hs_cfg.gpio;
38173b01e57SJeff Chen hs_cfg->gap = adapter->hs_cfg.gap;
38273b01e57SJeff Chen break;
38373b01e57SJeff Chen default:
38473b01e57SJeff Chen status = -EINVAL;
38573b01e57SJeff Chen break;
38673b01e57SJeff Chen }
38773b01e57SJeff Chen
38873b01e57SJeff Chen return status;
38973b01e57SJeff Chen }
39073b01e57SJeff Chen
39173b01e57SJeff Chen /* Sends IOCTL request to cancel the existing Host Sleep configuration */
nxpwifi_cancel_hs(struct nxpwifi_private * priv,int cmd_type)39273b01e57SJeff Chen int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type)
39373b01e57SJeff Chen {
39473b01e57SJeff Chen struct nxpwifi_ds_hs_cfg hscfg;
39573b01e57SJeff Chen
39673b01e57SJeff Chen hscfg.conditions = HS_CFG_CANCEL;
39773b01e57SJeff Chen hscfg.is_invoke_hostcmd = true;
39873b01e57SJeff Chen
39973b01e57SJeff Chen return nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET,
40073b01e57SJeff Chen cmd_type, &hscfg);
40173b01e57SJeff Chen }
40273b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_cancel_hs);
40373b01e57SJeff Chen
40473b01e57SJeff Chen /* Sends IOCTL request to cancel the existing Host Sleep configuration */
nxpwifi_enable_hs(struct nxpwifi_adapter * adapter)40573b01e57SJeff Chen bool nxpwifi_enable_hs(struct nxpwifi_adapter *adapter)
40673b01e57SJeff Chen {
40773b01e57SJeff Chen struct nxpwifi_ds_hs_cfg hscfg;
40873b01e57SJeff Chen struct nxpwifi_private *priv;
40973b01e57SJeff Chen int i;
41073b01e57SJeff Chen
41173b01e57SJeff Chen if (disconnect_on_suspend) {
41273b01e57SJeff Chen for (i = 0; i < adapter->priv_num; i++) {
41373b01e57SJeff Chen priv = adapter->priv[i];
41473b01e57SJeff Chen nxpwifi_deauthenticate(priv, NULL);
41573b01e57SJeff Chen }
41673b01e57SJeff Chen }
41773b01e57SJeff Chen
41873b01e57SJeff Chen priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
41973b01e57SJeff Chen
42073b01e57SJeff Chen if (priv && priv->sched_scanning) {
42173b01e57SJeff Chen #ifdef CONFIG_PM
42273b01e57SJeff Chen if (priv->wdev.wiphy->wowlan_config &&
42373b01e57SJeff Chen !priv->wdev.wiphy->wowlan_config->nd_config) {
42473b01e57SJeff Chen #endif
42573b01e57SJeff Chen nxpwifi_dbg(adapter, CMD, "aborting bgscan!\n");
42673b01e57SJeff Chen nxpwifi_stop_bg_scan(priv);
42773b01e57SJeff Chen cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
42873b01e57SJeff Chen #ifdef CONFIG_PM
42973b01e57SJeff Chen }
43073b01e57SJeff Chen #endif
43173b01e57SJeff Chen }
43273b01e57SJeff Chen
43373b01e57SJeff Chen if (adapter->hs_activated) {
43473b01e57SJeff Chen nxpwifi_dbg(adapter, CMD,
43573b01e57SJeff Chen "cmd: HS Already activated\n");
43673b01e57SJeff Chen return true;
43773b01e57SJeff Chen }
43873b01e57SJeff Chen
43973b01e57SJeff Chen adapter->hs_activate_wait_q_woken = false;
44073b01e57SJeff Chen
44173b01e57SJeff Chen memset(&hscfg, 0, sizeof(hscfg));
44273b01e57SJeff Chen hscfg.is_invoke_hostcmd = true;
44373b01e57SJeff Chen
44473b01e57SJeff Chen set_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags);
44573b01e57SJeff Chen nxpwifi_cancel_all_pending_cmd(adapter);
44673b01e57SJeff Chen
44773b01e57SJeff Chen if (nxpwifi_set_hs_params(nxpwifi_get_priv(adapter,
44873b01e57SJeff Chen NXPWIFI_BSS_ROLE_STA),
44973b01e57SJeff Chen HOST_ACT_GEN_SET, NXPWIFI_SYNC_CMD,
45073b01e57SJeff Chen &hscfg)) {
45173b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR,
45273b01e57SJeff Chen "IOCTL request HS enable failed\n");
45373b01e57SJeff Chen return false;
45473b01e57SJeff Chen }
45573b01e57SJeff Chen
45673b01e57SJeff Chen if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
45773b01e57SJeff Chen adapter->hs_activate_wait_q_woken,
45873b01e57SJeff Chen (10 * HZ)) <= 0) {
45973b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR,
46073b01e57SJeff Chen "hs_activate_wait_q terminated\n");
46173b01e57SJeff Chen return false;
46273b01e57SJeff Chen }
46373b01e57SJeff Chen
46473b01e57SJeff Chen return true;
46573b01e57SJeff Chen }
46673b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_enable_hs);
46773b01e57SJeff Chen
46873b01e57SJeff Chen /* IOCTL request handler to get BSS information */
nxpwifi_get_bss_info(struct nxpwifi_private * priv,struct nxpwifi_bss_info * info)46973b01e57SJeff Chen int nxpwifi_get_bss_info(struct nxpwifi_private *priv,
47073b01e57SJeff Chen struct nxpwifi_bss_info *info)
47173b01e57SJeff Chen {
47273b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
47373b01e57SJeff Chen struct nxpwifi_bssdescriptor *bss_desc;
47473b01e57SJeff Chen
47573b01e57SJeff Chen if (!info)
47673b01e57SJeff Chen return -EINVAL;
47773b01e57SJeff Chen
47873b01e57SJeff Chen bss_desc = &priv->curr_bss_params.bss_descriptor;
47973b01e57SJeff Chen
48073b01e57SJeff Chen info->bss_mode = priv->bss_mode;
48173b01e57SJeff Chen
48273b01e57SJeff Chen memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
48373b01e57SJeff Chen
48473b01e57SJeff Chen memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
48573b01e57SJeff Chen
48673b01e57SJeff Chen info->bss_chan = bss_desc->channel;
48773b01e57SJeff Chen
48873b01e57SJeff Chen memcpy(info->country_code, adapter->country_code,
48973b01e57SJeff Chen IEEE80211_COUNTRY_STRING_LEN);
49073b01e57SJeff Chen
49173b01e57SJeff Chen info->media_connected = priv->media_connected;
49273b01e57SJeff Chen
49373b01e57SJeff Chen info->max_power_level = priv->max_tx_power_level;
49473b01e57SJeff Chen info->min_power_level = priv->min_tx_power_level;
49573b01e57SJeff Chen
49673b01e57SJeff Chen info->bcn_nf_last = priv->bcn_nf_last;
49773b01e57SJeff Chen
49873b01e57SJeff Chen if (priv->sec_info.wep_enabled)
49973b01e57SJeff Chen info->wep_status = true;
50073b01e57SJeff Chen else
50173b01e57SJeff Chen info->wep_status = false;
50273b01e57SJeff Chen
50373b01e57SJeff Chen info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED,
50473b01e57SJeff Chen &adapter->work_flags);
50573b01e57SJeff Chen info->is_deep_sleep = adapter->is_deep_sleep;
50673b01e57SJeff Chen
50773b01e57SJeff Chen return 0;
50873b01e57SJeff Chen }
50973b01e57SJeff Chen
51073b01e57SJeff Chen /* The function disables auto deep sleep mode */
nxpwifi_disable_auto_ds(struct nxpwifi_private * priv)51173b01e57SJeff Chen int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv)
51273b01e57SJeff Chen {
51373b01e57SJeff Chen struct nxpwifi_ds_auto_ds auto_ds = {
51473b01e57SJeff Chen .auto_ds = DEEP_SLEEP_OFF,
51573b01e57SJeff Chen };
51673b01e57SJeff Chen
51773b01e57SJeff Chen return nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
51873b01e57SJeff Chen DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
51973b01e57SJeff Chen }
52073b01e57SJeff Chen EXPORT_SYMBOL_GPL(nxpwifi_disable_auto_ds);
52173b01e57SJeff Chen
52273b01e57SJeff Chen /* Sends IOCTL request to get the data rate */
nxpwifi_drv_get_data_rate(struct nxpwifi_private * priv,u32 * rate)52373b01e57SJeff Chen int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate)
52473b01e57SJeff Chen {
52573b01e57SJeff Chen int ret;
52673b01e57SJeff Chen
52773b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY,
52873b01e57SJeff Chen HOST_ACT_GEN_GET, 0, NULL, true);
52973b01e57SJeff Chen
53073b01e57SJeff Chen if (!ret) {
53173b01e57SJeff Chen if (priv->is_data_rate_auto)
53273b01e57SJeff Chen *rate = nxpwifi_index_to_data_rate(priv, priv->tx_rate,
53373b01e57SJeff Chen priv->tx_htinfo);
53473b01e57SJeff Chen else
53573b01e57SJeff Chen *rate = priv->data_rate;
53673b01e57SJeff Chen }
53773b01e57SJeff Chen
53873b01e57SJeff Chen return ret;
53973b01e57SJeff Chen }
54073b01e57SJeff Chen
54173b01e57SJeff Chen /* IOCTL request handler to set tx power configuration */
nxpwifi_set_tx_power(struct nxpwifi_private * priv,struct nxpwifi_power_cfg * power_cfg)54273b01e57SJeff Chen int nxpwifi_set_tx_power(struct nxpwifi_private *priv,
54373b01e57SJeff Chen struct nxpwifi_power_cfg *power_cfg)
54473b01e57SJeff Chen {
54573b01e57SJeff Chen int ret;
54673b01e57SJeff Chen struct host_cmd_ds_txpwr_cfg *txp_cfg;
54773b01e57SJeff Chen struct nxpwifi_types_power_group *pg_tlv;
54873b01e57SJeff Chen struct nxpwifi_power_group *pg;
54973b01e57SJeff Chen u8 *buf;
55073b01e57SJeff Chen u16 dbm = 0;
55173b01e57SJeff Chen
55273b01e57SJeff Chen if (!power_cfg->is_power_auto) {
55373b01e57SJeff Chen dbm = (u16)power_cfg->power_level;
55473b01e57SJeff Chen if (dbm < priv->min_tx_power_level ||
55573b01e57SJeff Chen dbm > priv->max_tx_power_level) {
55673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
55773b01e57SJeff Chen "txpower value %d dBm\t"
55873b01e57SJeff Chen "is out of range (%d dBm-%d dBm)\n",
55973b01e57SJeff Chen dbm, priv->min_tx_power_level,
56073b01e57SJeff Chen priv->max_tx_power_level);
56173b01e57SJeff Chen return -EINVAL;
56273b01e57SJeff Chen }
56373b01e57SJeff Chen }
56473b01e57SJeff Chen buf = kzalloc(NXPWIFI_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
56573b01e57SJeff Chen if (!buf)
56673b01e57SJeff Chen return -ENOMEM;
56773b01e57SJeff Chen
56873b01e57SJeff Chen txp_cfg = (struct host_cmd_ds_txpwr_cfg *)buf;
56973b01e57SJeff Chen txp_cfg->action = cpu_to_le16(HOST_ACT_GEN_SET);
57073b01e57SJeff Chen if (!power_cfg->is_power_auto) {
57173b01e57SJeff Chen u16 dbm_min = power_cfg->is_power_fixed ?
57273b01e57SJeff Chen dbm : priv->min_tx_power_level;
57373b01e57SJeff Chen
57473b01e57SJeff Chen txp_cfg->mode = cpu_to_le32(1);
57573b01e57SJeff Chen pg_tlv = (struct nxpwifi_types_power_group *)
57673b01e57SJeff Chen (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
57773b01e57SJeff Chen pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
57873b01e57SJeff Chen pg_tlv->length =
57973b01e57SJeff Chen cpu_to_le16(4 * sizeof(struct nxpwifi_power_group));
58073b01e57SJeff Chen pg = (struct nxpwifi_power_group *)
58173b01e57SJeff Chen (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
58273b01e57SJeff Chen + sizeof(struct nxpwifi_types_power_group));
58373b01e57SJeff Chen /* Power group for modulation class HR/DSSS */
58473b01e57SJeff Chen pg->first_rate_code = 0x00;
58573b01e57SJeff Chen pg->last_rate_code = 0x03;
58673b01e57SJeff Chen pg->modulation_class = MOD_CLASS_HR_DSSS;
58773b01e57SJeff Chen pg->power_step = 0;
58873b01e57SJeff Chen pg->power_min = (s8)dbm_min;
58973b01e57SJeff Chen pg->power_max = (s8)dbm;
59073b01e57SJeff Chen pg++;
59173b01e57SJeff Chen /* Power group for modulation class OFDM */
59273b01e57SJeff Chen pg->first_rate_code = 0x00;
59373b01e57SJeff Chen pg->last_rate_code = 0x07;
59473b01e57SJeff Chen pg->modulation_class = MOD_CLASS_OFDM;
59573b01e57SJeff Chen pg->power_step = 0;
59673b01e57SJeff Chen pg->power_min = (s8)dbm_min;
59773b01e57SJeff Chen pg->power_max = (s8)dbm;
59873b01e57SJeff Chen pg++;
59973b01e57SJeff Chen /* Power group for modulation class HTBW20 */
60073b01e57SJeff Chen pg->first_rate_code = 0x00;
60173b01e57SJeff Chen pg->last_rate_code = 0x20;
60273b01e57SJeff Chen pg->modulation_class = MOD_CLASS_HT;
60373b01e57SJeff Chen pg->power_step = 0;
60473b01e57SJeff Chen pg->power_min = (s8)dbm_min;
60573b01e57SJeff Chen pg->power_max = (s8)dbm;
60673b01e57SJeff Chen pg->ht_bandwidth = HT_BW_20;
60773b01e57SJeff Chen pg++;
60873b01e57SJeff Chen /* Power group for modulation class HTBW40 */
60973b01e57SJeff Chen pg->first_rate_code = 0x00;
61073b01e57SJeff Chen pg->last_rate_code = 0x20;
61173b01e57SJeff Chen pg->modulation_class = MOD_CLASS_HT;
61273b01e57SJeff Chen pg->power_step = 0;
61373b01e57SJeff Chen pg->power_min = (s8)dbm_min;
61473b01e57SJeff Chen pg->power_max = (s8)dbm;
61573b01e57SJeff Chen pg->ht_bandwidth = HT_BW_40;
61673b01e57SJeff Chen }
61773b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_TXPWR_CFG,
61873b01e57SJeff Chen HOST_ACT_GEN_SET, 0, buf, true);
61973b01e57SJeff Chen
62073b01e57SJeff Chen kfree(buf);
62173b01e57SJeff Chen return ret;
62273b01e57SJeff Chen }
62373b01e57SJeff Chen
62473b01e57SJeff Chen /* IOCTL request handler to get power save mode */
nxpwifi_drv_set_power(struct nxpwifi_private * priv,u32 * ps_mode)62573b01e57SJeff Chen int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode)
62673b01e57SJeff Chen {
62773b01e57SJeff Chen int ret;
62873b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
62973b01e57SJeff Chen u16 sub_cmd;
63073b01e57SJeff Chen
63173b01e57SJeff Chen if (*ps_mode)
63273b01e57SJeff Chen adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP;
63373b01e57SJeff Chen else
63473b01e57SJeff Chen adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
63573b01e57SJeff Chen sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
63673b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
63773b01e57SJeff Chen sub_cmd, BITMAP_STA_PS, NULL, true);
63873b01e57SJeff Chen if (!ret && sub_cmd == DIS_AUTO_PS)
63973b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
64073b01e57SJeff Chen GET_PS, 0, NULL, false);
64173b01e57SJeff Chen
64273b01e57SJeff Chen return ret;
64373b01e57SJeff Chen }
64473b01e57SJeff Chen
64573b01e57SJeff Chen /* IOCTL request handler to set/reset WPA element */
nxpwifi_set_wpa_ie(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)64673b01e57SJeff Chen static int nxpwifi_set_wpa_ie(struct nxpwifi_private *priv,
64773b01e57SJeff Chen u8 *ie_data_ptr, u16 ie_len)
64873b01e57SJeff Chen {
64973b01e57SJeff Chen if (ie_len) {
65073b01e57SJeff Chen if (ie_len > sizeof(priv->wpa_ie)) {
65173b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
65273b01e57SJeff Chen "failed to copy WPA element, too big\n");
65373b01e57SJeff Chen return -EINVAL;
65473b01e57SJeff Chen }
65573b01e57SJeff Chen memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
65673b01e57SJeff Chen priv->wpa_ie_len = ie_len;
65773b01e57SJeff Chen nxpwifi_dbg(priv->adapter, CMD,
65873b01e57SJeff Chen "cmd: Set WPA element len=%d element=%#x\n",
65973b01e57SJeff Chen priv->wpa_ie_len, priv->wpa_ie[0]);
66073b01e57SJeff Chen
66173b01e57SJeff Chen if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
66273b01e57SJeff Chen priv->sec_info.wpa_enabled = true;
66373b01e57SJeff Chen } else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
66473b01e57SJeff Chen priv->sec_info.wpa2_enabled = true;
66573b01e57SJeff Chen } else {
66673b01e57SJeff Chen priv->sec_info.wpa_enabled = false;
66773b01e57SJeff Chen priv->sec_info.wpa2_enabled = false;
66873b01e57SJeff Chen }
66973b01e57SJeff Chen } else {
67073b01e57SJeff Chen memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
67173b01e57SJeff Chen priv->wpa_ie_len = 0;
67273b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
67373b01e57SJeff Chen "info: reset WPA element len=%d element=%#x\n",
67473b01e57SJeff Chen priv->wpa_ie_len, priv->wpa_ie[0]);
67573b01e57SJeff Chen priv->sec_info.wpa_enabled = false;
67673b01e57SJeff Chen priv->sec_info.wpa2_enabled = false;
67773b01e57SJeff Chen }
67873b01e57SJeff Chen
67973b01e57SJeff Chen return 0;
68073b01e57SJeff Chen }
68173b01e57SJeff Chen
68273b01e57SJeff Chen /* IOCTL request handler to set/reset WPS element */
nxpwifi_set_wps_ie(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)68373b01e57SJeff Chen static int nxpwifi_set_wps_ie(struct nxpwifi_private *priv,
68473b01e57SJeff Chen u8 *ie_data_ptr, u16 ie_len)
68573b01e57SJeff Chen {
68673b01e57SJeff Chen if (ie_len) {
68773b01e57SJeff Chen if (ie_len > NXPWIFI_MAX_VSIE_LEN) {
68873b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
68973b01e57SJeff Chen "info: failed to copy WPS element, too big\n");
69073b01e57SJeff Chen return -EINVAL;
69173b01e57SJeff Chen }
69273b01e57SJeff Chen
69373b01e57SJeff Chen priv->wps_ie = kzalloc(NXPWIFI_MAX_VSIE_LEN, GFP_KERNEL);
69473b01e57SJeff Chen if (!priv->wps_ie)
69573b01e57SJeff Chen return -ENOMEM;
69673b01e57SJeff Chen
69773b01e57SJeff Chen memcpy(priv->wps_ie, ie_data_ptr, ie_len);
69873b01e57SJeff Chen priv->wps_ie_len = ie_len;
69973b01e57SJeff Chen nxpwifi_dbg(priv->adapter, CMD,
70073b01e57SJeff Chen "cmd: Set WPS element len=%d element=%#x\n",
70173b01e57SJeff Chen priv->wps_ie_len, priv->wps_ie[0]);
70273b01e57SJeff Chen } else {
70373b01e57SJeff Chen kfree(priv->wps_ie);
70473b01e57SJeff Chen priv->wps_ie_len = ie_len;
70573b01e57SJeff Chen nxpwifi_dbg(priv->adapter, INFO,
70673b01e57SJeff Chen "info: Reset WPS element len=%d\n", priv->wps_ie_len);
70773b01e57SJeff Chen }
70873b01e57SJeff Chen return 0;
70973b01e57SJeff Chen }
71073b01e57SJeff Chen
71173b01e57SJeff Chen /* IOCTL request handler to set WEP network key */
71273b01e57SJeff Chen static int
nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)71373b01e57SJeff Chen nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private *priv,
71473b01e57SJeff Chen struct nxpwifi_ds_encrypt_key *encrypt_key)
71573b01e57SJeff Chen {
71673b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
71773b01e57SJeff Chen int ret;
71873b01e57SJeff Chen struct nxpwifi_wep_key *wep_key;
71973b01e57SJeff Chen int index;
72073b01e57SJeff Chen
72173b01e57SJeff Chen if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
72273b01e57SJeff Chen priv->wep_key_curr_index = 0;
72373b01e57SJeff Chen wep_key = &priv->wep_key[priv->wep_key_curr_index];
72473b01e57SJeff Chen index = encrypt_key->key_index;
72573b01e57SJeff Chen if (encrypt_key->key_disable) {
72673b01e57SJeff Chen priv->sec_info.wep_enabled = 0;
72773b01e57SJeff Chen } else if (!encrypt_key->key_len) {
72873b01e57SJeff Chen /* Copy the required key as the current key */
72973b01e57SJeff Chen wep_key = &priv->wep_key[index];
73073b01e57SJeff Chen if (!wep_key->key_length) {
73173b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR,
73273b01e57SJeff Chen "key not set, so cannot enable it\n");
73373b01e57SJeff Chen return -EINVAL;
73473b01e57SJeff Chen }
73573b01e57SJeff Chen
73673b01e57SJeff Chen memcpy(encrypt_key->key_material,
73773b01e57SJeff Chen wep_key->key_material, wep_key->key_length);
73873b01e57SJeff Chen encrypt_key->key_len = wep_key->key_length;
73973b01e57SJeff Chen
74073b01e57SJeff Chen priv->wep_key_curr_index = (u16)index;
74173b01e57SJeff Chen priv->sec_info.wep_enabled = 1;
74273b01e57SJeff Chen } else {
74373b01e57SJeff Chen wep_key = &priv->wep_key[index];
74473b01e57SJeff Chen memset(wep_key, 0, sizeof(struct nxpwifi_wep_key));
74573b01e57SJeff Chen /* Copy the key in the driver */
74673b01e57SJeff Chen memcpy(wep_key->key_material,
74773b01e57SJeff Chen encrypt_key->key_material,
74873b01e57SJeff Chen encrypt_key->key_len);
74973b01e57SJeff Chen wep_key->key_index = index;
75073b01e57SJeff Chen wep_key->key_length = encrypt_key->key_len;
75173b01e57SJeff Chen priv->sec_info.wep_enabled = 1;
75273b01e57SJeff Chen }
75373b01e57SJeff Chen if (wep_key->key_length) {
75473b01e57SJeff Chen void *enc_key;
75573b01e57SJeff Chen
75673b01e57SJeff Chen if (encrypt_key->key_disable) {
75773b01e57SJeff Chen memset(&priv->wep_key[index], 0,
75873b01e57SJeff Chen sizeof(struct nxpwifi_wep_key));
75973b01e57SJeff Chen goto done;
76073b01e57SJeff Chen }
76173b01e57SJeff Chen
76273b01e57SJeff Chen enc_key = encrypt_key;
76373b01e57SJeff Chen
76473b01e57SJeff Chen /* Send request to firmware */
76573b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
76673b01e57SJeff Chen HOST_ACT_GEN_SET, 0, enc_key, false);
76773b01e57SJeff Chen if (ret)
76873b01e57SJeff Chen return ret;
76973b01e57SJeff Chen }
77073b01e57SJeff Chen
77173b01e57SJeff Chen done:
77273b01e57SJeff Chen if (priv->sec_info.wep_enabled)
77373b01e57SJeff Chen priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE;
77473b01e57SJeff Chen else
77573b01e57SJeff Chen priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE;
77673b01e57SJeff Chen
77773b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
77873b01e57SJeff Chen HOST_ACT_GEN_SET, 0,
77973b01e57SJeff Chen &priv->curr_pkt_filter, true);
78073b01e57SJeff Chen
78173b01e57SJeff Chen return ret;
78273b01e57SJeff Chen }
78373b01e57SJeff Chen
78473b01e57SJeff Chen /* IOCTL request handler to set WPA key */
78573b01e57SJeff Chen static int
nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)78673b01e57SJeff Chen nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private *priv,
78773b01e57SJeff Chen struct nxpwifi_ds_encrypt_key *encrypt_key)
78873b01e57SJeff Chen {
78973b01e57SJeff Chen int ret;
79073b01e57SJeff Chen u8 remove_key = false;
79173b01e57SJeff Chen
79273b01e57SJeff Chen /* Current driver only supports key length of up to 32 bytes */
79373b01e57SJeff Chen if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
79473b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
79573b01e57SJeff Chen "key length too long\n");
79673b01e57SJeff Chen return -EINVAL;
79773b01e57SJeff Chen }
79873b01e57SJeff Chen
79973b01e57SJeff Chen if (!encrypt_key->key_index)
80073b01e57SJeff Chen encrypt_key->key_index = NXPWIFI_KEY_INDEX_UNICAST;
80173b01e57SJeff Chen
80273b01e57SJeff Chen if (remove_key)
80373b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
80473b01e57SJeff Chen HOST_ACT_GEN_SET,
80573b01e57SJeff Chen !KEY_INFO_ENABLED, encrypt_key, true);
80673b01e57SJeff Chen else
80773b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
80873b01e57SJeff Chen HOST_ACT_GEN_SET,
80973b01e57SJeff Chen KEY_INFO_ENABLED, encrypt_key, true);
81073b01e57SJeff Chen
81173b01e57SJeff Chen return ret;
81273b01e57SJeff Chen }
81373b01e57SJeff Chen
81473b01e57SJeff Chen /* IOCTL request handler to set/get network keys */
81573b01e57SJeff Chen static int
nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private * priv,struct nxpwifi_ds_encrypt_key * encrypt_key)81673b01e57SJeff Chen nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private *priv,
81773b01e57SJeff Chen struct nxpwifi_ds_encrypt_key *encrypt_key)
81873b01e57SJeff Chen {
81973b01e57SJeff Chen int status;
82073b01e57SJeff Chen
82173b01e57SJeff Chen if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
82273b01e57SJeff Chen status = nxpwifi_sec_ioctl_set_wpa_key(priv, encrypt_key);
82373b01e57SJeff Chen else
82473b01e57SJeff Chen status = nxpwifi_sec_ioctl_set_wep_key(priv, encrypt_key);
82573b01e57SJeff Chen
82673b01e57SJeff Chen return status;
82773b01e57SJeff Chen }
82873b01e57SJeff Chen
82973b01e57SJeff Chen /* Return driver version string */
83073b01e57SJeff Chen int
nxpwifi_drv_get_driver_version(struct nxpwifi_adapter * adapter,char * version,int max_len)83173b01e57SJeff Chen nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, char *version,
83273b01e57SJeff Chen int max_len)
83373b01e57SJeff Chen {
83473b01e57SJeff Chen union {
83573b01e57SJeff Chen __le32 l;
83673b01e57SJeff Chen u8 c[4];
83773b01e57SJeff Chen } ver;
83873b01e57SJeff Chen char fw_ver[32];
83973b01e57SJeff Chen
84073b01e57SJeff Chen ver.l = cpu_to_le32(adapter->fw_release_number);
84173b01e57SJeff Chen sprintf(fw_ver, "%u.%u.%u.p%u.%u", ver.c[2], ver.c[1],
84273b01e57SJeff Chen ver.c[0], ver.c[3], adapter->fw_hotfix_ver);
84373b01e57SJeff Chen
84473b01e57SJeff Chen snprintf(version, max_len, nxpwifi_driver_version, fw_ver);
84573b01e57SJeff Chen
84673b01e57SJeff Chen nxpwifi_dbg(adapter, MSG, "info: NXPWIFI VERSION: %s\n", version);
84773b01e57SJeff Chen
84873b01e57SJeff Chen return 0;
84973b01e57SJeff Chen }
85073b01e57SJeff Chen
85173b01e57SJeff Chen /* Sends IOCTL request to set encoding parameters */
nxpwifi_set_encode(struct nxpwifi_private * priv,struct key_params * kp,const u8 * key,int key_len,u8 key_index,const u8 * mac_addr,int disable)85273b01e57SJeff Chen int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp,
85373b01e57SJeff Chen const u8 *key, int key_len, u8 key_index,
85473b01e57SJeff Chen const u8 *mac_addr, int disable)
85573b01e57SJeff Chen {
85673b01e57SJeff Chen struct nxpwifi_ds_encrypt_key encrypt_key;
85773b01e57SJeff Chen
85873b01e57SJeff Chen memset(&encrypt_key, 0, sizeof(encrypt_key));
85973b01e57SJeff Chen encrypt_key.key_len = key_len;
86073b01e57SJeff Chen encrypt_key.key_index = key_index;
86173b01e57SJeff Chen
86273b01e57SJeff Chen if (kp) {
86373b01e57SJeff Chen encrypt_key.key_cipher = kp->cipher;
86473b01e57SJeff Chen if (kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
86573b01e57SJeff Chen kp->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256)
86673b01e57SJeff Chen encrypt_key.is_igtk_key = true;
86773b01e57SJeff Chen }
86873b01e57SJeff Chen
86973b01e57SJeff Chen if (!disable) {
87073b01e57SJeff Chen if (key_len)
87173b01e57SJeff Chen memcpy(encrypt_key.key_material, key, key_len);
87273b01e57SJeff Chen else
87373b01e57SJeff Chen encrypt_key.is_current_wep_key = true;
87473b01e57SJeff Chen
87573b01e57SJeff Chen if (mac_addr)
87673b01e57SJeff Chen memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
87773b01e57SJeff Chen if (kp && kp->seq && kp->seq_len) {
87873b01e57SJeff Chen memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
87973b01e57SJeff Chen encrypt_key.pn_len = kp->seq_len;
88073b01e57SJeff Chen encrypt_key.is_rx_seq_valid = true;
88173b01e57SJeff Chen }
88273b01e57SJeff Chen } else {
88373b01e57SJeff Chen encrypt_key.key_disable = true;
88473b01e57SJeff Chen if (mac_addr)
88573b01e57SJeff Chen memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
88673b01e57SJeff Chen }
88773b01e57SJeff Chen
88873b01e57SJeff Chen return nxpwifi_sec_ioctl_encrypt_key(priv, &encrypt_key);
88973b01e57SJeff Chen }
89073b01e57SJeff Chen
89173b01e57SJeff Chen /* Sends IOCTL request to get extended version */
89273b01e57SJeff Chen int
nxpwifi_get_ver_ext(struct nxpwifi_private * priv,u32 version_str_sel)89373b01e57SJeff Chen nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel)
89473b01e57SJeff Chen {
89573b01e57SJeff Chen struct nxpwifi_ver_ext ver_ext;
89673b01e57SJeff Chen
89773b01e57SJeff Chen memset(&ver_ext, 0, sizeof(ver_ext));
89873b01e57SJeff Chen ver_ext.version_str_sel = version_str_sel;
89973b01e57SJeff Chen
90073b01e57SJeff Chen return nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT,
90173b01e57SJeff Chen HOST_ACT_GEN_GET, 0, &ver_ext, true);
90273b01e57SJeff Chen }
90373b01e57SJeff Chen
90473b01e57SJeff Chen int
nxpwifi_remain_on_chan_cfg(struct nxpwifi_private * priv,u16 action,struct ieee80211_channel * chan,unsigned int duration)90573b01e57SJeff Chen nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action,
90673b01e57SJeff Chen struct ieee80211_channel *chan,
90773b01e57SJeff Chen unsigned int duration)
90873b01e57SJeff Chen {
90973b01e57SJeff Chen struct host_cmd_ds_remain_on_chan roc_cfg;
91073b01e57SJeff Chen u8 sc;
91173b01e57SJeff Chen int ret;
91273b01e57SJeff Chen
91373b01e57SJeff Chen memset(&roc_cfg, 0, sizeof(roc_cfg));
91473b01e57SJeff Chen roc_cfg.action = cpu_to_le16(action);
91573b01e57SJeff Chen if (action == HOST_ACT_GEN_SET) {
91673b01e57SJeff Chen roc_cfg.band_cfg = chan->band;
91773b01e57SJeff Chen sc = nxpwifi_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
91873b01e57SJeff Chen roc_cfg.band_cfg |= (sc << 2);
91973b01e57SJeff Chen
92073b01e57SJeff Chen roc_cfg.channel =
92173b01e57SJeff Chen ieee80211_frequency_to_channel(chan->center_freq);
92273b01e57SJeff Chen roc_cfg.duration = cpu_to_le32(duration);
92373b01e57SJeff Chen }
92473b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_REMAIN_ON_CHAN,
92573b01e57SJeff Chen action, 0, &roc_cfg, true);
92673b01e57SJeff Chen if (ret) {
92773b01e57SJeff Chen nxpwifi_dbg(priv->adapter, ERROR,
92873b01e57SJeff Chen "failed to remain on channel\n");
92973b01e57SJeff Chen return ret;
93073b01e57SJeff Chen }
93173b01e57SJeff Chen
93273b01e57SJeff Chen return roc_cfg.status;
93373b01e57SJeff Chen }
93473b01e57SJeff Chen
93573b01e57SJeff Chen /* Sends IOCTL request to get statistics information */
93673b01e57SJeff Chen int
nxpwifi_get_stats_info(struct nxpwifi_private * priv,struct nxpwifi_ds_get_stats * log)93773b01e57SJeff Chen nxpwifi_get_stats_info(struct nxpwifi_private *priv,
93873b01e57SJeff Chen struct nxpwifi_ds_get_stats *log)
93973b01e57SJeff Chen {
94073b01e57SJeff Chen return nxpwifi_send_cmd(priv, HOST_CMD_802_11_GET_LOG,
94173b01e57SJeff Chen HOST_ACT_GEN_GET, 0, log, true);
94273b01e57SJeff Chen }
94373b01e57SJeff Chen
94473b01e57SJeff Chen /* IOCTL request handler to read/write register */
nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private * priv,struct nxpwifi_ds_reg_rw * reg_rw,u16 action)94573b01e57SJeff Chen static int nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private *priv,
94673b01e57SJeff Chen struct nxpwifi_ds_reg_rw *reg_rw,
94773b01e57SJeff Chen u16 action)
94873b01e57SJeff Chen {
94973b01e57SJeff Chen u16 cmd_no;
95073b01e57SJeff Chen
95173b01e57SJeff Chen switch (reg_rw->type) {
95273b01e57SJeff Chen case NXPWIFI_REG_MAC:
95373b01e57SJeff Chen cmd_no = HOST_CMD_MAC_REG_ACCESS;
95473b01e57SJeff Chen break;
95573b01e57SJeff Chen case NXPWIFI_REG_BBP:
95673b01e57SJeff Chen cmd_no = HOST_CMD_BBP_REG_ACCESS;
95773b01e57SJeff Chen break;
95873b01e57SJeff Chen case NXPWIFI_REG_RF:
95973b01e57SJeff Chen cmd_no = HOST_CMD_RF_REG_ACCESS;
96073b01e57SJeff Chen break;
96173b01e57SJeff Chen case NXPWIFI_REG_PMIC:
96273b01e57SJeff Chen cmd_no = HOST_CMD_PMIC_REG_ACCESS;
96373b01e57SJeff Chen break;
96473b01e57SJeff Chen case NXPWIFI_REG_CAU:
96573b01e57SJeff Chen cmd_no = HOST_CMD_CAU_REG_ACCESS;
96673b01e57SJeff Chen break;
96773b01e57SJeff Chen default:
96873b01e57SJeff Chen return -EINVAL;
96973b01e57SJeff Chen }
97073b01e57SJeff Chen
97173b01e57SJeff Chen return nxpwifi_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
97273b01e57SJeff Chen }
97373b01e57SJeff Chen
97473b01e57SJeff Chen /* Sends IOCTL request to write to a register */
97573b01e57SJeff Chen int
nxpwifi_reg_write(struct nxpwifi_private * priv,u32 reg_type,u32 reg_offset,u32 reg_value)97673b01e57SJeff Chen nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type,
97773b01e57SJeff Chen u32 reg_offset, u32 reg_value)
97873b01e57SJeff Chen {
97973b01e57SJeff Chen struct nxpwifi_ds_reg_rw reg_rw;
98073b01e57SJeff Chen
98173b01e57SJeff Chen reg_rw.type = reg_type;
98273b01e57SJeff Chen reg_rw.offset = reg_offset;
98373b01e57SJeff Chen reg_rw.value = reg_value;
98473b01e57SJeff Chen
98573b01e57SJeff Chen return nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_SET);
98673b01e57SJeff Chen }
98773b01e57SJeff Chen
98873b01e57SJeff Chen /* Sends IOCTL request to read from a register */
98973b01e57SJeff Chen int
nxpwifi_reg_read(struct nxpwifi_private * priv,u32 reg_type,u32 reg_offset,u32 * value)99073b01e57SJeff Chen nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type,
99173b01e57SJeff Chen u32 reg_offset, u32 *value)
99273b01e57SJeff Chen {
99373b01e57SJeff Chen int ret;
99473b01e57SJeff Chen struct nxpwifi_ds_reg_rw reg_rw;
99573b01e57SJeff Chen
99673b01e57SJeff Chen reg_rw.type = reg_type;
99773b01e57SJeff Chen reg_rw.offset = reg_offset;
99873b01e57SJeff Chen ret = nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_GET);
99973b01e57SJeff Chen
100073b01e57SJeff Chen if (!ret)
100173b01e57SJeff Chen *value = reg_rw.value;
100273b01e57SJeff Chen
100373b01e57SJeff Chen return ret;
100473b01e57SJeff Chen }
100573b01e57SJeff Chen
100673b01e57SJeff Chen /* Sends IOCTL request to read from EEPROM */
100773b01e57SJeff Chen int
nxpwifi_eeprom_read(struct nxpwifi_private * priv,u16 offset,u16 bytes,u8 * value)100873b01e57SJeff Chen nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes,
100973b01e57SJeff Chen u8 *value)
101073b01e57SJeff Chen {
101173b01e57SJeff Chen int ret;
101273b01e57SJeff Chen struct nxpwifi_ds_read_eeprom rd_eeprom;
101373b01e57SJeff Chen
101473b01e57SJeff Chen rd_eeprom.offset = offset;
101573b01e57SJeff Chen rd_eeprom.byte_count = bytes;
101673b01e57SJeff Chen
101773b01e57SJeff Chen /* Send request to firmware */
101873b01e57SJeff Chen ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_EEPROM_ACCESS,
101973b01e57SJeff Chen HOST_ACT_GEN_GET, 0, &rd_eeprom, true);
102073b01e57SJeff Chen
102173b01e57SJeff Chen if (!ret)
102273b01e57SJeff Chen memcpy(value, rd_eeprom.value,
102373b01e57SJeff Chen min((u16)MAX_EEPROM_DATA, rd_eeprom.byte_count));
102473b01e57SJeff Chen return ret;
102573b01e57SJeff Chen }
102673b01e57SJeff Chen
102773b01e57SJeff Chen /* Set generic IE(s); handle WPA/WPS specially */
102873b01e57SJeff Chen static int
nxpwifi_set_gen_ie_helper(struct nxpwifi_private * priv,u8 * ie_data_ptr,u16 ie_len)102973b01e57SJeff Chen nxpwifi_set_gen_ie_helper(struct nxpwifi_private *priv, u8 *ie_data_ptr,
103073b01e57SJeff Chen u16 ie_len)
103173b01e57SJeff Chen {
103273b01e57SJeff Chen struct ieee80211_vendor_ie *pvendor_ie;
103373b01e57SJeff Chen static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
103473b01e57SJeff Chen static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
103573b01e57SJeff Chen u16 unparsed_len = ie_len, cur_ie_len;
103673b01e57SJeff Chen
103773b01e57SJeff Chen /* If the passed length is zero, reset the buffer */
103873b01e57SJeff Chen if (!ie_len) {
103973b01e57SJeff Chen priv->gen_ie_buf_len = 0;
104073b01e57SJeff Chen priv->wps.session_enable = false;
104173b01e57SJeff Chen return 0;
104273b01e57SJeff Chen } else if (!ie_data_ptr ||
104373b01e57SJeff Chen ie_len <= sizeof(struct element)) {
104473b01e57SJeff Chen return -EINVAL;
104573b01e57SJeff Chen }
104673b01e57SJeff Chen pvendor_ie = (struct ieee80211_vendor_ie *)ie_data_ptr;
104773b01e57SJeff Chen
104873b01e57SJeff Chen while (pvendor_ie) {
104973b01e57SJeff Chen cur_ie_len = pvendor_ie->len + sizeof(struct element);
105073b01e57SJeff Chen
105173b01e57SJeff Chen if (pvendor_ie->element_id == WLAN_EID_RSN) {
105273b01e57SJeff Chen /* element is a WPA/WPA2 element so call set_wpa function */
105373b01e57SJeff Chen nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len);
105473b01e57SJeff Chen priv->wps.session_enable = false;
105573b01e57SJeff Chen goto next_ie;
105673b01e57SJeff Chen }
105773b01e57SJeff Chen
105873b01e57SJeff Chen if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
105973b01e57SJeff Chen /* Test to see if it is a WPA element, if not, then it is a gen element */
106073b01e57SJeff Chen if (!memcmp(&pvendor_ie->oui, wpa_oui,
106173b01e57SJeff Chen sizeof(wpa_oui))) {
106273b01e57SJeff Chen /* element is a WPA/WPA2 element so call set_wpa function */
106373b01e57SJeff Chen nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie,
106473b01e57SJeff Chen cur_ie_len);
106573b01e57SJeff Chen priv->wps.session_enable = false;
106673b01e57SJeff Chen goto next_ie;
106773b01e57SJeff Chen }
106873b01e57SJeff Chen
106973b01e57SJeff Chen if (!memcmp(&pvendor_ie->oui, wps_oui,
107073b01e57SJeff Chen sizeof(wps_oui))) {
107173b01e57SJeff Chen /*
107273b01e57SJeff Chen * Test to see if it is a WPS element, if so, enable wps session
107373b01e57SJeff Chen * flag
107473b01e57SJeff Chen */
107573b01e57SJeff Chen priv->wps.session_enable = true;
107673b01e57SJeff Chen nxpwifi_dbg(priv->adapter, MSG,
107773b01e57SJeff Chen "WPS Session Enabled.\n");
107873b01e57SJeff Chen nxpwifi_set_wps_ie(priv, (u8 *)pvendor_ie,
107973b01e57SJeff Chen cur_ie_len);
108073b01e57SJeff Chen goto next_ie;
108173b01e57SJeff Chen }
108273b01e57SJeff Chen }
108373b01e57SJeff Chen
108473b01e57SJeff Chen /*
108573b01e57SJeff Chen * Verify that the passed length is not larger than the available space
108673b01e57SJeff Chen * remaining in the buffer
108773b01e57SJeff Chen */
108873b01e57SJeff Chen if (cur_ie_len <
108973b01e57SJeff Chen (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
109073b01e57SJeff Chen /* Append the passed data to the end of the genIeBuffer */
109173b01e57SJeff Chen memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len,
109273b01e57SJeff Chen (u8 *)pvendor_ie, cur_ie_len);
109373b01e57SJeff Chen /* Increment the stored buffer length by the size passed */
109473b01e57SJeff Chen priv->gen_ie_buf_len += cur_ie_len;
109573b01e57SJeff Chen }
109673b01e57SJeff Chen
109773b01e57SJeff Chen next_ie:
109873b01e57SJeff Chen unparsed_len -= cur_ie_len;
109973b01e57SJeff Chen
110073b01e57SJeff Chen if (unparsed_len <= sizeof(struct element))
110173b01e57SJeff Chen pvendor_ie = NULL;
110273b01e57SJeff Chen else
110373b01e57SJeff Chen pvendor_ie = (struct ieee80211_vendor_ie *)
110473b01e57SJeff Chen (((u8 *)pvendor_ie) + cur_ie_len);
110573b01e57SJeff Chen }
110673b01e57SJeff Chen
110773b01e57SJeff Chen return 0;
110873b01e57SJeff Chen }
110973b01e57SJeff Chen
111073b01e57SJeff Chen /* IOCTL request handler to set/get generic element */
nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private * priv,struct nxpwifi_ds_misc_gen_ie * gen_ie,u16 action)111173b01e57SJeff Chen static int nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private *priv,
111273b01e57SJeff Chen struct nxpwifi_ds_misc_gen_ie *gen_ie,
111373b01e57SJeff Chen u16 action)
111473b01e57SJeff Chen {
111573b01e57SJeff Chen struct nxpwifi_adapter *adapter = priv->adapter;
111673b01e57SJeff Chen
111773b01e57SJeff Chen switch (gen_ie->type) {
111873b01e57SJeff Chen case NXPWIFI_IE_TYPE_GEN_IE:
111973b01e57SJeff Chen if (action == HOST_ACT_GEN_GET) {
112073b01e57SJeff Chen gen_ie->len = priv->wpa_ie_len;
112173b01e57SJeff Chen memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
112273b01e57SJeff Chen } else {
112373b01e57SJeff Chen nxpwifi_set_gen_ie_helper(priv, gen_ie->ie_data,
112473b01e57SJeff Chen (u16)gen_ie->len);
112573b01e57SJeff Chen }
112673b01e57SJeff Chen break;
112773b01e57SJeff Chen case NXPWIFI_IE_TYPE_ARP_FILTER:
112873b01e57SJeff Chen memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
112973b01e57SJeff Chen if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
113073b01e57SJeff Chen adapter->arp_filter_size = 0;
113173b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR,
113273b01e57SJeff Chen "invalid ARP filter size\n");
113373b01e57SJeff Chen return -EINVAL;
113473b01e57SJeff Chen }
113573b01e57SJeff Chen memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len);
113673b01e57SJeff Chen adapter->arp_filter_size = gen_ie->len;
113773b01e57SJeff Chen break;
113873b01e57SJeff Chen default:
113973b01e57SJeff Chen nxpwifi_dbg(adapter, ERROR, "invalid element type\n");
114073b01e57SJeff Chen return -EINVAL;
114173b01e57SJeff Chen }
114273b01e57SJeff Chen return 0;
114373b01e57SJeff Chen }
114473b01e57SJeff Chen
114573b01e57SJeff Chen /* Sends IOCTL request to set a generic element */
114673b01e57SJeff Chen int
nxpwifi_set_gen_ie(struct nxpwifi_private * priv,const u8 * ie,int ie_len)114773b01e57SJeff Chen nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len)
114873b01e57SJeff Chen {
114973b01e57SJeff Chen struct nxpwifi_ds_misc_gen_ie gen_ie;
115073b01e57SJeff Chen
115173b01e57SJeff Chen if (ie_len > IEEE_MAX_IE_SIZE)
115273b01e57SJeff Chen return -EFAULT;
115373b01e57SJeff Chen
115473b01e57SJeff Chen gen_ie.type = NXPWIFI_IE_TYPE_GEN_IE;
115573b01e57SJeff Chen gen_ie.len = ie_len;
115673b01e57SJeff Chen memcpy(gen_ie.ie_data, ie, ie_len);
115773b01e57SJeff Chen
115873b01e57SJeff Chen return nxpwifi_misc_ioctl_gen_ie(priv, &gen_ie, HOST_ACT_GEN_SET);
115973b01e57SJeff Chen }
116073b01e57SJeff Chen
116173b01e57SJeff Chen /* Get Host Sleep wakeup reason */
nxpwifi_get_wakeup_reason(struct nxpwifi_private * priv,u16 action,int cmd_type,struct nxpwifi_ds_wakeup_reason * wakeup_reason)116273b01e57SJeff Chen int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action,
116373b01e57SJeff Chen int cmd_type,
116473b01e57SJeff Chen struct nxpwifi_ds_wakeup_reason *wakeup_reason)
116573b01e57SJeff Chen {
116673b01e57SJeff Chen return nxpwifi_send_cmd(priv, HOST_CMD_HS_WAKEUP_REASON,
116773b01e57SJeff Chen HOST_ACT_GEN_GET, 0, wakeup_reason,
116873b01e57SJeff Chen cmd_type == NXPWIFI_SYNC_CMD);
116973b01e57SJeff Chen }
117073b01e57SJeff Chen
nxpwifi_get_chan_info(struct nxpwifi_private * priv,struct nxpwifi_channel_band * channel_band)117173b01e57SJeff Chen int nxpwifi_get_chan_info(struct nxpwifi_private *priv,
117273b01e57SJeff Chen struct nxpwifi_channel_band *channel_band)
117373b01e57SJeff Chen {
117473b01e57SJeff Chen return nxpwifi_send_cmd(priv, HOST_CMD_STA_CONFIGURE,
117573b01e57SJeff Chen HOST_ACT_GEN_GET, 0, channel_band,
117673b01e57SJeff Chen NXPWIFI_SYNC_CMD);
117773b01e57SJeff Chen }
1178